@try-works/dsh-recursive-mode 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -21,18 +21,18 @@ window.__ModuleLoader__.load({
21
21
  return state.current === void 0 ? "" : state.byId[state.current]?.cwd ?? "";
22
22
  }
23
23
  /**
24
- * Resolve the CURRENT workspace path for a workspace-scoped board (run 14):
25
- * the WORKSPACE owns the .recursive/ run-layer, not the session. Resolution:
26
- * 1) recentWorkspaceId's item path (the workspace the user is viewing), else
27
- * 2) the workspace whose sessionIds contains sessionList.current, else
28
- * 3) the workspace whose path === currentSessionCwd(sessionList), else
24
+ * Resolve the CURRENT workspace path for a workspace-scoped board (run 14,
25
+ * corrected run 16/0.1.14): the WORKSPACE owns the .recursive/ run-layer, not
26
+ * the session. Resolution mirrors the harness's startSession precedence
27
+ * (service.ts:183) the CURRENT session's workspace wins over the
28
+ * recentWorkspaceId (which the harness derives as the most recently ACTIVE
29
+ * workspace by session updatedAt, NOT the workspace being viewed):
30
+ * 1) the workspace whose sessionIds contains sessionList.current, else
31
+ * 2) the workspace whose path === currentSessionCwd(sessionList), else
32
+ * 3) recentWorkspaceId's item path (fallback ONLY when no current session resolves), else
29
33
  * 4) '' (empty -> the route returns null root -> board renders nothing).
30
34
  */
31
35
  function currentWorkspacePath(list, sessionList) {
32
- if (list.recentWorkspaceId !== void 0) {
33
- const recent = list.items.find((w) => w.workspaceId === list.recentWorkspaceId);
34
- if (recent !== void 0) return recent.path;
35
- }
36
36
  const current = sessionList.current;
37
37
  if (current !== void 0) {
38
38
  const bySession = list.items.find((w) => w.sessionIds.includes(current));
@@ -43,6 +43,10 @@ window.__ModuleLoader__.load({
43
43
  const byCwd = list.items.find((w) => w.path === cwd);
44
44
  if (byCwd !== void 0) return byCwd.path;
45
45
  }
46
+ if (list.recentWorkspaceId !== void 0) {
47
+ const recent = list.items.find((w) => w.workspaceId === list.recentWorkspaceId);
48
+ if (recent !== void 0) return recent.path;
49
+ }
46
50
  return "";
47
51
  }
48
52
  //#endregion
@@ -206,6 +210,34 @@ window.__ModuleLoader__.load({
206
210
  subagents
207
211
  };
208
212
  }
213
+ /** Human pill label per kind (neutral renders an empty dash). */
214
+ const PILL_LABELS = {
215
+ locked: "locked",
216
+ "in-progress": "in progress",
217
+ paused: "paused",
218
+ blocked: "blocked",
219
+ tampered: "tampered",
220
+ advisory: "advisory",
221
+ neutral: "—"
222
+ };
223
+ /**
224
+ * Solid-pill kind for a whole run card. Priority: tamper > fully-locked >
225
+ * run-state blocked > run-state paused > still in-progress.
226
+ */
227
+ function cardPill(card) {
228
+ const facts = cardFacts(card);
229
+ if (facts.tampered) return "tampered";
230
+ if (facts.lockValidity === "locked") return "locked";
231
+ if (facts.state === "blocked") return "blocked";
232
+ if (facts.state === "paused") return "paused";
233
+ return "in-progress";
234
+ }
235
+ /** Solid-pill kind for a phase row status string (LOCKED / DRAFT / '—'). */
236
+ function phaseStatusPill(status) {
237
+ if (status === "LOCKED") return "locked";
238
+ if (status === "" || status === "—") return "neutral";
239
+ return "in-progress";
240
+ }
209
241
  /**
210
242
  * Expand a card's phase chain into full inspector rows: the always-shown
211
243
  * '03.5' slot (§11.10 mandatory deviation) plus 01.5 when present, and
@@ -242,15 +274,59 @@ window.__ModuleLoader__.load({
242
274
  return rows;
243
275
  }
244
276
  //#endregion
277
+ //#region src/client/theme.ts
278
+ /**
279
+ * Board/Inspector theme state (run 16 / Paper). Default LIGHT, dark via a
280
+ * header toggle that flips data-theme on the root .rec-board / .rec-inspector.
281
+ * Persisted to localStorage under 'dsh-recursive-theme' (first visit = light).
282
+ */
283
+ const BOARD_THEME_STORAGE_KEY = "dsh-recursive-theme";
284
+ function readInitialTheme() {
285
+ if (typeof localStorage === "undefined") return "light";
286
+ try {
287
+ return localStorage.getItem("dsh-recursive-theme") === "dark" ? "dark" : "light";
288
+ } catch {
289
+ return "light";
290
+ }
291
+ }
292
+ /**
293
+ * Hoisted at the TOP of Board and Inspector (never after an early return — run
294
+ * 0.1.10 invariant). Defaults light, reads a persisted dark on init, and writes
295
+ * the chosen theme back to localStorage.
296
+ */
297
+ function useBoardTheme() {
298
+ const [theme, setTheme] = (0, react.useState)(readInitialTheme);
299
+ const toggle = (0, react.useCallback)(() => setTheme((t) => t === "light" ? "dark" : "light"), []);
300
+ (0, react.useEffect)(() => {
301
+ try {
302
+ localStorage.setItem(BOARD_THEME_STORAGE_KEY, theme);
303
+ } catch {}
304
+ }, [theme]);
305
+ return {
306
+ theme,
307
+ toggle
308
+ };
309
+ }
310
+ /** The shared header toggle button (moon in light, sun in dark). */
311
+ function ThemeToggle({ theme, toggle }) {
312
+ return (0, react.createElement)("button", {
313
+ type: "button",
314
+ className: "rec-theme-toggle",
315
+ onClick: toggle,
316
+ title: theme === "light" ? "Switch to dark theme" : "Switch to light theme",
317
+ "aria-label": "Toggle theme"
318
+ }, theme === "light" ? "☾" : "☀");
319
+ }
320
+ //#endregion
245
321
  //#region src/client/board.tsx
246
322
  /**
247
- * Board overlay (SP2 R1 + run 08 R3 + run 15): the kanban view over the live
248
- * host-route projection. Pure read-only renderer no filesystem, no run-file
249
- * scraping. Cards are grouped by worktree root and mapped to their current-phase
250
- * lane via derive.columnForRun.
323
+ * Board overlay (SP2 R1 + run 08 R3 + run 15/16): kanban over the live
324
+ * host-route projection, class-based grid board with the Paper theme.
325
+ * Pure read-only renderer no filesystem, no run-file scraping.
251
326
  *
252
- * Run 15 (task-board redesign): class-based structure + theme-token stylesheet
253
- * injected once via styles.ts (no inline styles unlocks hover/grid/theme).
327
+ * Run 16 (Paper): data-theme (default light), header theme toggle, workspace
328
+ * path + count chip, solid status pills. useBoardTheme is hoisted ABOVE the
329
+ * early returns (0.1.10 hook invariant).
254
330
  */
255
331
  /** Flatten the worktree-grouped projection into a stable run list. */
256
332
  function listRuns(projection) {
@@ -269,18 +345,36 @@ window.__ModuleLoader__.load({
269
345
  "aria-label": "Close"
270
346
  }, "×");
271
347
  }
348
+ function solidPill$1(kind) {
349
+ return (0, react.createElement)("span", {
350
+ className: "rec-pill",
351
+ "data-pill": kind
352
+ }, PILL_LABELS[kind]);
353
+ }
272
354
  function Board({ snapshot, onOpenInspector, onClose }) {
355
+ const { theme, toggle } = useBoardTheme();
273
356
  if (snapshot === null || snapshot.root === null) return null;
274
357
  const runs = listRuns(snapshot.projection);
358
+ const workspacePath = snapshot.root;
275
359
  const open = (run) => () => onOpenInspector?.({
276
360
  worktreeRoot: run.worktreeRoot,
277
361
  runId: run.runId
278
362
  });
279
363
  if (runs.length === 0) return (0, react.createElement)("div", {
280
364
  className: "rec-board",
365
+ "data-theme": theme,
281
366
  "data-empty": true
282
- }, (0, react.createElement)("header", { className: "rec-board-header" }, (0, react.createElement)("h2", { className: "rec-board-title" }, "Recursive runs"), closeButton$1(onClose)), (0, react.createElement)("p", { className: "rec-board-empty" }, "No recursive runs in this workspace yet."));
283
- return (0, react.createElement)("div", { className: "rec-board" }, (0, react.createElement)("header", { className: "rec-board-header" }, (0, react.createElement)("h2", { className: "rec-board-title" }, "Recursive runs"), (0, react.createElement)("span", { className: "rec-board-count" }, String(runs.length)), closeButton$1(onClose)), (0, react.createElement)("div", { className: "rec-columns" }, KANBAN_LANES.map((lane) => {
367
+ }, (0, react.createElement)("header", { className: "rec-board-header" }, (0, react.createElement)("h2", { className: "rec-board-title" }, "Recursive runs"), (0, react.createElement)("span", { className: "rec-board-path" }, workspacePath), (0, react.createElement)("span", { className: "rec-board-count" }, "0 runs"), (0, react.createElement)(ThemeToggle, {
368
+ theme,
369
+ toggle
370
+ }), closeButton$1(onClose)), (0, react.createElement)("p", { className: "rec-board-empty" }, "No recursive runs in this workspace yet."));
371
+ return (0, react.createElement)("div", {
372
+ className: "rec-board",
373
+ "data-theme": theme
374
+ }, (0, react.createElement)("header", { className: "rec-board-header" }, (0, react.createElement)("h2", { className: "rec-board-title" }, "Recursive runs"), (0, react.createElement)("span", { className: "rec-board-path" }, workspacePath), (0, react.createElement)("span", { className: "rec-board-count" }, runs.length + " runs"), (0, react.createElement)(ThemeToggle, {
375
+ theme,
376
+ toggle
377
+ }), closeButton$1(onClose)), (0, react.createElement)("div", { className: "rec-columns" }, KANBAN_LANES.map((lane) => {
284
378
  const laneRuns = runs.filter((r) => columnForRun(r) === lane.id);
285
379
  return (0, react.createElement)("section", {
286
380
  key: lane.id,
@@ -291,6 +385,7 @@ window.__ModuleLoader__.load({
291
385
  "aria-hidden": true
292
386
  }), (0, react.createElement)("h3", { className: "rec-column-title" }, lane.label), (0, react.createElement)("span", { className: "rec-column-count" }, String(laneRuns.length))), (0, react.createElement)("div", { className: "rec-cards" }, laneRuns.map((run) => {
293
387
  const facts = cardFacts(run);
388
+ const pill = cardPill(run);
294
389
  return (0, react.createElement)("article", {
295
390
  key: run.worktreeRoot + "\0" + run.runId,
296
391
  className: "rec-card",
@@ -299,23 +394,17 @@ window.__ModuleLoader__.load({
299
394
  }, (0, react.createElement)("div", { className: "rec-card-title" }, run.runId), (0, react.createElement)("div", { className: "rec-card-progress" }, (0, react.createElement)("div", {
300
395
  className: "rec-card-progress-fill",
301
396
  style: { width: Math.round(facts.progress * 100) + "%" }
302
- })), (0, react.createElement)("div", { className: "rec-card-meta" }, (0, react.createElement)("span", { className: "rec-card-phase" }, facts.currentPhase ?? "no phases"), facts.tampered && (0, react.createElement)("span", {
303
- className: "rec-badge rec-badge-tamper",
304
- "data-status": run.state,
305
- title: "tampered"
306
- }, "!"), (0, react.createElement)("span", {
307
- className: "rec-badge",
308
- "data-status": run.state
309
- }, run.state)));
397
+ })), (0, react.createElement)("div", { className: "rec-card-meta" }, (0, react.createElement)("span", { className: "rec-card-phase" }, facts.currentPhase ?? "no phases"), solidPill$1(pill)));
310
398
  }), laneRuns.length === 0 && (0, react.createElement)("div", { className: "rec-column-empty" }, "—")));
311
399
  })));
312
400
  }
313
401
  //#endregion
314
402
  //#region src/client/inspector.tsx
315
403
  /**
316
- * Drill-down inspector (SP2 R1 + run 15): centered modal-style detail panel
317
- * (task-board .detail shape) rendering one run's phase chain, lock badges, and
318
- * tamper/gate facts all from the live host-route snapshot, never disk.
404
+ * Drill-down inspector (SP2 R1 + run 15/16): centered modal-style detail panel
405
+ * (Paper .detail shape) with back + close, data-theme (default light), theme
406
+ * toggle, and solid status pills. All from the live host-route snapshot.
407
+ * useBoardTheme is hoisted ABOVE the not-found early return.
319
408
  */
320
409
  function closeButton(onClose) {
321
410
  if (onClose === void 0) return null;
@@ -327,37 +416,35 @@ window.__ModuleLoader__.load({
327
416
  "aria-label": "Close"
328
417
  }, "×");
329
418
  }
330
- function detailShell(runId, onBackToToolDetails, onClose, headerExtra, body) {
331
- return (0, react.createElement)("div", { className: "rec-inspector" }, (0, react.createElement)("div", { className: "rec-detail" }, (0, react.createElement)("div", { className: "rec-detail-header" }, (0, react.createElement)("button", {
419
+ function solidPill(kind) {
420
+ return (0, react.createElement)("span", {
421
+ className: "rec-pill",
422
+ "data-pill": kind
423
+ }, PILL_LABELS[kind]);
424
+ }
425
+ function detailShell(runId, onBackToToolDetails, onClose, headerExtra, body, theme, toggle) {
426
+ return (0, react.createElement)("div", {
427
+ className: "rec-inspector",
428
+ "data-theme": theme
429
+ }, (0, react.createElement)("div", { className: "rec-detail" }, (0, react.createElement)("div", { className: "rec-detail-header" }, (0, react.createElement)("button", {
332
430
  type: "button",
333
431
  className: "rec-back",
334
432
  onClick: onBackToToolDetails
335
- }, "← Back"), (0, react.createElement)("h2", { className: "rec-detail-title" }, runId), headerExtra, closeButton(onClose)), (0, react.createElement)("div", { className: "rec-detail-body" }, body)));
433
+ }, "← Back"), (0, react.createElement)("h2", { className: "rec-detail-title" }, runId), headerExtra, (0, react.createElement)(ThemeToggle, {
434
+ theme,
435
+ toggle
436
+ }), closeButton(onClose)), (0, react.createElement)("div", { className: "rec-detail-body" }, body)));
336
437
  }
337
438
  function Inspector({ runId, worktreeRoot, snapshot, onBackToToolDetails, onClose }) {
439
+ const { theme, toggle } = useBoardTheme();
338
440
  const card = snapshot?.projection?.[worktreeRoot]?.[runId];
339
- if (card === void 0) return detailShell(runId, onBackToToolDetails, onClose, null, (0, react.createElement)("p", { className: "rec-detail-text" }, "Run not found: " + runId));
441
+ if (card === void 0) return detailShell(runId, onBackToToolDetails, onClose, null, (0, react.createElement)("p", { className: "rec-detail-text" }, "Run not found: " + runId), theme, toggle);
340
442
  const facts = cardFacts(card);
341
443
  const rows = expandPhaseRows(card);
342
- const badge = (0, react.createElement)("span", {
343
- className: "rec-badge",
344
- "data-status": card.state
345
- }, facts.state);
346
- const tamper = facts.tampered ? (0, react.createElement)("span", {
347
- className: "rec-badge rec-badge-tamper",
348
- "data-status": card.state
349
- }, "tampered") : null;
350
- return detailShell(runId, onBackToToolDetails, onClose, (0, react.createElement)("span", { style: {
351
- display: "flex",
352
- alignItems: "center",
353
- gap: 6
354
- } }, badge, tamper), (0, react.createElement)("section", { className: "rec-detail-section" }, (0, react.createElement)("h3", {}, "Phases"), rows.map((row) => (0, react.createElement)("div", {
444
+ return detailShell(runId, onBackToToolDetails, onClose, solidPill(cardPill(card)), (0, react.createElement)("section", { className: "rec-detail-section" }, (0, react.createElement)("h3", {}, "Phases"), rows.map((row) => (0, react.createElement)("div", {
355
445
  key: row.phase,
356
446
  className: "rec-phase-row"
357
- }, (0, react.createElement)("span", { className: "rec-phase-id" }, row.phase), (0, react.createElement)("span", { className: "rec-phase-name" }, row.fileName ?? "—"), (0, react.createElement)("span", {
358
- className: "rec-badge",
359
- "data-status": row.status === "LOCKED" ? "complete" : "active"
360
- }, row.status), row.lockHash !== void 0 && (0, react.createElement)("code", { className: "rec-lockhash" }, "#" + row.lockHash.slice(0, 8)))), facts.gateBlocked && (0, react.createElement)("div", { className: "rec-detail-section" }, (0, react.createElement)("h3", {}, "Gate"), (0, react.createElement)("p", { className: "rec-detail-text" }, "Blocked (" + (facts.gateKind ?? "") + ")"))));
447
+ }, (0, react.createElement)("span", { className: "rec-phase-id" }, row.phase), (0, react.createElement)("span", { className: "rec-phase-name" }, row.fileName ?? "—"), solidPill(phaseStatusPill(row.status)), row.lockHash !== void 0 && (0, react.createElement)("code", { className: "rec-lockhash" }, "#" + row.lockHash.slice(0, 8)))), facts.gateBlocked && (0, react.createElement)("div", { className: "rec-detail-section" }, (0, react.createElement)("h3", {}, "Gate"), (0, react.createElement)("p", { className: "rec-detail-text" }, "Blocked (" + (facts.gateKind ?? "") + ")"))), theme, toggle);
361
448
  }
362
449
  //#endregion
363
450
  //#region src/client/strip.tsx
@@ -565,28 +652,138 @@ window.__ModuleLoader__.load({
565
652
  //#endregion
566
653
  //#region src/client/styles.ts
567
654
  /**
568
- * One-shot board/inspector stylesheet injection (run 15). The client bundle is a
569
- * plain CJS closure with NO CSS pipeline (tsdown has no CSS handling), so we
570
- * inject a single <style data-dsh-recursive> element into document.head and
571
- * style via class names + the shell's --dsw-* theme tokens. That unlocks hover,
572
- * grid layout, and light/dark/skin theme followingimpossible with inline
573
- * styles. Idempotent: guarded by document.querySelector, so every registration
574
- * reuses the one element; the returned disposer removes it.
655
+ * One-shot board/inspector stylesheet injection (run 15 + run 16 / Paper).
656
+ * The client bundle is a plain CJS closure with NO CSS pipeline (tsdown), so we
657
+ * inject a single <style data-dsh-recursive> element into document.head.
658
+ *
659
+ * Run 16 (Paper): the design is ported 1:1 DEFAULT LIGHT theme, dark via a
660
+ * data-theme attribute on the .rec-board / .rec-inspector root. Raw --rm3-*
661
+ * tokens live on the scoped roots; semantic --board-* aliases remap under
662
+ * [data-theme='dark']. EVERY component rule consumes ONLY --board-* aliases
663
+ * (never --rm3-* directly). Idempotent injection; disposer removes the element.
575
664
  */
576
665
  const BOARD_CSS = `
577
- /* dsh-recursive board + inspector (theme-token driven, .rec-* scoped) */
666
+ /* ===== dsh-recursive Paper theme (run 16) ===== */
667
+
668
+ /* Raw tokens: light (default) + dark + shared, scoped to the board/inspector. */
669
+ .rec-board, .rec-inspector {
670
+ /* light */
671
+ --rm3-light-background: #FFFFFF;
672
+ --rm3-light-foreground: #111111;
673
+ --rm3-light-card: #FFFFFF;
674
+ --rm3-light-muted: #FAFAFA;
675
+ --rm3-light-muted-foreground: #666666;
676
+ --rm3-light-accent: #F5F5F5;
677
+ --rm3-light-border: #EAEAEA;
678
+ --rm3-light-success: #27A644;
679
+ --rm3-light-warning: #B67A11;
680
+ --rm3-light-error: #D84F6A;
681
+ --rm3-light-advisory: #9664E8;
682
+ --rm3-light-info: #3F87F5;
683
+ --rm3-light-destructive: #B4261A;
684
+ /* dark */
685
+ --rm3-background: #0A0A0A;
686
+ --rm3-foreground: #EDEDED;
687
+ --rm3-card: #0F0F0F;
688
+ --rm3-muted: #141414;
689
+ --rm3-muted-foreground: #9A9A9A;
690
+ --rm3-accent: #1A1A1A;
691
+ --rm3-border: #1F1F1F;
692
+ --rm3-success: #27A644;
693
+ --rm3-warning: #D9A441;
694
+ --rm3-error: #E06C89;
695
+ --rm3-advisory: #B479FF;
696
+ --rm3-info: #6EA8FF;
697
+ /* shared */
698
+ --rm3-font-sans: 'Geist', ui-sans-serif, system-ui, sans-serif;
699
+ --rm3-font-mono: 'Geist Mono', ui-monospace, Menlo, monospace;
700
+ --rm3-text-xs: 12px;
701
+ --rm3-text-sm: 14px;
702
+ --rm3-text-md: 16px;
703
+ --rm3-text-lg: 20px;
704
+ --rm3-text-xl: 28px;
705
+ --rm3-font-weight-regular: 400;
706
+ --rm3-font-weight-medium: 500;
707
+ --rm3-font-weight-semibold: 600;
708
+ --rm3-radius-sm: 5px;
709
+ --rm3-radius-md: 6px;
710
+ --rm3-radius-lg: 8px;
711
+ --rm3-radius-xl: 11px;
712
+ --rm3-space-8: 8px;
713
+ --rm3-space-12: 12px;
714
+ --rm3-space-16: 16px;
715
+ --rm3-space-24: 24px;
716
+ --rm3-space-32: 32px;
717
+ --rm3-tracking-tight: -0.02em;
718
+ --rm3-tracking-mono: 0.02em;
719
+
720
+ /* Semantic aliases (LIGHT default) */
721
+ --board-bg: var(--rm3-light-background);
722
+ --board-fg: var(--rm3-light-foreground);
723
+ --board-card: var(--rm3-light-card);
724
+ --board-muted: var(--rm3-light-muted);
725
+ --board-muted-fg: var(--rm3-light-muted-foreground);
726
+ --board-accent: var(--rm3-light-accent);
727
+ --board-border: var(--rm3-light-border);
728
+ --board-success: var(--rm3-light-success);
729
+ --board-warning: var(--rm3-light-warning);
730
+ --board-error: var(--rm3-light-error);
731
+ --board-advisory: var(--rm3-light-advisory);
732
+ --board-info: var(--rm3-light-info);
733
+ --board-destructive: var(--rm3-light-destructive);
734
+ --board-font-sans: var(--rm3-font-sans);
735
+ --board-font-mono: var(--rm3-font-mono);
736
+ --board-text-xs: var(--rm3-text-xs);
737
+ --board-text-sm: var(--rm3-text-sm);
738
+ --board-text-md: var(--rm3-text-md);
739
+ --board-text-lg: var(--rm3-text-lg);
740
+ --board-text-xl: var(--rm3-text-xl);
741
+ --board-fw-regular: var(--rm3-font-weight-regular);
742
+ --board-fw-medium: var(--rm3-font-weight-medium);
743
+ --board-fw-semibold: var(--rm3-font-weight-semibold);
744
+ --board-radius-sm: var(--rm3-radius-sm);
745
+ --board-radius-md: var(--rm3-radius-md);
746
+ --board-radius-lg: var(--rm3-radius-lg);
747
+ --board-radius-xl: var(--rm3-radius-xl);
748
+ --board-space-8: var(--rm3-space-8);
749
+ --board-space-12: var(--rm3-space-12);
750
+ --board-space-16: var(--rm3-space-16);
751
+ --board-space-24: var(--rm3-space-24);
752
+ --board-space-32: var(--rm3-space-32);
753
+ --board-tracking-tight: var(--rm3-tracking-tight);
754
+ --board-tracking-mono: var(--rm3-tracking-mono);
755
+ }
756
+
757
+ /* Semantic aliases remapped for DARK. */
758
+ .rec-board[data-theme='dark'], .rec-inspector[data-theme='dark'] {
759
+ --board-bg: var(--rm3-background);
760
+ --board-fg: var(--rm3-foreground);
761
+ --board-card: var(--rm3-card);
762
+ --board-muted: var(--rm3-muted);
763
+ --board-muted-fg: var(--rm3-muted-foreground);
764
+ --board-accent: var(--rm3-accent);
765
+ --board-border: var(--rm3-border);
766
+ --board-success: var(--rm3-success);
767
+ --board-warning: var(--rm3-warning);
768
+ --board-error: var(--rm3-error);
769
+ --board-advisory: var(--rm3-advisory);
770
+ --board-info: var(--rm3-info);
771
+ --board-destructive: var(--rm3-error);
772
+ }
773
+
774
+ /* ===== Board ===== */
578
775
  .rec-board {
579
776
  position: fixed;
580
777
  inset: 0;
581
778
  z-index: 9999;
582
- background: var(--dsw-alias-bg-base);
583
- color: var(--dsw-alias-label-primary);
584
- font-family: var(--dsw-font-family);
585
779
  display: flex;
586
780
  flex-direction: column;
587
781
  box-sizing: border-box;
588
- padding: 14px 16px 16px;
589
- gap: 12px;
782
+ padding: var(--board-space-16);
783
+ gap: var(--board-space-12);
784
+ background: var(--board-bg);
785
+ color: var(--board-fg);
786
+ font-family: var(--board-font-sans);
590
787
  pointer-events: auto;
591
788
  overflow: hidden;
592
789
  }
@@ -594,86 +791,119 @@ window.__ModuleLoader__.load({
594
791
  .rec-board-header {
595
792
  display: flex;
596
793
  align-items: center;
597
- gap: 10px;
794
+ gap: var(--board-space-12);
598
795
  flex: none;
599
796
  }
600
797
 
601
798
  .rec-board-title {
602
799
  margin: 0;
603
- font-size: 16px;
604
- font-weight: 700;
605
- color: var(--dsw-alias-label-primary);
800
+ font-size: 22px;
801
+ font-weight: var(--board-fw-semibold);
802
+ letter-spacing: var(--board-tracking-tight);
803
+ color: var(--board-fg);
606
804
  white-space: nowrap;
607
805
  }
608
806
 
807
+ .rec-board-path {
808
+ flex: none;
809
+ font-family: var(--board-font-mono);
810
+ font-size: var(--board-text-xs);
811
+ letter-spacing: var(--board-tracking-mono);
812
+ color: var(--board-muted-fg);
813
+ overflow: hidden;
814
+ text-overflow: ellipsis;
815
+ white-space: nowrap;
816
+ max-width: 40%;
817
+ }
818
+
609
819
  .rec-board-count {
610
820
  flex: none;
611
- font-size: 12px;
612
- color: var(--dsw-alias-label-tertiary);
613
- background: var(--dsw-alias-interactive-bg-hover);
614
- border-radius: 999px;
615
- padding: 1px 8px;
821
+ font-size: var(--board-text-xs);
822
+ font-weight: var(--board-fw-medium);
823
+ color: var(--board-muted-fg);
824
+ background: var(--board-muted);
825
+ border-radius: 9999px;
826
+ padding: 4px 10px;
616
827
  }
617
828
 
618
829
  .rec-board-empty {
619
830
  margin: 0;
620
- padding: 40px;
831
+ padding: var(--board-space-32);
621
832
  text-align: center;
622
- color: var(--dsw-alias-label-tertiary);
833
+ color: var(--board-muted-fg);
623
834
  }
624
835
 
625
836
  .rec-close {
626
- position: absolute;
627
- top: 14px;
628
- right: 16px;
629
- z-index: 1;
630
837
  display: inline-flex;
631
838
  align-items: center;
632
839
  justify-content: center;
633
- width: 26px;
634
- height: 26px;
840
+ width: 28px;
841
+ height: 28px;
635
842
  padding: 0;
843
+ margin-left: auto;
844
+ flex: none;
636
845
  background: transparent;
637
- border: 1px solid var(--dsw-alias-border-l2);
638
- border-radius: 6px;
639
- color: var(--dsw-alias-label-secondary);
846
+ border: 1px solid var(--board-border);
847
+ border-radius: var(--board-radius-md);
848
+ color: var(--board-muted-fg);
640
849
  cursor: pointer;
641
- font-size: 15px;
850
+ font-size: var(--board-text-md);
642
851
  line-height: 1;
643
852
  }
644
853
 
645
854
  .rec-close:hover {
646
- background: var(--dsw-alias-interactive-bg-hover);
647
- color: var(--dsw-alias-label-primary);
855
+ background: var(--board-accent);
856
+ color: var(--board-fg);
857
+ }
858
+
859
+ .rec-theme-toggle {
860
+ display: inline-flex;
861
+ align-items: center;
862
+ justify-content: center;
863
+ width: 28px;
864
+ height: 28px;
865
+ padding: 0;
866
+ flex: none;
867
+ background: transparent;
868
+ border: 1px solid var(--board-border);
869
+ border-radius: var(--board-radius-md);
870
+ color: var(--board-muted-fg);
871
+ cursor: pointer;
872
+ font-size: var(--board-text-md);
873
+ line-height: 1;
874
+ }
875
+
876
+ .rec-theme-toggle:hover {
877
+ background: var(--board-accent);
878
+ color: var(--board-fg);
648
879
  }
649
880
 
650
881
  .rec-columns {
651
882
  display: grid;
652
883
  grid-auto-flow: column;
653
- grid-auto-columns: minmax(240px, 1fr);
654
- gap: 12px;
884
+ grid-auto-columns: minmax(220px, 1fr);
885
+ gap: var(--board-space-12);
655
886
  flex: 1;
656
887
  min-height: 0;
657
888
  overflow-x: auto;
658
889
  overflow-y: hidden;
659
890
  overscroll-behavior-inline: contain;
660
891
  padding-bottom: 6px;
661
- scrollbar-color: var(--dsw-alias-border-l3) var(--dsw-alias-interactive-bg-hover);
892
+ scrollbar-color: var(--board-border) var(--board-accent);
662
893
  scrollbar-width: thin;
663
894
  }
664
895
 
665
896
  .rec-columns::-webkit-scrollbar { height: 10px; }
666
- .rec-columns::-webkit-scrollbar-track { background: var(--dsw-alias-interactive-bg-hover); border-radius: 999px; }
667
- .rec-columns::-webkit-scrollbar-thumb { background: var(--dsw-alias-border-l3); background-clip: content-box; border: 2px solid transparent; border-radius: 999px; }
668
- .rec-columns::-webkit-scrollbar-thumb:hover { background: var(--dsw-alias-border-l3); background-clip: content-box; }
897
+ .rec-columns::-webkit-scrollbar-track { background: var(--board-accent); border-radius: 999px; }
898
+ .rec-columns::-webkit-scrollbar-thumb { background: var(--board-border); border-radius: 999px; }
669
899
 
670
900
  .rec-column {
671
901
  display: flex;
672
902
  flex-direction: column;
673
903
  min-height: 0;
674
- background: var(--dsw-alias-bg-layer-2);
675
- border: 1px solid var(--dsw-alias-border-l1);
676
- border-radius: 12px;
904
+ background: var(--board-muted);
905
+ border: 1px solid var(--board-border);
906
+ border-radius: var(--board-radius-xl);
677
907
  overflow: hidden;
678
908
  }
679
909
 
@@ -681,7 +911,7 @@ window.__ModuleLoader__.load({
681
911
  display: flex;
682
912
  align-items: center;
683
913
  gap: 6px;
684
- padding: 10px 12px;
914
+ padding: var(--board-space-12);
685
915
  flex: none;
686
916
  }
687
917
 
@@ -689,8 +919,8 @@ window.__ModuleLoader__.load({
689
919
  margin: 0;
690
920
  flex: 1;
691
921
  font-size: 13px;
692
- font-weight: 700;
693
- color: var(--dsw-alias-label-primary);
922
+ font-weight: var(--board-fw-semibold);
923
+ color: var(--board-fg);
694
924
  overflow: hidden;
695
925
  text-overflow: ellipsis;
696
926
  white-space: nowrap;
@@ -698,77 +928,71 @@ window.__ModuleLoader__.load({
698
928
 
699
929
  .rec-column-count {
700
930
  flex: none;
701
- font-size: 12px;
702
- color: var(--dsw-alias-label-tertiary);
703
- background: var(--dsw-alias-interactive-bg-hover);
704
- border-radius: 999px;
931
+ font-size: var(--board-text-xs);
932
+ font-weight: var(--board-fw-medium);
933
+ color: var(--board-muted-fg);
934
+ background: var(--board-accent);
935
+ border-radius: 9999px;
705
936
  padding: 1px 8px;
706
937
  }
707
938
 
708
939
  .rec-status-dot {
709
- width: 8px;
710
- height: 8px;
940
+ width: 6px;
941
+ height: 6px;
711
942
  border-radius: 50%;
712
943
  flex: none;
713
944
  }
714
945
 
715
- .rec-status-dot[data-status='new'] { background: var(--dsw-alias-label-tertiary); }
716
- .rec-status-dot[data-status='active'] { background: var(--dsw-alias-state-business-primary); }
717
- .rec-status-dot[data-status='paused'] { background: var(--dsw-alias-state-warn-primary); }
718
- .rec-status-dot[data-status='blocked'] { background: var(--dsw-alias-state-error-primary); }
719
- .rec-status-dot[data-status='complete'] { background: var(--dsw-alias-state-success-primary); }
946
+ .rec-status-dot[data-status='new'] { background: var(--board-muted-fg); }
947
+ .rec-status-dot[data-status='active'] { background: var(--board-info); }
948
+ .rec-status-dot[data-status='paused'] { background: var(--board-warning); }
949
+ .rec-status-dot[data-status='blocked'] { background: var(--board-error); }
950
+ .rec-status-dot[data-status='complete'] { background: var(--board-success); }
720
951
 
721
952
  .rec-cards {
722
953
  display: flex;
723
954
  flex-direction: column;
724
- gap: 8px;
725
- padding: 2px 8px 10px;
955
+ gap: var(--board-space-8);
956
+ padding: 10px;
726
957
  overflow-y: auto;
727
958
  flex: 1;
728
959
  min-height: 0;
729
960
  }
730
961
 
731
962
  .rec-column-empty {
732
- padding: 24px 8px;
963
+ padding: var(--board-space-24) var(--board-space-8);
733
964
  text-align: center;
734
- font-size: 12px;
735
- color: var(--dsw-alias-label-tertiary);
965
+ font-size: var(--board-text-xs);
966
+ color: var(--board-muted-fg);
736
967
  }
737
968
 
738
969
  .rec-card {
739
970
  display: flex;
740
971
  flex-direction: column;
741
- gap: 6px;
742
- padding: 10px 12px;
972
+ gap: var(--board-space-8);
973
+ padding: var(--board-space-12) 14px;
743
974
  text-align: left;
744
- background: var(--dsw-alias-bg-base);
745
- border: 1px solid var(--dsw-alias-border-l2);
746
- border-radius: 10px;
975
+ background: var(--board-card);
976
+ border: 1px solid var(--board-border);
977
+ border-radius: var(--board-radius-lg);
747
978
  cursor: pointer;
748
- color: var(--dsw-alias-label-primary);
979
+ color: var(--board-fg);
749
980
  font-family: inherit;
750
981
  transition: box-shadow 120ms ease, border-color 120ms ease, transform 120ms ease;
751
982
  }
752
983
 
753
984
  .rec-card:hover {
754
- box-shadow: var(--dsw-shadow-lv2);
755
- border-color: var(--dsw-alias-border-l3);
985
+ border-color: var(--board-info);
756
986
  transform: translateY(-1px);
757
987
  }
758
988
 
759
989
  .rec-card:active {
760
- box-shadow: var(--dsw-shadow-lv1);
761
990
  transform: translateY(0);
762
991
  }
763
992
 
764
- .rec-card[data-state='active'] { border-color: var(--dsw-alias-state-business-primary); }
765
- .rec-card[data-state='paused'] { border-color: var(--dsw-alias-state-warn-primary); }
766
- .rec-card[data-state='blocked'] { border-color: var(--dsw-alias-state-error-primary); }
767
- .rec-card[data-state='complete'] { border-color: var(--dsw-alias-state-success-primary); }
768
-
769
993
  .rec-card-title {
770
994
  font-size: 13px;
771
- font-weight: 600;
995
+ font-weight: var(--board-fw-semibold);
772
996
  line-height: 1.35;
773
997
  overflow: hidden;
774
998
  display: -webkit-box;
@@ -778,52 +1002,55 @@ window.__ModuleLoader__.load({
778
1002
 
779
1003
  .rec-card-progress {
780
1004
  height: 6px;
781
- background: var(--dsw-alias-interactive-bg-hover);
1005
+ background: var(--board-accent);
782
1006
  border-radius: 3px;
783
1007
  overflow: hidden;
784
1008
  }
785
1009
 
786
1010
  .rec-card-progress-fill {
787
1011
  height: 100%;
788
- background: var(--dsw-alias-state-business-primary);
1012
+ background: var(--board-info);
789
1013
  }
790
1014
 
791
1015
  .rec-card-meta {
792
1016
  display: flex;
793
1017
  align-items: center;
794
- gap: 8px;
795
- font-size: 11px;
796
- color: var(--dsw-alias-label-tertiary);
1018
+ gap: var(--board-space-8);
797
1019
  }
798
1020
 
799
1021
  .rec-card-phase {
800
1022
  flex: 1;
1023
+ font-family: var(--board-font-mono);
1024
+ font-size: var(--board-text-xs);
1025
+ letter-spacing: var(--board-tracking-mono);
1026
+ color: var(--board-muted-fg);
801
1027
  overflow: hidden;
802
1028
  text-overflow: ellipsis;
803
1029
  white-space: nowrap;
804
1030
  }
805
1031
 
806
- .rec-badge {
1032
+ /* ===== Solid status pill (Paper StatusPill) ===== */
1033
+ .rec-pill {
807
1034
  flex: none;
808
- padding: 2px 10px;
809
- font-size: 12px;
810
- border-radius: 999px;
811
- border: 1px solid var(--dsw-alias-border-l2);
812
- color: var(--dsw-alias-label-secondary);
1035
+ border-radius: 9999px;
1036
+ padding: 4px 10px;
1037
+ font-size: var(--board-text-xs);
1038
+ font-weight: var(--board-fw-medium);
1039
+ line-height: 1;
1040
+ border: none;
1041
+ background: var(--board-muted-fg);
1042
+ color: var(--board-bg);
813
1043
  }
814
1044
 
815
- .rec-badge[data-status='active'] { color: var(--dsw-alias-state-business-primary); border-color: var(--dsw-alias-state-business-primary); }
816
- .rec-badge[data-status='paused'] { color: var(--dsw-alias-state-warn-primary); border-color: var(--dsw-alias-state-warn-primary); }
817
- .rec-badge[data-status='blocked'] { color: var(--dsw-alias-state-error-primary); border-color: var(--dsw-alias-state-error-primary); }
818
- .rec-badge[data-status='complete'] { color: var(--dsw-alias-state-success-primary); border-color: var(--dsw-alias-state-success-primary); }
819
- .rec-badge[data-status='new'] { color: var(--dsw-alias-label-tertiary); }
1045
+ .rec-pill[data-pill='locked'] { background: var(--board-success); color: var(--board-bg); }
1046
+ .rec-pill[data-pill='in-progress'] { background: var(--board-info); color: var(--board-bg); }
1047
+ .rec-pill[data-pill='paused'] { background: var(--board-warning); color: var(--board-bg); }
1048
+ .rec-pill[data-pill='blocked'] { background: var(--board-error); color: var(--board-bg); }
1049
+ .rec-pill[data-pill='tampered'] { background: var(--board-error); color: var(--board-bg); }
1050
+ .rec-pill[data-pill='advisory'] { background: var(--board-advisory); color: var(--board-bg); }
1051
+ .rec-pill[data-pill='neutral'] { background: var(--board-muted-fg); color: var(--board-bg); }
820
1052
 
821
- .rec-badge-tamper {
822
- color: var(--dsw-alias-state-error-primary);
823
- border-color: var(--dsw-alias-state-error-primary);
824
- }
825
-
826
- /* inspector: centered modal-style detail */
1053
+ /* ===== Inspector: centered modal detail ===== */
827
1054
  .rec-inspector {
828
1055
  position: fixed;
829
1056
  inset: 0;
@@ -831,7 +1058,7 @@ window.__ModuleLoader__.load({
831
1058
  display: flex;
832
1059
  align-items: center;
833
1060
  justify-content: center;
834
- background: var(--dsw-alias-bg-mask-1);
1061
+ background: rgba(0, 0, 0, 0.4);
835
1062
  pointer-events: auto;
836
1063
  }
837
1064
 
@@ -840,20 +1067,21 @@ window.__ModuleLoader__.load({
840
1067
  flex-direction: column;
841
1068
  width: min(640px, calc(100vw - 48px));
842
1069
  max-height: calc(100vh - 80px);
843
- background: var(--dsw-alias-bg-base);
844
- border: 1px solid var(--dsw-alias-border-l2);
1070
+ background: var(--board-bg);
1071
+ border: 1px solid var(--board-border);
845
1072
  border-radius: 14px;
846
- box-shadow: var(--dsw-shadow-lv3);
847
- color: var(--dsw-alias-label-primary);
1073
+ box-shadow: 0 24px 64px rgba(0, 0, 0, 0.24);
1074
+ color: var(--board-fg);
1075
+ font-family: var(--board-font-sans);
848
1076
  overflow: hidden;
849
1077
  }
850
1078
 
851
1079
  .rec-detail-header {
852
1080
  display: flex;
853
1081
  align-items: center;
854
- gap: 10px;
855
- padding: 14px 18px;
856
- border-bottom: 1px solid var(--dsw-alias-separator-primary);
1082
+ gap: var(--board-space-12);
1083
+ padding: var(--board-space-16) var(--board-space-16);
1084
+ border-bottom: 1px solid var(--board-border);
857
1085
  flex: none;
858
1086
  }
859
1087
 
@@ -861,7 +1089,8 @@ window.__ModuleLoader__.load({
861
1089
  margin: 0;
862
1090
  flex: 1;
863
1091
  font-size: 15px;
864
- font-weight: 700;
1092
+ font-weight: var(--board-fw-semibold);
1093
+ letter-spacing: var(--board-tracking-tight);
865
1094
  overflow-wrap: anywhere;
866
1095
  }
867
1096
 
@@ -869,26 +1098,26 @@ window.__ModuleLoader__.load({
869
1098
  display: inline-flex;
870
1099
  align-items: center;
871
1100
  gap: 4px;
872
- padding: 5px 12px;
873
- font-size: 12px;
874
- color: var(--dsw-alias-label-primary);
1101
+ padding: 5px var(--board-space-12);
1102
+ font-size: var(--board-text-xs);
1103
+ color: var(--board-fg);
875
1104
  background: transparent;
876
- border: 1px solid var(--dsw-alias-border-l2);
877
- border-radius: 8px;
1105
+ border: 1px solid var(--board-border);
1106
+ border-radius: var(--board-radius-lg);
878
1107
  cursor: pointer;
879
1108
  white-space: nowrap;
880
1109
  }
881
1110
 
882
1111
  .rec-back:hover {
883
- background: var(--dsw-alias-interactive-bg-hover);
1112
+ background: var(--board-accent);
884
1113
  }
885
1114
 
886
1115
  .rec-detail-body {
887
- padding: 14px 18px;
1116
+ padding: var(--board-space-16);
888
1117
  overflow-y: auto;
889
1118
  display: flex;
890
1119
  flex-direction: column;
891
- gap: 16px;
1120
+ gap: var(--board-space-16);
892
1121
  flex: 1;
893
1122
  }
894
1123
 
@@ -900,43 +1129,70 @@ window.__ModuleLoader__.load({
900
1129
 
901
1130
  .rec-detail-section h3 {
902
1131
  margin: 0;
903
- font-size: 12px;
904
- font-weight: 700;
905
- color: var(--dsw-alias-label-tertiary);
1132
+ font-size: var(--board-text-xs);
1133
+ font-weight: var(--board-fw-semibold);
1134
+ color: var(--board-muted-fg);
1135
+ }
1136
+
1137
+ .rec-detail-text {
1138
+ margin: 0;
1139
+ font-size: var(--board-text-sm);
1140
+ color: var(--board-muted-fg);
906
1141
  }
907
1142
 
908
1143
  .rec-phase-row {
909
1144
  display: flex;
910
1145
  align-items: center;
911
- gap: 10px;
912
- padding: 8px 10px;
913
- border: 1px solid var(--dsw-alias-border-l1);
914
- border-radius: 8px;
1146
+ gap: var(--board-space-12);
1147
+ padding: var(--board-space-8) var(--board-space-12);
1148
+ border: 1px solid var(--board-border);
1149
+ border-radius: var(--board-radius-lg);
915
1150
  font-size: 13px;
916
1151
  }
917
1152
 
918
1153
  .rec-phase-id {
919
1154
  min-width: 48px;
920
- font-family: var(--dsw-font-markdown-code-block-small);
921
- color: var(--dsw-alias-state-business-primary);
1155
+ font-family: var(--board-font-mono);
1156
+ font-size: var(--board-text-xs);
1157
+ letter-spacing: var(--board-tracking-mono);
1158
+ color: var(--board-info);
922
1159
  }
923
1160
 
924
1161
  .rec-phase-name {
925
1162
  flex: 1;
926
- color: var(--dsw-alias-label-primary);
1163
+ color: var(--board-fg);
927
1164
  overflow: hidden;
928
1165
  text-overflow: ellipsis;
929
1166
  white-space: nowrap;
930
1167
  }
931
1168
 
932
1169
  .rec-lockhash {
933
- font-family: var(--dsw-font-markdown-code-block-small);
1170
+ font-family: var(--board-font-mono);
934
1171
  font-size: 11px;
935
- color: var(--dsw-alias-label-tertiary);
1172
+ letter-spacing: var(--board-tracking-mono);
1173
+ color: var(--board-muted-fg);
1174
+ }
1175
+
1176
+ /* ===== Strip (session dock) — keeps the shell --dsw-* theme ===== */
1177
+ .rec-badge {
1178
+ flex: none;
1179
+ padding: 2px 10px;
1180
+ font-size: 12px;
1181
+ border-radius: 999px;
1182
+ border: 1px solid var(--dsw-alias-border-l2);
1183
+ color: var(--dsw-alias-label-secondary);
1184
+ }
1185
+ .rec-badge-tamper {
1186
+ color: var(--dsw-alias-state-error-primary);
1187
+ border-color: var(--dsw-alias-state-error-primary);
1188
+ }
1189
+ .rec-badge-gate {
1190
+ color: var(--dsw-alias-state-warn-primary);
1191
+ border-color: var(--dsw-alias-state-warn-primary);
936
1192
  }
937
1193
 
938
1194
  @media (prefers-reduced-motion: reduce) {
939
- .rec-card, .rec-back, .rec-close { transition: none; }
1195
+ .rec-card, .rec-back, .rec-close, .rec-theme-toggle { transition: none; }
940
1196
  }
941
1197
  `;
942
1198
  /**