Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f90d4fa
feat(web): landing-page demo of the console over a scripted turn
anthonyiscoding Jul 27, 2026
e03411f
feat(web): sidebar and a three-way sub-agent fan-out in the demo
anthonyiscoding Jul 27, 2026
04389d2
fix(web): report sub-millisecond calls in µs, and let prose be read
anthonyiscoding Jul 27, 2026
bbeb0de
fix(web): let the reader take the scrollbar, and stop replaying
anthonyiscoding Jul 27, 2026
f972501
fix(web): hold on results worth reading, drop expand-all mid-stream
anthonyiscoding Jul 27, 2026
a72721b
feat(web): let the demo's sub-agents work out loud
anthonyiscoding Jul 27, 2026
efebb9a
feat(demo): traces surface parity and embed-friendly scrolling
anthonyiscoding Jul 28, 2026
a078a2b
feat(demo): inverted close button in the window chrome when embedded
anthonyiscoding Jul 28, 2026
0ef53c6
feat(console): trace timeline re-packs glide and stick
anthonyiscoding Jul 28, 2026
055a33b
feat(demo): ?paused=1 mounts the demo paused for scroll-gated embeds
anthonyiscoding Jul 28, 2026
c3d3ef5
fix(demo): build with absolute /console-demo/ asset paths
anthonyiscoding Jul 28, 2026
fbe251a
feat(demo): persistent greeting, pause/replay controls, scenario copy
anthonyiscoding Jul 29, 2026
b8c9002
fix(demo): align scripted copy with the run's end summary
anthonyiscoding Jul 29, 2026
8155f2f
feat(demo): chat-only layout on small screens
anthonyiscoding Jul 29, 2026
492192b
feat(demo): simplified-view notice on phone widths
anthonyiscoding Jul 29, 2026
9f4e99d
feat(demo): composer types a clickable try-the-harness invitation
anthonyiscoding Jul 29, 2026
8f17c76
fix(web): restore MessageList's defaultOpenCalls plumbing
anthonyiscoding Jul 29, 2026
8680622
feat(demo): "try it for yourself" CTA centered in the window chrome
anthonyiscoding Jul 30, 2026
a804dd9
fix(demo): drop wait()'s abort listeners, retype the CTA on replay
anthonyiscoding Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions console/web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ storybook-static
*.log
.vite
*.tsbuildinfo
dist-demo
22 changes: 22 additions & 0 deletions console/web/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!doctype html>
<html lang="en" class="antialiased">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex" />
<title>iii console — recorded session</title>
<script>
/* Theme before first paint so the embed never flashes the wrong one. */
try {
const t = new URLSearchParams(location.search).get('theme');
document.documentElement.dataset.theme = t === 'dark' ? 'dark' : 'light';
} catch (_) {
document.documentElement.dataset.theme = 'light';
}
</script>
</head>
<body class="bg-bg text-ink font-sans">
<div id="root"></div>
<script type="module" src="/src/demo/main.tsx"></script>
</body>
</html>
3 changes: 2 additions & 1 deletion console/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"test:e2e": "playwright test",
"test:e2e:install": "playwright install chromium",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"build-storybook": "storybook build",
"build:demo": "vite build --config vite.demo.config.ts"
},
"dependencies": {
"@fontsource/chivo-mono": "^5.2.8",
Expand Down
3 changes: 2 additions & 1 deletion console/web/src/components/chat/FunctionTriggerGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from '@/components/function-trigger/FunctionTriggerCard'
import type { FilesystemAccessAction } from '@/components/permissions/FilesystemAccessPrompt'
import { StatusDot } from '@/components/ui/StatusDot'
import { formatCallDuration } from '@/lib/format-call-duration'
import { cn } from '@/lib/utils'
import type { FunctionTriggerMessage as FunctionTriggerMessageType } from '@/types/chat'

Expand Down Expand Up @@ -117,7 +118,7 @@ function deriveStatus(messages: FunctionTriggerMessageType[]): GroupStatus {
label: (
<>
<span className="tabular-nums">{total}</span> functions for{' '}
<span className="tabular-nums">{sum}</span>ms
<span className="tabular-nums">{formatCallDuration(sum)}</span>
</>
),
}
Expand Down
4 changes: 4 additions & 0 deletions console/web/src/components/chat/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ interface MessageProps {
/** Copy payload for an assistant turn (prose + its function calls). Lazy so
the string is built on click, not on every streaming re-render. */
copyText?: string | (() => string)
/** Render function-call cards already expanded (showcase surfaces). */
defaultOpenCalls?: boolean
}

export function Message({
Expand All @@ -48,6 +50,7 @@ export function Message({
onManageFilesystemAccess,
workingDir,
copyText,
defaultOpenCalls,
}: MessageProps) {
switch (message.role) {
case 'user':
Expand Down Expand Up @@ -89,6 +92,7 @@ export function Message({
return (
<FunctionTriggerCard
message={message}
defaultOpen={defaultOpenCalls}
onApprove={onApprove}
onDeny={onDeny}
onAlwaysAllow={onAlwaysAllowHandler}
Expand Down
19 changes: 17 additions & 2 deletions console/web/src/components/chat/MessageList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef } from 'react'
import { type ReactNode, useEffect, useMemo, useRef } from 'react'
import type { FilesystemAccessAction } from '@/components/permissions/FilesystemAccessPrompt'
import { useConversationsCtxOptional } from '@/lib/conversations-context'
import {
Expand All @@ -25,6 +25,10 @@ interface MessageListProps {
when absent. */
thinkingDetail?: string
density?: 'route' | 'dock'
/** Rendered at the top of the transcript scroller, above the messages, so
it scrolls away with them instead of unmounting. When set it replaces
the `EmptyState` on an empty transcript (landing demo). */
header?: ReactNode
Comment thread
coderabbitai[bot] marked this conversation as resolved.
onResolveApproval?: (
sessionId: string,
functionTriggerId: string,
Expand All @@ -42,6 +46,12 @@ interface MessageListProps {
) => Promise<void>
onManageFilesystemAccess?: () => void
workingDir?: string | null
/**
* Render every function-call card (and group) already expanded. Off in the
* product, where a turn's calls collapse to one line each; on for showcase
* surfaces whose whole point is the result renderers.
*/
defaultOpenCalls?: boolean
}

type RenderItem =
Expand Down Expand Up @@ -94,11 +104,13 @@ export function MessageList({
isThinking,
thinkingDetail,
density = 'route',
header,
onResolveApproval,
onAlwaysAllow,
onResolveFilesystemAccess,
onManageFilesystemAccess,
workingDir,
defaultOpenCalls,
}: MessageListProps) {
const bottomRef = useRef<HTMLDivElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -166,7 +178,7 @@ export function MessageList({
})
}, [messages])

if (messages.length === 0) {
if (messages.length === 0 && !header) {
return <EmptyState {...resolveEmptyState(ctx, density)} />
}

Expand All @@ -175,12 +187,14 @@ export function MessageList({
return (
<div ref={containerRef} className={cn('flex-1 overflow-y-auto', listPad)}>
<div className="mx-auto max-w-[760px] flex flex-col gap-y-8">
{header}
{items.map((item) => {
if (item.kind === 'fcall-group') {
return (
<FunctionTriggerGroup
key={item.key}
messages={item.messages}
defaultOpen={defaultOpenCalls}
onResolveApproval={onResolveApproval}
onAlwaysAllow={onAlwaysAllow}
onResolveFilesystemAccess={onResolveFilesystemAccess}
Expand All @@ -205,6 +219,7 @@ export function MessageList({
key={item.key}
message={m}
copyText={copyText}
defaultOpenCalls={defaultOpenCalls}
onResolveApproval={onResolveApproval}
onAlwaysAllow={onAlwaysAllow}
onResolveFilesystemAccess={onResolveFilesystemAccess}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Button } from '@/components/ui/Button'
import { StatusDot } from '@/components/ui/StatusDot'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/Tabs'
import { copyTextToClipboard } from '@/lib/clipboard'
import { formatCallDuration } from '@/lib/format-call-duration'
import { JsonHighlight } from '@/lib/syntax'
import { cn } from '@/lib/utils'
import type { FunctionTriggerMessage as FunctionTriggerMessageType } from '@/types/chat'
Expand Down Expand Up @@ -365,8 +366,10 @@ export function FunctionTriggerCard({
typeof message.durationMs === 'number' ? (
<span className="text-ink-faint">
{' '}
for <span className="tabular-nums">{message.durationMs}</span>
ms
for{' '}
<span className="tabular-nums">
{formatCallDuration(message.durationMs)}
</span>
</span>
) : null}
</span>
Expand Down
36 changes: 36 additions & 0 deletions console/web/src/demo/EmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Demo-local copy of the chat empty state's hero. The product's `EmptyState`
* keeps its own words; the landing demo wants this greeting, rendered inside
* the transcript scroller (via `MessageList`'s `header` slot) so the replay
* scrolls it away instead of unmounting it.
*/

import { Prompt } from '@/components/ui/Prompt'

export function DemoEmptyState() {
return (
<div className="flex max-w-[520px] flex-col gap-6">
<div className="font-mono text-[11px] uppercase tracking-[0.06em] text-ink-faint">
<Prompt symbol="$">new session</Prompt>
</div>
<h1 className="font-mono text-[28px] font-medium tracking-[-0.01em] text-ink lowercase">
welcome to iii! 👋
</h1>
<div className="flex flex-col gap-2">
<p className="font-mono text-[14px] leading-[1.7] text-ink-faint lowercase">
You're in a 1:1 reproduction of our agent-oriented workspace where you
can leverage the power of a full-featured agentic system:
</p>
<ul className="font-mono text-[13px] leading-[1.7] text-ink-faint flex flex-col gap-1">
<li>· trigger functions via the function registry</li>
<li>· spawn subagents for parallel/async work</li>
<li>· register triggers for loop and graph control</li>
<li>
· query state, publish to queues, and do everything production
systems do
</li>
</ul>
</div>
</div>
)
}
Loading
Loading