@hansenexus/hud 0.1.1 → 0.2.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 (91) hide show
  1. package/README.md +42 -23
  2. package/dist/index.d.ts +39 -0
  3. package/dist/index.js +399 -0
  4. package/package.json +45 -136
  5. package/chunk-20zke7cs.js +0 -574
  6. package/chunk-24vrg16w.js +0 -39
  7. package/chunk-50pq4sg6.js +0 -41
  8. package/chunk-5gtx3pza.js +0 -9
  9. package/chunk-6cdpasm9.js +0 -79
  10. package/chunk-6jt8yg4m.js +0 -117
  11. package/chunk-6ngkpwz7.js +0 -127
  12. package/chunk-71se605m.js +0 -85
  13. package/chunk-82nm9wcg.js +0 -316
  14. package/chunk-9hw5r9y1.js +0 -67
  15. package/chunk-c5y856ad.js +0 -98
  16. package/chunk-edq4jqch.js +0 -89
  17. package/chunk-emfnvz4p.js +0 -166
  18. package/chunk-h0hmjdqa.js +0 -264
  19. package/chunk-hc2g0n35.js +0 -24
  20. package/chunk-hex3wqag.js +0 -179
  21. package/chunk-kvxve0hx.js +0 -140
  22. package/chunk-kyeb6qeq.js +0 -95
  23. package/chunk-mz55rvw3.js +0 -172
  24. package/chunk-nm82wr8r.js +0 -58
  25. package/chunk-q15xwpy8.js +0 -48
  26. package/chunk-q96mkjve.js +0 -68
  27. package/chunk-tczzx083.js +0 -26
  28. package/chunk-tf774c4w.js +0 -115
  29. package/chunk-v086j53b.js +0 -19
  30. package/chunk-vjh0d29g.js +0 -52
  31. package/chunk-xezxydm3.js +0 -47
  32. package/chunk-xztw0xyt.js +0 -113
  33. package/chunk-yn1d38pb.js +0 -172
  34. package/core/access.d.mts +0 -53
  35. package/core/access.js +0 -27
  36. package/core/api-types.d.mts +0 -146
  37. package/core/api-types.js +0 -9
  38. package/core/capabilities.d.mts +0 -27
  39. package/core/capabilities.js +0 -14
  40. package/core/capture-buffer.d.mts +0 -37
  41. package/core/capture-buffer.js +0 -9
  42. package/core/force-state.d.mts +0 -68
  43. package/core/force-state.js +0 -21
  44. package/core/mock-cookie.d.mts +0 -30
  45. package/core/mock-cookie.js +0 -15
  46. package/core/types.d.mts +0 -59
  47. package/core/types.js +0 -9
  48. package/hud-provider.d.mts +0 -38
  49. package/hud-provider.js +0 -15
  50. package/hud.d.mts +0 -15
  51. package/hud.js +0 -18
  52. package/index.d.mts +0 -94
  53. package/index.js +0 -170
  54. package/lazy-panel.d.mts +0 -28
  55. package/lazy-panel.js +0 -11
  56. package/panels/cicd-panel.d.mts +0 -9
  57. package/panels/cicd-panel.js +0 -13
  58. package/panels/debug-info-panel.d.mts +0 -21
  59. package/panels/debug-info-panel.js +0 -9
  60. package/panels/dispatch-panel.d.mts +0 -10
  61. package/panels/dispatch-panel.js +0 -14
  62. package/panels/env-info-panel.d.mts +0 -9
  63. package/panels/env-info-panel.js +0 -12
  64. package/panels/feature-flags-panel.d.mts +0 -26
  65. package/panels/feature-flags-panel.js +0 -9
  66. package/panels/force-state-panel.d.mts +0 -11
  67. package/panels/force-state-panel.js +0 -113
  68. package/panels/info-row.d.mts +0 -21
  69. package/panels/info-row.js +0 -14
  70. package/panels/mock-seed-panel.d.mts +0 -15
  71. package/panels/mock-seed-panel.js +0 -14
  72. package/panels/network-console-panel.d.mts +0 -6
  73. package/panels/network-console-panel.js +0 -9
  74. package/panels/role-switcher-panel.d.mts +0 -28
  75. package/panels/role-switcher-panel.js +0 -9
  76. package/panels/sessions-panel.d.mts +0 -6
  77. package/panels/sessions-panel.js +0 -13
  78. package/panels/tokens-panel.d.mts +0 -38
  79. package/panels/tokens-panel.js +0 -9
  80. package/panels/unlock-panel.d.mts +0 -33
  81. package/panels/unlock-panel.js +0 -14
  82. package/panels/variants-panel.d.mts +0 -51
  83. package/panels/variants-panel.js +0 -12
  84. package/panels/vitals-panel.d.mts +0 -11
  85. package/panels/vitals-panel.js +0 -15
  86. package/server/guard.d.mts +0 -42
  87. package/server/guard.js +0 -12
  88. package/server/unlock.d.mts +0 -28
  89. package/server/unlock.js +0 -108
  90. package/state-boundary.d.mts +0 -25
  91. package/state-boundary.js +0 -54
package/chunk-emfnvz4p.js DELETED
@@ -1,166 +0,0 @@
1
- import {
2
- useHud
3
- } from "./chunk-9hw5r9y1.js";
4
- import {
5
- PanelSection
6
- } from "./chunk-kyeb6qeq.js";
7
- import {
8
- Badge,
9
- Button
10
- } from "./chunk-xztw0xyt.js";
11
-
12
- // panels/variants-panel.tsx
13
- import { ArrowUpRight, Beaker, Wand2 } from "lucide-react";
14
- import { useState } from "react";
15
- import { jsx, jsxs } from "react/jsx-runtime";
16
- "use client";
17
- var STATUS_CLASS = {
18
- active: "text-emerald-500",
19
- decided: "text-muted-foreground"
20
- };
21
- function VariantsPanel({ adapter }) {
22
- const { can } = useHud();
23
- const { categories, reviewHref, categoryHref, openPreview, generate } = adapter;
24
- const [path, setPath] = useState("");
25
- const [brief, setBrief] = useState("");
26
- const [result, setResult] = useState(null);
27
- const [sending, setSending] = useState(false);
28
- const grouped = new Map;
29
- for (const category of categories) {
30
- const key = category.domain ?? "shared";
31
- grouped.set(key, [...grouped.get(key) ?? [], category]);
32
- }
33
- async function submitGenerate() {
34
- if (!generate || !path.trim())
35
- return;
36
- setSending(true);
37
- setResult(null);
38
- try {
39
- setResult(await generate({ path: path.trim(), brief: brief.trim(), count: 3 }));
40
- } catch (error) {
41
- setResult({ ok: false, error: error instanceof Error ? error.message : "generate failed" });
42
- } finally {
43
- setSending(false);
44
- }
45
- }
46
- return /* @__PURE__ */ jsxs("div", {
47
- className: "space-y-4",
48
- children: [
49
- /* @__PURE__ */ jsxs(PanelSection, {
50
- icon: Beaker,
51
- title: `Categories (${categories.length})`,
52
- children: [
53
- categories.length === 0 && /* @__PURE__ */ jsx("p", {
54
- className: "px-2 text-[10px] text-muted-foreground/60",
55
- children: "No preview categories registered for this app."
56
- }),
57
- /* @__PURE__ */ jsx("div", {
58
- className: "space-y-2 px-2",
59
- children: Array.from(grouped.entries()).map(([domain, list]) => /* @__PURE__ */ jsxs("div", {
60
- children: [
61
- /* @__PURE__ */ jsx("div", {
62
- className: "text-[9px] uppercase tracking-wider text-muted-foreground/50",
63
- children: domain
64
- }),
65
- list.map((category) => /* @__PURE__ */ jsxs("div", {
66
- className: "flex items-center gap-2 py-1 text-[11px]",
67
- children: [
68
- /* @__PURE__ */ jsx(Badge, {
69
- variant: "outline",
70
- className: `h-4 px-1.5 text-[9px] ${STATUS_CLASS[category.status] ?? "text-amber-500"}`,
71
- children: category.status
72
- }),
73
- /* @__PURE__ */ jsx("span", {
74
- className: "min-w-0 flex-1 truncate",
75
- children: category.name
76
- }),
77
- /* @__PURE__ */ jsxs("span", {
78
- className: "text-muted-foreground/60",
79
- children: [
80
- category.alternativeCount,
81
- " alt",
82
- category.alternativeCount === 1 ? "" : "s"
83
- ]
84
- }),
85
- openPreview ? /* @__PURE__ */ jsx(Button, {
86
- variant: "ghost",
87
- size: "sm",
88
- className: "h-5 px-1.5 text-[10px]",
89
- onClick: () => openPreview(category.id),
90
- children: "open"
91
- }) : categoryHref ? /* @__PURE__ */ jsxs("a", {
92
- href: categoryHref(category.id),
93
- className: "inline-flex items-center gap-0.5 text-[10px] text-primary hover:underline",
94
- children: [
95
- "open",
96
- /* @__PURE__ */ jsx(ArrowUpRight, {
97
- className: "h-2.5 w-2.5"
98
- })
99
- ]
100
- }) : null
101
- ]
102
- }, category.id))
103
- ]
104
- }, domain))
105
- }),
106
- /* @__PURE__ */ jsx("div", {
107
- className: "px-2 pt-1",
108
- children: /* @__PURE__ */ jsxs("a", {
109
- href: reviewHref,
110
- className: "inline-flex items-center gap-1 text-[10px] text-primary hover:underline",
111
- children: [
112
- "Full review dashboard (vote, showcase, decisions)",
113
- /* @__PURE__ */ jsx(ArrowUpRight, {
114
- className: "h-3 w-3"
115
- })
116
- ]
117
- })
118
- })
119
- ]
120
- }),
121
- generate && can("variants.generate") && /* @__PURE__ */ jsx(PanelSection, {
122
- icon: Wand2,
123
- title: "Generate variants",
124
- children: /* @__PURE__ */ jsxs("div", {
125
- className: "space-y-2 px-2",
126
- children: [
127
- /* @__PURE__ */ jsx("input", {
128
- value: path,
129
- onChange: (e) => setPath(e.target.value),
130
- placeholder: "apps/console/src/components/… (repo-relative)",
131
- className: "w-full rounded-md border bg-muted/20 p-2 font-mono text-[11px] outline-none focus:ring-1 focus:ring-ring"
132
- }),
133
- /* @__PURE__ */ jsx("textarea", {
134
- value: brief,
135
- onChange: (e) => setBrief(e.target.value),
136
- placeholder: "Brief, e.g.: three denser table treatments, keep the glass language",
137
- className: "min-h-[44px] w-full rounded-md border bg-muted/20 p-2 font-mono text-[11px] outline-none focus:ring-1 focus:ring-ring"
138
- }),
139
- /* @__PURE__ */ jsxs("div", {
140
- className: "flex items-center gap-2",
141
- children: [
142
- /* @__PURE__ */ jsx(Button, {
143
- size: "sm",
144
- className: "h-6 text-[11px]",
145
- disabled: sending || !path.trim(),
146
- onClick: submitGenerate,
147
- children: "Queue 3 variants"
148
- }),
149
- /* @__PURE__ */ jsx("span", {
150
- className: "text-[10px] text-muted-foreground/60",
151
- children: "Writes a .claude/queue marker — the next session runs /generate-variants."
152
- })
153
- ]
154
- }),
155
- result && /* @__PURE__ */ jsx("p", {
156
- className: `font-mono text-[10px] ${result.ok ? "text-emerald-500" : "text-red-500"}`,
157
- children: result.ok ? `queued → ${result.markerPath}` : result.error
158
- })
159
- ]
160
- })
161
- })
162
- ]
163
- });
164
- }
165
-
166
- export { VariantsPanel };
package/chunk-h0hmjdqa.js DELETED
@@ -1,264 +0,0 @@
1
- import {
2
- bridgeFetch,
3
- getBridgeInfo
4
- } from "./chunk-50pq4sg6.js";
5
- import {
6
- clearGrabbedPayload,
7
- getGrabberState,
8
- startGrab,
9
- subscribeGrabber,
10
- useDevDrawer
11
- } from "./chunk-82nm9wcg.js";
12
- import {
13
- useHud
14
- } from "./chunk-9hw5r9y1.js";
15
- import {
16
- LoadingSpinner,
17
- PanelSection
18
- } from "./chunk-kyeb6qeq.js";
19
- import {
20
- Badge,
21
- Button
22
- } from "./chunk-xztw0xyt.js";
23
-
24
- // panels/dispatch-panel.tsx
25
- import { Crosshair, Send, X } from "lucide-react";
26
- import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
27
- import { jsx, jsxs } from "react/jsx-runtime";
28
- "use client";
29
- function DispatchPanel() {
30
- const { config } = useHud();
31
- const { closeDrawer } = useDevDrawer();
32
- const grabber = useSyncExternalStore(subscribeGrabber, getGrabberState, getGrabberState);
33
- const [bridge, setBridge] = useState(undefined);
34
- const [target, setTarget] = useState("docket");
35
- const [note, setNote] = useState("");
36
- const [runs, setRuns] = useState([]);
37
- const apiBase = config.apiBase ?? "/api/hud";
38
- useEffect(() => {
39
- let cancelled = false;
40
- getBridgeInfo(apiBase).then((info) => {
41
- if (cancelled)
42
- return;
43
- setBridge(info);
44
- if (!info)
45
- setTarget("hermes");
46
- });
47
- return () => {
48
- cancelled = true;
49
- };
50
- }, [apiBase]);
51
- const payload = grabber.payload;
52
- const targets = useMemo(() => [
53
- {
54
- id: "docket",
55
- label: "Local Claude session (docket)",
56
- detail: "worktree + claude -p via the bridge — events land on the docket board",
57
- enabled: bridge != null,
58
- disabledReason: "bridge not reachable (run `bun hud:bridge` in the repo)"
59
- },
60
- {
61
- id: "queue",
62
- label: "Queue marker (.claude/queue)",
63
- detail: "human in the loop — the next Claude session picks it up",
64
- enabled: bridge != null,
65
- disabledReason: "bridge not reachable"
66
- },
67
- {
68
- id: "hermes",
69
- label: "Hermes (remote)",
70
- detail: "via the console hub → hermes-dispatch workflow, tier balanced",
71
- enabled: true,
72
- disabledReason: ""
73
- }
74
- ], [bridge]);
75
- async function send() {
76
- if (!payload)
77
- return;
78
- const title = `${payload.anchor.label} — ${note.slice(0, 60) || "hud dispatch"}`;
79
- const localId = `local-${Date.now()}`;
80
- setRuns((prev) => [{ id: localId, title, target, status: "sending" }, ...prev]);
81
- clearGrabbedPayload();
82
- setNote("");
83
- try {
84
- let res;
85
- if (target === "hermes") {
86
- res = await fetch(`${apiBase}/dispatch`, {
87
- method: "POST",
88
- headers: { "Content-Type": "application/json", "x-hud-internal": "1" },
89
- body: JSON.stringify({ target, note, payload })
90
- });
91
- } else {
92
- if (!bridge)
93
- throw new Error("bridge not reachable");
94
- res = await bridgeFetch(bridge, "/dispatch", {
95
- method: "POST",
96
- body: JSON.stringify({ target, note, payload })
97
- });
98
- }
99
- const body = await res.json();
100
- setRuns((prev) => prev.map((run) => run.id === localId ? {
101
- ...run,
102
- status: body.ok ? "dispatched" : "failed",
103
- detail: body.runId ?? body.markerPath ?? body.error
104
- } : run));
105
- } catch (error) {
106
- setRuns((prev) => prev.map((run) => run.id === localId ? {
107
- ...run,
108
- status: "failed",
109
- detail: error instanceof Error ? error.message : "dispatch failed"
110
- } : run));
111
- }
112
- }
113
- return /* @__PURE__ */ jsxs("div", {
114
- className: "space-y-4",
115
- children: [
116
- !payload && /* @__PURE__ */ jsx(PanelSection, {
117
- icon: Crosshair,
118
- title: "Grab an element",
119
- children: /* @__PURE__ */ jsxs("div", {
120
- className: "px-2",
121
- children: [
122
- /* @__PURE__ */ jsxs(Button, {
123
- size: "sm",
124
- className: "h-7 text-xs",
125
- onClick: () => {
126
- closeDrawer();
127
- startGrab();
128
- },
129
- children: [
130
- /* @__PURE__ */ jsx(Crosshair, {
131
- className: "mr-1.5 h-3.5 w-3.5"
132
- }),
133
- "Grab element"
134
- ]
135
- }),
136
- /* @__PURE__ */ jsx("p", {
137
- className: "mt-2 text-[10px] text-muted-foreground/60",
138
- children: "Click any element on the page — selector, source hints, recent errors, and vitals become the dispatch payload."
139
- })
140
- ]
141
- })
142
- }),
143
- payload && /* @__PURE__ */ jsx(PanelSection, {
144
- icon: Send,
145
- title: "Dispatch",
146
- children: /* @__PURE__ */ jsxs("div", {
147
- className: "space-y-2 px-2",
148
- children: [
149
- /* @__PURE__ */ jsx(PayloadPreview, {
150
- payload,
151
- onDiscard: clearGrabbedPayload
152
- }),
153
- /* @__PURE__ */ jsx("textarea", {
154
- value: note,
155
- onChange: (e) => setNote(e.target.value),
156
- placeholder: "Instruction, e.g.: button feels heavy — try a ghost variant",
157
- className: "min-h-[52px] w-full rounded-md border bg-muted/20 p-2 font-mono text-[11px] outline-none focus:ring-1 focus:ring-ring"
158
- }),
159
- /* @__PURE__ */ jsx("div", {
160
- className: "space-y-1",
161
- children: targets.map((t) => /* @__PURE__ */ jsxs("label", {
162
- className: `flex cursor-pointer items-start gap-2 rounded-md border p-2 ${target === t.id ? "border-primary/60 bg-muted/30" : "border-border/50"} ${t.enabled ? "" : "cursor-not-allowed opacity-50"}`,
163
- children: [
164
- /* @__PURE__ */ jsx("input", {
165
- type: "radio",
166
- name: "hud-dispatch-target",
167
- checked: target === t.id,
168
- disabled: !t.enabled,
169
- onChange: () => setTarget(t.id),
170
- className: "mt-0.5"
171
- }),
172
- /* @__PURE__ */ jsxs("span", {
173
- children: [
174
- /* @__PURE__ */ jsx("span", {
175
- className: "block text-xs font-medium",
176
- children: t.label
177
- }),
178
- /* @__PURE__ */ jsx("span", {
179
- className: "block text-[10px] text-muted-foreground",
180
- children: t.enabled ? t.detail : t.disabledReason
181
- })
182
- ]
183
- })
184
- ]
185
- }, t.id))
186
- }),
187
- /* @__PURE__ */ jsxs(Button, {
188
- size: "sm",
189
- className: "h-7 text-xs",
190
- onClick: send,
191
- children: [
192
- /* @__PURE__ */ jsx(Send, {
193
- className: "mr-1.5 h-3 w-3"
194
- }),
195
- "Dispatch"
196
- ]
197
- })
198
- ]
199
- })
200
- }),
201
- bridge === undefined && /* @__PURE__ */ jsx(LoadingSpinner, {}),
202
- runs.length > 0 && /* @__PURE__ */ jsx(PanelSection, {
203
- title: "Dispatched from this session",
204
- children: runs.map((run) => /* @__PURE__ */ jsxs("div", {
205
- className: "flex items-center gap-2 px-2 py-1 font-mono text-[10.5px]",
206
- children: [
207
- /* @__PURE__ */ jsx(Badge, {
208
- variant: "outline",
209
- className: `h-4 px-1.5 text-[9px] ${run.status === "dispatched" ? "text-emerald-500" : run.status === "failed" ? "text-red-500" : "text-amber-500"}`,
210
- children: run.status
211
- }),
212
- /* @__PURE__ */ jsx("span", {
213
- className: "min-w-0 flex-1 truncate",
214
- children: run.title
215
- }),
216
- /* @__PURE__ */ jsx("span", {
217
- className: "text-muted-foreground/60",
218
- children: run.target
219
- }),
220
- run.detail && /* @__PURE__ */ jsx("span", {
221
- className: "max-w-[180px] truncate text-muted-foreground/60",
222
- title: run.detail,
223
- children: run.detail
224
- })
225
- ]
226
- }, run.id))
227
- })
228
- ]
229
- });
230
- }
231
- function PayloadPreview({
232
- payload,
233
- onDiscard
234
- }) {
235
- return /* @__PURE__ */ jsxs("div", {
236
- className: "rounded-md border bg-muted/20 p-2",
237
- children: [
238
- /* @__PURE__ */ jsxs("div", {
239
- className: "flex items-center gap-2",
240
- children: [
241
- /* @__PURE__ */ jsx("span", {
242
- className: "min-w-0 flex-1 truncate font-mono text-[11px]",
243
- children: payload.anchor.label
244
- }),
245
- /* @__PURE__ */ jsx(Button, {
246
- variant: "ghost",
247
- size: "icon",
248
- className: "h-5 w-5",
249
- onClick: onDiscard,
250
- children: /* @__PURE__ */ jsx(X, {
251
- className: "h-3 w-3"
252
- })
253
- })
254
- ]
255
- }),
256
- /* @__PURE__ */ jsx("pre", {
257
- className: "mt-1 max-h-40 overflow-auto text-[9.5px] leading-relaxed text-muted-foreground",
258
- children: JSON.stringify(payload, null, 2)
259
- })
260
- ]
261
- });
262
- }
263
-
264
- export { DispatchPanel };
package/chunk-hc2g0n35.js DELETED
@@ -1,24 +0,0 @@
1
- // core/mock-cookie.ts
2
- var HUD_MOCK_COOKIE = "hud-mock";
3
- function isHudMockEnabled(cookieValue, env) {
4
- const hudAllowed = env.nodeEnv === "development" || env.hudEnabled === "true";
5
- return hudAllowed && cookieValue === "1";
6
- }
7
- function hudMockCookieString(enabled) {
8
- if (!enabled)
9
- return `${HUD_MOCK_COOKIE}=; path=/; max-age=0; samesite=lax`;
10
- return `${HUD_MOCK_COOKIE}=1; path=/; max-age=${60 * 60 * 24 * 30}; samesite=lax`;
11
- }
12
- function readHudMockCookie() {
13
- if (typeof document === "undefined")
14
- return;
15
- const match = document.cookie.split("; ").find((part) => part.startsWith(`${HUD_MOCK_COOKIE}=`));
16
- return match?.slice(HUD_MOCK_COOKIE.length + 1);
17
- }
18
- function setHudMockCookie(enabled) {
19
- if (typeof document === "undefined")
20
- return;
21
- document.cookie = hudMockCookieString(enabled);
22
- }
23
-
24
- export { HUD_MOCK_COOKIE, isHudMockEnabled, hudMockCookieString, readHudMockCookie, setHudMockCookie };
package/chunk-hex3wqag.js DELETED
@@ -1,179 +0,0 @@
1
- import {
2
- bridgeFetch,
3
- getBridgeInfo
4
- } from "./chunk-50pq4sg6.js";
5
- import {
6
- useHud
7
- } from "./chunk-9hw5r9y1.js";
8
- import {
9
- LoadingSpinner,
10
- PanelSection
11
- } from "./chunk-kyeb6qeq.js";
12
- import {
13
- Badge,
14
- Button,
15
- Switch
16
- } from "./chunk-xztw0xyt.js";
17
- import {
18
- readHudMockCookie,
19
- setHudMockCookie
20
- } from "./chunk-hc2g0n35.js";
21
-
22
- // panels/mock-seed-panel.tsx
23
- import { Database, FlaskConical, RefreshCw } from "lucide-react";
24
- import { useEffect, useState } from "react";
25
- import { jsx, jsxs } from "react/jsx-runtime";
26
- "use client";
27
- function MockSeedPanel({ seeds = [] }) {
28
- const { config, can } = useHud();
29
- const [mockCookie, setMockCookie] = useState(undefined);
30
- const [bridge, setBridge] = useState(undefined);
31
- const [runs, setRuns] = useState([]);
32
- const apiBase = config.apiBase ?? "/api/hud";
33
- const canSeed = can("inject.seed") && seeds.length > 0;
34
- useEffect(() => {
35
- setMockCookie(readHudMockCookie());
36
- }, []);
37
- useEffect(() => {
38
- if (!canSeed)
39
- return;
40
- let cancelled = false;
41
- getBridgeInfo(apiBase).then((info) => {
42
- if (!cancelled)
43
- setBridge(info);
44
- });
45
- return () => {
46
- cancelled = true;
47
- };
48
- }, [apiBase, canSeed]);
49
- const mockOn = mockCookie === "1";
50
- function toggleMock(next) {
51
- setHudMockCookie(next);
52
- setMockCookie(next ? "1" : undefined);
53
- window.location.reload();
54
- }
55
- async function runSeed(seed) {
56
- if (!bridge)
57
- return;
58
- const runId = `${seed.id}-${Date.now()}`;
59
- setRuns((prev) => [{ id: runId, label: seed.label, status: "running" }, ...prev]);
60
- try {
61
- const res = await bridgeFetch(bridge, "/seed", {
62
- method: "POST",
63
- body: JSON.stringify({
64
- app: config.appSlug,
65
- fn: seed.fn,
66
- args: seed.args,
67
- local: seed.local
68
- })
69
- });
70
- const body = await res.json();
71
- setRuns((prev) => prev.map((run) => run.id === runId ? { ...run, status: body.ok ? "done" : "failed", detail: body.output ?? body.error } : run));
72
- } catch (error) {
73
- setRuns((prev) => prev.map((run) => run.id === runId ? {
74
- ...run,
75
- status: "failed",
76
- detail: error instanceof Error ? error.message : "seed failed"
77
- } : run));
78
- }
79
- }
80
- return /* @__PURE__ */ jsxs("div", {
81
- className: "space-y-4",
82
- children: [
83
- can("mock.toggle") && /* @__PURE__ */ jsx(PanelSection, {
84
- icon: FlaskConical,
85
- title: "Mock Convex",
86
- children: /* @__PURE__ */ jsxs("div", {
87
- className: "flex items-center justify-between px-2",
88
- children: [
89
- /* @__PURE__ */ jsxs("div", {
90
- children: [
91
- /* @__PURE__ */ jsx("div", {
92
- className: "text-xs font-medium",
93
- children: "Mock backend"
94
- }),
95
- /* @__PURE__ */ jsx("p", {
96
- className: "text-[10px] text-muted-foreground/60",
97
- children: "Swaps the Convex client for a dummy — reload applies (server re-reads the cookie)."
98
- })
99
- ]
100
- }),
101
- /* @__PURE__ */ jsxs("div", {
102
- className: "flex items-center gap-2",
103
- children: [
104
- mockOn && /* @__PURE__ */ jsx(Badge, {
105
- variant: "outline",
106
- className: "h-4 px-1.5 text-[9px] text-amber-500",
107
- children: "mock"
108
- }),
109
- /* @__PURE__ */ jsx(Switch, {
110
- checked: mockOn,
111
- onCheckedChange: toggleMock
112
- })
113
- ]
114
- })
115
- ]
116
- })
117
- }),
118
- canSeed && /* @__PURE__ */ jsxs(PanelSection, {
119
- icon: Database,
120
- title: "Seed data",
121
- children: [
122
- bridge === undefined && /* @__PURE__ */ jsx(LoadingSpinner, {}),
123
- bridge === null && /* @__PURE__ */ jsx("p", {
124
- className: "px-2 text-[10px] text-amber-500",
125
- children: "bridge not reachable — run `bun hud:bridge` in the repo to seed from here."
126
- }),
127
- bridge != null && /* @__PURE__ */ jsx("div", {
128
- className: "space-y-1 px-2",
129
- children: seeds.map((seed) => /* @__PURE__ */ jsxs("div", {
130
- className: "flex items-center gap-2",
131
- children: [
132
- /* @__PURE__ */ jsxs(Button, {
133
- size: "sm",
134
- variant: "outline",
135
- className: "h-6 text-[11px]",
136
- onClick: () => runSeed(seed),
137
- children: [
138
- /* @__PURE__ */ jsx(RefreshCw, {
139
- className: "mr-1.5 h-3 w-3"
140
- }),
141
- seed.label
142
- ]
143
- }),
144
- seed.description && /* @__PURE__ */ jsx("span", {
145
- className: "min-w-0 flex-1 truncate text-[10px] text-muted-foreground/60",
146
- children: seed.description
147
- })
148
- ]
149
- }, seed.id))
150
- })
151
- ]
152
- }),
153
- runs.length > 0 && /* @__PURE__ */ jsx(PanelSection, {
154
- title: "Seed runs",
155
- children: runs.map((run) => /* @__PURE__ */ jsxs("div", {
156
- className: "flex items-center gap-2 px-2 py-1 font-mono text-[10.5px]",
157
- children: [
158
- /* @__PURE__ */ jsx(Badge, {
159
- variant: "outline",
160
- className: `h-4 px-1.5 text-[9px] ${run.status === "done" ? "text-emerald-500" : run.status === "failed" ? "text-red-500" : "text-amber-500"}`,
161
- children: run.status
162
- }),
163
- /* @__PURE__ */ jsx("span", {
164
- className: "min-w-0 flex-1 truncate",
165
- children: run.label
166
- }),
167
- run.detail && /* @__PURE__ */ jsx("span", {
168
- className: "max-w-[220px] truncate text-muted-foreground/60",
169
- title: run.detail,
170
- children: run.detail
171
- })
172
- ]
173
- }, run.id))
174
- })
175
- ]
176
- });
177
- }
178
-
179
- export { MockSeedPanel };