From 10c6ab5b7db7754cca6c981930335b92e8c72d5a Mon Sep 17 00:00:00 2001 From: bo Date: Thu, 30 Jul 2026 12:32:56 +0800 Subject: [PATCH 1/4] chore(design): organize design system artifacts --- AGENTS.md | 59 + .../MASTER.md | 8 +- .../pages/dashboard.md | 0 .../pages/session.md | 0 .../pages/todos.md | 0 .../prototypes}/app.js | 0 design-system/prototypes/dashboard.html | 849 +++++++ .../prototypes/session.html | 2 +- .../prototypes}/styles.css | 0 design-system/prototypes/todos.html | 849 +++++++ design/prototypes/signal-workbench/README.md | 20 - .../design-system/signal-workbench/README.md | 30 - design/tui-design.html | 1942 ----------------- 13 files changed, 1763 insertions(+), 1996 deletions(-) rename {design/prototypes/signal-workbench/design-system/signal-workbench => design-system}/MASTER.md (98%) rename {design/prototypes/signal-workbench/design-system/signal-workbench => design-system}/pages/dashboard.md (100%) rename {design/prototypes/signal-workbench/design-system/signal-workbench => design-system}/pages/session.md (100%) rename {design/prototypes/signal-workbench/design-system/signal-workbench => design-system}/pages/todos.md (100%) rename {design/prototypes/signal-workbench => design-system/prototypes}/app.js (100%) create mode 100644 design-system/prototypes/dashboard.html rename design/prototypes/signal-workbench/index.html => design-system/prototypes/session.html (99%) rename {design/prototypes/signal-workbench => design-system/prototypes}/styles.css (100%) create mode 100644 design-system/prototypes/todos.html delete mode 100644 design/prototypes/signal-workbench/README.md delete mode 100644 design/prototypes/signal-workbench/design-system/signal-workbench/README.md delete mode 100644 design/tui-design.html diff --git a/AGENTS.md b/AGENTS.md index 64e73d36..07440a23 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,65 @@ Validation order: `typecheck` → `test` (enforced by Turborepo task graph). Agent Core test lanes are hard-separated by naming: `*.integration.test.ts` owns real subprocess, Git/worktree, and LSP process lifecycles; `src/__arch__/**/*.test.ts` owns architecture contracts; all remaining `*.test.ts` files are unit tests. Do not use `test.concurrent`, `--concurrent`, test-runner retries, or retry-based flaky-test mitigation. +## UI/UX Pro Max Workflow + +Use the `ui-ux-pro-max` Skill for work that changes UI structure, visual +language, interaction, motion, responsive behavior, or accessibility. + +The only authoritative design artifact hierarchy is: + +```text +design-system/ +├── MASTER.md +├── pages/ +└── prototypes/ +``` + +Follow this order for every UI task: + +1. Read `design-system/MASTER.md`. It is the single global design baseline. +2. Read `design-system/pages/.md` when it exists. Page files contain only + explicit deviations from the Master and override it only for that page. +3. Inspect the current rendered product. Inspect the relevant current HTML + prototype under `design-system/prototypes/` when one exists. +4. UI/UX Pro Max is advisory, not authoritative. Use its searches as candidate + evidence and filter recommendations through ArchCode's established quiet + engineering workbench direction; never replace the Master with generic + landing-page, purple AI-SaaS, glassmorphism, or decorative-motion output. +5. Record an approved cross-page rule in `MASTER.md`; record a page-only + exception in `pages/.md`; then synchronize the current prototype and + product implementation as applicable. + +Create or update an HTML prototype only for a new complex module, a major +layout change, or an uncertain visual direction. Routine UI fixes and small +component changes do not require a prototype. + +Keep one current effective HTML prototype per page, named +`design-system/prototypes/.html`. Do not retain parallel `v2`, `final`, +`latest`, or dated variants. Keep only cross-page styles and behavior in +`design-system/prototypes/styles.css` and +`design-system/prototypes/app.js`. Put page-only CSS and JavaScript directly in +that page's HTML instead of creating page-specific `.css` or `.js` files. + +The current references are: + +- Dashboard: `design-system/prototypes/dashboard.html` +- Session: `design-system/prototypes/session.html` +- Todos: `design-system/prototypes/todos.html` + +When browser QA needs an HTTP origin, serve the prototype root without first +changing into that directory: + +```sh +python3 -m http.server 4181 \ + --bind 127.0.0.1 \ + --directory design-system/prototypes +``` + +Files under `docs/` are historical work records, not active design-system +artifacts. Preserve their original content and paths; an older prototype or +design reference there does not compete with the current prototype above. + ## Architecture ``` diff --git a/design/prototypes/signal-workbench/design-system/signal-workbench/MASTER.md b/design-system/MASTER.md similarity index 98% rename from design/prototypes/signal-workbench/design-system/signal-workbench/MASTER.md rename to design-system/MASTER.md index 38724924..49bd57c7 100644 --- a/design/prototypes/signal-workbench/design-system/signal-workbench/MASTER.md +++ b/design-system/MASTER.md @@ -1,12 +1,14 @@ # Signal Workbench Design System -> Persisted from the approved interactive prototype, last synchronized on +> Persisted from the approved interactive prototype, last content sync on > 2026-07-27. > > When designing or implementing a page, read this file first and then read > `pages/[page-name].md`. A page file overrides this Master only where it says -> so. The interactive prototype remains the rendered reference: -> [`../../index.html`](../../index.html). +> so. When a current prototype exists, use the matching rendered reference: +> [`dashboard.html`](prototypes/dashboard.html), +> [`session.html`](prototypes/session.html), or +> [`todos.html`](prototypes/todos.html). ## Product Fit diff --git a/design/prototypes/signal-workbench/design-system/signal-workbench/pages/dashboard.md b/design-system/pages/dashboard.md similarity index 100% rename from design/prototypes/signal-workbench/design-system/signal-workbench/pages/dashboard.md rename to design-system/pages/dashboard.md diff --git a/design/prototypes/signal-workbench/design-system/signal-workbench/pages/session.md b/design-system/pages/session.md similarity index 100% rename from design/prototypes/signal-workbench/design-system/signal-workbench/pages/session.md rename to design-system/pages/session.md diff --git a/design/prototypes/signal-workbench/design-system/signal-workbench/pages/todos.md b/design-system/pages/todos.md similarity index 100% rename from design/prototypes/signal-workbench/design-system/signal-workbench/pages/todos.md rename to design-system/pages/todos.md diff --git a/design/prototypes/signal-workbench/app.js b/design-system/prototypes/app.js similarity index 100% rename from design/prototypes/signal-workbench/app.js rename to design-system/prototypes/app.js diff --git a/design-system/prototypes/dashboard.html b/design-system/prototypes/dashboard.html new file mode 100644 index 00000000..9c3a634b --- /dev/null +++ b/design-system/prototypes/dashboard.html @@ -0,0 +1,849 @@ + + + + + + + ArchCode — Dashboard Design Prototype + + + + + + +
+ + + + +
+
+
+ + +
+
+ Runtime authority migration + Running +
+
+ /Users/bo/Developer/AI/archcode + + 12 tools · 42,806 tokens + + Todo · Runtime authority migration +
+
+
+ + +
+
+ +
+
+
+

Move all project-owned runtime state under .archcode/runtime. Make it a hard cut, and keep plans and skills directly editable.

+
+ +
+ +
+
+ + +
+ +
+

The runtime authority migration is complete.

+
    +
  • System-owned project state now lives under .archcode/runtime.
  • +
  • Plans and Skills remain directly editable outside the protected subtree.
  • +
  • Legacy reads and compatibility aliases were removed; the focused verification suite passes.
  • +
+
+
Lead · principal · just now
+
+ + + +
+
+ +
+

The implementation is already verified. I’m checking the user-facing explanation and asking an Analyst to challenge the final wording.

+
+ + delegate + Analyst · Review the boundary explanation +
+
+
+
+
+ +
+
+
+
!
+
+ Permission + Run documentation verification +
+
Tool
bash
+
Command
bun run docs:verify
+
+
+
+ + + +
+
+ +
+ G + Goal + Active + Move runtime authority without compatibility paths + 2 executions · 42,806 tokens · 14m + +
+ +
+ Queued +

After the tests, summarize the remaining migration risk.

+ GLM-5 · deep + +
+ +
+
+ +
+
+ + + Waiting for input +
+
+ Enter to queue + + +
+
+
+
+
+
+
+ + + + +
+ + +
+ +
+
+ + + + diff --git a/design/prototypes/signal-workbench/index.html b/design-system/prototypes/session.html similarity index 99% rename from design/prototypes/signal-workbench/index.html rename to design-system/prototypes/session.html index 2bf4c0cc..c6246b7c 100644 --- a/design/prototypes/signal-workbench/index.html +++ b/design-system/prototypes/session.html @@ -7,7 +7,7 @@ name="description" content="ArchCode Indigo Ink and Signal Lime visual direction prototype" /> - ArchCode — Signal Workbench Design Prototype + ArchCode — Session Design Prototype diff --git a/design/prototypes/signal-workbench/styles.css b/design-system/prototypes/styles.css similarity index 100% rename from design/prototypes/signal-workbench/styles.css rename to design-system/prototypes/styles.css diff --git a/design-system/prototypes/todos.html b/design-system/prototypes/todos.html new file mode 100644 index 00000000..996a9733 --- /dev/null +++ b/design-system/prototypes/todos.html @@ -0,0 +1,849 @@ + + + + + + + ArchCode — Todos Design Prototype + + + + + + +
+ + + + +
+
+
+ + +
+
+ Runtime authority migration + Running +
+
+ /Users/bo/Developer/AI/archcode + + 12 tools · 42,806 tokens + + Todo · Runtime authority migration +
+
+
+ + +
+
+ +
+
+
+

Move all project-owned runtime state under .archcode/runtime. Make it a hard cut, and keep plans and skills directly editable.

+
+ +
+ +
+
+ + +
+ +
+

The runtime authority migration is complete.

+
    +
  • System-owned project state now lives under .archcode/runtime.
  • +
  • Plans and Skills remain directly editable outside the protected subtree.
  • +
  • Legacy reads and compatibility aliases were removed; the focused verification suite passes.
  • +
+
+
Lead · principal · just now
+
+ + + +
+
+ +
+

The implementation is already verified. I’m checking the user-facing explanation and asking an Analyst to challenge the final wording.

+
+ + delegate + Analyst · Review the boundary explanation +
+
+
+
+
+ +
+
+
+
!
+
+ Permission + Run documentation verification +
+
Tool
bash
+
Command
bun run docs:verify
+
+
+
+ + + +
+
+ +
+ G + Goal + Active + Move runtime authority without compatibility paths + 2 executions · 42,806 tokens · 14m + +
+ +
+ Queued +

After the tests, summarize the remaining migration risk.

+ GLM-5 · deep + +
+ +
+
+ +
+
+ + + Waiting for input +
+
+ Enter to queue + + +
+
+
+
+
+
+
+ + + + +
+ + +
+ +
+
+ + + + diff --git a/design/prototypes/signal-workbench/README.md b/design/prototypes/signal-workbench/README.md deleted file mode 100644 index 38e92bb4..00000000 --- a/design/prototypes/signal-workbench/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Signal Workbench design prototype - -Interactive UI proposal for the ArchCode workbench. It includes: - -- Session workspace with live Work streams, collapsed completed Work, always-visible final responses, grouped read-only calls, independently expandable ToolCards, a compact HITL-first Composer Dock, and the Session context inspector -- Project Dashboard -- Project Todos: Board, Rejected, and Archived -- Todo detail drawer -- Light and dark themes -- Responsive, collapsible, resizable project navigation and context inspector - -The approved design system is persisted in -[`design-system/signal-workbench/`](design-system/signal-workbench/). Read its -`MASTER.md` and the relevant page override before implementing this direction. - -Open `index.html` directly, or serve this directory: - -```sh -python3 -m http.server 4181 --bind 127.0.0.1 -``` diff --git a/design/prototypes/signal-workbench/design-system/signal-workbench/README.md b/design/prototypes/signal-workbench/design-system/signal-workbench/README.md deleted file mode 100644 index ae3ea5d5..00000000 --- a/design/prototypes/signal-workbench/design-system/signal-workbench/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Signal Workbench Design-System Package - -This directory persists the approved Signal Workbench prototype as a reusable -Master + Page Overrides design system. - -## Reading Order - -1. Read [`MASTER.md`](MASTER.md). -2. Read the relevant page file: - - [`pages/session.md`](pages/session.md) - - [`pages/dashboard.md`](pages/dashboard.md) - - [`pages/todos.md`](pages/todos.md) -3. Use the interactive prototype for rendered behavior: - [`../../index.html`](../../index.html). - -Page files override the Master only where they explicitly differ. - -## Artifact Map - -| Artifact | Purpose | -|---|---| -| `MASTER.md` | Brand, tokens, typography, layout, components, motion, accessibility, forbidden patterns | -| `pages/session.md` | Conversation, Work/final-response hierarchy, tool expansion, inspector, composer | -| `pages/dashboard.md` | Attention/running/resumption/upcoming hierarchy | -| `pages/todos.md` | Board, Rejected, Archived, capture, and Todo detail drawer | -| `../../styles.css` | Exact prototype token and component implementation | -| `../../app.js` | Prototype interaction states | - -This package documents design intent. It does not authorize removing or -renaming existing product entities or changing backend behavior. diff --git a/design/tui-design.html b/design/tui-design.html deleted file mode 100644 index b0f08335..00000000 --- a/design/tui-design.html +++ /dev/null @@ -1,1942 +0,0 @@ - - - - - -Specra TUI Design Prototype - - - - -
- - -
- - · - claude-3.5-sonnet - · - - - main - - · - Refactor Auth to JWT - -
- 18.2k -
-
-
- 62% -
-
- - -
- - -
Step 1
- - -
-
You
-
帮我重构 auth 模块,把 session 方案换成 JWT。需要支持 token 刷新和黑名单机制
-
- - -
-
- - 💭 Thinking - 2.3s -
-
The user wants to refactor from session-based auth to JWT. Let me think about the architecture: - -1. Current session auth uses express-session with memory store -2. JWT approach needs: access token + refresh token pair -3. Token blacklist for logout/revocation - can use Redis -4. Need to check existing code structure first -5. Should maintain backward compatibility during migration - -Key considerations: -- Access token: short-lived (15min), stored in httpOnly cookie -- Refresh token: longer-lived (7d), stored in httpOnly cookie -- Blacklist: Redis SET with token jti claims -- Need middleware order: rate limit → cors → jwt verification → routes
-
- - -
-
Specra
-

好的,我来帮你把 session 认证方案重构为 JWT。方案如下:

-

• Access Token: 15分钟有效期,httpOnly cookie
• Refresh Token: 7天有效期,httpOnly cookie
• 黑名单: Redis SET 存储 token jti,用于登出/撤销

-

先看一下现有代码结构。

-
- - -
-
- - file_read - src/auth/session.ts - - 1.2s - -
-
-
- 1 import express from 'express';
- 2 import session from 'express-session';
- 3 import MemoryStore from 'memorystore';
- 4 
- 5 export function createSessionMiddleware() {
- 6   return session({
- 7     store: new MemoryStore({ checkPeriod: 86400000 }),
- 8     secret: process.env.SESSION_SECRET!,
-
-
-
- ▸ 14 more lines -
-
- - -
-
- - grep - "session" in src/auth/ - - 0.3s - -
-
-
-  src/auth/session.ts:5:  export function createSessionMiddleware() {
-  src/auth/session.ts:12:  export function destroySession() {
-  src/auth/routes.ts:3:   import { createSessionMiddleware } from './session';
-  src/auth/routes.ts:8:   router.use(createSessionMiddleware());
-  src/middleware/auth.ts:4: export function requireAuth(req, res, next) {
-
-
-
- - -
-
Specra
-

现有代码结构清楚了。我来创建 JWT 模块并修改相关文件。

-
- - -
-
- - file_write - src/auth/jwt.ts - - 0.8s - -
-
-
-   @@ -0,0 +1,42 @@
-+ import { jwtVerify, SignJWT } from 'jose';
-+ import type { JWTPayload } from 'jose';
-+ 
-+ interface TokenPair {
-+   accessToken: string;
-+   refreshToken: string;
-+ }
-+ 
-+ export async function generateTokenPair(
-+   userId: string
-+ ): Promise<TokenPair> {
-+   const accessToken = await new SignJWT({ sub: userId })
-+     .setProtectedHeader({ alg: 'HS256' })
-+     .setExpirationTime('15m')
-+     .sign(getSecret());
-+ 
-+   const refreshToken = await new SignJWT({ sub: userId })
-+     .setProtectedHeader({ alg: 'HS256' })
-+     .setExpirationTime('7d')
-+     .sign(getSecret());
-+ 
-+   return { accessToken, refreshToken };
-+ }
-
-
-
- ▾ 24 more lines -
-
- - -
-
- - file_edit - src/auth/routes.ts - - 0.6s - -
-
-
-   @@ -3,8 +3,12 @@
-- import { createSessionMiddleware } from './session';
-+ import { jwtMiddleware, refreshTokenMiddleware } from './jwt';
-+ import { blacklistMiddleware } from './blacklist';
-  
-   export const authRouter = Router();
-
-
-
- - -
-
- - bash - bun run typecheck - - 4.2s - -
-
-
-  $ bun run typecheck
-  
-  src/auth/jwt.ts(18,5): error TS2345: Argument of type 'string | undefined'
-    is not assignable to parameter of type 'KeyLike'.
-  
-  Found 1 error. Watching for file changes.
-
-
-
- - -
-
- - git_diff - --stat - - 0.1s - -
-
-
-   A src/auth/jwt.ts       | 42 ++++++++++++++++++++++
-   A src/auth/blacklist.ts | 28 ++++++++++++++
-   M src/auth/routes.ts   |  8 ++---
-   D src/auth/session.ts  | 22 -----------
-   4 files changed, 70 insertions(+), 30 deletions(-)
-
-
-
- - -
Step 2
- - -
-
- - delegate - test: write jwt integration tests - - 12s - -
-
-
-  Sub-agent running...
-  
-  Category: deep
-  Skills: git-master, review-work
-  
-  Writing integration tests for JWT module:
-  • token generation (access + refresh)
-  • token verification with expired tokens
-  • blacklist revocation flow
-  • refresh token rotation
-
-
-
- ▸ 3 more lines -
-
- - -
-
- - background_output - bg_a3f2k1 - - 0.1s - -
-
-
-   Task completed: Explore auth patterns
-  Session: ses_1e9f3bfb
-  Duration: 45s
-  
-  Found 3 implementations using jose for JWT
-  in express apps. Key patterns: httpOnly
-
-
-
- ▸ 8 more lines -
-
- - -
-
- - lsp_diagnostics - src/auth/ - - 0.2s - -
-
-
-  error src/auth/jwt.ts:18 — TS2345: Argument of type
-    'string | undefined' is not assignable to 'KeyLike'
-  warning src/auth/jwt.ts:24 — TS6133: 'exp'
-    is declared but never read
-
-
-
- - -
- 💬 - Sub-agent (explore) completed — results available via background_output -
- - -
- 💬 - Todo continuation: 3 items remaining -
- - -
-
- 📋 - Todos -
- -
- - Analyze existing session auth implementation -
-
- - Create JWT token generation module (src/auth/jwt.ts) -
-
- - Create token blacklist with Redis (src/auth/blacklist.ts) -
-
- - Update routes to use JWT middleware -
-
- - Write integration tests -
-
- - Migrate existing sessions (decided to skip) -
-
- - - - - -
-
- - bash - bun test src/auth/jwt.test.ts - - 6.8s - -
-
-
-  $ bun test src/auth/jwt.test.ts
-  
-  FAIL src/auth/jwt.test.ts > generateTokenPair
-    AssertionError: Expected "accessToken" to be a valid JWT
-  
-    at /src/auth/jwt.test.ts:24:5
-  
-  1 test failed | 3 passed | 2 skipped
-
-
-
- - -
- - Thinking -
- - -
-
Specra
-
-

我看到类型错误了 — `getSecret()` 返回 `string | undefined` 但 `SignJWT.sign()` 需要 `KeyLike`。我来修复这个问题,加上非空断言和类型转换。

-
-
- -
- - -
- INPUT - · - - - Running - - · - Steps 5/50 - · - $0.24 - - - - - Tab - Navigate - · - Ctrl+C - Interrupt - -
- - -
- - - Tab → back to input -
- - -
-
- - Permission Required -
-
-
-
Tool
-
file_write
-
-
-
Action
-
Create new file: src/auth/jwt.ts
-
-
-
Reason
-
Workspace guard — writing outside project root
-
-
-
   @@ -0,0 +1,3 @@
-+ import { jwtVerify, SignJWT } from 'jose';
-+ import type { JWTPayload } from 'jose';
-+ 
-
-
-
-
- [y] - Allow -
-
- [n] - Deny -
-
-
- - -
-
- - ask_user - [1/3] -
-
-
JWT secret 的管理方式?
-
- 1. -
-
环境变量
-
从 process.env.JWT_SECRET 读取,适合12-factor部署
-
-
-
- 2. -
-
密钥文件
-
从文件系统读取 PEM 密钥,适合 Kubernetes secrets
-
-
-
- 3. -
-
Vault 集成
-
从 HashiCorp Vault 动态获取,适合生产高安全场景
-
-
-
-
Enter number to select · Type custom answer · Esc to cancel
-
- -
- - - - - - - \ No newline at end of file From f6128438b2089fcee87a4ff4c3a5ad6e9ec41839 Mon Sep 17 00:00:00 2001 From: bo Date: Thu, 30 Jul 2026 13:53:08 +0800 Subject: [PATCH 2/4] chore(design): align prototypes with current workbench --- AGENTS.md | 22 +- design-system/MASTER.md | 170 +- design-system/pages/dashboard.md | 12 +- design-system/pages/session.md | 33 +- design-system/pages/todos.md | 39 +- design-system/prototypes/app.js | 535 +-- design-system/prototypes/dashboard.html | 1179 ++---- design-system/prototypes/session.html | 2234 +++++++---- design-system/prototypes/styles.css | 4765 +++-------------------- design-system/prototypes/todos.html | 1398 +++---- 10 files changed, 3199 insertions(+), 7188 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 07440a23..2f40d4b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,7 +62,7 @@ Agent Core test lanes are hard-separated by naming: `*.integration.test.ts` owns Use the `ui-ux-pro-max` Skill for work that changes UI structure, visual language, interaction, motion, responsive behavior, or accessibility. -The only authoritative design artifact hierarchy is: +The design-system working structure is: ```text design-system/ @@ -71,21 +71,33 @@ design-system/ └── prototypes/ ``` +`MASTER.md` and `pages/` are normative design specifications. +`prototypes/` contains supporting visual references, not authoritative +specifications. + Follow this order for every UI task: 1. Read `design-system/MASTER.md`. It is the single global design baseline. 2. Read `design-system/pages/.md` when it exists. Page files contain only explicit deviations from the Master and override it only for that page. -3. Inspect the current rendered product. Inspect the relevant current HTML - prototype under `design-system/prototypes/` when one exists. -4. UI/UX Pro Max is advisory, not authoritative. Use its searches as candidate +3. Inspect the current rendered product and implementation for established + product behavior, state semantics, and component interactions. +4. Inspect the relevant current HTML prototype under + `design-system/prototypes/` when one exists. Use it only as a supporting + rendered reference. +5. UI/UX Pro Max is advisory, not authoritative. Use its searches as candidate evidence and filter recommendations through ArchCode's established quiet engineering workbench direction; never replace the Master with generic landing-page, purple AI-SaaS, glassmorphism, or decorative-motion output. -5. Record an approved cross-page rule in `MASTER.md`; record a page-only +6. Record an approved cross-page rule in `MASTER.md`; record a page-only exception in `pages/.md`; then synchronize the current prototype and product implementation as applicable. +Resolve conflicts as follows: a page override modifies `MASTER.md` only for +that page; the current product is authoritative for existing behavior and state +mechanics; an HTML prototype never overrides `MASTER.md`, a page override, or +current product behavior. + Create or update an HTML prototype only for a new complex module, a major layout change, or an uncertain visual direction. Routine UI fixes and small component changes do not require a prototype. diff --git a/design-system/MASTER.md b/design-system/MASTER.md index 49bd57c7..af6ae043 100644 --- a/design-system/MASTER.md +++ b/design-system/MASTER.md @@ -1,11 +1,11 @@ # Signal Workbench Design System -> Persisted from the approved interactive prototype, last content sync on -> 2026-07-27. +> Synchronized from the current product UI and implementation on 2026-07-30. > > When designing or implementing a page, read this file first and then read > `pages/[page-name].md`. A page file overrides this Master only where it says -> so. When a current prototype exists, use the matching rendered reference: +> so. The current product UI is authoritative. When a current prototype exists, +> use it as a supporting rendered reference: > [`dashboard.html`](prototypes/dashboard.html), > [`session.html`](prototypes/session.html), or > [`todos.html`](prototypes/todos.html). @@ -66,39 +66,45 @@ Core visual principles: ## Color System -Use semantic tokens. Do not place raw colors in page-specific components. +Use the semantic tokens defined in `apps/web/src/styles/globals.css`. Do not +place raw colors in page-specific components or introduce a second token naming +scheme in prototypes. ### Light Theme | Token | Value | Role | |---|---|---| -| `--canvas` | `#f2f3f0` | Warm-neutral workspace background | -| `--surface` | `#f8f8f6` | Navigation, headers, large work surfaces | -| `--surface-raised` | `#ffffff` | Inputs, ToolCards, compact controls | -| `--surface-muted` | `#eceeea` | User messages and secondary fields | -| `--surface-hover` | `#e6e8e4` | Hover state | -| `--surface-active` | `#dde0da` | Pressed neutral state | -| `--border-soft` | `#e1e3df` | Internal separators | -| `--border` | `#cdd1ca` | Default boundary | +| `--bg-base` | `#f2f3f0` | Warm-neutral workspace background | +| `--bg-surface` | `#f8f8f6` | Navigation, headers, large work surfaces | +| `--bg-elevated` | `#ffffff` | Inputs, ToolCards, compact controls | +| `--bg-overlay` | `#ffffff` | Drawers, dialogs, and popovers | +| `--bg-muted` | `#eceeea` | User messages and secondary fields | +| `--bg-hover` | `#e6e8e4` | Hover state | +| `--bg-active` | `#dde0da` | Pressed neutral state | +| `--border-subtle` | `#e1e3df` | Internal separators | +| `--border-default` | `#cdd1ca` | Default boundary | | `--border-strong` | `#9fa69d` | Structural boundary | -| `--ink` | `#171917` | Primary text | -| `--ink-secondary` | `#424742` | Body and explanation text | -| `--ink-tertiary` | `#626a62` | Secondary metadata | -| `--ink-muted` | `#747c74` | De-emphasized metadata | +| `--control-border` | `var(--border-default)` | Form-control boundary | +| `--text-primary` | `#171917` | Primary text | +| `--text-secondary` | `#424742` | Body and explanation text | +| `--text-tertiary` | `#626a62` | Secondary metadata | +| `--text-muted` | `#747c74` | De-emphasized metadata | | `--brand` | `#6157d5` | Selection, primary action, active navigation | | `--brand-hover` | `#5148c1` | Primary action hover | | `--brand-field` | `#eeecfb` | Explicit brand-tinted field | | `--brand-ink` | `#ffffff` | Text on brand | | `--signal` | `#758b22` | Running/live indicator | -| `--signal-ink` | `#202807` | Text on signal | +| `--signal-ink` | `#151b00` | Text on signal | | `--signal-foreground` | `#506015` | Live text on neutral surfaces | | `--signal-field` | `#eef3d8` | Explicit running status field | | `--success` | `#2f7752` | Completed/success | | `--success-field` | `#e8f2eb` | Completed field | | `--warning` | `#8a5e13` | Attention/decision required | | `--warning-field` | `#f7eddd` | Explicit attention status field | -| `--danger` | `#b34d45` | Error/destructive/diff removal | -| `--danger-field` | `#f8e8e6` | Error/removal field | +| `--error` | `#b14840` | Error/destructive/diff removal | +| `--error-field` | `#f8e8e6` | Error/removal field | +| `--neutral` | `#626a62` | Neutral status | +| `--neutral-field` | `#eceeea` | Neutral status field | | `--selection-field` | `#eeedf8` | Quiet selected row | | `--running-field` | `#f1f4e7` | Quiet running row | | `--attention-field` | `#f8f2e8` | Quiet attention band | @@ -116,19 +122,21 @@ Use semantic tokens. Do not place raw colors in page-specific components. | Token | Value | Role | |---|---|---| -| `--canvas` | `#101210` | Graphite workspace background | -| `--surface` | `#151815` | Navigation, headers, large work surfaces | -| `--surface-raised` | `#1c201c` | Inputs, ToolCards, controls | -| `--surface-muted` | `#232723` | Secondary fields | -| `--surface-hover` | `#292e29` | Hover state | -| `--surface-active` | `#303630` | Pressed neutral state | -| `--border-soft` | `#262b26` | Internal separators | -| `--border` | `#363c36` | Default boundary | +| `--bg-base` | `#101210` | Graphite workspace background | +| `--bg-surface` | `#151815` | Navigation, headers, large work surfaces | +| `--bg-elevated` | `#1c201c` | Inputs, ToolCards, controls | +| `--bg-overlay` | `#1c201c` | Drawers, dialogs, and popovers | +| `--bg-muted` | `#232723` | Secondary fields | +| `--bg-hover` | `#292e29` | Hover state | +| `--bg-active` | `#303630` | Pressed neutral state | +| `--border-subtle` | `#262b26` | Internal separators | +| `--border-default` | `#363c36` | Default boundary | | `--border-strong` | `#596159` | Structural boundary | -| `--ink` | `#f3f5f0` | Primary text | -| `--ink-secondary` | `#c9cec6` | Body text | -| `--ink-tertiary` | `#9ca49a` | Secondary metadata | -| `--ink-muted` | `#868f84` | De-emphasized metadata | +| `--control-border` | `var(--border-default)` | Form-control boundary | +| `--text-primary` | `#f3f5f0` | Primary text | +| `--text-secondary` | `#c9cec6` | Body text | +| `--text-tertiary` | `#9ca49a` | Secondary metadata | +| `--text-muted` | `#868f84` | De-emphasized metadata | | `--brand` | `#a49bff` | Selection and primary action | | `--brand-hover` | `#b9b2ff` | Primary action hover | | `--brand-field` | `#2b2845` | Explicit brand-tinted field | @@ -141,8 +149,10 @@ Use semantic tokens. Do not place raw colors in page-specific components. | `--success-field` | `#203329` | Completed field | | `--warning` | `#deb96e` | Attention/decision required | | `--warning-field` | `#352b1b` | Explicit attention status field | -| `--danger` | `#f08b82` | Error/destructive/diff removal | -| `--danger-field` | `#3b2421` | Error/removal field | +| `--error` | `#f08b82` | Error/destructive/diff removal | +| `--error-field` | `#3b2421` | Error/removal field | +| `--neutral` | `#9ca49a` | Neutral status | +| `--neutral-field` | `#232723` | Neutral status field | | `--selection-field` | `#26243a` | Quiet selected row | | `--running-field` | `#22291b` | Quiet running row | | `--attention-field` | `#2c271e` | Quiet attention band | @@ -156,6 +166,14 @@ Use semantic tokens. Do not place raw colors in page-specific components. | `--terminal-error` | `#ed8178` | Failed Bash exit | | `--focus` | `0 0 0 3px rgb(164 155 255 / 26%)` | Focus ring | +Implementation aliases do not introduce new colors: + +- `--info` follows `--brand`; +- `--brand-subtle` and `--info-muted` follow `--brand-field`; +- `--success-muted`, `--warning-muted`, `--error-muted`, and + `--neutral-muted` follow their matching `*-field` token; +- `--terminal-border` is `rgb(255 255 255 / 10%)` in both themes. + ### Color Discipline - Indigo means selected, navigable, or user-triggered action. @@ -173,8 +191,8 @@ Use semantic tokens. Do not place raw colors in page-specific components. No network font dependency is required. ```css ---font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", sans-serif; ---font-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace; +--font-stack-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", sans-serif; +--font-stack-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace; ``` Type scale: @@ -186,9 +204,9 @@ Type scale: | Operational title | 13–14px | 600–680 | Navigation, tool targets, rows | | Commentary | 13–14px | 400–500 | Process explanation inside Work | | User message | 15px | 400–500 | User intent | -| Final response | 16px | 400–600 | Agent outcome and supporting detail | -| Session title | 20px | 700 | Active work identity | -| Page title | 24–26px | 700–720 | Dashboard and flat Todo views | +| Final response | 15px | 400–600 | Agent outcome and supporting detail | +| Session / Todos title | 20px | 600 | Active work or page identity | +| Dashboard title | 26px | 700 | Operational overview identity | Rules: @@ -197,7 +215,8 @@ Rules: structure use the available Session canvas. - Inline and operational code uses monospace. - Numeric timers and counters use tabular figures. -- Small text is metadata only; primary mobile input text remains at least 16px. +- Small text is metadata only. The Session Composer uses 16px input text on + narrow screens; compact operational inputs follow their page specification. ## Spacing, Radius, and Elevation @@ -205,16 +224,15 @@ Use a 2/4px-derived dense scale: `2, 4, 6, 8, 10, 12, 14, 18, 24, 30, 40px`. -Radius strategy: +Radius tokens: -| Radius | Usage | -|---:|---| -| 3–4px | Status badges, tight metadata fields | -| 5–6px | Tool children, rows, cards, page marks | -| 7–8px | Inputs, segmented controls, primary icon buttons | -| 8px | User message surface | -| 12px | Composer only | -| 50% / 999px | Status orbits, pulses, compact numeric counters only | +| Token | Value | Usage | +|---|---:|---| +| `--shape-control` | 4px | Buttons, inputs, tight metadata fields | +| `--shape-card` | 6px | Tool children, rows, and compact cards | +| `--shape-popover` | 8px | Menus, lane surfaces, and user messages | +| `--shape-dialog` | 12px | Composer and dialogs | +| circle / full | — | Status orbits, pulses, compact counters only | Do not make every surface a rounded card. Structural groups should prefer dividers, background changes, and inset rules. @@ -222,7 +240,9 @@ dividers, background changes, and inset rules. Elevation: - Ordinary rows and cards have no drop shadow. -- The compact Composer input surface uses one soft shadow inside its bottom dock. +- `--elevation-sm` is the compact Composer/input shadow: + `0 10px 30px rgb(26 31 25 / 9%)` in light mode and + `0 14px 34px rgb(0 0 0 / 32%)` in dark mode. - Inspectors, drawers, and off-canvas navigation use `0 22px 56px rgb(25 28 22 / 18%)` in light mode and `0 22px 56px rgb(0 0 0 / 52%)` in dark mode. @@ -234,11 +254,12 @@ Layer scale: |---|---:|---| | Base | 0 | Canvas, rails, ordinary content | | Composer dock | 4 | Session controls and input | -| Local scrim | 18 | Todo detail scrim | -| Local drawer | 19 | Todo detail | | Inspector | 30 | Responsive context inspector | -| Navigation | 40 | Responsive project navigation | -| Accessibility | 1000 | Skip link while focused | +| Desktop navigation / resize | 40 | Project rail and resize affordances | +| Mobile overlay | 50 | Mobile navigation and Inspector drawers | +| Mobile project rail | 55 | Stable rail above mobile overlays | +| Toast / Todo scrim | 60 | Attention toast and Todo detail scrim | +| Todo drawer | 61 | Todo detail surface | ## Workbench Layout @@ -252,7 +273,8 @@ Desktop shell defaults: - Context Inspector is resizable from 280–460px. - User-adjusted widths persist across visits. Collapse and focus mode never discard the last expanded width. -- Header height: 64px; the narrow Session header becomes 60px. +- Workbench headers use a 64px minimum height and expand when their two-line + content needs more room. - Conversation structure follows the flexible work canvas with safe horizontal gutters; prose inside Agent responses uses a 65–72ch reading measure and user messages remain capped at 660px. @@ -265,9 +287,9 @@ Responsive behavior: | Breakpoint | Behavior | |---|---| | `>1180px` | Four-region Session shell; inspector and sidebar may collapse | -| `761–1180px` | 52px rail + 228px sidebar + canvas; inspector becomes a right overlay | +| `761–1180px` | 52px rail + persisted/resizable sidebar + canvas; inspector becomes a right overlay below the 64px header | | `≤760px` | 48px rail + canvas; project sidebar and inspector become overlays | -| `≤620px` | Todo Board becomes one column; flat rows reflow actions below copy | +| `<700px` | Todo Board becomes one column | Narrow-screen rules: @@ -325,9 +347,9 @@ pulse, and terminal cursor may loop. ### Buttons -- Primary: indigo fill, 6–8px radius, 34px default height. -- Secondary: raised neutral surface, 1px border, 6px radius. -- Icon button: 34–38px visible control; expand the hit area to 44px on coarse +- Primary: indigo fill, 4px radius, 32px default height. +- Secondary: elevated neutral surface, 1px border, 4px radius. +- Icon button: 32–40px visible control; expand the hit area to 44px on coarse pointers. - Hover changes color, border, or surface only. No scale or vertical movement. - Each view has one visually dominant primary action. @@ -368,10 +390,10 @@ Execution is a mandatory product entity, not an optional visual section. - A running summary reads `Working · {duration}` and may append one current activity label after an em dash. - Do not show Execution number, model, message count, step count, Tool count, or - Child count in the visible Work row. Preserve Execution identity in semantics, - data, and the accessible name. -- The accessible disclosure name includes Execution number, terminal/live state, - elapsed duration, and current activity when present. + Child count in the visible Work row. Preserve Execution identity in product + data and stable DOM identity. +- The accessible disclosure name includes the Work segment, terminal/live + state, elapsed duration, and current activity when present. - A final Agent response is editorial content after Work and remains visible when Work is collapsed. Never place the final response inside the disclosure. - An Execution without final Agent text does not receive a fabricated empty @@ -441,8 +463,8 @@ Execution is a mandatory product entity, not an optional visual section. below 1181px. - Persistent desktop navigation and Inspector widths are user-resizable and restored after collapse or focus mode. -- Todo detail is a right drawer with objective, confirmed decisions, linked - work, and lifecycle actions. +- Todo detail is a right drawer with the Todo body, editing controls, linked + Sessions and Automations, and lifecycle actions. - Overlays use a scrim and a visible close action. ### Feedback and Loading @@ -471,14 +493,14 @@ Execution is a mandatory product entity, not an optional visual section. Motion explains state changes; it is not decoration. -| Interaction | Duration | -|---|---:| -| Hover/surface response | 140ms | -| Chevron expansion | 160ms | -| Theme color transition | 180ms | -| Drawer/sidebar movement | 220ms, `cubic-bezier(0.16, 1, 0.3, 1)` | -| Live pulse | 1.8s loop, running state only | -| Terminal cursor | 1.1s stepped loop | +| Token | Duration | Usage | +|---|---:|---| +| `--motion-hover` | 140ms | Hover and surface response | +| `--motion-icon` | 160ms | Chevron and icon state | +| `--motion-overlay` | 220ms | Drawer and overlay entry/exit | +| `--motion-complete` | 180ms | One-shot completion feedback | +| `--motion-attention` | 700ms | Bounded attention feedback | +| `--motion-activity` | 1.8s | Running activity only | - Do not add route-transition choreography or GSAP. - Do not animate layout width/height for disclosure; switch content and rotate @@ -496,7 +518,7 @@ Motion explains state changes; it is not decoration. - Status meaning always includes text or an icon in addition to color. - Toasts use `role="status"` and `aria-live="polite"`. - Preserve keyboard reading order when sidebars and inspectors become overlays. -- Mobile input text is 16px to avoid platform zoom. +- The mobile Session Composer uses 16px input text to avoid platform zoom. ## Forbidden Patterns diff --git a/design-system/pages/dashboard.md b/design-system/pages/dashboard.md index d360734a..031d3757 100644 --- a/design-system/pages/dashboard.md +++ b/design-system/pages/dashboard.md @@ -17,14 +17,16 @@ It is a decision and resumption surface, not an analytics report. ## Layout - Context Inspector is absent on this page. -- Keep the project rail and project navigation. +- Keep the project rail. The project-scoped Dashboard also keeps project + navigation; the global Dashboard does not add an empty project sidebar. - Main content width is at most 1180px. - Use 46px top, 40px horizontal, and 72px bottom padding on desktop. -- Pair Needs attention with Running now in one priority row. Pair Continue - working with a narrower Upcoming region below it. These are compositional - surface bands, not a KPI or Bento-card grid. +- At `≥1001px`, pair Needs attention with Running now in one priority row, then + pair Continue working with a narrower Upcoming region below it. At narrower + widths, all four regions stack in that order. These are compositional surface + bands, not KPI or Bento cards. - Keep 20px gaps between paired regions and 34px between major rows. -- At `≤760px`, use 18px horizontal padding and reflow row metadata below the +- At `≤760px`, use 16px horizontal padding and reflow row metadata below the main copy. ## Section Order diff --git a/design-system/pages/session.md b/design-system/pages/session.md index 5644f3cc..61e03c59 100644 --- a/design-system/pages/session.md +++ b/design-system/pages/session.md @@ -10,6 +10,24 @@ must let the user understand the objective, conversation, live Execution, tool activity, delegated Agents, changes, context, and next input without navigating away. +## Prototype Coverage + +- `../prototypes/session.html` is a representative state sample, not a literal + copy of one persisted Session. +- Synthetic content is allowed when it is needed to expose the current product + states, but every state must use the current component hierarchy, labels, + visual semantics, and interaction rules. +- Keep the sample compact while covering: completed and suspended Work, + Reasoning, grouped ordinary Tools, singleton mutation/Bash calls, a failed + Tool, Delegation, Recovery, Compaction, Permission, Ask User, + Queue/Steering, a visible final response, and Agent/Changes/Context inspector + states. +- The Permission and Ask User tabs in the prototype are a state-preview control + for reviewing both current HITL presentations. The product continues to show + only the active request family at one time. +- Do not replace this representative sample with whichever live Session happens + to contain the least content. + ## Layout - Use the complete workbench shell: @@ -61,7 +79,7 @@ header and conversation. Do not move Execution into the inspector. oversized chat bubbles. - Work commentary uses secondary 13–14px text. Tool names and Work summaries remain quieter than conversational content. -- The final Agent response is the strongest reading layer: 16px primary text, +- The final Agent response is the strongest reading layer: 15px primary text, 1.68 line-height, and a full-width structural surface. Its prose children use a 65–72ch reading measure while code, tables, Mermaid, and other technical blocks may use the available canvas. Its opening outcome may use 600 weight. @@ -85,8 +103,9 @@ running: user message → expanded Work reads `Working · {duration}` and may append `— {current activity}`. - Do not show `Execution {number}`, steps, Tool count, Child count, model, or binding metadata in the visible Work row. -- Preserve the Execution number in the disclosure's accessible name and product - data even though it is visually omitted. +- Preserve the Execution identity in product data even though it is visually + omitted. The accessible disclosure names the Work segment, state, duration, + and current activity when present. - Running Work is expanded by default. Historical completed Work is collapsed by default. - When a followed live Execution completes, collapse Work only if the user is @@ -107,7 +126,7 @@ running: user message → expanded Work coarse pointers. - Put the chevron first. Running Work adds one small live pulse before `Working`; completed Work needs no repeated success icon. -- The label is 12px/600. Duration uses tabular figures. +- The label is 13px/600. Duration uses tabular figures. - A running current-activity label is quiet, single-line, and separated with an em dash. Truncate it before expanding the Work row into multiple metadata lines. @@ -127,9 +146,9 @@ running: user message → expanded Work - A user-explicit Work state wins over automatic defaults for the current route lifetime. - Keyboard focus remains on the disclosure button after opening or closing. -- `aria-expanded`, `aria-controls`, and an accessible name containing the - Execution number, state, elapsed duration, and current activity when present - are required. +- `aria-expanded`, `aria-controls`, and an accessible name containing the Work + segment, state, elapsed duration, and current activity when present are + required. ## Work content diff --git a/design-system/pages/todos.md b/design-system/pages/todos.md index 4d1addbb..656c3416 100644 --- a/design-system/pages/todos.md +++ b/design-system/pages/todos.md @@ -33,18 +33,21 @@ Responsive columns: | Width | Columns | |---|---:| -| `>1180px` | 4 | -| `621–1180px` | 2 | -| `≤620px` | 1 | +| `≥1100px` | 4 | +| `700–1099px` | 2 | +| `<700px` | 1 | Lane rules: -- Lanes are layout columns, not large rounded containers. +- Lanes are structural columns with one 1px boundary, an 8px radius, and the + surface background. At four columns they use a 500px minimum height; stacked + lanes remain content-sized with a 160px minimum. - Lane headers use a status orbit, title, short explanation, and count. - Cards use one border, 6px radius, and no elevation. -- Card order is state → title → linked work/status → next action. -- Selection uses an indigo inset rule and border change. -- Running linked work uses lime only on the relevant status marker. +- Card order is state → title → optional body preview. Linked Sessions, + Automations, and lifecycle actions live in the detail drawer. +- Selection changes the card border to indigo. +- Lifecycle state uses its matching status icon plus text; color is secondary. ## Rejected Surface @@ -57,17 +60,17 @@ Lane rules: ## Archived Surface - Use the same flat-list structure as Rejected for visual continuity. -- Show origin/state and archived date. +- Show the Todo title and a quiet `Archived` state label. - Primary recovery is `Restore`. - Archived work remains recoverable but visually quiet. - Do not replace the list with a hidden archive menu. ## Quick Capture -- One horizontal input surface on desktop: - `plus icon → title input → New Todo`. -- At `≤620px`, the primary action moves below the input rather than shrinking - the title field. +- Use one horizontal input surface at every width: + `plus icon → title input → Add`. +- The input stays flexible; the 32px Add button remains visible and does not + imply that execution has started. - Capture creates intent only; it must not imply that execution has started. ## Todo Detail Drawer @@ -75,9 +78,8 @@ Lane rules: The drawer preserves: - title and lifecycle state; -- objective; -- confirmed decisions and unresolved decisions; -- linked Discussion, Session, or Automation; +- Todo body and editing controls; +- linked Discussions, work Sessions, and Automations; - lifecycle-appropriate primary and secondary actions. The entity and its actions are mandatory. Visual redesign may reorder or @@ -85,12 +87,9 @@ reweight actions, but must not silently remove them. Drawer behavior: -- maximum width 430px; -- right-side overlay with scrim and visible close action; -- at narrow widths, leave an 18px outer margin and start below the taller Todo - header; +- width is `min(430px, 100% - 18px)`; +- full-height right-side overlay with scrim and visible close action; - use thin section rules instead of nested cards; -- unresolved decisions use an amber marker plus text. ## Todos-Specific Avoidances diff --git a/design-system/prototypes/app.js b/design-system/prototypes/app.js index 0771d628..0d876ef7 100644 --- a/design-system/prototypes/app.js +++ b/design-system/prototypes/app.js @@ -1,488 +1,185 @@ -// ArchCode Signal Workbench design prototype interactions. -const iconPaths = { - arrow: "M5 12h14m-6-6 6 6-6 6", - automation: "M4 7h11m0 0-3-3m3 3-3 3M20 17H9m0 0 3-3m-3 3 3 3", +const icons = { + app: "M5 5h14v14H5zM9 9h6v6H9z", bell: "M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9ZM10 21h4", - changes: "M7 4v11m0 0-3-3m3 3 3-3M17 20V9m0 0-3 3m3-3 3 3", check: "m5 12 4 4L19 6", - chevron: "m8 10 4 4 4-4", - "chevron-right": "m9 6 6 6-6 6", + chevron: "m9 6 6 6-6 6", close: "M6 6l12 12M18 6 6 18", - collapse: "M4 5h16v14H4zM9 5v14m7-10-3 3 3 3", dashboard: "M4 4h6v6H4zm10 0h6v9h-6zM4 14h6v6H4zm10 3h6v3h-6z", - delegate: "M5 5h6v6H5zm8 8h6v6h-6zm-2-5h4v5", - edit: "m4 20 4.5-1 10-10-3.5-3.5-10 10zM13.5 7 17 10.5", - expand: "M4 5h16v14H4zM9 5v14m4 4 3-3-3-3", - file: "M6 3h8l4 4v14H6zM14 3v5h5", - focus: "M8 3H3v5m13-5h5v5M8 21H3v-5m13 5h5v-5", + grip: "M9 7h.01M15 7h.01M9 12h.01M15 12h.01M9 17h.01M15 17h.01", menu: "M4 7h16M4 12h16M4 17h16", moon: "M20 15.5A8.5 8.5 0 0 1 8.5 4 8.5 8.5 0 1 0 20 15.5Z", + more: "M6 12h.01M12 12h.01M18 12h.01", panel: "M4 4h16v16H4zM15 4v16", plus: "M12 5v14M5 12h14", - question: "M9.1 9a3 3 0 1 1 4.8 2.4c-1.2.8-1.9 1.4-1.9 2.6m0 4h.01M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Z", - review: "M12 3 5 6v5c0 4.6 2.9 8.3 7 10 4.1-1.7 7-5.4 7-10V6zM9 12l2 2 4-5", search: "m20 20-4.3-4.3M18 11a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z", - settings: "M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7ZM19 13.5l2 1-2 3-2-1.1a8 8 0 0 1-2 1.2L14.8 20h-3.6l-.2-2.4a8 8 0 0 1-2-1.2L7 17.5l-2-3 2-1a8 8 0 0 1 0-3L5 9.5l2-3 2 1.1a8 8 0 0 1 2-1.2l.2-2.4h3.6l.2 2.4a8 8 0 0 1 2 1.2L19 6.5l2 3-2 1a8 8 0 0 1 0 3Z", - sparkles: "m12 3 1.2 3.3L16.5 7.5l-3.3 1.2L12 12l-1.2-3.3-3.3-1.2 3.3-1.2ZM18 14l.8 2.2L21 17l-2.2.8L18 20l-.8-2.2L15 17l2.2-.8Z", - stop: "M7 7h10v10H7z", + settings: + "M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7ZM19 13.5l2 1-2 3-2-1.1a8 8 0 0 1-2 1.2L14.8 20h-3.6l-.2-2.4a8 8 0 0 1-2-1.2L7 17.5l-2-3 2-1a8 8 0 0 1 0-3L5 9.5l2-3 2 1.1a8 8 0 0 1 2-1.2l.2-2.4h3.6l.2 2.4a8 8 0 0 1 2 1.2L19 6.5l2 3-2 1a8 8 0 0 1 0 3Z", sun: "M12 4V2m0 20v-2M4 12H2m20 0h-2m-2.3-5.7 1.4-1.4M4.9 19.1l1.4-1.4m0-11.4L4.9 4.9m14.2 14.2-1.4-1.4M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z", - target: "M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Zm0-4a6 6 0 1 0 0-12 6 6 0 0 0 0 12Zm0-4a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z", - terminal: "m5 7 5 5-5 5m8 0h6", todo: "M8 6h12M8 12h12M8 18h12M3.5 6h.01M3.5 12h.01M3.5 18h.01", }; function icon(name) { - return ``; + return ``; } document.querySelectorAll("[data-icon]").forEach((element) => { - const iconName = element.dataset.icon; - if (iconPaths[iconName]) { - element.insertAdjacentHTML("afterbegin", icon(iconName)); - } + const name = element.dataset.icon; + if (icons[name]) element.innerHTML = icon(name); }); -const documentElement = document.documentElement; +const root = document.documentElement; const toast = document.querySelector(".toast"); -const inspector = document.querySelector("#changes-inspector"); -const sidebar = document.querySelector("#thread-sidebar"); -const workCanvas = document.querySelector("#work-canvas"); -const todosView = document.querySelector("[data-canvas-view='todos']"); -const focusModeButton = document.querySelector("[data-focus-mode]"); let toastTimer; -const layoutDimensions = { - sidebar: { - variable: "--sidebar-width", - storageKey: "signal-workbench-sidebar-width", - min: 210, - max: 340, - fallback: 248, - direction: 1, - handle: document.querySelector("[data-resize-sidebar]"), - }, - inspector: { - variable: "--inspector-width", - storageKey: "signal-workbench-inspector-width", - min: 280, - max: 460, - fallback: 330, - direction: -1, - handle: document.querySelector("[data-resize-inspector]"), - }, -}; - -function clamp(value, min, max) { - return Math.min(max, Math.max(min, value)); -} - -function readStoredWidth(config) { - try { - const stored = Number(window.localStorage.getItem(config.storageKey)); - return Number.isFinite(stored) && stored > 0 ? stored : config.fallback; - } catch { - return config.fallback; - } -} - -function setLayoutWidth(config, value, persist = true) { - const next = Math.round(clamp(value, config.min, config.max)); - document.documentElement.style.setProperty(config.variable, `${next}px`); - config.handle.setAttribute("aria-valuenow", String(next)); - config.handle.setAttribute("aria-valuetext", `${next} pixels`); - if (persist) { - try { - window.localStorage.setItem(config.storageKey, String(next)); - } catch { - // Persistence is optional when the prototype is opened from a restricted origin. - } +function setTheme(theme, persist = true) { + root.dataset.theme = theme; + const toggle = document.querySelector("[data-theme-toggle]"); + if (toggle) { + const next = theme === "dark" ? "light" : "dark"; + toggle.setAttribute("aria-label", `Switch to ${next} theme`); + toggle.setAttribute("title", `Switch to ${next} theme`); + toggle.innerHTML = icon(theme === "dark" ? "sun" : "moon"); } - return next; + if (persist) localStorage.setItem("archcode-prototype-theme", theme); } -function bindLayoutResize(config) { - setLayoutWidth(config, readStoredWidth(config), false); - - config.handle.addEventListener("pointerdown", (event) => { - if (event.button !== 0) return; - event.preventDefault(); - const startX = event.clientX; - const startWidth = Number(config.handle.getAttribute("aria-valuenow")); - config.handle.classList.add("dragging"); - - const onMove = (moveEvent) => { - const delta = (moveEvent.clientX - startX) * config.direction; - setLayoutWidth(config, startWidth + delta, false); - }; - const onEnd = () => { - const finalWidth = Number(config.handle.getAttribute("aria-valuenow")); - setLayoutWidth(config, finalWidth, true); - config.handle.classList.remove("dragging"); - window.removeEventListener("pointermove", onMove); - window.removeEventListener("pointerup", onEnd); - window.removeEventListener("pointercancel", onEnd); - }; - - window.addEventListener("pointermove", onMove); - window.addEventListener("pointerup", onEnd); - window.addEventListener("pointercancel", onEnd); - }); - - config.handle.addEventListener("keydown", (event) => { - if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return; - event.preventDefault(); - const current = Number(config.handle.getAttribute("aria-valuenow")); - const movement = event.key === "ArrowRight" ? 12 : -12; - setLayoutWidth(config, current + movement * config.direction); - }); -} +setTheme(localStorage.getItem("archcode-prototype-theme") || root.dataset.theme || "dark", false); -Object.values(layoutDimensions).forEach(bindLayoutResize); +document.querySelector("[data-theme-toggle]")?.addEventListener("click", () => { + setTheme(root.dataset.theme === "dark" ? "light" : "dark"); +}); function showToast(message) { + if (!toast) return; toast.textContent = `${message} · 原型演示`; toast.classList.add("visible"); - window.clearTimeout(toastTimer); - toastTimer = window.setTimeout(() => toast.classList.remove("visible"), 2400); -} - -function setTheme(theme) { - documentElement.dataset.theme = theme; - const toggle = document.querySelector("[data-theme-toggle]"); - const nextTheme = theme === "light" ? "dark" : "light"; - toggle.setAttribute("aria-label", `Switch to ${nextTheme} theme`); - toggle.setAttribute("title", `Switch to ${nextTheme} theme`); - toggle.innerHTML = icon(theme === "light" ? "moon" : "sun"); -} - -function closeOverlays() { - inspector.classList.remove("open"); - sidebar.classList.remove("open"); - document.body.classList.remove("inspector-open", "sidebar-open"); + clearTimeout(toastTimer); + toastTimer = setTimeout(() => toast.classList.remove("visible"), 2200); } -function closeTodoDetail() { - todosView.classList.remove("detail-open"); - document.querySelectorAll("[data-todo-card]").forEach((card) => card.classList.remove("selected")); -} +document.querySelectorAll("[data-action]").forEach((element) => { + element.addEventListener("click", () => showToast(element.dataset.action)); +}); -function setFocusMode(active) { - document.body.classList.toggle("focus-mode", active); - focusModeButton.setAttribute("aria-pressed", String(active)); - focusModeButton.setAttribute("aria-label", active ? "Exit focus mode" : "Enter focus mode"); -} +const sidebar = document.querySelector(".project-sidebar"); +const inspector = document.querySelector(".context-inspector"); -function showCanvas(view) { - document.querySelectorAll("[data-canvas-view]").forEach((canvasView) => { - const active = canvasView.dataset.canvasView === view; - canvasView.hidden = !active; - canvasView.classList.toggle("active", active); - }); - document.querySelectorAll("[data-canvas-target]").forEach((button) => { - button.classList.toggle("active", button.dataset.canvasTarget === view); - }); - document.body.classList.toggle("non-session-view", view !== "session"); - closeOverlays(); - closeTodoDetail(); - workCanvas.focus({ preventScroll: true }); +function closeOverlays() { + document.body.classList.remove("sidebar-open", "inspector-open"); } -document.querySelector("[data-theme-toggle]").addEventListener("click", () => { - setTheme(documentElement.dataset.theme === "light" ? "dark" : "light"); -}); - -document.querySelector("[data-open-inspector]").addEventListener("click", () => { - if (window.matchMedia("(min-width: 1181px)").matches) { - if (document.body.classList.contains("focus-mode")) { - setFocusMode(false); - document.body.classList.remove("inspector-collapsed"); - document.querySelector("[data-open-inspector]").setAttribute("aria-expanded", "true"); - return; - } - const collapsed = document.body.classList.toggle("inspector-collapsed"); - document.querySelector("[data-open-inspector]").setAttribute("aria-expanded", String(!collapsed)); - return; - } - inspector.classList.add("open"); - document.body.classList.add("inspector-open"); -}); - -document.querySelector("[data-close-inspector]").addEventListener("click", closeOverlays); document.querySelectorAll("[data-open-sidebar]").forEach((button) => { button.addEventListener("click", () => { - sidebar.classList.add("open"); - document.body.classList.add("sidebar-open"); - }); -}); -document.querySelector("[data-close-sidebar]").addEventListener("click", closeOverlays); -document.querySelector("[data-close-overlays]").addEventListener("click", closeOverlays); -document.querySelector("[data-collapse-sidebar]").addEventListener("click", () => { - document.body.classList.toggle("sidebar-collapsed"); -}); -focusModeButton.addEventListener("click", () => { - const active = !document.body.classList.contains("focus-mode"); - setFocusMode(active); - closeOverlays(); - showToast(active ? "已进入专注模式" : "已退出专注模式"); -}); -document.querySelectorAll("[data-expand-sidebar]").forEach((button) => { - button.addEventListener("click", () => { - if (document.body.classList.contains("focus-mode")) { - setFocusMode(false); + if (window.matchMedia("(min-width: 761px)").matches) { + const collapsed = document.body.classList.toggle("sidebar-collapsed"); + button.setAttribute("aria-expanded", String(!collapsed)); return; } - document.body.classList.remove("sidebar-collapsed"); + closeOverlays(); + document.body.classList.add("sidebar-open"); }); }); -document.querySelectorAll("[data-canvas-target]").forEach((button) => { +document.querySelectorAll("[data-open-inspector]").forEach((button) => { button.addEventListener("click", () => { - showCanvas(button.dataset.canvasTarget); - }); -}); - -document.querySelectorAll("[data-action]").forEach((button) => { - button.addEventListener("click", () => showToast(button.dataset.action)); -}); - -function setCurrentThread(nextThread) { - document.querySelectorAll("[data-thread]").forEach((thread) => { - const current = thread === nextThread; - thread.classList.toggle("active", current); - if (current) { - thread.setAttribute("aria-current", "page"); - } else { - thread.removeAttribute("aria-current"); + if (window.matchMedia("(min-width: 1181px)").matches) { + const collapsed = document.body.classList.toggle("inspector-collapsed"); + button.setAttribute("aria-expanded", String(!collapsed)); + return; } - }); -} - -document.querySelectorAll("[data-thread]").forEach((button) => { - button.addEventListener("click", () => { - setCurrentThread(button); - showCanvas("session"); - showToast(`切换到 ${button.querySelector("strong").textContent}`); + closeOverlays(); + document.body.classList.add("inspector-open"); }); }); -document.querySelectorAll("[data-thread-jump]").forEach((button) => { - button.addEventListener("click", () => { - const target = document.querySelector(`[data-thread="${button.dataset.threadJump}"]`); - if (target) setCurrentThread(target); - showCanvas("session"); - showToast(`切换到 ${target?.querySelector("strong")?.textContent ?? "Session"}`); - }); +document.querySelectorAll("[data-close-overlay]").forEach((button) => { + button.addEventListener("click", closeOverlays); }); -document.querySelectorAll("[data-file]").forEach((button) => { - button.addEventListener("click", () => { - document.querySelectorAll("[data-file]").forEach((file) => file.classList.remove("active")); - button.classList.add("active"); - const name = button.querySelector("strong").textContent; - showToast(`在主画布打开 ${name} 的 Diff`); - }); -}); +document.querySelector(".scrim")?.addEventListener("click", closeOverlays); -function bindTabs(tabSelector, panelSelector, tabKey, panelKey) { - document.querySelectorAll(tabSelector).forEach((button) => { - button.addEventListener("click", () => { - document.querySelectorAll(tabSelector).forEach((tab) => tab.setAttribute("aria-selected", "false")); - document.querySelectorAll(panelSelector).forEach((panel) => { - panel.hidden = panel.dataset[panelKey] !== button.dataset[tabKey]; - panel.classList.toggle("active", !panel.hidden); - }); - button.setAttribute("aria-selected", "true"); - }); - }); -} - -bindTabs("[data-sidebar-tab]", "[data-sidebar-panel]", "sidebarTab", "sidebarPanel"); -bindTabs("[data-inspector-tab]", "[data-inspector-panel]", "inspectorTab", "inspectorPanel"); - -document.querySelectorAll("[data-work-toggle]").forEach((button) => { +document.querySelectorAll("[data-sidebar-tab]").forEach((button) => { button.addEventListener("click", () => { - const expanded = button.getAttribute("aria-expanded") === "true"; - const body = document.getElementById(button.getAttribute("aria-controls")); - const scroller = button.closest(".conversation"); - const anchorTop = button.getBoundingClientRect().top; - - button.setAttribute("aria-expanded", String(!expanded)); - body.hidden = expanded; - - window.requestAnimationFrame(() => { - if (!scroller) return; - const anchorDelta = button.getBoundingClientRect().top - anchorTop; - scroller.scrollTop += anchorDelta; + const target = button.dataset.sidebarTab; + document.querySelectorAll("[data-sidebar-tab]").forEach((tab) => { + tab.setAttribute("aria-selected", String(tab === button)); + }); + document.querySelectorAll("[data-sidebar-panel]").forEach((panel) => { + panel.hidden = panel.dataset.sidebarPanel !== target; }); }); }); -document.querySelectorAll("[data-tool-run-toggle]").forEach((button) => { - button.addEventListener("click", () => { - const expanded = button.getAttribute("aria-expanded") === "true"; - button.setAttribute("aria-expanded", String(!expanded)); - button.closest(".tool-run-card").querySelector("[data-tool-run-list]").hidden = expanded; - }); -}); - -document.querySelectorAll("[data-reasoning-toggle]").forEach((button) => { - button.addEventListener("click", () => { - const expanded = button.getAttribute("aria-expanded") === "true"; - button.setAttribute("aria-expanded", String(!expanded)); - document.getElementById(button.getAttribute("aria-controls")).hidden = expanded; - }); -}); - -document.querySelectorAll("[data-tool-call-toggle]").forEach((button) => { - button.addEventListener("click", () => { - const expanded = button.getAttribute("aria-expanded") === "true"; - button.setAttribute("aria-expanded", String(!expanded)); - button.closest(".tool-call-card").querySelector("[data-tool-call-detail]").hidden = expanded; - }); -}); - -document.querySelector("#composer").addEventListener("submit", (event) => { - event.preventDefault(); - const input = document.querySelector("#follow-up"); - showToast(input.value.trim() ? "消息已加入队列" : "输入一条消息后加入队列"); - if (input.value.trim()) input.value = ""; -}); - -const todoDetails = { - "visual-qa": { - title: "Visual QA for the workbench", - state: "Idea · Discussion waiting for you", - objective: "Define the visual acceptance surface before handing implementation to a new Lead Session.", - link: "Discussion Session · waiting for your response", - primary: "Continue Discussion", - secondary: ["Edit", "Mark Ready", "Reject", "Archive"], - }, - "output-boundary": { - title: "Finalize tool output boundary", - state: "Ready · no linked work yet", - objective: "Complete the remaining implementation and verify that raw output is finalized exactly once.", - link: "No linked work yet", - primary: "Start Session", - secondary: ["Edit", "Discuss", "Create Automation", "Move to Idea", "Reject", "Mark Done", "Archive"], - }, - runtime: { - title: "Move runtime authority", - state: "In Progress · Session running", - objective: "Move project-owned runtime state into one protected authority subtree with no dual reads.", - link: "Runtime authority migration · Lead · running 14:32", - primary: "Open Session", - secondary: ["Edit", "Discuss", "Return to Ready", "Mark Done"], - }, - regression: { - title: "Nightly regression review", - state: "In Progress · Automation active", - objective: "Run a scheduled review over newly changed paths and surface only actionable regressions.", - link: "Nightly regression review · next run tomorrow at 02:00", - primary: "Open Automation", - secondary: ["Edit", "Discuss", "Return to Ready", "Mark Done"], - }, - model: { - title: "Model runtime snapshots", - state: "Done · completed Tuesday", - objective: "Make resolved model identity immutable for each execution and preserve the audit trail.", - link: "Completed Session · no active execution", - primary: "Reopen", - secondary: ["Edit", "Archive"], - }, - "legacy-aliases": { - title: "Keep compatibility aliases for runtime paths", - state: "Rejected · reason preserved", - objective: "Retain legacy runtime paths alongside the new authority tree.", - link: "Discussion Session · completed", - primary: "Restore to Idea", - secondary: ["Edit", "Discuss", "Archive"], - }, - "dashboard-metrics": { - title: "Add velocity metrics to Project Dashboard", - state: "Rejected · reason preserved", - objective: "Add task throughput and activity counters to the project overview.", - link: "No linked work", - primary: "Restore to Idea", - secondary: ["Edit", "Discuss", "Archive"], - }, - "old-provider-audit": { - title: "Audit retired provider configuration", - state: "Archived · formerly Done", - objective: "Review the configuration surface removed by the provider hard cut.", - link: "Completed Session · June 18", - primary: "Restore", - secondary: ["Edit"], +const resizeConfigs = [ + { + handle: document.querySelector("[data-resize-sidebar]"), + variable: "--sidebar-width", + key: "archcode-prototype-sidebar-width", + min: 210, + max: 340, + fallback: 264, + direction: 1, }, - "memory-notes": { - title: "Collect memory extraction notes", - state: "Archived · formerly Done", - objective: "Consolidate implementation notes from the memory extraction work.", - link: "Completed Session · May 29", - primary: "Restore", - secondary: ["Edit"], + { + handle: document.querySelector("[data-resize-inspector]"), + variable: "--inspector-width", + key: "archcode-prototype-inspector-width", + min: 280, + max: 460, + fallback: 312, + direction: -1, }, -}; +]; -function openTodoDetail(card) { - const detail = todoDetails[card.dataset.todoCard]; - if (!detail) return; - document.querySelectorAll("[data-todo-card]").forEach((item) => item.classList.toggle("selected", item === card)); - document.querySelector("[data-todo-detail-title]").textContent = detail.title; - document.querySelector("[data-todo-detail-state]").textContent = detail.state; - document.querySelector("[data-todo-detail-objective]").textContent = detail.objective; - document.querySelector("[data-todo-detail-link]").textContent = detail.link; - const primary = document.querySelector("[data-todo-detail-primary]"); - primary.textContent = detail.primary; - const secondary = document.querySelector("[data-todo-detail-secondary]"); - secondary.replaceChildren(); - detail.secondary.forEach((label) => { - const button = document.createElement("button"); - button.type = "button"; - button.className = "secondary-button"; - button.textContent = label; - button.addEventListener("click", () => showToast(label)); - secondary.appendChild(button); - }); - todosView.classList.add("detail-open"); +function applyWidth(config, value, persist = true) { + const next = Math.round(Math.max(config.min, Math.min(config.max, value))); + root.style.setProperty(config.variable, `${next}px`); + config.handle?.setAttribute("aria-valuenow", String(next)); + if (persist) localStorage.setItem(config.key, String(next)); } -document.querySelectorAll("[data-todo-view]").forEach((button) => { - button.addEventListener("click", () => { - document.querySelectorAll("[data-todo-view]").forEach((tab) => { - tab.setAttribute("aria-pressed", String(tab === button)); - }); - document.querySelectorAll("[data-todo-panel]").forEach((panel) => { - panel.hidden = panel.dataset.todoPanel !== button.dataset.todoView; - panel.classList.toggle("active", !panel.hidden); - }); - closeTodoDetail(); - }); -}); +resizeConfigs.forEach((config) => { + if (!config.handle) return; + const stored = Number(localStorage.getItem(config.key)); + applyWidth(config, Number.isFinite(stored) && stored > 0 ? stored : config.fallback, false); -document.querySelectorAll("[data-todo-card]").forEach((card) => { - card.addEventListener("click", () => openTodoDetail(card)); -}); - -document.querySelectorAll("[data-close-todo-detail]").forEach((button) => { - button.addEventListener("click", closeTodoDetail); -}); + config.handle.addEventListener("pointerdown", (event) => { + if (event.button !== 0) return; + const startX = event.clientX; + const startWidth = Number(config.handle.getAttribute("aria-valuenow")); + config.handle.classList.add("dragging"); + config.handle.setPointerCapture(event.pointerId); -document.querySelector("[data-todo-detail-primary]").addEventListener("click", (event) => { - showToast(event.currentTarget.textContent); -}); + const move = (moveEvent) => { + applyWidth(config, startWidth + (moveEvent.clientX - startX) * config.direction, false); + }; + const finish = (upEvent) => { + applyWidth(config, Number(config.handle.getAttribute("aria-valuenow"))); + config.handle.classList.remove("dragging"); + config.handle.releasePointerCapture(upEvent.pointerId); + config.handle.removeEventListener("pointermove", move); + config.handle.removeEventListener("pointerup", finish); + config.handle.removeEventListener("pointercancel", finish); + }; + config.handle.addEventListener("pointermove", move); + config.handle.addEventListener("pointerup", finish); + config.handle.addEventListener("pointercancel", finish); + }); -document.querySelector("#todo-capture").addEventListener("submit", (event) => { - event.preventDefault(); - const input = document.querySelector("#new-todo-title"); - showToast(input.value.trim() ? "已创建 Todo" : "输入 Todo 标题后创建"); - if (input.value.trim()) input.value = ""; + config.handle.addEventListener("keydown", (event) => { + if (!["ArrowLeft", "ArrowRight"].includes(event.key)) return; + event.preventDefault(); + const delta = event.key === "ArrowRight" ? 12 : -12; + applyWidth( + config, + Number(config.handle.getAttribute("aria-valuenow")) + delta * config.direction, + ); + }); }); window.addEventListener("keydown", (event) => { - if (event.key === "Escape") { - closeTodoDetail(); - closeOverlays(); - } + if (event.key === "Escape") closeOverlays(); }); -setTheme("light"); +void sidebar; +void inspector; diff --git a/design-system/prototypes/dashboard.html b/design-system/prototypes/dashboard.html index 9c3a634b..882c0bda 100644 --- a/design-system/prototypes/dashboard.html +++ b/design-system/prototypes/dashboard.html @@ -1,849 +1,418 @@ - + - - ArchCode — Dashboard Design Prototype - + + ArchCode — Dashboard Prototype + + + - - + -
- - -
-
-
- - -
-
- Runtime authority migration - Running -
-
- /Users/bo/Developer/AI/archcode - - 12 tools · 42,806 tokens - - Todo · Runtime authority migration -
-
-
- - +
- - +
+ + -
-
-
- - - - -
- -
-
-
IdeasCapture first, shape later1
-
- -
-
- -
-
ReadyClear enough to hand off1
-
- -
-
- -
-
In ProgressConnected to active work2
-
- - -
-
- -
-
DoneExplicitly completed1
-
- -
-
+
+
+
+
+ + +
+

Project overview

+

Dashboard

+

What needs you, what is moving, and where to continue.

+
-
-
- - -
- - +
-
+
- - diff --git a/design-system/prototypes/session.html b/design-system/prototypes/session.html index c6246b7c..d0297f30 100644 --- a/design-system/prototypes/session.html +++ b/design-system/prototypes/session.html @@ -1,846 +1,1532 @@ - + - ArchCode — Session Design Prototype - + ArchCode — Session Prototype + + + - - + -
-
-
- - - - -
+ +
- + -
+
- From c483dfc723d2e89ff9d6f464f5c51c69b3da43a9 Mon Sep 17 00:00:00 2001 From: bo Date: Thu, 30 Jul 2026 15:18:48 +0800 Subject: [PATCH 3/4] fix(design): address prototype review feedback --- design-system/pages/session.md | 19 +-- design-system/prototypes/app.js | 25 +--- design-system/prototypes/dashboard.html | 2 +- design-system/prototypes/session.html | 155 +++++++++++++++++++++++- design-system/prototypes/styles.css | 83 +------------ design-system/prototypes/todos.html | 7 +- 6 files changed, 167 insertions(+), 124 deletions(-) diff --git a/design-system/pages/session.md b/design-system/pages/session.md index 61e03c59..d3fbbab2 100644 --- a/design-system/pages/session.md +++ b/design-system/pages/session.md @@ -74,15 +74,10 @@ header and conversation. Do not move Execution into the inspector. ## Conversation Hierarchy -- User messages use one flat muted surface, 15px text, comfortable 1.66 - line-height, and right alignment. They are visually distinct without becoming - oversized chat bubbles. -- Work commentary uses secondary 13–14px text. Tool names and Work summaries - remain quieter than conversational content. -- The final Agent response is the strongest reading layer: 15px primary text, - 1.68 line-height, and a full-width structural surface. Its prose children use - a 65–72ch reading measure while code, tables, Mermaid, and other technical - blocks may use the available canvas. Its opening outcome may use 600 weight. +- User messages align right on one flat muted surface without becoming oversized + chat bubbles. +- Agent responses use the full-width structural surface; code, tables, Mermaid, + and other technical blocks may use the available canvas. - Agent responses are editorial text, not bubbles. Avoid avatars, role headers, timestamps, and cards competing with the actual outcome. - Use spacing and contrast, not additional boxes, to separate user intent, @@ -104,8 +99,7 @@ running: user message → expanded Work - Do not show `Execution {number}`, steps, Tool count, Child count, model, or binding metadata in the visible Work row. - Preserve the Execution identity in product data even though it is visually - omitted. The accessible disclosure names the Work segment, state, duration, - and current activity when present. + omitted. - Running Work is expanded by default. Historical completed Work is collapsed by default. - When a followed live Execution completes, collapse Work only if the user is @@ -146,9 +140,6 @@ running: user message → expanded Work - A user-explicit Work state wins over automatic defaults for the current route lifetime. - Keyboard focus remains on the disclosure button after opening or closing. -- `aria-expanded`, `aria-controls`, and an accessible name containing the Work - segment, state, elapsed duration, and current activity when present are - required. ## Work content diff --git a/design-system/prototypes/app.js b/design-system/prototypes/app.js index 0d876ef7..b9a4e874 100644 --- a/design-system/prototypes/app.js +++ b/design-system/prototypes/app.js @@ -62,10 +62,9 @@ document.querySelectorAll("[data-action]").forEach((element) => { }); const sidebar = document.querySelector(".project-sidebar"); -const inspector = document.querySelector(".context-inspector"); function closeOverlays() { - document.body.classList.remove("sidebar-open", "inspector-open"); + document.body.classList.remove("sidebar-open"); } document.querySelectorAll("[data-open-sidebar]").forEach((button) => { @@ -80,18 +79,6 @@ document.querySelectorAll("[data-open-sidebar]").forEach((button) => { }); }); -document.querySelectorAll("[data-open-inspector]").forEach((button) => { - button.addEventListener("click", () => { - if (window.matchMedia("(min-width: 1181px)").matches) { - const collapsed = document.body.classList.toggle("inspector-collapsed"); - button.setAttribute("aria-expanded", String(!collapsed)); - return; - } - closeOverlays(); - document.body.classList.add("inspector-open"); - }); -}); - document.querySelectorAll("[data-close-overlay]").forEach((button) => { button.addEventListener("click", closeOverlays); }); @@ -120,15 +107,6 @@ const resizeConfigs = [ fallback: 264, direction: 1, }, - { - handle: document.querySelector("[data-resize-inspector]"), - variable: "--inspector-width", - key: "archcode-prototype-inspector-width", - min: 280, - max: 460, - fallback: 312, - direction: -1, - }, ]; function applyWidth(config, value, persist = true) { @@ -182,4 +160,3 @@ window.addEventListener("keydown", (event) => { }); void sidebar; -void inspector; diff --git a/design-system/prototypes/dashboard.html b/design-system/prototypes/dashboard.html index 882c0bda..cb59597a 100644 --- a/design-system/prototypes/dashboard.html +++ b/design-system/prototypes/dashboard.html @@ -286,7 +286,7 @@