diff --git a/AGENTS.md b/AGENTS.md index 64e73d36..2f40d4b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,77 @@ 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 design-system working structure is: + +```text +design-system/ +├── MASTER.md +├── pages/ +└── 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 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. +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. + +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 78% rename from design/prototypes/signal-workbench/design-system/signal-workbench/MASTER.md rename to design-system/MASTER.md index 38724924..af6ae043 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 -> 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. The interactive prototype remains the rendered reference: -> [`../../index.html`](../../index.html). +> 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). ## Product Fit @@ -64,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 | @@ -114,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 | @@ -139,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 | @@ -154,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. @@ -171,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: @@ -184,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: @@ -195,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 @@ -203,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. @@ -220,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. @@ -232,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 @@ -250,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. @@ -263,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: @@ -323,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. @@ -366,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 @@ -439,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 @@ -469,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 @@ -494,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/prototypes/signal-workbench/design-system/signal-workbench/pages/dashboard.md b/design-system/pages/dashboard.md similarity index 79% rename from design/prototypes/signal-workbench/design-system/signal-workbench/pages/dashboard.md rename to design-system/pages/dashboard.md index d360734a..031d3757 100644 --- a/design/prototypes/signal-workbench/design-system/signal-workbench/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/prototypes/signal-workbench/design-system/signal-workbench/pages/session.md b/design-system/pages/session.md similarity index 89% rename from design/prototypes/signal-workbench/design-system/signal-workbench/pages/session.md rename to design-system/pages/session.md index 5644f3cc..d3fbbab2 100644 --- a/design/prototypes/signal-workbench/design-system/signal-workbench/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: @@ -56,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: 16px 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, @@ -85,8 +98,8 @@ 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. - 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 +120,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 +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 - Execution number, state, elapsed duration, and current activity when present - are required. ## Work content diff --git a/design/prototypes/signal-workbench/design-system/signal-workbench/pages/todos.md b/design-system/pages/todos.md similarity index 72% rename from design/prototypes/signal-workbench/design-system/signal-workbench/pages/todos.md rename to design-system/pages/todos.md index 4d1addbb..656c3416 100644 --- a/design/prototypes/signal-workbench/design-system/signal-workbench/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 new file mode 100644 index 00000000..b9a4e874 --- /dev/null +++ b/design-system/prototypes/app.js @@ -0,0 +1,162 @@ +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", + check: "m5 12 4 4L19 6", + chevron: "m9 6 6 6-6 6", + close: "M6 6l12 12M18 6 6 18", + dashboard: "M4 4h6v6H4zm10 0h6v9h-6zM4 14h6v6H4zm10 3h6v3h-6z", + 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", + 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", + 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", + todo: "M8 6h12M8 12h12M8 18h12M3.5 6h.01M3.5 12h.01M3.5 18h.01", +}; + +function icon(name) { + return ``; +} + +document.querySelectorAll("[data-icon]").forEach((element) => { + const name = element.dataset.icon; + if (icons[name]) element.innerHTML = icon(name); +}); + +const root = document.documentElement; +const toast = document.querySelector(".toast"); +let toastTimer; + +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"); + } + if (persist) localStorage.setItem("archcode-prototype-theme", theme); +} + +setTheme(localStorage.getItem("archcode-prototype-theme") || root.dataset.theme || "dark", false); + +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"); + clearTimeout(toastTimer); + toastTimer = setTimeout(() => toast.classList.remove("visible"), 2200); +} + +document.querySelectorAll("[data-action]").forEach((element) => { + element.addEventListener("click", () => showToast(element.dataset.action)); +}); + +const sidebar = document.querySelector(".project-sidebar"); + +function closeOverlays() { + document.body.classList.remove("sidebar-open"); +} + +document.querySelectorAll("[data-open-sidebar]").forEach((button) => { + button.addEventListener("click", () => { + if (window.matchMedia("(min-width: 761px)").matches) { + const collapsed = document.body.classList.toggle("sidebar-collapsed"); + button.setAttribute("aria-expanded", String(!collapsed)); + return; + } + closeOverlays(); + document.body.classList.add("sidebar-open"); + }); +}); + +document.querySelectorAll("[data-close-overlay]").forEach((button) => { + button.addEventListener("click", closeOverlays); +}); + +document.querySelector(".scrim")?.addEventListener("click", closeOverlays); + +document.querySelectorAll("[data-sidebar-tab]").forEach((button) => { + button.addEventListener("click", () => { + 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; + }); + }); +}); + +const resizeConfigs = [ + { + handle: document.querySelector("[data-resize-sidebar]"), + variable: "--sidebar-width", + key: "archcode-prototype-sidebar-width", + min: 210, + max: 340, + fallback: 264, + direction: 1, + }, +]; + +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)); +} + +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); + + 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); + + 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); + }); + + 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") closeOverlays(); +}); + +void sidebar; diff --git a/design-system/prototypes/dashboard.html b/design-system/prototypes/dashboard.html new file mode 100644 index 00000000..cb59597a --- /dev/null +++ b/design-system/prototypes/dashboard.html @@ -0,0 +1,418 @@ + + + + + + + ArchCode — Dashboard Prototype + + + + + + +
+ + + + +
+
+
+
+ + +
+

Project overview

+

Dashboard

+

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

+
+
+ +
+
+
+

Needs attention

0 +
+
Nothing needs your attention.
+
+ +
+
+

Running now

0 +
+
No sessions are running.
+
+
+ +
+
+
+

Continue working

2 +
+
+ + + Brief Greeting SessionIdle · Updated 2h ago + Open + + +
+
+ +
+
+

Upcoming

1 +
+
+ +
+
+
+
+
+
+
+ + +
+ + diff --git a/design-system/prototypes/session.html b/design-system/prototypes/session.html new file mode 100644 index 00000000..7090bfb2 --- /dev/null +++ b/design-system/prototypes/session.html @@ -0,0 +1,1690 @@ + + + + + + + ArchCode — Session Prototype + + + + + + +
+ + + + +
+
+
+ +
+
+

Session execution state audit

Needs you +
+
+ /workspace/archcode + · + 12 tools · 48,206 tokens + · + Todo: Verify Session UI +
+
+ + +
+ +
+
+
+
+

Audit the Session execution UI. Verify tools, delegation, recovery, HITL, and queued follow-up behavior.

+ +
+
+ + +
+

First pass complete. The Workstream projection is sound; the remaining risk is interaction coverage around suspended Executions and queued instructions.

+
+ +
+
+

Run the focused interaction checks and stop before any broad workspace mutation.

+ +
+
+ +
+

I found the focused test path. Before running the command, I’m checking the exact scope and preserving the current Session state.

+ +
+ +
+ The requested check only needs the Web interaction suite. A full monorepo run would add time and unrelated Agent Core process lifecycles. +
+
+ +
+ +
+
file_readHitlCard.tsxCompleted
+
grep“Queue another instruction”Completed
+
lsp_diagnosticsExecutionWorkstream.tsxCompleted
+
+
+ +
+ + +
+ +
+
+ ↳ delegate + Verify Session HITL semanticsAnalyst · deep · Completed · 31s + +
+
+ +
+ + +
+ +
+ ✓ Recovered + Provider stream resumed on attempt 2 · stream_eof + +
+ +
+ Hard context compaction + Preserved the current and last two rounds; tool artifacts remain recoverable. + +
+ +
+ +
+ $ bun run --cwd apps/web test SessionComposerDock.test.tsx + Waiting for permission before starting the process… +
+
+
+
+
+
+
+ +
+
+
+
+ Needs attention + + +
+ +
+
+

Run focused Web verification

bash · 1/2 +
+

The Agent wants to run one scoped interaction-test command in apps/web.

+ + +
+ ‹ 1 / 2 › + + + + + + +
+
+ + +
+ +
+
+ Queued + After verification, summarize the remaining risk. + GPT-5.6 Luna · deep + +
+
+ Steering + Also check narrow-screen overflow. + GPT-5.6 Luna · deep + +
+
+ +
+ + Queue another instruction… + Needs attention + Lead · principal + +
+
+
+
+
+ + +
+ + +
+ + + diff --git a/design-system/prototypes/styles.css b/design-system/prototypes/styles.css new file mode 100644 index 00000000..a2644e5b --- /dev/null +++ b/design-system/prototypes/styles.css @@ -0,0 +1,724 @@ +:root { + color-scheme: light; + --bg-base: #f2f3f0; + --bg-surface: #f8f8f6; + --bg-elevated: #ffffff; + --bg-overlay: #ffffff; + --bg-muted: #eceeea; + --bg-hover: #e6e8e4; + --bg-active: #dde0da; + --border-subtle: #e1e3df; + --border-default: #cdd1ca; + --border-strong: #9fa69d; + --control-border: var(--border-default); + --text-primary: #171917; + --text-secondary: #424742; + --text-tertiary: #626a62; + --text-muted: #747c74; + --brand: #6157d5; + --brand-hover: #5148c1; + --brand-field: #eeecfb; + --brand-ink: #ffffff; + --signal: #758b22; + --signal-field: #eef3d8; + --signal-foreground: #506015; + --signal-ink: #151b00; + --success: #2f7752; + --success-field: #e8f2eb; + --warning: #8a5e13; + --warning-field: #f7eddd; + --error: #b14840; + --error-field: #f8e8e6; + --neutral: #626a62; + --neutral-field: #eceeea; + --selection-field: #eeedf8; + --running-field: #f1f4e7; + --attention-field: #f8f2e8; + --rail: #191c19; + --rail-ink: #f4f5f1; + --rail-muted: #939a91; + --focus: 0 0 0 3px rgb(97 87 213 / 23%); + --elevation-sm: 0 10px 30px rgb(26 31 25 / 9%); + --elevation-md: 0 22px 56px rgb(25 28 22 / 18%); + --elevation-lg: 0 22px 56px rgb(25 28 22 / 18%); + --shape-control: 4px; + --shape-card: 6px; + --shape-popover: 8px; + --shape-dialog: 12px; + --font-stack-sans: + -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", sans-serif; + --font-stack-mono: + "SFMono-Regular", Consolas, "Liberation Mono", monospace; + --rail-width: 52px; + --sidebar-width: 264px; +} + +:root[data-theme="dark"] { + color-scheme: dark; + --bg-base: #101210; + --bg-surface: #151815; + --bg-elevated: #1c201c; + --bg-overlay: #1c201c; + --bg-muted: #232723; + --bg-hover: #292e29; + --bg-active: #303630; + --border-subtle: #262b26; + --border-default: #363c36; + --border-strong: #596159; + --control-border: var(--border-default); + --text-primary: #f3f5f0; + --text-secondary: #c9cec6; + --text-tertiary: #9ca49a; + --text-muted: #868f84; + --brand: #a49bff; + --brand-hover: #b9b2ff; + --brand-field: #2b2845; + --brand-ink: #17181f; + --signal: #c1dd64; + --signal-field: #29331b; + --signal-foreground: #d9ec8c; + --signal-ink: #202807; + --success: #82c49a; + --success-field: #203329; + --warning: #deb96e; + --warning-field: #352b1b; + --error: #f08b82; + --error-field: #3b2421; + --neutral: #9ca49a; + --neutral-field: #232723; + --selection-field: #26243a; + --running-field: #22291b; + --attention-field: #2c271e; + --rail: #0c0e0c; + --rail-ink: #f2f4ef; + --rail-muted: #858c83; + --focus: 0 0 0 3px rgb(164 155 255 / 26%); + --elevation-sm: 0 14px 34px rgb(0 0 0 / 32%); + --elevation-md: 0 22px 56px rgb(0 0 0 / 52%); + --elevation-lg: 0 22px 56px rgb(0 0 0 / 52%); +} + +* { + box-sizing: border-box; +} + +html, +body { + height: 100%; +} + +html { + background: var(--bg-base); +} + +body { + margin: 0; + overflow: hidden; + background: var(--bg-base); + color: var(--text-primary); + font-family: var(--font-stack-sans); + font-size: 15px; + line-height: 1.45; + -webkit-font-smoothing: antialiased; +} + +button, +input, +textarea { + color: inherit; + font: inherit; +} + +button, +a { + -webkit-tap-highlight-color: transparent; +} + +button { + border: 0; + background: transparent; +} + +button:not(:disabled), +a { + cursor: pointer; +} + +button:focus-visible, +a:focus-visible, +input:focus-visible, +textarea:focus-visible, +[tabindex]:focus-visible { + outline: 2px solid var(--brand); + outline-offset: 2px; +} + +a { + color: inherit; + text-decoration: none; +} + +h1, +h2, +h3, +p { + margin: 0; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.skip-link { + position: fixed; + z-index: 100; + top: 8px; + left: 8px; + padding: 8px 12px; + border-radius: 6px; + background: var(--text-primary); + color: var(--bg-base); + transform: translateY(-150%); +} + +.skip-link:focus { + transform: none; +} + +.app-shell { + display: grid; + grid-template-columns: var(--rail-width) var(--sidebar-width) minmax(0, 1fr); + width: 100vw; + height: 100dvh; +} + +.dashboard-page.sidebar-collapsed .app-shell, +.todos-page.sidebar-collapsed .app-shell { + grid-template-columns: var(--rail-width) minmax(0, 1fr); +} + +.sidebar-collapsed .project-sidebar { + display: none; +} + +.project-rail { + z-index: 40; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + min-width: 0; + padding: 8px 0; + overflow: hidden; + border-right: 1px solid var(--border-default); + background: var(--rail); + color: var(--rail-muted); +} + +.brand-mark, +.rail-project, +.rail-icon { + position: relative; + display: grid; + place-items: center; + flex: 0 0 auto; + width: 36px; + height: 36px; + border-radius: 5px; + background: transparent; + color: var(--rail-muted); + font-size: 12px; +} + +.brand-mark { + margin-bottom: 4px; + background: var(--bg-elevated); + color: var(--brand); + font-size: 20px; + font-weight: 750; + box-shadow: inset 0 0 0 1px var(--border-subtle); +} + +.brand-mark span { + transform: translateY(-1px); +} + +.rail-project { + text-transform: lowercase; +} + +.rail-project:hover, +.rail-icon:hover { + background: rgb(255 255 255 / 8%); + color: var(--rail-ink); +} + +.rail-project.active { + background: rgb(255 255 255 / 12%); + color: var(--rail-ink); + box-shadow: inset 2px 0 0 var(--brand); +} + +.rail-project.active::before { + position: absolute; + left: -8px; + width: 2px; + height: 24px; + border-radius: 0 2px 2px 0; + background: var(--brand); + content: ""; +} + +.rail-spacer { + flex: 1; +} + +.rail-separator { + width: 32px; + height: 1px; + margin: 4px 0; + background: var(--border-subtle); +} + +.rail-attention-count { + position: absolute; + top: 1px; + right: 1px; + display: grid; + place-items: center; + min-width: 15px; + height: 15px; + padding: 0 4px; + border-radius: 8px; + background: var(--signal); + color: var(--signal-ink); + font-size: 9px; + font-weight: 700; +} + +[data-icon] { + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; +} + +[data-icon] svg { + width: 16px; + height: 16px; +} + +.project-sidebar { + position: relative; + z-index: 30; + min-width: 0; + overflow: hidden; + border-right: 1px solid var(--border-default); + background: var(--bg-surface); +} + +.project-sidebar-inner { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; +} + +.sidebar-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 10px; + min-height: 64px; + padding: 14px 12px 10px 16px; +} + +.project-copy { + min-width: 0; +} + +.project-copy strong, +.project-copy span { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.project-copy strong { + font-size: 14px; + font-weight: 650; +} + +.project-copy span { + margin-top: 5px; + color: var(--text-tertiary); + font-family: var(--font-stack-mono); + font-size: 11px; +} + +.icon-button { + display: inline-grid; + place-items: center; + flex: 0 0 auto; + width: 32px; + height: 32px; + border-radius: 5px; + background: transparent; + color: var(--text-secondary); +} + +.icon-button:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.sidebar-nav { + display: grid; + gap: 2px; + padding: 0 8px 10px; + border-bottom: 1px solid var(--border-subtle); +} + +.sidebar-nav a { + display: grid; + grid-template-columns: 18px minmax(0, 1fr) auto; + align-items: center; + gap: 10px; + min-height: 38px; + padding: 0 9px; + border-radius: 5px; + color: var(--text-secondary); + font-size: 13px; + font-weight: 560; +} + +.sidebar-nav a:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.sidebar-nav a.active { + background: var(--selection-field); + color: var(--brand-hover); +} + +.nav-count { + color: var(--text-tertiary); + font-size: 11px; +} + +.sidebar-tabs { + display: grid; + grid-template-columns: 1fr 1fr; + flex: 0 0 42px; + border-bottom: 1px solid var(--border-subtle); +} + +.sidebar-tabs button { + position: relative; + background: transparent; + color: var(--text-secondary); + font-size: 12px; + font-weight: 580; +} + +.sidebar-tabs button[aria-selected="true"] { + color: var(--text-primary); +} + +.sidebar-tabs button[aria-selected="true"]::after { + position: absolute; + right: 8px; + bottom: -1px; + left: 8px; + height: 2px; + background: var(--brand); + content: ""; +} + +.sidebar-panel { + min-height: 0; + overflow: auto; +} + +.sidebar-toolbar { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 8px; + padding: 10px 12px 12px; +} + +.search-field { + display: grid; + grid-template-columns: 16px minmax(0, 1fr); + align-items: center; + gap: 7px; + min-width: 0; + height: 40px; + padding: 0 10px; + border: 1px solid var(--border-default); + border-radius: 6px; + background: var(--bg-muted); + color: var(--text-tertiary); +} + +.search-field input { + min-width: 0; + border: 0; + outline: 0; + background: transparent; + font-size: 12px; +} + +.search-field input::placeholder { + color: var(--text-tertiary); +} + +.new-session { + height: 40px; + padding: 0 12px; + border: 1px solid color-mix(in srgb, var(--brand) 58%, var(--border-default)); + border-radius: 6px; + background: var(--brand-field); + color: var(--brand); + font-size: 12px; + font-weight: 650; + white-space: nowrap; +} + +.new-session:hover { + border-color: var(--brand); +} + +.session-group { + padding: 2px 8px 10px; +} + +.session-group-label { + display: flex; + align-items: center; + justify-content: space-between; + height: 28px; + padding: 0 9px; + color: var(--text-tertiary); + font-size: 10px; + font-weight: 700; + letter-spacing: 0.09em; + text-transform: uppercase; +} + +.session-item { + display: grid; + grid-template-columns: 12px minmax(0, 1fr) auto 24px; + align-items: center; + gap: 7px; + width: 100%; + min-height: 38px; + padding: 0 5px 0 9px; + border-radius: 4px; + background: transparent; + color: var(--text-secondary); + text-align: left; +} + +.session-item:hover, +.session-item.active { + background: var(--bg-hover); + color: var(--text-primary); +} + +.session-item.active { + background: var(--selection-field); + box-shadow: inset 2px 0 0 var(--brand); +} + +.session-state { + width: 11px; + height: 11px; + border: 1px solid var(--text-tertiary); + border-radius: 50%; +} + +.session-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 12px; + font-weight: 560; +} + +.session-time { + color: var(--text-tertiary); + font-size: 10px; +} + +.session-actions { + opacity: 0; +} + +.session-item:hover .session-actions, +.session-item:focus-within .session-actions { + opacity: 1; +} + +.resize-handle { + position: absolute; + z-index: 5; + top: 0; + right: -4px; + bottom: 0; + width: 8px; + cursor: col-resize; +} + +.resize-handle::before { + position: absolute; + top: 0; + bottom: 0; + left: 3px; + width: 1px; + background: transparent; + content: ""; +} + +.resize-handle:hover::before, +.resize-handle.dragging::before, +.resize-handle:focus-visible::before { + background: var(--brand); +} + +.work-canvas { + position: relative; + min-width: 0; + min-height: 0; + overflow: hidden; + background: var(--bg-base); +} + +.canvas-sidebar-toggle { + display: inline-grid; + place-items: center; + width: 30px; + height: 30px; + border-radius: 5px; + background: transparent; + color: var(--text-secondary); +} + +.canvas-sidebar-toggle:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.mobile-close { + display: none; +} + +.scrim { + position: fixed; + z-index: 25; + inset: 0; + display: none; + background: rgba(8, 10, 8, 0.42); +} + +.toast { + position: fixed; + z-index: 90; + right: 18px; + bottom: 18px; + max-width: min(360px, calc(100vw - 36px)); + padding: 10px 13px; + border: 1px solid var(--border-default); + border-radius: 7px; + background: var(--bg-elevated); + color: var(--text-secondary); + box-shadow: var(--elevation-md); + font-size: 12px; + opacity: 0; + transform: translateY(8px); + transition: + opacity 140ms ease, + transform 140ms ease; + pointer-events: none; +} + +.toast.visible { + opacity: 1; + transform: none; +} + +@media (max-width: 760px) { + :root { + --rail-width: 48px; + } + + .app-shell, + .session-page .app-shell { + grid-template-columns: var(--rail-width) minmax(0, 1fr); + } + + .dashboard-page.sidebar-collapsed .app-shell, + .todos-page.sidebar-collapsed .app-shell { + grid-template-columns: var(--rail-width) minmax(0, 1fr); + } + + .sidebar-collapsed .project-sidebar { + display: block; + } + + .project-sidebar { + position: fixed; + z-index: 36; + top: 0; + bottom: 0; + left: var(--rail-width); + width: min(var(--sidebar-width), calc(100vw - var(--rail-width))); + box-shadow: var(--elevation-md); + transform: translateX(-105%); + transition: transform 160ms ease; + } + + body.sidebar-open .project-sidebar { + transform: none; + } + + body.sidebar-open .scrim { + display: block; + } + + .project-sidebar .mobile-close { + display: inline-grid; + } + + .resize-handle { + display: none; + } + + .rail-project:nth-of-type(n + 3) { + display: none; + } + + .project-rail { + padding-top: 6px; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + } +} diff --git a/design-system/prototypes/todos.html b/design-system/prototypes/todos.html new file mode 100644 index 00000000..65701a8d --- /dev/null +++ b/design-system/prototypes/todos.html @@ -0,0 +1,670 @@ + + + + + + + ArchCode — Todos Prototype + + + + + + +
+ + + + +
+
+
+ +
+

Todos

+

Shape intent, then start as many discussions or work sessions as useful.

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

Ideas

Capture first, shape later

1 +
+
+
+ + +
+
+
+ +
+
+

Ready

Clear enough to hand off

0 +
+

Nothing readyMove an idea here when it is ready.

+
+ +
+
+

In Progress

Work underway

0 +
+

No work in progressStart work or drag a Todo here.

+
+ +
+
+

Done

Explicitly completed

0 +
+

Nothing completedCompleted Todos stay visible here.

+
+
+ + + + +
+
+
+
+
+ + + + +
+ + + 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/app.js b/design/prototypes/signal-workbench/app.js deleted file mode 100644 index 0771d628..00000000 --- a/design/prototypes/signal-workbench/app.js +++ /dev/null @@ -1,488 +0,0 @@ -// 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", - 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", - 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", - 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", - 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", - 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 ``; -} - -document.querySelectorAll("[data-icon]").forEach((element) => { - const iconName = element.dataset.icon; - if (iconPaths[iconName]) { - element.insertAdjacentHTML("afterbegin", icon(iconName)); - } -}); - -const documentElement = 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. - } - } - return next; -} - -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); - }); -} - -Object.values(layoutDimensions).forEach(bindLayoutResize); - -function showToast(message) { - 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"); -} - -function closeTodoDetail() { - todosView.classList.remove("detail-open"); - document.querySelectorAll("[data-todo-card]").forEach((card) => card.classList.remove("selected")); -} - -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"); -} - -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 }); -} - -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); - return; - } - document.body.classList.remove("sidebar-collapsed"); - }); -}); - -document.querySelectorAll("[data-canvas-target]").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"); - } - }); -} - -document.querySelectorAll("[data-thread]").forEach((button) => { - button.addEventListener("click", () => { - setCurrentThread(button); - showCanvas("session"); - showToast(`切换到 ${button.querySelector("strong").textContent}`); - }); -}); - -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-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`); - }); -}); - -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) => { - 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; - }); - }); -}); - -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"], - }, - "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"], - }, -}; - -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"); -} - -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(); - }); -}); - -document.querySelectorAll("[data-todo-card]").forEach((card) => { - card.addEventListener("click", () => openTodoDetail(card)); -}); - -document.querySelectorAll("[data-close-todo-detail]").forEach((button) => { - button.addEventListener("click", closeTodoDetail); -}); - -document.querySelector("[data-todo-detail-primary]").addEventListener("click", (event) => { - showToast(event.currentTarget.textContent); -}); - -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 = ""; -}); - -window.addEventListener("keydown", (event) => { - if (event.key === "Escape") { - closeTodoDetail(); - closeOverlays(); - } -}); - -setTheme("light"); 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/prototypes/signal-workbench/index.html b/design/prototypes/signal-workbench/index.html deleted file mode 100644 index 2bf4c0cc..00000000 --- a/design/prototypes/signal-workbench/index.html +++ /dev/null @@ -1,846 +0,0 @@ - - - - - - - ArchCode — Signal Workbench 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/styles.css b/design/prototypes/signal-workbench/styles.css deleted file mode 100644 index 6ba4f59b..00000000 --- a/design/prototypes/signal-workbench/styles.css +++ /dev/null @@ -1,4614 +0,0 @@ -/* ArchCode Signal Workbench design prototype */ - -:root { - color-scheme: light; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", sans-serif; - font-size: 15px; - --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", sans-serif; - --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", monospace; - --canvas: #f2f3f0; - --surface: #f8f8f6; - --surface-raised: #ffffff; - --surface-muted: #eceeea; - --surface-hover: #e6e8e4; - --surface-active: #dde0da; - --border-soft: #e1e3df; - --border: #cdd1ca; - --border-strong: #9fa69d; - --ink: #171917; - --ink-secondary: #424742; - --ink-tertiary: #626a62; - --ink-muted: #747c74; - --brand: #6157d5; - --brand-hover: #5148c1; - --brand-field: #eeecfb; - --brand-ink: #ffffff; - --signal: #758b22; - --signal-ink: #202807; - --signal-foreground: #506015; - --signal-field: #eef3d8; - --success: #2f7752; - --success-field: #e8f2eb; - --warning: #8a5e13; - --warning-field: #f7eddd; - --danger: #b34d45; - --danger-field: #f8e8e6; - --selection-field: #eeedf8; - --running-field: #f1f4e7; - --attention-field: #f8f2e8; - --rail: #191c19; - --rail-ink: #f4f5f1; - --rail-muted: #939a91; - --terminal-bg: #252620; - --terminal-text: #d7d6cd; - --terminal-muted: #aaa99f; - --terminal-success: #b6d84b; - --terminal-error: #ed8178; - --shadow-dock: 0 10px 30px rgb(26 31 25 / 9%); - --shadow-overlay: 0 22px 56px rgb(25 28 22 / 18%); - --focus: 0 0 0 3px rgb(97 87 213 / 23%); - --header-height: 64px; - --rail-width: 52px; - --sidebar-width: 264px; - --inspector-width: 312px; -} - -[data-theme="dark"] { - color-scheme: dark; - --canvas: #101210; - --surface: #151815; - --surface-raised: #1c201c; - --surface-muted: #232723; - --surface-hover: #292e29; - --surface-active: #303630; - --border-soft: #262b26; - --border: #363c36; - --border-strong: #596159; - --ink: #f3f5f0; - --ink-secondary: #c9cec6; - --ink-tertiary: #9ca49a; - --ink-muted: #868f84; - --brand: #a49bff; - --brand-hover: #b9b2ff; - --brand-field: #2b2845; - --brand-ink: #17181f; - --signal: #c1dd64; - --signal-ink: #202807; - --signal-foreground: #d9ec8c; - --signal-field: #29331b; - --success: #82c49a; - --success-field: #203329; - --warning: #deb96e; - --warning-field: #352b1b; - --danger: #f08b82; - --danger-field: #3b2421; - --selection-field: #26243a; - --running-field: #22291b; - --attention-field: #2c271e; - --rail: #0c0e0c; - --rail-ink: #f2f4ef; - --rail-muted: #858c83; - --terminal-bg: #0f100e; - --terminal-text: #dad9d1; - --terminal-muted: #aaa99f; - --terminal-success: #b6d84b; - --terminal-error: #ed8178; - --shadow-dock: 0 14px 34px rgb(0 0 0 / 32%); - --shadow-overlay: 0 22px 56px rgb(0 0 0 / 52%); - --focus: 0 0 0 3px rgb(164 155 255 / 26%); -} - -* { - box-sizing: border-box; -} - -html, -body { - width: 100%; - min-height: 100%; - margin: 0; - background: var(--canvas); -} - -body { - overflow: hidden; - color: var(--ink); - transition: - color 180ms ease, - background-color 180ms ease; -} - -button, -input, -textarea { - font: inherit; -} - -button { - color: inherit; - cursor: pointer; -} - -button, -input, -textarea { - touch-action: manipulation; -} - -button:focus-visible, -input:focus-visible, -textarea:focus-visible, -[tabindex]:focus-visible { - outline: 2px solid var(--brand); - outline-offset: 2px; -} - -svg { - width: 17px; - height: 17px; - display: block; - flex: 0 0 auto; -} - -code, -kbd, -.elapsed, -.step-time, -.file-stat { - font-family: var(--font-mono); -} - -code { - font-size: 0.93em; -} - -kbd { - padding: 2px 4px; - border: 1px solid var(--border); - border-radius: 4px; - color: var(--ink-muted); - background: var(--surface-raised); - font-size: 9px; - font-weight: 400; -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.skip-link { - position: fixed; - z-index: 1000; - top: -60px; - left: 12px; - padding: 10px 14px; - color: var(--brand-ink); - background: var(--brand); -} - -.skip-link:focus { - top: 10px; -} - -.app-shell { - width: 100%; - height: 100dvh; - display: grid; - grid-template-columns: - var(--rail-width) - var(--sidebar-width) - minmax(440px, 1fr) - var(--inspector-width); - overflow: hidden; - background: var(--canvas); -} - -.project-rail { - min-width: 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 6px; - padding: 9px 7px; - color: var(--rail-ink); - background: var(--rail); -} - -.brand-mark, -.rail-project, -.rail-icon { - width: 36px; - height: 36px; - display: grid; - place-items: center; - border: 0; - border-radius: 8px; - background: transparent; -} - -.brand-mark { - margin-bottom: 10px; - color: #fff; - background: var(--brand); - font-size: 17px; - font-weight: 800; - letter-spacing: -0.05em; -} - -.rail-project { - position: relative; - color: var(--rail-muted); - font-size: 9px; - font-weight: 700; -} - -.rail-project:hover, -.rail-icon:hover { - color: var(--rail-ink); - background: rgb(255 255 255 / 7%); -} - -.rail-project.active { - color: var(--rail-ink); - background: rgb(255 255 255 / 11%); -} - -.rail-project.active::before { - position: absolute; - left: -7px; - width: 3px; - height: 18px; - border-radius: 0 3px 3px 0; - background: var(--signal); - content: ""; -} - -.rail-projects { - display: flex; - flex-direction: column; - gap: 4px; -} - -.rail-spacer { - flex: 1; -} - -.rail-icon { - position: relative; - color: var(--rail-muted); -} - -.rail-attention-count { - position: absolute; - top: 2px; - right: 1px; - min-width: 14px; - height: 14px; - display: grid; - place-items: center; - border-radius: 999px; - color: var(--signal-ink); - background: var(--signal); - font-size: 8px; - font-weight: 800; -} - -.thread-sidebar { - min-width: 0; - position: relative; - display: flex; - flex-direction: column; - container-name: session-sidebar; - container-type: inline-size; - overflow: hidden; - background: var(--surface); - border-right: 1px solid var(--border); -} - -.layout-resize-handle { - position: absolute; - z-index: 12; - top: 0; - bottom: 0; - width: 9px; - border: 0; - cursor: col-resize; - touch-action: none; -} - -.layout-resize-handle::after { - position: absolute; - top: 50%; - bottom: 50%; - left: 4px; - width: 1px; - background: var(--brand); - content: ""; - opacity: 0; - transition: - top 160ms ease, - bottom 160ms ease, - opacity 140ms ease; -} - -.layout-resize-handle:hover::after, -.layout-resize-handle:focus-visible::after, -.layout-resize-handle.dragging::after { - top: 36%; - bottom: 36%; - opacity: 0.75; -} - -.layout-resize-handle:focus-visible { - outline-offset: -2px; -} - -.sidebar-resize { - right: -5px; -} - -.inspector-resize { - left: -5px; -} - -.sidebar-head { - min-height: var(--header-height); - display: flex; - align-items: center; - justify-content: space-between; - gap: 6px; - padding: 0 14px; - border-bottom: 1px solid var(--border-soft); -} - -.project-title { - min-width: 0; - flex: 1; -} - -.project-title strong, -.project-title span { - display: block; -} - -.project-title strong { - margin-bottom: 2px; - font-size: 14px; - font-weight: 650; -} - -.project-title span { - overflow: hidden; - color: var(--ink-muted); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 11px; - white-space: nowrap; - text-overflow: ellipsis; -} - -.sidebar-head-actions { - display: flex; - align-items: center; - gap: 1px; -} - -.sidebar-head-actions .icon-button { - width: 28px; - height: 28px; - font-size: 11px; -} - -.sidebar-head-actions .icon-button svg { - width: 14px; - height: 14px; -} - -.icon-button { - width: 40px; - height: 40px; - display: inline-grid; - place-items: center; - border: 0; - border-radius: 7px; - color: var(--ink-secondary); - background: transparent; -} - -.icon-button:hover { - color: var(--ink); - background: var(--surface-muted); -} - -.mobile-close, -.mobile-menu, -.sidebar-expand { - display: none; -} - -.project-links { - display: grid; - gap: 4px; - padding: 9px 10px; - border-bottom: 1px solid var(--border-soft); -} - -.project-links button { - min-height: 34px; - display: grid; - grid-template-columns: 18px minmax(0, 1fr) 14px; - align-items: center; - gap: 7px; - border: 1px solid transparent; - border-radius: 6px; - padding: 0 9px; - color: var(--ink-secondary); - background: transparent; - text-align: left; - font-size: 12px; - font-weight: 600; -} - -.project-links button:hover { - color: var(--ink); - border-color: var(--border); - background: var(--surface-muted); -} - -.project-links svg { - width: 13px; - height: 13px; -} - -.sidebar-tabs, -.inspector-tabs { - display: flex; - border-bottom: 1px solid var(--border-soft); -} - -.sidebar-tabs button, -.inspector-tabs button { - min-width: 0; - min-height: 38px; - flex: 1; - border: 0; - border-bottom: 2px solid transparent; - color: var(--ink-tertiary); - background: transparent; - font-size: 11px; - font-weight: 600; -} - -.sidebar-tabs button:hover, -.inspector-tabs button:hover { - color: var(--ink); -} - -.sidebar-tabs button[aria-selected="true"], -.inspector-tabs button[aria-selected="true"] { - color: var(--ink); - border-bottom-color: var(--brand); -} - -.sidebar-panel { - min-height: 0; - flex: 1; - overflow-y: auto; -} - -.sidebar-panel[hidden], -.inspector-panel[hidden] { - display: none; -} - -.sidebar-toolbar { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 6px; - padding: 9px 10px 4px; -} - -.sidebar-toolbar .search-field { - min-width: 0; - margin: 0; -} - -.compact-create { - min-height: 36px; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 5px; - border: 1px solid color-mix(in srgb, var(--brand) 48%, var(--border)); - border-radius: 6px; - padding: 0 8px; - color: var(--brand); - background: var(--brand-field); - font-size: 11px; - font-weight: 700; -} - -.compact-create:hover { - border-color: var(--brand); -} - -.compact-create svg { - width: 12px; - height: 12px; -} - -.automation-link { - border-bottom: 0; - padding-bottom: 0; -} - -.new-task { - min-height: 40px; - display: flex; - align-items: center; - gap: 8px; - margin: 12px 12px 8px; - padding: 0 10px; - border: 0; - border-radius: 7px; - color: var(--brand-ink); - background: var(--brand); - font-size: 12px; - font-weight: 650; - transition: background-color 140ms ease; -} - -.new-task:hover { - background: var(--brand-hover); -} - -.new-task kbd { - margin-left: auto; - border-color: rgb(255 255 255 / 24%); - color: currentColor; - background: rgb(255 255 255 / 10%); -} - -[data-theme="dark"] .new-task kbd { - border-color: rgb(0 0 0 / 18%); - background: rgb(0 0 0 / 9%); -} - -.search-field { - min-height: 36px; - display: flex; - align-items: center; - gap: 7px; - margin: 0 12px 8px; - padding: 0 8px; - border: 1px solid var(--border); - border-radius: 6px; - color: var(--ink-tertiary); - background: var(--surface-raised); -} - -.search-field svg { - width: 14px; - height: 14px; -} - -.search-field input { - min-width: 0; - flex: 1; - border: 0; - outline: 0; - color: var(--ink); - background: transparent; - font-size: 12px; -} - -.search-field input::placeholder { - color: var(--ink-muted); - opacity: 1; -} - -.sidebar-section { - padding: 6px 7px 0; -} - -.section-label { - min-height: 24px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 7px; - color: var(--ink-muted); - font-size: 11px; - font-weight: 600; -} - -.thread { - width: 100%; - min-height: 38px; - display: grid; - grid-template-columns: 15px minmax(0, 1fr) auto; - align-items: center; - gap: 7px; - border: 0; - border-radius: 6px; - padding: 0 8px; - color: var(--ink-secondary); - background: transparent; - text-align: left; -} - -.thread:hover { - color: var(--ink); - background: var(--surface-muted); -} - -.thread.active { - color: var(--ink); - background: var(--selection-field); - box-shadow: inset 2px 0 0 var(--brand); -} - -.thread.attention { - background: transparent; - box-shadow: none; -} - -.thread-state { - width: 15px; - height: 15px; - display: grid; - place-items: center; - color: var(--ink-muted); -} - -.thread-state.live { - width: 13px; - height: 13px; - justify-self: center; - border: 1.5px solid color-mix(in srgb, var(--ink-muted) 34%, transparent); - border-top-color: var(--signal); - border-right-color: var(--signal); - border-radius: 50%; - background: transparent; - animation: thread-spin 1.1s linear infinite; -} - -.thread.active .thread-state.live { - background: transparent; - box-shadow: none; -} - -.thread-state.permission, -.thread-state.question { - color: var(--warning); -} - -.thread-state.done { - color: var(--success); -} - -.thread-state.automation { - color: var(--ink-muted); -} - -.thread-state svg { - width: 14px; - height: 14px; -} - -.thread-copy { - min-width: 0; - overflow: hidden; -} - -.thread-copy strong { - overflow: hidden; - display: block; - font-size: 12px; - font-weight: 600; - white-space: nowrap; - text-overflow: ellipsis; -} - -.thread-tail { - min-width: 0; - display: flex; - align-items: center; - justify-content: flex-end; - gap: 6px; -} - -.thread-time { - color: var(--ink-muted); - font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; - font-size: 10px; - font-variant-numeric: tabular-nums; -} - -.thread-goal { - width: 14px; - height: 14px; - display: grid; - place-items: center; - color: var(--brand); -} - -.thread-goal svg { - width: 13px; - height: 13px; -} - -.thread-attention-tag { - min-height: 18px; - display: inline-flex; - align-items: center; - border: 1px solid color-mix(in srgb, var(--warning) 34%, transparent); - border-radius: 4px; - padding: 0 5px; - color: var(--warning); - background: var(--warning-field); - font-size: 10px; - font-weight: 700; - line-height: 1; -} - -.history { - overflow: auto; -} - -@container session-sidebar (max-width: 260px) { - .thread { - gap: 6px; - padding-inline: 7px; - } - - .thread-time { - display: none; - } -} - -.sidebar-footer { - min-height: 38px; - display: grid; - grid-template-columns: 8px minmax(0, 1fr) auto; - align-items: center; - gap: 7px; - margin-top: auto; - padding: 0 14px; - border-top: 1px solid var(--border-soft); - color: var(--ink-tertiary); - font-size: 11px; -} - -.sidebar-footer > :last-child { - color: var(--ink-muted); -} - -.connection-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--success); -} - -.work-canvas { - min-width: 0; - position: relative; - display: flex; - flex-direction: column; - overflow: hidden; - background: var(--canvas); -} - -.work-header { - min-height: var(--header-height); - display: flex; - align-items: center; - gap: 10px; - padding: 8px 14px 8px 18px; - background: var(--surface); - border-bottom: 1px solid var(--border); -} - -.work-title { - min-width: 0; - flex: 1; -} - -.work-title-line, -.work-title-meta { - min-width: 0; - display: flex; - align-items: center; -} - -.work-title-line { - gap: 8px; -} - -.work-title-line > strong { - min-width: 0; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - font-size: 18px; - font-weight: 650; - letter-spacing: -0.02em; - line-height: 24px; -} - -.session-status { - height: 22px; - display: inline-flex; - flex: 0 0 auto; - align-items: center; - gap: 6px; - padding: 0 8px; - border-left: 2px solid var(--signal); - color: var(--signal-foreground); - background: var(--signal-field); - font-size: 12px; - font-weight: 650; -} - -.session-status-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--signal); -} - -.work-title-meta { - gap: 8px; - margin-top: 3px; - overflow: hidden; - color: var(--ink-tertiary); - font-size: 12px; - white-space: nowrap; -} - -.work-title-meta > span { - flex: 0 0 auto; -} - -.work-title-meta .header-cwd { - min-width: 0; - max-width: 420px; - overflow: hidden; - flex: 1 100 220px; - font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; - text-overflow: ellipsis; -} - -.work-title-meta .header-usage { - flex: 0 0 auto; - color: var(--ink-secondary); - font-variant-numeric: tabular-nums; -} - -.work-title-meta .header-source { - min-width: 0; - overflow: hidden; - flex: 1 1 195px; - text-overflow: ellipsis; -} - -.work-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.theme-switch { - display: flex; - padding: 2px; - border: 1px solid var(--border); - border-radius: 7px; - background: var(--surface-muted); -} - -.theme-switch button { - min-height: 28px; - border: 0; - border-radius: 5px; - padding: 0 9px; - color: var(--ink-tertiary); - background: transparent; - font-size: 11px; -} - -.theme-switch button[aria-pressed="true"] { - color: var(--ink); - background: var(--surface-raised); - box-shadow: 0 1px 2px rgb(36 36 31 / 8%); -} - -.secondary-button, -.primary-button { - min-height: 34px; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 7px; - border-radius: 6px; - padding: 0 10px; - font-size: 11px; - font-weight: 600; -} - -.secondary-button { - border: 1px solid var(--border); - color: var(--ink-secondary); - background: var(--surface-raised); -} - -.inspector-trigger { - width: 32px; - min-height: 32px; - padding: 0; -} - -.secondary-button:hover { - color: var(--ink); - border-color: var(--border-strong); -} - -.button-count { - min-width: 18px; - padding: 2px 5px; - border-radius: 999px; - color: var(--brand); - background: var(--brand-field); - font-size: 10px; -} - -.conversation { - min-height: 0; - flex: 1; - overflow: auto; - padding: 28px 26px 40px; - scrollbar-color: var(--border-strong) transparent; -} - -.task-heading { - padding-bottom: 30px; - border-bottom: 1px solid var(--border); -} - -.task-meta { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px 12px; - margin-bottom: 14px; - color: var(--ink-tertiary); - font-size: 10px; -} - -.agent-chip { - display: inline-flex; - align-items: center; - gap: 6px; - color: var(--brand); - font-weight: 650; -} - -.mini-mark { - width: 20px; - height: 20px; - display: grid; - place-items: center; - border-radius: 5px; - color: var(--brand-ink); - background: var(--brand); - font-size: 9px; -} - -.task-heading h1 { - max-width: 720px; - margin: 0 0 10px; - font-size: clamp(22px, 2.2vw, 30px); - font-weight: 650; - letter-spacing: -0.03em; - line-height: 1.22; -} - -.task-heading h1 code { - color: var(--brand); - font-family: inherit; - font-weight: inherit; -} - -.task-heading > p { - max-width: 68ch; - margin: 0; - color: var(--ink-secondary); - font-size: 13px; - line-height: 1.65; -} - -.user-turn { - min-width: 0; - margin-bottom: 16px; -} - -.message { - max-width: 660px; - margin: 0 0 0 auto; - padding: 14px 16px; - border: 0; - border-radius: 8px 8px 3px 8px; - color: var(--ink); - background: var(--surface-muted); - box-shadow: none; -} - -.message p { - margin: 0; - font-size: 15px; - line-height: 1.66; -} - -.message-time { - display: block; - margin-top: 7px; - color: var(--ink-tertiary); - font-size: 12px; - text-align: right; -} - -.agent-response { - width: 100%; - max-width: none; - margin-bottom: 36px; -} - -.work-disclosure { - margin: 0 0 18px; - border: 0; -} - -.work-summary { - width: fit-content; - max-width: 100%; - min-height: 32px; - display: flex; - align-items: center; - justify-content: flex-start; - gap: 8px; - border: 0; - border-radius: 5px; - padding: 3px 6px 3px 0; - color: var(--ink-tertiary); - background: transparent; - text-align: left; -} - -.work-summary:hover { - color: var(--ink-secondary); - background: var(--surface-muted); -} - -.work-disclosure.running .work-summary { - color: var(--ink-secondary); - background: transparent; - box-shadow: none; -} - -.work-disclosure.running .work-summary:hover { - background: var(--surface-muted); -} - -.work-summary-primary { - display: flex; - align-items: center; - gap: 7px; -} - -.work-summary-primary { - min-width: 0; -} - -.work-summary-copy { - min-width: 0; - display: block; -} - -.work-summary-copy strong { - color: inherit; - font-size: 12px; - font-weight: 600; - line-height: 1.2; -} - -.work-summary-current { - min-width: 0; - overflow: hidden; - color: var(--ink-tertiary); - font-size: 12px; - line-height: 1.2; - text-overflow: ellipsis; - white-space: nowrap; -} - -.work-summary-current::before { - margin-right: 7px; - color: var(--border-strong); - content: "—"; -} - -.work-chevron { - flex: 0 0 auto; - color: var(--ink-muted); - transition: transform 160ms ease; -} - -.work-chevron svg { - width: 13px; - height: 13px; -} - -.work-summary[aria-expanded="true"] .work-chevron { - transform: rotate(90deg); -} - -.live-pulse { - width: 7px; - height: 7px; - margin-inline: 1px; - position: relative; - flex: 0 0 auto; - border-radius: 50%; - background: var(--signal); -} - -.live-pulse::after { - display: none; -} - -.elapsed { - color: var(--ink-secondary); - font-size: inherit; - font-variant-numeric: tabular-nums; -} - -.work-body[hidden] { - display: none; -} - -.work-body { - position: relative; - padding: 8px 2px 6px 20px; -} - -.work-body::before { - display: none; -} - -.work-commentary { - max-width: 65ch; - margin: 10px 0 4px; - color: var(--ink-secondary); - font-size: 13px; - line-height: 1.62; -} - -.execution-list { - margin: 0; - padding: 0; - list-style: none; -} - -.execution-list li { - min-height: 58px; - display: grid; - grid-template-columns: 26px minmax(0, 1fr) auto; - align-items: center; - gap: 10px; - padding: 10px 0; -} - -.step-glyph { - width: 20px; - height: 20px; - display: grid; - place-items: center; - border-radius: 50%; - color: var(--success); - background: var(--surface); -} - -.step-glyph svg { - width: 13px; - height: 13px; -} - -.execution-list li strong { - display: block; - margin-bottom: 4px; - font-size: 13px; - font-weight: 600; -} - -.execution-list li p { - margin: 0; - color: var(--ink-secondary); - font-size: 12px; - line-height: 1.55; -} - -.execution-list li.compact-step { - min-height: 38px; - grid-template-columns: 20px minmax(0, 1fr); - gap: 8px; - padding: 5px 0; -} - -.execution-list li.compact-step .step-glyph { - width: 18px; - height: 18px; -} - -.execution-list li.compact-step > div { - min-width: 0; - display: flex; - align-items: baseline; - gap: 7px; -} - -.execution-list li.compact-step strong { - flex: 0 0 auto; - margin: 0; - color: var(--ink-secondary); - font-size: 12px; -} - -.execution-list li.compact-step p { - overflow: hidden; - color: var(--ink-tertiary); - font-size: 12px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.execution-list li.tool-run-event { - min-height: 0; - display: block; - padding: 7px 0; -} - -.execution-list li.work-commentary-event { - min-height: 0; - display: block; - padding: 10px 0; -} - -.execution-list li.work-commentary-event p { - max-width: 65ch; - color: var(--ink-secondary); - font-size: 14px; - line-height: 1.66; -} - -.tool-run-card { - overflow: visible; - border: 0; - background: transparent; -} - -.tool-run-toggle { - width: 100%; - min-height: 30px; - display: grid; - grid-template-columns: 12px minmax(0, 1fr); - align-items: center; - gap: 6px; - border-radius: 5px; - padding: 3px 6px 3px 0; - border: 0; - color: var(--ink-secondary); - background: transparent; - text-align: left; -} - -.tool-run-toggle:hover { - background: var(--surface-hover); -} - -.tool-run-chevron { - color: var(--ink-muted); - transition: transform 160ms ease; -} - -.tool-run-chevron svg { - width: 10px; - height: 10px; -} - -.tool-run-toggle[aria-expanded="true"] .tool-run-chevron { - transform: rotate(90deg); -} - -.tool-run-names { - overflow: hidden; - min-width: 0; - color: var(--ink-secondary); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 12.5px; - font-weight: 550; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tool-run-list[hidden] { - display: none; -} - -.tool-run-list { - display: flex; - flex-direction: column; - margin: 0 0 0 5px; - padding: 4px 0 0 13px; - border-left: 1px solid var(--border-soft); -} - -.reasoning-block { - min-width: 0; - padding: 7px 0; -} - -.reasoning-toggle { - width: fit-content; - max-width: 100%; - min-height: 32px; - display: flex; - align-items: center; - gap: 7px; - border: 0; - border-radius: 5px; - padding: 3px 6px 3px 0; - color: var(--ink-tertiary); - background: transparent; - text-align: left; -} - -.reasoning-toggle:hover { - color: var(--ink-secondary); - background: var(--surface-hover); -} - -.reasoning-chevron { - color: var(--ink-muted); - transition: transform 160ms ease; -} - -.reasoning-toggle[aria-expanded="true"] .reasoning-chevron { - transform: rotate(90deg); -} - -.reasoning-body { - max-width: 72ch; - margin: 0 0 0 5px; - padding: 7px 8px 8px 13px; - border-left: 1px solid var(--border-soft); - color: var(--ink-secondary); - font-size: 13px; - line-height: 1.62; -} - -.reasoning-body[hidden] { - display: none; -} - -.tool-child { - overflow: hidden; - border: 0; - border-bottom: 1px solid var(--border-soft); - background: transparent; -} - -.tool-child:last-child { - border-bottom: 0; -} - -.tool-child-row { - width: 100%; - min-height: 36px; - display: grid; - grid-template-columns: minmax(104px, auto) minmax(0, 1fr); - align-items: center; - gap: 12px; - padding: 6px 8px; -} - -.tool-child-row code { - color: var(--ink-tertiary); - font-size: 12px; - font-weight: 600; - white-space: nowrap; -} - -.tool-child-target { - overflow: hidden; - color: var(--ink-secondary); - font-size: 13px; - font-weight: 500; - text-overflow: ellipsis; - white-space: nowrap; -} - -.execution-list li.tool-call-event { - min-height: 0; - display: block; - padding: 8px 0; -} - -.tool-call-card { - overflow: visible; - border: 0; - border-radius: 0; - background: transparent; -} - -.tool-call-toggle { - width: 100%; - min-height: 36px; - display: grid; - grid-template-columns: 12px auto minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - border-radius: 6px; - padding: 4px 6px 4px 0; - border: 0; - color: var(--ink-secondary); - background: transparent; - text-align: left; -} - -.tool-call-toggle:hover { - background: var(--surface-hover); -} - -.tool-call-toggle code { - color: var(--ink-tertiary); - font-size: 12px; - font-weight: 600; - white-space: nowrap; -} - -.tool-call-summary { - overflow: hidden; - color: var(--ink); - font-size: 13px; - font-weight: 500; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tool-call-diff { - font-size: 12px; - white-space: nowrap; -} - -.tool-call-diff { - color: var(--ink-muted); - font-family: "SFMono-Regular", Consolas, monospace; -} - -.tool-call-chevron { - color: var(--ink-muted); - transition: transform 160ms ease; -} - -.tool-call-chevron svg { - width: 10px; - height: 10px; -} - -.tool-call-toggle[aria-expanded="true"] .tool-call-chevron { - transform: rotate(90deg); -} - -.shell-toggle { - grid-template-columns: 12px auto minmax(0, 1fr); -} - -.tool-call-command { - min-width: 0; - display: flex; - align-items: baseline; - gap: 7px; - overflow: hidden; - color: var(--signal-foreground); -} - -.tool-call-command > span { - flex: none; - color: var(--signal-foreground); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 11px; -} - -.tool-call-command code { - overflow: hidden; - color: var(--ink-secondary); - font-size: 12.5px; - font-weight: 500; - text-overflow: ellipsis; -} - -.tool-call-detail[hidden] { - display: none; -} - -.tool-call-detail { - overflow: hidden; - border-top: 1px solid var(--border-soft); - border-radius: 0 0 6px 6px; - background: var(--surface); -} - -.terminal-detail { - margin: 2px 0 0 18px; - border: 0; - border-radius: 5px; - color: var(--terminal-text); - background: var(--terminal-bg); -} - -[data-theme="dark"] .terminal-detail { - color: var(--terminal-text); - background: var(--terminal-bg); -} - -.terminal-detail pre { - margin: 0; - padding: 10px 12px 8px; - overflow-x: auto; - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 12px; - line-height: 1.65; -} - -.terminal-detail pre span { - display: block; - min-width: max-content; -} - -.terminal-meta { - min-height: 30px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 6px 12px; - border-top: 1px solid rgb(255 255 255 / 10%); - color: var(--terminal-muted); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 11px; -} - -.terminal-meta span:first-child { - color: var(--terminal-success); -} - -.tool-diff-preview { - margin: 2px 0 0 18px; - overflow: hidden; - border: 1px solid var(--border-soft); - border-radius: 5px; - background: var(--surface-raised); -} - -.tool-diff-preview header { - min-height: 30px; - display: grid; - grid-template-columns: 14px minmax(0, 1fr) auto; - align-items: center; - gap: 7px; - padding: 5px 8px; - border-bottom: 1px solid var(--border-soft); - color: var(--ink-muted); -} - -.tool-diff-preview header svg { - width: 12px; - height: 12px; -} - -.execution-list .tool-diff-preview header strong { - overflow: hidden; - display: block; - margin: 0; - color: var(--ink-secondary); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 12px; - font-weight: 650; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tool-diff-preview header > span:last-child { - color: var(--ink-muted); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 12px; -} - -.tool-diff-preview pre { - max-width: 100%; - margin: 0; - overflow: auto; - color: var(--ink-secondary); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 12px; - line-height: 1.65; -} - -.tool-diff-preview pre span { - display: block; - min-width: max-content; - padding: 1px 9px; -} - -.diff-remove { - color: var(--danger); - background: var(--danger-field); -} - -.diff-add { - color: var(--success); - background: var(--success-field); -} - -.delegation-glyph { - color: var(--brand); - background: var(--brand-field); -} - -.active-tool-line { - min-height: 34px; - display: flex; - align-items: center; - gap: 8px; - margin-top: 7px; - color: var(--ink-secondary); - font-size: 13px; -} - -.active-tool-indicator { - width: 7px; - height: 7px; - flex: 0 0 auto; - border-radius: 50%; - background: var(--signal); -} - -.active-tool-line code { - flex: 0 0 auto; - color: var(--ink-tertiary); - font-size: 12px; - font-weight: 600; -} - -.active-tool-line > span:last-child { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.final-response { - width: 100%; - max-width: none; - margin-top: 22px; - color: var(--ink); - font-size: 16px; - line-height: 1.68; -} - -.final-response > :where(p, h1, h2, h3, h4, h5, h6, ul, ol, blockquote) { - max-width: 72ch; -} - -.final-response p { - margin: 0 0 12px; -} - -.final-response > p:first-child { - font-weight: 600; - letter-spacing: -0.01em; -} - -.final-response ul { - margin: 0; - padding-left: 20px; -} - -.final-response li + li { - margin-top: 7px; -} - -.final-response code { - color: var(--brand); - font-family: "SFMono-Regular", Consolas, monospace; - font-size: 0.92em; -} - -.response-meta { - margin-top: 14px; - color: var(--ink-tertiary); - font-size: 12px; -} - -.follow-up-message { - margin-top: 0; -} - -.active-turn { - padding-bottom: 8px; -} - -.session-composer-dock { - min-height: 0; - flex: 0 0 auto; - max-height: min(60dvh, 640px); - padding: 0; - border-top: 1px solid var(--border); - background: var(--surface); -} - -.composer-rail { - width: 100%; - margin: 0; - padding: 8px 26px 12px; -} - -.composer-attention { - display: grid; - grid-template-columns: 25px minmax(0, 1fr) auto; - align-items: start; - gap: 10px; - margin-bottom: 8px; - padding: 12px; - border: 1px solid color-mix(in srgb, var(--warning) 40%, var(--border)); - border-left: 3px solid var(--warning); - border-radius: 6px; - background: var(--attention-field); -} - -.composer-status-mark { - width: 24px; - height: 24px; - display: grid; - place-items: center; - border-radius: 6px; - font-size: 9px; - font-weight: 800; -} - -.goal-mark { - color: var(--brand-ink); - background: var(--brand); -} - -.attention-mark { - color: var(--warning-field); - background: var(--warning); -} - -.composer-status-copy { - min-width: 0; -} - -.composer-status-copy > strong { - display: block; - margin-top: 2px; - color: var(--ink); - font-size: 14px; - font-weight: 600; - line-height: 1.35; -} - -.attention-type { - display: block; - color: var(--ink-muted); - font-size: 11px; - letter-spacing: 0.06em; - text-transform: uppercase; -} - -.composer-status-copy dl { - display: grid; - gap: 4px; - margin: 10px 0 0; - color: var(--ink-secondary); - font-size: 12px; -} - -.composer-status-copy dl div { - display: grid; - grid-template-columns: 62px minmax(0, 1fr); - gap: 8px; -} - -.composer-status-copy dt, -.composer-status-copy dd { - min-width: 0; - margin: 0; -} - -.composer-status-copy dt { - color: var(--ink-tertiary); -} - -.composer-status-copy dd { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.composer-status-copy code { - font-size: 11px; -} - -.attention-actions button, -.composer-goal-row > button, -.composer-queue > button { - min-height: 32px; - border: 1px solid var(--border); - border-radius: 4px; - padding: 0 10px; - color: var(--ink-secondary); - background: transparent; - font-size: 12px; - font-weight: 600; -} - -.attention-actions button:hover, -.composer-goal-row > button:hover, -.composer-queue > button:hover { - color: var(--ink); - border-color: var(--border-strong); - background: var(--surface-hover); -} - -.attention-actions { - display: flex; - flex-wrap: wrap; - gap: 5px; -} - -.attention-actions .attention-primary { - color: var(--canvas); - border-color: var(--ink); - background: var(--ink); -} - -.composer-goal-row { - min-width: 0; - min-height: 40px; - display: flex; - align-items: center; - gap: 8px; - padding: 7px 4px; - border-bottom: 1px solid var(--border-soft); -} - -.composer-goal-row .composer-status-mark { - width: 20px; - height: 20px; -} - -.goal-label { - color: var(--ink-muted); - font-size: 10px; - font-weight: 650; - letter-spacing: 0.08em; - text-transform: uppercase; -} - -.composer-goal-row > strong { - color: var(--success); - font-size: 11px; - font-weight: 650; -} - -.goal-objective { - min-width: 48px; - overflow: hidden; - flex: 1; - color: var(--ink); - font-size: 12px; - font-weight: 550; - text-overflow: ellipsis; - white-space: nowrap; -} - -.goal-metrics, -.queue-model { - flex: 0 0 auto; - color: var(--ink-tertiary); - font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; - font-size: 10px; - white-space: nowrap; -} - -.composer-goal-row > button, -.composer-queue > button { - min-height: 28px; - padding-inline: 8px; - font-size: 11px; -} - -.composer-queue { - min-height: 36px; - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto auto; - align-items: center; - gap: 8px; - margin: 0; - padding: 6px 4px; - border: 0; - border-bottom: 1px solid var(--border-soft); - border-radius: 0; - background: transparent; -} - -.composer-queue p { - margin: 0; - color: var(--ink-secondary); - font-size: 12px; - line-height: 1.5; -} - -.queue-count { - display: inline-flex; - align-items: center; - gap: 4px; - color: var(--ink-secondary); - font-size: 11px; - font-weight: 700; -} - -.queue-count::before { - width: 7px; - height: 7px; - border: 1px solid var(--brand); - border-radius: 50%; - content: ""; -} - -.composer-input-slot { - flex: 0 0 auto; - padding-top: 8px; -} - -.composer-card { - overflow: visible; - border: 1px solid var(--border); - border-radius: 12px; - background: var(--surface-raised); - box-shadow: 0 4px 14px rgb(34 33 28 / 9%); -} - -.composer-card textarea { - width: 100%; - min-height: 48px; - resize: none; - border: 0; - padding: 12px 16px 8px; - outline: 0; - color: var(--ink); - background: transparent; - font-size: 13px; - line-height: 1.55; -} - -.composer-card textarea::placeholder { - color: var(--ink-muted); - opacity: 1; -} - -.composer-foot { - min-height: 38px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 0 12px 8px; -} - -.composer-context { - min-width: 0; - display: flex; - align-items: center; - gap: 4px; - color: var(--ink-tertiary); - font-size: 11px; -} - -.composer-context button { - min-height: 28px; - display: flex; - align-items: center; - gap: 5px; - border: 0; - border-radius: 5px; - padding: 0 7px; - color: var(--ink-tertiary); - background: transparent; - font-size: 11px; -} - -.composer-context button:hover { - color: var(--ink); - background: var(--surface-muted); -} - -.composer-context svg { - width: 11px; - height: 11px; -} - -.agent-dot { - width: 7px; - height: 7px; - border-radius: 50%; - background: var(--brand); -} - -.composer-divider { - width: 1px; - height: 12px; - margin-inline: 4px; - background: var(--border); -} - -.composer-runtime-status { - display: inline-flex; - align-items: center; - gap: 6px; - white-space: nowrap; -} - -.attention-dot { - width: 7px; - height: 7px; - border-radius: 50%; - background: var(--warning); -} - -.composer-actions { - display: flex; - align-items: center; - gap: 6px; -} - -.composer-hint { - margin-right: 6px; - color: var(--ink-tertiary); - font-size: 11px; - white-space: nowrap; -} - -.stop-button { - width: 32px; - height: 32px; - display: grid; - place-items: center; - border: 1px solid color-mix(in srgb, var(--danger) 50%, var(--border)); - border-radius: 8px; - color: var(--danger); - background: var(--danger-field); -} - -.stop-button:hover { - border-color: var(--danger); -} - -.queue-button { - width: 32px; - min-height: 32px; - display: inline-flex; - align-items: center; - justify-content: center; - border: 0; - border-radius: 8px; - padding: 0; - color: var(--brand-ink); - background: var(--brand); - font-size: 11px; - font-weight: 700; -} - -.queue-button:hover { - background: var(--brand-hover); -} - -.stop-button svg, -.queue-button svg { - width: 15px; - height: 15px; -} - -.changes-inspector { - min-width: 0; - position: relative; - display: flex; - flex-direction: column; - overflow: hidden; - background: var(--surface); - border-left: 1px solid var(--border); -} - -.inspector-head { - min-height: var(--header-height); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 12px 0 15px; - border-bottom: 1px solid var(--border); -} - -.inspector-head strong, -.inspector-head span { - display: block; -} - -.inspector-head strong { - margin-bottom: 2px; - font-size: 13px; -} - -.inspector-head span { - color: var(--ink-muted); - font-size: 11px; -} - -.inspector-close { - display: none; -} - -.inspector-panel { - min-height: 0; - flex: 1; - overflow-y: auto; -} - -.inspector-panel.active { - display: flex; - flex-direction: column; -} - -.agent-tree { - padding: 9px 7px; -} - -.agent-row { - width: 100%; - min-height: 56px; - position: relative; - display: grid; - grid-template-columns: 26px minmax(0, 1fr) auto; - grid-template-rows: auto auto; - align-items: center; - gap: 2px 8px; - border: 0; - border-radius: 6px; - padding: 7px 8px; - color: var(--ink-secondary); - background: transparent; - text-align: left; -} - -.agent-row:hover { - color: var(--ink); - background: var(--surface-hover); -} - -.agent-row.active { - color: var(--ink); - background: var(--selection-field); - box-shadow: inset 2px 0 0 var(--brand); -} - -.agent-row.child { - width: calc(100% - 18px); - margin-left: 18px; -} - -.tree-line { - position: absolute; - top: -10px; - bottom: 50%; - left: -8px; - width: 13px; - border-bottom: 1px solid var(--border-strong); - border-left: 1px solid var(--border-strong); - border-radius: 0 0 0 6px; -} - -.root-line { - display: none; -} - -.agent-role { - width: 25px; - height: 25px; - grid-row: 1 / 3; - display: grid; - place-items: center; - border-radius: 6px; - color: #fff; - font-size: 10px; - font-weight: 750; -} - -.lead-role { - background: var(--brand); -} - -.analyst-role { - background: #557893; -} - -.build-role { - color: #102421; - background: #6fb7a8; -} - -.agent-row-copy { - min-width: 0; -} - -.agent-row-copy strong, -.agent-row-copy small { - overflow: hidden; - display: block; - white-space: nowrap; - text-overflow: ellipsis; -} - -.agent-row-copy strong { - margin-bottom: 3px; - font-size: 12px; -} - -.agent-row-copy small, -.agent-profile { - color: var(--ink-muted); - font-size: 10px; -} - -.agent-profile { - align-self: end; - justify-self: start; - font-family: "SFMono-Regular", Consolas, monospace; -} - -.agent-status { - grid-column: 3; - grid-row: 1 / 3; - display: inline-flex; - align-items: center; - gap: 4px; - color: var(--ink-muted); - font-size: 10px; - font-weight: 650; -} - -.agent-status.running { - color: var(--ink-muted); -} - -[data-theme="dark"] .agent-status.running { - color: var(--ink-muted); -} - -.agent-status.running > span { - width: 7px; - height: 7px; - border-radius: 50%; - background: var(--ink-muted); -} - -.agent-row.active .agent-status.running { - color: var(--signal-foreground); -} - -.agent-row.active .agent-status.running > span { - background: var(--signal); -} - -.agent-status.complete { - color: var(--ink-muted); -} - -.agent-status.complete svg { - width: 11px; - height: 11px; -} - -.change-summary { - min-height: 72px; - display: grid; - grid-template-columns: repeat(3, 1fr); - align-items: center; - border-bottom: 1px solid var(--border-soft); -} - -.change-summary div { - padding-left: 15px; - border-left: 1px solid var(--border-soft); -} - -.change-summary div:first-child { - border-left: 0; -} - -.change-summary strong, -.change-summary span { - display: block; -} - -.change-summary strong { - margin-bottom: 3px; - font-size: 13px; - font-weight: 650; -} - -.change-summary span { - color: var(--ink-muted); - font-size: 9px; -} - -.insertions strong { - color: var(--success); -} - -.deletions strong { - color: var(--danger); -} - -.inspector-section-head { - min-height: 42px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 13px; - color: var(--ink-secondary); - font-size: 10px; -} - -.inspector-section-head button { - border: 0; - color: var(--brand); - background: transparent; - font-size: 9px; -} - -.file-list { - min-height: 0; - flex: 1; - overflow: auto; - border-top: 1px solid var(--border-soft); -} - -.file-row { - width: 100%; - min-height: 52px; - display: grid; - grid-template-columns: 18px minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - border: 0; - border-bottom: 1px solid var(--border-soft); - padding: 7px 12px; - color: var(--ink-secondary); - background: transparent; - text-align: left; -} - -.file-row:hover, -.file-row.active { - color: var(--ink); - background: var(--surface-muted); -} - -.file-row > span:nth-child(2) { - min-width: 0; -} - -.file-row strong, -.file-row small { - overflow: hidden; - display: block; - white-space: nowrap; - text-overflow: ellipsis; -} - -.file-row strong { - margin-bottom: 3px; - font-size: 10px; -} - -.file-row small { - color: var(--ink-muted); - font-size: 8px; -} - -.file-row svg { - width: 14px; - height: 14px; -} - -.file-stat { - color: var(--ink-tertiary); - font-size: 8px; -} - -.full-diff-button { - min-height: 36px; - margin: 12px; - border: 1px solid var(--border); - border-radius: 6px; - color: var(--ink-secondary); - background: var(--surface-raised); - font-size: 9px; - font-weight: 650; -} - -.full-diff-button:hover { - color: var(--ink); - border-color: var(--border-strong); - background: var(--surface-muted); -} - -.context-panel { - padding: 13px; -} - -.context-section { - padding: 13px 0; - border-bottom: 1px solid var(--border-soft); -} - -.context-section:first-child { - padding-top: 0; -} - -.context-section h2 { - margin: 0 0 8px; - color: var(--ink-muted); - font-size: 9px; - font-weight: 650; -} - -.context-section > code { - overflow-wrap: anywhere; - color: var(--ink-secondary); - font-size: 9px; - line-height: 1.55; -} - -.context-section > p { - margin: 0; - color: var(--ink); - font-size: 10px; -} - -.context-section > p span { - color: var(--ink-tertiary); -} - -.context-section dl { - margin: 0; -} - -.context-section dl div { - min-height: 30px; - display: flex; - align-items: center; - justify-content: space-between; - border-top: 1px solid var(--border-soft); - font-size: 9px; -} - -.context-section dl div:first-child { - border-top: 0; -} - -.context-section dt { - color: var(--ink-tertiary); -} - -.context-section dd { - margin: 0; - color: var(--ink); - font-family: "SFMono-Regular", Consolas, monospace; -} - -.mini-diff { - padding: 8px 0; - border-bottom: 1px solid var(--border); - background: var(--surface-raised); - font-size: 8px; -} - -.mini-diff div { - display: grid; - grid-template-columns: 18px minmax(0, 1fr); - padding: 3px 11px; - white-space: nowrap; -} - -.mini-diff code { - overflow: hidden; - text-overflow: ellipsis; -} - -.mini-diff .removed { - color: var(--danger); - background: var(--danger-field); -} - -.mini-diff .added { - color: var(--success); - background: var(--success-field); -} - -.review-gate { - padding: 14px; - border-top: 1px solid var(--border); -} - -.gate-label { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 7px; - color: var(--warning); - font-size: 10px; -} - -.wait-glyph { - width: 22px; - height: 22px; - display: grid; - place-items: center; - border-radius: 6px; - background: var(--warning-field); -} - -.wait-glyph svg { - width: 13px; - height: 13px; -} - -.review-gate p { - margin: 0 0 12px; - color: var(--ink-tertiary); - font-size: 9px; - line-height: 1.5; -} - -.primary-button { - width: 100%; - border: 0; - color: var(--brand-ink); - background: var(--brand); -} - -.primary-button:hover { - background: var(--brand-hover); -} - -.scrim { - position: fixed; - z-index: 20; - inset: 0; - display: none; - background: rgb(12 12 10 / 52%); -} - -.toast { - position: fixed; - z-index: 100; - right: 18px; - bottom: 18px; - max-width: 340px; - padding: 11px 14px; - border-radius: 7px; - color: var(--brand-ink); - background: var(--brand); - box-shadow: var(--shadow-overlay); - font-size: 11px; - opacity: 0; - pointer-events: none; - transform: translateY(8px); - transition: - opacity 160ms ease, - transform 160ms cubic-bezier(0.16, 1, 0.3, 1); -} - -.toast.visible { - opacity: 1; - transform: translateY(0); -} - -@keyframes pulse { - from { - opacity: 0.85; - transform: scale(0.65); - } - to { - opacity: 0; - transform: scale(1.35); - } -} - -@keyframes thread-spin { - to { - transform: rotate(1turn); - } -} - -@keyframes blink { - 0%, - 52% { - opacity: 1; - } - 53%, - 100% { - opacity: 0; - } -} - -.canvas-view { - min-width: 0; - min-height: 0; - height: 100%; - position: relative; - display: flex; - flex-direction: column; -} - -.canvas-view[hidden] { - display: none; -} - -.project-links button.active { - color: var(--ink); - border-color: color-mix(in srgb, var(--brand) 34%, var(--border)); - background: var(--selection-field); - box-shadow: inset 2px 0 0 var(--brand); -} - -body.non-session-view .changes-inspector { - display: none; -} - -body.non-session-view .app-shell { - grid-template-columns: var(--rail-width) var(--sidebar-width) minmax(440px, 1fr); -} - -body.non-session-view.sidebar-collapsed .app-shell { - grid-template-columns: var(--rail-width) minmax(440px, 1fr); -} - -.page-work-header { - min-height: 58px; -} - -.dashboard-scroll, -.todos-scroll { - min-height: 0; - flex: 1; - overflow: auto; - scrollbar-color: var(--border-strong) transparent; -} - -.dashboard-content { - width: min(1100px, 100%); - display: flex; - flex-direction: column; - gap: 26px; - margin: 0 auto; - padding: 30px 32px 60px; -} - -.dashboard-intro { - display: flex; - align-items: flex-start; - gap: 12px; - padding-bottom: 4px; -} - -.page-mark { - width: 32px; - height: 32px; - display: grid; - place-items: center; - border: 1px solid color-mix(in srgb, var(--brand) 28%, var(--border)); - border-radius: 6px; - color: var(--brand); - background: var(--brand-field); -} - -.dashboard-intro h1, -.dashboard-intro p, -.dashboard-section h2, -.flat-todo-head h1, -.flat-todo-head p { - margin: 0; -} - -.dashboard-intro h1, -.flat-todo-head h1 { - font-size: 18px; - font-weight: 700; - letter-spacing: -0.02em; -} - -.dashboard-intro p, -.flat-todo-head p { - margin-top: 4px; - color: var(--ink-tertiary); - font-size: 11px; -} - -.dashboard-section { - min-width: 0; -} - -.dashboard-section-head { - min-height: 30px; - display: flex; - align-items: center; - gap: 8px; - padding: 0 2px 8px; - border-bottom: 1px solid var(--border); -} - -.dashboard-section-head h2 { - color: var(--ink); - font-size: 12px; - font-weight: 700; -} - -.section-count { - min-width: 20px; - padding: 2px 6px; - border-radius: 999px; - color: var(--ink-tertiary); - background: var(--surface-muted); - font-size: 9px; - font-weight: 650; - text-align: center; -} - -.live-count { - color: color-mix(in srgb, var(--signal) 68%, var(--ink) 32%); - background: var(--signal-field); -} - -.status-orbit { - width: 9px; - height: 9px; - display: inline-block; - flex: 0 0 auto; - border: 1.5px solid currentColor; - border-radius: 50%; -} - -.status-orbit.warning { - color: var(--warning); -} - -.status-orbit.brand { - color: var(--brand); -} - -.status-orbit.success { - color: var(--success); -} - -.status-orbit.quiet { - color: var(--border-strong); -} - -.status-orbit.live { - position: relative; - border-color: var(--signal); - background: var(--signal); -} - -.status-orbit.live::after { - position: absolute; - inset: -4px; - border: 1px solid color-mix(in srgb, var(--signal) 48%, transparent); - border-radius: 50%; - content: ""; -} - -.dashboard-list { - display: flex; - flex-direction: column; -} - -.dashboard-row { - width: 100%; - min-height: 64px; - display: grid; - grid-template-columns: 12px minmax(0, 1fr) auto; - align-items: center; - gap: 13px; - padding: 10px 12px; - border: 0; - border-bottom: 1px solid var(--border-soft); - color: var(--ink); - background: transparent; - text-align: left; -} - -.dashboard-row:hover { - background: var(--surface-hover); -} - -.dashboard-row.attention-row { - background: var(--attention-field); - box-shadow: inset 3px 0 0 var(--warning); -} - -.dashboard-row.running-row { - background: var(--running-field); - box-shadow: inset 3px 0 0 var(--signal); -} - -.dashboard-row-copy { - min-width: 0; -} - -.dashboard-row-copy strong, -.dashboard-row-copy small, -.dashboard-row-meta small { - display: block; -} - -.dashboard-row-copy strong { - overflow: hidden; - font-size: 11px; - font-weight: 650; - text-overflow: ellipsis; - white-space: nowrap; -} - -.dashboard-row-copy small { - margin-top: 4px; - overflow: hidden; - color: var(--ink-tertiary); - font-size: 10px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.dashboard-row-meta { - display: flex; - align-items: center; - gap: 14px; - color: var(--brand); - font-size: 10px; - font-weight: 650; - white-space: nowrap; -} - -.dashboard-row-meta small { - color: var(--ink-muted); - font-size: 9px; - font-weight: 500; -} - -.dashboard-row-meta .live-word { - color: color-mix(in srgb, var(--signal) 68%, var(--ink) 32%); - font-weight: 750; - letter-spacing: 0.04em; -} - -.todos-work-header { - align-items: center; -} - -.todo-view-tabs { - display: flex; - gap: 2px; - padding: 2px; - border: 1px solid var(--border); - border-radius: 7px; - background: var(--surface-muted); -} - -.todo-view-tabs button { - min-height: 28px; - padding: 0 10px; - border: 0; - border-radius: 5px; - color: var(--ink-tertiary); - background: transparent; - font-size: 9px; - font-weight: 650; -} - -.todo-view-tabs button:hover { - color: var(--ink); -} - -.todo-view-tabs button[aria-pressed="true"] { - color: var(--brand); - background: var(--surface-raised); - box-shadow: 0 1px 2px rgb(36 36 31 / 8%); -} - -.todos-content { - width: min(1500px, 100%); - margin: 0 auto; - padding: 18px 22px 54px; -} - -.todo-capture { - min-height: 48px; - display: grid; - grid-template-columns: 24px minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - margin-bottom: 18px; - padding: 5px 6px 5px 12px; - border: 1px solid var(--border-strong); - border-radius: 7px; - color: var(--ink-muted); - background: var(--surface-raised); -} - -.todo-capture:focus-within { - border-color: var(--brand); - box-shadow: var(--focus); -} - -.todo-capture input { - min-width: 0; - height: 36px; - border: 0; - color: var(--ink); - background: transparent; - font-size: 11px; - outline: 0; -} - -.todo-capture input::placeholder { - color: var(--ink-muted); -} - -.primary-button { - border: 1px solid var(--brand); - color: var(--brand-ink); - background: var(--brand); -} - -.primary-button:hover { - background: var(--brand-hover); -} - -.todo-panel[hidden] { - display: none; -} - -.board-panel { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - align-items: start; - gap: 12px; -} - -.todo-lane { - min-width: 0; -} - -.todo-lane-head { - display: grid; - grid-template-columns: 10px minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - min-height: 42px; - padding: 0 3px 8px; - border-bottom: 1px solid var(--border); -} - -.todo-lane-head strong, -.todo-lane-head small { - display: block; -} - -.todo-lane-head strong { - font-size: 11px; - font-weight: 700; -} - -.todo-lane-head small, -.todo-lane-head > :last-child { - color: var(--ink-muted); - font-size: 9px; -} - -.todo-lane-head small { - margin-top: 2px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.todo-lane-list { - display: flex; - flex-direction: column; - gap: 8px; - padding-top: 9px; -} - -.todo-card { - width: 100%; - min-height: 128px; - display: flex; - flex-direction: column; - align-items: flex-start; - padding: 12px; - border: 1px solid var(--border); - border-radius: 6px; - color: var(--ink); - background: var(--surface-raised); - text-align: left; -} - -.todo-card:hover, -.todo-card.selected { - border-color: color-mix(in srgb, var(--brand) 48%, var(--border)); - box-shadow: inset 2px 0 0 var(--brand); -} - -.todo-card-state { - display: inline-flex; - align-items: center; - gap: 6px; - color: var(--ink-tertiary); - font-size: 9px; - font-weight: 650; -} - -.todo-card > strong { - margin-top: 9px; - font-size: 11px; - font-weight: 700; - line-height: 1.4; -} - -.todo-card > small { - min-width: 0; - display: flex; - align-items: center; - gap: 5px; - margin-top: 10px; - color: var(--ink-muted); - font-size: 9px; -} - -.todo-card > small svg { - width: 12px; - height: 12px; -} - -.todo-next { - margin-top: auto; - padding-top: 12px; - color: var(--brand); - font-size: 9px; - font-weight: 700; -} - -.flat-todo-panel { - width: min(980px, 100%); - margin: 0 auto; -} - -.flat-todo-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 14px; - padding: 4px 4px 14px; - border-bottom: 1px solid var(--border); -} - -.flat-todo-head > span { - color: var(--ink-muted); - font-size: 9px; -} - -.flat-todo-row { - width: 100%; - min-height: 68px; - display: grid; - grid-template-columns: 12px minmax(0, 1fr) auto; - align-items: center; - gap: 12px; - padding: 10px 8px; - border: 0; - border-bottom: 1px solid var(--border-soft); - color: var(--ink); - background: transparent; - text-align: left; -} - -.flat-todo-row:hover, -.flat-todo-row.selected { - background: var(--surface-hover); - box-shadow: inset 2px 0 0 var(--brand); -} - -.flat-todo-row strong, -.flat-todo-row small { - display: block; -} - -.flat-todo-row strong { - font-size: 11px; - font-weight: 650; -} - -.flat-todo-row small { - margin-top: 4px; - color: var(--ink-muted); - font-size: 9px; -} - -.flat-todo-row > :last-child { - color: var(--brand); - font-size: 9px; - font-weight: 700; - white-space: nowrap; -} - -.todo-detail-scrim { - position: absolute; - z-index: 18; - inset: var(--header-height) 0 0; - display: none; - background: rgb(24 24 20 / 18%); -} - -.todo-detail { - position: absolute; - z-index: 19; - top: var(--header-height); - right: 0; - bottom: 0; - width: min(430px, calc(100% - var(--rail-width))); - border-left: 1px solid var(--border); - color: var(--ink); - background: var(--surface-raised); - box-shadow: var(--shadow-overlay); - transform: translateX(105%); - transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1); -} - -.todos-view.detail-open .todo-detail { - transform: translateX(0); -} - -.todos-view.detail-open .todo-detail-scrim { - display: block; -} - -.todo-detail-head { - min-height: 64px; - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; - padding: 14px 14px 12px 18px; - border-bottom: 1px solid var(--border); -} - -.todo-detail-head strong, -.todo-detail-head span { - display: block; -} - -.todo-detail-head strong { - font-size: 12px; - font-weight: 700; -} - -.todo-detail-head span { - margin-top: 4px; - color: var(--ink-muted); - font-size: 9px; -} - -.todo-detail-body { - height: calc(100% - 64px); - overflow: auto; - padding: 4px 18px 24px; -} - -.todo-detail-body section { - padding: 16px 0; - border-bottom: 1px solid var(--border-soft); -} - -.todo-detail-body h2, -.todo-detail-body p { - margin: 0; -} - -.todo-detail-body h2 { - margin-bottom: 8px; - font-size: 10px; - font-weight: 750; - letter-spacing: 0.02em; -} - -.todo-detail-body p, -.todo-detail-body li { - color: var(--ink-secondary); - font-size: 10px; - line-height: 1.65; -} - -.todo-detail-body ul { - display: flex; - flex-direction: column; - gap: 7px; - margin: 0; - padding-left: 17px; -} - -.todo-detail-body li::marker { - color: var(--border-strong); -} - -.todo-detail-body li.needs-decision::marker { - color: var(--warning); -} - -.todo-detail-actions { - display: flex; - flex-wrap: wrap; - gap: 7px; - padding-top: 16px; -} - -.todo-detail-actions > div { - display: contents; -} - -.todo-detail-actions .secondary-button, -.todo-detail-actions .primary-button { - min-height: 32px; -} - -@media (min-width: 1181px) { - body.inspector-collapsed .app-shell { - grid-template-columns: var(--rail-width) var(--sidebar-width) minmax(440px, 1fr); - } - - body.inspector-collapsed .changes-inspector { - display: none; - } - - body.sidebar-collapsed .app-shell { - grid-template-columns: var(--rail-width) minmax(440px, 1fr) var(--inspector-width); - } - - body.sidebar-collapsed .thread-sidebar { - display: none; - } - - body.sidebar-collapsed .sidebar-expand { - display: inline-grid; - } - - body.sidebar-collapsed.inspector-collapsed .app-shell { - grid-template-columns: var(--rail-width) minmax(440px, 1fr); - } - - body.focus-mode .app-shell { - grid-template-columns: var(--rail-width) minmax(440px, 1fr); - } - - body.focus-mode .thread-sidebar, - body.focus-mode .changes-inspector { - display: none; - } - - body.focus-mode .sidebar-expand { - display: inline-grid; - } -} - -@media (max-width: 1180px) { - .app-shell { - grid-template-columns: var(--rail-width) clamp(210px, var(--sidebar-width), 228px) minmax(420px, 1fr); - } - - body.sidebar-collapsed .app-shell { - grid-template-columns: var(--rail-width) minmax(420px, 1fr); - } - - body.sidebar-collapsed .thread-sidebar { - display: none; - } - - body.sidebar-collapsed .sidebar-expand { - display: inline-grid; - } - - .changes-inspector { - position: fixed; - z-index: 30; - top: 0; - right: 0; - bottom: 0; - width: min(360px, calc(100vw - 70px)); - box-shadow: var(--shadow-overlay); - transform: translateX(105%); - transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1); - } - - .inspector-resize { - display: none; - } - - .changes-inspector.open { - transform: translateX(0); - } - - .inspector-close { - display: inline-grid; - } - - body.inspector-open .scrim { - display: block; - } -} - -@media (max-width: 760px) { - :root { - font-size: 16px; - --header-height: 52px; - --rail-width: 44px; - } - - .app-shell { - grid-template-columns: var(--rail-width) minmax(0, 1fr); - } - - .project-rail { - padding-inline: 4px; - } - - .brand-mark, - .rail-project, - .rail-icon { - width: 36px; - height: 36px; - } - - .thread-sidebar { - position: fixed; - z-index: 40; - top: 0; - bottom: 0; - left: var(--rail-width); - width: min(290px, calc(100vw - var(--rail-width))); - box-shadow: var(--shadow-overlay); - transform: translateX(-110%); - transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1); - } - - .thread-sidebar.open { - transform: translateX(0); - } - - .mobile-close, - .mobile-menu { - display: inline-grid; - } - - .sidebar-expand { - display: none !important; - } - - .desktop-sidebar-action { - display: none; - } - - body.sidebar-open .scrim { - display: block; - } - - .work-header { - padding-inline: 7px 9px; - } - - .work-title-meta { - display: flex; - gap: 0; - margin-top: 2px; - font-size: 11px; - } - - .work-title-meta .header-cwd, - .work-title-meta .header-source, - .work-title-meta .header-separator { - display: none; - } - - .work-title-meta .header-usage { - min-width: 0; - overflow: hidden; - flex: 0 1 auto; - text-overflow: ellipsis; - } - - .work-actions { - gap: 4px; - } - - .todo-button { - display: none; - } - - .inspector-trigger { - min-width: 36px; - width: 36px; - min-height: 36px; - padding: 0; - } - - .inspector-trigger > span:not([data-icon]) { - display: none; - } - - .inspector-trigger > svg + text { - display: none; - } - - .inspector-trigger { - font-size: 0; - } - - .button-count { - display: none; - } - - .conversation { - padding: 24px 16px 32px; - } - - .task-heading { - padding-bottom: 22px; - } - - .task-heading h1 { - font-size: 22px; - } - - .task-heading > p, - .work-commentary { - font-size: 13px; - } - - .final-response { - font-size: 15px; - } - - .message p { - font-size: 14px; - } - - .message { - padding: 14px 16px; - } - - .work-summary { - padding: 4px 6px 4px 0; - } - - .work-body { - padding-left: 24px; - } - - .execution-list li { - grid-template-columns: 26px minmax(0, 1fr); - } - - .execution-list li.compact-step { - grid-template-columns: 20px minmax(0, 1fr); - } - - .session-composer-dock { - max-height: min(70dvh, 620px); - padding: 0; - } - - .composer-rail { - padding: 7px 8px 8px; - } - - .composer-context button:not(:first-child) { - display: none; - } - - .composer-attention { - grid-template-columns: 25px minmax(0, 1fr); - } - - .composer-attention .attention-actions { - grid-column: 2; - } - - .goal-metrics, - .queue-model, - .composer-hint { - display: none; - } - - .goal-label, - .composer-goal-row > strong { - display: none; - } - - .sidebar-resize { - display: none; - } -} - -@media (max-width: 430px) { - .work-title-line > strong { - max-width: 150px; - } - - .task-meta > span:last-child { - width: 100%; - } -} - -@media (max-width: 1180px) { - body.non-session-view .app-shell { - grid-template-columns: var(--rail-width) clamp(210px, var(--sidebar-width), 228px) minmax(420px, 1fr); - } - - body.non-session-view.sidebar-collapsed .app-shell { - grid-template-columns: var(--rail-width) minmax(420px, 1fr); - } - - .board-panel { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width: 760px) { - body.non-session-view .app-shell, - body.non-session-view.sidebar-collapsed .app-shell { - grid-template-columns: var(--rail-width) minmax(0, 1fr); - } - - .dashboard-content { - padding: 24px 16px 42px; - } - - .dashboard-row { - grid-template-columns: 10px minmax(0, 1fr); - } - - .dashboard-row-meta { - grid-column: 2; - justify-content: space-between; - } - - .todos-work-header { - min-height: 92px; - flex-wrap: wrap; - align-content: center; - } - - .todos-work-header .work-title { - min-width: 180px; - } - - .todo-view-tabs { - order: 3; - width: 100%; - } - - .todo-view-tabs button { - flex: 1; - } - - .todos-content { - padding: 14px 12px 40px; - } - - .todo-detail { - top: 92px; - width: min(430px, calc(100% - 18px)); - } - - .todo-detail-scrim { - top: 92px; - } - - .tool-call-toggle { - grid-template-columns: 12px auto minmax(0, 1fr) auto; - } - - .tool-call-diff { - display: none; - } - - .shell-toggle { - grid-template-columns: 12px auto minmax(0, 1fr); - } - - .tool-child-row { - grid-template-columns: minmax(100px, auto) minmax(0, 1fr); - } -} - -@media (max-width: 620px) { - .board-panel { - grid-template-columns: 1fr; - } - - .todo-capture { - grid-template-columns: 20px minmax(0, 1fr); - } - - .todo-capture .primary-button { - grid-column: 2; - justify-self: start; - margin-bottom: 5px; - } - - .flat-todo-row { - grid-template-columns: 10px minmax(0, 1fr); - } - - .flat-todo-row > :last-child { - grid-column: 2; - } -} - -/* Quiet Precision visual refinement */ - -body { - font-family: var(--font-ui); - letter-spacing: -0.006em; -} - -.app-shell { - background: var(--canvas); -} - -.project-rail { - gap: 8px; - padding: 10px 6px; -} - -.brand-mark, -.rail-project, -.rail-icon { - width: 38px; - height: 38px; - border-radius: 8px; -} - -.brand-mark { - margin-bottom: 12px; - border: 1px solid rgb(255 255 255 / 14%); - background: var(--brand); - box-shadow: inset 0 0 0 1px rgb(255 255 255 / 8%); - font-size: 18px; -} - -.rail-project { - font-size: 11px; - letter-spacing: 0.01em; -} - -.rail-project.active { - color: var(--rail-ink); - background: rgb(255 255 255 / 12%); - box-shadow: inset 0 0 0 1px rgb(255 255 255 / 7%); -} - -.rail-project.active::before { - left: -7px; - width: 2px; - height: 22px; - background: var(--brand); -} - -.rail-attention-count { - top: 1px; - right: 0; - min-width: 16px; - height: 16px; - font-size: 10px; -} - -.thread-sidebar, -.changes-inspector { - background: var(--surface); -} - -.thread-sidebar { - border-right-color: var(--border-soft); -} - -.changes-inspector { - border-left-color: var(--border-soft); -} - -.sidebar-head { - padding-inline: 16px 10px; -} - -.project-title strong { - margin-bottom: 3px; - font-size: 16px; - font-weight: 700; - letter-spacing: -0.02em; -} - -.project-title span { - font-family: var(--font-mono); - font-size: 12px; -} - -.project-links { - gap: 5px; - padding: 12px; -} - -.project-links button { - min-height: 40px; - grid-template-columns: 20px minmax(0, 1fr) 14px; - gap: 8px; - border-radius: 7px; - padding-inline: 10px; - font-size: 13px; - font-weight: 620; -} - -.project-links button.active { - border-color: color-mix(in srgb, var(--brand) 22%, var(--border)); - background: var(--brand-field); - box-shadow: inset 2px 0 0 var(--brand); -} - -.sidebar-tabs button, -.inspector-tabs button { - min-height: 44px; - font-size: 12px; - font-weight: 650; -} - -.sidebar-toolbar { - gap: 8px; - padding: 12px 12px 5px; -} - -.search-field, -.compact-create { - min-height: 40px; -} - -.search-field { - padding-inline: 10px; - border-color: var(--border-soft); - background: var(--surface-raised); -} - -.search-field:focus-within { - border-color: var(--brand); - box-shadow: var(--focus); -} - -.search-field input, -.compact-create { - font-size: 12px; -} - -.compact-create { - border-color: color-mix(in srgb, var(--brand) 34%, var(--border)); - background: transparent; -} - -.sidebar-section { - padding: 9px 9px 0; -} - -.section-label { - min-height: 28px; - padding-inline: 8px; - font-size: 11px; - font-weight: 700; - letter-spacing: 0.035em; -} - -.thread { - min-height: 42px; - grid-template-columns: 16px minmax(0, 1fr) auto; - gap: 8px; - padding-inline: 9px; -} - -.thread-copy strong { - font-size: 13px; - font-weight: 620; -} - -.thread-time, -.thread-attention-tag { - font-size: 11px; -} - -.thread-attention-tag { - min-height: 20px; - padding-inline: 6px; -} - -.work-header { - gap: 12px; - padding: 9px 16px 9px 20px; - border-bottom-color: var(--border-soft); -} - -.work-title-line { - gap: 10px; -} - -.work-title-line > strong { - font-size: 20px; - font-weight: 700; - line-height: 26px; -} - -.session-status { - height: 24px; - gap: 7px; - padding-inline: 9px; - border-left-width: 2px; - font-size: 12px; -} - -.work-title-meta { - gap: 9px; - margin-top: 4px; - font-size: 12px; -} - -.work-title-meta .header-cwd { - font-family: var(--font-mono); -} - -.secondary-button, -.primary-button { - min-height: 38px; - border-radius: 7px; - padding-inline: 12px; - font-size: 12px; -} - -.conversation { - padding: 36px 34px 48px; -} - -.conversation > :where(.user-turn, .agent-response) { - width: min(900px, 100%); - margin-right: auto; - margin-left: auto; -} - -.user-turn { - margin-bottom: 24px; -} - -.message { - padding: 17px 19px; - border: 1px solid color-mix(in srgb, var(--border) 62%, transparent); - border-radius: 10px 10px 4px 10px; - background: var(--surface-muted); -} - -.message p { - font-size: 15px; - line-height: 1.68; -} - -.message-time { - margin-top: 8px; - font-size: 12px; -} - -.agent-response { - margin-bottom: 44px; -} - -.work-disclosure { - margin-bottom: 22px; -} - -.work-summary { - min-height: 36px; - gap: 9px; - border-radius: 6px; - padding: 4px 8px 4px 2px; -} - -.work-summary-copy strong, -.work-summary-current { - font-size: 13px; -} - -.work-body { - padding: 10px 4px 8px 26px; -} - -.work-body::before { - width: 1px; - display: block; - position: absolute; - top: 8px; - bottom: 10px; - left: 8px; - background: var(--border); - content: ""; -} - -.work-commentary, -.execution-list li.work-commentary-event p { - max-width: 70ch; - color: var(--ink-secondary); - font-size: 14px; - line-height: 1.68; -} - -.tool-run-toggle, -.reasoning-toggle, -.tool-call-toggle { - min-height: 36px; -} - -.tool-run-names, -.tool-child-row code, -.tool-call-toggle code, -.tool-call-command code { - font-family: var(--font-mono); - font-size: 12px; -} - -.tool-child-target, -.tool-call-summary, -.active-tool-line { - font-size: 13px; -} - -.final-response { - margin-top: 28px; - padding: 2px 0 8px; - font-size: 16px; - line-height: 1.72; -} - -.final-response > p:first-child { - margin-bottom: 16px; - font-size: 18px; - font-weight: 680; - letter-spacing: -0.02em; - line-height: 1.45; -} - -.final-response li + li { - margin-top: 9px; -} - -.response-meta { - margin-top: 18px; - font-size: 12px; -} - -.session-composer-dock { - border-top-color: var(--border-soft); - background: color-mix(in srgb, var(--surface) 92%, var(--canvas)); -} - -.composer-rail { - width: min(900px, 100%); - margin-inline: auto; - padding: 12px 26px 16px; -} - -.composer-attention { - gap: 12px; - margin-bottom: 10px; - padding: 14px; - border-color: color-mix(in srgb, var(--warning) 34%, var(--border)); - border-left-width: 3px; - border-radius: 8px; -} - -.composer-status-copy > strong { - font-size: 15px; -} - -.attention-type, -.goal-label { - font-size: 11px; - font-weight: 750; -} - -.composer-status-copy dl, -.attention-actions button, -.composer-goal-row > button, -.composer-queue > button, -.composer-queue p { - font-size: 12px; -} - -.composer-goal-row, -.composer-queue { - min-height: 44px; -} - -.goal-objective { - font-size: 13px; -} - -.goal-metrics, -.queue-model { - font-family: var(--font-mono); - font-size: 11px; -} - -.composer-input-slot { - padding-top: 10px; -} - -.composer-card { - border-color: var(--border); - background: var(--surface-raised); - box-shadow: var(--shadow-dock); -} - -.composer-card textarea { - min-height: 56px; - padding: 15px 17px 9px; - font-size: 15px; - line-height: 1.6; -} - -.composer-foot { - min-height: 42px; - padding: 0 12px 10px; -} - -.composer-context, -.composer-context button, -.composer-hint { - font-size: 12px; -} - -.stop-button, -.queue-button { - width: 36px; - min-height: 36px; -} - -.inspector-head { - padding-inline: 17px 12px; - border-bottom-color: var(--border-soft); -} - -.inspector-head strong { - font-size: 14px; -} - -.inspector-head span { - font-size: 12px; -} - -.agent-tree { - padding: 12px 9px; -} - -.agent-row { - min-height: 62px; - grid-template-columns: 29px minmax(0, 1fr) auto; - gap: 3px 9px; - padding: 9px; -} - -.agent-role { - width: 28px; - height: 28px; - font-size: 11px; -} - -.agent-row-copy strong { - font-size: 13px; -} - -.agent-row-copy small, -.agent-profile, -.agent-status { - font-size: 11px; -} - -.page-work-header .work-title > strong { - display: block; - font-size: 16px; - font-weight: 700; -} - -.page-work-header .work-title > span { - display: block; - margin-top: 3px; - color: var(--ink-tertiary); - font-size: 12px; -} - -.dashboard-content { - width: min(1180px, 100%); - gap: 34px; - padding: 46px 40px 72px; -} - -.dashboard-intro { - align-items: center; - gap: 16px; - padding-bottom: 2px; -} - -.page-mark { - width: 46px; - height: 46px; - border: 0; - border-radius: 10px; - color: var(--brand); - background: var(--brand-field); - box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 18%, var(--border)); -} - -.page-mark svg { - width: 21px; - height: 21px; -} - -.page-overline { - display: block; - margin-bottom: 4px; - color: var(--brand); - font-size: 11px; - font-weight: 750; - letter-spacing: 0.09em; - text-transform: uppercase; -} - -.dashboard-intro h1, -.flat-todo-head h1 { - font-size: 26px; - font-weight: 720; - letter-spacing: -0.035em; - line-height: 1.15; -} - -.dashboard-intro p, -.flat-todo-head p { - margin-top: 7px; - color: var(--ink-secondary); - font-size: 14px; - line-height: 1.5; -} - -.dashboard-priority-grid, -.dashboard-secondary-grid { - min-width: 0; - display: grid; - gap: 20px; -} - -.dashboard-priority-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.dashboard-secondary-grid { - grid-template-columns: minmax(0, 1.45fr) minmax(300px, 0.75fr); -} - -.dashboard-section { - min-width: 0; - padding: 18px 18px 10px; - border-top: 1px solid var(--border); - background: var(--surface); -} - -.attention-section { - border-top: 3px solid var(--warning); -} - -.running-section { - border-top: 3px solid var(--signal); -} - -.dashboard-section-head { - min-height: 34px; - padding: 0 1px 12px; - border-bottom: 0; -} - -.dashboard-section-head h2 { - font-size: 14px; - font-weight: 700; -} - -.section-count { - min-width: 23px; - padding: 3px 7px; - font-size: 11px; -} - -.dashboard-row { - min-height: 76px; - gap: 14px; - padding: 13px 11px; - border-top: 1px solid var(--border-soft); - border-bottom: 0; -} - -.dashboard-row:first-child { - border-top: 0; -} - -.dashboard-row.attention-row, -.dashboard-row.running-row { - grid-template-columns: 12px minmax(0, 1fr); - min-height: 96px; - padding: 16px 14px; - box-shadow: inset 2px 0 0 var(--warning); -} - -.dashboard-row.running-row { - box-shadow: inset 2px 0 0 var(--signal); -} - -.dashboard-row.attention-row .dashboard-row-meta, -.dashboard-row.running-row .dashboard-row-meta { - grid-column: 2; - justify-content: space-between; -} - -.dashboard-row-copy strong { - font-size: 14px; - font-weight: 680; -} - -.dashboard-row-copy small { - margin-top: 6px; - font-size: 12px; - line-height: 1.45; -} - -.dashboard-row-meta { - gap: 16px; - font-size: 12px; -} - -.dashboard-row-meta small { - font-size: 11px; -} - -.upcoming-note { - margin: 16px 10px 8px; - color: var(--ink-muted); - font-size: 12px; - line-height: 1.55; -} - -.upcoming-section .dashboard-row { - grid-template-columns: 12px minmax(0, 1fr); - min-height: 96px; -} - -.upcoming-section .dashboard-row-meta { - grid-column: 2; - justify-content: space-between; -} - -.todos-work-header { - min-height: 68px; -} - -.todo-view-tabs { - border-color: var(--border-soft); - border-radius: 8px; -} - -.todo-view-tabs button { - min-height: 34px; - padding-inline: 13px; - font-size: 12px; -} - -.todos-content { - padding: 26px 26px 64px; -} - -.todo-capture { - min-height: 58px; - grid-template-columns: 26px minmax(0, 1fr) auto; - gap: 10px; - margin-bottom: 24px; - padding: 7px 8px 7px 15px; - border-color: var(--border); - border-radius: 10px; - box-shadow: 0 8px 24px rgb(30 36 29 / 5%); -} - -.todo-capture input { - height: 40px; - font-size: 14px; -} - -.board-panel { - gap: 16px; -} - -.todo-lane { - min-height: 520px; - padding: 14px; - border-top: 2px solid var(--border); - background: var(--surface); -} - -.todo-lane:nth-child(1) { - border-top-color: color-mix(in srgb, var(--brand) 66%, var(--border)); -} - -.todo-lane:nth-child(3) { - border-top-color: color-mix(in srgb, var(--signal) 72%, var(--border)); -} - -.todo-lane:nth-child(4) { - border-top-color: color-mix(in srgb, var(--success) 68%, var(--border)); -} - -.todo-lane-head { - min-height: 54px; - gap: 9px; - padding: 0 2px 13px; - border-bottom-color: var(--border-soft); -} - -.todo-lane-head strong { - font-size: 14px; -} - -.todo-lane-head small, -.todo-lane-head > :last-child { - font-size: 11px; -} - -.todo-lane-head small { - margin-top: 4px; -} - -.todo-lane-list { - gap: 11px; - padding-top: 13px; -} - -.todo-card { - min-height: 148px; - padding: 16px; - border-color: var(--border-soft); - border-radius: 8px; -} - -.todo-card:hover, -.todo-card.selected { - border-color: color-mix(in srgb, var(--brand) 52%, var(--border)); - background: color-mix(in srgb, var(--surface-raised) 94%, var(--brand-field)); -} - -.todo-card-state, -.todo-card > small, -.todo-next { - font-size: 11px; -} - -.todo-card > strong { - margin-top: 11px; - font-size: 14px; - font-weight: 680; - line-height: 1.42; -} - -.todo-card > small { - margin-top: 12px; - line-height: 1.45; -} - -.todo-next { - padding-top: 15px; - font-weight: 700; -} - -.flat-todo-row { - min-height: 78px; - padding: 13px 10px; -} - -.flat-todo-row strong { - font-size: 14px; -} - -.flat-todo-row small, -.flat-todo-row > :last-child, -.flat-todo-head > span { - font-size: 12px; -} - -.todo-detail-head { - min-height: 72px; - padding: 17px 15px 14px 20px; -} - -.todo-detail-head strong { - font-size: 15px; -} - -.todo-detail-head span { - font-size: 12px; -} - -.todo-detail-body { - height: calc(100% - 72px); - padding-inline: 20px; -} - -.todo-detail-body h2 { - font-size: 12px; -} - -.todo-detail-body p, -.todo-detail-body li { - font-size: 13px; - line-height: 1.68; -} - -@media (max-width: 1000px) { - .dashboard-priority-grid, - .dashboard-secondary-grid { - grid-template-columns: 1fr; - } - - .dashboard-section { - padding-inline: 16px; - } -} - -@media (max-width: 760px) { - :root { - --header-height: 60px; - --rail-width: 48px; - } - - .brand-mark, - .rail-project, - .rail-icon { - width: 40px; - height: 40px; - } - - .work-header { - padding-inline: 9px 11px; - } - - .work-title-line > strong { - font-size: 18px; - } - - .conversation { - padding: 28px 16px 38px; - } - - .message p, - .final-response, - .composer-card textarea { - font-size: 16px; - } - - .final-response > p:first-child { - font-size: 18px; - } - - .composer-rail { - padding: 9px 10px 11px; - } - - .dashboard-content { - gap: 24px; - padding: 30px 18px 48px; - } - - .dashboard-intro h1 { - font-size: 24px; - } - - .dashboard-section { - padding: 15px 14px 8px; - } - - .dashboard-row.attention-row, - .dashboard-row.running-row { - min-height: 90px; - } - - .todos-work-header { - min-height: 104px; - } - - .todos-content { - padding: 18px 14px 46px; - } - - .todo-detail, - .todo-detail-scrim { - top: 104px; - } -} - -@media (max-width: 620px) { - .todo-lane { - min-height: 0; - } - - .todo-capture input { - font-size: 16px; - } -} - -@media (pointer: coarse) { - .thread, - .work-summary, - .tool-run-toggle, - .tool-call-toggle { - min-height: 44px; - } -} - -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - scroll-behavior: auto !important; - transition-duration: 0.01ms !important; - } -} 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