diff --git a/docs/screenshots/live-dream-progress/active.png b/docs/screenshots/live-dream-progress/active.png new file mode 100644 index 0000000..b29668b Binary files /dev/null and b/docs/screenshots/live-dream-progress/active.png differ diff --git a/docs/screenshots/live-dream-progress/idle.png b/docs/screenshots/live-dream-progress/idle.png new file mode 100644 index 0000000..bb037b1 Binary files /dev/null and b/docs/screenshots/live-dream-progress/idle.png differ diff --git a/docs/screenshots/live-dream-progress/overview.png b/docs/screenshots/live-dream-progress/overview.png new file mode 100644 index 0000000..744a658 Binary files /dev/null and b/docs/screenshots/live-dream-progress/overview.png differ diff --git a/docs/screenshots/live-dream-progress/stalled.png b/docs/screenshots/live-dream-progress/stalled.png new file mode 100644 index 0000000..26a4c6e Binary files /dev/null and b/docs/screenshots/live-dream-progress/stalled.png differ diff --git a/packages/web/scripts/screenshot-dream-progress.mjs b/packages/web/scripts/screenshot-dream-progress.mjs new file mode 100644 index 0000000..c20045c --- /dev/null +++ b/packages/web/scripts/screenshot-dream-progress.mjs @@ -0,0 +1,99 @@ +#!/usr/bin/env node +/** + * Capture documentation screenshots for the Dream Progress panel. + * + * The dev server must already be running at the URL passed in via PREVIEW_URL + * (defaults to http://localhost:5178). The /dream-progress showcase route is + * DEV-only and renders three variants of the panel against mock data. + * + * Usage: + * PREVIEW_URL=http://localhost:5178 OUT_DIR=../../docs/screenshots/live-dream-progress \ + * node scripts/screenshot-dream-progress.mjs + */ +import { mkdir } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { chromium } from "@playwright/test"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const PREVIEW_URL = process.env.PREVIEW_URL ?? "http://localhost:5178"; +const OUT_DIR = path.resolve( + __dirname, + process.env.OUT_DIR ?? "../../../docs/screenshots/live-dream-progress", +); + +async function main() { + await mkdir(OUT_DIR, { recursive: true }); + const browser = await chromium.launch(); + const context = await browser.newContext({ + viewport: { width: 1440, height: 900 }, + deviceScaleFactor: 2, + colorScheme: "dark", + }); + const page = await context.newPage(); + + // Seed localStorage with a fake instance so the root redirect doesn't kick + // us to the settings page. The showcase doesn't actually make any network + // requests — it renders against in-memory mock data. + await page.addInitScript(() => { + localStorage.setItem( + "openconcho:instances", + JSON.stringify({ + instances: [ + { + id: "inst_dev_demo", + name: "Demo (mock)", + baseUrl: "http://localhost:9999", + token: "", + }, + ], + activeId: "inst_dev_demo", + }), + ); + }); + + await page.goto(`${PREVIEW_URL}/dream-progress`, { waitUntil: "networkidle" }); + await page.waitForSelector('[data-testid="dream-progress-panel"]'); + // Let framer-motion entrance animations settle. + await page.waitForTimeout(600); + + // Full showcase — top-to-bottom view of all three variants. + await page.screenshot({ + path: path.join(OUT_DIR, "overview.png"), + fullPage: true, + }); + + // Variant: idle + { + const handle = await page.locator("section").nth(0); + await handle.scrollIntoViewIfNeeded(); + await page.waitForTimeout(150); + await handle.screenshot({ path: path.join(OUT_DIR, "idle.png") }); + } + + // Variant: active (with per-session breakdown) + { + const handle = await page.locator("section").nth(1); + await handle.scrollIntoViewIfNeeded(); + await page.waitForTimeout(150); + await handle.screenshot({ path: path.join(OUT_DIR, "active.png") }); + } + + // Variant: stalled (>30m without forward progress) + { + const handle = await page.locator("section").nth(2); + await handle.scrollIntoViewIfNeeded(); + await page.waitForTimeout(150); + await handle.screenshot({ path: path.join(OUT_DIR, "stalled.png") }); + } + + await browser.close(); + console.log(`Saved screenshots to ${OUT_DIR}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/web/src/components/workspaces/DreamProgressPanel.tsx b/packages/web/src/components/workspaces/DreamProgressPanel.tsx index 36712f4..e389014 100644 --- a/packages/web/src/components/workspaces/DreamProgressPanel.tsx +++ b/packages/web/src/components/workspaces/DreamProgressPanel.tsx @@ -144,9 +144,9 @@ export function DreamProgressPanel({ Stalled for {formatElapsed(stale.elapsedMs)} without forward progress - Work has been in-flight since{" "} - {new Date(stale.stalledSince).toLocaleTimeString()} with no advance in the - completed count. A specialist may be hung — check Honcho logs. + Work has been in-flight since {new Date(stale.stalledSince).toLocaleTimeString()}{" "} + with no advance in the completed count. A specialist may be hung — check Honcho + logs. @@ -197,9 +197,9 @@ export function DreamProgressPanel({ strokeWidth={2} /> - Honcho's /queue/status exposes aggregate counts - only. Per-dream observer/observed pair, specialist phase (deduction vs. induction), - and token telemetry are tracked upstream —{" "} + Honcho's /queue/status exposes aggregate counts only. + Per-dream observer/observed pair, specialist phase (deduction vs. induction), and token + telemetry are tracked upstream —{" "}
- + {entries.length} session{entries.length !== 1 ? "s" : ""} with active work @@ -281,10 +277,7 @@ function SessionsTable({ {entries.map(([sid, s], i) => ( - 0 ? "1px solid var(--border)" : undefined }} - > + 0 ? "1px solid var(--border)" : undefined }}> - + {s.total_work_units} - + {s.completed_work_units} - + {s.in_progress_work_units} - + {s.pending_work_units} diff --git a/packages/web/src/routeTree.gen.ts b/packages/web/src/routeTree.gen.ts index 934ef2b..528019a 100644 --- a/packages/web/src/routeTree.gen.ts +++ b/packages/web/src/routeTree.gen.ts @@ -14,8 +14,10 @@ import { Route as SettingsRouteImport } from './routes/settings' import { Route as ExploreRouteImport } from './routes/explore' import { Route as IndexRouteImport } from './routes/index' import { Route as WorkspacesWorkspaceIdRouteImport } from './routes/workspaces_.$workspaceId' +import { Route as DevDreamProgressRouteImport } from './routes/_dev.dream-progress' import { Route as WorkspacesWorkspaceIdWebhooksRouteImport } from './routes/workspaces_.$workspaceId_.webhooks' import { Route as WorkspacesWorkspaceIdSessionsRouteImport } from './routes/workspaces_.$workspaceId_.sessions' +import { Route as WorkspacesWorkspaceIdQueueRouteImport } from './routes/workspaces_.$workspaceId_.queue' import { Route as WorkspacesWorkspaceIdPeersRouteImport } from './routes/workspaces_.$workspaceId_.peers' import { Route as WorkspacesWorkspaceIdConclusionsRouteImport } from './routes/workspaces_.$workspaceId_.conclusions' import { Route as WorkspacesWorkspaceIdSessionsSessionIdRouteImport } from './routes/workspaces_.$workspaceId_.sessions_.$sessionId' @@ -47,6 +49,11 @@ const WorkspacesWorkspaceIdRoute = WorkspacesWorkspaceIdRouteImport.update({ path: '/workspaces/$workspaceId', getParentRoute: () => rootRouteImport, } as any) +const DevDreamProgressRoute = DevDreamProgressRouteImport.update({ + id: '/_dev/dream-progress', + path: '/dream-progress', + getParentRoute: () => rootRouteImport, +} as any) const WorkspacesWorkspaceIdWebhooksRoute = WorkspacesWorkspaceIdWebhooksRouteImport.update({ id: '/workspaces_/$workspaceId_/webhooks', @@ -59,6 +66,12 @@ const WorkspacesWorkspaceIdSessionsRoute = path: '/workspaces/$workspaceId/sessions', getParentRoute: () => rootRouteImport, } as any) +const WorkspacesWorkspaceIdQueueRoute = + WorkspacesWorkspaceIdQueueRouteImport.update({ + id: '/workspaces_/$workspaceId_/queue', + path: '/workspaces/$workspaceId/queue', + getParentRoute: () => rootRouteImport, + } as any) const WorkspacesWorkspaceIdPeersRoute = WorkspacesWorkspaceIdPeersRouteImport.update({ id: '/workspaces_/$workspaceId_/peers', @@ -95,9 +108,11 @@ export interface FileRoutesByFullPath { '/explore': typeof ExploreRoute '/settings': typeof SettingsRoute '/workspaces': typeof WorkspacesRoute + '/dream-progress': typeof DevDreamProgressRoute '/workspaces/$workspaceId': typeof WorkspacesWorkspaceIdRoute '/workspaces/$workspaceId/conclusions': typeof WorkspacesWorkspaceIdConclusionsRoute '/workspaces/$workspaceId/peers': typeof WorkspacesWorkspaceIdPeersRoute + '/workspaces/$workspaceId/queue': typeof WorkspacesWorkspaceIdQueueRoute '/workspaces/$workspaceId/sessions': typeof WorkspacesWorkspaceIdSessionsRoute '/workspaces/$workspaceId/webhooks': typeof WorkspacesWorkspaceIdWebhooksRoute '/workspaces/$workspaceId/peers/$peerId': typeof WorkspacesWorkspaceIdPeersPeerIdRoute @@ -109,9 +124,11 @@ export interface FileRoutesByTo { '/explore': typeof ExploreRoute '/settings': typeof SettingsRoute '/workspaces': typeof WorkspacesRoute + '/dream-progress': typeof DevDreamProgressRoute '/workspaces/$workspaceId': typeof WorkspacesWorkspaceIdRoute '/workspaces/$workspaceId/conclusions': typeof WorkspacesWorkspaceIdConclusionsRoute '/workspaces/$workspaceId/peers': typeof WorkspacesWorkspaceIdPeersRoute + '/workspaces/$workspaceId/queue': typeof WorkspacesWorkspaceIdQueueRoute '/workspaces/$workspaceId/sessions': typeof WorkspacesWorkspaceIdSessionsRoute '/workspaces/$workspaceId/webhooks': typeof WorkspacesWorkspaceIdWebhooksRoute '/workspaces/$workspaceId/peers/$peerId': typeof WorkspacesWorkspaceIdPeersPeerIdRoute @@ -124,9 +141,11 @@ export interface FileRoutesById { '/explore': typeof ExploreRoute '/settings': typeof SettingsRoute '/workspaces': typeof WorkspacesRoute + '/_dev/dream-progress': typeof DevDreamProgressRoute '/workspaces_/$workspaceId': typeof WorkspacesWorkspaceIdRoute '/workspaces_/$workspaceId_/conclusions': typeof WorkspacesWorkspaceIdConclusionsRoute '/workspaces_/$workspaceId_/peers': typeof WorkspacesWorkspaceIdPeersRoute + '/workspaces_/$workspaceId_/queue': typeof WorkspacesWorkspaceIdQueueRoute '/workspaces_/$workspaceId_/sessions': typeof WorkspacesWorkspaceIdSessionsRoute '/workspaces_/$workspaceId_/webhooks': typeof WorkspacesWorkspaceIdWebhooksRoute '/workspaces_/$workspaceId_/peers_/$peerId': typeof WorkspacesWorkspaceIdPeersPeerIdRoute @@ -140,9 +159,11 @@ export interface FileRouteTypes { | '/explore' | '/settings' | '/workspaces' + | '/dream-progress' | '/workspaces/$workspaceId' | '/workspaces/$workspaceId/conclusions' | '/workspaces/$workspaceId/peers' + | '/workspaces/$workspaceId/queue' | '/workspaces/$workspaceId/sessions' | '/workspaces/$workspaceId/webhooks' | '/workspaces/$workspaceId/peers/$peerId' @@ -154,9 +175,11 @@ export interface FileRouteTypes { | '/explore' | '/settings' | '/workspaces' + | '/dream-progress' | '/workspaces/$workspaceId' | '/workspaces/$workspaceId/conclusions' | '/workspaces/$workspaceId/peers' + | '/workspaces/$workspaceId/queue' | '/workspaces/$workspaceId/sessions' | '/workspaces/$workspaceId/webhooks' | '/workspaces/$workspaceId/peers/$peerId' @@ -168,9 +191,11 @@ export interface FileRouteTypes { | '/explore' | '/settings' | '/workspaces' + | '/_dev/dream-progress' | '/workspaces_/$workspaceId' | '/workspaces_/$workspaceId_/conclusions' | '/workspaces_/$workspaceId_/peers' + | '/workspaces_/$workspaceId_/queue' | '/workspaces_/$workspaceId_/sessions' | '/workspaces_/$workspaceId_/webhooks' | '/workspaces_/$workspaceId_/peers_/$peerId' @@ -183,9 +208,11 @@ export interface RootRouteChildren { ExploreRoute: typeof ExploreRoute SettingsRoute: typeof SettingsRoute WorkspacesRoute: typeof WorkspacesRoute + DevDreamProgressRoute: typeof DevDreamProgressRoute WorkspacesWorkspaceIdRoute: typeof WorkspacesWorkspaceIdRoute WorkspacesWorkspaceIdConclusionsRoute: typeof WorkspacesWorkspaceIdConclusionsRoute WorkspacesWorkspaceIdPeersRoute: typeof WorkspacesWorkspaceIdPeersRoute + WorkspacesWorkspaceIdQueueRoute: typeof WorkspacesWorkspaceIdQueueRoute WorkspacesWorkspaceIdSessionsRoute: typeof WorkspacesWorkspaceIdSessionsRoute WorkspacesWorkspaceIdWebhooksRoute: typeof WorkspacesWorkspaceIdWebhooksRoute WorkspacesWorkspaceIdPeersPeerIdRoute: typeof WorkspacesWorkspaceIdPeersPeerIdRoute @@ -230,6 +257,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof WorkspacesWorkspaceIdRouteImport parentRoute: typeof rootRouteImport } + '/_dev/dream-progress': { + id: '/_dev/dream-progress' + path: '/dream-progress' + fullPath: '/dream-progress' + preLoaderRoute: typeof DevDreamProgressRouteImport + parentRoute: typeof rootRouteImport + } '/workspaces_/$workspaceId_/webhooks': { id: '/workspaces_/$workspaceId_/webhooks' path: '/workspaces/$workspaceId/webhooks' @@ -244,6 +278,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof WorkspacesWorkspaceIdSessionsRouteImport parentRoute: typeof rootRouteImport } + '/workspaces_/$workspaceId_/queue': { + id: '/workspaces_/$workspaceId_/queue' + path: '/workspaces/$workspaceId/queue' + fullPath: '/workspaces/$workspaceId/queue' + preLoaderRoute: typeof WorkspacesWorkspaceIdQueueRouteImport + parentRoute: typeof rootRouteImport + } '/workspaces_/$workspaceId_/peers': { id: '/workspaces_/$workspaceId_/peers' path: '/workspaces/$workspaceId/peers' @@ -287,9 +328,11 @@ const rootRouteChildren: RootRouteChildren = { ExploreRoute: ExploreRoute, SettingsRoute: SettingsRoute, WorkspacesRoute: WorkspacesRoute, + DevDreamProgressRoute: DevDreamProgressRoute, WorkspacesWorkspaceIdRoute: WorkspacesWorkspaceIdRoute, WorkspacesWorkspaceIdConclusionsRoute: WorkspacesWorkspaceIdConclusionsRoute, WorkspacesWorkspaceIdPeersRoute: WorkspacesWorkspaceIdPeersRoute, + WorkspacesWorkspaceIdQueueRoute: WorkspacesWorkspaceIdQueueRoute, WorkspacesWorkspaceIdSessionsRoute: WorkspacesWorkspaceIdSessionsRoute, WorkspacesWorkspaceIdWebhooksRoute: WorkspacesWorkspaceIdWebhooksRoute, WorkspacesWorkspaceIdPeersPeerIdRoute: WorkspacesWorkspaceIdPeersPeerIdRoute, diff --git a/packages/web/src/routes/_dev.dream-progress.tsx b/packages/web/src/routes/_dev.dream-progress.tsx index 4a8c0e5..7bc4e05 100644 --- a/packages/web/src/routes/_dev.dream-progress.tsx +++ b/packages/web/src/routes/_dev.dream-progress.tsx @@ -86,12 +86,7 @@ function DreamProgressShowcase() { > Idle - +