CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
live-feed.tsx
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| febd4f0 | 1 | /** |
| 2 | * LiveFeed — small SSR component that renders an empty <ul> and an inline | |
| 3 | * <script> which subscribes to a server-sent-events topic and appends | |
| 4 | * formatted list items as events arrive. | |
| 5 | * | |
| 6 | * Events on the wire are expected to shape `{action, actor, target}`. | |
| 7 | * If SSE fails or EventSource is unsupported, the <ul> simply stays empty | |
| 8 | * — the rest of the page still renders normally. | |
| 9 | */ | |
| 10 | ||
| 11 | import { liveSubscribeScript } from "../lib/sse-client"; | |
| 12 | ||
| 13 | export function LiveFeed(props: { | |
| 14 | topic: string; | |
| 15 | title?: string; | |
| 2316901 | 16 | }) { |
| febd4f0 | 17 | const title = props.title ?? "Live activity"; |
| 18 | const listId = "live-feed"; | |
| 19 | ||
| 20 | // formatFn is inlined client-side JS. It receives the parsed event payload | |
| 21 | // and returns an HTML string (an <li>). All interpolated values are HTML- | |
| 22 | // escaped to avoid breakout. | |
| 23 | const formatFn = ` | |
| 24 | function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c];});} | |
| 25 | var d = event && event.data ? event.data : event; | |
| 26 | if (!d) return ''; | |
| 27 | return '<li>' + esc(d.actor) + ' ' + esc(d.action) + ' ' + esc(d.target) + '</li>'; | |
| 28 | `; | |
| 29 | ||
| 30 | const script = liveSubscribeScript({ | |
| 31 | topic: props.topic, | |
| 32 | targetElementId: listId, | |
| 33 | formatFn, | |
| 34 | }); | |
| 35 | ||
| 36 | return ( | |
| 37 | <section | |
| 38 | class="live-feed" | |
| 39 | style="margin-top: 24px; padding: 16px; background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius)" | |
| 40 | > | |
| 41 | <h3 style="font-size: 14px; margin: 0 0 12px 0; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px"> | |
| 42 | {title} | |
| 43 | </h3> | |
| 44 | <ul | |
| 45 | id={listId} | |
| 46 | style="list-style: none; padding: 0; margin: 0; font-size: 13px; color: var(--text)" | |
| 47 | /> | |
| 48 | <script dangerouslySetInnerHTML={{ __html: script }} /> | |
| 49 | </section> | |
| 50 | ); | |
| 51 | } |