create-pmos 0.134.0 → 0.136.0

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.
Files changed (211) hide show
  1. package/package.json +1 -1
  2. package/payload/KIT-VERSION +2 -2
  3. package/payload/planning/evals/eval-calibration.md +1 -1
  4. package/payload/planning/okrs/health-budgets.md +1 -1
  5. package/payload/state/discoveries.md +1 -1
  6. package/payload/state/log.md +1 -1
  7. package/workspace/bin/notes-replay.js +235 -0
  8. package/workspace/c3-briefing-test.js +130 -0
  9. package/workspace/c3-foundation-test.js +159 -0
  10. package/workspace/c3-ib-items-test.js +105 -0
  11. package/workspace/c3-ib-more-test.js +120 -0
  12. package/workspace/c3-ib-triage-backlog-test.js +119 -0
  13. package/workspace/c3-ini-harness.js +66 -0
  14. package/workspace/c3-initiatives-test.js +140 -0
  15. package/workspace/c3-one-home-test.js +14 -1
  16. package/workspace/c3-reports-test.js +57 -18
  17. package/workspace/c3-settings-test.js +139 -0
  18. package/workspace/c3-work-test.js +42 -10
  19. package/workspace/fixtures/one-home-baseline.json +1 -9
  20. package/workspace/guide/briefing-cards.md +15 -3
  21. package/workspace/guide/briefing.md +12 -8
  22. package/workspace/guide/calendar-popup.md +2 -2
  23. package/workspace/guide/console-calendar.md +39 -10
  24. package/workspace/guide/index.md +1 -0
  25. package/workspace/guide/initiative-boards.md +65 -0
  26. package/workspace/guide/jira.md +3 -1
  27. package/workspace/guide/layout.md +2 -0
  28. package/workspace/guide/my-work.md +5 -3
  29. package/workspace/guide/notes.md +16 -1
  30. package/workspace/guide/reports-dashboards.md +29 -5
  31. package/workspace/guide/settings.md +68 -0
  32. package/workspace/guide/welcome.md +10 -10
  33. package/workspace/lib/calendar-cli.js +3 -2
  34. package/workspace/lib/calendar-layers.js +21 -10
  35. package/workspace/lib/calendar-tidy.js +230 -0
  36. package/workspace/lib/engine/boards.js +542 -0
  37. package/workspace/lib/engine/breakdown-ops.js +180 -0
  38. package/workspace/lib/engine/calendar-import.js +84 -0
  39. package/workspace/lib/engine/calendar-model.js +185 -0
  40. package/workspace/lib/engine/card-state.js +92 -0
  41. package/workspace/lib/engine/derived/derive.js +92 -0
  42. package/workspace/lib/engine/derived/plan.js +3 -0
  43. package/workspace/lib/engine/derived/trust.js +3 -0
  44. package/workspace/lib/engine/doc-health.js +142 -0
  45. package/workspace/lib/engine/doc-sheet.js +75 -0
  46. package/workspace/lib/engine/intake.js +78 -0
  47. package/workspace/lib/engine/outbox.js +86 -0
  48. package/workspace/lib/engine/plan-forecast.js +342 -0
  49. package/workspace/lib/engine/promises.js +29 -0
  50. package/workspace/lib/engine/records/index.js +114 -0
  51. package/workspace/lib/engine/releases.js +96 -0
  52. package/workspace/lib/engine/replies.js +99 -0
  53. package/workspace/lib/engine/reply-contract/reply-contract.js +372 -0
  54. package/workspace/lib/engine/report-families.js +175 -0
  55. package/workspace/lib/engine/roadmap-ops.js +150 -0
  56. package/workspace/lib/engine/sessions-list.js +27 -0
  57. package/workspace/lib/engine/signal-rules.js +95 -0
  58. package/workspace/lib/engine/signals/cards.js +57 -0
  59. package/workspace/lib/engine/signals/detectors.js +370 -0
  60. package/workspace/lib/engine/signals/facts-cli.js +8 -0
  61. package/workspace/lib/engine/signals/facts.js +197 -0
  62. package/workspace/lib/engine/signals/index.js +33 -0
  63. package/workspace/lib/engine/signals/kinds.js +110 -0
  64. package/workspace/lib/engine/signals/layout-paths.js +44 -0
  65. package/workspace/lib/engine/signals/options.js +24 -0
  66. package/workspace/lib/engine/signals/score.js +72 -0
  67. package/workspace/lib/engine/signals/select.js +47 -0
  68. package/workspace/lib/engine/step-planner.js +65 -0
  69. package/workspace/lib/engine/trust/claim-rules.json +93 -0
  70. package/workspace/lib/engine/trust/import-calendar.js +21 -0
  71. package/workspace/lib/engine/trust/import-guard.js +36 -0
  72. package/workspace/lib/engine/trust/ledger.js +53 -0
  73. package/workspace/lib/engine/trust/outbox.js +45 -0
  74. package/workspace/lib/engine/trust/plan.js +5 -0
  75. package/workspace/lib/engine/trust/trust.js +21 -0
  76. package/workspace/lib/engine/truth-table.js +57 -0
  77. package/workspace/lib/engine/workspace-lib.js +24 -0
  78. package/workspace/lib/factory-join.js +1 -1
  79. package/workspace/lib/facts-async.js +4 -4
  80. package/workspace/lib/headless.js +95 -0
  81. package/workspace/lib/import-overlay.js +2 -1
  82. package/workspace/lib/jira-status.js +30 -3
  83. package/workspace/lib/notes-capture.js +9 -1
  84. package/workspace/lib/notes-final.js +38 -13
  85. package/workspace/lib/notes-replay.js +125 -0
  86. package/workspace/lib/notes-screen.js +180 -0
  87. package/workspace/lib/notes-whisper.js +10 -3
  88. package/workspace/lib/outbox-store.js +10 -6
  89. package/workspace/lib/pmos-verbs.js +3 -1
  90. package/workspace/lib/proactive-cli.js +7 -0
  91. package/workspace/lib/proactive.js +15 -2
  92. package/workspace/lib/records-dir.js +29 -0
  93. package/workspace/lib/repo-script.js +36 -0
  94. package/workspace/lib/report-dashboards.js +5 -2
  95. package/workspace/lib/report-frame.js +122 -0
  96. package/workspace/lib/report-schedules.js +113 -0
  97. package/workspace/lib/route-modules.js +9 -2
  98. package/workspace/lib/routes-boards.js +734 -0
  99. package/workspace/lib/routes-brief-page.js +13 -4
  100. package/workspace/lib/routes-briefing.js +22 -11
  101. package/workspace/lib/routes-c3-init.js +9 -8
  102. package/workspace/lib/routes-c3-life.js +2 -1
  103. package/workspace/lib/routes-c3-reports.js +99 -15
  104. package/workspace/lib/routes-c3-strategy.js +7 -5
  105. package/workspace/lib/routes-c3-timeline.js +3 -2
  106. package/workspace/lib/routes-calendar-import.js +3 -2
  107. package/workspace/lib/routes-calendar-views.js +9 -4
  108. package/workspace/lib/routes-doc-health.js +2 -1
  109. package/workspace/lib/routes-doc-sheet.js +2 -1
  110. package/workspace/lib/routes-forecast.js +2 -1
  111. package/workspace/lib/routes-hooks.js +3 -2
  112. package/workspace/lib/routes-initiatives.js +2 -1
  113. package/workspace/lib/routes-intake.js +2 -1
  114. package/workspace/lib/routes-notes.js +55 -9
  115. package/workspace/lib/routes-outbox.js +23 -3
  116. package/workspace/lib/routes-pm-items.js +19 -6
  117. package/workspace/lib/routes-records.js +10 -7
  118. package/workspace/lib/routes-releases.js +2 -1
  119. package/workspace/lib/routes-report-facts.js +4 -3
  120. package/workspace/lib/routes-report-families.js +2 -1
  121. package/workspace/lib/routes-roadmap-ops.js +17 -14
  122. package/workspace/lib/routes-runs.js +6 -5
  123. package/workspace/lib/routes-settings.js +51 -0
  124. package/workspace/lib/routes-signals.js +3 -2
  125. package/workspace/lib/routes-stage-tools.js +220 -0
  126. package/workspace/lib/run-answers.js +2 -1
  127. package/workspace/lib/settings-store.js +226 -0
  128. package/workspace/lib/stage-tools.js +208 -0
  129. package/workspace/lib/vendor/scripts/signals/layout-paths.js +2 -1
  130. package/workspace/lib/vendor/scripts/workspace-lib.js +24 -0
  131. package/workspace/lib/work-evidence.js +73 -0
  132. package/workspace/package-lock.json +2 -2
  133. package/workspace/package.json +1 -1
  134. package/workspace/public/pm/briefing-deck.js +63 -17
  135. package/workspace/public/pm/briefing-model.js +36 -7
  136. package/workspace/public/pm/c3-ambient.js +100 -0
  137. package/workspace/public/pm/c3-brief-needs.js +26 -6
  138. package/workspace/public/pm/c3-brief-pulse.js +12 -4
  139. package/workspace/public/pm/c3-brief-today.js +56 -21
  140. package/workspace/public/pm/c3-brief.js +2 -1
  141. package/workspace/public/pm/c3-calendar.js +161 -42
  142. package/workspace/public/pm/c3-calpop.js +5 -5
  143. package/workspace/public/pm/c3-demo-boards.js +649 -0
  144. package/workspace/public/pm/c3-demo-brief-page.js +1 -1
  145. package/workspace/public/pm/c3-demo-cal4.js +3 -2
  146. package/workspace/public/pm/c3-demo-cal5.js +52 -0
  147. package/workspace/public/pm/c3-demo-data.js +17 -27
  148. package/workspace/public/pm/c3-demo-rep3.js +46 -5
  149. package/workspace/public/pm/c3-demo.js +2 -1
  150. package/workspace/public/pm/c3-dock.js +1 -1
  151. package/workspace/public/pm/c3-ib-docs.js +80 -0
  152. package/workspace/public/pm/c3-ib-initiatives.js +134 -0
  153. package/workspace/public/pm/c3-ib-items.js +201 -0
  154. package/workspace/public/pm/c3-ib-releases.js +51 -0
  155. package/workspace/public/pm/c3-ib-sprints.js +55 -0
  156. package/workspace/public/pm/c3-ib-triage.js +178 -0
  157. package/workspace/public/pm/c3-ii-tabs.js +240 -0
  158. package/workspace/public/pm/c3-ini-records.js +197 -0
  159. package/workspace/public/pm/c3-ini-timeline.js +278 -0
  160. package/workspace/public/pm/c3-ini-ui.js +229 -0
  161. package/workspace/public/pm/c3-ini-work.js +139 -0
  162. package/workspace/public/pm/c3-initiatives.css +218 -0
  163. package/workspace/public/pm/c3-initiatives.js +741 -0
  164. package/workspace/public/pm/c3-plan.js +2 -2
  165. package/workspace/public/pm/c3-reports.css +47 -47
  166. package/workspace/public/pm/c3-reports.js +223 -185
  167. package/workspace/public/pm/c3-settings.css +74 -0
  168. package/workspace/public/pm/c3-settings.js +703 -0
  169. package/workspace/public/pm/c3-shell.js +102 -36
  170. package/workspace/public/pm/c3-terminal-icons.js +2 -2
  171. package/workspace/public/pm/c3-terminal.js +2 -2
  172. package/workspace/public/pm/calendar-views.js +1 -1
  173. package/workspace/public/pm/components.css +319 -213
  174. package/workspace/public/pm/console3.css +99 -23
  175. package/workspace/public/pm/dest-notes.js +26 -4
  176. package/workspace/public/pm/notes-screen-capture.js +165 -0
  177. package/workspace/public/pm/notes-screen-view.js +98 -0
  178. package/workspace/public/pm/panel-trees.js +5 -2
  179. package/workspace/public/pm/pm-work-view.js +233 -146
  180. package/workspace/public/pm/shell-bell.js +3 -2
  181. package/workspace/public/pm/shell-recording.js +29 -8
  182. package/workspace/public/pm/tokens.css +2 -2
  183. package/workspace/public/pm/view-notes.js +46 -9
  184. package/workspace/public/pm/welcome.js +197 -89
  185. package/workspace/public/welcome.html +6 -4
  186. package/workspace/server.js +105 -6
  187. package/workspace/skills/day-check.skill +1 -1
  188. package/workspace/skills/jira-sync.skill +6 -4
  189. package/workspace/skills/morning-brief.skill +1 -1
  190. package/workspace/test-files.json +12 -3
  191. package/workspace/test-lists/c3-ib-items.json +9 -0
  192. package/workspace/test-lists/c3-ib-more.json +9 -0
  193. package/workspace/test-lists/c3-ib-triage-backlog.json +9 -0
  194. package/workspace/test-lists/c3-initiative-boards.json +6 -0
  195. package/workspace/test-lists/calendar-tidy-up.json +6 -0
  196. package/workspace/test-lists/console3-briefing.json +9 -0
  197. package/workspace/test-lists/console3-foundation.json +9 -0
  198. package/workspace/test-lists/console3-reports.json +3 -3
  199. package/workspace/test-lists/meeting-notes.json +10 -4
  200. package/workspace/test-lists/workspace-headless-jobs.json +5 -0
  201. package/workspace/test-lists/workspace-package-script-closure.json +6 -0
  202. package/workspace/lib/vendor/workspace/lib/actor.js +0 -91
  203. package/workspace/lib/vendor/workspace/lib/board-cache.js +0 -156
  204. package/workspace/lib/vendor/workspace/lib/console-state.js +0 -295
  205. package/workspace/lib/vendor/workspace/lib/factory-join.js +0 -158
  206. package/workspace/lib/vendor/workspace/lib/git-info.js +0 -306
  207. package/workspace/lib/vendor/workspace/lib/plan-items.js +0 -922
  208. package/workspace/lib/vendor/workspace/lib/plan-tree.js +0 -419
  209. package/workspace/lib/vendor/workspace/lib/review-gate.js +0 -39
  210. package/workspace/lib/vendor/workspace/lib/sink.js +0 -321
  211. package/workspace/lib/vendor/workspace/lib/state-root.js +0 -587
@@ -3,7 +3,7 @@
3
3
  * file draws only the frame and composes the real views into it, each through its own mount function:
4
4
  * Briefing one column, the brief: the deck (mountBriefing), Today (c3-brief-today.js), Needs you (c3-brief-needs.js), Portfolio pulse (c3-brief-pulse.js)
5
5
  * right the dock (c3-dock.js): the repository, branch and session tree, the selected session's detail, "Open the session" to /
6
- * Every tab but Briefing also carries the mockup's "Plan: Decide what gates the day" lens row and deck (c3-plan.js planNode).
6
+ * Initiatives and Reports also carry the mockup's "Plan: Decide what gates the day" lens row and deck (c3-plan.js planNode).
7
7
  * Calendar mountCalendarViews Work mountPmWork
8
8
  * Initiatives a switch among mountLifecycle, mountForecast and mountReleases Reports mountReportFamilies, mountOutbox
9
9
  * The bell, the ticker, the banner and the recording group are the shell-*.js modules. The old app (/) is not touched.
@@ -11,27 +11,27 @@
11
11
  * The workspace id is ?ws= (default: the first of GET /api/workspaces). The views read some routes without a ws, so a GET to /api/
12
12
  * that names none gets the page's ws added (see withWs). No dialog is used. Presses are the views' own. */
13
13
  import { el } from './dom.js';
14
- import { row } from './primitives.js';
14
+ import { row, button } from './primitives.js';
15
15
  import { pmGet } from './http.js';
16
16
  import { ensureC3Css, c3ic, c3Phone, mountPmWork } from './pm-work-view.js';
17
17
  import { mountBriefing, showCard, flipBack } from './briefing-deck.js';
18
18
  import { mountCalendarViews } from './calendar-views.js';
19
19
  import { todaySection } from './c3-brief-today.js';
20
- import { needsSection } from './c3-brief-needs.js';
20
+ import { needsSection, needsCount } from './c3-brief-needs.js';
21
21
  import { pulse } from './c3-brief-pulse.js';
22
- import { loadBriefPage } from './c3-brief-data.js';
22
+ import { loadBriefPage, onBriefPage } from './c3-brief-data.js';
23
23
  import { mountLifecycle } from './lifecycle-lens.js';
24
24
  import { mountForecast } from './timeline-forecast-view.js';
25
25
  import { mountReleases } from './releases-view.js';
26
26
  import { mountReportFamilies } from './report-families-view.js';
27
- import { mountReports } from './c3-reports.js';
27
+ import { mountReports, showReportsView } from './c3-reports.js';
28
28
  import { mountOutbox } from './outbox-panel.js';
29
29
  import { createDock } from './c3-dock.js';
30
30
  import { reviewSection, handledRow, trustCard, footLine, rowApi, BX, toast, setNeedsTitle, hooks as briefHooks } from './c3-brief.js';
31
31
  import { stopCard } from './c3-misc-panels.js';
32
32
  import { openSheet } from './c3-sheet.js';
33
33
  import { mountCalPopupTrigger } from './c3-calpop.js';
34
- import { planNode, setPlanScope, dueCard, crossLinks, newInitLine, portfolioCard } from './c3-plan.js';
34
+ import { planNode, dueCard, crossLinks, newInitLine, portfolioCard } from './c3-plan.js';
35
35
  import { initiativesViewCtx } from './registry.js';
36
36
  import { mount as mountRoadmap } from './c3-roadmap.js';
37
37
  import { mount as mountBreakdown } from './c3-breakdown.js';
@@ -40,13 +40,16 @@ import { mount as mountTickets, badge as ticketsBadge } from './c3-tickets.js';
40
40
  import { mount as mountDocs, badge as docsBadge } from './c3-docs.js';
41
41
  import { mount as mountSignals, badge as signalsBadge } from './c3-signals.js';
42
42
  import { mountDaypart } from './c3-daypart.js';
43
+ import { mountAmbient } from './c3-ambient.js';
43
44
  import { mountTicker } from './shell-ticker.js';
44
45
  import { mountBell, openBellList } from './shell-bell.js';
45
46
  import { onBriefing, loadBriefing } from './shell-ticker.js';
46
47
  import { registry, filterPalette, rowStep } from './briefing-model.js';
47
48
  import { freshnessLine } from './freshness-line.js';
49
+ import { mountInitiatives } from './c3-initiatives.js';
50
+ import { mountSettings, gearIcon, applyLook } from './c3-settings.js';
48
51
 
49
- /* In demo mode each tab's deck asks for its own cards (the mockup draws a different deck per tab); real mode sends no scope. */
52
+ /* In demo mode each tab's deck asks for its own cards (the mockup draws a different deck per tab); real mode sends no scope. Reports always sends its own (repTab). */
50
53
  const sc = (name) => (DEMO ? name : undefined);
51
54
  const TABS = [['brief', 'Briefing', 'sun'], ['cal', 'Calendar', 'cal'], ['work', 'Work', 'todo'], ['init', 'Initiatives', 'init'], ['rep', 'Reports', 'report']];
52
55
  /* The mockup's eight Initiatives views, in its order: Lifecycle, Roadmap, Breakdown, Timeline (= Forecast), Tickets, Releases, Documents, Signals. */
@@ -74,12 +77,17 @@ function withWs() {
74
77
  };
75
78
  }
76
79
 
80
+ /* t is light, dark or system (Settings, Appearance); system follows the computer and changes with it. */
81
+ const darkQuery = typeof matchMedia === 'function' ? matchMedia('(prefers-color-scheme: dark)') : null;
77
82
  function setTheme(t) {
78
- S.theme = t === 'dark' ? 'dark' : 'light';
83
+ S.themePref = t === 'dark' || t === 'system' ? t : 'light';
84
+ S.theme = S.themePref === 'system' ? (darkQuery && darkQuery.matches ? 'dark' : 'light') : S.themePref;
79
85
  document.documentElement.setAttribute('data-pm-page-theme', S.theme);
80
86
  S.root.setAttribute('data-pm-theme', S.theme);
81
- store.set(THEME_KEY, S.theme);
87
+ store.set(THEME_KEY, S.themePref);
88
+ if (S.settings && S.settingsCtx) { S.settingsCtx.theme = S.theme; S.settingsCtx.themePref = S.themePref; }
82
89
  }
90
+ if (darkQuery && darkQuery.addEventListener) darkQuery.addEventListener('change', () => { if (S.themePref === 'system' && S.root) setTheme('system'); });
83
91
 
84
92
  /* ── header: the mockup's .top (title, date line) and .tools (terminal, recording, bell, search, more) ── */
85
93
  const dateLine = () => {
@@ -94,9 +102,9 @@ function header() {
94
102
  S.ttl = el('h1', { 'data-c3pg': 'title' });
95
103
  S.sub = el('div', { class: 'sub', 'data-c3pg': 'date' }, dateLine());
96
104
  S.termBdg = el('span', { class: 'bdg', hidden: true, 'data-c3pg': 'term-count' }, '0');
97
- /* the AGENT button (the pulse glyph, as the mockup's Sessions pill): the Terminal page, the full agent surfaces, with the needs-you badge. The dock's own button is the TERMINAL glyph (c3-terminal-icons.js); Ctrl J and the More menu fold the dock. */
98
- S.termBtn = el('button', { type: 'button', class: 'psess', title: 'Sessions: the agent surfaces, results to accept and questions for you', 'aria-label': 'Sessions: the agent surfaces, results to accept and questions for you', 'aria-haspopup': 'dialog', 'data-c3pg': 'agent-button',
99
- style: 'display:flex;gap:6px;align-items:center', onclick: () => { import('./c3-terminal.js').then((m) => m.openTerminalPage()); } }, ic('pulse', 'sm'), 'Sessions', S.termBdg); // the mockup's header button (agent-surfaces.html #act)
105
+ /* the AGENT button (the bolt glyph, as the mockup's Sessions pill): the Terminal page, the full agent surfaces, with the needs-you badge. The dock's own button is the TERMINAL glyph (c3-terminal-icons.js); Ctrl J and the More menu fold the dock. */
106
+ S.termBtn = button({ variant: 'ghost', label: [ic('bolt', 'sm'), 'Sessions', S.termBdg], onClick: () => { import('./c3-terminal.js').then((m) => m.openTerminalPage()); },
107
+ attrs: { class: 'psess', title: 'Agent surfaces: sessions, results to accept, questions for you', 'aria-label': 'Agent surfaces: sessions, results to accept, questions for you', 'aria-haspopup': 'dialog', 'data-c3pg': 'agent-button' } }); // the mockup's header button (agent-surfaces.html #act)
100
108
  S.tools = el('div', { class: 'tools', 'data-c3pg': 'tools' });
101
109
  if (S.demoMod) S.tools.append(S.demoMod.demoPill());
102
110
  S.tools.append(S.termBtn);
@@ -104,6 +112,9 @@ function header() {
104
112
  S.tools.append(S.bellHost);
105
113
  S.tools.append(ibtn('Search (Ctrl K)', 'search', { 'data-c3pg': 'search', 'aria-haspopup': 'dialog', onclick: () => openPalette() }));
106
114
  mountCalPopupTrigger(S.tools); // the calendar button (before Search) and the key C (c3-calpop.js)
115
+ /* Settings is a page of its own, not a tab: the gear opens it, as do the More menu, the palette and ?tab=settings (c3-settings.js). */
116
+ S.gearBtn = ibtn('Settings', 'info', { 'data-c3pg': 'settings', onclick: () => showTab('settings', true) }); S.gearBtn.replaceChildren(gearIcon());
117
+ S.tools.append(S.gearBtn);
107
118
  S.moreBtn = ibtn('More', 'more', { 'aria-haspopup': 'menu', 'aria-expanded': 'false', 'data-c3pg': 'more', onclick: toggleMore });
108
119
  S.tools.append(el('span', { class: 'c3pg-menuwrap' }, S.moreBtn));
109
120
  /* workspace-data-freshness R9: how old the sources are, quiet when they are up to date (freshness-line.js) */
@@ -123,6 +134,7 @@ function toggleMore() {
123
134
  item('spark', 'Agent playbooks', () => openPlaybooks(), false, { 'data-a': 'pbopen' }),
124
135
  item('phone', 'Phone preview', () => togglePreview(), !!S.preview, { 'data-a': 'phone' }),
125
136
  item(S.theme === 'dark' ? 'sun' : 'moon', S.theme === 'dark' ? 'Light theme' : 'Dark theme', () => setTheme(S.theme === 'dark' ? 'light' : 'dark')),
137
+ ((b) => { b.firstChild.replaceWith(gearIcon('sm')); return b; })(item('info', 'Settings', () => showTab('settings', true), false, { 'data-a': 'settings' })),
126
138
  item('term', S.dockOpen ? 'Fold the dock' : 'Open the dock', () => { S.dockOpen = !S.dockOpen; syncDock(); }, false, { 'data-a': 'dockt' }, 'Ctrl J'),
127
139
  el('a', { class: 'c3pg-mi', role: 'menuitem', href: '/' }, c3ic('term', 'sm'), el('span', null, 'Open the main app')));
128
140
  S.root.append(S.menu); // on the page root with position:fixed, so no ancestor's overflow, transform or stacking order can hide it
@@ -226,9 +238,13 @@ function openPlaybooks() {
226
238
  function tabBar() {
227
239
  S.tabBtns = {};
228
240
  const nav = el('nav', { class: 'c3tabs', role: 'tablist', 'aria-label': 'Places' });
229
- S.workBdg = el('span', { class: 'bdg', hidden: true, 'data-c3pg': 'work-count' }, '0');
241
+ S.workBdg = el('span', { class: 'bdg', hidden: true, 'data-c3pg': 'work-count', title: 'Inbox to decide' }, '0');
242
+ S.briefBdg = el('span', { class: 'bdg', hidden: true, 'data-c3pg': 'brief-count', title: 'Needs you' }, '0');
243
+ S.initBdg = el('span', { class: 'bdg', hidden: true, 'data-c3pg': 'init-count', title: 'New asks to triage' }, '0');
244
+ const BDG = { brief: S.briefBdg, work: S.workBdg, init: S.initBdg };
230
245
  for (const [id, label, icon] of TABS) {
231
246
  const b = el('button', { type: 'button', role: 'tab', id: 'c3tab-' + id, 'aria-controls': 'c3pane-' + id, 'data-c3-tab': id, onclick: () => showTab(id, true) }, c3ic(icon), label, id === 'work' ? S.workBdg : null);
247
+ if (BDG[id] && id !== 'work') b.append(BDG[id]);
232
248
  S.tabBtns[id] = b;
233
249
  nav.append(b);
234
250
  }
@@ -243,41 +259,47 @@ function tabBar() {
243
259
  return nav;
244
260
  }
245
261
  function moveInd() {
262
+ if (S.ind) S.ind.hidden = !S.tabBtns[S.tab];
246
263
  const b = S.tabBtns[S.tab]; if (!b || !S.ind) return;
247
264
  S.ind.style.width = b.offsetWidth + 'px'; S.ind.style.transform = 'translateX(' + b.offsetLeft + 'px)';
248
265
  }
249
266
 
250
- /* ── the Briefing tab: one column, the brief ──
251
- * In order: the headline deck, Today (c3-brief-today.js), Needs you (c3-brief-needs.js) and Portfolio pulse (c3-brief-pulse.js). The ribbon and the embedded calendar are not here:
252
- * the calendar is the Calendar tab. The rows below Needs you (drafts, handled, what to stop) and the data-trust card are the briefing's own, when it carries them (the demo's). */
267
+ /* ── the Briefing tab: two columns, decisions and reads ──
268
+ * Left: the stance bar with the day-part switch and the headline deck (briefing-deck.js), then Needs you (c3-brief-needs.js).
269
+ * Right: Today (c3-brief-today.js) and Portfolio pulse (c3-brief-pulse.js). Review lives in Work's Review lens; Handled, What I could stop and
270
+ * Data you can lean on live in Reports > Drafts (console3-briefing-parity, PM decision 1). The ribbon and the embedded calendar are not here:
271
+ * the calendar is the Calendar tab. */
253
272
  function briefTab(pane) {
254
- const col = el('div', { class: 'bcol l' });
273
+ const left = el('div', { class: 'bcol l' });
274
+ const right = el('div', { class: 'bcol r' });
255
275
  const deckHost = el('div', { 'data-c3pg': 'deck-host' });
256
- const rvHost = el('div', { 'data-c3pg': 'review-host' }); const hdHost = el('div', { 'data-c3pg': 'handled-host' }); const trHost = el('div', { 'data-c3pg': 'trust-host' }); const ftHost = el('div'); const stHost = el('div', { 'data-c3pg': 'stop-host' }); S.stopHost = stHost; S.needsBox = null;
257
- S.deckHost = deckHost;
276
+ const ftHost = el('div');
277
+ S.deckHost = deckHost; S.needsBox = null;
258
278
  let sig = '';
259
- onBriefing((b) => { // below "Needs you": only what the briefing carries (the demo's); the real route sends none of these. Built again only when it changed: an open draft stays open.
260
- const g = JSON.stringify([b && b.review, b && b.handled, b && b.trust, b && b.foot, b && b.stop]);
279
+ onBriefing((b) => { // the briefing's own foot line, when it carries one (the demo's). Built again only when it changed.
280
+ const g = JSON.stringify(b && b.foot);
261
281
  if (g === sig) return; sig = g;
262
- rvHost.replaceChildren(...(b && b.review ? [reviewSection(b.review)] : [])); hdHost.replaceChildren(...(b && b.handled ? [handledRow(b.handled)] : []));
263
- stHost.replaceChildren(...[stopCard(b && b.stop)].filter(Boolean)); trHost.replaceChildren(...(b && b.trust ? [trustCard(b.trust)] : [])); ftHost.replaceChildren(...(b && b.foot ? [footLine(b.foot)] : []));
282
+ ftHost.replaceChildren(...(b && b.foot ? [footLine(b.foot)] : []));
264
283
  if (S.why) whyMark();
265
284
  });
266
285
  S.needsBox = needsSection(S);
267
- col.append(deckHost, todaySection(), S.needsBox, rvHost, hdHost, stHost, pulse(S), trHost);
268
- pane.append(el('div', { class: 'brief one' }, el('div', { class: 'bcols' }, col), ftHost));
286
+ left.append(deckHost, S.needsBox);
287
+ right.append(todaySection(), pulse(S));
288
+ pane.append(el('div', { class: 'brief' }, el('div', { class: 'bcols' }, left, right), ftHost));
269
289
  mountBriefing(deckHost, { noTicker: true, onLens: lensChanged });
270
290
  loadBriefPage();
271
291
  }
272
292
 
273
- /* The day-part switch: Reflect shows "Carries over" and "What I could stop" (the mockup's vBrief, line 1470). */
293
+ /* The day-part switch: Reflect names Needs you "Carries over" (the mockup's vBrief, line 1470). */
274
294
  function lensChanged(l) {
275
- const rf = l === 'Reflect'; if (S.stopHost) S.stopHost.hidden = !rf; setNeedsTitle(S.needsBox, rf);
295
+ setNeedsTitle(S.needsBox, l === 'Reflect');
276
296
  }
277
297
 
278
298
  /* ── global: where a notification goes, the person sheet, the palette and the keys ── */
279
299
  /* A bell entry or the banner goes to its target (console3.html nopen, line 2530): the card to turn over, the row to open, or the sheet to show. */
280
300
  function goTarget(t) {
301
+ /* a dashboard schedule's notice (routes-briefing.js notices): Reports, then its Drafts tab */
302
+ if (t && t.tab && BUILD[t.tab]) { showTab(t.tab, true); if (t.tab === 'rep' && t.view) showReportsView(t.view); return; }
281
303
  showTab('brief', true);
282
304
  if (!t) return;
283
305
  if (t.card !== undefined && t.card !== null) { showCard(S.deckHost, t.card); return; }
@@ -313,7 +335,8 @@ function openPerson(k) {
313
335
  /* the palette (console3.html palette, line 1944): one input, up to nine results across places, people, documents and sessions */
314
336
  function paletteItems() {
315
337
  const items = [{ label: 'Agent playbooks', kind: 'place', icon: 'spark', run: () => openPlaybooks() },
316
- ...TABS.map(([id, label, icon]) => ({ label, kind: 'place', icon, run: () => showTab(id, true) }))];
338
+ ...TABS.map(([id, label, icon]) => ({ label, kind: 'place', icon, run: () => showTab(id, true) })),
339
+ { label: 'Settings', kind: 'place', icon: 'shield', run: () => showTab('settings', true) }];
317
340
  for (const k of Object.keys(registry('people') || {})) { const p = registry('people')[k]; items.push({ label: p.n + ' · ' + p.role, kind: 'person', icon: 'user', run: () => openPerson(k) }); }
318
341
  for (const d of registry('docs') || []) items.push({ label: d.n, kind: 'document', icon: d.icon || 'doc', run: () => { showTab('init', true); const b = S.initSeg && S.initSeg.querySelector('[data-c3-init="documents"]'); if (b) b.click(); } });
319
342
  for (const x of S.palSessions || []) items.push({ label: (x.branch || x.tree || '') ? ((x.branch || String(x.tree).split('/').pop()) + ' · ' + x.name) : x.name, kind: 'session', icon: 'branch', run: () => openSession(x) });
@@ -474,12 +497,24 @@ function showInit(id) {
474
497
  }
475
498
  }
476
499
  }
477
- function repTab(pane) { mountReports(pane, { ws: S.ws, pmGet, drafts: draftsTab }); } // dashboards and the library; the old Reports content is the Drafts tab
500
+ /* Reports: the headline deck on top (the Plan deck of the Reports scope), the tabs, and on Drafts what the Briefing no longer carries
501
+ * (Handled, What I could stop, Data you can lean on) above "Reports to send" (the old families, their claims check and the outbox). */
502
+ /* The Reports deck always names its scope, so real mode gets the engine's report signals and never the Briefing's cards (console3-deck-flow-fixes). */
503
+ function repTab(pane) { mountReports(pane, { ws: S.ws, pmGet, deck: (h) => h.append(planNode('rep')), more: draftsMore, drafts: draftsTab }); }
504
+ function draftsMore(host) {
505
+ let sig = '';
506
+ onBriefing((b) => { // the same cards and the same condition as the Briefing had: only what the briefing carries (the demo's); redrawn only when it changed, so an open row stays open
507
+ const g = JSON.stringify([b && b.handled, b && b.stop, b && b.trust]);
508
+ if (g === sig) return; sig = g;
509
+ host.replaceChildren(...[b && b.handled ? handledRow(b.handled) : null, stopCard(b && b.stop), b && b.trust ? trustCard(b.trust) : null].filter(Boolean));
510
+ });
511
+ loadBriefing();
512
+ }
478
513
  function draftsTab(pane) {
479
514
  const fam = el('div', { 'data-c3pg': 'families-host' }); const out = el('div', { class: 'brief', 'data-c3pg': 'outbox-host', style: 'margin-top:var(--sp-4)' });
480
515
  pane.append(fam, out);
481
516
  pmGet('/api/report-families/facts?ws=' + encodeURIComponent(S.ws)).then((j) => {
482
- mountReportFamilies(fam, { reportFacts: j && j.ok ? j.facts : {}, plan: planNode(sc('rep')), portfolio: portfolioCard() });
517
+ mountReportFamilies(fam, { reportFacts: j && j.ok ? j.facts : {}, plan: null, portfolio: portfolioCard() });
483
518
  mountOutbox(out, {});
484
519
  /* The mockup's Reports draw the outbox's drafts under Communications only (the Messages queue); the other families have no outbox. */
485
520
  const sync = () => { out.hidden = !fam.querySelector('[data-family="communications"][aria-pressed="true"]'); };
@@ -487,25 +522,50 @@ function draftsTab(pane) {
487
522
  });
488
523
  }
489
524
 
490
- const BUILD = { brief: briefTab, cal: (p) => mountCalendarViews(p, { view: 'week', page: true, scope: sc('cal') }), work: (p) => { const plan = planNode(sc('work') && 'work:triage'); mountPmWork(p, { plan, onLens: (l) => { if (DEMO) setPlanScope(plan, 'work:' + l); } }); }, init: initTab, rep: repTab };
525
+ const BUILD = { brief: briefTab, cal: (p) => mountCalendarViews(p, { view: 'week', page: true, scope: sc('cal'), deck: planNode('cal', { bare: true }) }), work: (p) => { mountPmWork(p, { go: (t) => showTab(t, true) }); }, init: (p) => mountInitiatives(p, { ws: S.ws, workspaces: S.workspaces, demo: DEMO, showTab }), rep: repTab };
491
526
 
492
527
  function showTab(id, push) {
493
- if (!BUILD[id]) id = 'brief';
528
+ if (!BUILD[id] && id !== 'settings') id = 'brief';
494
529
  S.tab = id;
495
530
  for (const [t, label, icon] of TABS) {
496
531
  const on = t === id;
497
- S.tabBtns[t].setAttribute('aria-selected', String(on)); S.tabBtns[t].tabIndex = on ? 0 : -1;
532
+ S.tabBtns[t].setAttribute('aria-selected', String(on)); S.tabBtns[t].tabIndex = on ? 0 : (id === 'settings' && t === 'brief' ? 0 : -1);
498
533
  S.panes[t].hidden = !on;
499
534
  if (on) { S.ttl.replaceChildren(c3ic(icon, 'lg'), label); document.title = label + ' · PMOS console'; }
500
535
  }
501
- if (!S.mounted[id]) { S.mounted[id] = true; BUILD[id](S.panes[id]); }
536
+ S.panes.settings.hidden = id !== 'settings';
537
+ if (S.gearBtn) S.gearBtn.setAttribute('aria-pressed', String(id === 'settings'));
538
+ if (id === 'settings') {
539
+ S.ttl.replaceChildren(gearIcon('lg'), 'Settings'); document.title = 'Settings · PMOS console';
540
+ if (!S.settings) { S.settingsCtx = { ws: S.ws, workspaces: S.workspaces, theme: S.theme, themePref: S.themePref, setTheme, goTab: (t) => showTab(t, true), section: q.get('section') || '', root: S.root }; S.settings = mountSettings(S.panes.settings, S.settingsCtx); }
541
+ else S.settings.refresh();
542
+ } else if (!S.mounted[id]) { S.mounted[id] = true; BUILD[id](S.panes[id]); }
502
543
  S.root.dataset.tab = id;
503
544
  S.dockOpen = S.wide && id === 'brief'; syncDock();
504
545
  S.pageView.classList.remove('view'); void S.pageView.offsetWidth; S.pageView.classList.add('view');
505
546
  moveInd();
547
+ if (push) S.userNav = true;
506
548
  if (push) { try { const u = new URL(location.href); u.searchParams.set('tab', id); history.replaceState(null, '', u); } catch { /* no history */ } }
507
549
  }
508
550
 
551
+ const setBdg = (b, n) => { b.textContent = String(n); b.hidden = !n; };
552
+ S.setNeedsBdg = (n) => { S.needsLive = true; setBdg(S.briefBdg, n); };
553
+
554
+ /* Tab badges. Briefing: the Needs you count (the section's own count once it is drawn; before that, the same rules over the briefing and the brief page).
555
+ * Initiatives: the new asks to triage, that is the captures an agent triaged and the PM has not added yet (GET /api/needs, kind triage); the demo has four. */
556
+ function briefCount() {
557
+ let b = null; let page = null;
558
+ const upd = () => { if (!S.needsLive) setBdg(S.briefBdg, needsCount(b, page)); };
559
+ onBriefing((x) => { b = x; upd(); }); onBriefPage((x) => { page = x; upd(); });
560
+ loadBriefing(); if (S.tab !== 'brief') loadBriefPage();
561
+ }
562
+ async function askCount() {
563
+ if (S.demo) { setBdg(S.initBdg, 4); return; }
564
+ const j = await pmGet('/api/needs?ws=' + encodeURIComponent(S.ws)).catch(() => null);
565
+ if (!j || !Array.isArray(j.needs)) return;
566
+ setBdg(S.initBdg, j.needs.filter((r) => r && r.kind === 'triage').length);
567
+ }
568
+
509
569
  async function workCount() {
510
570
  const j = await pmGet('/api/pm-items').catch(() => null);
511
571
  if (!j || !j.ok || !j.lists) return;
@@ -533,10 +593,12 @@ async function boot() {
533
593
  S.tickHost = el('div', { class: 'c3pg-tick', 'data-c3pg': 'ticker-host' });
534
594
  S.pageView = el('div', { class: 'view' });
535
595
  for (const [id] of TABS) { S.panes[id] = el('div', { class: 'c3pg-pane', role: 'tabpanel', id: 'c3pane-' + id, 'aria-labelledby': 'c3tab-' + id, hidden: true, 'data-c3-pane': id }); S.pageView.append(S.panes[id]); }
596
+ S.panes.settings = el('div', { class: 'c3pg-pane', role: 'region', 'aria-label': 'Settings', hidden: true, 'data-c3-pane': 'settings' }); S.pageView.append(S.panes.settings);
536
597
  const main = el('div', { class: 'c3main' }, header(), S.tickHost, tabBar(), S.pageView);
537
598
  S.grid = el('div', { class: 'c3grid' }, main, ...dock());
538
599
  root.replaceChildren(S.grid);
539
600
  mountDaypart(root, root.querySelector('.c3top'));
601
+ mountAmbient(root); // the ambient motion canvas, once, behind the grid
540
602
 
541
603
  S.dock.setWorkspaces(S.workspaces, S.ws);
542
604
  mountBell(S.bellHost, { go: (_, t) => goTarget(t) });
@@ -548,9 +610,13 @@ async function boot() {
548
610
  S.narrow = matchMedia('(max-width:980px)');
549
611
  S.narrow.addEventListener('change', () => { S.wide = !S.narrow.matches; S.dockOpen = S.wide && S.tab === 'brief'; syncDock(); });
550
612
  S.wide = !S.narrow.matches;
551
- showTab(q.get('tab') || 'brief', false);
613
+ /* The saved settings set the start page (look.start) when the address names no tab, and turn the time-of-day tint and strip off or on. */
614
+ let start = q.get('tab');
615
+ const saved = DEMO ? null : await pmGet('/api/settings' + (S.ws ? '?ws=' + encodeURIComponent(S.ws) : '')).catch(() => null); // demo mode's workspace is not registered
616
+ if (saved && saved.ok) { applyLook(root, saved.settings); if (!start) { const s = (saved.settings || []).find((x) => x.key === 'look.start'); if (s && BUILD[s.value]) start = s.value; } }
617
+ if (!S.userNav) showTab(start || 'brief', false); // a press during the read above (the gear, a tab) wins over the start page
552
618
  new ResizeObserver(moveInd).observe(S.tabNav);
553
- workCount(); S.dock.start();
619
+ workCount(); briefCount(); askCount(); S.dock.start();
554
620
  setInterval(() => { S.sub.textContent = dateLine(); }, 30000);
555
621
  }
556
622
 
@@ -1,7 +1,7 @@
1
1
  /* pm/c3-terminal-icons.js — the icon language of the agent surfaces, and the few glyphs the console3 sprite does not hold.
2
2
  *
3
3
  * Two glyphs, never swapped (the mockups draw them apart: agent-surfaces.html and terminal-layout.html):
4
- * AGENT `pulse` (the mockup's i-pulse) the agent surfaces: sessions that need you, results to accept. Header button (with the needs-you badge) and the
4
+ * AGENT `bolt` (the mockup's i-bolt) the agent surfaces: sessions that need you, results to accept. Header button (with the needs-you badge) and the
5
5
  * dock's "Agent surfaces" button. It also means "the agent acts" everywhere else on the page (swipe right, suggested move).
6
6
  * TERMINAL `term` (`>_`) a real session's terminal: the dock rail button, a session's "Open the session" link, a session row's mark.
7
7
  * AGENT = "the interface that reads sessions for you". TERMINAL = "the raw CLI session". A button shows one or the other, with its own tooltip.
@@ -10,7 +10,7 @@
10
10
  * another module already added is not added again. */
11
11
  import { c3ic } from './pm-work-view.js';
12
12
 
13
- export const ICON = { agent: 'pulse', terminal: 'term' }; // the mockup's header Sessions pill draws the pulse glyph; the rail draws the terminal glyph
13
+ export const ICON = { agent: 'bolt', terminal: 'term' }; // the mockup's header Sessions pill draws the bolt glyph; the rail draws the terminal glyph
14
14
  export const TIPS = {
15
15
  agentHeader: (n) => (n ? 'Agent surfaces: ' + n + (n === 1 ? ' session needs you' : ' sessions need you') : 'Agent surfaces: sessions, results to accept, questions for you'),
16
16
  agentDock: 'Agent surfaces: the full page of sessions that need you',
@@ -13,7 +13,7 @@ import { source, isDemo } from './c3-terminal-data.js';
13
13
  import { mountSessions, disposeSessions } from './sessions-panel.js';
14
14
  import { mountEdge, disposeEdge } from './c3-terminal-edge.js';
15
15
 
16
- const TABS = [['sessions', 'Sessions', 'pulse'], ['edge', 'Edge cases', 'alert']];
16
+ const TABS = [['sessions', 'Sessions', 'bolt'], ['edge', 'Edge cases', 'alert']];
17
17
  const NEXT = [['', 'replies normally'], ['noresult', 'ends with no result'], ['rejected', 'is unreadable'], ['quiet', 'goes quiet'], ['unlabelled', 'has an unlabelled claim'], ['prose', 'answers in plain text'], ['attention', 'needs a sign-in'], ['crashed', 'crashes']];
18
18
  let sheet = null;
19
19
 
@@ -45,7 +45,7 @@ export async function openTerminalPage(o = {}) {
45
45
  }
46
46
  };
47
47
  const body = [el('div', { class: 'c3 c3e c3t c3t-page', 'data-c3t': 'page' }, el('div', { class: 'chips2', style: 'justify-content:space-between' }, seg, demoBar), note, host)];
48
- const s = openSheet({ title: 'Agent surfaces', icon: 'pulse', dw: true, body, onClose: () => { disposeSessions(host); disposeEdge(host); sheet = null; } });
48
+ const s = openSheet({ title: 'Agent surfaces', icon: 'bolt', dw: true, body, onClose: () => { disposeSessions(host); disposeEdge(host); sheet = null; } });
49
49
  sheet = s;
50
50
  await draw();
51
51
  return s;
@@ -31,7 +31,7 @@ const IX = () => (typeof window !== 'undefined' && window.__c3calix) || null;
31
31
  const baseId = (e) => String((e && e.id) || '').split('@')[0];
32
32
  const gotoTab = (id) => { const b = document.querySelector('[data-c3-tab="' + id + '"]'); if (b) b.click(); };
33
33
  const isC3 = (r) => !!(r && r.ctx && (r.ctx.ribbon || r.ctx.plan || r.ctx.page));
34
- const isCalTab = (x) => !!(x && x.ctx && (x.ctx.plan || x.ctx.page)); // the Calendar tab's own instance (the older mount passes plan, the page passes page)
34
+ const isCalTab = (x) => !!(x && x.ctx && (x.ctx.plan || (x.ctx.page && !x.ctx.overlay))); // the Calendar tab's own instance (the older mount passes plan, the page passes page)
35
35
 
36
36
  const NS = 'http://www.w3.org/2000/svg';
37
37
  const VIEWS = [['day', 'Day'], ['week', 'Week'], ['month', 'Month'], ['quarter', 'Quarter'], ['year', 'Year']];