@arqel-dev/ui 0.11.0 → 0.13.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 (111) hide show
  1. package/SKILL.md +5 -2
  2. package/dist/AreaChart-NMQ2RQGA.js +33 -0
  3. package/dist/AreaChart-NMQ2RQGA.js.map +1 -0
  4. package/dist/ArqelCreatePage-P3G6TKIK.js +8 -0
  5. package/dist/ArqelCreatePage-P3G6TKIK.js.map +1 -0
  6. package/dist/ArqelDashboardPage-PX3Y75AA.js +6 -0
  7. package/dist/ArqelDashboardPage-PX3Y75AA.js.map +1 -0
  8. package/dist/ArqelEditPage-HKQSE6E5.js +8 -0
  9. package/dist/ArqelEditPage-HKQSE6E5.js.map +1 -0
  10. package/dist/ArqelIndexPage-MRH3572Z.js +12 -0
  11. package/dist/ArqelIndexPage-MRH3572Z.js.map +1 -0
  12. package/dist/ArqelShowPage-4FS5MTUN.js +6 -0
  13. package/dist/ArqelShowPage-4FS5MTUN.js.map +1 -0
  14. package/dist/BarChart-KLHKTLO2.js +19 -0
  15. package/dist/BarChart-KLHKTLO2.js.map +1 -0
  16. package/dist/DonutChart-L56R4QF2.js +11 -0
  17. package/dist/DonutChart-L56R4QF2.js.map +1 -0
  18. package/dist/LineChart-GHMPHFZZ.js +30 -0
  19. package/dist/LineChart-GHMPHFZZ.js.map +1 -0
  20. package/dist/PieChart-T2VQOFPP.js +4 -0
  21. package/dist/PieChart-T2VQOFPP.js.map +1 -0
  22. package/dist/RadarChart-CSWR5AO7.js +32 -0
  23. package/dist/RadarChart-CSWR5AO7.js.map +1 -0
  24. package/dist/Topbar-DffcNdcJ.d.ts +50 -0
  25. package/dist/action.d.ts +12 -2
  26. package/dist/action.js +6 -1090
  27. package/dist/action.js.map +1 -1
  28. package/dist/auth.js +1 -10
  29. package/dist/auth.js.map +1 -1
  30. package/dist/chunk-2LTZJ4R7.js +110 -0
  31. package/dist/chunk-2LTZJ4R7.js.map +1 -0
  32. package/dist/chunk-3JJOHYD7.js +57 -0
  33. package/dist/chunk-3JJOHYD7.js.map +1 -0
  34. package/dist/chunk-47HD7ALI.js +370 -0
  35. package/dist/chunk-47HD7ALI.js.map +1 -0
  36. package/dist/chunk-6UJQP7KU.js +737 -0
  37. package/dist/chunk-6UJQP7KU.js.map +1 -0
  38. package/dist/chunk-7LSZADUC.js +50 -0
  39. package/dist/chunk-7LSZADUC.js.map +1 -0
  40. package/dist/chunk-B7ULUJVO.js +11 -0
  41. package/dist/chunk-B7ULUJVO.js.map +1 -0
  42. package/dist/chunk-C5H44NSP.js +37 -0
  43. package/dist/chunk-C5H44NSP.js.map +1 -0
  44. package/dist/chunk-DSULVRB6.js +101 -0
  45. package/dist/chunk-DSULVRB6.js.map +1 -0
  46. package/dist/chunk-ERT7LL6C.js +199 -0
  47. package/dist/chunk-ERT7LL6C.js.map +1 -0
  48. package/dist/chunk-EYAUPJ6J.js +64 -0
  49. package/dist/chunk-EYAUPJ6J.js.map +1 -0
  50. package/dist/chunk-FHRQ7PWE.js +58 -0
  51. package/dist/chunk-FHRQ7PWE.js.map +1 -0
  52. package/dist/chunk-FV7RESKL.js +285 -0
  53. package/dist/chunk-FV7RESKL.js.map +1 -0
  54. package/dist/chunk-K7QY56XN.js +610 -0
  55. package/dist/chunk-K7QY56XN.js.map +1 -0
  56. package/dist/chunk-L3JBBN4H.js +27 -0
  57. package/dist/chunk-L3JBBN4H.js.map +1 -0
  58. package/dist/chunk-MSRTGCTJ.js +597 -0
  59. package/dist/chunk-MSRTGCTJ.js.map +1 -0
  60. package/dist/chunk-MTNFFSVA.js +92 -0
  61. package/dist/chunk-MTNFFSVA.js.map +1 -0
  62. package/dist/chunk-MTZJDFQ6.js +32 -0
  63. package/dist/chunk-MTZJDFQ6.js.map +1 -0
  64. package/dist/chunk-MXUXMHOK.js +16 -0
  65. package/dist/chunk-MXUXMHOK.js.map +1 -0
  66. package/dist/chunk-PXON56HL.js +35 -0
  67. package/dist/chunk-PXON56HL.js.map +1 -0
  68. package/dist/chunk-PZEVKMZX.js +598 -0
  69. package/dist/chunk-PZEVKMZX.js.map +1 -0
  70. package/dist/chunk-R4BOUU6Y.js +55 -0
  71. package/dist/chunk-R4BOUU6Y.js.map +1 -0
  72. package/dist/chunk-RMPOR2YZ.js +12 -0
  73. package/dist/chunk-RMPOR2YZ.js.map +1 -0
  74. package/dist/chunk-RNAMHQM6.js +12 -0
  75. package/dist/chunk-RNAMHQM6.js.map +1 -0
  76. package/dist/chunk-TNJEHYRF.js +387 -0
  77. package/dist/chunk-TNJEHYRF.js.map +1 -0
  78. package/dist/chunk-VSTRVXXZ.js +49 -0
  79. package/dist/chunk-VSTRVXXZ.js.map +1 -0
  80. package/dist/chunk-WKWV36PG.js +140 -0
  81. package/dist/chunk-WKWV36PG.js.map +1 -0
  82. package/dist/cn-By8XWiSA.d.ts +12 -0
  83. package/dist/flash.js +2 -104
  84. package/dist/flash.js.map +1 -1
  85. package/dist/form.js +4 -673
  86. package/dist/form.js.map +1 -1
  87. package/dist/index.d.ts +2 -2
  88. package/dist/index.js +18 -3706
  89. package/dist/index.js.map +1 -1
  90. package/dist/pages.js +23 -3025
  91. package/dist/pages.js.map +1 -1
  92. package/dist/palette.js +1 -283
  93. package/dist/palette.js.map +1 -1
  94. package/dist/primitives.js +3 -658
  95. package/dist/primitives.js.map +1 -1
  96. package/dist/resource.js +5 -720
  97. package/dist/resource.js.map +1 -1
  98. package/dist/shell.d.ts +26 -46
  99. package/dist/shell.js +46 -1039
  100. package/dist/shell.js.map +1 -1
  101. package/dist/table.js +4 -618
  102. package/dist/table.js.map +1 -1
  103. package/dist/utility.js +3 -104
  104. package/dist/utility.js.map +1 -1
  105. package/dist/utils.d.ts +12 -6
  106. package/dist/utils.js +2 -9
  107. package/dist/utils.js.map +1 -1
  108. package/dist/widgets.d.ts +7 -2
  109. package/dist/widgets.js +3 -675
  110. package/dist/widgets.js.map +1 -1
  111. package/package.json +15 -14
package/dist/palette.js CHANGED
@@ -1,285 +1,3 @@
1
- import { useRef, useState, useId, useEffect, useCallback } from 'react';
2
- import { jsx, jsxs } from 'react/jsx-runtime';
3
-
4
- // src/palette/CommandPalette.tsx
5
- var RECENT_KEY = "arqel:cmdpal:recent";
6
- var RECENT_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
7
- var RECENT_LIMIT = 5;
8
- var DEBOUNCE_MS = 150;
9
- function readRecents() {
10
- try {
11
- const raw = globalThis.localStorage?.getItem(RECENT_KEY);
12
- if (!raw) return [];
13
- const parsed = JSON.parse(raw);
14
- if (!Array.isArray(parsed)) return [];
15
- const now = Date.now();
16
- return parsed.filter(
17
- (e) => typeof e === "object" && e !== null && typeof e.id === "string" && typeof e.count === "number" && typeof e.lastUsed === "number" && now - e.lastUsed <= RECENT_WINDOW_MS && typeof e.command === "object"
18
- );
19
- } catch {
20
- return [];
21
- }
22
- }
23
- function writeRecents(entries) {
24
- try {
25
- globalThis.localStorage?.setItem(RECENT_KEY, JSON.stringify(entries));
26
- } catch {
27
- }
28
- }
29
- function recordUsage(command) {
30
- const entries = readRecents();
31
- const now = Date.now();
32
- const idx = entries.findIndex((e) => e.id === command.id);
33
- if (idx >= 0) {
34
- const existing = entries[idx];
35
- if (existing) {
36
- entries[idx] = {
37
- ...existing,
38
- count: existing.count + 1,
39
- lastUsed: now,
40
- command
41
- };
42
- }
43
- } else {
44
- entries.push({ id: command.id, count: 1, lastUsed: now, command });
45
- }
46
- const fresh = entries.filter((e) => now - e.lastUsed <= RECENT_WINDOW_MS);
47
- writeRecents(fresh);
48
- }
49
- function topRecents() {
50
- return readRecents().slice().sort((a, b) => b.count === a.count ? b.lastUsed - a.lastUsed : b.count - a.count).slice(0, RECENT_LIMIT).map((e) => e.command);
51
- }
52
- function groupByCategory(commands) {
53
- const order = [];
54
- const buckets = /* @__PURE__ */ new Map();
55
- for (const cmd of commands) {
56
- const key = cmd.category ?? "General";
57
- if (!buckets.has(key)) {
58
- buckets.set(key, []);
59
- order.push(key);
60
- }
61
- buckets.get(key)?.push(cmd);
62
- }
63
- return order.map((category) => ({ category, commands: buckets.get(category) ?? [] }));
64
- }
65
- function flattenGroups(groups) {
66
- return groups.flatMap((g) => g.commands);
67
- }
68
- var dialogStyle = {
69
- border: "none",
70
- borderRadius: "0.75rem",
71
- padding: 0,
72
- width: "min(640px, 92vw)",
73
- maxHeight: "70vh",
74
- background: "var(--background, #fff)",
75
- color: "var(--foreground, #111)"
76
- };
77
- function CommandPalette({ endpoint = "/admin/commands" }) {
78
- const dialogRef = useRef(null);
79
- const inputRef = useRef(null);
80
- const debounceTimer = useRef(null);
81
- const [open, setOpen] = useState(false);
82
- const [query, setQuery] = useState("");
83
- const [results, setResults] = useState([]);
84
- const [recents, setRecents] = useState([]);
85
- const [highlight, setHighlight] = useState(0);
86
- const titleId = useId();
87
- const hintId = useId();
88
- const listId = useId();
89
- const optBaseId = useId();
90
- const visibleCommands = query.trim().length === 0 ? recents : results;
91
- const groups = query.trim().length === 0 && recents.length > 0 ? [{ category: "Recent", commands: recents }] : groupByCategory(visibleCommands);
92
- const flat = flattenGroups(groups);
93
- useEffect(() => {
94
- function onKey(event) {
95
- const isToggle = (event.key === "k" || event.key === "K") && (event.metaKey || event.ctrlKey);
96
- if (!isToggle) return;
97
- event.preventDefault();
98
- setOpen((prev) => !prev);
99
- }
100
- window.addEventListener("keydown", onKey);
101
- return () => window.removeEventListener("keydown", onKey);
102
- }, []);
103
- useEffect(() => {
104
- const dialog = dialogRef.current;
105
- if (!dialog) return;
106
- if (open && !dialog.open) {
107
- try {
108
- dialog.showModal();
109
- } catch {
110
- }
111
- setRecents(topRecents());
112
- setQuery("");
113
- setResults([]);
114
- setHighlight(0);
115
- queueMicrotask(() => inputRef.current?.focus());
116
- } else if (!open && dialog.open) {
117
- dialog.close();
118
- }
119
- }, [open]);
120
- useEffect(() => {
121
- if (!open) return;
122
- if (query.trim().length === 0) {
123
- setResults([]);
124
- return;
125
- }
126
- if (debounceTimer.current) clearTimeout(debounceTimer.current);
127
- debounceTimer.current = setTimeout(() => {
128
- const url = `${endpoint}?q=${encodeURIComponent(query)}`;
129
- fetch(url, { headers: { Accept: "application/json" } }).then((res) => res.ok ? res.json() : { commands: [] }).then((data) => {
130
- setResults(Array.isArray(data?.commands) ? data.commands : []);
131
- setHighlight(0);
132
- }).catch(() => {
133
- setResults([]);
134
- });
135
- }, DEBOUNCE_MS);
136
- return () => {
137
- if (debounceTimer.current) clearTimeout(debounceTimer.current);
138
- };
139
- }, [query, open, endpoint]);
140
- const close = useCallback(() => setOpen(false), []);
141
- const execute = useCallback((command) => {
142
- recordUsage(command);
143
- setOpen(false);
144
- window.location.assign(command.url);
145
- }, []);
146
- const onKeyDown = useCallback(
147
- (event) => {
148
- if (event.key === "Escape") {
149
- event.preventDefault();
150
- close();
151
- return;
152
- }
153
- if (flat.length === 0) return;
154
- if (event.key === "ArrowDown") {
155
- event.preventDefault();
156
- setHighlight((h) => (h + 1) % flat.length);
157
- } else if (event.key === "ArrowUp") {
158
- event.preventDefault();
159
- setHighlight((h) => (h - 1 + flat.length) % flat.length);
160
- } else if (event.key === "Enter") {
161
- event.preventDefault();
162
- const target = flat[highlight];
163
- if (target) execute(target);
164
- }
165
- },
166
- [flat, highlight, close, execute]
167
- );
168
- const onDialogClick = useCallback(
169
- (event) => {
170
- if (event.target === dialogRef.current) close();
171
- },
172
- [close]
173
- );
174
- const optionId = (idx) => `${optBaseId}-opt-${idx}`;
175
- const activeOptionId = flat.length > 0 ? optionId(highlight) : void 0;
176
- return (
177
- // biome-ignore lint/a11y/useKeyWithClickEvents: dialog backdrop click closes; Escape (onCancel) handles keyboard close
178
- /* @__PURE__ */ jsx(
179
- "dialog",
180
- {
181
- ref: dialogRef,
182
- "aria-labelledby": titleId,
183
- "aria-describedby": hintId,
184
- onClick: onDialogClick,
185
- onClose: () => setOpen(false),
186
- onCancel: (event) => {
187
- event.preventDefault();
188
- close();
189
- },
190
- style: dialogStyle,
191
- className: "backdrop:bg-black/40",
192
- "data-arqel-cmdpal": "",
193
- children: /* @__PURE__ */ jsxs("div", { onKeyDown, className: "flex flex-col", children: [
194
- /* @__PURE__ */ jsx("h2", { id: titleId, className: "sr-only", children: "Command palette" }),
195
- /* @__PURE__ */ jsx(
196
- "input",
197
- {
198
- ref: inputRef,
199
- type: "text",
200
- role: "combobox",
201
- "aria-expanded": open,
202
- "aria-controls": listId,
203
- "aria-autocomplete": "list",
204
- ...activeOptionId ? { "aria-activedescendant": activeOptionId } : {},
205
- placeholder: "Type a command\u2026",
206
- value: query,
207
- onChange: (e) => setQuery(e.target.value),
208
- className: "w-full border-b border-[var(--border,#e5e7eb)] bg-transparent px-4 py-3 text-base outline-none"
209
- }
210
- ),
211
- /* @__PURE__ */ jsxs("div", { role: "status", "aria-live": "polite", className: "sr-only", children: [
212
- flat.length,
213
- " commands"
214
- ] }),
215
- /* @__PURE__ */ jsxs(
216
- "ul",
217
- {
218
- id: listId,
219
- role: "listbox",
220
- "aria-label": "Commands",
221
- className: "max-h-[50vh] overflow-y-auto py-2",
222
- children: [
223
- groups.length === 0 && /* @__PURE__ */ jsx("li", { role: "presentation", className: "px-4 py-3 text-sm opacity-60", children: "No commands found" }),
224
- groups.map((group) => {
225
- const startIdx = flat.indexOf(group.commands[0] ?? {});
226
- return /* @__PURE__ */ jsxs("li", { role: "presentation", children: [
227
- /* @__PURE__ */ jsx(
228
- "div",
229
- {
230
- role: "presentation",
231
- className: "px-4 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide opacity-60",
232
- children: group.category
233
- }
234
- ),
235
- /* @__PURE__ */ jsx("ul", { role: "presentation", className: "m-0 list-none p-0", children: group.commands.map((cmd, j) => {
236
- const idx = startIdx + j;
237
- const isActive = idx === highlight;
238
- return (
239
- // biome-ignore lint/a11y/useFocusableInteractive: focus stays on combobox; selection via aria-activedescendant
240
- // biome-ignore lint/a11y/useKeyWithClickEvents: keyboard nav handled by combobox input ArrowUp/Down/Enter handler
241
- /* @__PURE__ */ jsxs(
242
- "li",
243
- {
244
- id: optionId(idx),
245
- role: "option",
246
- "aria-selected": isActive,
247
- "data-active": isActive ? "" : void 0,
248
- onMouseEnter: () => setHighlight(idx),
249
- onClick: () => execute(cmd),
250
- className: "flex cursor-pointer items-center gap-3 px-4 py-2 text-sm " + (isActive ? "bg-[var(--accent,#f1f5f9)]" : ""),
251
- children: [
252
- /* @__PURE__ */ jsx("span", { className: "flex-1", children: cmd.label }),
253
- cmd.description ? /* @__PURE__ */ jsx("span", { className: "text-xs opacity-60", children: cmd.description }) : null
254
- ]
255
- },
256
- cmd.id
257
- )
258
- );
259
- }) })
260
- ] }, group.category);
261
- })
262
- ]
263
- }
264
- ),
265
- /* @__PURE__ */ jsxs(
266
- "footer",
267
- {
268
- id: hintId,
269
- className: "flex items-center gap-3 border-t border-[var(--border,#e5e7eb)] px-4 py-2 text-xs opacity-70",
270
- children: [
271
- /* @__PURE__ */ jsx("span", { children: "\u2191\u2193 navigate" }),
272
- /* @__PURE__ */ jsx("span", { children: "\u21B5 select" }),
273
- /* @__PURE__ */ jsx("span", { children: "esc close" })
274
- ]
275
- }
276
- )
277
- ] })
278
- }
279
- )
280
- );
281
- }
282
-
283
- export { CommandPalette };
1
+ export { CommandPalette } from './chunk-FV7RESKL.js';
284
2
  //# sourceMappingURL=palette.js.map
285
3
  //# sourceMappingURL=palette.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/palette/CommandPalette.tsx"],"names":[],"mappings":";;;;AAmCA,IAAM,UAAA,GAAa,qBAAA;AACnB,IAAM,gBAAA,GAAmB,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAC7C,IAAM,YAAA,GAAe,CAAA;AACrB,IAAM,WAAA,GAAc,GAAA;AAEpB,SAAS,WAAA,GAA6B;AACpC,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,YAAA,EAAc,OAAA,CAAQ,UAAU,CAAA;AACvD,IAAA,IAAI,CAAC,GAAA,EAAK,OAAO,EAAC;AAClB,IAAA,MAAM,MAAA,GAAkB,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACtC,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,SAAU,EAAC;AACpC,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,OAAO,MAAA,CAAO,MAAA;AAAA,MACZ,CAAC,CAAA,KACC,OAAO,CAAA,KAAM,QAAA,IACb,MAAM,IAAA,IACN,OAAQ,CAAA,CAAkB,EAAA,KAAO,QAAA,IACjC,OAAQ,EAAkB,KAAA,KAAU,QAAA,IACpC,OAAQ,CAAA,CAAkB,QAAA,KAAa,QAAA,IACvC,GAAA,GAAO,CAAA,CAAkB,QAAA,IAAY,gBAAA,IACrC,OAAQ,CAAA,CAAkB,OAAA,KAAY;AAAA,KAC1C;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAC;AAAA,EACV;AACF;AAEA,SAAS,aAAa,OAAA,EAA8B;AAClD,EAAA,IAAI;AACF,IAAA,UAAA,CAAW,cAAc,OAAA,CAAQ,UAAA,EAAY,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,EACtE,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAEA,SAAS,YAAY,OAAA,EAA+B;AAClD,EAAA,MAAM,UAAU,WAAA,EAAY;AAC5B,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,MAAM,GAAA,GAAM,QAAQ,SAAA,CAAU,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,QAAQ,EAAE,CAAA;AACxD,EAAA,IAAI,OAAO,CAAA,EAAG;AACZ,IAAA,MAAM,QAAA,GAAW,QAAQ,GAAG,CAAA;AAC5B,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,OAAA,CAAQ,GAAG,CAAA,GAAI;AAAA,QACb,GAAG,QAAA;AAAA,QACH,KAAA,EAAO,SAAS,KAAA,GAAQ,CAAA;AAAA,QACxB,QAAA,EAAU,GAAA;AAAA,QACV;AAAA,OACF;AAAA,IACF;AAAA,EACF,CAAA,MAAO;AACL,IAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,EAAA,EAAI,OAAA,CAAQ,EAAA,EAAI,OAAO,CAAA,EAAG,QAAA,EAAU,GAAA,EAAK,OAAA,EAAS,CAAA;AAAA,EACnE;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAQ,MAAA,CAAO,CAAC,MAAM,GAAA,GAAM,CAAA,CAAE,YAAY,gBAAgB,CAAA;AACxE,EAAA,YAAA,CAAa,KAAK,CAAA;AACpB;AAEA,SAAS,UAAA,GAA+B;AACtC,EAAA,OAAO,WAAA,EAAY,CAChB,KAAA,EAAM,CACN,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAO,CAAA,CAAE,KAAA,KAAU,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,QAAA,GAAW,CAAA,CAAE,QAAA,GAAW,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,KAAM,CAAA,CAClF,KAAA,CAAM,CAAA,EAAG,YAAY,CAAA,CACrB,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,OAAO,CAAA;AACzB;AAOA,SAAS,gBAAgB,QAAA,EAAqC;AAC5D,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,OAAA,uBAAc,GAAA,EAA8B;AAClD,EAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,IAAA,MAAM,GAAA,GAAM,IAAI,QAAA,IAAY,SAAA;AAC5B,IAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,EAAG;AACrB,MAAA,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,EAAE,CAAA;AACnB,MAAA,KAAA,CAAM,KAAK,GAAG,CAAA;AAAA,IAChB;AACA,IAAA,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,EAAG,IAAA,CAAK,GAAG,CAAA;AAAA,EAC5B;AACA,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,QAAA,MAAc,EAAE,QAAA,EAAU,QAAA,EAAU,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA,IAAK,IAAG,CAAE,CAAA;AACtF;AAEA,SAAS,cAAc,MAAA,EAAmC;AACxD,EAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,KAAM,EAAE,QAAQ,CAAA;AACzC;AAEA,IAAM,WAAA,GAA6B;AAAA,EACjC,MAAA,EAAQ,MAAA;AAAA,EACR,YAAA,EAAc,SAAA;AAAA,EACd,OAAA,EAAS,CAAA;AAAA,EACT,KAAA,EAAO,kBAAA;AAAA,EACP,SAAA,EAAW,MAAA;AAAA,EACX,UAAA,EAAY,yBAAA;AAAA,EACZ,KAAA,EAAO;AACT,CAAA;AAEO,SAAS,cAAA,CAAe,EAAE,QAAA,GAAW,iBAAA,EAAkB,EAAwB;AACpF,EAAA,MAAM,SAAA,GAAY,OAAiC,IAAI,CAAA;AACvD,EAAA,MAAM,QAAA,GAAW,OAAgC,IAAI,CAAA;AACrD,EAAA,MAAM,aAAA,GAAgB,OAA6C,IAAI,CAAA;AACvE,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AACtC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,EAAE,CAAA;AACrC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAA2B,EAAE,CAAA;AAC3D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAA2B,EAAE,CAAA;AAC3D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,CAAC,CAAA;AAE5C,EAAA,MAAM,UAAU,KAAA,EAAM;AACtB,EAAA,MAAM,SAAS,KAAA,EAAM;AACrB,EAAA,MAAM,SAAS,KAAA,EAAM;AACrB,EAAA,MAAM,YAAY,KAAA,EAAM;AAExB,EAAA,MAAM,kBAAkB,KAAA,CAAM,IAAA,EAAK,CAAE,MAAA,KAAW,IAAI,OAAA,GAAU,OAAA;AAC9D,EAAA,MAAM,SACJ,KAAA,CAAM,IAAA,GAAO,MAAA,KAAW,CAAA,IAAK,QAAQ,MAAA,GAAS,CAAA,GAC1C,CAAC,EAAE,UAAU,QAAA,EAAU,QAAA,EAAU,SAAS,CAAA,GAC1C,gBAAgB,eAAe,CAAA;AACrC,EAAA,MAAM,IAAA,GAAO,cAAc,MAAM,CAAA;AAGjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAS,MAAM,KAAA,EAAsB;AACnC,MAAA,MAAM,QAAA,GAAA,CAAY,MAAM,GAAA,KAAQ,GAAA,IAAO,MAAM,GAAA,KAAQ,GAAA,MAAS,KAAA,CAAM,OAAA,IAAW,KAAA,CAAM,OAAA,CAAA;AACrF,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,KAAA,CAAM,cAAA,EAAe;AACrB,MAAA,OAAA,CAAQ,CAAC,IAAA,KAAS,CAAC,IAAI,CAAA;AAAA,IACzB;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,KAAK,CAAA;AACxC,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,KAAK,CAAA;AAAA,EAC1D,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,SAAS,SAAA,CAAU,OAAA;AACzB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,IAAI,IAAA,IAAQ,CAAC,MAAA,CAAO,IAAA,EAAM;AACxB,MAAA,IAAI;AACF,QAAA,MAAA,CAAO,SAAA,EAAU;AAAA,MACnB,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,UAAA,CAAW,YAAY,CAAA;AACvB,MAAA,QAAA,CAAS,EAAE,CAAA;AACX,MAAA,UAAA,CAAW,EAAE,CAAA;AACb,MAAA,YAAA,CAAa,CAAC,CAAA;AAEd,MAAA,cAAA,CAAe,MAAM,QAAA,CAAS,OAAA,EAAS,KAAA,EAAO,CAAA;AAAA,IAChD,CAAA,MAAA,IAAW,CAAC,IAAA,IAAQ,MAAA,CAAO,IAAA,EAAM;AAC/B,MAAA,MAAA,CAAO,KAAA,EAAM;AAAA,IACf;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAGT,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,IAAI,KAAA,CAAM,IAAA,EAAK,CAAE,MAAA,KAAW,CAAA,EAAG;AAC7B,MAAA,UAAA,CAAW,EAAE,CAAA;AACb,MAAA;AAAA,IACF;AACA,IAAA,IAAI,aAAA,CAAc,OAAA,EAAS,YAAA,CAAa,aAAA,CAAc,OAAO,CAAA;AAC7D,IAAA,aAAA,CAAc,OAAA,GAAU,WAAW,MAAM;AACvC,MAAA,MAAM,MAAM,CAAA,EAAG,QAAQ,CAAA,GAAA,EAAM,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AACtD,MAAA,KAAA,CAAM,GAAA,EAAK,EAAE,OAAA,EAAS,EAAE,MAAA,EAAQ,oBAAmB,EAAG,CAAA,CACnD,IAAA,CAAK,CAAC,GAAA,KAAS,IAAI,EAAA,GAAK,GAAA,CAAI,IAAA,EAAK,GAAI,EAAE,QAAA,EAAU,EAAC,EAAI,CAAA,CACtD,IAAA,CAAK,CAAC,IAAA,KAA0C;AAC/C,QAAA,UAAA,CAAW,KAAA,CAAM,QAAQ,IAAA,EAAM,QAAQ,IAAI,IAAA,CAAK,QAAA,GAAW,EAAE,CAAA;AAC7D,QAAA,YAAA,CAAa,CAAC,CAAA;AAAA,MAChB,CAAC,CAAA,CACA,KAAA,CAAM,MAAM;AACX,QAAA,UAAA,CAAW,EAAE,CAAA;AAAA,MACf,CAAC,CAAA;AAAA,IACL,GAAG,WAAW,CAAA;AACd,IAAA,OAAO,MAAM;AACX,MAAA,IAAI,aAAA,CAAc,OAAA,EAAS,YAAA,CAAa,aAAA,CAAc,OAAO,CAAA;AAAA,IAC/D,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,KAAA,EAAO,IAAA,EAAM,QAAQ,CAAC,CAAA;AAE1B,EAAA,MAAM,QAAQ,WAAA,CAAY,MAAM,QAAQ,KAAK,CAAA,EAAG,EAAE,CAAA;AAElD,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,CAAC,OAAA,KAA4B;AACvD,IAAA,WAAA,CAAY,OAAO,CAAA;AACnB,IAAA,OAAA,CAAQ,KAAK,CAAA;AACb,IAAA,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA;AAAA,EACpC,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,KAAA,KAA+C;AAC9C,MAAA,IAAI,KAAA,CAAM,QAAQ,QAAA,EAAU;AAC1B,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,KAAA,EAAM;AACN,QAAA;AAAA,MACF;AACA,MAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACvB,MAAA,IAAI,KAAA,CAAM,QAAQ,WAAA,EAAa;AAC7B,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,YAAA,CAAa,CAAC,CAAA,KAAA,CAAO,CAAA,GAAI,CAAA,IAAK,KAAK,MAAM,CAAA;AAAA,MAC3C,CAAA,MAAA,IAAW,KAAA,CAAM,GAAA,KAAQ,SAAA,EAAW;AAClC,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,YAAA,CAAa,CAAC,CAAA,KAAA,CAAO,CAAA,GAAI,IAAI,IAAA,CAAK,MAAA,IAAU,KAAK,MAAM,CAAA;AAAA,MACzD,CAAA,MAAA,IAAW,KAAA,CAAM,GAAA,KAAQ,OAAA,EAAS;AAChC,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,MAAM,MAAA,GAAS,KAAK,SAAS,CAAA;AAC7B,QAAA,IAAI,MAAA,UAAgB,MAAM,CAAA;AAAA,MAC5B;AAAA,IACF,CAAA;AAAA,IACA,CAAC,IAAA,EAAM,SAAA,EAAW,KAAA,EAAO,OAAO;AAAA,GAClC;AAGA,EAAA,MAAM,aAAA,GAAgB,WAAA;AAAA,IACpB,CAAC,KAAA,KAA+C;AAC9C,MAAA,IAAI,KAAA,CAAM,MAAA,KAAW,SAAA,CAAU,OAAA,EAAS,KAAA,EAAM;AAAA,IAChD,CAAA;AAAA,IACA,CAAC,KAAK;AAAA,GACR;AAEA,EAAA,MAAM,WAAW,CAAC,GAAA,KAAgB,CAAA,EAAG,SAAS,QAAQ,GAAG,CAAA,CAAA;AACzD,EAAA,MAAM,iBAAiB,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,QAAA,CAAS,SAAS,CAAA,GAAI,MAAA;AAE/D,EAAA;AAAA;AAAA,oBAEE,GAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,SAAA;AAAA,QACL,iBAAA,EAAiB,OAAA;AAAA,QACjB,kBAAA,EAAkB,MAAA;AAAA,QAClB,OAAA,EAAS,aAAA;AAAA,QACT,OAAA,EAAS,MAAM,OAAA,CAAQ,KAAK,CAAA;AAAA,QAC5B,QAAA,EAAU,CAAC,KAAA,KAAU;AACnB,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,KAAA,EAAM;AAAA,QACR,CAAA;AAAA,QACA,KAAA,EAAO,WAAA;AAAA,QACP,SAAA,EAAU,sBAAA;AAAA,QACV,mBAAA,EAAkB,EAAA;AAAA,QAGlB,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAsB,SAAA,EAAU,eAAA,EACnC,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,IAAA,EAAA,EAAG,EAAA,EAAI,OAAA,EAAS,SAAA,EAAU,WAAU,QAAA,EAAA,iBAAA,EAErC,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,QAAA;AAAA,cACL,IAAA,EAAK,MAAA;AAAA,cACL,IAAA,EAAK,UAAA;AAAA,cACL,eAAA,EAAe,IAAA;AAAA,cACf,eAAA,EAAe,MAAA;AAAA,cACf,mBAAA,EAAkB,MAAA;AAAA,cACjB,GAAI,cAAA,GAAiB,EAAE,uBAAA,EAAyB,cAAA,KAAmB,EAAC;AAAA,cACrE,WAAA,EAAY,sBAAA;AAAA,cACZ,KAAA,EAAO,KAAA;AAAA,cACP,UAAU,CAAC,CAAA,KAAM,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,cACxC,SAAA,EAAU;AAAA;AAAA,WACZ;AAAA,+BACC,KAAA,EAAA,EAAI,IAAA,EAAK,UAAS,WAAA,EAAU,QAAA,EAAS,WAAU,SAAA,EAC7C,QAAA,EAAA;AAAA,YAAA,IAAA,CAAK,MAAA;AAAA,YAAO;AAAA,WAAA,EACf,CAAA;AAAA,0BACA,IAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cACC,EAAA,EAAI,MAAA;AAAA,cAEJ,IAAA,EAAK,SAAA;AAAA,cACL,YAAA,EAAW,UAAA;AAAA,cACX,SAAA,EAAU,mCAAA;AAAA,cAET,QAAA,EAAA;AAAA,gBAAA,MAAA,CAAO,MAAA,KAAW,qBACjB,GAAA,CAAC,IAAA,EAAA,EAAG,MAAK,cAAA,EAAe,SAAA,EAAU,gCAA+B,QAAA,EAAA,mBAAA,EAEjE,CAAA;AAAA,gBAED,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,KAAU;AACrB,kBAAA,MAAM,QAAA,GAAW,KAAK,OAAA,CAAQ,KAAA,CAAM,SAAS,CAAC,CAAA,IAAM,EAAqB,CAAA;AACzE,kBAAA,uBACE,IAAA,CAAC,IAAA,EAAA,EAAwB,IAAA,EAAK,cAAA,EAC5B,QAAA,EAAA;AAAA,oCAAA,GAAA;AAAA,sBAAC,KAAA;AAAA,sBAAA;AAAA,wBACC,IAAA,EAAK,cAAA;AAAA,wBACL,SAAA,EAAU,yEAAA;AAAA,wBAET,QAAA,EAAA,KAAA,CAAM;AAAA;AAAA,qBACT;AAAA,oCACA,GAAA,CAAC,IAAA,EAAA,EAAG,IAAA,EAAK,cAAA,EAAe,SAAA,EAAU,mBAAA,EAC/B,QAAA,EAAA,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,KAAM;AAC9B,sBAAA,MAAM,MAAM,QAAA,GAAW,CAAA;AACvB,sBAAA,MAAM,WAAW,GAAA,KAAQ,SAAA;AACzB,sBAAA;AAAA;AAAA;AAAA,wCAGE,IAAA;AAAA,0BAAC,IAAA;AAAA,0BAAA;AAAA,4BAEC,EAAA,EAAI,SAAS,GAAG,CAAA;AAAA,4BAEhB,IAAA,EAAK,QAAA;AAAA,4BACL,eAAA,EAAe,QAAA;AAAA,4BACf,aAAA,EAAa,WAAW,EAAA,GAAK,MAAA;AAAA,4BAC7B,YAAA,EAAc,MAAM,YAAA,CAAa,GAAG,CAAA;AAAA,4BACpC,OAAA,EAAS,MAAM,OAAA,CAAQ,GAAG,CAAA;AAAA,4BAC1B,SAAA,EACE,2DAAA,IACC,QAAA,GAAW,4BAAA,GAA+B,EAAA,CAAA;AAAA,4BAG7C,QAAA,EAAA;AAAA,8CAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAU,QAAA,EAAA,GAAA,CAAI,KAAA,EAAM,CAAA;AAAA,8BACnC,GAAA,CAAI,8BACH,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,oBAAA,EAAsB,QAAA,EAAA,GAAA,CAAI,aAAY,CAAA,GACpD;AAAA;AAAA,2BAAA;AAAA,0BAhBC,GAAA,CAAI;AAAA;AAiBX;AAAA,oBAEJ,CAAC,CAAA,EACH;AAAA,mBAAA,EAAA,EAnCO,MAAM,QAoCf,CAAA;AAAA,gBAEJ,CAAC;AAAA;AAAA;AAAA,WACH;AAAA,0BACA,IAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,EAAA,EAAI,MAAA;AAAA,cACJ,SAAA,EAAU,8FAAA;AAAA,cAEV,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,UAAK,QAAA,EAAA,uBAAA,EAAW,CAAA;AAAA,gCACjB,GAAA,CAAC,UAAK,QAAA,EAAA,eAAA,EAAQ,CAAA;AAAA,gCACd,GAAA,CAAC,UAAK,QAAA,EAAA,WAAA,EAAS;AAAA;AAAA;AAAA;AACjB,SAAA,EACF;AAAA;AAAA;AACF;AAEJ","file":"palette.js","sourcesContent":["/**\n * `<CommandPalette>` — global Cmd+K admin command palette.\n *\n * Wraps the `/admin/commands` endpoint shipped by `arqel-dev/core` (CMDPAL-002)\n * with a native `<dialog>`-based modal. No external dialog/combobox library\n * is used: the surface area is small enough that the platform primitives\n * (HTML dialog + ARIA combobox/listbox roles) cover every requirement.\n *\n * Usage: render once at the top of `<AppShell>` — the global Cmd+K /\n * Ctrl+K listener is mounted on `window` while the component is alive.\n */\n\nimport { type CSSProperties, useCallback, useEffect, useId, useRef, useState } from 'react';\n\nexport interface PaletteCommand {\n id: string;\n label: string;\n url: string;\n description?: string | null;\n category?: string | null;\n icon?: string | null;\n}\n\nexport interface CommandPaletteProps {\n /** Endpoint that returns `{ commands: PaletteCommand[] }`. */\n endpoint?: string;\n}\n\ninterface RecentEntry {\n id: string;\n count: number;\n lastUsed: number;\n command: PaletteCommand;\n}\n\nconst RECENT_KEY = 'arqel:cmdpal:recent';\nconst RECENT_WINDOW_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\nconst RECENT_LIMIT = 5;\nconst DEBOUNCE_MS = 150;\n\nfunction readRecents(): RecentEntry[] {\n try {\n const raw = globalThis.localStorage?.getItem(RECENT_KEY);\n if (!raw) return [];\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n const now = Date.now();\n return parsed.filter(\n (e): e is RecentEntry =>\n typeof e === 'object' &&\n e !== null &&\n typeof (e as RecentEntry).id === 'string' &&\n typeof (e as RecentEntry).count === 'number' &&\n typeof (e as RecentEntry).lastUsed === 'number' &&\n now - (e as RecentEntry).lastUsed <= RECENT_WINDOW_MS &&\n typeof (e as RecentEntry).command === 'object',\n );\n } catch {\n return [];\n }\n}\n\nfunction writeRecents(entries: RecentEntry[]): void {\n try {\n globalThis.localStorage?.setItem(RECENT_KEY, JSON.stringify(entries));\n } catch {\n // Ignore quota/serialization failures — recents are best-effort.\n }\n}\n\nfunction recordUsage(command: PaletteCommand): void {\n const entries = readRecents();\n const now = Date.now();\n const idx = entries.findIndex((e) => e.id === command.id);\n if (idx >= 0) {\n const existing = entries[idx];\n if (existing) {\n entries[idx] = {\n ...existing,\n count: existing.count + 1,\n lastUsed: now,\n command,\n };\n }\n } else {\n entries.push({ id: command.id, count: 1, lastUsed: now, command });\n }\n // Prune anything older than the recency window.\n const fresh = entries.filter((e) => now - e.lastUsed <= RECENT_WINDOW_MS);\n writeRecents(fresh);\n}\n\nfunction topRecents(): PaletteCommand[] {\n return readRecents()\n .slice()\n .sort((a, b) => (b.count === a.count ? b.lastUsed - a.lastUsed : b.count - a.count))\n .slice(0, RECENT_LIMIT)\n .map((e) => e.command);\n}\n\ninterface Group {\n category: string;\n commands: PaletteCommand[];\n}\n\nfunction groupByCategory(commands: PaletteCommand[]): Group[] {\n const order: string[] = [];\n const buckets = new Map<string, PaletteCommand[]>();\n for (const cmd of commands) {\n const key = cmd.category ?? 'General';\n if (!buckets.has(key)) {\n buckets.set(key, []);\n order.push(key);\n }\n buckets.get(key)?.push(cmd);\n }\n return order.map((category) => ({ category, commands: buckets.get(category) ?? [] }));\n}\n\nfunction flattenGroups(groups: Group[]): PaletteCommand[] {\n return groups.flatMap((g) => g.commands);\n}\n\nconst dialogStyle: CSSProperties = {\n border: 'none',\n borderRadius: '0.75rem',\n padding: 0,\n width: 'min(640px, 92vw)',\n maxHeight: '70vh',\n background: 'var(--background, #fff)',\n color: 'var(--foreground, #111)',\n};\n\nexport function CommandPalette({ endpoint = '/admin/commands' }: CommandPaletteProps) {\n const dialogRef = useRef<HTMLDialogElement | null>(null);\n const inputRef = useRef<HTMLInputElement | null>(null);\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [results, setResults] = useState<PaletteCommand[]>([]);\n const [recents, setRecents] = useState<PaletteCommand[]>([]);\n const [highlight, setHighlight] = useState(0);\n\n const titleId = useId();\n const hintId = useId();\n const listId = useId();\n const optBaseId = useId();\n\n const visibleCommands = query.trim().length === 0 ? recents : results;\n const groups =\n query.trim().length === 0 && recents.length > 0\n ? [{ category: 'Recent', commands: recents }]\n : groupByCategory(visibleCommands);\n const flat = flattenGroups(groups);\n\n // Cmd+K / Ctrl+K global toggle.\n useEffect(() => {\n function onKey(event: KeyboardEvent) {\n const isToggle = (event.key === 'k' || event.key === 'K') && (event.metaKey || event.ctrlKey);\n if (!isToggle) return;\n event.preventDefault();\n setOpen((prev) => !prev);\n }\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, []);\n\n // Sync `open` state with the native <dialog>.\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n if (open && !dialog.open) {\n try {\n dialog.showModal();\n } catch {\n // jsdom or already-open: ignore.\n }\n setRecents(topRecents());\n setQuery('');\n setResults([]);\n setHighlight(0);\n // Defer focus after the dialog is on-screen.\n queueMicrotask(() => inputRef.current?.focus());\n } else if (!open && dialog.open) {\n dialog.close();\n }\n }, [open]);\n\n // Debounced fetch.\n useEffect(() => {\n if (!open) return;\n if (query.trim().length === 0) {\n setResults([]);\n return;\n }\n if (debounceTimer.current) clearTimeout(debounceTimer.current);\n debounceTimer.current = setTimeout(() => {\n const url = `${endpoint}?q=${encodeURIComponent(query)}`;\n fetch(url, { headers: { Accept: 'application/json' } })\n .then((res) => (res.ok ? res.json() : { commands: [] }))\n .then((data: { commands?: PaletteCommand[] }) => {\n setResults(Array.isArray(data?.commands) ? data.commands : []);\n setHighlight(0);\n })\n .catch(() => {\n setResults([]);\n });\n }, DEBOUNCE_MS);\n return () => {\n if (debounceTimer.current) clearTimeout(debounceTimer.current);\n };\n }, [query, open, endpoint]);\n\n const close = useCallback(() => setOpen(false), []);\n\n const execute = useCallback((command: PaletteCommand) => {\n recordUsage(command);\n setOpen(false);\n window.location.assign(command.url);\n }, []);\n\n const onKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Escape') {\n event.preventDefault();\n close();\n return;\n }\n if (flat.length === 0) return;\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n setHighlight((h) => (h + 1) % flat.length);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n setHighlight((h) => (h - 1 + flat.length) % flat.length);\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const target = flat[highlight];\n if (target) execute(target);\n }\n },\n [flat, highlight, close, execute],\n );\n\n // Close on backdrop click — dialog click hits the dialog itself, not children.\n const onDialogClick = useCallback(\n (event: React.MouseEvent<HTMLDialogElement>) => {\n if (event.target === dialogRef.current) close();\n },\n [close],\n );\n\n const optionId = (idx: number) => `${optBaseId}-opt-${idx}`;\n const activeOptionId = flat.length > 0 ? optionId(highlight) : undefined;\n\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: dialog backdrop click closes; Escape (onCancel) handles keyboard close\n <dialog\n ref={dialogRef}\n aria-labelledby={titleId}\n aria-describedby={hintId}\n onClick={onDialogClick}\n onClose={() => setOpen(false)}\n onCancel={(event) => {\n event.preventDefault();\n close();\n }}\n style={dialogStyle}\n className=\"backdrop:bg-black/40\"\n data-arqel-cmdpal=\"\"\n >\n {/* biome-ignore lint/a11y/noStaticElementInteractions: this wrapper only delegates ArrowUp/Down/Enter/Escape from the combobox input below */}\n <div onKeyDown={onKeyDown} className=\"flex flex-col\">\n <h2 id={titleId} className=\"sr-only\">\n Command palette\n </h2>\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={listId}\n aria-autocomplete=\"list\"\n {...(activeOptionId ? { 'aria-activedescendant': activeOptionId } : {})}\n placeholder=\"Type a command…\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n className=\"w-full border-b border-[var(--border,#e5e7eb)] bg-transparent px-4 py-3 text-base outline-none\"\n />\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {flat.length} commands\n </div>\n <ul\n id={listId}\n // biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: WAI-ARIA combobox/listbox pattern requires <ul role=\"listbox\">\n role=\"listbox\"\n aria-label=\"Commands\"\n className=\"max-h-[50vh] overflow-y-auto py-2\"\n >\n {groups.length === 0 && (\n <li role=\"presentation\" className=\"px-4 py-3 text-sm opacity-60\">\n No commands found\n </li>\n )}\n {groups.map((group) => {\n const startIdx = flat.indexOf(group.commands[0] ?? ({} as PaletteCommand));\n return (\n <li key={group.category} role=\"presentation\">\n <div\n role=\"presentation\"\n className=\"px-4 pt-2 pb-1 text-xs font-semibold uppercase tracking-wide opacity-60\"\n >\n {group.category}\n </div>\n <ul role=\"presentation\" className=\"m-0 list-none p-0\">\n {group.commands.map((cmd, j) => {\n const idx = startIdx + j;\n const isActive = idx === highlight;\n return (\n // biome-ignore lint/a11y/useFocusableInteractive: focus stays on combobox; selection via aria-activedescendant\n // biome-ignore lint/a11y/useKeyWithClickEvents: keyboard nav handled by combobox input ArrowUp/Down/Enter handler\n <li\n key={cmd.id}\n id={optionId(idx)}\n // biome-ignore lint/a11y/noNoninteractiveElementToInteractiveRole: WAI-ARIA combobox/listbox pattern requires <li role=\"option\">\n role=\"option\"\n aria-selected={isActive}\n data-active={isActive ? '' : undefined}\n onMouseEnter={() => setHighlight(idx)}\n onClick={() => execute(cmd)}\n className={\n 'flex cursor-pointer items-center gap-3 px-4 py-2 text-sm ' +\n (isActive ? 'bg-[var(--accent,#f1f5f9)]' : '')\n }\n >\n <span className=\"flex-1\">{cmd.label}</span>\n {cmd.description ? (\n <span className=\"text-xs opacity-60\">{cmd.description}</span>\n ) : null}\n </li>\n );\n })}\n </ul>\n </li>\n );\n })}\n </ul>\n <footer\n id={hintId}\n className=\"flex items-center gap-3 border-t border-[var(--border,#e5e7eb)] px-4 py-2 text-xs opacity-70\"\n >\n <span>↑↓ navigate</span>\n <span>↵ select</span>\n <span>esc close</span>\n </footer>\n </div>\n </dialog>\n );\n}\n"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"palette.js"}