@hansenexus/hud 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 (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/chunk-0cj8dkk7.js +127 -0
  4. package/chunk-1atxwdhw.js +48 -0
  5. package/chunk-24vrg16w.js +39 -0
  6. package/chunk-3j9sm249.js +26 -0
  7. package/chunk-50pq4sg6.js +41 -0
  8. package/chunk-5gtx3pza.js +9 -0
  9. package/chunk-6gw4x08j.js +264 -0
  10. package/chunk-6jt8yg4m.js +117 -0
  11. package/chunk-71se605m.js +85 -0
  12. package/chunk-82nm9wcg.js +316 -0
  13. package/chunk-8tam7r3h.js +68 -0
  14. package/chunk-babjpqkb.js +52 -0
  15. package/chunk-c5y856ad.js +98 -0
  16. package/chunk-ghxh8926.js +172 -0
  17. package/chunk-hc2g0n35.js +24 -0
  18. package/chunk-k0vr8dsy.js +179 -0
  19. package/chunk-k50yfp5d.js +166 -0
  20. package/chunk-kyeb6qeq.js +95 -0
  21. package/chunk-msqkkqw7.js +140 -0
  22. package/chunk-nm82wr8r.js +58 -0
  23. package/chunk-r06bpmy6.js +79 -0
  24. package/chunk-t9eh4t0b.js +89 -0
  25. package/chunk-tf774c4w.js +115 -0
  26. package/chunk-v086j53b.js +19 -0
  27. package/chunk-wspz08q7.js +67 -0
  28. package/chunk-xezxydm3.js +47 -0
  29. package/chunk-xjn0896g.js +574 -0
  30. package/chunk-xztw0xyt.js +113 -0
  31. package/chunk-zzrpd92r.js +172 -0
  32. package/core/access.d.mts +53 -0
  33. package/core/access.js +27 -0
  34. package/core/api-types.d.mts +146 -0
  35. package/core/api-types.js +9 -0
  36. package/core/capabilities.d.mts +27 -0
  37. package/core/capabilities.js +14 -0
  38. package/core/capture-buffer.d.mts +37 -0
  39. package/core/capture-buffer.js +9 -0
  40. package/core/force-state.d.mts +68 -0
  41. package/core/force-state.js +21 -0
  42. package/core/mock-cookie.d.mts +30 -0
  43. package/core/mock-cookie.js +15 -0
  44. package/core/types.d.mts +59 -0
  45. package/core/types.js +9 -0
  46. package/hud-provider.d.mts +38 -0
  47. package/hud-provider.js +15 -0
  48. package/hud.d.mts +15 -0
  49. package/hud.js +18 -0
  50. package/index.d.mts +94 -0
  51. package/index.js +170 -0
  52. package/lazy-panel.d.mts +28 -0
  53. package/lazy-panel.js +11 -0
  54. package/package.json +153 -0
  55. package/panels/cicd-panel.d.mts +9 -0
  56. package/panels/cicd-panel.js +13 -0
  57. package/panels/debug-info-panel.d.mts +21 -0
  58. package/panels/debug-info-panel.js +9 -0
  59. package/panels/dispatch-panel.d.mts +10 -0
  60. package/panels/dispatch-panel.js +14 -0
  61. package/panels/env-info-panel.d.mts +9 -0
  62. package/panels/env-info-panel.js +12 -0
  63. package/panels/feature-flags-panel.d.mts +26 -0
  64. package/panels/feature-flags-panel.js +9 -0
  65. package/panels/force-state-panel.d.mts +11 -0
  66. package/panels/force-state-panel.js +113 -0
  67. package/panels/info-row.d.mts +21 -0
  68. package/panels/info-row.js +14 -0
  69. package/panels/mock-seed-panel.d.mts +15 -0
  70. package/panels/mock-seed-panel.js +14 -0
  71. package/panels/network-console-panel.d.mts +6 -0
  72. package/panels/network-console-panel.js +9 -0
  73. package/panels/role-switcher-panel.d.mts +28 -0
  74. package/panels/role-switcher-panel.js +9 -0
  75. package/panels/sessions-panel.d.mts +6 -0
  76. package/panels/sessions-panel.js +13 -0
  77. package/panels/tokens-panel.d.mts +38 -0
  78. package/panels/tokens-panel.js +9 -0
  79. package/panels/unlock-panel.d.mts +33 -0
  80. package/panels/unlock-panel.js +14 -0
  81. package/panels/variants-panel.d.mts +51 -0
  82. package/panels/variants-panel.js +12 -0
  83. package/panels/vitals-panel.d.mts +11 -0
  84. package/panels/vitals-panel.js +15 -0
  85. package/server/guard.d.mts +42 -0
  86. package/server/guard.js +12 -0
  87. package/server/unlock.d.mts +28 -0
  88. package/server/unlock.js +108 -0
  89. package/state-boundary.d.mts +25 -0
  90. package/state-boundary.js +54 -0
@@ -0,0 +1,117 @@
1
+ import {
2
+ LoadingSpinner,
3
+ PanelSection
4
+ } from "./chunk-kyeb6qeq.js";
5
+ import {
6
+ Badge,
7
+ Button
8
+ } from "./chunk-xztw0xyt.js";
9
+
10
+ // panels/role-switcher-panel.tsx
11
+ import { Check, Shield, User } from "lucide-react";
12
+ import { useState } from "react";
13
+ import { jsx, jsxs } from "react/jsx-runtime";
14
+ "use client";
15
+ function RoleSwitcherPanel({ adapter }) {
16
+ const { roles, currentRoleId, currentUserLabel, isLoading = false, onSwitch } = adapter;
17
+ const [switching, setSwitching] = useState(false);
18
+ const [error, setError] = useState(null);
19
+ async function handleSwitch(roleId) {
20
+ if (roleId === currentRoleId)
21
+ return;
22
+ setSwitching(true);
23
+ setError(null);
24
+ try {
25
+ await onSwitch(roleId);
26
+ } catch (err) {
27
+ setError(err instanceof Error ? err.message : "Role switch failed");
28
+ } finally {
29
+ setSwitching(false);
30
+ }
31
+ }
32
+ if (isLoading)
33
+ return /* @__PURE__ */ jsx(LoadingSpinner, {});
34
+ const current = roles.find((role) => role.id === currentRoleId);
35
+ return /* @__PURE__ */ jsxs("div", {
36
+ className: "space-y-4",
37
+ children: [
38
+ /* @__PURE__ */ jsx(PanelSection, {
39
+ icon: Shield,
40
+ title: "Current Role",
41
+ children: /* @__PURE__ */ jsxs("div", {
42
+ className: "border bg-muted/30 px-3 py-2",
43
+ children: [
44
+ /* @__PURE__ */ jsxs("div", {
45
+ className: "flex items-center gap-2",
46
+ children: [
47
+ /* @__PURE__ */ jsx(User, {
48
+ className: "h-4 w-4 text-muted-foreground"
49
+ }),
50
+ /* @__PURE__ */ jsx("span", {
51
+ className: "text-sm font-medium",
52
+ children: currentUserLabel ?? "Unknown"
53
+ })
54
+ ]
55
+ }),
56
+ /* @__PURE__ */ jsx(Badge, {
57
+ variant: "outline",
58
+ className: `mt-1 ${current?.badgeClassName ?? ""}`,
59
+ children: current?.label ?? currentRoleId ?? "—"
60
+ })
61
+ ]
62
+ })
63
+ }),
64
+ /* @__PURE__ */ jsx(PanelSection, {
65
+ title: "Switch Role",
66
+ children: roles.map((role) => {
67
+ const isCurrent = role.id === currentRoleId;
68
+ return /* @__PURE__ */ jsxs(Button, {
69
+ variant: "ghost",
70
+ size: "sm",
71
+ disabled: isCurrent || switching,
72
+ className: "w-full justify-between text-xs",
73
+ onClick: () => handleSwitch(role.id),
74
+ children: [
75
+ /* @__PURE__ */ jsxs("div", {
76
+ className: "flex items-center gap-2",
77
+ children: [
78
+ /* @__PURE__ */ jsx("span", {
79
+ className: `h-2 w-2 rounded-full ${isCurrent ? "bg-primary" : "bg-muted-foreground/30"}`
80
+ }),
81
+ /* @__PURE__ */ jsx("span", {
82
+ children: role.label
83
+ })
84
+ ]
85
+ }),
86
+ /* @__PURE__ */ jsxs("div", {
87
+ className: "flex items-center gap-1.5",
88
+ children: [
89
+ role.level !== undefined && /* @__PURE__ */ jsxs("span", {
90
+ className: "text-[10px] text-muted-foreground",
91
+ children: [
92
+ "Lvl ",
93
+ role.level
94
+ ]
95
+ }),
96
+ isCurrent && /* @__PURE__ */ jsx(Check, {
97
+ className: "h-3 w-3 text-primary"
98
+ })
99
+ ]
100
+ })
101
+ ]
102
+ }, role.id);
103
+ })
104
+ }),
105
+ error && /* @__PURE__ */ jsx("p", {
106
+ className: "px-2 text-[10px] text-destructive",
107
+ children: error
108
+ }),
109
+ /* @__PURE__ */ jsx("p", {
110
+ className: "text-[10px] text-muted-foreground/60",
111
+ children: "Role changes take effect immediately. Use this for testing role-based access."
112
+ })
113
+ ]
114
+ });
115
+ }
116
+
117
+ export { RoleSwitcherPanel };
@@ -0,0 +1,85 @@
1
+ import {
2
+ LoadingSpinner
3
+ } from "./chunk-kyeb6qeq.js";
4
+ import {
5
+ Badge,
6
+ Switch
7
+ } from "./chunk-xztw0xyt.js";
8
+
9
+ // panels/feature-flags-panel.tsx
10
+ import { Flag } from "lucide-react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ "use client";
13
+ function FeatureFlagsPanel({ adapter }) {
14
+ const { flags, isLoading = false, onToggle } = adapter;
15
+ if (isLoading)
16
+ return /* @__PURE__ */ jsx(LoadingSpinner, {});
17
+ const categories = [...new Set(flags.map((flag) => flag.category ?? "general"))];
18
+ return /* @__PURE__ */ jsxs("div", {
19
+ className: "space-y-4",
20
+ children: [
21
+ categories.map((category) => /* @__PURE__ */ jsxs("div", {
22
+ children: [
23
+ /* @__PURE__ */ jsxs("h4", {
24
+ className: "mb-2 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground",
25
+ children: [
26
+ /* @__PURE__ */ jsx(Flag, {
27
+ className: "h-3 w-3"
28
+ }),
29
+ category
30
+ ]
31
+ }),
32
+ /* @__PURE__ */ jsx("div", {
33
+ className: "space-y-1",
34
+ children: flags.filter((flag) => (flag.category ?? "general") === category).map((flag) => /* @__PURE__ */ jsx(FlagRow, {
35
+ flag,
36
+ onToggle: () => void onToggle(flag.id)
37
+ }, flag.id))
38
+ })
39
+ ]
40
+ }, category)),
41
+ flags.length === 0 && /* @__PURE__ */ jsx("p", {
42
+ className: "px-2 text-[10px] text-muted-foreground/60",
43
+ children: "No flags registered."
44
+ })
45
+ ]
46
+ });
47
+ }
48
+ function FlagRow({ flag, onToggle }) {
49
+ const isOverridden = flag.enabled !== flag.defaultValue;
50
+ return /* @__PURE__ */ jsxs("div", {
51
+ className: "flex items-center justify-between px-2 py-1.5 hover:bg-muted/50",
52
+ children: [
53
+ /* @__PURE__ */ jsxs("div", {
54
+ className: "min-w-0 flex-1",
55
+ children: [
56
+ /* @__PURE__ */ jsxs("div", {
57
+ className: "flex items-center gap-1.5",
58
+ children: [
59
+ /* @__PURE__ */ jsx("span", {
60
+ className: "text-xs font-medium",
61
+ children: flag.label
62
+ }),
63
+ isOverridden && /* @__PURE__ */ jsx(Badge, {
64
+ variant: "outline",
65
+ className: "h-4 border-amber-500/50 px-1 text-[9px] text-amber-500",
66
+ children: "overridden"
67
+ })
68
+ ]
69
+ }),
70
+ flag.description && /* @__PURE__ */ jsx("p", {
71
+ className: "truncate text-[10px] text-muted-foreground",
72
+ children: flag.description
73
+ })
74
+ ]
75
+ }),
76
+ /* @__PURE__ */ jsx(Switch, {
77
+ checked: flag.enabled,
78
+ onCheckedChange: onToggle,
79
+ className: "ml-2 shrink-0"
80
+ })
81
+ ]
82
+ });
83
+ }
84
+
85
+ export { FeatureFlagsPanel };
@@ -0,0 +1,316 @@
1
+ import {
2
+ Badge,
3
+ Button,
4
+ cn
5
+ } from "./chunk-xztw0xyt.js";
6
+
7
+ // grabber/grabber-store.ts
8
+ var state = { mode: "idle", payload: null };
9
+ var listeners = new Set;
10
+ function setState(next) {
11
+ state = next;
12
+ for (const listener of listeners)
13
+ listener();
14
+ }
15
+ function getGrabberState() {
16
+ return state;
17
+ }
18
+ function subscribeGrabber(listener) {
19
+ listeners.add(listener);
20
+ return () => listeners.delete(listener);
21
+ }
22
+ function startGrab() {
23
+ setState({ ...state, mode: "grabbing" });
24
+ }
25
+ function cancelGrab() {
26
+ setState({ ...state, mode: "idle" });
27
+ }
28
+ function completeGrab(payload) {
29
+ setState({ mode: "idle", payload });
30
+ }
31
+ function clearGrabbedPayload() {
32
+ setState({ ...state, payload: null });
33
+ }
34
+
35
+ // ../dev-tools/drawer/dev-drawer-provider.tsx
36
+ import {
37
+ createContext,
38
+ useCallback,
39
+ useContext,
40
+ useEffect,
41
+ useMemo,
42
+ useState
43
+ } from "react";
44
+ import { jsx } from "react/jsx-runtime";
45
+ "use client";
46
+ var DevDrawerContext = createContext(null);
47
+ var DEFAULTS = {
48
+ defaultHeight: 40,
49
+ minHeight: 25,
50
+ maxHeight: 80,
51
+ shortcutKey: "d"
52
+ };
53
+ function loadStored(storageKey) {
54
+ if (!storageKey || typeof window === "undefined")
55
+ return {};
56
+ try {
57
+ const raw = localStorage.getItem(storageKey);
58
+ if (!raw)
59
+ return {};
60
+ const parsed = JSON.parse(raw);
61
+ return {
62
+ height: typeof parsed.height === "number" ? parsed.height : undefined,
63
+ activeTab: typeof parsed.activeTab === "string" ? parsed.activeTab : undefined
64
+ };
65
+ } catch {
66
+ return {};
67
+ }
68
+ }
69
+ function persist(storageKey, state2) {
70
+ if (!storageKey || typeof window === "undefined")
71
+ return;
72
+ try {
73
+ localStorage.setItem(storageKey, JSON.stringify(state2));
74
+ } catch {}
75
+ }
76
+ function DevDrawerProvider({ config, children }) {
77
+ const defaultHeight = config?.defaultHeight ?? DEFAULTS.defaultHeight;
78
+ const minHeight = config?.minHeight ?? DEFAULTS.minHeight;
79
+ const maxHeight = config?.maxHeight ?? DEFAULTS.maxHeight;
80
+ const shortcutKey = config?.shortcutKey === null ? null : config?.shortcutKey ?? DEFAULTS.shortcutKey;
81
+ const storageKey = config?.storageKey;
82
+ const [isOpen, setIsOpen] = useState(false);
83
+ const [activeTab, setActiveTabState] = useState(config?.defaultActiveTab ?? null);
84
+ const [height, setHeightState] = useState(defaultHeight);
85
+ useEffect(() => {
86
+ const stored = loadStored(storageKey);
87
+ if (stored.height != null)
88
+ setHeightState(stored.height);
89
+ if (stored.activeTab != null)
90
+ setActiveTabState(stored.activeTab);
91
+ }, [storageKey]);
92
+ useEffect(() => {
93
+ persist(storageKey, { height, activeTab });
94
+ }, [storageKey, height, activeTab]);
95
+ useEffect(() => {
96
+ if (!shortcutKey)
97
+ return;
98
+ const handler = (event) => {
99
+ if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key.toLowerCase() === shortcutKey) {
100
+ event.preventDefault();
101
+ setIsOpen((prev) => !prev);
102
+ }
103
+ };
104
+ window.addEventListener("keydown", handler);
105
+ return () => window.removeEventListener("keydown", handler);
106
+ }, [shortcutKey]);
107
+ const toggleDrawer = useCallback(() => setIsOpen((prev) => !prev), []);
108
+ const openDrawer = useCallback(() => setIsOpen(true), []);
109
+ const closeDrawer = useCallback(() => setIsOpen(false), []);
110
+ const setActiveTab = useCallback((tab) => setActiveTabState(tab), []);
111
+ const setHeight = useCallback((next) => setHeightState(Math.min(maxHeight, Math.max(minHeight, next))), [minHeight, maxHeight]);
112
+ const value = useMemo(() => ({
113
+ isOpen,
114
+ activeTab,
115
+ height,
116
+ minHeight,
117
+ maxHeight,
118
+ toggleDrawer,
119
+ openDrawer,
120
+ closeDrawer,
121
+ setActiveTab,
122
+ setHeight
123
+ }), [
124
+ isOpen,
125
+ activeTab,
126
+ height,
127
+ minHeight,
128
+ maxHeight,
129
+ toggleDrawer,
130
+ openDrawer,
131
+ closeDrawer,
132
+ setActiveTab,
133
+ setHeight
134
+ ]);
135
+ return /* @__PURE__ */ jsx(DevDrawerContext.Provider, {
136
+ value,
137
+ children
138
+ });
139
+ }
140
+ function useDevDrawer() {
141
+ const ctx = useContext(DevDrawerContext);
142
+ if (!ctx)
143
+ throw new Error("useDevDrawer must be used within a DevDrawerProvider");
144
+ return ctx;
145
+ }
146
+
147
+ // ../dev-tools/drawer/dev-drawer.tsx
148
+ import { Wrench, X } from "lucide-react";
149
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
150
+ "use client";
151
+ function DevDrawer({
152
+ tabs,
153
+ children,
154
+ title = "Dev Toolbar",
155
+ badgeLabel = "DEV",
156
+ headerIcon: HeaderIcon = Wrench,
157
+ className
158
+ }) {
159
+ const { isOpen, activeTab, setActiveTab, closeDrawer, height, minHeight, maxHeight, setHeight } = useDevDrawer();
160
+ if (!isOpen)
161
+ return null;
162
+ const resolvedTabs = tabs && tabs.length > 0 ? tabs : null;
163
+ const currentTab = resolvedTabs?.find((tab) => tab.id === activeTab) ?? resolvedTabs?.[0];
164
+ const handleResizeStart = (event) => {
165
+ event.preventDefault();
166
+ const startY = event.clientY;
167
+ const startHeight = height;
168
+ function onMove(moveEvent) {
169
+ const delta = startY - moveEvent.clientY;
170
+ const deltaPercent = delta / window.innerHeight * 100;
171
+ setHeight(startHeight + deltaPercent);
172
+ }
173
+ function onUp() {
174
+ document.removeEventListener("pointermove", onMove);
175
+ document.removeEventListener("pointerup", onUp);
176
+ }
177
+ document.addEventListener("pointermove", onMove);
178
+ document.addEventListener("pointerup", onUp);
179
+ };
180
+ return /* @__PURE__ */ jsxs("div", {
181
+ className: cn("fixed inset-x-0 bottom-0 z-50 border-t border-border/50 bg-background/95 backdrop-blur-md", className),
182
+ style: { height: `${height}vh` },
183
+ children: [
184
+ /* @__PURE__ */ jsx2("div", {
185
+ role: "slider",
186
+ tabIndex: 0,
187
+ "aria-label": "Resize dev drawer",
188
+ "aria-orientation": "horizontal",
189
+ "aria-valuemin": Math.round(minHeight),
190
+ "aria-valuemax": Math.round(maxHeight),
191
+ "aria-valuenow": Math.round(height),
192
+ className: "absolute inset-x-0 -top-1 flex h-3 cursor-ns-resize items-center justify-center focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
193
+ onPointerDown: handleResizeStart,
194
+ onKeyDown: (event) => {
195
+ if (event.key === "ArrowUp") {
196
+ event.preventDefault();
197
+ setHeight(height + 2);
198
+ } else if (event.key === "ArrowDown") {
199
+ event.preventDefault();
200
+ setHeight(height - 2);
201
+ }
202
+ },
203
+ children: /* @__PURE__ */ jsx2("div", {
204
+ className: "h-1 w-8 bg-muted-foreground/30"
205
+ })
206
+ }),
207
+ /* @__PURE__ */ jsxs("div", {
208
+ className: "flex h-10 items-center justify-between border-b border-border/50 px-3",
209
+ children: [
210
+ /* @__PURE__ */ jsxs("div", {
211
+ className: "flex items-center gap-2",
212
+ children: [
213
+ /* @__PURE__ */ jsx2(HeaderIcon, {
214
+ className: "h-3.5 w-3.5 text-amber-500"
215
+ }),
216
+ /* @__PURE__ */ jsx2("span", {
217
+ className: "text-xs font-semibold",
218
+ children: title
219
+ }),
220
+ badgeLabel ? /* @__PURE__ */ jsx2(Badge, {
221
+ variant: "outline",
222
+ className: "h-4 border-amber-500/30 px-1 text-[9px] text-amber-500",
223
+ children: badgeLabel
224
+ }) : null
225
+ ]
226
+ }),
227
+ /* @__PURE__ */ jsxs("div", {
228
+ className: "flex items-center gap-1",
229
+ children: [
230
+ resolvedTabs?.map((tab) => {
231
+ const Icon = tab.icon;
232
+ const isActive = tab.id === (currentTab?.id ?? null);
233
+ return /* @__PURE__ */ jsxs(Button, {
234
+ variant: isActive ? "secondary" : "ghost",
235
+ size: "sm",
236
+ className: "h-6 gap-1 px-2 text-[10px]",
237
+ onClick: () => setActiveTab(tab.id),
238
+ children: [
239
+ Icon ? /* @__PURE__ */ jsx2(Icon, {
240
+ className: "h-3 w-3"
241
+ }) : null,
242
+ tab.label
243
+ ]
244
+ }, tab.id);
245
+ }),
246
+ resolvedTabs ? /* @__PURE__ */ jsx2("div", {
247
+ className: "mx-1 h-4 w-px bg-border"
248
+ }) : null,
249
+ /* @__PURE__ */ jsx2(Button, {
250
+ variant: "ghost",
251
+ size: "icon",
252
+ className: "h-6 w-6",
253
+ onClick: closeDrawer,
254
+ children: /* @__PURE__ */ jsx2(X, {
255
+ className: "h-3 w-3"
256
+ })
257
+ })
258
+ ]
259
+ })
260
+ ]
261
+ }),
262
+ /* @__PURE__ */ jsx2("div", {
263
+ className: "h-[calc(100%-2.5rem)] overflow-y-auto px-3 py-3",
264
+ children: resolvedTabs ? currentTab?.content : children
265
+ })
266
+ ]
267
+ });
268
+ }
269
+ // ../ui/components/tooltip.tsx
270
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
271
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
272
+ "use client";
273
+
274
+ // ../dev-tools/fab/dev-floating-menu.tsx
275
+ import { MoreHorizontal } from "lucide-react";
276
+ import { AnimatePresence, motion } from "motion/react";
277
+
278
+ // ../ui/components/kbd.tsx
279
+ import { jsx as jsx4 } from "react/jsx-runtime";
280
+
281
+ // ../dev-tools/fab/fab-action.tsx
282
+ import Link from "next/link";
283
+ import { useCallback as useCallback2, useRef } from "react";
284
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
285
+ "use client";
286
+ var baseClass = cn("size-9 rounded-full", "bg-background/80 backdrop-blur-sm border border-border/50", "shadow-sm hover:shadow-md transition-all duration-200");
287
+ var bauhausBaseClass = cn("size-9 rounded-full border backdrop-blur-2xl transition-all duration-200", "border-[rgb(var(--bauhaus-border-strong))] bg-bauhaus-bg/60 text-bauhaus-fg/55", "shadow-[0_6px_24px_rgba(0,0,0,0.45)]", "hover:bg-bauhaus-bg/80 hover:text-[var(--fab-ink)] hover:border-[var(--fab-ink)]");
288
+
289
+ // ../dev-tools/fab/use-fab-state.ts
290
+ import { useCallback as useCallback3, useEffect as useEffect2, useState as useState2 } from "react";
291
+ "use client";
292
+
293
+ // ../dev-tools/fab/dev-floating-menu.tsx
294
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
295
+ "use client";
296
+ // ../dev-tools/fab/dock-context.tsx
297
+ import {
298
+ createContext as createContext2,
299
+ useCallback as useCallback4,
300
+ useContext as useContext2,
301
+ useEffect as useEffect3,
302
+ useMemo as useMemo2,
303
+ useState as useState3
304
+ } from "react";
305
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
306
+ "use client";
307
+ var FloatingDockContext = createContext2(null);
308
+ // ../dev-tools/gate/dev-only.tsx
309
+ import { jsx as jsx8, Fragment } from "react/jsx-runtime";
310
+ "use client";
311
+ // ../dev-tools/panels/variants-link-panel.tsx
312
+ import { ArrowUpRight, Beaker } from "lucide-react";
313
+ import Link2 from "next/link";
314
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
315
+ "use client";
316
+ export { getGrabberState, subscribeGrabber, startGrab, cancelGrab, completeGrab, clearGrabbedPayload, DevDrawerProvider, useDevDrawer, DevDrawer };
@@ -0,0 +1,68 @@
1
+ import {
2
+ InfoRow,
3
+ LoadingSpinner,
4
+ PanelSection
5
+ } from "./chunk-kyeb6qeq.js";
6
+
7
+ // panels/debug-info-panel.tsx
8
+ import { Info, Monitor } from "lucide-react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ "use client";
11
+ function DebugInfoPanel({ user, isLoading = false }) {
12
+ if (isLoading)
13
+ return /* @__PURE__ */ jsx(LoadingSpinner, {});
14
+ return /* @__PURE__ */ jsxs("div", {
15
+ className: "space-y-4",
16
+ children: [
17
+ user && /* @__PURE__ */ jsxs(PanelSection, {
18
+ icon: Info,
19
+ title: "User",
20
+ children: [
21
+ /* @__PURE__ */ jsx(InfoRow, {
22
+ label: "Name",
23
+ value: user.name ?? "—"
24
+ }),
25
+ /* @__PURE__ */ jsx(InfoRow, {
26
+ label: "Email",
27
+ value: user.email ?? "—",
28
+ copyable: !!user.email
29
+ }),
30
+ /* @__PURE__ */ jsx(InfoRow, {
31
+ label: "ID",
32
+ value: user.id ?? "—",
33
+ copyable: !!user.id
34
+ }),
35
+ /* @__PURE__ */ jsx(InfoRow, {
36
+ label: "Role",
37
+ value: user.role ?? "—"
38
+ }),
39
+ Object.entries(user.extra ?? {}).map(([label, value]) => /* @__PURE__ */ jsx(InfoRow, {
40
+ label,
41
+ value
42
+ }, label))
43
+ ]
44
+ }),
45
+ /* @__PURE__ */ jsxs(PanelSection, {
46
+ icon: Monitor,
47
+ title: "Environment",
48
+ children: [
49
+ /* @__PURE__ */ jsx(InfoRow, {
50
+ label: "NODE_ENV",
51
+ value: "production"
52
+ }),
53
+ /* @__PURE__ */ jsx(InfoRow, {
54
+ label: "Convex URL",
55
+ value: process.env.NEXT_PUBLIC_CONVEX_URL ?? "not set",
56
+ copyable: true
57
+ }),
58
+ /* @__PURE__ */ jsx(InfoRow, {
59
+ label: "Origin",
60
+ value: typeof window !== "undefined" ? window.location.origin : "—"
61
+ })
62
+ ]
63
+ })
64
+ ]
65
+ });
66
+ }
67
+
68
+ export { DebugInfoPanel };
@@ -0,0 +1,52 @@
1
+ import {
2
+ HUD_CAPABILITIES
3
+ } from "./chunk-v086j53b.js";
4
+
5
+ // core/capabilities.ts
6
+ var CAPABILITY_MATRIX = {
7
+ development: HUD_CAPABILITIES,
8
+ staging: [
9
+ "observe.app",
10
+ "observe.estate",
11
+ "cicd.read",
12
+ "mock.toggle",
13
+ "auth.impersonate",
14
+ "variants.switch",
15
+ "variants.vote"
16
+ ],
17
+ preview: [
18
+ "observe.app",
19
+ "observe.estate",
20
+ "cicd.read",
21
+ "mock.toggle",
22
+ "variants.switch",
23
+ "variants.vote"
24
+ ],
25
+ production: ["observe.app", "observe.estate", "cicd.read"],
26
+ disabled: []
27
+ };
28
+ function resolveHudEnv() {
29
+ if (process.env.NEXT_PUBLIC_HUD_DISABLED === "true")
30
+ return "disabled";
31
+ if (false)
32
+ ;
33
+ const explicit = process.env.NEXT_PUBLIC_HUD_ENV;
34
+ if (explicit === "staging" || explicit === "preview" || explicit === "production") {
35
+ return explicit;
36
+ }
37
+ if (process.env.VERCEL_ENV === "preview")
38
+ return "preview";
39
+ return "production";
40
+ }
41
+ function isHudVisible(env = resolveHudEnv()) {
42
+ if (env === "disabled")
43
+ return false;
44
+ if (env === "development")
45
+ return true;
46
+ return process.env.NEXT_PUBLIC_HUD_ENABLED === "true";
47
+ }
48
+ function hasCapability(env, capability) {
49
+ return CAPABILITY_MATRIX[env].includes(capability);
50
+ }
51
+
52
+ export { CAPABILITY_MATRIX, resolveHudEnv, isHudVisible, hasCapability };