(MOT-3554) feat(console): landing-page demo build of the console for the website - #624
(MOT-3554) feat(console): landing-page demo build of the console for the website#624anthonyiscoding wants to merge 19 commits into
Conversation
Adds `src/demo/`: the console's own chat transcript and trace waterfall replaying a canned payments-ledger turn, built as a standalone page the marketing site embeds in an iframe. The chat half rides the existing `ChatBackend` seam — the scenario emits real `StreamEvent`s in the documented order and `usePlayer` folds them with ChatView's own reducer, so `MessageList` and every per-worker result renderer behave exactly as they do in the product. Three demo-only markers ride alongside for the spans and the callout strip, which keeps the trace pane in lockstep with the transcript without a second timeline. `npm run build:demo` (vite.demo.config.ts) emits a self-contained `dist-demo/` at ~1.9MB. `@pierre/diffs` is aliased to a stub there: the scenario dispatches no `coder::*` calls, and the package would otherwise land every shiki grammar (~13MB of chunks) in a committed artifact. MessageList/Message gain `defaultOpenCalls`, forwarding the `defaultOpen` that FunctionTriggerCard and FunctionTriggerGroup already accept. Off in the product; on for showcase surfaces whose point is the renderers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The demo dropped the console's left column, which meant the session tree — the thing that shows sub-agents are sessions and not log lines — had nowhere to appear. Brings back `ConversationSidebar` as the third column (hidden below `lg`, where a session tree is not worth a phone's width) and gives it something to show. Step 3 now dispatches three `harness::spawn` calls as one parallel batch: ledger core, stripe webhook, test suite. Each opens its own session, its own `execute harness::spawn` span with the child's `harness::turn step` nested inside, and its own little transcript — click a child in the sidebar and the transcript pane switches to it, header and all. They report out of dispatch order, as children do. Only the first asks for `database::*`, a scope the session does not hold, so only the first stops at the approval gate while the other two are already running. That is what a deny-by-default glob policy does to a batch, and it reads better than gating the batch wholesale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two things the landing demo made obvious.
Call durations rendered as a bare `{durationMs}ms`, so anything faster
than a millisecond read as `0ms` or `0.31ms`. A lot of what an agent
dispatches is engine-local — a trigger registration, a state read — and
finishes in microseconds. `formatCallDuration` keeps whole milliseconds
exactly as they render today and switches to `μs` below one, matching
the traces surface (`traceUtils.formatDuration`). Applied to the card
header and the group header's total.
In the demo itself, a call's reported duration is now separate from how
long the demo lingers on it (`reportedMs`): `engine::register_trigger`
takes 310µs and gets a third of a second of screen time, `shell::exec`
reports the 6041ms its own payload always claimed. Spans close at the
reported duration too, so the waterfall shows the sliver it should.
Prose was streaming at about twice reading speed and then being buried
by the next card. Tokens are slower, and a finished thought or answer now
holds for a beat scaled to its length before anything lands on top of it.
The hold before the loop restarts grew to match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both panes were pinned to their newest row unconditionally, so scrolling up to reread a card was yanked back by the next token. `useTailFollow` keeps the pin but releases it on real input (wheel, touch) and re-arms when a scroll lands back at the bottom. Position alone cannot tell a person's scroll from the programmatic ones, hence gating on the input events rather than on distance. The demo also no longer loops by default. It ends holding a finished transcript, a full trace and three child sessions, which is the state worth clicking through — restarting on top of someone reading it is not. `?loop=1` brings the old behaviour back, and the host can post `iii-demo-replay` to start it over. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The catalogue, a worker's contract, a test run's output: cards big enough to want reading, replaced by the next beat before anyone could. A `readPause` now sits between those steps, rendered as a thought reading "letting the user read…" so the wait is a visible line rather than the demo appearing to stall, and placed between durable steps so it never inflates a step's span. The waterfall's expand-all / collapse-all are hidden while a trace is still arriving (`showExpandControls`). The chart re-expands the whole tree whenever `data.spans` changes, so a collapse taken mid-stream is undone by the next span to land — the buttons look broken because, against a live trace, they are. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each spawned child now replays its own transcript while the parent waits: a plan in prose, short thoughts between calls, and the calls themselves — two `database::create_table`s and the files it writes. Clicking a sidebar row mid-run shows that child reasoning and dispatching while the other two keep going. Those dispatches also open their own `execute` spans under the child's `harness::turn step`, so the fan-out reads as three branches of one trace rather than three opaque `harness::spawn` bars. The children are driven off one clock instead of a per-child finish loop, because they interleave. The @pierre/diffs build stub renders file bodies unhighlighted now rather than a "not bundled" notice — the coder card is reachable once children write files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The landing demo's traces pane renders the real console surface (TimelineStrip, TraceFilters with stats, the timeline/waterfall switcher, WorkerBreakdown), with the waterfall filling the whole pane rather than the console page's 420px box. Demo-only scroll patches keep the embed well behaved: scrollIntoView stays inside the frame, and inner scrollports contain the wheel in the two-pane layout so the embedder's scroll-zoom only advances from surface with nothing of its own to scroll. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sits right of the recorded-session dot and posts iii-demo-close to the host page, which scrolls the console away. bg-ink/text-bg flips with the theme, so it reads as the way out in both. Standalone opens of the demo never see it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Streaming traces re-lay the flame-graph on every span, and bars used to teleport: the axis refits, greedy first-fit reshuffles lines as live subtrees grow, and provisional geometry snaps when real end times land. Two changes calm it. packSubtrees now prefers each span's line from the previous layout while it still fits, threaded through a ref so only genuine collisions move bars. And a timeline-glide utility transitions bar, connector, and spill-label geometry over 300ms (off under reduced motion), composing with timeline-enter so new bars still mount in place. The masthead strip keeps its per-frame imperative updates; transitions would lag its sliding window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The host page posts {type:'iii-demo', active:true} once the frame is
actually on screen; without the flag the demo still autoplays.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Relative ('./') paths break behind Vercel's cleanUrls, which redirects
/console-demo/index.html to /console-demo without the trailing slash;
./assets/* then resolves to the domain root and the bundle 404s. The
marketing site vendors this build at /console-demo/, so the base is
that mount point.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The landing demo's empty-state greeting is now demo-local (DemoEmptyState) and rides the transcript scroller via MessageList's new optional header slot, so the replay scrolls it away instead of unmounting it. The chrome bar gains pause/play and replay controls; the player holds between scenario events while paused and can restart the turn from any state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Subagents everywhere a reader sees the word (no child/children, no hyphen), sessions framed as started via the sandbox worker, the trace callouts credit the observability worker and the queue worker the way the final answer does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Below lg the traces pane hides with the sidebar and the transcript takes the whole frame; desktop keeps the three-column layout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
skill-check — worker0 verified, 49 skipped (no docs/).
Four for four. Nicely done. |
|
Warning Review limit reached
Next review available in: 33 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis change adds a standalone iframe-aware landing demo with scripted transcript and trace playback, approval-gated child sessions, chat display options, human-readable call durations, and sticky trace timeline layout with configurable waterfall controls. ChangesLanding demo and console UI
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Host
participant DemoEntry
participant LandingDemo
participant usePlayer
participant runScenario
Host->>DemoEntry: send theme, replay, or playback message
DemoEntry->>LandingDemo: render controlled demo state
LandingDemo->>usePlayer: start or pause playback
usePlayer->>runScenario: consume scripted events
runScenario-->>usePlayer: transcript and trace events
usePlayer-->>LandingDemo: messages, spans, callouts, and controls
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
console/web/src/pages/TracesV2/components/WaterfallChart.tsx (1)
378-432: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDivider renders orphaned if
showExpandControls={false}is ever combined with aspanFilter.The separator
<div className="w-px h-4 bg-rule-2 mx-1" />at line ~414 is gated only onspanFilter/group presence, not onshowExpandControls. It's meant to separate the expand/collapse buttons from the filter menu, so if a future caller setsshowExpandControls={false}while also supplyingspanFilter+spanGroupKey, the divider shows with nothing to its left. Not triggered today since the only new caller (LandingDemo.tsx) passes neitherspanFilternorspanGroupKey, but it's a real latent bug in the new conditional.🐛 Proposed fix
{spanFilter && (spanGroups.length > 0 || workerGroups.length > 0 || internalGroups.length > 0 || hiddenSpanCount > 0) && ( <> - <div aria-hidden className="w-px h-4 bg-rule-2 mx-1" /> + {showExpandControls && ( + <div aria-hidden className="w-px h-4 bg-rule-2 mx-1" /> + )} <SpanFilterMenu🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@console/web/src/pages/TracesV2/components/WaterfallChart.tsx` around lines 378 - 432, Gate the separator div in the span filter toolbar on showExpandControls as well as the existing spanFilter/group-presence condition. Update the conditional surrounding the SpanFilterMenu in the WaterfallChart render, while leaving the filter menu itself available whenever its current filter condition is met.
🧹 Nitpick comments (4)
console/web/src/pages/TracesV2/components/timeline/TraceTimeline.tsx (1)
446-453: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAvoid mutating the ref during render; defer to
useLayoutEffect.
stickyOffsets.current = layout.offsetsruns unconditionally in the render body. React's own docs state renders should not writeref.currentexcept for one-time initialization, since a render pass that gets discarded (Strict Mode double-invoke, or future concurrent/prerendering paths) would still leave the cache mutated, desyncing the "sticky" hint from what actually committed.useLayoutEffectis already used elsewhere in this file and gives the same “read last committed layout” semantics without the render-purity violation.♻️ Proposed fix
const stickyOffsets = useRef<ReadonlyMap<string, number>>(new Map()) const layout = useMemo( () => buildLayout(visibleData.spans, detail.spans, stickyOffsets.current), [visibleData.spans, detail.spans], ) - stickyOffsets.current = layout.offsets + useLayoutEffect(() => { + stickyOffsets.current = layout.offsets + }, [layout])Based on React's docs — "Do not write or read ref.current during rendering, except for initialization" — this pattern is also what the
eslint-plugin-react-hooks"refs" rule flags and can even raise a hard "Cannot access refs during render" error under the React Compiler.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@console/web/src/pages/TracesV2/components/timeline/TraceTimeline.tsx` around lines 446 - 453, Move the stickyOffsets cache assignment out of the render body and into a useLayoutEffect tied to the computed layout, updating stickyOffsets.current only after the layout commits. Keep buildLayout reading the previously committed stickyOffsets.current inside useMemo, and preserve the existing dependency behavior for visibleData.spans and detail.spans.console/web/src/demo/scenario.ts (1)
229-247: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueMake the gate precondition consistent.
fcall-startmarks the cardpendingApprovalongatealone, while the release below also requiresfunctionTriggerId. A caller passinggatewithout an id would render a card that is pending forever and never awaited. Gate both on the same condition (or makefunctionTriggerIdrequired whengateis set).♻️ Proposed tightening
+ const gated = Boolean(gate && functionTriggerId) yield { kind: "fcall-start", functionId: fn, input, - ...(gate + ...(gated ? { pendingApproval: true, functionTriggerId, sessionId: SESSION_ID, } : {}), }; if (callout) yield { kind: "demo-callout", callout }; - if (gate && functionTriggerId) { + if (gated && gate) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@console/web/src/demo/scenario.ts` around lines 229 - 247, Make the `fcall-start` pending-approval condition match the release condition used by the `gate` invocation: only set `pendingApproval` and include approval metadata when both `gate` and `functionTriggerId` are present. Preserve the existing await, abort check, and approval-cleared behavior in the `if (gate && functionTriggerId)` block.console/web/src/demo/main.tsx (1)
116-118: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low valueConsider validating the message origin.
Any framing/opener page (or any window with a handle to this frame) can post
iii-demo*messages and drive pause/replay/theme. Gating on an expected host origin keeps control with the marketing site.🛡️ Sketch
window.addEventListener('message', (event: MessageEvent) => { + if (event.source !== window.parent) return const data = event.data if (!data || typeof data !== 'object') return🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@console/web/src/demo/main.tsx` around lines 116 - 118, Update the message handler registered by the window.addEventListener('message', ...) call to validate event.origin against the configured or expected marketing-site origin before processing event.data. Ignore messages from all other origins while preserving the existing handling for valid iii-demo* messages.console/web/src/lib/format-call-duration.ts (1)
10-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding unit tests for the boundary branches.
The function has four distinct branches (invalid/negative, sub-ms, exactly 1ms, ms-and-above); a small test covering
NaN, negative,0,0.5,1, and1500would lock in the rounding/floor behavior described in the comment.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@console/web/src/lib/format-call-duration.ts` around lines 10 - 14, Add unit tests for formatCallDuration covering NaN and negative inputs, zero, a sub-millisecond value such as 0.5, exactly 1ms, and a larger value such as 1500ms; assert the expected microsecond/millisecond strings to preserve each boundary branch and rounding behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@console/web/src/components/chat/MessageList.tsx`:
- Around line 28-31: Update MessageListProps and the MessageList component to
accept and destructure defaultOpenCalls, then forward it through the message
rendering path to FunctionTriggerGroup and Message so FunctionTriggerCard
receives it and landing demo call groups open by default.
In `@console/web/src/demo/main.tsx`:
- Around line 107-111: Update the LandingDemo loop prop in the rendering flow to
default to true and disable looping only when the query parameter explicitly
equals '0'. Preserve the existing query parsing and pass the corrected boolean
to LandingDemo.
In `@console/web/src/demo/usePlayer.ts`:
- Around line 318-333: Update the local wait helper near holdWhilePaused so each
timeout-based wait removes its abort listener when the timer resolves, while
abort still clears the timer and resolves immediately. Preserve the existing
wait behavior for prompt typing, pause polling, and looped play calls, including
the shared signal handling.
---
Outside diff comments:
In `@console/web/src/pages/TracesV2/components/WaterfallChart.tsx`:
- Around line 378-432: Gate the separator div in the span filter toolbar on
showExpandControls as well as the existing spanFilter/group-presence condition.
Update the conditional surrounding the SpanFilterMenu in the WaterfallChart
render, while leaving the filter menu itself available whenever its current
filter condition is met.
---
Nitpick comments:
In `@console/web/src/demo/main.tsx`:
- Around line 116-118: Update the message handler registered by the
window.addEventListener('message', ...) call to validate event.origin against
the configured or expected marketing-site origin before processing event.data.
Ignore messages from all other origins while preserving the existing handling
for valid iii-demo* messages.
In `@console/web/src/demo/scenario.ts`:
- Around line 229-247: Make the `fcall-start` pending-approval condition match
the release condition used by the `gate` invocation: only set `pendingApproval`
and include approval metadata when both `gate` and `functionTriggerId` are
present. Preserve the existing await, abort check, and approval-cleared behavior
in the `if (gate && functionTriggerId)` block.
In `@console/web/src/lib/format-call-duration.ts`:
- Around line 10-14: Add unit tests for formatCallDuration covering NaN and
negative inputs, zero, a sub-millisecond value such as 0.5, exactly 1ms, and a
larger value such as 1500ms; assert the expected microsecond/millisecond strings
to preserve each boundary branch and rounding behavior.
In `@console/web/src/pages/TracesV2/components/timeline/TraceTimeline.tsx`:
- Around line 446-453: Move the stickyOffsets cache assignment out of the render
body and into a useLayoutEffect tied to the computed layout, updating
stickyOffsets.current only after the layout commits. Keep buildLayout reading
the previously committed stickyOffsets.current inside useMemo, and preserve the
existing dependency behavior for visibleData.spans and detail.spans.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: aa688453-5862-4829-b378-f4cc1e90a27b
📒 Files selected for processing (18)
console/web/.gitignoreconsole/web/demo.htmlconsole/web/package.jsonconsole/web/src/components/chat/FunctionTriggerGroup.tsxconsole/web/src/components/chat/Message.tsxconsole/web/src/components/chat/MessageList.tsxconsole/web/src/components/function-trigger/FunctionTriggerCard.tsxconsole/web/src/demo/EmptyState.tsxconsole/web/src/demo/LandingDemo.tsxconsole/web/src/demo/main.tsxconsole/web/src/demo/scenario.tsconsole/web/src/demo/stubs/pierre-diffs.tsxconsole/web/src/demo/usePlayer.tsconsole/web/src/index.cssconsole/web/src/lib/format-call-duration.tsconsole/web/src/pages/TracesV2/components/WaterfallChart.tsxconsole/web/src/pages/TracesV2/components/timeline/TraceTimeline.tsxconsole/web/vite.demo.config.ts
~8s into the replay, whenever the player isn't using the composer, it types out "click here to try the harness for yourself" — the type-out is the attention cue, the text is a live link to the harness quickstart on the worker registry, and the composer frame glows accent while the invitation is up. Hidden whenever the loop retypes the scenario prompt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@console/web/src/demo/LandingDemo.tsx`:
- Around line 198-200: Update the LandingDemo layout around MobileNotice so the
banner no longer uses absolute top positioning that overlaps the header’s
model/status controls; render it in normal flow below the header or explicitly
reserve equivalent vertical space while preserving the notice’s mobile behavior.
- Around line 127-152: Update the CTA typing logic around ctaChars and the
started effect so each replay/run resets ctaChars to zero and schedules the
8-second delay and type-out anew. Key the effect to the player’s per-run/replay
epoch rather than only started, while preserving cleanup of timers and hiding
the CTA when player.typed is true.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: cf056aab-cd77-4fb2-8693-81ab208ad122
📒 Files selected for processing (2)
console/web/src/demo/LandingDemo.tsxconsole/web/src/demo/scenario.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- console/web/src/demo/scenario.ts
| <div className="pointer-events-none absolute inset-x-0 top-0 z-10 flex flex-col items-center"> | ||
| <MobileNotice /> | ||
| </div> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep the mobile notice out of the header’s content area.
This absolute top-0 z-10 banner shares the transcript section’s top edge with the following header, so it overlays the model/status controls on mobile. Render it in normal flow below the header, or reserve vertical space for it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@console/web/src/demo/LandingDemo.tsx` around lines 198 - 200, Update the
LandingDemo layout around MobileNotice so the banner no longer uses absolute top
positioning that overlaps the header’s model/status controls; render it in
normal flow below the header or explicitly reserve equivalent vertical space
while preserving the notice’s mobile behavior.
The header-slot rewrite of MessageList was based on a copy that predated defaultOpenCalls, so LandingDemo's prop no longer typechecked (vite builds don't run tsc; CI does). The prop threads to Message and FunctionTriggerGroup exactly as before. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The one thing in the frame that leads out of the recording: an accent-token pill linking to the harness quickstart, with a shine sweep and a slow 3D tilt to earn a look. Motion lives in a demo-local stylesheet so the console's index.css stays the product's; both animations drop under reduced motion. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
console/web/src/demo/LandingDemo.tsx (1)
73-78: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDisable tail-follow whenever the pane is no longer near the bottom.
onScrollonly re-enables following near the bottom; keyboard scrolling and scrollbar dragging never clearfollowRef. The next transcript update can therefore snap the user back to the newest content. SetfollowRef.current = fromBottom <= FOLLOW_SLACK_PXin the scroll handler.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@console/web/src/demo/LandingDemo.tsx` around lines 73 - 78, Update the onScroll handler to assign followRef.current based on whether fromBottom is within FOLLOW_SLACK_PX, setting it false when the pane is scrolled farther away instead of only enabling it near the bottom.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@console/web/src/demo/demo.css`:
- Line 13: Update the currentColor keyword occurrences in the demo stylesheet,
including the locations around lines 13 and 41-42, to the Stylelint-approved
lowercase currentcolor casing. Preserve the surrounding CSS declarations
unchanged.
---
Outside diff comments:
In `@console/web/src/demo/LandingDemo.tsx`:
- Around line 73-78: Update the onScroll handler to assign followRef.current
based on whether fromBottom is within FOLLOW_SLACK_PX, setting it false when the
pane is scrolled farther away instead of only enabling it near the bottom.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: dca6aad9-4512-4506-b428-4e70835079ea
📒 Files selected for processing (4)
console/web/src/components/chat/MessageList.tsxconsole/web/src/demo/LandingDemo.tsxconsole/web/src/demo/demo.cssconsole/web/src/demo/main.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- console/web/src/demo/main.tsx
CodeRabbit on #624: every timer-based wait left an abort listener attached for the life of the run, and the paused-hold polls every 150ms. The CTA type-out now keys on the run, so a replay types it again instead of flashing the finished line. Also lowercase `currentcolor` for stylelint, and the ?loop doc matches the opt-in the code actually implements. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What
A separate vite build (
pnpm build:demo/vite.demo.config.ts) that renders the console's real chat transcript and traces surface replaying a scripted turn (web/src/demo/). The iii.dev website vendors the output of this build at/console-demo/and embeds it in the landing page's scroll-pinned console section, so this processing and bundling exists for the website: it gets the real product UI without depending on the console's toolchain, and the website CI stays standalone (seewebsite/scripts/sync-console-demo.shin iii-hq/iii).The demo entry is its own
demo.htmlwith no engine client and no injectable-UI import map;@pierre/diffsis stubbed so ~13MB of shiki grammars stay out of the vendored artifact, and assets build with absolute/console-demo/paths to survive Vercel's cleanUrls.Demo-local surface (
web/src/demo/, not in the console bundle)LandingDemo.tsx— chrome, transcript + traces panes, pause/replay controls, chat-only layout and a simplified-view notice on phone widthsscenario.ts/usePlayer.ts— the scripted turn, folded through the same stream reducerChatViewusesEmptyState.tsx— demo-local greeting that scrolls away with the transcriptProduct changes (deliberate, small)
MessageListgains an optionalheaderslot (renders nothing when unset — the real console is unchanged)Message/FunctionTriggerCardgain optionaldefaultOpenprops (off by default)WaterfallChartexpand controls become suppressible (showExpandControls, defaulttrue)Nμsinstead of0msin chat call cardstimeline-glide)🤖 Generated with Claude Code
Summary by CodeRabbit
dist-demo.