From b90066a008107ff7a879243c34ad6a0a2853f296 Mon Sep 17 00:00:00 2001 From: Guilherme Rodrigues Date: Mon, 15 Jun 2026 14:39:48 -0300 Subject: [PATCH 01/37] feat(web): scripted full-screen product demo mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a public, fully-mocked walkthrough that drives the REAL Studio chat components from a recorded stream — no backend, MCP transport, or auth. Framework (apps/mesh/src/web/demo/): - Director: a non-React class that owns all timing via @decocms/std sleep and mutates external Stores; components subscribe via useSyncExternalStore, so no useEffect is needed to animate. Screenplays read as a storyboard (await d.user/stream/think/tool/parallel/endTurn) and support multiple parallel chat tracks plus org switching, previews, a ghost cursor, and a typed terminal. - Chat seam: DemoChatStreamProvider supplies a scripted ChatStreamContextValue (via an exported DemoChatStreamContext) so the real renderers paint it identically to a live stream. - DemoProviders: mock ProjectContext + a network-free QueryClient. Two demos, each on its own URL (/demo chooser, /demo/), light mode: - /demo/storefront — business user drops in a URL and gets an accurate perf+SEO diagnosis scorecard, a fix plan, parallel fixes, and a re-audit. - /demo/agents — Studio as a web Conductor: ghost cursor opens the connect- desktop modal, a mocked iTerm runs `bunx decocms link` then minimizes to "connected", then work runs across two orgs in parallel (chat + live preview) while switching context. The autoplay runner is guarded by a per-stores singleton with deferred teardown so the dev runtime's spurious effect re-runs can't restart a scenario mid-play. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/web/components/chat/chat-context.tsx | 7 + apps/mesh/src/web/demo/chrome.tsx | 65 ++++ apps/mesh/src/web/demo/demo-chat-stream.tsx | 55 +++ apps/mesh/src/web/demo/demo-providers.tsx | 57 +++ apps/mesh/src/web/demo/director-stores.ts | 103 ++++++ apps/mesh/src/web/demo/director.ts | 326 ++++++++++++++++++ apps/mesh/src/web/demo/ghost-cursor.tsx | 57 +++ apps/mesh/src/web/demo/link-flow.tsx | 130 +++++++ apps/mesh/src/web/demo/message-builders.ts | 127 +++++++ apps/mesh/src/web/demo/runner.ts | 39 +++ apps/mesh/src/web/demo/scenarios/agents.tsx | 305 ++++++++++++++++ apps/mesh/src/web/demo/scenarios/index.ts | 10 + .../src/web/demo/scenarios/storefront.tsx | 162 +++++++++ apps/mesh/src/web/demo/stage.tsx | 106 ++++++ apps/mesh/src/web/demo/types.ts | 16 + apps/mesh/src/web/demo/use-demo-stores.ts | 43 +++ apps/mesh/src/web/index.tsx | 16 + apps/mesh/src/web/routes/demo-scenario.tsx | 16 + apps/mesh/src/web/routes/demo.tsx | 38 ++ 19 files changed, 1678 insertions(+) create mode 100644 apps/mesh/src/web/demo/chrome.tsx create mode 100644 apps/mesh/src/web/demo/demo-chat-stream.tsx create mode 100644 apps/mesh/src/web/demo/demo-providers.tsx create mode 100644 apps/mesh/src/web/demo/director-stores.ts create mode 100644 apps/mesh/src/web/demo/director.ts create mode 100644 apps/mesh/src/web/demo/ghost-cursor.tsx create mode 100644 apps/mesh/src/web/demo/link-flow.tsx create mode 100644 apps/mesh/src/web/demo/message-builders.ts create mode 100644 apps/mesh/src/web/demo/runner.ts create mode 100644 apps/mesh/src/web/demo/scenarios/agents.tsx create mode 100644 apps/mesh/src/web/demo/scenarios/index.ts create mode 100644 apps/mesh/src/web/demo/scenarios/storefront.tsx create mode 100644 apps/mesh/src/web/demo/stage.tsx create mode 100644 apps/mesh/src/web/demo/types.ts create mode 100644 apps/mesh/src/web/demo/use-demo-stores.ts create mode 100644 apps/mesh/src/web/routes/demo-scenario.tsx create mode 100644 apps/mesh/src/web/routes/demo.tsx diff --git a/apps/mesh/src/web/components/chat/chat-context.tsx b/apps/mesh/src/web/components/chat/chat-context.tsx index dff6d91f20..57067ca276 100644 --- a/apps/mesh/src/web/components/chat/chat-context.tsx +++ b/apps/mesh/src/web/components/chat/chat-context.tsx @@ -328,6 +328,13 @@ interface TaskProviderInternals { // ============================================================================ const ChatStreamCtx = createContext(null); +/** + * Exposed so Demo Mode (`web/demo/`) can supply a scripted `ChatStreamContextValue` + * directly — feeding the real chat renderers a recorded stream without the live + * `ActiveTaskProvider` (MCP transport, SSE, etc.). Not for app use; prefer the + * hooks. See `web/demo/demo-chat-stream.tsx`. + */ +export { ChatStreamCtx as DemoChatStreamContext }; const ChatTaskCtx = createContext(null); const ChatPrefsCtx = createContext(null); diff --git a/apps/mesh/src/web/demo/chrome.tsx b/apps/mesh/src/web/demo/chrome.tsx new file mode 100644 index 0000000000..710cf10f1e --- /dev/null +++ b/apps/mesh/src/web/demo/chrome.tsx @@ -0,0 +1,65 @@ +/** + * Demo Mode — shared chrome for scenario stages. + * + * Lightweight product-like framing (top bar, preview pane) around the REAL + * chat. Pure presentational; all live content comes from the Director's stores. + */ + +export function DemoTopBar({ + org, + agent, + left, + right, +}: { + org: string; + agent: string; + left?: React.ReactNode; + right?: React.ReactNode; +}) { + return ( +
+
+ {org.slice(0, 1)} +
+
+ {org} + / + {agent} +
+ {left} +
{right}
+
+ ); +} + +/** A browser-like preview pane that renders demo HTML in an isolated iframe. */ +export function PreviewFrame({ url, html }: { url: string; html: string }) { + return ( +
+
+ + + + + +
+ {url} +
+
+
+ {html ? ( +