Skip to content

(MOT-3554) feat(console): landing-page demo build of the console for the website - #624

Open
anthonyiscoding wants to merge 19 commits into
mainfrom
add/landing-console-demo
Open

(MOT-3554) feat(console): landing-page demo build of the console for the website#624
anthonyiscoding wants to merge 19 commits into
mainfrom
add/landing-console-demo

Conversation

@anthonyiscoding

@anthonyiscoding anthonyiscoding commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

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 (see website/scripts/sync-console-demo.sh in iii-hq/iii).

The demo entry is its own demo.html with no engine client and no injectable-UI import map; @pierre/diffs is 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 widths
  • scenario.ts / usePlayer.ts — the scripted turn, folded through the same stream reducer ChatView uses
  • EmptyState.tsx — demo-local greeting that scrolls away with the transcript

Product changes (deliberate, small)

  • MessageList gains an optional header slot (renders nothing when unset — the real console is unchanged)
  • Message/FunctionTriggerCard gain optional defaultOpen props (off by default)
  • WaterfallChart expand controls become suppressible (showExpandControls, default true)
  • sub-millisecond call durations render as Nμs instead of 0ms in chat call cards
  • live trace timeline re-packs glide instead of teleporting (timeline-glide)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a standalone landing-page demo with replayable conversations, traces, waterfall views, approvals, callouts, and responsive layout.
    • Introduced demo build support and a dedicated HTML entrypoint with light/dark theme query handling.
    • Function-call cards/groups can render expanded by default; message lists can include an optional header.
  • Bug Fixes
    • Improved timeline stability with sticky re-packing and glide transitions; reduced-motion mode further limits timeline motion.
    • Waterfall expand/collapse controls can now be hidden.
  • Enhancements
    • Call durations are now formatted more readably with microsecond precision and edge-case handling.
  • Chores
    • Updated demo build output ignoring for generated dist-demo.

anthonyiscoding and others added 15 commits July 27, 2026 13:13
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>
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview Jul 30, 2026 2:29pm
workers-tech-spec Ready Ready Preview Jul 30, 2026 2:29pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 49 skipped (no docs/).

Layer Result
structure
vale
ai
render

Four for four. Nicely done.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@anthonyiscoding, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4f9e3c33-b0d3-4eb3-8012-e29482add265

📥 Commits

Reviewing files that changed from the base of the PR and between 8680622 and a804dd9.

📒 Files selected for processing (6)
  • console/web/src/demo/EmptyState.tsx
  • console/web/src/demo/LandingDemo.tsx
  • console/web/src/demo/demo.css
  • console/web/src/demo/main.tsx
  • console/web/src/demo/scenario.ts
  • console/web/src/demo/usePlayer.ts
📝 Walkthrough

Walkthrough

This 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.

Changes

Landing demo and console UI

Layer / File(s) Summary
Demo build and iframe entrypoint
console/web/.gitignore, console/web/demo.html, console/web/package.json, console/web/vite.demo.config.ts, console/web/src/demo/stubs/*, console/web/src/demo/main.tsx
Adds the demo build target, standalone entrypoint, local diff stubs, iframe-safe scrolling, host messaging, theme handling, replay, and ignored output.
Scripted scenario and playback state
console/web/src/demo/scenario.ts, console/web/src/demo/usePlayer.ts
Adds the payments-ledger scenario and event-driven player for streaming messages, child sessions, approval gates, spans, callouts, looping, and playback controls.
Landing surface and chat integration
console/web/src/demo/EmptyState.tsx, console/web/src/demo/LandingDemo.tsx, console/web/src/components/chat/Message.tsx, console/web/src/components/chat/MessageList.tsx, console/web/src/demo/demo.css
Adds the demo layout, transcript and trace tail-following, controls, callouts, composer, empty state, transcript headers, expanded function-call cards, and demo styling.
Trace layout stability and motion
console/web/src/pages/TracesV2/components/timeline/TraceTimeline.tsx, console/web/src/pages/TracesV2/components/WaterfallChart.tsx, console/web/src/index.css
Preserves compatible timeline offsets, adds glide transitions with reduced-motion support, and makes waterfall expansion controls configurable.
Human-readable call durations
console/web/src/lib/format-call-duration.ts, console/web/src/components/chat/FunctionTriggerGroup.tsx, console/web/src/components/function-trigger/FunctionTriggerCard.tsx
Formats duration values and uses the formatter in function-trigger displays.

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
Loading

Possibly related PRs

Poem

I’m a bunny with a build to hop,
A ledger tale that streams nonstop.
Traces glide and cards unfold,
Replay, pause—ears perk anew!
“Welcome to iii!” from me to you.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding a landing-page demo build of the console for the website.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add/landing-console-demo

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Divider renders orphaned if showExpandControls={false} is ever combined with a spanFilter.

The separator <div className="w-px h-4 bg-rule-2 mx-1" /> at line ~414 is gated only on spanFilter/group presence, not on showExpandControls. It's meant to separate the expand/collapse buttons from the filter menu, so if a future caller sets showExpandControls={false} while also supplying spanFilter + spanGroupKey, the divider shows with nothing to its left. Not triggered today since the only new caller (LandingDemo.tsx) passes neither spanFilter nor spanGroupKey, 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 win

Avoid mutating the ref during render; defer to useLayoutEffect.

stickyOffsets.current = layout.offsets runs unconditionally in the render body. React's own docs state renders should not write ref.current except 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. useLayoutEffect is 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 value

Make the gate precondition consistent.

fcall-start marks the card pendingApproval on gate alone, while the release below also requires functionTriggerId. A caller passing gate without an id would render a card that is pending forever and never awaited. Gate both on the same condition (or make functionTriggerId required when gate is 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 value

Consider 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 win

Consider 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, and 1500 would 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0cba488 and 492192b.

📒 Files selected for processing (18)
  • console/web/.gitignore
  • console/web/demo.html
  • console/web/package.json
  • console/web/src/components/chat/FunctionTriggerGroup.tsx
  • console/web/src/components/chat/Message.tsx
  • console/web/src/components/chat/MessageList.tsx
  • console/web/src/components/function-trigger/FunctionTriggerCard.tsx
  • console/web/src/demo/EmptyState.tsx
  • console/web/src/demo/LandingDemo.tsx
  • console/web/src/demo/main.tsx
  • console/web/src/demo/scenario.ts
  • console/web/src/demo/stubs/pierre-diffs.tsx
  • console/web/src/demo/usePlayer.ts
  • console/web/src/index.css
  • console/web/src/lib/format-call-duration.ts
  • console/web/src/pages/TracesV2/components/WaterfallChart.tsx
  • console/web/src/pages/TracesV2/components/timeline/TraceTimeline.tsx
  • console/web/vite.demo.config.ts

Comment thread console/web/src/components/chat/MessageList.tsx
Comment thread console/web/src/demo/main.tsx
Comment thread console/web/src/demo/usePlayer.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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 492192b and 9f4e99d.

📒 Files selected for processing (2)
  • console/web/src/demo/LandingDemo.tsx
  • console/web/src/demo/scenario.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • console/web/src/demo/scenario.ts

Comment thread console/web/src/demo/LandingDemo.tsx
Comment on lines +198 to +200
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 flex flex-col items-center">
<MobileNotice />
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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>
@anthonyiscoding anthonyiscoding changed the title feat(console): landing-page demo build of the console for the website (MOT-3554) feat(console): landing-page demo build of the console for the website Jul 29, 2026
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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Disable tail-follow whenever the pane is no longer near the bottom.

onScroll only re-enables following near the bottom; keyboard scrolling and scrollbar dragging never clear followRef. The next transcript update can therefore snap the user back to the newest content. Set followRef.current = fromBottom <= FOLLOW_SLACK_PX in 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

📥 Commits

Reviewing files that changed from the base of the PR and between 9f4e99d and 8680622.

📒 Files selected for processing (4)
  • console/web/src/components/chat/MessageList.tsx
  • console/web/src/demo/LandingDemo.tsx
  • console/web/src/demo/demo.css
  • console/web/src/demo/main.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • console/web/src/demo/main.tsx

Comment thread console/web/src/demo/demo.css Outdated
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant