@legioncodeinc/hive 0.1.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 (190) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +262 -0
  3. package/dist/cli-commands.d.ts +41 -0
  4. package/dist/cli-commands.js +76 -0
  5. package/dist/cli-commands.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +46 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/daemon/dashboard/app.js +118 -0
  10. package/dist/daemon/dashboard/host.d.ts +90 -0
  11. package/dist/daemon/dashboard/host.js +172 -0
  12. package/dist/daemon/dashboard/host.js.map +1 -0
  13. package/dist/daemon/dashboard/web-assets.d.ts +88 -0
  14. package/dist/daemon/dashboard/web-assets.js +195 -0
  15. package/dist/daemon/dashboard/web-assets.js.map +1 -0
  16. package/dist/daemon/fleet-status.d.ts +15 -0
  17. package/dist/daemon/fleet-status.js +56 -0
  18. package/dist/daemon/fleet-status.js.map +1 -0
  19. package/dist/daemon/gate.d.ts +55 -0
  20. package/dist/daemon/gate.js +128 -0
  21. package/dist/daemon/gate.js.map +1 -0
  22. package/dist/daemon/proxy.d.ts +38 -0
  23. package/dist/daemon/proxy.js +126 -0
  24. package/dist/daemon/proxy.js.map +1 -0
  25. package/dist/daemon/registry.d.ts +31 -0
  26. package/dist/daemon/registry.js +120 -0
  27. package/dist/daemon/registry.js.map +1 -0
  28. package/dist/daemon/server.d.ts +41 -0
  29. package/dist/daemon/server.js +126 -0
  30. package/dist/daemon/server.js.map +1 -0
  31. package/dist/daemon/setup-auth.d.ts +37 -0
  32. package/dist/daemon/setup-auth.js +51 -0
  33. package/dist/daemon/setup-auth.js.map +1 -0
  34. package/dist/daemon/telemetry-proxy.d.ts +47 -0
  35. package/dist/daemon/telemetry-proxy.js +63 -0
  36. package/dist/daemon/telemetry-proxy.js.map +1 -0
  37. package/dist/dashboard/contracts.d.ts +235 -0
  38. package/dist/dashboard/contracts.js +43 -0
  39. package/dist/dashboard/contracts.js.map +1 -0
  40. package/dist/dashboard/web/app.d.ts +44 -0
  41. package/dist/dashboard/web/app.js +179 -0
  42. package/dist/dashboard/web/app.js.map +1 -0
  43. package/dist/dashboard/web/boot-route.d.ts +23 -0
  44. package/dist/dashboard/web/boot-route.js +29 -0
  45. package/dist/dashboard/web/boot-route.js.map +1 -0
  46. package/dist/dashboard/web/build-graph-button.d.ts +46 -0
  47. package/dist/dashboard/web/build-graph-button.js +81 -0
  48. package/dist/dashboard/web/build-graph-button.js.map +1 -0
  49. package/dist/dashboard/web/buzzing-screen.d.ts +30 -0
  50. package/dist/dashboard/web/buzzing-screen.js +135 -0
  51. package/dist/dashboard/web/buzzing-screen.js.map +1 -0
  52. package/dist/dashboard/web/folder-picker.d.ts +54 -0
  53. package/dist/dashboard/web/folder-picker.js +125 -0
  54. package/dist/dashboard/web/folder-picker.js.map +1 -0
  55. package/dist/dashboard/web/graph-layout.d.ts +101 -0
  56. package/dist/dashboard/web/graph-layout.js +0 -0
  57. package/dist/dashboard/web/graph-layout.js.map +1 -0
  58. package/dist/dashboard/web/harness-strip.d.ts +38 -0
  59. package/dist/dashboard/web/harness-strip.js +65 -0
  60. package/dist/dashboard/web/harness-strip.js.map +1 -0
  61. package/dist/dashboard/web/health-rail.d.ts +26 -0
  62. package/dist/dashboard/web/health-rail.js +61 -0
  63. package/dist/dashboard/web/health-rail.js.map +1 -0
  64. package/dist/dashboard/web/main.d.ts +22 -0
  65. package/dist/dashboard/web/main.js +51 -0
  66. package/dist/dashboard/web/main.js.map +1 -0
  67. package/dist/dashboard/web/needs-project.d.ts +51 -0
  68. package/dist/dashboard/web/needs-project.js +69 -0
  69. package/dist/dashboard/web/needs-project.js.map +1 -0
  70. package/dist/dashboard/web/page-frame.d.ts +95 -0
  71. package/dist/dashboard/web/page-frame.js +98 -0
  72. package/dist/dashboard/web/page-frame.js.map +1 -0
  73. package/dist/dashboard/web/pages/coming-soon.d.ts +23 -0
  74. package/dist/dashboard/web/pages/coming-soon.js +19 -0
  75. package/dist/dashboard/web/pages/coming-soon.js.map +1 -0
  76. package/dist/dashboard/web/pages/dashboard.d.ts +32 -0
  77. package/dist/dashboard/web/pages/dashboard.js +205 -0
  78. package/dist/dashboard/web/pages/dashboard.js.map +1 -0
  79. package/dist/dashboard/web/pages/graph.d.ts +72 -0
  80. package/dist/dashboard/web/pages/graph.js +362 -0
  81. package/dist/dashboard/web/pages/graph.js.map +1 -0
  82. package/dist/dashboard/web/pages/harnesses.d.ts +65 -0
  83. package/dist/dashboard/web/pages/harnesses.js +210 -0
  84. package/dist/dashboard/web/pages/harnesses.js.map +1 -0
  85. package/dist/dashboard/web/pages/health.d.ts +22 -0
  86. package/dist/dashboard/web/pages/health.js +91 -0
  87. package/dist/dashboard/web/pages/health.js.map +1 -0
  88. package/dist/dashboard/web/pages/lifecycle-panel.d.ts +60 -0
  89. package/dist/dashboard/web/pages/lifecycle-panel.js +187 -0
  90. package/dist/dashboard/web/pages/lifecycle-panel.js.map +1 -0
  91. package/dist/dashboard/web/pages/logs.d.ts +55 -0
  92. package/dist/dashboard/web/pages/logs.js +311 -0
  93. package/dist/dashboard/web/pages/logs.js.map +1 -0
  94. package/dist/dashboard/web/pages/memories.d.ts +35 -0
  95. package/dist/dashboard/web/pages/memories.js +439 -0
  96. package/dist/dashboard/web/pages/memories.js.map +1 -0
  97. package/dist/dashboard/web/pages/projects.d.ts +38 -0
  98. package/dist/dashboard/web/pages/projects.js +325 -0
  99. package/dist/dashboard/web/pages/projects.js.map +1 -0
  100. package/dist/dashboard/web/pages/roi-chart.d.ts +52 -0
  101. package/dist/dashboard/web/pages/roi-chart.js +133 -0
  102. package/dist/dashboard/web/pages/roi-chart.js.map +1 -0
  103. package/dist/dashboard/web/pages/roi.d.ts +93 -0
  104. package/dist/dashboard/web/pages/roi.js +375 -0
  105. package/dist/dashboard/web/pages/roi.js.map +1 -0
  106. package/dist/dashboard/web/pages/settings.d.ts +103 -0
  107. package/dist/dashboard/web/pages/settings.js +420 -0
  108. package/dist/dashboard/web/pages/settings.js.map +1 -0
  109. package/dist/dashboard/web/pages/sync.d.ts +59 -0
  110. package/dist/dashboard/web/pages/sync.js +280 -0
  111. package/dist/dashboard/web/pages/sync.js.map +1 -0
  112. package/dist/dashboard/web/panels.d.ts +178 -0
  113. package/dist/dashboard/web/panels.js +471 -0
  114. package/dist/dashboard/web/panels.js.map +1 -0
  115. package/dist/dashboard/web/primitives.d.ts +80 -0
  116. package/dist/dashboard/web/primitives.js +208 -0
  117. package/dist/dashboard/web/primitives.js.map +1 -0
  118. package/dist/dashboard/web/readiness-splash.d.ts +39 -0
  119. package/dist/dashboard/web/readiness-splash.js +201 -0
  120. package/dist/dashboard/web/readiness-splash.js.map +1 -0
  121. package/dist/dashboard/web/registry.d.ts +96 -0
  122. package/dist/dashboard/web/registry.js +99 -0
  123. package/dist/dashboard/web/registry.js.map +1 -0
  124. package/dist/dashboard/web/router.d.ts +48 -0
  125. package/dist/dashboard/web/router.js +76 -0
  126. package/dist/dashboard/web/router.js.map +1 -0
  127. package/dist/dashboard/web/scope-context.d.ts +155 -0
  128. package/dist/dashboard/web/scope-context.js +266 -0
  129. package/dist/dashboard/web/scope-context.js.map +1 -0
  130. package/dist/dashboard/web/service-icons.d.ts +33 -0
  131. package/dist/dashboard/web/service-icons.js +80 -0
  132. package/dist/dashboard/web/service-icons.js.map +1 -0
  133. package/dist/dashboard/web/setup-gate.d.ts +116 -0
  134. package/dist/dashboard/web/setup-gate.js +272 -0
  135. package/dist/dashboard/web/setup-gate.js.map +1 -0
  136. package/dist/dashboard/web/sidebar.d.ts +63 -0
  137. package/dist/dashboard/web/sidebar.js +91 -0
  138. package/dist/dashboard/web/sidebar.js.map +1 -0
  139. package/dist/dashboard/web/use-fleet-telemetry.d.ts +111 -0
  140. package/dist/dashboard/web/use-fleet-telemetry.js +278 -0
  141. package/dist/dashboard/web/use-fleet-telemetry.js.map +1 -0
  142. package/dist/dashboard/web/wire.d.ts +1937 -0
  143. package/dist/dashboard/web/wire.js +1837 -0
  144. package/dist/dashboard/web/wire.js.map +1 -0
  145. package/dist/errors.d.ts +5 -0
  146. package/dist/errors.js +11 -0
  147. package/dist/errors.js.map +1 -0
  148. package/dist/install/registry.d.ts +35 -0
  149. package/dist/install/registry.js +110 -0
  150. package/dist/install/registry.js.map +1 -0
  151. package/dist/lock.d.ts +10 -0
  152. package/dist/lock.js +87 -0
  153. package/dist/lock.js.map +1 -0
  154. package/dist/service/commands.d.ts +15 -0
  155. package/dist/service/commands.js +49 -0
  156. package/dist/service/commands.js.map +1 -0
  157. package/dist/service/index.d.ts +38 -0
  158. package/dist/service/index.js +174 -0
  159. package/dist/service/index.js.map +1 -0
  160. package/dist/service/platform.d.ts +37 -0
  161. package/dist/service/platform.js +67 -0
  162. package/dist/service/platform.js.map +1 -0
  163. package/dist/service/templates.d.ts +10 -0
  164. package/dist/service/templates.js +120 -0
  165. package/dist/service/templates.js.map +1 -0
  166. package/dist/shared/constants.d.ts +15 -0
  167. package/dist/shared/constants.js +19 -0
  168. package/dist/shared/constants.js.map +1 -0
  169. package/dist/shared/daemon-routing.d.ts +11 -0
  170. package/dist/shared/daemon-routing.js +38 -0
  171. package/dist/shared/daemon-routing.js.map +1 -0
  172. package/dist/shared/fleet-readiness.d.ts +18 -0
  173. package/dist/shared/fleet-readiness.js +9 -0
  174. package/dist/shared/fleet-readiness.js.map +1 -0
  175. package/dist/shared/fleet-telemetry.d.ts +68 -0
  176. package/dist/shared/fleet-telemetry.js +61 -0
  177. package/dist/shared/fleet-telemetry.js.map +1 -0
  178. package/dist/shared/lifecycle-flags.d.ts +34 -0
  179. package/dist/shared/lifecycle-flags.js +39 -0
  180. package/dist/shared/lifecycle-flags.js.map +1 -0
  181. package/dist/shared/memory-types.d.ts +55 -0
  182. package/dist/shared/memory-types.js +75 -0
  183. package/dist/shared/memory-types.js.map +1 -0
  184. package/dist/shared/service-status.d.ts +82 -0
  185. package/dist/shared/service-status.js +97 -0
  186. package/dist/shared/service-status.js.map +1 -0
  187. package/dist/telemetry/emit.d.ts +197 -0
  188. package/dist/telemetry/emit.js +196 -0
  189. package/dist/telemetry/emit.js.map +1 -0
  190. package/package.json +50 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The PROJECTS page — PRD-059c (the steady-state project manager) + PRD-059d (cross-device import).
3
+ *
4
+ * Mounts on the `#/projects` route inside the PRD-037 shell. It receives {@link PageProps} (the SHARED
5
+ * `wire` — never `createWireClient` — plus `daemonUp`) and renders inside `<PageFrame eyebrow="projects">`.
6
+ * It is the management home for everything Honeycomb is actively sourcing in the current workspace:
7
+ *
8
+ * 059c — LIST + STATE. Every project the workspace is sourcing (from `GET /api/diagnostics/scope/projects`,
9
+ * split on `boundLocally`: the ACTIVE list is the locally-bound projects). The reserved
10
+ * `__unsorted__` inbox is shown DISTINCTLY with its size (c-AC-2). Each project carries its
11
+ * name + the per-project state the daemon serves: `scope/projects` aggregates bound path(s),
12
+ * git remote, last-capture, and memory/session counts (paths/remote from the local store +
13
+ * registry, counts best-effort fail-soft to 0 on a backend flap). A field served empty/absent
14
+ * (no path, no remote) renders as an honest "—"; a missing last-capture renders as "never".
15
+ * 059c — ADD (top-right "+"). A menu with two options (parent lean: one "+" with two options) — "New
16
+ * folder" runs the 059b daemon-served folder-pick → bind flow ({@link FolderPicker}); "Import
17
+ * existing" opens the 059d import modal. On a successful add the list re-hydrates (c-AC-3).
18
+ * 059c — per-project UNBIND (`POST projects/unbind`) removes the LOCAL binding only — capture stops
19
+ * for that folder, the registry project + its existing data are UNTOUCHED (c-AC-4); and OPEN
20
+ * re-scopes the other surfaces to that project via the scope context (c-AC-5 / 049e view scope).
21
+ * 059d — IMPORT MODAL lists the workspace's registry projects with NO local binding on this device
22
+ * (`?unbound=1`), and binds a chosen folder to the selected existing `project_id` via
23
+ * `POST projects/bind-existing` (d-AC-1 UI / d-AC-2 path).
24
+ *
25
+ * Security (inherited from the shell): LOCAL-MODE-ONLY; every name/path renders as ESCAPED TEXT (React
26
+ * default); NO token/secret rides any list/bind/unbind body. Every visual value is an existing DS token.
27
+ */
28
+ import React from "react";
29
+ import { type PageProps } from "../page-frame.js";
30
+ /** The reserved inbox project id (mirrored from the resolver; a literal so this stays a thin-client view). */
31
+ export declare const UNSORTED_PROJECT_ID: "__unsorted__";
32
+ /**
33
+ * The Projects page (059c/059d). Hydrates the workspace's projects on mount (split into active vs the
34
+ * `__unsorted__` inbox), runs the "+ Add" flows (new-folder pick→bind and import-existing), per-project
35
+ * Unbind (confirm-gated, registry-safe) and Open (re-scope via the scope context). After any add/unbind
36
+ * the list re-hydrates so the rendered state is the daemon's persisted truth, never optimistic.
37
+ */
38
+ export declare function ProjectsPage({ wire }: PageProps): React.JSX.Element;
@@ -0,0 +1,325 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The PROJECTS page — PRD-059c (the steady-state project manager) + PRD-059d (cross-device import).
4
+ *
5
+ * Mounts on the `#/projects` route inside the PRD-037 shell. It receives {@link PageProps} (the SHARED
6
+ * `wire` — never `createWireClient` — plus `daemonUp`) and renders inside `<PageFrame eyebrow="projects">`.
7
+ * It is the management home for everything Honeycomb is actively sourcing in the current workspace:
8
+ *
9
+ * 059c — LIST + STATE. Every project the workspace is sourcing (from `GET /api/diagnostics/scope/projects`,
10
+ * split on `boundLocally`: the ACTIVE list is the locally-bound projects). The reserved
11
+ * `__unsorted__` inbox is shown DISTINCTLY with its size (c-AC-2). Each project carries its
12
+ * name + the per-project state the daemon serves: `scope/projects` aggregates bound path(s),
13
+ * git remote, last-capture, and memory/session counts (paths/remote from the local store +
14
+ * registry, counts best-effort fail-soft to 0 on a backend flap). A field served empty/absent
15
+ * (no path, no remote) renders as an honest "—"; a missing last-capture renders as "never".
16
+ * 059c — ADD (top-right "+"). A menu with two options (parent lean: one "+" with two options) — "New
17
+ * folder" runs the 059b daemon-served folder-pick → bind flow ({@link FolderPicker}); "Import
18
+ * existing" opens the 059d import modal. On a successful add the list re-hydrates (c-AC-3).
19
+ * 059c — per-project UNBIND (`POST projects/unbind`) removes the LOCAL binding only — capture stops
20
+ * for that folder, the registry project + its existing data are UNTOUCHED (c-AC-4); and OPEN
21
+ * re-scopes the other surfaces to that project via the scope context (c-AC-5 / 049e view scope).
22
+ * 059d — IMPORT MODAL lists the workspace's registry projects with NO local binding on this device
23
+ * (`?unbound=1`), and binds a chosen folder to the selected existing `project_id` via
24
+ * `POST projects/bind-existing` (d-AC-1 UI / d-AC-2 path).
25
+ *
26
+ * Security (inherited from the shell): LOCAL-MODE-ONLY; every name/path renders as ESCAPED TEXT (React
27
+ * default); NO token/secret rides any list/bind/unbind body. Every visual value is an existing DS token.
28
+ */
29
+ import React from "react";
30
+ import { Badge, Button } from "../primitives.js";
31
+ import { PageFrame } from "../page-frame.js";
32
+ import { useScope } from "../scope-context.js";
33
+ import { FolderPicker } from "../folder-picker.js";
34
+ /** The reserved inbox project id (mirrored from the resolver; a literal so this stays a thin-client view). */
35
+ export const UNSORTED_PROJECT_ID = "__unsorted__";
36
+ /** The shared surface card style (matches the kit's panel/elevated rhythm — mirrors memories.tsx). */
37
+ const SURFACE = {
38
+ padding: 16,
39
+ background: "var(--bg-surface)",
40
+ border: "1px solid var(--border-default)",
41
+ borderRadius: "var(--radius-lg)",
42
+ };
43
+ /** A muted em-dash for a per-project field the daemon serves empty/absent (no path, no remote, no captures). */
44
+ function Dash() {
45
+ return _jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-tertiary)" }, children: "\u2014" });
46
+ }
47
+ /**
48
+ * Humanize the per-project `lastCapture` ISO timestamp (PRD-059c c-AC-1). `null` ⇒ the project has never
49
+ * captured (or the aggregate flapped) → "never"; a malformed value degrades to the raw em-dash rather than
50
+ * a thrown `Invalid Date`. A valid ISO renders as the locale date so the operator sees real recency.
51
+ */
52
+ function humanizeCapture(iso) {
53
+ if (iso === null || iso === "")
54
+ return _jsx("span", { style: { color: "var(--text-tertiary)" }, children: "never" });
55
+ const t = Date.parse(iso);
56
+ if (Number.isNaN(t))
57
+ return _jsx(Dash, {});
58
+ return new Date(t).toLocaleString();
59
+ }
60
+ /**
61
+ * One ACTIVE project row (059c). Shows the project name + the per-project state the daemon serves:
62
+ * bound path(s), git remote, last capture, and memory/session counts (from `scope/projects`). A field
63
+ * served empty/absent renders as an honest "—"; a missing last-capture renders as "never". Per-row OPEN
64
+ * (re-scope) + UNBIND actions.
65
+ *
66
+ * UNBIND is binding-driven (the daemon's `projects/unbind` keys on the absolute folder PATH, not the
67
+ * project id — and the registry read does not serve the path). So Unbind opens the folder picker to
68
+ * select the folder to release, then POSTs the unbind. This keeps the action genuinely live (c-AC-4 —
69
+ * a real binding is removed) without inventing a path the daemon never gave us.
70
+ */
71
+ function ProjectRow({ project, wire, onOpen, onUnbound }) {
72
+ const [unbinding, setUnbinding] = React.useState(false);
73
+ const [path, setPath] = React.useState(null);
74
+ const [busy, setBusy] = React.useState(false);
75
+ const [note, setNote] = React.useState("");
76
+ const inFlightRef = React.useRef(false);
77
+ const confirmUnbind = async () => {
78
+ if (path === null || inFlightRef.current)
79
+ return;
80
+ inFlightRef.current = true;
81
+ setBusy(true);
82
+ setNote("");
83
+ const ack = await wire.unbindProject({ path });
84
+ setBusy(false);
85
+ inFlightRef.current = false;
86
+ if (ack.unbound) {
87
+ setUnbinding(false);
88
+ setPath(null);
89
+ onUnbound();
90
+ return;
91
+ }
92
+ setNote("No binding matched that folder — capture is unchanged.");
93
+ };
94
+ return (_jsxs("div", { "data-testid": "project-row", "data-project-id": project.projectId, style: { ...SURFACE, display: "flex", flexDirection: "column", gap: 10 }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10 }, children: [_jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 14, fontWeight: 600, color: "var(--honey)" }, children: project.name !== "" ? project.name : project.projectId }), _jsx(Badge, { tone: "verified", mono: true, dot: true, children: "sourcing" }), _jsx("span", { style: { flex: 1 } }), _jsx(Button, { variant: "secondary", size: "sm", onClick: () => onOpen(project.projectId), "data-testid": "project-open", children: "Open" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setUnbinding((u) => !u), "data-testid": "project-unbind", children: unbinding ? "Cancel" : "Unbind" })] }), _jsxs("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 8, paddingTop: 4, borderTop: "1px solid var(--border-subtle)" }, children: [_jsx(MetaCell, { label: "bound path", value: _jsx(BoundPaths, { paths: project.boundPaths }) }), _jsx(MetaCell, { label: "git remote", value: project.remote !== "" ? project.remote : _jsx(Dash, {}) }), _jsx(MetaCell, { label: "last capture", value: humanizeCapture(project.lastCapture) }), _jsx(MetaCell, { label: "memories / sessions", value: `${project.memoryCount} / ${project.sessionCount}` })] }), unbinding && (_jsxs("div", { "data-testid": "project-unbind-panel", style: { display: "flex", flexDirection: "column", gap: 10, paddingTop: 4, borderTop: "1px solid var(--border-subtle)" }, children: [_jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-tertiary)" }, children: "Pick the folder to release. Unbind removes this device\u2019s binding (capture stops there); the project and its memories are kept." }), _jsx(FolderSelect, { wire: wire, onSelect: setPath, selected: path }), _jsxs("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: [_jsx(Button, { variant: "danger", size: "sm", onClick: () => void confirmUnbind(), disabled: busy || path === null, "data-testid": "project-unbind-confirm", children: busy ? "…" : "Unbind this folder" }), note !== "" && (_jsx("span", { "data-testid": "project-unbind-note", style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--severity-warning)" }, children: note }))] })] }))] }));
95
+ }
96
+ /**
97
+ * The "bound path" cell value (PRD-059c c-AC-1): this device's bound folder path(s) for the project. Empty
98
+ * (an importable/registry-only project, or none recorded on this device) ⇒ the honest em-dash. Each path is
99
+ * ESCAPED text (React default) and ellipsis-clipped so a long absolute path never blows out the cell.
100
+ */
101
+ function BoundPaths({ paths }) {
102
+ if (paths.length === 0)
103
+ return _jsx(Dash, {});
104
+ return (_jsx("span", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: paths.map((p) => (_jsx("span", { title: p, style: { whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", minWidth: 0 }, children: p }, p))) }));
105
+ }
106
+ /** A labeled metadata cell (mono caption + value). */
107
+ function MetaCell({ label, value }) {
108
+ return (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [_jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--text-tertiary)", textTransform: "uppercase", letterSpacing: "0.06em" }, children: label }), _jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-secondary)" }, children: value })] }));
109
+ }
110
+ // ─────────────────────────────────────────────────────────────────────────────
111
+ // The __unsorted__ inbox row (059c c-AC-2)
112
+ // ─────────────────────────────────────────────────────────────────────────────
113
+ /**
114
+ * The reserved inbox shown DISTINCTLY with its size (PRD-059c c-AC-2). The size is the daemon's aggregated
115
+ * `sessionCount` for the `__unsorted__` row (the `''`/`__unsorted__` capture buckets summed) — the count of
116
+ * unsorted captured turns waiting to be assigned to a real project. Best-effort: `0` when empty or on a
117
+ * backend flap, never a fabricated value.
118
+ */
119
+ function InboxRow({ inbox }) {
120
+ return (_jsxs("div", { "data-testid": "inbox-row", style: { ...SURFACE, display: "flex", alignItems: "center", gap: 10, borderStyle: "dashed" }, children: [_jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 14, fontWeight: 600, color: "var(--text-secondary)" }, children: "(unsorted inbox)" }), _jsx(Badge, { tone: "neutral", mono: true, children: UNSORTED_PROJECT_ID }), _jsx("span", { style: { flex: 1 } }), _jsxs("span", { "data-testid": "inbox-size", style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-tertiary)" }, children: ["size ", inbox.sessionCount] })] }));
121
+ }
122
+ /** The top-right "+ Add" menu: New folder (059b pick→bind) or Import existing (059d modal). */
123
+ function AddMenu({ onNew, onImport }) {
124
+ const [open, setOpen] = React.useState(false);
125
+ return (_jsxs("div", { style: { position: "relative" }, children: [_jsx(Button, { variant: "primary", size: "sm", onClick: () => setOpen((o) => !o), "data-testid": "add-menu-toggle", children: "+ Add" }), open && (_jsxs("div", { "data-testid": "add-menu", style: {
126
+ position: "absolute",
127
+ right: 0,
128
+ top: 38,
129
+ zIndex: 5,
130
+ display: "flex",
131
+ flexDirection: "column",
132
+ minWidth: 180,
133
+ background: "var(--bg-elevated)",
134
+ border: "1px solid var(--border-default)",
135
+ borderRadius: "var(--radius-md)",
136
+ boxShadow: "var(--shadow-md, 0 6px 24px rgba(0,0,0,0.18))",
137
+ overflow: "hidden",
138
+ }, children: [_jsx(MenuItem, { label: "New folder", hint: "pick a folder to bind", onClick: () => { setOpen(false); onNew(); }, testId: "add-new" }), _jsx(MenuItem, { label: "Import existing", hint: "from the cloud registry", onClick: () => { setOpen(false); onImport(); }, testId: "add-import" })] }))] }));
139
+ }
140
+ /** One row in the Add menu. */
141
+ function MenuItem({ label, hint, onClick, testId }) {
142
+ return (_jsxs("button", { type: "button", "data-testid": testId, onClick: onClick, style: {
143
+ display: "flex",
144
+ flexDirection: "column",
145
+ gap: 2,
146
+ width: "100%",
147
+ textAlign: "left",
148
+ padding: "9px 12px",
149
+ background: "transparent",
150
+ border: "none",
151
+ borderBottom: "1px solid var(--border-subtle)",
152
+ cursor: "pointer",
153
+ }, children: [_jsx("span", { style: { fontSize: 13, fontWeight: 600, color: "var(--text-primary)" }, children: label }), _jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--text-tertiary)" }, children: hint })] }));
154
+ }
155
+ /**
156
+ * The cross-device IMPORT modal (PRD-059d). Lists the workspace's registry projects with NO local
157
+ * binding on this device (`scopeProjects({ unbound: true })` → d-AC-1 UI), lets the user select one,
158
+ * then pick a local folder ({@link FolderPicker} in select-only mode is not reused — instead the user
159
+ * picks the folder here and we bind-to-existing). Selecting a project + a folder POSTs
160
+ * `projects/bind-existing` (d-AC-2 path). Privilege-scoped: the list is exactly what the token can see.
161
+ */
162
+ function ImportModal({ wire, onClose, onImported }) {
163
+ const [importable, setImportable] = React.useState([]);
164
+ const [hydrated, setHydrated] = React.useState(false);
165
+ const [selectedProject, setSelectedProject] = React.useState(null);
166
+ const [browsePath, setBrowsePath] = React.useState(null);
167
+ const [busy, setBusy] = React.useState(false);
168
+ const [error, setError] = React.useState("");
169
+ const inFlightRef = React.useRef(false);
170
+ // Hydrate the importable (registry-only) projects on mount (d-AC-1).
171
+ React.useEffect(() => {
172
+ let alive = true;
173
+ void (async () => {
174
+ const rows = await wire.scopeProjects({ unbound: true });
175
+ if (!alive)
176
+ return;
177
+ setImportable(rows.filter((p) => p.projectId !== UNSORTED_PROJECT_ID));
178
+ setHydrated(true);
179
+ })();
180
+ return () => {
181
+ alive = false;
182
+ };
183
+ }, [wire]);
184
+ // Bind the chosen folder to the selected EXISTING project (d-AC-2). The FolderPicker drives the
185
+ // browse, but its bind is the NEW-project path — so here we intercept its selection: we render the
186
+ // picker and, on its onBound (which would create a new project), we instead want bind-existing. To
187
+ // keep one picker, we capture the chosen path via a select-folder step below.
188
+ const confirmImport = React.useCallback(async () => {
189
+ if (selectedProject === null || browsePath === null || inFlightRef.current)
190
+ return;
191
+ inFlightRef.current = true;
192
+ setBusy(true);
193
+ setError("");
194
+ const ack = await wire.bindExistingProject({ path: browsePath, projectId: selectedProject });
195
+ setBusy(false);
196
+ inFlightRef.current = false;
197
+ if (ack.bound) {
198
+ onImported(ack);
199
+ return;
200
+ }
201
+ setError(ack.error !== undefined && ack.error !== "" ? ack.error : "Could not import that project.");
202
+ }, [selectedProject, browsePath, wire, onImported]);
203
+ return (_jsx(ModalShell, { title: "Import project from cloud", onClose: onClose, testId: "import-modal", children: _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 14 }, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-tertiary)", textTransform: "uppercase", letterSpacing: "0.06em" }, children: "projects in this workspace not yet on this device" }), !hydrated ? (_jsx("span", { style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-tertiary)" }, children: "loading\u2026" })) : importable.length === 0 ? (_jsx("span", { "data-testid": "import-empty", style: { fontSize: 13, color: "var(--text-tertiary)" }, children: "Every registry project is already bound on this device. Nothing to import." })) : (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6, maxHeight: 200, overflowY: "auto" }, children: importable.map((p) => (_jsx("button", { type: "button", "data-testid": "import-project", "data-project-id": p.projectId, onClick: () => setSelectedProject(p.projectId), "aria-pressed": selectedProject === p.projectId, style: {
204
+ display: "flex",
205
+ alignItems: "center",
206
+ gap: 10,
207
+ width: "100%",
208
+ textAlign: "left",
209
+ padding: "9px 12px",
210
+ background: selectedProject === p.projectId ? "var(--honey-subtle)" : "var(--bg-elevated)",
211
+ border: `1px solid ${selectedProject === p.projectId ? "var(--honey-border)" : "var(--border-default)"}`,
212
+ borderRadius: "var(--radius-md)",
213
+ cursor: "pointer",
214
+ color: selectedProject === p.projectId ? "var(--honey)" : "var(--text-primary)",
215
+ fontFamily: "var(--font-mono)",
216
+ fontSize: 13,
217
+ fontWeight: 600,
218
+ }, children: p.name !== "" ? p.name : p.projectId }, p.projectId))) }))] }), selectedProject !== null && (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsxs("span", { style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-tertiary)", textTransform: "uppercase", letterSpacing: "0.06em" }, children: ["pick this device\u2019s folder for ", _jsx("span", { style: { color: "var(--honey)" }, children: selectedProject })] }), _jsx(FolderSelect, { wire: wire, onSelect: setBrowsePath, selected: browsePath })] })), _jsxs("div", { style: { display: "flex", gap: 10, alignItems: "center" }, children: [_jsx(Button, { variant: "primary", onClick: () => void confirmImport(), disabled: busy || selectedProject === null || browsePath === null, "data-testid": "import-confirm", children: busy ? "Importing…" : "Import project" }), _jsx(Button, { variant: "ghost", onClick: onClose, disabled: busy, children: "Cancel" }), error !== "" && (_jsx("span", { "data-testid": "import-error", style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--severity-critical)" }, children: error }))] })] }) }));
219
+ }
220
+ /**
221
+ * A folder SELECT-ONLY browser (059d step 2). Reuses the same daemon browse tree as the {@link FolderPicker}
222
+ * but its job is only to emit the chosen ABSOLUTE path (the bind is bind-to-existing, owned by the modal).
223
+ * Kept thin (browse + "use this folder") to avoid duplicating the full picker's bind UI.
224
+ */
225
+ function FolderSelect({ wire, onSelect, selected }) {
226
+ const [path, setPath] = React.useState("");
227
+ const [parent, setParent] = React.useState(null);
228
+ const [children, setChildren] = React.useState([]);
229
+ const [hydrated, setHydrated] = React.useState(false);
230
+ const goTo = React.useCallback(async (to) => {
231
+ const body = await wire.fsBrowse(to);
232
+ setPath(body.path);
233
+ setParent(body.parent);
234
+ setChildren(body.children);
235
+ setHydrated(true);
236
+ }, [wire]);
237
+ React.useEffect(() => {
238
+ void goTo(undefined);
239
+ }, [goTo]);
240
+ if (hydrated && path === "" && children.length === 0) {
241
+ return (_jsxs("span", { "data-testid": "folderselect-unavailable", style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-tertiary)" }, children: ["The folder browser isn\u2019t available \u2014 bind from your terminal: ", _jsx("code", { style: { color: "var(--honey)" }, children: "honeycomb project bind" })] }));
242
+ }
243
+ return (_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [_jsx("span", { style: { flex: 1, minWidth: 0, fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-secondary)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: path !== "" ? path : "loading…" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void goTo(parent ?? undefined), disabled: parent === null, children: "\u2191 up" }), _jsx(Button, { variant: "secondary", size: "sm", onClick: () => onSelect(path), disabled: path === "", "data-testid": "folderselect-use", children: "Use this folder" })] }), _jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6, maxHeight: 180, overflowY: "auto" }, children: children.map((c) => (_jsxs("button", { type: "button", "data-testid": "folderselect-row", onClick: () => void goTo(c.path), style: {
244
+ display: "flex",
245
+ alignItems: "center",
246
+ gap: 8,
247
+ width: "100%",
248
+ textAlign: "left",
249
+ padding: "8px 12px",
250
+ background: "var(--bg-elevated)",
251
+ border: "1px solid var(--border-default)",
252
+ borderRadius: "var(--radius-md)",
253
+ cursor: "pointer",
254
+ color: "var(--text-primary)",
255
+ fontFamily: "var(--font-mono)",
256
+ fontSize: 13,
257
+ }, children: [_jsx("span", { style: { flex: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", minWidth: 0 }, children: c.name }), c.isGitRepo && _jsx(Badge, { tone: "honey", mono: true, children: "git" })] }, c.path))) }), selected !== null && (_jsxs("span", { style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--verified)" }, children: ["selected: ", selected] }))] }));
258
+ }
259
+ // ─────────────────────────────────────────────────────────────────────────────
260
+ // A minimal centered modal shell (no shared primitive exists; built from DS tokens)
261
+ // ─────────────────────────────────────────────────────────────────────────────
262
+ /** A centered overlay modal shell (059c/059d). Click the backdrop or Close to dismiss. Escaped content. */
263
+ function ModalShell({ title, onClose, testId, children }) {
264
+ return (_jsx("div", { "data-testid": testId, role: "dialog", "aria-modal": "true", "aria-label": title, onClick: onClose, style: {
265
+ position: "fixed",
266
+ inset: 0,
267
+ zIndex: 50,
268
+ display: "flex",
269
+ alignItems: "flex-start",
270
+ justifyContent: "center",
271
+ padding: "64px 16px",
272
+ background: "rgba(0,0,0,0.45)",
273
+ }, children: _jsxs("div", { onClick: (e) => e.stopPropagation(), style: {
274
+ width: "100%",
275
+ maxWidth: 560,
276
+ maxHeight: "80vh",
277
+ overflowY: "auto",
278
+ padding: 20,
279
+ background: "var(--bg-surface)",
280
+ border: "1px solid var(--border-default)",
281
+ borderRadius: "var(--radius-lg)",
282
+ }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }, children: [_jsx("h2", { style: { fontSize: "var(--text-base)", fontWeight: 700, color: "var(--text-primary)", margin: 0 }, children: title }), _jsx("span", { style: { flex: 1 } }), _jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, "aria-label": "close", children: "\u2715" })] }), children] }) }));
283
+ }
284
+ // ─────────────────────────────────────────────────────────────────────────────
285
+ // The page
286
+ // ─────────────────────────────────────────────────────────────────────────────
287
+ /**
288
+ * The Projects page (059c/059d). Hydrates the workspace's projects on mount (split into active vs the
289
+ * `__unsorted__` inbox), runs the "+ Add" flows (new-folder pick→bind and import-existing), per-project
290
+ * Unbind (confirm-gated, registry-safe) and Open (re-scope via the scope context). After any add/unbind
291
+ * the list re-hydrates so the rendered state is the daemon's persisted truth, never optimistic.
292
+ */
293
+ export function ProjectsPage({ wire }) {
294
+ const { scope, setScope } = useScope();
295
+ const [projects, setProjects] = React.useState([]);
296
+ const [hydrated, setHydrated] = React.useState(false);
297
+ const [flow, setFlow] = React.useState("none");
298
+ /** Re-list the workspace's projects (the persisted truth). */
299
+ const reList = React.useCallback(async () => {
300
+ const rows = await wire.scopeProjects();
301
+ setProjects(rows);
302
+ setHydrated(true);
303
+ }, [wire]);
304
+ React.useEffect(() => {
305
+ void reList();
306
+ }, [reList]);
307
+ // ACTIVE = locally-bound, non-inbox projects (c-AC-1). The inbox is shown distinctly (c-AC-2) with its
308
+ // daemon-aggregated size (its `sessionCount`) — resolved here so the row can render the real count.
309
+ const active = projects.filter((p) => p.boundLocally && p.projectId !== UNSORTED_PROJECT_ID);
310
+ const inbox = projects.find((p) => p.projectId === UNSORTED_PROJECT_ID);
311
+ // c-AC-5: open a project → re-scope the other surfaces (memories/graph/sync) to it (049e view scope).
312
+ const onOpen = React.useCallback((projectId) => {
313
+ setScope({ org: scope.org, workspace: scope.workspace, project: projectId });
314
+ }, [setScope, scope.org, scope.workspace]);
315
+ const onBoundNew = React.useCallback((_ack) => {
316
+ setFlow("none");
317
+ void reList();
318
+ }, [reList]);
319
+ const onImported = React.useCallback((_ack) => {
320
+ setFlow("none");
321
+ void reList();
322
+ }, [reList]);
323
+ return (_jsxs(PageFrame, { eyebrow: "projects", title: "Projects", right: _jsx(AddMenu, { onNew: () => setFlow("new"), onImport: () => setFlow("import") }), children: [flow === "new" && (_jsxs("div", { style: { ...SURFACE, marginBottom: 16 }, children: [_jsx("div", { style: { fontSize: 14, fontWeight: 600, color: "var(--text-primary)", marginBottom: 10 }, children: "Add a project" }), _jsx(FolderPicker, { wire: wire, onBound: onBoundNew, onCancel: () => setFlow("none") })] })), flow === "import" && _jsx(ImportModal, { wire: wire, onClose: () => setFlow("none"), onImported: onImported }), _jsxs("div", { "data-testid": "projects-list", style: { display: "flex", flexDirection: "column", gap: 10 }, children: [!hydrated ? (_jsx("div", { style: { fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-tertiary)" }, children: "loading\u2026" })) : active.length === 0 ? (_jsxs("div", { "data-testid": "projects-empty", style: { padding: "10px 4px", fontSize: 13, color: "var(--text-tertiary)" }, children: ["No active projects on this device yet. Use ", _jsx("strong", { children: "+ Add" }), " to bind a folder or import an existing project."] })) : (active.map((p) => _jsx(ProjectRow, { project: p, wire: wire, onOpen: onOpen, onUnbound: () => void reList() }, p.projectId))), inbox !== undefined && _jsx(InboxRow, { inbox: inbox })] })] }));
324
+ }
325
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../../../../src/dashboard/web/pages/projects.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The ROI TREND CHART — PRD-060e (e-AC-10), an INLINE-SVG line chart in the GraphCanvas idiom.
3
+ *
4
+ * No charting dependency: this hand-draws one `<polyline>` per {@link RoiTrendSeries} over a bounded
5
+ * SVG `viewBox`, exactly like `panels.tsx` `GraphCanvas` hand-draws nodes/edges. The measured-vs-modeled
6
+ * language is honored on the stroke (the FOURTH of the four signals, folded in from the page): a
7
+ * `modeled` series draws a DASHED stroke, a measured series a SOLID one. Money is INTEGER cents at every
8
+ * point ({@link RoiTrendPoint.cents}); this component only divides by the series max to map cents → the
9
+ * pixel y-axis, never formatting dollars (that is the page's render edge).
10
+ *
11
+ * Motion (e-AC-10): a one-shot draw-in transition reuses ONLY the existing `--dur-base` token and is
12
+ * SUPPRESSED under `prefers-reduced-motion` — there is no count-up odometer and no infinite animation.
13
+ * Every visual value is an existing `var(--…)` DS token; no new token, no new dependency.
14
+ *
15
+ * The chart is a PURE function of the {@link RoiTrendView} the page hands it (the page fetched it via
16
+ * `wire.roiTrend`); it does no fetching itself.
17
+ */
18
+ import React from "react";
19
+ import type { RoiTrendSeries, RoiTrendView } from "../../contracts.js";
20
+ /**
21
+ * The cents range [min, max] across every point of every series, ALWAYS including 0 so the zero axis is
22
+ * on-scale (pure, exported for tests). Finding (chart-negative): net trend CAN be negative (PRD), so the
23
+ * scale must span both negative and positive cents -- the prior max-only/clamp-to-0 flattened loss
24
+ * periods onto the baseline. `min <= 0 <= max` is guaranteed (0 is folded in), and `max > min` is
25
+ * guaranteed (a degenerate all-zero series widens to [0, 1]) so we never divide by 0.
26
+ */
27
+ export declare function seriesCentsRange(series: readonly RoiTrendSeries[]): {
28
+ min: number;
29
+ max: number;
30
+ };
31
+ /** The viewBox y-coordinate of the ZERO line for a given range (where the baseline is drawn). */
32
+ export declare function zeroBaselineY(range: {
33
+ min: number;
34
+ max: number;
35
+ }): number;
36
+ /** Map one series' points to an SVG `points` string over the bounded box (pure, exported for tests). */
37
+ export declare function seriesPolylinePoints(series: RoiTrendSeries, range: {
38
+ min: number;
39
+ max: number;
40
+ }): string;
41
+ /** Props for {@link RoiTrendChart}. */
42
+ export interface RoiTrendChartProps {
43
+ /** The trend view-model the page fetched via `wire.roiTrend` (the chart is a pure function of it). */
44
+ readonly trend: RoiTrendView;
45
+ }
46
+ /**
47
+ * The inline-SVG trend chart (e-AC-10). Renders one polyline per series — DASHED for a modeled series,
48
+ * SOLID for a measured one — plus a small legend. An absent/empty trend renders an honest empty state
49
+ * (NOT a fabricated flat line). The draw-in transition reuses `--dur-base` and is suppressed under
50
+ * `prefers-reduced-motion`.
51
+ */
52
+ export declare function RoiTrendChart({ trend }: RoiTrendChartProps): React.JSX.Element;
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The ROI TREND CHART — PRD-060e (e-AC-10), an INLINE-SVG line chart in the GraphCanvas idiom.
4
+ *
5
+ * No charting dependency: this hand-draws one `<polyline>` per {@link RoiTrendSeries} over a bounded
6
+ * SVG `viewBox`, exactly like `panels.tsx` `GraphCanvas` hand-draws nodes/edges. The measured-vs-modeled
7
+ * language is honored on the stroke (the FOURTH of the four signals, folded in from the page): a
8
+ * `modeled` series draws a DASHED stroke, a measured series a SOLID one. Money is INTEGER cents at every
9
+ * point ({@link RoiTrendPoint.cents}); this component only divides by the series max to map cents → the
10
+ * pixel y-axis, never formatting dollars (that is the page's render edge).
11
+ *
12
+ * Motion (e-AC-10): a one-shot draw-in transition reuses ONLY the existing `--dur-base` token and is
13
+ * SUPPRESSED under `prefers-reduced-motion` — there is no count-up odometer and no infinite animation.
14
+ * Every visual value is an existing `var(--…)` DS token; no new token, no new dependency.
15
+ *
16
+ * The chart is a PURE function of the {@link RoiTrendView} the page hands it (the page fetched it via
17
+ * `wire.roiTrend`); it does no fetching itself.
18
+ */
19
+ import React from "react";
20
+ /** The chart canvas extent — the line points map into this box; the SVG scales to its container. */
21
+ const CHART_VIEW = { width: 640, height: 200 };
22
+ /** Inner padding (px in viewBox units) so strokes + the baseline never clip the edge. */
23
+ const PAD = { top: 12, right: 12, bottom: 18, left: 12 };
24
+ /** The per-series stroke color (existing DS tokens only). Measured savings is the defensible green. */
25
+ function seriesColor(label, modeled) {
26
+ if (label.includes("net"))
27
+ return "var(--honey)";
28
+ if (label.includes("infra") || label.includes("cost"))
29
+ return "var(--severity-info)";
30
+ // Savings: measured is the proven green; the modeled estimate rides the amber warning tone (e-AC-3).
31
+ return modeled ? "var(--severity-warning)" : "var(--verified)";
32
+ }
33
+ /**
34
+ * The cents range [min, max] across every point of every series, ALWAYS including 0 so the zero axis is
35
+ * on-scale (pure, exported for tests). Finding (chart-negative): net trend CAN be negative (PRD), so the
36
+ * scale must span both negative and positive cents -- the prior max-only/clamp-to-0 flattened loss
37
+ * periods onto the baseline. `min <= 0 <= max` is guaranteed (0 is folded in), and `max > min` is
38
+ * guaranteed (a degenerate all-zero series widens to [0, 1]) so we never divide by 0.
39
+ */
40
+ export function seriesCentsRange(series) {
41
+ let min = 0;
42
+ let max = 0;
43
+ for (const s of series) {
44
+ for (const p of s.points) {
45
+ if (p.cents < min)
46
+ min = p.cents;
47
+ if (p.cents > max)
48
+ max = p.cents;
49
+ }
50
+ }
51
+ // Degenerate (all points exactly 0, or no points): widen so max > min and the divide is safe.
52
+ if (max === min)
53
+ max = min + 1;
54
+ return { min, max };
55
+ }
56
+ /** The viewBox y-coordinate of the ZERO line for a given range (where the baseline is drawn). */
57
+ export function zeroBaselineY(range) {
58
+ const innerH = CHART_VIEW.height - PAD.top - PAD.bottom;
59
+ const frac = (0 - range.min) / (range.max - range.min); // fraction of the range that 0 sits at
60
+ return PAD.top + innerH - frac * innerH;
61
+ }
62
+ /** Map one series' points to an SVG `points` string over the bounded box (pure, exported for tests). */
63
+ export function seriesPolylinePoints(series, range) {
64
+ const pts = series.points;
65
+ if (pts.length === 0)
66
+ return "";
67
+ const innerW = CHART_VIEW.width - PAD.left - PAD.right;
68
+ const innerH = CHART_VIEW.height - PAD.top - PAD.bottom;
69
+ // A single point sits at the left edge; >=2 points spread evenly across the inner width.
70
+ const stepX = pts.length > 1 ? innerW / (pts.length - 1) : 0;
71
+ const span = range.max - range.min;
72
+ return pts
73
+ .map((p, i) => {
74
+ const x = PAD.left + i * stepX;
75
+ // cents -> y: map against the FULL [min, max] range (which includes 0). Higher cents sit
76
+ // HIGHER (smaller y); a NEGATIVE cents value sits BELOW the zero line, never clamped to it.
77
+ const frac = Math.max(0, Math.min(1, (p.cents - range.min) / span));
78
+ const y = PAD.top + innerH - frac * innerH;
79
+ return `${x.toFixed(1)},${y.toFixed(1)}`;
80
+ })
81
+ .join(" ");
82
+ }
83
+ /** Read the OS reduced-motion preference (fail-soft: treats an unavailable matchMedia as "no preference"). */
84
+ function prefersReducedMotion() {
85
+ try {
86
+ return typeof window !== "undefined" && typeof window.matchMedia === "function"
87
+ ? window.matchMedia("(prefers-reduced-motion: reduce)").matches
88
+ : false;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ /**
95
+ * The inline-SVG trend chart (e-AC-10). Renders one polyline per series — DASHED for a modeled series,
96
+ * SOLID for a measured one — plus a small legend. An absent/empty trend renders an honest empty state
97
+ * (NOT a fabricated flat line). The draw-in transition reuses `--dur-base` and is suppressed under
98
+ * `prefers-reduced-motion`.
99
+ */
100
+ export function RoiTrendChart({ trend }) {
101
+ const reduceMotion = React.useMemo(() => prefersReducedMotion(), []);
102
+ // The one-shot draw-in: start hidden, flip visible after mount so the stroke transitions in once.
103
+ const [drawn, setDrawn] = React.useState(reduceMotion);
104
+ React.useEffect(() => {
105
+ if (reduceMotion)
106
+ return;
107
+ const id = requestAnimationFrame(() => setDrawn(true));
108
+ return () => cancelAnimationFrame(id);
109
+ }, [reduceMotion]);
110
+ const hasData = trend.series.some((s) => s.points.length > 0);
111
+ if (trend.status === "absent" || !hasData) {
112
+ return (_jsx("div", { "data-testid": "roi-trend-empty", style: { padding: "28px 8px", textAlign: "center", fontSize: 13, color: "var(--text-tertiary)" }, children: trend.startedAt !== "" ? `Savings tracked from ${trend.startedAt}.` : "No trend history yet." }));
113
+ }
114
+ const range = seriesCentsRange(trend.series);
115
+ // Finding (chart-negative): the baseline is the ZERO crossing within the [min,max] range, not a
116
+ // fixed bottom edge -- so a loss period draws BELOW it and a gain ABOVE it.
117
+ const baselineY = zeroBaselineY(range);
118
+ return (_jsxs("div", { "data-testid": "roi-trend-chart", children: [_jsxs("svg", { viewBox: `0 0 ${CHART_VIEW.width} ${CHART_VIEW.height}`, style: { width: "100%", height: 200, display: "block" }, role: "img", "aria-label": "ROI trend over time", children: [_jsx("line", { x1: PAD.left, y1: baselineY, x2: CHART_VIEW.width - PAD.right, y2: baselineY, stroke: "var(--border-subtle)", strokeWidth: "1" }), trend.series.map((s) => {
119
+ const points = seriesPolylinePoints(s, range);
120
+ if (points === "")
121
+ return null;
122
+ const color = seriesColor(s.label, s.modeled);
123
+ return (_jsx("polyline", { "data-testid": `roi-trend-series-${s.label}`, "data-modeled": s.modeled ? "true" : "false", points: points, fill: "none", stroke: color, strokeWidth: "2", strokeLinejoin: "round", strokeLinecap: "round",
124
+ // The FOURTH measured-vs-modeled signal (e-AC-3/e-AC-10): dashed = modeled, solid = measured.
125
+ strokeDasharray: s.modeled ? "5 4" : undefined,
126
+ // One-shot draw-in via opacity; reuses `--dur-base`, suppressed under reduced-motion.
127
+ style: {
128
+ opacity: drawn ? 1 : 0,
129
+ transition: reduceMotion ? undefined : "opacity var(--dur-base) var(--ease-out)",
130
+ } }, s.label));
131
+ })] }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 14, marginTop: 8 }, children: trend.series.map((s) => (_jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [_jsx("svg", { width: 22, height: 8, "aria-hidden": "true", style: { flex: "none" }, children: _jsx("line", { x1: 1, y1: 4, x2: 21, y2: 4, stroke: seriesColor(s.label, s.modeled), strokeWidth: "2", strokeDasharray: s.modeled ? "4 3" : undefined }) }), _jsxs("span", { style: { fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-tertiary)" }, children: [s.label, s.modeled ? " (est.)" : ""] })] }, s.label))) })] }));
132
+ }
133
+ //# sourceMappingURL=roi-chart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roi-chart.js","sourceRoot":"","sources":["../../../../src/dashboard/web/pages/roi-chart.tsx"],"names":[],"mappings":"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"}