@bysages/react 0.0.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.
package/dist/index.mjs CHANGED
@@ -1,32 +1,42 @@
1
1
  import { injectComponentStyle } from "@bysages/core";
2
2
  import { Accordion as Accordion$1 } from "@ark-ui/react/accordion";
3
- import { renderHtml } from "@tanstack/markdown/html";
4
- import { Children, cloneElement, createContext, isValidElement, useContext, useEffect, useLayoutEffect, useMemo, useReducer, useRef } from "react";
5
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { Children, cloneElement, createContext, isValidElement, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
5
+ import { Field as Field$1, useFieldContext } from "@ark-ui/react/field";
6
+ import { Popover as Popover$1 } from "@ark-ui/react/popover";
7
+ import { Portal } from "@ark-ui/react/portal";
6
8
  import { Collapsible as Collapsible$1 } from "@ark-ui/react/collapsible";
7
- import { Field as Field$1 } from "@ark-ui/react/field";
9
+ import { renderHtml } from "@tanstack/markdown/html";
8
10
  import { AngleSlider as AngleSlider$1 } from "@ark-ui/react/angle-slider";
11
+ import { createFileTreeCollection, createGridCollection, createTreeCollection, useAsyncList, useListCollection, useListCollection as useListCollection$1, useListSelection } from "@ark-ui/react/collection";
12
+ import { Combobox as Combobox$1 } from "@ark-ui/react/combobox";
9
13
  import { Avatar as Avatar$1 } from "@ark-ui/react/avatar";
10
14
  import { DatePicker as DatePicker$1 } from "@ark-ui/react/date-picker";
15
+ import { useEnvironmentContext } from "@ark-ui/react/environment";
16
+ import { useFilter, useLocaleContext } from "@ark-ui/react/locale";
17
+ import * as cascade from "@zag-js/cascade-select";
18
+ import { normalizeProps, useMachine } from "@zag-js/react";
11
19
  import { Carousel as Carousel$1 } from "@ark-ui/react/carousel";
12
20
  import { Checkbox as Checkbox$1 } from "@ark-ui/react/checkbox";
13
21
  import { Clipboard as Clipboard$1 } from "@ark-ui/react/clipboard";
22
+ import { ClientOnly } from "@ark-ui/react/client-only";
14
23
  import { ColorPicker as ColorPicker$1 } from "@ark-ui/react/color-picker";
15
- import { Combobox as Combobox$1 } from "@ark-ui/react/combobox";
16
- import { DateInput as DateInput$1 } from "@ark-ui/react/date-input";
17
24
  import { Dialog as Dialog$1 } from "@ark-ui/react/dialog";
25
+ import { DateInput as DateInput$1 } from "@ark-ui/react/date-input";
26
+ import { DownloadTrigger, useDownload } from "@ark-ui/react/download-trigger";
18
27
  import { Drawer as Drawer$1 } from "@ark-ui/react/drawer";
19
28
  import { Editable as Editable$1 } from "@ark-ui/react/editable";
20
29
  import { Fieldset as Fieldset$1 } from "@ark-ui/react/fieldset";
21
30
  import { FileUpload as FileUpload$1 } from "@ark-ui/react/file-upload";
22
31
  import { FloatingPanel as FloatingPanel$1 } from "@ark-ui/react/floating-panel";
32
+ import { FocusTrap } from "@ark-ui/react/focus-trap";
23
33
  import { Format as Format$1 } from "@ark-ui/react/format";
24
34
  import { Frame as Frame$1 } from "@ark-ui/react/frame";
25
35
  import { Highlight as Highlight$1 } from "@ark-ui/react/highlight";
26
36
  import { HoverCard as HoverCard$1 } from "@ark-ui/react/hover-card";
27
37
  import { ImageCropper as ImageCropper$1 } from "@ark-ui/react/image-cropper";
28
38
  import { JsonTreeView as JsonTreeView$1 } from "@ark-ui/react/json-tree-view";
29
- import { Listbox as Listbox$1, createListCollection } from "@ark-ui/react/listbox";
39
+ import { Listbox as Listbox$1 } from "@ark-ui/react/listbox";
30
40
  import { Marquee as Marquee$1 } from "@ark-ui/react/marquee";
31
41
  import { Menu as Menu$1 } from "@ark-ui/react/menu";
32
42
  import { NavigationMenu as NavigationMenu$1 } from "@ark-ui/react/navigation-menu";
@@ -34,14 +44,14 @@ import { NumberInput as NumberInput$1 } from "@ark-ui/react/number-input";
34
44
  import { Pagination as Pagination$1 } from "@ark-ui/react/pagination";
35
45
  import { PasswordInput as PasswordInput$1 } from "@ark-ui/react/password-input";
36
46
  import { PinInput as PinInput$1 } from "@ark-ui/react/pin-input";
37
- import { Popover as Popover$1 } from "@ark-ui/react/popover";
47
+ import { Presence, PresenceProvider, usePresence, usePresenceContext } from "@ark-ui/react/presence";
38
48
  import { Progress as Progress$1 } from "@ark-ui/react/progress";
39
49
  import { QrCode as QrCode$1 } from "@ark-ui/react/qr-code";
40
50
  import { RadioGroup as RadioGroup$1 } from "@ark-ui/react/radio-group";
41
51
  import { RatingGroup as RatingGroup$1 } from "@ark-ui/react/rating-group";
42
52
  import { ScrollArea as ScrollArea$1 } from "@ark-ui/react/scroll-area";
43
53
  import { SegmentGroup as SegmentGroup$1 } from "@ark-ui/react/segment-group";
44
- import { Select as Select$1 } from "@ark-ui/react/select";
54
+ import { Select as Select$1, createListCollection } from "@ark-ui/react/select";
45
55
  import { SignaturePad as SignaturePad$1 } from "@ark-ui/react/signature-pad";
46
56
  import { Slider as Slider$1 } from "@ark-ui/react/slider";
47
57
  import { Splitter as Splitter$1 } from "@ark-ui/react/splitter";
@@ -59,7 +69,7 @@ import { Toggle as Toggle$1 } from "@ark-ui/react/toggle";
59
69
  import { ToggleGroup as ToggleGroup$1 } from "@ark-ui/react/toggle-group";
60
70
  import { Tooltip as Tooltip$1 } from "@ark-ui/react/tooltip";
61
71
  import { Tour as Tour$1, useTour } from "@ark-ui/react/tour";
62
- import { TreeView as TreeView$1 } from "@ark-ui/react/tree-view";
72
+ import { TreeView as TreeView$1, createTreeCollection as createTreeCollection$1 } from "@ark-ui/react/tree-view";
63
73
  export * from "@bysages/core";
64
74
  //#region src/components/accordion/index.tsx
65
75
  /** Ark's Accordion, dressed in the paper-and-ink system: a ruled sheet
@@ -68,6 +78,21 @@ export * from "@bysages/core";
68
78
  const Accordion = Accordion$1;
69
79
  injectComponentStyle("accordion");
70
80
  //#endregion
81
+ //#region src/components/affix/index.tsx
82
+ function Affix({ offsetTop = "0px", offsetBottom = "0px", ...rest }) {
83
+ return /* @__PURE__ */ jsx("div", {
84
+ ...rest,
85
+ style: {
86
+ ...rest.style,
87
+ top: offsetTop,
88
+ bottom: offsetBottom
89
+ },
90
+ "data-scope": "affix",
91
+ "data-part": "root"
92
+ });
93
+ }
94
+ injectComponentStyle("affix");
95
+ //#endregion
71
96
  //#region src/components/button/index.tsx
72
97
  function Button({ variant = "solid", tone = "ink", size = "md", square = false, asChild = false, disabled = false, type = "button", children, ...rest }) {
73
98
  const partProps = {
@@ -93,12 +118,31 @@ function Button({ variant = "solid", tone = "ink", size = "md", square = false,
93
118
  }
94
119
  injectComponentStyle("button");
95
120
  //#endregion
96
- //#region src/components/collapsible/index.tsx
97
- /** Ark's Collapsible, dressed in the paper-and-ink system: one control on
98
- * the paper, its panel dissolving open to the machine's measured height.
99
- * The API is Ark's own — Root, Trigger, Content, Indicator. */
100
- const Collapsible = Collapsible$1;
101
- injectComponentStyle("collapsible");
121
+ //#region src/components/ai-action/index.tsx
122
+ function Action({ label, children, ...rest }) {
123
+ return /* @__PURE__ */ jsx(Button, {
124
+ variant: "ghost",
125
+ size: "sm",
126
+ square: true,
127
+ "aria-label": label,
128
+ title: label,
129
+ ...rest,
130
+ children
131
+ });
132
+ }
133
+ injectComponentStyle("ai");
134
+ //#endregion
135
+ //#region src/components/ai-message/index.tsx
136
+ function Message({ role = "assistant", children, ...rest }) {
137
+ return /* @__PURE__ */ jsx("article", {
138
+ ...rest,
139
+ "data-scope": "ai",
140
+ "data-part": "message",
141
+ "data-role": role,
142
+ children
143
+ });
144
+ }
145
+ injectComponentStyle("ai");
102
146
  //#endregion
103
147
  //#region src/components/field/index.tsx
104
148
  /** Ark's Field, dressed in the paper-and-ink system: a tracked label, a
@@ -108,38 +152,308 @@ injectComponentStyle("collapsible");
108
152
  const Field = Field$1;
109
153
  injectComponentStyle("field");
110
154
  //#endregion
111
- //#region src/components/ai/index.tsx
112
- /** A conversation column: Root is the log, Message carries a role, and
113
- * the speaking parts — Response, Reasoning, Tool, Sources — part the
114
- * stream. The interactive folds are the shared Collapsible wearing a
115
- * `data-ai` marker, so the machine work is never ours. Parts stay
116
- * agnostic of any client; consumers map their message format (e.g. the
117
- * `UIMessage` parts re-exported below) onto these primitives. */
118
- function part$9(name, tag, extra = {}) {
119
- const Tag = tag;
120
- const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
121
- ...extra,
155
+ //#region src/components/mentions/use-mentions.ts
156
+ /** The @-detection machinery behind a mention field: watch the caret,
157
+ * offer the matching candidates, and answer for the keys that drive
158
+ * them. The field element arrives as a getter because hosts wrap it
159
+ * differently (a bare textarea, a field part's ref).
160
+ *
161
+ * `onKeydown` answers whether the vessel consumed the key, so a host
162
+ * with its own Enter semantics — a composer that sends — can yield to
163
+ * the candidates first and act only when the vessel stands down. */
164
+ function useMentions(options, textarea, handlers) {
165
+ const [popupOpen, setPopupOpen] = useState(false);
166
+ const [matches, setMatches] = useState([]);
167
+ const [active, setActive] = useState(0);
168
+ const anchorIndex = useRef(0);
169
+ const caretIndex = useRef(0);
170
+ const resolve = () => typeof options === "function" ? options() : options;
171
+ const closePopup = () => {
172
+ setPopupOpen(false);
173
+ setMatches([]);
174
+ };
175
+ const detect = () => {
176
+ const text = handlers.getText();
177
+ const caret = textarea()?.selectionStart ?? text.length;
178
+ caretIndex.current = caret;
179
+ const head = text.slice(0, caret);
180
+ const char = resolve().trigger ?? "@";
181
+ const anchor = head.lastIndexOf(char);
182
+ const token = anchor === -1 ? "" : head.slice(anchor + char.length);
183
+ if (anchor === -1 || /\s/.test(token)) {
184
+ closePopup();
185
+ return;
186
+ }
187
+ const found = resolve().items.filter((entry) => entry.label.toLowerCase().startsWith(token.toLowerCase()));
188
+ setMatches(found);
189
+ setActive(0);
190
+ anchorIndex.current = anchor;
191
+ setPopupOpen(found.length > 0);
192
+ };
193
+ const insert = (entry) => {
194
+ const text = handlers.getText();
195
+ const char = resolve().trigger ?? "@";
196
+ const next = text.slice(0, anchorIndex.current) + char + entry.label + " " + text.slice(caretIndex.current);
197
+ handlers.setText(next);
198
+ closePopup();
199
+ setTimeout(() => {
200
+ const el = textarea();
201
+ if (!el) return;
202
+ const caret = anchorIndex.current + char.length + entry.label.length + 1;
203
+ el.focus();
204
+ el.setSelectionRange(caret, caret);
205
+ }, 0);
206
+ };
207
+ /** Feed the field's input events through here — the vessel watches
208
+ * the text as it lands, from the element itself. */
209
+ const onInput = () => detect();
210
+ /** Arrows move, Enter inserts, Escape dismisses. Answers `true` when
211
+ * the vessel consumed the key. */
212
+ const onKeydown = (event) => {
213
+ if (!popupOpen || matches.length === 0) return false;
214
+ if (event.key === "ArrowDown") {
215
+ event.preventDefault();
216
+ setActive((current) => (current + 1) % matches.length);
217
+ } else if (event.key === "ArrowUp") {
218
+ event.preventDefault();
219
+ setActive((current) => (current - 1 + matches.length) % matches.length);
220
+ } else if (event.key === "Enter") {
221
+ event.preventDefault();
222
+ insert(matches[active]);
223
+ } else if (event.key === "Escape") closePopup();
224
+ else return false;
225
+ return true;
226
+ };
227
+ return {
228
+ open: popupOpen,
229
+ matches,
230
+ active,
231
+ setActive,
232
+ insert,
233
+ close: closePopup,
234
+ onInput,
235
+ onKeydown
236
+ };
237
+ }
238
+ //#endregion
239
+ //#region src/components/mentions/index.tsx
240
+ /** The vessel: the candidates themselves as a floating card. The anchor
241
+ * is virtual — a live rectangle off the host's field — so a host keeps
242
+ * its own anatomy (the textarea rides where the host puts it) and the
243
+ * vessel still points at the right place. Shares the detection state
244
+ * with the host through `useMentions`. */
245
+ function MentionsVessel({ open = false, matches = [], active = 0, anchor = null, onInsert, onActiveChange, onOpenChange }) {
246
+ return /* @__PURE__ */ jsx(Popover$1.Root, {
247
+ open,
248
+ onOpenChange: (details) => onOpenChange?.(details.open),
249
+ positioning: {
250
+ placement: "bottom-start",
251
+ getAnchorRect: () => anchor?.getBoundingClientRect() ?? null
252
+ },
253
+ children: /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsx(Popover$1.Content, {
254
+ asChild: true,
255
+ children: /* @__PURE__ */ jsx("div", {
256
+ "data-scope": "mentions",
257
+ "data-part": "popup",
258
+ children: matches.map((entry, index) => /* @__PURE__ */ jsx("div", {
259
+ "data-scope": "mentions",
260
+ "data-part": "option",
261
+ "data-active": index === active ? "" : void 0,
262
+ onMouseEnter: () => onActiveChange?.(index),
263
+ onMouseDown: (event) => event.preventDefault(),
264
+ onClick: () => onInsert?.(entry),
265
+ children: entry.label
266
+ }, entry.value))
267
+ })
268
+ }) }) })
269
+ });
270
+ }
271
+ function Mentions({ items = [], value, trigger = "@", placeholder, autoresize = false, invalid = false, onValueChange, children, ...rest }) {
272
+ const [internal, setInternal] = useState("");
273
+ const fieldRef = useRef(null);
274
+ const el = () => fieldRef.current;
275
+ const current = value ?? internal;
276
+ const mentions = useMentions({
277
+ items,
278
+ trigger
279
+ }, el, {
280
+ getText: () => el()?.value ?? current,
281
+ setText: (next) => {
282
+ setInternal(next);
283
+ onValueChange?.(next);
284
+ }
285
+ });
286
+ const handleChange = (event) => {
287
+ const node = event.currentTarget;
288
+ setInternal(node.value);
289
+ onValueChange?.(node.value);
290
+ mentions.onInput();
291
+ };
292
+ return /* @__PURE__ */ jsxs("div", {
122
293
  ...rest,
123
- "data-scope": "ai",
124
- "data-part": name.toLowerCase(),
125
- children
294
+ "data-scope": "mentions",
295
+ "data-part": "root",
296
+ children: [
297
+ /* @__PURE__ */ jsx(Field.Textarea, {
298
+ ref: fieldRef,
299
+ autoresize,
300
+ rows: 3,
301
+ placeholder,
302
+ value: current,
303
+ onChange: handleChange,
304
+ onKeyDown: (event) => mentions.onKeydown(event),
305
+ "data-invalid": invalid || void 0,
306
+ "data-scope": "mentions",
307
+ "data-part": "textarea"
308
+ }),
309
+ /* @__PURE__ */ jsx(MentionsVessel, {
310
+ open: mentions.open,
311
+ matches: mentions.matches,
312
+ active: mentions.active,
313
+ anchor: el(),
314
+ onInsert: mentions.insert,
315
+ onActiveChange: mentions.setActive,
316
+ onOpenChange: (open) => {
317
+ if (!open) mentions.close();
318
+ }
319
+ }),
320
+ children
321
+ ]
126
322
  });
127
- Component.displayName = "Ai" + name;
128
- return Component;
129
323
  }
130
- const Conversation = part$9("Conversation", "div", {
131
- role: "log",
132
- "aria-label": "Conversation"
324
+ injectComponentStyle("mentions");
325
+ //#endregion
326
+ //#region src/components/ai-prompt-input/index.tsx
327
+ const arrowUpGlyph = /* @__PURE__ */ jsx("svg", {
328
+ viewBox: "0 0 16 16",
329
+ width: 14,
330
+ height: 14,
331
+ "aria-hidden": "true",
332
+ fill: "none",
333
+ stroke: "currentColor",
334
+ strokeWidth: 1.5,
335
+ strokeLinecap: "square",
336
+ children: /* @__PURE__ */ jsx("path", { d: "M8 13V3M3.5 7.5 8 3l4.5 4.5" })
133
337
  });
134
- const MessageContent = part$9("Content", "div");
135
- const Sources = part$9("Sources", "ol");
136
- const Actions$3 = part$9("Actions", "div");
137
- const Loader = part$9("Loader", "span", {
138
- role: "status",
139
- "aria-label": "Loading"
338
+ const stopGlyph = /* @__PURE__ */ jsx("svg", {
339
+ viewBox: "0 0 16 16",
340
+ width: 14,
341
+ height: 14,
342
+ "aria-hidden": "true",
343
+ children: /* @__PURE__ */ jsx("rect", {
344
+ x: 4.5,
345
+ y: 4.5,
346
+ width: 7,
347
+ height: 7,
348
+ fill: "currentColor"
349
+ })
140
350
  });
351
+ /** An empty slot leaves no part in the anatomy — the bare vessel keeps
352
+ * its single-line posture. */
353
+ function row(part, content) {
354
+ if (content == null || Array.isArray(content) && content.length === 0) return null;
355
+ return /* @__PURE__ */ jsx("div", {
356
+ "data-scope": "ai",
357
+ "data-part": part,
358
+ children: content
359
+ });
360
+ }
361
+ function PromptInput({ value, onValueChange, onSubmit, onStop, placeholder = "Send a message", disabled = false, busy = false, mentions, header, leading, trailing, footer, footerEnd, ...rest }) {
362
+ const fieldRef = useRef(null);
363
+ const mentionState = useMentions(() => ({
364
+ items: mentions?.items ?? [],
365
+ trigger: mentions?.trigger
366
+ }), () => fieldRef.current, {
367
+ getText: () => fieldRef.current?.value ?? value,
368
+ setText: (next) => onValueChange?.(next)
369
+ });
370
+ const mentionsActive = mentions != null;
371
+ const submit = () => {
372
+ const text = value.trim();
373
+ if (!text || disabled || busy) return;
374
+ onSubmit?.(text);
375
+ onValueChange?.("");
376
+ };
377
+ const seal = busy ? /* @__PURE__ */ jsx(Button, {
378
+ variant: "solid",
379
+ size: "sm",
380
+ square: true,
381
+ type: "button",
382
+ "aria-label": "Stop",
383
+ disabled,
384
+ onClick: () => onStop?.(),
385
+ children: stopGlyph
386
+ }) : /* @__PURE__ */ jsx(Button, {
387
+ variant: "solid",
388
+ size: "sm",
389
+ square: true,
390
+ type: "submit",
391
+ "aria-label": "Send",
392
+ disabled: disabled || !value.trim(),
393
+ children: arrowUpGlyph
394
+ });
395
+ const hasFooter = footer != null && (!Array.isArray(footer) || footer.length > 0);
396
+ return /* @__PURE__ */ jsxs("form", {
397
+ ...rest,
398
+ "data-scope": "ai",
399
+ "data-part": "prompt",
400
+ onSubmit: (event) => {
401
+ event.preventDefault();
402
+ submit();
403
+ },
404
+ children: [
405
+ row("prompt-header", header),
406
+ /* @__PURE__ */ jsxs("div", {
407
+ "data-scope": "ai",
408
+ "data-part": "prompt-main",
409
+ children: [
410
+ row("prompt-leading", leading),
411
+ /* @__PURE__ */ jsx(Field.Root, { children: /* @__PURE__ */ jsx(Field.Textarea, {
412
+ ref: fieldRef,
413
+ autoresize: true,
414
+ rows: 1,
415
+ value,
416
+ placeholder,
417
+ disabled,
418
+ onChange: (event) => {
419
+ onValueChange?.(event.target.value);
420
+ mentionState.onInput();
421
+ },
422
+ onKeyDown: (event) => {
423
+ if (mentionState.onKeydown(event)) return;
424
+ if (event.key === "Enter" && !event.shiftKey) {
425
+ event.preventDefault();
426
+ submit();
427
+ }
428
+ }
429
+ }) }),
430
+ mentionsActive ? /* @__PURE__ */ jsx(MentionsVessel, {
431
+ open: mentionState.open,
432
+ matches: mentionState.matches,
433
+ active: mentionState.active,
434
+ anchor: fieldRef.current,
435
+ onInsert: mentionState.insert,
436
+ onActiveChange: mentionState.setActive,
437
+ onOpenChange: (open) => {
438
+ if (!open) mentionState.close();
439
+ }
440
+ }) : null,
441
+ row("prompt-trailing", hasFooter ? trailing : trailing ?? seal)
442
+ ]
443
+ }),
444
+ hasFooter ? /* @__PURE__ */ jsxs("div", {
445
+ "data-scope": "ai",
446
+ "data-part": "prompt-footer",
447
+ children: [footer, row("prompt-end", footerEnd == null ? seal : /* @__PURE__ */ jsxs(Fragment, { children: [footerEnd, seal] }))]
448
+ }) : null
449
+ ]
450
+ });
451
+ }
452
+ injectComponentStyle("ai");
453
+ //#endregion
454
+ //#region src/components/ai/chevron.tsx
141
455
  /** The folding chevron the shared indicator turns. */
142
- const chevron$1 = /* @__PURE__ */ jsx("svg", {
456
+ const chevron$2 = /* @__PURE__ */ jsx("svg", {
143
457
  viewBox: "0 0 16 16",
144
458
  fill: "none",
145
459
  "aria-hidden": "true",
@@ -151,62 +465,111 @@ const chevron$1 = /* @__PURE__ */ jsx("svg", {
151
465
  strokeLinejoin: "round"
152
466
  })
153
467
  });
154
- function Message({ role = "assistant", children, ...rest }) {
155
- return /* @__PURE__ */ jsx("article", {
156
- ...rest,
157
- "data-scope": "ai",
158
- "data-part": "message",
159
- "data-role": role,
160
- children
161
- });
162
- }
163
- function Response({ content, ...rest }) {
164
- return /* @__PURE__ */ jsx("div", {
165
- ...rest,
166
- "data-scope": "ai",
167
- "data-part": "response",
168
- dangerouslySetInnerHTML: { __html: renderHtml(content) }
169
- });
170
- }
468
+ //#endregion
469
+ //#region src/components/collapsible/index.tsx
470
+ /** Ark's Collapsible, dressed in the paper-and-ink system: one control on
471
+ * the paper, its panel dissolving open to the machine's measured height.
472
+ * The API is Ark's own — Root, Trigger, Content, Indicator. */
473
+ const Collapsible = Collapsible$1;
474
+ injectComponentStyle("collapsible");
475
+ //#endregion
476
+ //#region src/components/ai-reasoning/index.tsx
171
477
  function Reasoning({ label = "Thinking", children, ...rest }) {
172
478
  return /* @__PURE__ */ jsxs(Collapsible.Root, {
173
479
  ...rest,
174
480
  "data-ai": "reasoning",
175
- children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$1 })] }), /* @__PURE__ */ jsx(Collapsible.Content, { children: /* @__PURE__ */ jsx("div", {
481
+ children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$2 })] }), /* @__PURE__ */ jsx(Collapsible.Content, { children: /* @__PURE__ */ jsx("div", {
176
482
  "data-scope": "ai",
177
483
  "data-part": "reasoning-content",
178
484
  children
179
485
  }) })]
180
486
  });
181
487
  }
182
- function Tool({ name, label, status, input, output, ...rest }) {
183
- return /* @__PURE__ */ jsxs(Collapsible.Root, {
488
+ injectComponentStyle("ai");
489
+ //#endregion
490
+ //#region src/components/ai-response/code-copy.ts
491
+ /** The two glyphs a copy affordance needs: the stamp and its
492
+ * confirmation. Inline SVG keeps the enhancement framework-agnostic —
493
+ * it lives beside the renderer's generated code HTML. */
494
+ const COPY_CODE_ICON = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`;
495
+ const COPIED_CODE_ICON = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
496
+ /** How long the confirmation stays before the stamp returns. */
497
+ const REVERT_MS = 1500;
498
+ /** Make sure every fenced block under `host` carries its copy stamp.
499
+ * The stamp anchors to a wrapper outside the scrolling `pre` — inside,
500
+ * it would fix to the overflow area and travel out of view with the
501
+ * code. The renderer rebuilds the markdown as one HTML string on every
502
+ * stream tick, so this stays idempotent and cheap enough to re-run
503
+ * after each patch. */
504
+ function decorateCodeCopy(host, copyLabel) {
505
+ for (const pre of host.querySelectorAll("pre")) {
506
+ let body = pre.parentElement;
507
+ if (!body || body.getAttribute("data-part") !== "code-body") {
508
+ body = document.createElement("div");
509
+ body.setAttribute("data-scope", "ai");
510
+ body.setAttribute("data-part", "code-body");
511
+ pre.replaceWith(body);
512
+ body.appendChild(pre);
513
+ }
514
+ let button = body.querySelector("[data-part=\"code-copy\"]");
515
+ if (!button) {
516
+ button = document.createElement("button");
517
+ button.type = "button";
518
+ button.setAttribute("data-scope", "ai");
519
+ button.setAttribute("data-part", "code-copy");
520
+ button.innerHTML = COPY_CODE_ICON;
521
+ body.appendChild(button);
522
+ }
523
+ button.title = copyLabel;
524
+ button.setAttribute("aria-label", copyLabel);
525
+ }
526
+ }
527
+ /** The one delegated click for the whole response: copy the block's
528
+ * text and confirm in place, then return the stamp after a beat. */
529
+ async function clickCodeCopy(event, copyLabel, copiedLabel) {
530
+ const button = event.target?.closest("[data-part=\"code-copy\"]");
531
+ const pre = button?.closest("[data-part=\"code-body\"]")?.querySelector("pre");
532
+ if (!button || !pre) return;
533
+ await navigator.clipboard.writeText(pre.textContent ?? "");
534
+ button.innerHTML = COPIED_CODE_ICON;
535
+ button.setAttribute("data-state", "copied");
536
+ button.title = copiedLabel;
537
+ button.setAttribute("aria-label", copiedLabel);
538
+ setTimeout(() => {
539
+ if (!button.isConnected) return;
540
+ button.innerHTML = COPY_CODE_ICON;
541
+ button.removeAttribute("data-state");
542
+ button.title = copyLabel;
543
+ button.setAttribute("aria-label", copyLabel);
544
+ }, REVERT_MS);
545
+ }
546
+ //#endregion
547
+ //#region src/components/ai-response/index.tsx
548
+ function Response({ content, highlighter, copyLabel = "Copy code", copiedLabel = "Copied", ...rest }) {
549
+ const root = useRef(null);
550
+ useEffect(() => {
551
+ const host = root.current;
552
+ if (host) decorateCodeCopy(host, copyLabel);
553
+ }, [
554
+ content,
555
+ highlighter,
556
+ copyLabel
557
+ ]);
558
+ const onClick = (event) => {
559
+ clickCodeCopy(event.nativeEvent, copyLabel, copiedLabel);
560
+ };
561
+ return /* @__PURE__ */ jsx("div", {
184
562
  ...rest,
185
- "data-ai": "tool",
186
- "data-status": status,
187
- children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, { children: [
188
- /* @__PURE__ */ jsx("span", { children: label ?? name }),
189
- status ? /* @__PURE__ */ jsx("span", {
190
- "data-scope": "ai",
191
- "data-part": "tool-status",
192
- children: status.charAt(0).toUpperCase() + status.slice(1)
193
- }) : null,
194
- /* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$1 })
195
- ] }), /* @__PURE__ */ jsx(Collapsible.Content, { children: /* @__PURE__ */ jsxs("div", {
196
- "data-scope": "ai",
197
- "data-part": "tool-body",
198
- children: [input ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
199
- "data-scope": "ai",
200
- "data-part": "tool-label",
201
- children: "Input"
202
- }), /* @__PURE__ */ jsx("pre", { children: input })] }) : null, output ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
203
- "data-scope": "ai",
204
- "data-part": "tool-label",
205
- children: "Output"
206
- }), /* @__PURE__ */ jsx("pre", { children: output })] }) : null]
207
- }) })]
563
+ ref: root,
564
+ "data-scope": "ai",
565
+ "data-part": "response",
566
+ onClick,
567
+ dangerouslySetInnerHTML: { __html: renderHtml(content, highlighter ? { highlighter } : void 0) }
208
568
  });
209
569
  }
570
+ injectComponentStyle("ai");
571
+ //#endregion
572
+ //#region src/components/ai-source/index.tsx
210
573
  function Source({ href, children, ...rest }) {
211
574
  return /* @__PURE__ */ jsx("li", {
212
575
  "data-scope": "ai",
@@ -220,17 +583,17 @@ function Source({ href, children, ...rest }) {
220
583
  })
221
584
  });
222
585
  }
223
- function Action({ label, children, ...rest }) {
224
- return /* @__PURE__ */ jsx(Button, {
225
- variant: "ghost",
226
- size: "sm",
227
- square: true,
228
- "aria-label": label,
229
- title: label,
586
+ function Sources({ children, ...rest }) {
587
+ return /* @__PURE__ */ jsx("ol", {
230
588
  ...rest,
589
+ "data-scope": "ai",
590
+ "data-part": "sources",
231
591
  children
232
592
  });
233
593
  }
594
+ injectComponentStyle("ai");
595
+ //#endregion
596
+ //#region src/components/ai-suggestion/index.tsx
234
597
  function Suggestion({ prompt, onSelect, ...rest }) {
235
598
  return /* @__PURE__ */ jsx(Button, {
236
599
  variant: "outline",
@@ -240,76 +603,196 @@ function Suggestion({ prompt, onSelect, ...rest }) {
240
603
  children: prompt
241
604
  });
242
605
  }
243
- function PromptInput({ value, onValueChange, onSubmit, placeholder = "Send a message", disabled = false, ...rest }) {
244
- const send = () => {
245
- const text = value.trim();
246
- if (!text || disabled) return;
247
- onSubmit?.(text);
248
- onValueChange?.("");
249
- };
250
- return /* @__PURE__ */ jsxs("form", {
606
+ injectComponentStyle("ai");
607
+ //#endregion
608
+ //#region src/components/ai-tool/index.tsx
609
+ function Tool({ name, label, status, input, output, ...rest }) {
610
+ return /* @__PURE__ */ jsxs(Collapsible.Root, {
251
611
  ...rest,
252
- "data-scope": "ai",
253
- "data-part": "prompt",
254
- onSubmit: (event) => {
255
- event.preventDefault();
256
- send();
257
- },
258
- children: [/* @__PURE__ */ jsx(Field.Root, { children: /* @__PURE__ */ jsx(Field.Textarea, {
259
- autoresize: true,
260
- rows: 1,
261
- value,
262
- placeholder,
263
- disabled,
264
- onChange: (event) => onValueChange?.(event.target.value),
265
- onKeyDown: (event) => {
266
- if (event.key === "Enter" && !event.shiftKey) {
267
- event.preventDefault();
268
- send();
269
- }
270
- }
271
- }) }), /* @__PURE__ */ jsx("div", {
612
+ "data-ai": "tool",
613
+ "data-status": status,
614
+ children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, { children: [
615
+ /* @__PURE__ */ jsx("span", { children: label ?? name }),
616
+ status ? /* @__PURE__ */ jsx("span", {
617
+ "data-scope": "ai",
618
+ "data-part": "tool-status",
619
+ children: status.charAt(0).toUpperCase() + status.slice(1)
620
+ }) : null,
621
+ /* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$2 })
622
+ ] }), /* @__PURE__ */ jsx(Collapsible.Content, { children: /* @__PURE__ */ jsxs("div", {
272
623
  "data-scope": "ai",
273
- "data-part": "prompt-footer",
274
- children: /* @__PURE__ */ jsx(Button, {
275
- variant: "solid",
276
- size: "sm",
277
- square: true,
278
- type: "submit",
279
- "aria-label": "Send",
280
- disabled: disabled || !value.trim(),
281
- children: /* @__PURE__ */ jsx("svg", {
282
- viewBox: "0 0 16 16",
283
- width: 14,
284
- height: 14,
285
- "aria-hidden": "true",
286
- fill: "none",
287
- stroke: "currentColor",
288
- strokeWidth: 1.5,
289
- strokeLinecap: "square",
290
- children: /* @__PURE__ */ jsx("path", { d: "M8 13V3M3.5 7.5 8 3l4.5 4.5" })
291
- })
292
- })
293
- })]
624
+ "data-part": "tool-body",
625
+ children: [input ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
626
+ "data-scope": "ai",
627
+ "data-part": "tool-label",
628
+ children: "Input"
629
+ }), /* @__PURE__ */ jsx("pre", { children: input })] }) : null, output ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
630
+ "data-scope": "ai",
631
+ "data-part": "tool-label",
632
+ children: "Output"
633
+ }), /* @__PURE__ */ jsx("pre", { children: output })] }) : null]
634
+ }) })]
294
635
  });
295
636
  }
637
+ injectComponentStyle("ai");
638
+ //#endregion
639
+ //#region src/components/ai/index.tsx
640
+ /** A conversation column: Root is the log, Message carries a role, and
641
+ * the speaking parts — Response, Reasoning, Tool, Sources — part the
642
+ * stream. The interactive folds are the shared Collapsible wearing a
643
+ * `data-ai` marker, so the machine work is never ours. Parts stay
644
+ * agnostic of any client; consumers map their message format (e.g. the
645
+ * `UIMessage` parts re-exported here) onto these primitives. */
646
+ function part$12(name, tag, extra = {}) {
647
+ const Tag = tag;
648
+ const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
649
+ ...extra,
650
+ ...rest,
651
+ "data-scope": "ai",
652
+ "data-part": name.toLowerCase(),
653
+ children
654
+ });
655
+ Component.displayName = "Ai" + name;
656
+ return Component;
657
+ }
658
+ /** The log itself: the column every stroke lands in, a landmark to
659
+ * screen readers. */
660
+ const Conversation = part$12("Conversation", "div", {
661
+ role: "log",
662
+ "aria-label": "Conversation"
663
+ });
664
+ /** The bubble's inner measure — content that belongs to neither side
665
+ * specifically. */
666
+ const MessageContent = part$12("Content", "div");
667
+ /** The quiet row under a message — copy, retry, feedback. */
668
+ const Actions$4 = part$12("Actions", "div");
669
+ /** The while-it-works whisper for the in-flight turns. */
670
+ const Loader = part$12("Loader", "span", {
671
+ role: "status",
672
+ "aria-label": "Loading"
673
+ });
674
+ /** The whole family under one handle — `Ai.Conversation`,
675
+ * `Ai.Message`, and the rest, exactly as before the split. */
296
676
  const Ai = Object.assign(Conversation, {
297
677
  Conversation,
298
- Message,
299
678
  MessageContent,
679
+ Actions: Actions$4,
680
+ Loader,
681
+ Message,
300
682
  Response,
301
683
  Reasoning,
302
684
  Tool,
303
685
  Sources,
304
686
  Source,
305
- Actions: Actions$3,
306
687
  Action,
307
688
  Suggestion,
308
- PromptInput,
309
- Loader
689
+ PromptInput
310
690
  });
311
691
  injectComponentStyle("ai");
312
692
  //#endregion
693
+ //#region src/components/ai-attachments/index.tsx
694
+ const imageGlyph = /* @__PURE__ */ jsxs("svg", {
695
+ viewBox: "0 0 16 16",
696
+ "aria-hidden": "true",
697
+ fill: "none",
698
+ stroke: "currentColor",
699
+ strokeWidth: 1.5,
700
+ strokeLinecap: "square",
701
+ children: [
702
+ /* @__PURE__ */ jsx("rect", {
703
+ x: 2.5,
704
+ y: 3.5,
705
+ width: 11,
706
+ height: 9
707
+ }),
708
+ /* @__PURE__ */ jsx("path", { d: "M2.5 10.5 6 7l3 3 2-1.5 2.5 2" }),
709
+ /* @__PURE__ */ jsx("circle", {
710
+ cx: 6,
711
+ cy: 6,
712
+ r: .75,
713
+ fill: "currentColor",
714
+ stroke: "none"
715
+ })
716
+ ]
717
+ });
718
+ const fileGlyph = /* @__PURE__ */ jsxs("svg", {
719
+ viewBox: "0 0 16 16",
720
+ "aria-hidden": "true",
721
+ fill: "none",
722
+ stroke: "currentColor",
723
+ strokeWidth: 1.5,
724
+ strokeLinecap: "square",
725
+ children: [/* @__PURE__ */ jsx("path", { d: "M4 2.5h5l3 3V13.5H4z" }), /* @__PURE__ */ jsx("path", { d: "M9 2.5v3h3" })]
726
+ });
727
+ const removeGlyph = /* @__PURE__ */ jsx("svg", {
728
+ viewBox: "0 0 16 16",
729
+ width: 12,
730
+ height: 12,
731
+ "aria-hidden": "true",
732
+ fill: "none",
733
+ stroke: "currentColor",
734
+ strokeWidth: 1.5,
735
+ strokeLinecap: "square",
736
+ children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" })
737
+ });
738
+ const IMAGE_EXTS = [
739
+ "png",
740
+ "jpg",
741
+ "jpeg",
742
+ "gif",
743
+ "webp",
744
+ "svg",
745
+ "avif",
746
+ "bmp",
747
+ "ico"
748
+ ];
749
+ const isImage = (name) => IMAGE_EXTS.includes(name.split(".").pop()?.toLowerCase() ?? "");
750
+ const humanSize = (bytes) => {
751
+ if (bytes < 1024) return `${bytes} B`;
752
+ const units = [
753
+ "KB",
754
+ "MB",
755
+ "GB"
756
+ ];
757
+ let value = bytes;
758
+ let unit = -1;
759
+ do {
760
+ value /= 1024;
761
+ unit += 1;
762
+ } while (value >= 1024 && unit < units.length - 1);
763
+ return `${value.toFixed(1)} ${units[unit]}`;
764
+ };
765
+ function Attachment({ name, size, status = "ready", onRemove, ...rest }) {
766
+ return /* @__PURE__ */ jsxs("span", {
767
+ ...rest,
768
+ "data-scope": "ai",
769
+ "data-part": "attachment",
770
+ "data-status": status,
771
+ children: [
772
+ isImage(name) ? imageGlyph : fileGlyph,
773
+ /* @__PURE__ */ jsx("span", { children: name }),
774
+ size !== void 0 ? /* @__PURE__ */ jsx("span", { children: humanSize(size) }) : null,
775
+ /* @__PURE__ */ jsx("button", {
776
+ type: "button",
777
+ "data-remove": "",
778
+ "aria-label": `Remove ${name}`,
779
+ onClick: () => onRemove?.(),
780
+ children: removeGlyph
781
+ })
782
+ ]
783
+ });
784
+ }
785
+ /** The row the files ride in — a wrapping line of chips. */
786
+ function Attachments({ children, ...rest }) {
787
+ return /* @__PURE__ */ jsx("span", {
788
+ ...rest,
789
+ "data-scope": "ai",
790
+ "data-part": "attachments",
791
+ children
792
+ });
793
+ }
794
+ injectComponentStyle("ai");
795
+ //#endregion
313
796
  //#region src/components/alert/index.tsx
314
797
  const StatusContext = createContext("ink");
315
798
  function glyph(status) {
@@ -345,7 +828,7 @@ function AlertRoot({ status = "ink", children, ...rest }) {
345
828
  })
346
829
  });
347
830
  }
348
- function Icon$1() {
831
+ function Icon$2() {
349
832
  const status = useContext(StatusContext);
350
833
  return /* @__PURE__ */ jsx("span", {
351
834
  "data-scope": "alert",
@@ -379,7 +862,7 @@ function Description$5({ children, ...rest }) {
379
862
  }
380
863
  const Alert = Object.assign(AlertRoot, {
381
864
  Root: AlertRoot,
382
- Icon: Icon$1,
865
+ Icon: Icon$2,
383
866
  Body: Body$2,
384
867
  Title: Title$4,
385
868
  Description: Description$5
@@ -394,11 +877,56 @@ injectComponentStyle("alert");
394
877
  const AngleSlider = AngleSlider$1;
395
878
  injectComponentStyle("angle-slider");
396
879
  //#endregion
880
+ //#region src/components/aspect-ratio/index.tsx
881
+ function AspectRatio({ ratio = "1 / 1", ...rest }) {
882
+ return /* @__PURE__ */ jsx("div", {
883
+ ...rest,
884
+ style: {
885
+ ...rest.style,
886
+ "--bs-aspect-ratio": ratio
887
+ },
888
+ "data-scope": "aspect-ratio",
889
+ "data-part": "root"
890
+ });
891
+ }
892
+ injectComponentStyle("aspect-ratio");
893
+ //#endregion
894
+ //#region src/components/autocomplete/index.tsx
895
+ function AutoComplete({ value = "", items = [], placeholder, filter, onValueChange, ...rest }) {
896
+ const { collection, filter: filterItems } = useListCollection$1({
897
+ initialItems: items,
898
+ filter: (item, input) => filter ? filter(item, input) : item.toLowerCase().includes(input.toLowerCase())
899
+ });
900
+ return /* @__PURE__ */ jsxs(Combobox$1.Root, {
901
+ ...rest,
902
+ collection,
903
+ inputValue: value,
904
+ allowCustomValue: true,
905
+ onValueChange: (details) => {
906
+ const [first] = details.value;
907
+ if (first != null) onValueChange?.(first);
908
+ },
909
+ onInputValueChange: (details) => {
910
+ filterItems(details.inputValue);
911
+ onValueChange?.(details.inputValue);
912
+ },
913
+ positioning: { sameWidth: true },
914
+ children: [/* @__PURE__ */ jsx(Combobox$1.Control, { children: /* @__PURE__ */ jsx(Combobox$1.Input, { placeholder }) }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Combobox$1.Positioner, { children: /* @__PURE__ */ jsxs(Combobox$1.Content, { children: [/* @__PURE__ */ jsx(Combobox$1.Empty, { children: "No matches" }), collection.items.map((item) => /* @__PURE__ */ jsx(Combobox$1.Item, {
915
+ item,
916
+ children: /* @__PURE__ */ jsx(Combobox$1.ItemText, { children: item })
917
+ }, item))] }) }) })]
918
+ });
919
+ }
920
+ injectComponentStyle("combobox");
921
+ //#endregion
397
922
  //#region src/components/avatar/index.tsx
398
- /** Ark's Avatar, dressed in the paper-and-ink system: a circular seal on
399
- * inset paper that holds initials until the image loads over them. The
400
- * API is Ark's own — Root, Image, Fallback. */
401
- const Avatar = Avatar$1;
923
+ function AvatarRoot({ size, ...rest }) {
924
+ return /* @__PURE__ */ jsx(Avatar$1.Root, {
925
+ ...rest,
926
+ "data-size": size
927
+ });
928
+ }
929
+ const Avatar = Object.assign({}, Avatar$1, { Root: AvatarRoot });
402
930
  injectComponentStyle("avatar");
403
931
  //#endregion
404
932
  //#region src/components/avatar-group/index.tsx
@@ -412,6 +940,83 @@ function AvatarGroup({ children, ...rest }) {
412
940
  }
413
941
  injectComponentStyle("avatar-group");
414
942
  //#endregion
943
+ //#region src/components/back-top/index.tsx
944
+ /** The single glyph a way-home control needs: one stroke pointing up. */
945
+ function chevronUp() {
946
+ return /* @__PURE__ */ jsx("svg", {
947
+ width: 16,
948
+ height: 16,
949
+ viewBox: "0 0 24 24",
950
+ fill: "none",
951
+ stroke: "currentColor",
952
+ strokeWidth: 1.75,
953
+ strokeLinecap: "round",
954
+ strokeLinejoin: "round",
955
+ "aria-hidden": "true",
956
+ children: /* @__PURE__ */ jsx("path", { d: "m6 14 6-6 6 6" })
957
+ });
958
+ }
959
+ /**
960
+ * A way home: after the page has scrolled past `threshold`, a small
961
+ * floating control rises at the page's corner and returns the reader to
962
+ * the top. The scroll itself stays native — `window.scrollTo` defers to
963
+ * the stylesheet's `scroll-behavior: smooth`, which reduced motion turns
964
+ * back into an instant jump (a moored control asks the media query
965
+ * directly, since a scroller of its own has no stylesheet to defer to).
966
+ * The button stays mounted either way so the entrance is a transition,
967
+ * never a pop.
968
+ *
969
+ * The control itself is the shared `Button` (outline, square) — the
970
+ * paper, hairline and halo are its; this family owns only the floating
971
+ * and the entrance.
972
+ */
973
+ function BackTop({ threshold = 400, label = "Back to top", scrollEl, ...rest }) {
974
+ const [visible, setVisible] = useState(false);
975
+ useEffect(() => {
976
+ if (typeof window === "undefined") return;
977
+ const el = scrollEl?.() ?? null;
978
+ const onScroll = () => setVisible((el ? el.scrollTop : window.scrollY) > threshold);
979
+ onScroll();
980
+ if (el) el.addEventListener("scroll", onScroll, { passive: true });
981
+ else window.addEventListener("scroll", onScroll, { passive: true });
982
+ return () => {
983
+ if (el) el.removeEventListener("scroll", onScroll);
984
+ else window.removeEventListener("scroll", onScroll);
985
+ };
986
+ }, [threshold, scrollEl]);
987
+ const toTop = () => {
988
+ const el = scrollEl?.();
989
+ if (!el) {
990
+ window.scrollTo({ top: 0 });
991
+ return;
992
+ }
993
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
994
+ el.scrollTo({
995
+ top: 0,
996
+ behavior: reduce ? "auto" : "smooth"
997
+ });
998
+ };
999
+ return /* @__PURE__ */ jsx("div", {
1000
+ "data-scope": "back-top",
1001
+ "data-part": "root",
1002
+ "data-state": visible ? "shown" : "hidden",
1003
+ ...scrollEl ? { "data-container": "" } : {},
1004
+ children: /* @__PURE__ */ jsx(Button, {
1005
+ ...rest,
1006
+ variant: "outline",
1007
+ square: true,
1008
+ size: "lg",
1009
+ type: "button",
1010
+ "aria-label": label,
1011
+ "aria-hidden": visible ? void 0 : "true",
1012
+ tabIndex: visible ? 0 : -1,
1013
+ onClick: toTop,
1014
+ children: rest.children ?? chevronUp()
1015
+ })
1016
+ });
1017
+ }
1018
+ injectComponentStyle("back-top");
1019
+ //#endregion
415
1020
  //#region src/components/badge/index.tsx
416
1021
  function Badge({ tone = "ink", variant = "solid", ...rest }) {
417
1022
  return /* @__PURE__ */ jsx("span", {
@@ -428,7 +1033,7 @@ injectComponentStyle("badge");
428
1033
  /** A trail of waymarks: Root wraps the nav, List the ordered trail, and
429
1034
  * each Item carries a Link — or the Current page — parted by a quiet
430
1035
  * Separator. Links take href and the rest through attributes. */
431
- function part$8(name, tag, extra = {}, fallback) {
1036
+ function part$11(name, tag, extra = {}, fallback) {
432
1037
  const Tag = tag;
433
1038
  const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
434
1039
  ...extra,
@@ -440,28 +1045,50 @@ function part$8(name, tag, extra = {}, fallback) {
440
1045
  Component.displayName = "Breadcrumb" + name;
441
1046
  return Component;
442
1047
  }
443
- const Root$7 = part$8("Root", "nav", { "aria-label": "Breadcrumb" });
444
- const List = part$8("List", "ol");
445
- const Item$1 = part$8("Item", "li");
446
- const Link$1 = part$8("Link", "a");
447
- const Current = part$8("Current", "span", { "aria-current": "page" });
448
- const Separator$1 = part$8("Separator", "span", { "aria-hidden": "true" }, "/");
449
- const Breadcrumb = Object.assign(Root$7, {
450
- Root: Root$7,
451
- List,
452
- Item: Item$1,
1048
+ const Root$12 = part$11("Root", "nav", { "aria-label": "Breadcrumb" });
1049
+ const List$1 = part$11("List", "ol");
1050
+ const Item$4 = part$11("Item", "li");
1051
+ const Link$1 = part$11("Link", "a");
1052
+ const Current = part$11("Current", "span", { "aria-current": "page" });
1053
+ const Separator$1 = part$11("Separator", "span", { "aria-hidden": "true" }, "/");
1054
+ const Breadcrumb = Object.assign(Root$12, {
1055
+ Root: Root$12,
1056
+ List: List$1,
1057
+ Item: Item$4,
453
1058
  Link: Link$1,
454
1059
  Current,
455
1060
  Separator: Separator$1
456
1061
  });
457
1062
  injectComponentStyle("breadcrumb");
458
1063
  //#endregion
1064
+ //#region src/components/button-group/index.tsx
1065
+ /**
1066
+ * Buttons fused into one control: the group owns only the joinery, so
1067
+ * members keep every variant they were given — a solid action can sit
1068
+ * beside an outline one and the seam still reads. Selection belongs to
1069
+ * the toggle group; this is layout alone.
1070
+ */
1071
+ function Root$11({ orientation = "horizontal", size, radius, children, ...rest }) {
1072
+ return /* @__PURE__ */ jsx("div", {
1073
+ ...rest,
1074
+ role: "group",
1075
+ "data-scope": "button-group",
1076
+ "data-part": "root",
1077
+ "data-orientation": orientation,
1078
+ "data-size": size,
1079
+ "data-radius": radius,
1080
+ children
1081
+ });
1082
+ }
1083
+ const ButtonGroup = Object.assign(Root$11, { Root: Root$11 });
1084
+ injectComponentStyle("button-group");
1085
+ //#endregion
459
1086
  //#region src/components/banner/index.tsx
460
1087
  /** A page-level notice, spoken across the full measure: a wash of the
461
1088
  * status pigment, one heavier hairline on the leading edge, and room
462
1089
  * for actions and a quiet close. Ink is the neutral register; the four
463
1090
  * semantic pigments are fixed. */
464
- const Root$6 = ({ status = "ink", children, ...rest }) => /* @__PURE__ */ jsx("div", {
1091
+ const Root$10 = ({ status = "ink", children, ...rest }) => /* @__PURE__ */ jsx("div", {
465
1092
  ...rest,
466
1093
  role: status === "ink" ? void 0 : "status",
467
1094
  "data-scope": "banner",
@@ -469,7 +1096,7 @@ const Root$6 = ({ status = "ink", children, ...rest }) => /* @__PURE__ */ jsx("d
469
1096
  "data-status": status,
470
1097
  children
471
1098
  });
472
- function part$7(name, tag) {
1099
+ function part$10(name, tag) {
473
1100
  const Tag = tag;
474
1101
  const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
475
1102
  ...rest,
@@ -480,14 +1107,14 @@ function part$7(name, tag) {
480
1107
  Component.displayName = "Banner" + name;
481
1108
  return Component;
482
1109
  }
483
- const Icon = part$7("Icon", "span");
484
- const Body$1 = part$7("Body", "div");
485
- const Title$3 = part$7("Title", "p");
486
- const Description$4 = part$7("Description", "p");
487
- const Actions$2 = part$7("Actions", "div");
1110
+ const Icon$1 = part$10("Icon", "span");
1111
+ const Body$1 = part$10("Body", "div");
1112
+ const Title$3 = part$10("Title", "p");
1113
+ const Description$4 = part$10("Description", "p");
1114
+ const Actions$3 = part$10("Actions", "div");
488
1115
  /** The quiet close: a plain square-cut button; dismissal stays the
489
1116
  * consumer's state. */
490
- const Close = ({ children, ...rest }) => /* @__PURE__ */ jsx("button", {
1117
+ const Close = (rest) => /* @__PURE__ */ jsx("button", {
491
1118
  ...rest,
492
1119
  type: rest.type ?? "button",
493
1120
  "aria-label": rest["aria-label"] ?? "Dismiss",
@@ -505,19 +1132,19 @@ const Close = ({ children, ...rest }) => /* @__PURE__ */ jsx("button", {
505
1132
  })
506
1133
  })
507
1134
  });
508
- const Banner = Object.assign(Root$6, {
509
- Root: Root$6,
510
- Icon,
1135
+ const Banner = Object.assign(Root$10, {
1136
+ Root: Root$10,
1137
+ Icon: Icon$1,
511
1138
  Body: Body$1,
512
1139
  Title: Title$3,
513
1140
  Description: Description$4,
514
- Actions: Actions$2,
1141
+ Actions: Actions$3,
515
1142
  Close
516
1143
  });
517
1144
  injectComponentStyle("banner");
518
1145
  //#endregion
519
1146
  //#region src/components/calendar/index.tsx
520
- function chevron(dir) {
1147
+ function chevron$1(dir) {
521
1148
  return /* @__PURE__ */ jsx("svg", {
522
1149
  width: 16,
523
1150
  height: 16,
@@ -551,9 +1178,9 @@ function Calendar({ value, min, max, onValueChange, children, ...rest }) {
551
1178
  "data-scope": "calendar",
552
1179
  "data-part": "header",
553
1180
  children: /* @__PURE__ */ jsxs(DatePicker$1.ViewControl, { children: [
554
- /* @__PURE__ */ jsx(DatePicker$1.PrevTrigger, { children: chevron("left") }),
1181
+ /* @__PURE__ */ jsx(DatePicker$1.PrevTrigger, { children: chevron$1("left") }),
555
1182
  /* @__PURE__ */ jsx(DatePicker$1.Context, { children: (dp) => /* @__PURE__ */ jsx(DatePicker$1.ViewTrigger, { children: title(dp) }) }),
556
- /* @__PURE__ */ jsx(DatePicker$1.NextTrigger, { children: chevron("right") })
1183
+ /* @__PURE__ */ jsx(DatePicker$1.NextTrigger, { children: chevron$1("right") })
557
1184
  ] })
558
1185
  });
559
1186
  return /* @__PURE__ */ jsxs("div", {
@@ -606,12 +1233,13 @@ function Calendar({ value, min, max, onValueChange, children, ...rest }) {
606
1233
  });
607
1234
  }
608
1235
  injectComponentStyle("calendar");
1236
+ injectComponentStyle("date-picker");
609
1237
  //#endregion
610
1238
  //#region src/components/card/index.tsx
611
1239
  /** A vessel: round, resting at the first elevation, one hairline for its
612
1240
  * edge. Root, Header, Title, Description, Content, Footer — sections
613
1241
  * carry their own whitespace, so any subset composes. */
614
- function part$6(name, tag) {
1242
+ function part$9(name, tag) {
615
1243
  const Tag = tag;
616
1244
  const Component = ({ asChild, children, ...rest }) => {
617
1245
  const partProps = {
@@ -623,29 +1251,253 @@ function part$6(name, tag) {
623
1251
  const child = Children.only(children);
624
1252
  return isValidElement(child) ? cloneElement(child, partProps) : null;
625
1253
  }
626
- return /* @__PURE__ */ jsx(Tag, {
627
- ...partProps,
628
- children
1254
+ return /* @__PURE__ */ jsx(Tag, {
1255
+ ...partProps,
1256
+ children
1257
+ });
1258
+ };
1259
+ Component.displayName = "Card" + name;
1260
+ return Component;
1261
+ }
1262
+ const Root$9 = part$9("Root", "section");
1263
+ const Header$1 = part$9("Header", "header");
1264
+ const Title$2 = part$9("Title", "h3");
1265
+ const Description$3 = part$9("Description", "p");
1266
+ const Content$3 = part$9("Content", "div");
1267
+ const Footer$1 = part$9("Footer", "footer");
1268
+ const Card = Object.assign(Root$9, {
1269
+ Root: Root$9,
1270
+ Header: Header$1,
1271
+ Title: Title$2,
1272
+ Description: Description$3,
1273
+ Content: Content$3,
1274
+ Footer: Footer$1
1275
+ });
1276
+ injectComponentStyle("card");
1277
+ //#endregion
1278
+ //#region src/components/input/index.tsx
1279
+ function Input({ value, size = "md", invalid = false, onValueChange, ...rest }) {
1280
+ const field = useFieldContext();
1281
+ const fieldProps = field?.getInputProps() ?? {};
1282
+ return /* @__PURE__ */ jsx("input", {
1283
+ ...fieldProps,
1284
+ ...rest,
1285
+ ...value !== void 0 ? { value } : {},
1286
+ "data-scope": "input",
1287
+ "data-part": "root",
1288
+ "data-size": size,
1289
+ "data-invalid": invalid || field?.invalid ? "" : void 0,
1290
+ onChange: (event) => onValueChange?.(event.target.value)
1291
+ });
1292
+ }
1293
+ injectComponentStyle("input");
1294
+ //#endregion
1295
+ //#region src/components/cascade-select/index.tsx
1296
+ const chevronDown$2 = /* @__PURE__ */ jsx("svg", {
1297
+ viewBox: "0 0 24 24",
1298
+ fill: "none",
1299
+ stroke: "currentColor",
1300
+ strokeWidth: 2,
1301
+ strokeLinecap: "round",
1302
+ strokeLinejoin: "round",
1303
+ "aria-hidden": "true",
1304
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
1305
+ });
1306
+ const chevronRight = /* @__PURE__ */ jsx("svg", {
1307
+ viewBox: "0 0 24 24",
1308
+ fill: "none",
1309
+ stroke: "currentColor",
1310
+ strokeWidth: 2,
1311
+ strokeLinecap: "round",
1312
+ strokeLinejoin: "round",
1313
+ "aria-hidden": "true",
1314
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
1315
+ });
1316
+ const checkGlyph$2 = /* @__PURE__ */ jsx("svg", {
1317
+ viewBox: "0 0 24 24",
1318
+ fill: "none",
1319
+ stroke: "currentColor",
1320
+ strokeWidth: 3,
1321
+ strokeLinecap: "round",
1322
+ strokeLinejoin: "round",
1323
+ "aria-hidden": "true",
1324
+ children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 5 5L19 7" })
1325
+ });
1326
+ function CascadeSelect({ value, data, placeholder = "Select…", highlightTrigger, filterable = false, multiple = false, disabled = false, onValueChange, ...rest }) {
1327
+ const id = useId();
1328
+ const locale = useLocaleContext();
1329
+ const env = useEnvironmentContext();
1330
+ const collection = cascade.collection({
1331
+ nodeToValue: (node) => node.value,
1332
+ nodeToString: (node) => node.label,
1333
+ nodeToChildren: (node) => node.children ?? [],
1334
+ rootNode: {
1335
+ value: "ROOT",
1336
+ label: "",
1337
+ children: data
1338
+ }
1339
+ });
1340
+ const service = useMachine(cascade.machine, {
1341
+ id,
1342
+ collection,
1343
+ dir: locale.dir,
1344
+ getRootNode: env.getRootNode.bind(env),
1345
+ disabled: disabled || void 0,
1346
+ multiple: multiple || void 0,
1347
+ ...value !== void 0 ? { value } : {},
1348
+ ...highlightTrigger != null ? { highlightTrigger } : {},
1349
+ onValueChange(details) {
1350
+ onValueChange?.(details.value);
1351
+ }
1352
+ });
1353
+ const api = cascade.connect(service, normalizeProps);
1354
+ const [query, setQuery] = useState("");
1355
+ const filterFns = useFilter({ sensitivity: "base" });
1356
+ const filtering = filterable && query.trim().length > 0;
1357
+ /** The corridor's answer to a query: every leaf whose route matches —
1358
+ * the query may land on any hop, and the whole route still shows. */
1359
+ const matchPaths = useMemo(() => {
1360
+ if (!filtering) return [];
1361
+ const q = query.trim();
1362
+ const hits = [];
1363
+ const walk = (nodes, path, labels, matched) => {
1364
+ for (const node of nodes ?? []) {
1365
+ const hit = matched || filterFns.contains(node.label, q);
1366
+ const nextPath = [...path, node.value];
1367
+ const nextLabels = [...labels, node.label];
1368
+ if (node.children?.length) walk(node.children, nextPath, nextLabels, hit);
1369
+ else if (hit) hits.push({
1370
+ path: nextPath,
1371
+ labels: nextLabels
1372
+ });
1373
+ }
1374
+ };
1375
+ walk(data, [], [], false);
1376
+ return hits;
1377
+ }, [
1378
+ filtering,
1379
+ query,
1380
+ filterFns,
1381
+ data
1382
+ ]);
1383
+ const isSelected = (path) => (value ?? []).some((p) => p.join("/") === path.join("/"));
1384
+ function pickMatch(path) {
1385
+ if (multiple) {
1386
+ const current = value ?? [];
1387
+ const key = path.join("/");
1388
+ const next = current.some((p) => p.join("/") === key) ? current.filter((p) => p.join("/") !== key) : [...current, path];
1389
+ api.setValue(next);
1390
+ } else {
1391
+ api.selectValue(path);
1392
+ api.setOpen(false);
1393
+ }
1394
+ }
1395
+ /** The machine's `valueAsString` only updates through its select
1396
+ * event, so an externally-set `value` would render the placeholder
1397
+ * forever — resolve the labels from the data instead. */
1398
+ function labelsFor(path) {
1399
+ const out = [];
1400
+ let nodes = data;
1401
+ for (const entry of path) {
1402
+ const node = nodes?.find((n) => n.value === entry);
1403
+ if (!node) return null;
1404
+ out.push(node.label);
1405
+ nodes = node.children;
1406
+ }
1407
+ return out;
1408
+ }
1409
+ const display = (value ?? []).map((path) => labelsFor(path)?.join(" / ")).filter((label) => label != null).join(", ");
1410
+ /** One column per walk of the highlighted path — the list binds to
1411
+ * the node, the next column hangs off its highlighted child. */
1412
+ function renderColumn(node, indexPath, valuePath) {
1413
+ const nodeProps = {
1414
+ item: node,
1415
+ indexPath,
1416
+ value: valuePath
1417
+ };
1418
+ const nodeState = api.getItemState(nodeProps);
1419
+ const children = collection.getNodeChildren(node);
1420
+ const list = /* @__PURE__ */ jsx("ul", {
1421
+ ...api.getListProps(nodeProps),
1422
+ children: children.map((item, index) => {
1423
+ const itemValue = collection.getNodeValue(item);
1424
+ const itemProps = {
1425
+ item,
1426
+ indexPath: [...indexPath, index],
1427
+ value: [...valuePath, itemValue]
1428
+ };
1429
+ const itemState = api.getItemState(itemProps);
1430
+ return /* @__PURE__ */ jsxs("li", {
1431
+ ...api.getItemProps(itemProps),
1432
+ children: [
1433
+ /* @__PURE__ */ jsx("span", {
1434
+ ...api.getItemTextProps(itemProps),
1435
+ children: item.label
1436
+ }),
1437
+ itemState.hasChildren ? /* @__PURE__ */ jsx("span", {
1438
+ "data-part": "branch-indicator",
1439
+ "aria-hidden": "true",
1440
+ children: chevronRight
1441
+ }) : null,
1442
+ /* @__PURE__ */ jsx("span", {
1443
+ ...api.getItemIndicatorProps(itemProps),
1444
+ children: checkGlyph$2
1445
+ })
1446
+ ]
1447
+ }, itemValue);
1448
+ })
629
1449
  });
630
- };
631
- Component.displayName = "Card" + name;
632
- return Component;
1450
+ const next = nodeState.highlightedChild;
1451
+ return [list, next != null && collection.isBranchNode(next) ? renderColumn(next, [...indexPath, nodeState.highlightedIndex], [...valuePath, collection.getNodeValue(next)]) : null];
1452
+ }
1453
+ return /* @__PURE__ */ jsxs("div", {
1454
+ ...rest,
1455
+ ...api.getRootProps(),
1456
+ children: [/* @__PURE__ */ jsx("div", {
1457
+ ...api.getControlProps(),
1458
+ children: /* @__PURE__ */ jsxs("button", {
1459
+ ...api.getTriggerProps(),
1460
+ onClick: (event) => {
1461
+ api.getTriggerProps().onClick?.(event);
1462
+ setQuery("");
1463
+ },
1464
+ disabled: disabled || void 0,
1465
+ children: [/* @__PURE__ */ jsx("span", {
1466
+ ...api.getValueTextProps(),
1467
+ children: display || placeholder
1468
+ }), /* @__PURE__ */ jsx("span", {
1469
+ ...api.getIndicatorProps(),
1470
+ children: chevronDown$2
1471
+ })]
1472
+ })
1473
+ }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx("div", {
1474
+ ...api.getPositionerProps(),
1475
+ children: /* @__PURE__ */ jsxs("div", {
1476
+ ...api.getContentProps(),
1477
+ children: [filterable ? /* @__PURE__ */ jsx("div", {
1478
+ "data-part": "search",
1479
+ children: /* @__PURE__ */ jsx(Input, {
1480
+ size: "sm",
1481
+ value: query,
1482
+ onValueChange: setQuery,
1483
+ placeholder: "Filter…",
1484
+ "aria-label": "Filter options"
1485
+ })
1486
+ }) : null, filtering ? matchPaths.length === 0 ? /* @__PURE__ */ jsx("p", {
1487
+ "data-part": "empty",
1488
+ children: "Nothing matches"
1489
+ }) : matchPaths.map((hit) => /* @__PURE__ */ jsx("button", {
1490
+ type: "button",
1491
+ "data-part": "match",
1492
+ "data-selected": isSelected(hit.path) || void 0,
1493
+ onClick: () => pickMatch(hit.path),
1494
+ children: hit.labels.join(" / ")
1495
+ }, hit.path.join("/"))) : renderColumn(collection.rootNode, [], [])]
1496
+ })
1497
+ }) })]
1498
+ });
633
1499
  }
634
- const Root$5 = part$6("Root", "section");
635
- const Header = part$6("Header", "header");
636
- const Title$2 = part$6("Title", "h3");
637
- const Description$3 = part$6("Description", "p");
638
- const Content$1 = part$6("Content", "div");
639
- const Footer = part$6("Footer", "footer");
640
- const Card = Object.assign(Root$5, {
641
- Root: Root$5,
642
- Header,
643
- Title: Title$2,
644
- Description: Description$3,
645
- Content: Content$1,
646
- Footer
647
- });
648
- injectComponentStyle("card");
1500
+ injectComponentStyle("cascade-select");
649
1501
  //#endregion
650
1502
  //#region src/components/carousel/index.tsx
651
1503
  /** Ark's Carousel, dressed in the paper-and-ink system: slides ride in one
@@ -664,6 +1516,59 @@ injectComponentStyle("carousel");
664
1516
  const Checkbox = Checkbox$1;
665
1517
  injectComponentStyle("checkbox");
666
1518
  //#endregion
1519
+ //#region src/components/checkbox-group/index.tsx
1520
+ const checkGlyph$1 = /* @__PURE__ */ jsx("svg", {
1521
+ viewBox: "0 0 24 24",
1522
+ fill: "none",
1523
+ stroke: "currentColor",
1524
+ strokeWidth: 3,
1525
+ strokeLinecap: "round",
1526
+ strokeLinejoin: "round",
1527
+ "aria-hidden": "true",
1528
+ children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 5 5L19 7" })
1529
+ });
1530
+ /**
1531
+ * One question, many answers: a labelled stack (or row) of the seal-cut
1532
+ * checkboxes bound to a single array. Toggling a box adds or removes its
1533
+ * value; the group itself is semantics (`role="group"`), the boxes stay
1534
+ * the machine-driven originals. Inside a `Field.Root` the group picks up
1535
+ * the field context, so the invalid and disabled states a Form routes to
1536
+ * its name dress every box at once.
1537
+ */
1538
+ function CheckboxGroup({ value = [], options, layout = "vertical", invalid = false, disabled = false, onValueChange, ...rest }) {
1539
+ const field = useFieldContext();
1540
+ const selected = new Set(value);
1541
+ const isInvalid = invalid || field?.invalid === true;
1542
+ const isDisabled = disabled || field?.disabled === true;
1543
+ function toggle(option) {
1544
+ const next = new Set(selected);
1545
+ if (next.has(option)) next.delete(option);
1546
+ else next.add(option);
1547
+ onValueChange?.([...next]);
1548
+ }
1549
+ return /* @__PURE__ */ jsx("div", {
1550
+ ...rest,
1551
+ role: "group",
1552
+ "data-scope": "checkbox-group",
1553
+ "data-part": "root",
1554
+ "data-layout": layout,
1555
+ "data-invalid": isInvalid ? "" : void 0,
1556
+ children: options.map((option) => /* @__PURE__ */ jsxs(Checkbox$1.Root, {
1557
+ checked: selected.has(option.value),
1558
+ invalid: isInvalid,
1559
+ disabled: isDisabled || option.disabled === true,
1560
+ onCheckedChange: () => toggle(option.value),
1561
+ children: [
1562
+ /* @__PURE__ */ jsx(Checkbox$1.Control, { children: /* @__PURE__ */ jsx(Checkbox$1.Indicator, { children: checkGlyph$1 }) }),
1563
+ /* @__PURE__ */ jsx(Checkbox$1.Label, { children: option.label }),
1564
+ /* @__PURE__ */ jsx(Checkbox$1.HiddenInput, {})
1565
+ ]
1566
+ }, option.value))
1567
+ });
1568
+ }
1569
+ injectComponentStyle("checkbox-group");
1570
+ injectComponentStyle("checkbox");
1571
+ //#endregion
667
1572
  //#region src/components/chip/index.tsx
668
1573
  function Chip({ value, max, tone = "ink", variant = "solid", ...rest }) {
669
1574
  const text = max != null && value > max ? `${max}+` : String(value);
@@ -707,6 +1612,227 @@ injectComponentStyle("color-picker");
707
1612
  const Combobox = Combobox$1;
708
1613
  injectComponentStyle("combobox");
709
1614
  //#endregion
1615
+ //#region src/components/command/index.tsx
1616
+ function Command({ items = [], placeholder, open, autoFilter = true, inputValue, emptyText = "No matching commands", onSelect, onOpenChange, onInputValueChange }) {
1617
+ const [internalOpen, setInternalOpen] = useState(false);
1618
+ const [listOpen, setListOpen] = useState(true);
1619
+ const [fieldText, setFieldText] = useState("");
1620
+ const { collection, set, filter } = useListCollection$1({
1621
+ initialItems: items.map((entry) => entry.value),
1622
+ filter: (value, input) => {
1623
+ const entry = items.find((candidate) => candidate.value === value);
1624
+ if (!entry) return false;
1625
+ const query = input.toLowerCase();
1626
+ return entry.label.toLowerCase().includes(query) || value.toLowerCase().includes(query);
1627
+ }
1628
+ });
1629
+ useEffect(() => {
1630
+ set(items.map((entry) => entry.value));
1631
+ if (autoFilter) filter(fieldText);
1632
+ }, [items]);
1633
+ const currentOpen = open ?? internalOpen;
1634
+ useEffect(() => {
1635
+ if (currentOpen) setListOpen(true);
1636
+ }, [currentOpen]);
1637
+ const setOpen = (value) => {
1638
+ if (open === void 0) setInternalOpen(value);
1639
+ onOpenChange?.(value);
1640
+ };
1641
+ const available = new Set(collection.items);
1642
+ const groups = /* @__PURE__ */ new Map();
1643
+ for (const entry of items) {
1644
+ if (!available.has(entry.value)) continue;
1645
+ const key = entry.group ?? "";
1646
+ if (!groups.has(key)) groups.set(key, []);
1647
+ groups.get(key).push(entry);
1648
+ }
1649
+ const row = (entry) => /* @__PURE__ */ jsx(Combobox$1.Item, {
1650
+ item: entry.value,
1651
+ asChild: true,
1652
+ children: /* @__PURE__ */ jsxs("div", {
1653
+ "data-scope": "command",
1654
+ "data-part": "item",
1655
+ children: [/* @__PURE__ */ jsx(Combobox$1.ItemText, {
1656
+ asChild: true,
1657
+ children: /* @__PURE__ */ jsx("span", {
1658
+ "data-scope": "command",
1659
+ "data-part": "item-label",
1660
+ children: entry.label
1661
+ })
1662
+ }), entry.hint ? /* @__PURE__ */ jsx("span", {
1663
+ "data-scope": "command",
1664
+ "data-part": "item-hint",
1665
+ children: entry.hint
1666
+ }) : void 0]
1667
+ })
1668
+ }, entry.value);
1669
+ return /* @__PURE__ */ jsx(Dialog$1.Root, {
1670
+ open: currentOpen,
1671
+ onOpenChange: (details) => setOpen(details.open),
1672
+ children: /* @__PURE__ */ jsxs(Portal, { children: [/* @__PURE__ */ jsx(Dialog$1.Backdrop, {}), /* @__PURE__ */ jsx(Dialog$1.Positioner, {
1673
+ asChild: true,
1674
+ children: /* @__PURE__ */ jsx("div", {
1675
+ "data-scope": "command",
1676
+ "data-part": "positioner",
1677
+ children: /* @__PURE__ */ jsx(Dialog$1.Content, {
1678
+ asChild: true,
1679
+ children: /* @__PURE__ */ jsx("div", {
1680
+ "data-scope": "command",
1681
+ "data-part": "content",
1682
+ children: /* @__PURE__ */ jsxs(Combobox$1.Root, {
1683
+ collection,
1684
+ inputValue,
1685
+ open: listOpen,
1686
+ onOpenChange: (details) => setListOpen(details.open),
1687
+ inputBehavior: "autohighlight",
1688
+ loopFocus: true,
1689
+ onInputValueChange: (details) => {
1690
+ setFieldText(details.inputValue);
1691
+ if (autoFilter) filter(details.inputValue);
1692
+ onInputValueChange?.(details.inputValue);
1693
+ },
1694
+ onValueChange: (details) => {
1695
+ const [first] = details.value;
1696
+ if (first == null) return;
1697
+ onSelect?.(first);
1698
+ setOpen(false);
1699
+ },
1700
+ children: [/* @__PURE__ */ jsx(Combobox$1.Input, {
1701
+ asChild: true,
1702
+ children: /* @__PURE__ */ jsx("input", {
1703
+ type: "text",
1704
+ placeholder,
1705
+ "data-scope": "command",
1706
+ "data-part": "input"
1707
+ })
1708
+ }), /* @__PURE__ */ jsx(Combobox$1.Content, {
1709
+ asChild: true,
1710
+ children: /* @__PURE__ */ jsxs("div", {
1711
+ "data-scope": "command",
1712
+ "data-part": "list",
1713
+ children: [Array.from(groups, ([group, entries]) => group === "" ? entries.map(row) : /* @__PURE__ */ jsx(Combobox$1.ItemGroup, {
1714
+ asChild: true,
1715
+ children: /* @__PURE__ */ jsxs("div", {
1716
+ "data-scope": "command",
1717
+ "data-part": "group",
1718
+ children: [/* @__PURE__ */ jsx(Combobox$1.ItemGroupLabel, {
1719
+ asChild: true,
1720
+ children: /* @__PURE__ */ jsx("div", {
1721
+ "data-scope": "command",
1722
+ "data-part": "group-label",
1723
+ children: group
1724
+ })
1725
+ }), entries.map(row)]
1726
+ })
1727
+ }, group)), groups.size === 0 ? /* @__PURE__ */ jsx("div", {
1728
+ "data-scope": "command",
1729
+ "data-part": "empty",
1730
+ children: emptyText
1731
+ }) : null]
1732
+ })
1733
+ })]
1734
+ })
1735
+ })
1736
+ })
1737
+ })
1738
+ })] })
1739
+ });
1740
+ }
1741
+ injectComponentStyle("command");
1742
+ injectComponentStyle("dialog");
1743
+ //#endregion
1744
+ //#region src/components/comment/index.tsx
1745
+ function Comment({ author, datetime, avatar, actions, children, ...rest }) {
1746
+ return /* @__PURE__ */ jsxs("article", {
1747
+ ...rest,
1748
+ "data-scope": "comment",
1749
+ "data-part": "root",
1750
+ children: [avatar ? /* @__PURE__ */ jsx("div", {
1751
+ "data-scope": "comment",
1752
+ "data-part": "avatar",
1753
+ children: avatar
1754
+ }) : null, /* @__PURE__ */ jsxs("div", {
1755
+ "data-scope": "comment",
1756
+ "data-part": "body",
1757
+ children: [
1758
+ author || datetime ? /* @__PURE__ */ jsxs("header", {
1759
+ "data-scope": "comment",
1760
+ "data-part": "header",
1761
+ children: [author ? /* @__PURE__ */ jsx("span", {
1762
+ "data-scope": "comment",
1763
+ "data-part": "author",
1764
+ children: author
1765
+ }) : null, datetime ? /* @__PURE__ */ jsx("time", {
1766
+ "data-scope": "comment",
1767
+ "data-part": "datetime",
1768
+ dateTime: datetime,
1769
+ children: datetime
1770
+ }) : null]
1771
+ }) : null,
1772
+ /* @__PURE__ */ jsx("div", {
1773
+ "data-scope": "comment",
1774
+ "data-part": "content",
1775
+ children
1776
+ }),
1777
+ actions ? /* @__PURE__ */ jsx("div", {
1778
+ "data-scope": "comment",
1779
+ "data-part": "actions",
1780
+ children: actions
1781
+ }) : null
1782
+ ]
1783
+ })]
1784
+ });
1785
+ }
1786
+ injectComponentStyle("comment");
1787
+ //#endregion
1788
+ //#region src/components/config-provider/index.tsx
1789
+ const ConfigContextImpl = createContext({});
1790
+ /** The nearest provider's snapshot, or an empty one when no provider
1791
+ * wraps the caller — the hook the date and format families will consume
1792
+ * to localize their output. */
1793
+ function useConfig() {
1794
+ return useContext(ConfigContextImpl);
1795
+ }
1796
+ /**
1797
+ * The declarative host for global configuration: one element that both
1798
+ * carries the token layer's attributes — `[data-density]` and
1799
+ * `[data-accent]` fire on any element — and provides the same values to
1800
+ * descendants through `useConfig`, so interactive behavior (formatting,
1801
+ * messages) and visual theming stay one decision.
1802
+ */
1803
+ function ConfigProvider({ density, accent, dir, locale, children, ...rest }) {
1804
+ return /* @__PURE__ */ jsx(ConfigContextImpl.Provider, {
1805
+ value: {
1806
+ density,
1807
+ accent,
1808
+ dir,
1809
+ locale
1810
+ },
1811
+ children: /* @__PURE__ */ jsx("div", {
1812
+ ...rest,
1813
+ "data-scope": "config-provider",
1814
+ "data-part": "root",
1815
+ ...density != null ? { "data-density": density } : {},
1816
+ ...accent != null ? { "data-accent": accent } : {},
1817
+ ...dir != null ? { dir } : {},
1818
+ ...locale != null ? { lang: locale } : {},
1819
+ children
1820
+ })
1821
+ });
1822
+ }
1823
+ //#endregion
1824
+ //#region src/components/container/index.tsx
1825
+ function Container({ size = "readable", padding = true, ...rest }) {
1826
+ return /* @__PURE__ */ jsx("div", {
1827
+ ...rest,
1828
+ "data-scope": "container",
1829
+ "data-part": "root",
1830
+ "data-size": size,
1831
+ "data-padding": padding ? "" : void 0
1832
+ });
1833
+ }
1834
+ injectComponentStyle("container");
1835
+ //#endregion
710
1836
  //#region src/components/date-input/index.tsx
711
1837
  /** Ark's DateInput, dressed in the paper-and-ink system: a segmented
712
1838
  * field where the focused segment takes the flat ink fill. The API is
@@ -725,6 +1851,35 @@ injectComponentStyle("date-input");
725
1851
  const DatePicker = DatePicker$1;
726
1852
  injectComponentStyle("date-picker");
727
1853
  //#endregion
1854
+ //#region src/components/descriptions/index.tsx
1855
+ function DescriptionsRoot({ layout = "horizontal", children, ...rest }) {
1856
+ return /* @__PURE__ */ jsx("dl", {
1857
+ ...rest,
1858
+ "data-scope": "descriptions",
1859
+ "data-part": "root",
1860
+ "data-layout": layout,
1861
+ children
1862
+ });
1863
+ }
1864
+ function part$8(name, tag) {
1865
+ const Tag = tag;
1866
+ const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
1867
+ ...rest,
1868
+ "data-scope": "descriptions",
1869
+ "data-part": name.toLowerCase(),
1870
+ children
1871
+ });
1872
+ Component.displayName = "Descriptions" + name;
1873
+ return Component;
1874
+ }
1875
+ const Descriptions = {
1876
+ Root: DescriptionsRoot,
1877
+ Item: part$8("Item", "div"),
1878
+ Term: part$8("Term", "dt"),
1879
+ Detail: part$8("Detail", "dd")
1880
+ };
1881
+ injectComponentStyle("descriptions");
1882
+ //#endregion
728
1883
  //#region src/components/dialog/index.tsx
729
1884
  /** Ark's Dialog, dressed in the paper-and-ink system: the sheet dissolves
730
1885
  * in on elevation, the backdrop fades, and nested overlays stack through
@@ -742,6 +1897,81 @@ injectComponentStyle("dialog");
742
1897
  const Drawer = Drawer$1;
743
1898
  injectComponentStyle("drawer");
744
1899
  //#endregion
1900
+ //#region src/components/dynamic-input/index.tsx
1901
+ /**
1902
+ * A column of entry rows: one Input per line, each with a quiet remove
1903
+ * seal, and an add row at the tail. The list is controlled — every edit
1904
+ * reports a fresh array through `onValueChange` and the caller's array
1905
+ * stays the only truth. Deleting never drops below `min`, adding never
1906
+ * grows past `max`, and the group always keeps one row: an entry list
1907
+ * that emptied itself would leave the reader no place to type.
1908
+ */
1909
+ function DynamicInput({ value, min = 0, max, placeholder, addLabel = "Add entry", disabled = false, invalid = false, onValueChange, children: _children, ...rest }) {
1910
+ const values = value ?? [""];
1911
+ const update = (index, next) => {
1912
+ const rows = values.slice();
1913
+ rows[index] = next;
1914
+ onValueChange?.(rows);
1915
+ };
1916
+ const remove = (index) => {
1917
+ const rows = values.filter((_, i) => i !== index);
1918
+ onValueChange?.(rows.length > 0 ? rows : [""]);
1919
+ };
1920
+ const add = () => {
1921
+ onValueChange?.([...values, ""]);
1922
+ };
1923
+ const canRemove = values.length > Math.max(min, 1);
1924
+ const canAdd = max === void 0 || values.length < max;
1925
+ return /* @__PURE__ */ jsxs("div", {
1926
+ ...rest,
1927
+ "data-scope": "dynamic-input",
1928
+ "data-part": "root",
1929
+ children: [values.map((value, index) => /* @__PURE__ */ jsxs("div", {
1930
+ "data-scope": "dynamic-input",
1931
+ "data-part": "row",
1932
+ children: [/* @__PURE__ */ jsx(Input, {
1933
+ value,
1934
+ placeholder,
1935
+ disabled,
1936
+ invalid,
1937
+ onValueChange: (next) => update(index, next)
1938
+ }), /* @__PURE__ */ jsx(Button, {
1939
+ variant: "ghost",
1940
+ square: true,
1941
+ disabled: disabled || !canRemove,
1942
+ "aria-label": `Remove entry ${index + 1}`,
1943
+ onClick: () => remove(index),
1944
+ children: crossIcon()
1945
+ })]
1946
+ }, index)), /* @__PURE__ */ jsx("div", {
1947
+ "data-scope": "dynamic-input",
1948
+ "data-part": "add",
1949
+ children: /* @__PURE__ */ jsxs(Button, {
1950
+ variant: "ghost",
1951
+ disabled: disabled || !canAdd,
1952
+ onClick: () => add(),
1953
+ children: ["+", addLabel]
1954
+ })
1955
+ })]
1956
+ });
1957
+ }
1958
+ /** The one glyph a remove seal needs: a single crossing stroke. */
1959
+ function crossIcon() {
1960
+ return /* @__PURE__ */ jsx("svg", {
1961
+ width: 16,
1962
+ height: 16,
1963
+ viewBox: "0 0 24 24",
1964
+ fill: "none",
1965
+ stroke: "currentColor",
1966
+ strokeWidth: 1.75,
1967
+ strokeLinecap: "round",
1968
+ strokeLinejoin: "round",
1969
+ "aria-hidden": "true",
1970
+ children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" })
1971
+ });
1972
+ }
1973
+ injectComponentStyle("dynamic-input");
1974
+ //#endregion
745
1975
  //#region src/components/editable/index.tsx
746
1976
  /** Ark's Editable, dressed in the paper-and-ink system: bare ink while
747
1977
  * reading, the full field recipe while editing. The API is Ark's own —
@@ -750,11 +1980,27 @@ injectComponentStyle("drawer");
750
1980
  const Editable = Editable$1;
751
1981
  injectComponentStyle("editable");
752
1982
  //#endregion
1983
+ //#region src/components/ellipsis/index.tsx
1984
+ function Ellipsis({ lines = 1, ...rest }) {
1985
+ const multiline = lines > 1;
1986
+ return /* @__PURE__ */ jsx("span", {
1987
+ ...rest,
1988
+ style: multiline ? {
1989
+ ...rest.style,
1990
+ "--bs-ellipsis-lines": String(lines)
1991
+ } : rest.style,
1992
+ "data-scope": "ellipsis",
1993
+ "data-part": "root",
1994
+ "data-multiline": multiline ? "" : void 0
1995
+ });
1996
+ }
1997
+ injectComponentStyle("ellipsis");
1998
+ //#endregion
753
1999
  //#region src/components/empty/index.tsx
754
2000
  /** An empty state: the page holds its breath. Root centers the column,
755
2001
  * Visual carries the mark, Title and Description carry the ink, Actions
756
2002
  * the way out. Any subset composes. */
757
- function part$5(name, tag) {
2003
+ function part$7(name, tag) {
758
2004
  const Tag = tag;
759
2005
  const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
760
2006
  ...rest,
@@ -765,17 +2011,17 @@ function part$5(name, tag) {
765
2011
  Component.displayName = "Empty" + name;
766
2012
  return Component;
767
2013
  }
768
- const Root$4 = part$5("Root", "div");
769
- const Visual = part$5("Visual", "div");
770
- const Title$1 = part$5("Title", "h3");
771
- const Description$2 = part$5("Description", "p");
772
- const Actions$1 = part$5("Actions", "div");
773
- const Empty = Object.assign(Root$4, {
774
- Root: Root$4,
2014
+ const Root$8 = part$7("Root", "div");
2015
+ const Visual = part$7("Visual", "div");
2016
+ const Title$1 = part$7("Title", "h3");
2017
+ const Description$2 = part$7("Description", "p");
2018
+ const Actions$2 = part$7("Actions", "div");
2019
+ const Empty = Object.assign(Root$8, {
2020
+ Root: Root$8,
775
2021
  Visual,
776
2022
  Title: Title$1,
777
2023
  Description: Description$2,
778
- Actions: Actions$1
2024
+ Actions: Actions$2
779
2025
  });
780
2026
  injectComponentStyle("empty");
781
2027
  //#endregion
@@ -796,6 +2042,85 @@ injectComponentStyle("fieldset");
796
2042
  const FileUpload = FileUpload$1;
797
2043
  injectComponentStyle("file-upload");
798
2044
  //#endregion
2045
+ //#region src/components/float-button/index.tsx
2046
+ /** Context for {@link FloatButtonContextValue}, exported so a custom
2047
+ * part can read the group's state. */
2048
+ const FLOAT_BUTTON_CONTEXT = createContext(null);
2049
+ /**
2050
+ * A floating action and its fanned-out alternatives — FAB and speed
2051
+ * dial in one family. The Root moors the group at a page corner and
2052
+ * holds the openness (mirrored from `open` when controlled); the
2053
+ * Trigger flips it; each Item is a round action with its name surfacing
2054
+ * beside it while the group is open. The buttons are the shared recipe
2055
+ * (vessels here: round); this family owns only the mooring, the fan
2056
+ * and the fold.
2057
+ */
2058
+ function Root$7({ open: openProp, placement = "bottom-end", onOpenChange, children, ...rest }) {
2059
+ const [inner, setInner] = useState(false);
2060
+ const open = openProp !== void 0 ? openProp : inner;
2061
+ const setOpen = (value) => {
2062
+ setInner(value);
2063
+ onOpenChange?.(value);
2064
+ };
2065
+ return /* @__PURE__ */ jsx(FLOAT_BUTTON_CONTEXT.Provider, {
2066
+ value: {
2067
+ open,
2068
+ toggle: () => setOpen(!open),
2069
+ close: () => setOpen(false)
2070
+ },
2071
+ children: /* @__PURE__ */ jsx("div", {
2072
+ ...rest,
2073
+ "data-scope": "float-button",
2074
+ "data-part": "root",
2075
+ "data-placement": placement,
2076
+ "data-state": open ? "open" : "closed",
2077
+ children
2078
+ })
2079
+ });
2080
+ }
2081
+ function Trigger({ label, children }) {
2082
+ const context = useContext(FLOAT_BUTTON_CONTEXT);
2083
+ return /* @__PURE__ */ jsx(Button, {
2084
+ variant: "solid",
2085
+ square: true,
2086
+ size: "lg",
2087
+ onClick: () => context?.toggle(),
2088
+ "aria-label": label || "Actions",
2089
+ "aria-expanded": context ? context.open : false,
2090
+ children
2091
+ });
2092
+ }
2093
+ function Item$2({ label, disabled = false, onClick, children }) {
2094
+ const context = useContext(FLOAT_BUTTON_CONTEXT);
2095
+ return /* @__PURE__ */ jsxs("div", {
2096
+ "data-scope": "float-button",
2097
+ "data-part": "item",
2098
+ children: [/* @__PURE__ */ jsx(Button, {
2099
+ variant: "outline",
2100
+ square: true,
2101
+ size: "md",
2102
+ disabled,
2103
+ "aria-label": label,
2104
+ onClick: () => {
2105
+ onClick?.();
2106
+ context?.close();
2107
+ },
2108
+ children
2109
+ }), /* @__PURE__ */ jsx("span", {
2110
+ "data-scope": "float-button",
2111
+ "data-part": "item-label",
2112
+ "aria-hidden": "true",
2113
+ children: label
2114
+ })]
2115
+ });
2116
+ }
2117
+ const FloatButton = Object.assign(Root$7, {
2118
+ Root: Root$7,
2119
+ Trigger,
2120
+ Item: Item$2
2121
+ });
2122
+ injectComponentStyle("float-button");
2123
+ //#endregion
799
2124
  //#region src/components/floating-panel/index.tsx
800
2125
  /** Ark's FloatingPanel, dressed in the paper-and-ink system: the shared
801
2126
  * popup vessel let loose — a draggable, resizable sheet whose header is the
@@ -805,6 +2130,134 @@ injectComponentStyle("file-upload");
805
2130
  const FloatingPanel = FloatingPanel$1;
806
2131
  injectComponentStyle("floating-panel");
807
2132
  //#endregion
2133
+ //#region src/components/form/index.tsx
2134
+ const FormContext = createContext(null);
2135
+ /** Walk a Standard Schema issue's path back to the dotted name the
2136
+ * matching FormField declared. */
2137
+ function issueName(issue) {
2138
+ return (issue.path ?? []).map((segment) => typeof segment === "object" ? segment.key : segment).join(".");
2139
+ }
2140
+ /**
2141
+ * The validation scheduler: one error map, a submit that validates
2142
+ * before it fires, live re-validation on the events the `validateOn`
2143
+ * prop names, and the imperative handle. The schema is any Standard
2144
+ * Schema (valibot, zod, arktype, …) — none are bundled; a `validate`
2145
+ * function composes with it for the cases schemas can't express. Errors
2146
+ * reach their field by name, through FormField.
2147
+ */
2148
+ function Form({ state, schema, validate, validateOn = [
2149
+ "input",
2150
+ "change",
2151
+ "blur"
2152
+ ], disabled = false, onSubmit, onError, ref, children, ...rest }) {
2153
+ const [errors, setErrors] = useState(() => /* @__PURE__ */ new Map());
2154
+ const run = useRef(0);
2155
+ async function runValidate() {
2156
+ const seq = ++run.current;
2157
+ const next = /* @__PURE__ */ new Map();
2158
+ if (validate) for (const error of await validate(state)) next.set(error.name, error.message);
2159
+ if (schema) {
2160
+ const result = await schema["~standard"].validate(state);
2161
+ for (const issue of result.issues ?? []) {
2162
+ const name = issueName(issue);
2163
+ if (!next.has(name)) next.set(name, issue.message);
2164
+ }
2165
+ }
2166
+ if (seq === run.current) setErrors(next);
2167
+ return next;
2168
+ }
2169
+ function clear(name) {
2170
+ if (!name) {
2171
+ setErrors(/* @__PURE__ */ new Map());
2172
+ return;
2173
+ }
2174
+ setErrors((prev) => {
2175
+ const next = new Map(prev);
2176
+ for (const key of next.keys()) if (typeof name === "string" ? key === name : name.test(key)) next.delete(key);
2177
+ return next;
2178
+ });
2179
+ }
2180
+ function setErrorsFrom(list) {
2181
+ setErrors(new Map(list.map((error) => [error.name, error.message])));
2182
+ }
2183
+ function getErrors(name) {
2184
+ const all = [...errors].map(([entry, message]) => ({
2185
+ name: entry,
2186
+ message
2187
+ }));
2188
+ if (!name) return all;
2189
+ return all.filter((error) => typeof name === "string" ? error.name === name : name.test(error.name));
2190
+ }
2191
+ useImperativeHandle(ref, () => ({
2192
+ validate: runValidate,
2193
+ clear,
2194
+ setErrors: setErrorsFrom,
2195
+ getErrors,
2196
+ errors
2197
+ }));
2198
+ function handleSubmit(event) {
2199
+ event.preventDefault();
2200
+ runValidate().then((map) => {
2201
+ if (map.size === 0) {
2202
+ onSubmit?.(state);
2203
+ return;
2204
+ }
2205
+ onError?.([...map].map(([name, message]) => ({
2206
+ name,
2207
+ message
2208
+ })));
2209
+ });
2210
+ }
2211
+ const timer = useRef(void 0);
2212
+ function onDelegated(kind, event) {
2213
+ if (!validateOn.includes(kind)) return;
2214
+ const target = event.target;
2215
+ if (!target || !("value" in target)) return;
2216
+ clearTimeout(timer.current);
2217
+ timer.current = setTimeout(() => void runValidate(), kind === "blur" ? 0 : 300);
2218
+ }
2219
+ return /* @__PURE__ */ jsx(FormContext.Provider, {
2220
+ value: {
2221
+ errors,
2222
+ disabled
2223
+ },
2224
+ children: /* @__PURE__ */ jsx("form", {
2225
+ ...rest,
2226
+ "data-scope": "form",
2227
+ "data-part": "root",
2228
+ noValidate: true,
2229
+ onSubmit: handleSubmit,
2230
+ onInput: (event) => onDelegated("input", event),
2231
+ onChange: (event) => onDelegated("change", event),
2232
+ onBlurCapture: (event) => onDelegated("blur", event),
2233
+ children
2234
+ })
2235
+ });
2236
+ }
2237
+ function FormField({ name, label, hint, required = false, invalid = false, disabled = false, children, ...rest }) {
2238
+ const form = useContext(FormContext);
2239
+ const formError = name ? form?.errors.get(name) : void 0;
2240
+ const isInvalid = invalid || formError != null;
2241
+ const isDisabled = disabled || (form?.disabled ?? false);
2242
+ return /* @__PURE__ */ jsx("div", {
2243
+ ...rest,
2244
+ "data-form-field": name,
2245
+ children: /* @__PURE__ */ jsxs(Field$1.Root, {
2246
+ invalid: isInvalid,
2247
+ required,
2248
+ disabled: isDisabled,
2249
+ children: [
2250
+ label ? /* @__PURE__ */ jsx(Field$1.Label, { children: label }) : null,
2251
+ children,
2252
+ hint && formError == null ? /* @__PURE__ */ jsx(Field$1.HelperText, { children: hint }) : null,
2253
+ formError != null ? /* @__PURE__ */ jsx(Field$1.ErrorText, { children: formError }) : null
2254
+ ]
2255
+ })
2256
+ });
2257
+ }
2258
+ injectComponentStyle("form");
2259
+ injectComponentStyle("field");
2260
+ //#endregion
808
2261
  //#region src/components/format/index.tsx
809
2262
  /** Ark's Format, dressed in the paper-and-ink system: Intl formatting for
810
2263
  * numbers, currency, bytes and relative time. The components render bare
@@ -823,6 +2276,34 @@ const Format = Format$1;
823
2276
  * to hook. The API is Ark's own. */
824
2277
  const Frame = Frame$1;
825
2278
  //#endregion
2279
+ //#region src/components/grid/index.tsx
2280
+ /** The same named steps of the space ramp the stack uses — one
2281
+ * vocabulary of distance across the layout primitives. */
2282
+ const gapVars$2 = {
2283
+ none: "0",
2284
+ xs: "var(--bs-gap-xs)",
2285
+ sm: "var(--bs-gap-sm)",
2286
+ md: "var(--bs-gap-md)",
2287
+ lg: "var(--bs-gap-lg)",
2288
+ xl: "var(--bs-gap-xl)"
2289
+ };
2290
+ function Grid({ columns = 12, gap = "md", minChildWidth, ...rest }) {
2291
+ const style = {
2292
+ ...rest.style,
2293
+ "--bs-grid-columns": String(columns),
2294
+ "--bs-grid-gap": gapVars$2[gap] ?? gapVars$2.md,
2295
+ ...minChildWidth != null ? { "--bs-grid-min-child-width": minChildWidth } : {}
2296
+ };
2297
+ return /* @__PURE__ */ jsx("div", {
2298
+ ...rest,
2299
+ style,
2300
+ "data-scope": "grid",
2301
+ "data-part": "root",
2302
+ "data-autofill": minChildWidth != null ? "" : void 0
2303
+ });
2304
+ }
2305
+ injectComponentStyle("grid");
2306
+ //#endregion
826
2307
  //#region src/components/highlight/index.tsx
827
2308
  /** Ark's Highlight, dressed in the paper-and-ink system: query hits are
828
2309
  * strokes of pigment on the page — a quiet tint of the accent behind the
@@ -838,6 +2319,74 @@ injectComponentStyle("highlight");
838
2319
  const HoverCard = HoverCard$1;
839
2320
  injectComponentStyle("hover-card");
840
2321
  //#endregion
2322
+ //#region src/components/icon/index.tsx
2323
+ function Icon({ size = "inherit", label, children, ...rest }) {
2324
+ return /* @__PURE__ */ jsx("span", {
2325
+ ...rest,
2326
+ role: label != null ? "img" : void 0,
2327
+ "aria-label": label,
2328
+ "aria-hidden": label != null ? void 0 : "true",
2329
+ "data-scope": "icon",
2330
+ "data-part": "root",
2331
+ "data-size": size,
2332
+ children
2333
+ });
2334
+ }
2335
+ injectComponentStyle("icon");
2336
+ //#endregion
2337
+ //#region src/components/image/index.tsx
2338
+ /** The mark for a source that never arrived: a quiet mountain-and-sun,
2339
+ * drawn in the stylesheet's stroke and hidden from the reader. */
2340
+ function placeholderGlyph() {
2341
+ return /* @__PURE__ */ jsxs("svg", {
2342
+ viewBox: "0 0 24 24",
2343
+ fill: "none",
2344
+ stroke: "currentColor",
2345
+ "aria-hidden": "true",
2346
+ children: [
2347
+ /* @__PURE__ */ jsx("rect", {
2348
+ x: 3,
2349
+ y: 4,
2350
+ width: 18,
2351
+ height: 16,
2352
+ rx: 1.5
2353
+ }),
2354
+ /* @__PURE__ */ jsx("circle", {
2355
+ cx: 9,
2356
+ cy: 10,
2357
+ r: 1.5
2358
+ }),
2359
+ /* @__PURE__ */ jsx("path", { d: "m5.5 17.5 4.5-5 3 3.5 2.5-3 3 4.5" })
2360
+ ]
2361
+ });
2362
+ }
2363
+ function Image({ src, alt = "", fit = "cover", loading = "lazy", fallback, ...rest }) {
2364
+ const [state, setState] = useState("loading");
2365
+ useEffect(() => setState("loading"), [src]);
2366
+ return /* @__PURE__ */ jsxs("figure", {
2367
+ ...rest,
2368
+ "data-scope": "image",
2369
+ "data-part": "root",
2370
+ "data-state": state,
2371
+ "data-fit": fit,
2372
+ children: [/* @__PURE__ */ jsx("img", {
2373
+ "data-scope": "image",
2374
+ "data-part": "img",
2375
+ src,
2376
+ alt,
2377
+ loading,
2378
+ decoding: "async",
2379
+ onLoad: () => setState("loaded"),
2380
+ onError: () => setState("error")
2381
+ }), state === "error" ? /* @__PURE__ */ jsx("div", {
2382
+ "data-scope": "image",
2383
+ "data-part": "fallback",
2384
+ children: fallback ?? placeholderGlyph()
2385
+ }) : null]
2386
+ });
2387
+ }
2388
+ injectComponentStyle("image");
2389
+ //#endregion
841
2390
  //#region src/components/image-cropper/index.tsx
842
2391
  /** Ark's ImageCropper, dressed in the paper-and-ink system: a rounded vessel
843
2392
  * holding the photograph, one lit selection window framed by light hairlines.
@@ -846,6 +2395,136 @@ injectComponentStyle("hover-card");
846
2395
  const ImageCropper = ImageCropper$1;
847
2396
  injectComponentStyle("image-cropper");
848
2397
  //#endregion
2398
+ //#region src/components/image-viewer/index.tsx
2399
+ const MIN_SCALE = .5;
2400
+ const MAX_SCALE = 3;
2401
+ const SCALE_STEP = .25;
2402
+ /** The toolbar's line-drawn glyphs, fresh per render. */
2403
+ const TOOL_GLYPHS = {
2404
+ zoomIn: () => /* @__PURE__ */ jsxs(Fragment, { children: [
2405
+ /* @__PURE__ */ jsx("circle", {
2406
+ cx: 11,
2407
+ cy: 11,
2408
+ r: 7
2409
+ }),
2410
+ /* @__PURE__ */ jsx("path", { d: "m16.2 16.2 4.8 4.8" }),
2411
+ /* @__PURE__ */ jsx("path", { d: "M8 11h6" }),
2412
+ /* @__PURE__ */ jsx("path", { d: "M11 8v6" })
2413
+ ] }),
2414
+ zoomOut: () => /* @__PURE__ */ jsxs(Fragment, { children: [
2415
+ /* @__PURE__ */ jsx("circle", {
2416
+ cx: 11,
2417
+ cy: 11,
2418
+ r: 7
2419
+ }),
2420
+ /* @__PURE__ */ jsx("path", { d: "m16.2 16.2 4.8 4.8" }),
2421
+ /* @__PURE__ */ jsx("path", { d: "M8 11h6" })
2422
+ ] }),
2423
+ rotate: () => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M20.49 12A8.5 8.5 0 1 1 18 6.06" }), /* @__PURE__ */ jsx("path", { d: "M20.5 3.5v4h-4" })] }),
2424
+ close: () => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" }), /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" })] })
2425
+ };
2426
+ function ImageViewer({ src, alt = "", open, zoomable = true, onOpenChange }) {
2427
+ const [localOpen, setLocalOpen] = useState(false);
2428
+ const [scale, setScale] = useState(1);
2429
+ const [rotation, setRotation] = useState(0);
2430
+ const isOpen = open ?? localOpen;
2431
+ const setOpen = (value) => {
2432
+ if (open === void 0) setLocalOpen(value);
2433
+ onOpenChange?.(value);
2434
+ };
2435
+ useEffect(() => {
2436
+ if (isOpen) {
2437
+ setScale(1);
2438
+ setRotation(0);
2439
+ }
2440
+ }, [isOpen]);
2441
+ function zoom(step) {
2442
+ setScale((current) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, current + step)));
2443
+ }
2444
+ function rotate() {
2445
+ setRotation((current) => (current + 90) % 360);
2446
+ }
2447
+ function toolButton(label, glyph, onClick) {
2448
+ return /* @__PURE__ */ jsx(Button, {
2449
+ variant: "ghost",
2450
+ square: true,
2451
+ size: "lg",
2452
+ "aria-label": label,
2453
+ onClick,
2454
+ children: /* @__PURE__ */ jsx("svg", {
2455
+ viewBox: "0 0 24 24",
2456
+ fill: "none",
2457
+ stroke: "currentColor",
2458
+ strokeWidth: 1.5,
2459
+ strokeLinecap: "round",
2460
+ strokeLinejoin: "round",
2461
+ "aria-hidden": "true",
2462
+ children: glyph()
2463
+ })
2464
+ });
2465
+ }
2466
+ return /* @__PURE__ */ jsx(Dialog$1.Root, {
2467
+ open: isOpen,
2468
+ onOpenChange: (details) => setOpen(details.open),
2469
+ lazyMount: true,
2470
+ unmountOnExit: true,
2471
+ children: /* @__PURE__ */ jsxs(Portal, { children: [/* @__PURE__ */ jsx(Dialog$1.Backdrop, { className: "bs-image-viewer-backdrop" }), /* @__PURE__ */ jsx(Dialog$1.Positioner, {
2472
+ className: "bs-image-viewer-positioner",
2473
+ children: /* @__PURE__ */ jsxs(Dialog$1.Content, {
2474
+ className: "bs-image-viewer-content",
2475
+ "aria-label": alt || "Image preview",
2476
+ onClick: (event) => {
2477
+ if (event.target === event.currentTarget) setOpen(false);
2478
+ },
2479
+ children: [/* @__PURE__ */ jsx("img", {
2480
+ "data-scope": "image-viewer",
2481
+ "data-part": "viewport",
2482
+ src,
2483
+ alt,
2484
+ style: { transform: `scale(${scale}) rotate(${rotation}deg)` }
2485
+ }), /* @__PURE__ */ jsx("div", {
2486
+ "data-scope": "image-viewer",
2487
+ "data-part": "toolbar",
2488
+ children: /* @__PURE__ */ jsxs(ButtonGroup, { children: [
2489
+ zoomable ? [toolButton("Zoom in", TOOL_GLYPHS.zoomIn, () => zoom(SCALE_STEP)), toolButton("Zoom out", TOOL_GLYPHS.zoomOut, () => zoom(-.25))] : null,
2490
+ toolButton("Rotate 90 degrees", TOOL_GLYPHS.rotate, rotate),
2491
+ toolButton("Close", TOOL_GLYPHS.close, () => setOpen(false))
2492
+ ] })
2493
+ })]
2494
+ })
2495
+ })] })
2496
+ });
2497
+ }
2498
+ injectComponentStyle("image-viewer");
2499
+ //#endregion
2500
+ //#region src/components/input-group/index.tsx
2501
+ function part$6(name) {
2502
+ const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx("div", {
2503
+ ...rest,
2504
+ "data-scope": "input-group",
2505
+ "data-part": name.toLowerCase(),
2506
+ children
2507
+ });
2508
+ Component.displayName = "InputGroup" + name;
2509
+ return Component;
2510
+ }
2511
+ /**
2512
+ * Merged controls: attachments and the entry fused into one seal. The
2513
+ * Root draws the single hairline and carries the group's focus halo;
2514
+ * the Addon is a recessed cell for the reader's fixed words — a scheme,
2515
+ * a unit, a quiet button — placed before or after the entry. Put our
2516
+ * Input (or Textarea) inside and its own border and halo step aside in
2517
+ * favor of the group's; the stylesheet does the merging, the wrapper
2518
+ * adds no visuals of its own.
2519
+ */
2520
+ const Root$6 = part$6("Root");
2521
+ const Addon = part$6("Addon");
2522
+ const InputGroup = Object.assign(Root$6, {
2523
+ Root: Root$6,
2524
+ Addon
2525
+ });
2526
+ injectComponentStyle("input-group");
2527
+ //#endregion
849
2528
  //#region src/components/json-tree-view/index.tsx
850
2529
  /** Ark's JsonTreeView, dressed in the paper-and-ink system: the tree-view
851
2530
  * recipes re-scoped, with value nodes reading as tabular data. The API is
@@ -864,6 +2543,135 @@ function Kbd({ children, ...rest }) {
864
2543
  }
865
2544
  injectComponentStyle("kbd");
866
2545
  //#endregion
2546
+ //#region src/components/layout/index.tsx
2547
+ const LAYOUT_CONTEXT = createContext(null);
2548
+ /**
2549
+ * The application skeleton: Root, Header, Sider, Content, Footer —
2550
+ * the admin arrangement dressed in the paper-and-ink surfaces. The
2551
+ * root is a full-height grid; a declared `sider` turns the columns
2552
+ * around to meet whichever edge the rail stands on.
2553
+ */
2554
+ function Root$5({ sider, children, ...rest }) {
2555
+ return /* @__PURE__ */ jsx(LAYOUT_CONTEXT.Provider, {
2556
+ value: { siderPlacement: () => sider },
2557
+ children: /* @__PURE__ */ jsx("div", {
2558
+ ...rest,
2559
+ "data-scope": "layout",
2560
+ "data-part": "root",
2561
+ "data-sider": sider,
2562
+ children
2563
+ })
2564
+ });
2565
+ }
2566
+ /** A semantic block of the skeleton — header, footer, and the flow's
2567
+ * main content each land on their native element and their grid area. */
2568
+ function region(name, tag) {
2569
+ const Tag = tag;
2570
+ const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
2571
+ ...rest,
2572
+ "data-scope": "layout",
2573
+ "data-part": name.toLowerCase(),
2574
+ children
2575
+ });
2576
+ Component.displayName = "Layout" + name;
2577
+ return Component;
2578
+ }
2579
+ const Header = region("Header", "header");
2580
+ const Content$2 = region("Content", "main");
2581
+ const Footer = region("Footer", "footer");
2582
+ /** One step of the arrow-key resize, in px. */
2583
+ const RESIZE_STEP = 16;
2584
+ function Sider({ collapsed = false, width: widthProp, collapsedWidth = "3.5rem", resizable = false, minWidth = "12rem", maxWidth = "24rem", style, onCollapsedChange, onWidthChange, children, ...rest }) {
2585
+ const layout = useContext(LAYOUT_CONTEXT);
2586
+ const rail = useRef(null);
2587
+ const [dragging, setDragging] = useState(false);
2588
+ const [innerWidth, setInnerWidth] = useState(widthProp ?? "16rem");
2589
+ const width = widthProp !== void 0 ? widthProp : innerWidth;
2590
+ const prevCollapsed = useRef(collapsed);
2591
+ useEffect(() => {
2592
+ if (prevCollapsed.current === collapsed) return;
2593
+ prevCollapsed.current = collapsed;
2594
+ onCollapsedChange?.(collapsed);
2595
+ }, [collapsed, onCollapsedChange]);
2596
+ const innerWidthRef = useRef(innerWidth);
2597
+ innerWidthRef.current = innerWidth;
2598
+ useEffect(() => {
2599
+ if (widthProp === void 0 || widthProp === innerWidthRef.current) return;
2600
+ setInnerWidth(widthProp);
2601
+ onWidthChange?.(widthProp);
2602
+ }, [widthProp, onWidthChange]);
2603
+ const applyWidth = (next) => {
2604
+ setInnerWidth(next);
2605
+ onWidthChange?.(next);
2606
+ };
2607
+ const dragStart = useRef({
2608
+ x: 0,
2609
+ width: 0
2610
+ });
2611
+ const onPointerDown = (event) => {
2612
+ setDragging(true);
2613
+ dragStart.current = {
2614
+ x: event.clientX,
2615
+ width: rail.current ? rail.current.getBoundingClientRect().width : 0
2616
+ };
2617
+ event.currentTarget.setPointerCapture(event.pointerId);
2618
+ };
2619
+ const onPointerMove = (event) => {
2620
+ if (!dragging) return;
2621
+ const towardFlow = (layout?.siderPlacement() ?? "start") === "end" ? -1 : 1;
2622
+ applyWidth(`${Math.round(dragStart.current.width + towardFlow * (event.clientX - dragStart.current.x))}px`);
2623
+ };
2624
+ const onPointerUp = (event) => {
2625
+ setDragging(false);
2626
+ event.currentTarget.releasePointerCapture(event.pointerId);
2627
+ };
2628
+ const onKeyDown = (event) => {
2629
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
2630
+ event.preventDefault();
2631
+ const widen = (layout?.siderPlacement() ?? "start") === "end" ? event.key === "ArrowLeft" : event.key === "ArrowRight";
2632
+ const current = rail.current ? rail.current.getBoundingClientRect().width : 0;
2633
+ applyWidth(`${Math.round(current + (widen ? RESIZE_STEP : -16))}px`);
2634
+ };
2635
+ const handle = resizable && !collapsed ? /* @__PURE__ */ jsx("div", {
2636
+ "data-scope": "layout",
2637
+ "data-part": "sider-resize",
2638
+ "data-dragging": dragging ? "" : void 0,
2639
+ role: "separator",
2640
+ "aria-orientation": "vertical",
2641
+ tabIndex: 0,
2642
+ "aria-label": "Resize sidebar",
2643
+ "aria-valuenow": Math.round(rail.current ? rail.current.getBoundingClientRect().width : 0),
2644
+ onPointerDown,
2645
+ onPointerMove,
2646
+ onPointerUp,
2647
+ onKeyDown
2648
+ }) : null;
2649
+ return /* @__PURE__ */ jsxs("aside", {
2650
+ ...rest,
2651
+ ref: rail,
2652
+ style: {
2653
+ ...style,
2654
+ "--bs-layout-sider-width": collapsed ? collapsedWidth : width,
2655
+ "--bs-layout-sider-min": collapsed ? collapsedWidth : minWidth,
2656
+ "--bs-layout-sider-max": collapsed ? collapsedWidth : maxWidth
2657
+ },
2658
+ "data-scope": "layout",
2659
+ "data-part": "sider",
2660
+ "data-collapsed": collapsed ? "" : void 0,
2661
+ "data-dragging": dragging ? "" : void 0,
2662
+ "data-resizable": resizable ? "" : void 0,
2663
+ children: [typeof children === "function" ? children({ collapsed }) : children, handle]
2664
+ });
2665
+ }
2666
+ const Layout = Object.assign(Root$5, {
2667
+ Root: Root$5,
2668
+ Header,
2669
+ Sider,
2670
+ Content: Content$2,
2671
+ Footer
2672
+ });
2673
+ injectComponentStyle("layout");
2674
+ //#endregion
867
2675
  //#region src/components/link/index.tsx
868
2676
  /** A link is ink in the accent's voice: quiet at rest, deepening under
869
2677
  * the hand, the halo at focus. The underline follows the prose —
@@ -879,6 +2687,69 @@ function Link({ underline = "hover", children, ...rest }) {
879
2687
  }
880
2688
  injectComponentStyle("link");
881
2689
  //#endregion
2690
+ //#region src/components/list/index.tsx
2691
+ /** A ledger of rows: Root is the list, Item one row, Leading the mark
2692
+ * before the words, Content the title and its quiet echo, Actions the
2693
+ * way out. The bordered variant draws the hairlines; the hoverable
2694
+ * variant gives every row the wash — and any row the caller makes
2695
+ * clickable (role="button") answers the pointer on its own. */
2696
+ function part$5(name, tag) {
2697
+ const Tag = tag;
2698
+ function Component({ children, ...rest }) {
2699
+ return /* @__PURE__ */ jsx(Tag, {
2700
+ ...rest,
2701
+ "data-scope": "list",
2702
+ "data-part": name.toLowerCase(),
2703
+ children
2704
+ });
2705
+ }
2706
+ Component.displayName = "List" + name;
2707
+ return Component;
2708
+ }
2709
+ function Root$4({ bordered = false, hoverable = false, children, ...rest }) {
2710
+ return /* @__PURE__ */ jsx("ul", {
2711
+ ...rest,
2712
+ "data-scope": "list",
2713
+ "data-part": "root",
2714
+ "data-bordered": bordered || void 0,
2715
+ "data-hoverable": hoverable || void 0,
2716
+ children
2717
+ });
2718
+ }
2719
+ Root$4.displayName = "ListRoot";
2720
+ const Item$1 = part$5("Item", "li");
2721
+ const Leading = part$5("Leading", "div");
2722
+ const Actions$1 = part$5("Actions", "div");
2723
+ function Content$1({ title, description, children, ...rest }) {
2724
+ return /* @__PURE__ */ jsxs("div", {
2725
+ ...rest,
2726
+ "data-scope": "list",
2727
+ "data-part": "content",
2728
+ children: [
2729
+ title ? /* @__PURE__ */ jsx("div", {
2730
+ "data-scope": "list",
2731
+ "data-part": "title",
2732
+ children: title
2733
+ }) : null,
2734
+ description ? /* @__PURE__ */ jsx("div", {
2735
+ "data-scope": "list",
2736
+ "data-part": "description",
2737
+ children: description
2738
+ }) : null,
2739
+ children
2740
+ ]
2741
+ });
2742
+ }
2743
+ Content$1.displayName = "ListContent";
2744
+ const List = Object.assign(Root$4, {
2745
+ Root: Root$4,
2746
+ Item: Item$1,
2747
+ Leading,
2748
+ Content: Content$1,
2749
+ Actions: Actions$1
2750
+ });
2751
+ injectComponentStyle("list");
2752
+ //#endregion
882
2753
  //#region src/components/listbox/index.tsx
883
2754
  /** Ark's Listbox, dressed in the paper-and-ink system: quiet rows of ink
884
2755
  * where the checked row alone takes the flat primary fill. The API is
@@ -895,6 +2766,32 @@ injectComponentStyle("listbox");
895
2766
  const Marquee = Marquee$1;
896
2767
  injectComponentStyle("marquee");
897
2768
  //#endregion
2769
+ //#region src/components/masonry/index.tsx
2770
+ /** The same named steps of the space ramp the stack uses — one
2771
+ * vocabulary of distance across the layout primitives. */
2772
+ const gapVars$1 = {
2773
+ none: "0",
2774
+ xs: "var(--bs-gap-xs)",
2775
+ sm: "var(--bs-gap-sm)",
2776
+ md: "var(--bs-gap-md)",
2777
+ lg: "var(--bs-gap-lg)",
2778
+ xl: "var(--bs-gap-xl)"
2779
+ };
2780
+ function Masonry({ columns = 3, gap = "md", ...rest }) {
2781
+ const style = {
2782
+ ...rest.style,
2783
+ "--bs-masonry-columns": String(columns),
2784
+ "--bs-masonry-gap": gapVars$1[gap] ?? gapVars$1.md
2785
+ };
2786
+ return /* @__PURE__ */ jsx("div", {
2787
+ ...rest,
2788
+ style,
2789
+ "data-scope": "masonry",
2790
+ "data-part": "root"
2791
+ });
2792
+ }
2793
+ injectComponentStyle("masonry");
2794
+ //#endregion
898
2795
  //#region src/components/menu/index.tsx
899
2796
  /** Ark's Menu, dressed in the paper-and-ink system: a quiet paper vessel on
900
2797
  * elevation, hover as light on the row, checked items as the flat ink fill.
@@ -904,6 +2801,36 @@ injectComponentStyle("marquee");
904
2801
  const Menu = Menu$1;
905
2802
  injectComponentStyle("menu");
906
2803
  //#endregion
2804
+ //#region src/components/menubar/index.tsx
2805
+ function Menubar({ items, onSelect, children, ...rest }) {
2806
+ return /* @__PURE__ */ jsxs("div", {
2807
+ ...rest,
2808
+ "data-scope": "menubar",
2809
+ "data-part": "root",
2810
+ role: "menubar",
2811
+ children: [items.map((group) => /* @__PURE__ */ jsxs(Menu$1.Root, {
2812
+ positioning: { placement: "bottom-start" },
2813
+ children: [/* @__PURE__ */ jsx(Menu$1.Trigger, {
2814
+ asChild: true,
2815
+ children: /* @__PURE__ */ jsx("button", {
2816
+ type: "button",
2817
+ "data-scope": "menubar",
2818
+ "data-part": "trigger",
2819
+ children: group.label
2820
+ })
2821
+ }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, { children: /* @__PURE__ */ jsx(Menu$1.Content, { children: group.items.map((entry) => /* @__PURE__ */ jsx(Menu$1.Item, {
2822
+ value: entry.value,
2823
+ disabled: entry.disabled,
2824
+ "data-danger": entry.danger ? "" : void 0,
2825
+ onSelect: () => onSelect?.(entry.value),
2826
+ children: /* @__PURE__ */ jsx(Menu$1.ItemText, { children: entry.label })
2827
+ }, entry.value)) }) }) })]
2828
+ }, group.label)), children]
2829
+ });
2830
+ }
2831
+ injectComponentStyle("menubar");
2832
+ injectComponentStyle("menu");
2833
+ //#endregion
907
2834
  //#region src/components/meter/index.tsx
908
2835
  function part$4(name, tag) {
909
2836
  const Tag = tag;
@@ -918,7 +2845,7 @@ function part$4(name, tag) {
918
2845
  }
919
2846
  const Label$2 = part$4("Label", "span");
920
2847
  const ValueText = part$4("ValueText", "span");
921
- const Track = ({ children, ...rest }) => /* @__PURE__ */ jsx("span", {
2848
+ const Track = ({ ...rest }) => /* @__PURE__ */ jsx("span", {
922
2849
  ...rest,
923
2850
  "data-scope": "meter",
924
2851
  "data-part": "track",
@@ -1000,6 +2927,43 @@ injectComponentStyle("password-input");
1000
2927
  const PinInput = PinInput$1;
1001
2928
  injectComponentStyle("pin-input");
1002
2929
  //#endregion
2930
+ //#region src/components/popconfirm/index.tsx
2931
+ function Popconfirm({ message, confirmText = "Confirm", cancelText = "Cancel", children, onConfirm, onCancel }) {
2932
+ const [open, setOpen] = useState(false);
2933
+ function settle(confirmed) {
2934
+ setOpen(false);
2935
+ (confirmed ? onConfirm : onCancel)?.();
2936
+ }
2937
+ return /* @__PURE__ */ jsxs(Popover$1.Root, {
2938
+ open,
2939
+ onOpenChange: (details) => setOpen(details.open),
2940
+ positioning: { placement: "top" },
2941
+ children: [/* @__PURE__ */ jsx(Popover$1.Trigger, {
2942
+ asChild: true,
2943
+ children
2944
+ }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsxs(Popover$1.Content, {
2945
+ className: "bs-popconfirm",
2946
+ children: [/* @__PURE__ */ jsx("p", {
2947
+ "data-part": "message",
2948
+ children: message
2949
+ }), /* @__PURE__ */ jsxs("div", {
2950
+ "data-part": "actions",
2951
+ children: [/* @__PURE__ */ jsx(Button, {
2952
+ variant: "ghost",
2953
+ size: "sm",
2954
+ onClick: () => settle(false),
2955
+ children: cancelText
2956
+ }), /* @__PURE__ */ jsx(Button, {
2957
+ size: "sm",
2958
+ onClick: () => settle(true),
2959
+ children: confirmText
2960
+ })]
2961
+ })]
2962
+ }) }) })]
2963
+ });
2964
+ }
2965
+ injectComponentStyle("popconfirm");
2966
+ //#endregion
1003
2967
  //#region src/components/popover/index.tsx
1004
2968
  /** Ark's Popover, dressed in the paper-and-ink system: a paper vessel that
1005
2969
  * dissolves in on elevation, anchored to its trigger by a whisker arrow. The
@@ -1016,6 +2980,59 @@ injectComponentStyle("popover");
1016
2980
  const Progress = Progress$1;
1017
2981
  injectComponentStyle("progress");
1018
2982
  //#endregion
2983
+ //#region src/components/progress-group/index.tsx
2984
+ function ProgressGroup({ segments, max, showLegend = true, ...rest }) {
2985
+ const total = max ?? segments.reduce((sum, segment) => sum + segment.value, 0);
2986
+ const share = (value) => total > 0 ? `${value / total * 100}%` : "0%";
2987
+ const nameOf = (segment, index) => segment.label ?? segment.pigment ?? `Segment ${index + 1}`;
2988
+ return /* @__PURE__ */ jsxs("div", {
2989
+ ...rest,
2990
+ "data-scope": "progress-group",
2991
+ "data-part": "root",
2992
+ children: [/* @__PURE__ */ jsx("div", {
2993
+ "data-scope": "progress-group",
2994
+ "data-part": "track",
2995
+ children: segments.map((segment, index) => /* @__PURE__ */ jsx("div", {
2996
+ "data-scope": "progress-group",
2997
+ "data-part": "segment",
2998
+ "data-pigment": segment.pigment,
2999
+ role: "progressbar",
3000
+ "aria-valuenow": segment.value,
3001
+ "aria-valuemin": 0,
3002
+ "aria-valuemax": total,
3003
+ "aria-label": nameOf(segment, index),
3004
+ style: { inlineSize: share(segment.value) }
3005
+ }, index))
3006
+ }), showLegend ? /* @__PURE__ */ jsx("div", {
3007
+ "data-scope": "progress-group",
3008
+ "data-part": "legend",
3009
+ children: segments.map((segment, index) => /* @__PURE__ */ jsxs("div", {
3010
+ "data-scope": "progress-group",
3011
+ "data-part": "legend-item",
3012
+ "data-pigment": segment.pigment,
3013
+ children: [
3014
+ /* @__PURE__ */ jsx("span", {
3015
+ "data-scope": "progress-group",
3016
+ "data-part": "swatch",
3017
+ "aria-hidden": "true"
3018
+ }),
3019
+ /* @__PURE__ */ jsx("span", {
3020
+ "data-scope": "progress-group",
3021
+ "data-part": "legend-label",
3022
+ children: nameOf(segment, index)
3023
+ }),
3024
+ /* @__PURE__ */ jsx("span", {
3025
+ "data-scope": "progress-group",
3026
+ "data-part": "legend-value",
3027
+ children: String(segment.value)
3028
+ })
3029
+ ]
3030
+ }, index))
3031
+ }) : null]
3032
+ });
3033
+ }
3034
+ injectComponentStyle("progress-group");
3035
+ //#endregion
1019
3036
  //#region src/components/qr-code/index.tsx
1020
3037
  /** Ark's QrCode, dressed in the paper-and-ink system: the pattern prints in
1021
3038
  * ink on the page, with an optional paper badge and a seal-cut download
@@ -1103,6 +3120,71 @@ injectComponentStyle("skeleton");
1103
3120
  const Slider = Slider$1;
1104
3121
  injectComponentStyle("slider");
1105
3122
  //#endregion
3123
+ //#region src/components/split-button/index.tsx
3124
+ /**
3125
+ * A primary action with its alternatives one seam away: the main button
3126
+ * fires `onClick`, the fitted arrow opens a paper vessel of the same
3127
+ * register whose entries report `onSelect` with their value. The arrow
3128
+ * is the menu machine's trigger grafting the button recipe — the
3129
+ * machine keeps the element (positioning, focus, `data-state`), the
3130
+ * button stylesheet's trigger register dresses it. The popup keeps the
3131
+ * menu parts untouched.
3132
+ */
3133
+ function SplitButton({ label, items = [], variant = "solid", tone = "ink", size = "md", disabled = false, onClick, onSelect, ...rest }) {
3134
+ return /* @__PURE__ */ jsxs("div", {
3135
+ ...rest,
3136
+ "data-scope": "split-button",
3137
+ "data-part": "root",
3138
+ children: [/* @__PURE__ */ jsx(Button, {
3139
+ variant,
3140
+ tone,
3141
+ size,
3142
+ disabled,
3143
+ onClick,
3144
+ children: label
3145
+ }), /* @__PURE__ */ jsxs(Menu$1.Root, {
3146
+ positioning: { placement: "bottom-end" },
3147
+ children: [/* @__PURE__ */ jsx(Menu$1.Trigger, {
3148
+ asChild: true,
3149
+ children: /* @__PURE__ */ jsx("button", {
3150
+ type: "button",
3151
+ disabled,
3152
+ "data-variant": variant,
3153
+ "data-tone": tone,
3154
+ "data-size": size,
3155
+ "data-square": "true",
3156
+ "data-motion": "ink-ripple lit",
3157
+ "aria-label": "More actions",
3158
+ children: chevronDown$1()
3159
+ })
3160
+ }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, { children: /* @__PURE__ */ jsx(Menu$1.Content, { children: items.map((entry) => /* @__PURE__ */ jsx(Menu$1.Item, {
3161
+ value: entry.value,
3162
+ disabled: entry.disabled,
3163
+ "data-danger": entry.danger ? "" : void 0,
3164
+ onSelect: () => onSelect?.(entry.value),
3165
+ children: /* @__PURE__ */ jsx(Menu$1.ItemText, { children: entry.label })
3166
+ }, entry.value)) }) }) })]
3167
+ })]
3168
+ });
3169
+ }
3170
+ /** One stroke pointing down: all a fitted dropdown arrow needs. */
3171
+ function chevronDown$1() {
3172
+ return /* @__PURE__ */ jsx("svg", {
3173
+ width: 16,
3174
+ height: 16,
3175
+ viewBox: "0 0 24 24",
3176
+ fill: "none",
3177
+ stroke: "currentColor",
3178
+ strokeWidth: 1.75,
3179
+ strokeLinecap: "round",
3180
+ strokeLinejoin: "round",
3181
+ "aria-hidden": "true",
3182
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
3183
+ });
3184
+ }
3185
+ injectComponentStyle("split-button");
3186
+ injectComponentStyle("menu");
3187
+ //#endregion
1106
3188
  //#region src/components/splitter/index.tsx
1107
3189
  /** Ark's Splitter, dressed in the paper-and-ink system: panels divide on
1108
3190
  * a hairline and a small paper-seal thumb answers the hand. The API is
@@ -1110,6 +3192,35 @@ injectComponentStyle("slider");
1110
3192
  const Splitter = Splitter$1;
1111
3193
  injectComponentStyle("splitter");
1112
3194
  //#endregion
3195
+ //#region src/components/stack/index.tsx
3196
+ /** Whitespace chosen by name: the named steps point at the space ramp so
3197
+ * siblings are held apart by one token, never by ad-hoc margins. */
3198
+ const gapVars = {
3199
+ none: "0",
3200
+ xs: "var(--bs-gap-xs)",
3201
+ sm: "var(--bs-gap-sm)",
3202
+ md: "var(--bs-gap-md)",
3203
+ lg: "var(--bs-gap-lg)",
3204
+ xl: "var(--bs-gap-xl)"
3205
+ };
3206
+ function Stack({ direction = "column", gap = "md", wrap = false, align, justify, ...rest }) {
3207
+ const style = {
3208
+ ...rest.style,
3209
+ "--bs-stack-gap": gapVars[gap] ?? gapVars.md,
3210
+ alignItems: align,
3211
+ justifyContent: justify,
3212
+ flexWrap: wrap ? "wrap" : void 0
3213
+ };
3214
+ return /* @__PURE__ */ jsx("div", {
3215
+ ...rest,
3216
+ style,
3217
+ "data-scope": "stack",
3218
+ "data-part": "root",
3219
+ "data-direction": direction
3220
+ });
3221
+ }
3222
+ injectComponentStyle("stack");
3223
+ //#endregion
1113
3224
  //#region src/components/page-header/index.tsx
1114
3225
  /** The page's face: an eyebrow whisper, a serif title, one line of
1115
3226
  * description, and the actions resting beside the title on the same
@@ -1255,6 +3366,36 @@ const TagsInput = TagsInput$1;
1255
3366
  injectComponentStyle("tags-input");
1256
3367
  //#endregion
1257
3368
  //#region src/components/table/index.tsx
3369
+ /** The pagination bar's arrows — thin chevrons for the row of seals. */
3370
+ function pageGlyph(direction) {
3371
+ return /* @__PURE__ */ jsx("svg", {
3372
+ viewBox: "0 0 24 24",
3373
+ width: 14,
3374
+ height: 14,
3375
+ fill: "none",
3376
+ stroke: "currentColor",
3377
+ strokeWidth: 2,
3378
+ strokeLinecap: "round",
3379
+ strokeLinejoin: "round",
3380
+ "aria-hidden": "true",
3381
+ children: /* @__PURE__ */ jsx("path", { d: direction === "start" ? "m15 18-6-6 6-6" : "m9 18 6-6-6-6" })
3382
+ });
3383
+ }
3384
+ /** The page-size select's pointing chevron. */
3385
+ function chevronDownGlyph() {
3386
+ return /* @__PURE__ */ jsx("svg", {
3387
+ viewBox: "0 0 24 24",
3388
+ width: 14,
3389
+ height: 14,
3390
+ fill: "none",
3391
+ stroke: "currentColor",
3392
+ strokeWidth: 2.5,
3393
+ strokeLinecap: "round",
3394
+ strokeLinejoin: "round",
3395
+ "aria-hidden": "true",
3396
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
3397
+ });
3398
+ }
1258
3399
  const SELECT_COL_ID = "__select";
1259
3400
  const SELECT_COL_WIDTH = 48;
1260
3401
  /** The list holding `id` and the index inside it, or null when absent. */
@@ -1334,7 +3475,7 @@ function SelectBox(props) {
1334
3475
  }
1335
3476
  function DataTable(rawProps) {
1336
3477
  const props = rawProps;
1337
- const { selectable = false, sortable = true, filterable = false, tree = false, virtual: virtualProp = false, rowHeight = 40, paginated = false, pageSize = 10, pageSizeOptions = [
3478
+ const { selectable = false, sortable = true, filterable = false, showToolbar = true, tree = false, virtual: virtualProp = false, rowHeight = 40, paginated = false, pageSize = 10, pageSizeOptions = [
1338
3479
  10,
1339
3480
  20,
1340
3481
  50
@@ -1702,7 +3843,7 @@ function DataTable(rawProps) {
1702
3843
  onDragLeave: canDrag ? onColDragLeave : void 0,
1703
3844
  onDrop: canDrag ? (e) => onColDrop(column, e) : void 0,
1704
3845
  onDragEnd: canDrag ? onColDragEnd : void 0,
1705
- children: [header.isPlaceholder ? null : /* @__PURE__ */ jsx(FlexRender, { header }), canFilter ? /* @__PURE__ */ jsx("input", {
3846
+ children: [header.isPlaceholder || header.column.columnDef.header === "" ? null : /* @__PURE__ */ jsx(FlexRender, { header }), canFilter ? /* @__PURE__ */ jsx("input", {
1706
3847
  type: "text",
1707
3848
  "data-scope": "table",
1708
3849
  "data-part": "header-filter",
@@ -1809,55 +3950,53 @@ function DataTable(rawProps) {
1809
3950
  const paginationBar = paginated ? (() => {
1810
3951
  const pagination = table.atoms.pagination.get();
1811
3952
  const rowCount = table.getRowCount();
1812
- const pageCount = table.getPageCount();
3953
+ const sizeItems = createListCollection({ items: pageSizeOptions.map((size) => ({
3954
+ label: `${size} / page`,
3955
+ value: String(size)
3956
+ })) });
3957
+ const pageSize = /* @__PURE__ */ jsxs(Select.Root, {
3958
+ collection: sizeItems,
3959
+ value: [String(pagination.pageSize)],
3960
+ onValueChange: (details) => table.setPageSize(Number(details.value[0])),
3961
+ positioning: { placement: "top-start" },
3962
+ children: [/* @__PURE__ */ jsx(Select.Control, { children: /* @__PURE__ */ jsxs(Select.Trigger, {
3963
+ "aria-label": "Rows per page",
3964
+ children: [/* @__PURE__ */ jsx(Select.ValueText, {}), /* @__PURE__ */ jsx(Select.Indicator, { children: chevronDownGlyph() })]
3965
+ }) }), /* @__PURE__ */ jsx(Select.Positioner, { children: /* @__PURE__ */ jsx(Select.Content, { children: sizeItems.items.map((item) => /* @__PURE__ */ jsxs(Select.Item, {
3966
+ item,
3967
+ children: [/* @__PURE__ */ jsx(Select.ItemText, { children: item.label }), /* @__PURE__ */ jsx(Select.ItemIndicator, { children: "✓" })]
3968
+ }, item.value)) }) })]
3969
+ });
3970
+ const pager = /* @__PURE__ */ jsxs(Pagination.Root, {
3971
+ count: rowCount,
3972
+ pageSize: pagination.pageSize,
3973
+ page: pagination.pageIndex + 1,
3974
+ onPageChange: (details) => table.setPageIndex(details.page - 1),
3975
+ children: [
3976
+ /* @__PURE__ */ jsx(Pagination.PrevTrigger, { children: pageGlyph("start") }),
3977
+ /* @__PURE__ */ jsx(Pagination.Context, { children: (scope) => scope.pages.map((page, index) => page.type === "ellipsis" ? /* @__PURE__ */ jsx(Pagination.Ellipsis, { index }, `e${index}`) : /* @__PURE__ */ jsx(Pagination.Item, {
3978
+ type: "page",
3979
+ value: page.value,
3980
+ children: String(page.value)
3981
+ }, page.value)) }),
3982
+ /* @__PURE__ */ jsx(Pagination.NextTrigger, { children: pageGlyph("end") })
3983
+ ]
3984
+ });
1813
3985
  return /* @__PURE__ */ jsxs("div", {
1814
3986
  "data-scope": "table",
1815
3987
  "data-part": "pagination",
1816
- children: [
1817
- /* @__PURE__ */ jsx("button", {
1818
- type: "button",
1819
- "data-scope": "table",
1820
- "data-part": "page-button",
1821
- disabled: !table.getCanPreviousPage(),
1822
- onClick: () => table.previousPage(),
1823
- children: "Prev"
1824
- }),
1825
- /* @__PURE__ */ jsx("button", {
1826
- type: "button",
1827
- "data-scope": "table",
1828
- "data-part": "page-button",
1829
- disabled: !table.getCanNextPage(),
1830
- onClick: () => table.nextPage(),
1831
- children: "Next"
1832
- }),
1833
- /* @__PURE__ */ jsx("select", {
1834
- "data-scope": "table",
1835
- "data-part": "page-size",
1836
- "aria-label": "Rows per page",
1837
- value: pagination.pageSize,
1838
- onChange: (e) => table.setPageSize(Number(e.target.value)),
1839
- children: pageSizeOptions.map((size) => /* @__PURE__ */ jsxs("option", {
1840
- value: size,
1841
- children: [size, " / page"]
1842
- }, size))
1843
- }),
1844
- /* @__PURE__ */ jsxs("span", {
1845
- "data-scope": "table",
1846
- "data-part": "page-status",
1847
- children: [
1848
- "Page ",
1849
- pagination.pageIndex + 1,
1850
- " of ",
1851
- pageCount,
1852
- " · ",
1853
- rowCount,
1854
- " rows"
1855
- ]
1856
- })
1857
- ]
3988
+ children: [/* @__PURE__ */ jsxs("span", {
3989
+ "data-scope": "table",
3990
+ "data-part": "page-status",
3991
+ children: [rowCount, " rows"]
3992
+ }), /* @__PURE__ */ jsxs("div", {
3993
+ "data-scope": "table",
3994
+ "data-part": "page-nav",
3995
+ children: [pageSize, pager]
3996
+ })]
1858
3997
  });
1859
3998
  })() : null;
1860
- const toolbar = filterable ? /* @__PURE__ */ jsx("div", {
3999
+ const toolbar = filterable && showToolbar ? /* @__PURE__ */ jsx("div", {
1861
4000
  "data-scope": "table",
1862
4001
  "data-part": "toolbar",
1863
4002
  children: /* @__PURE__ */ jsx("input", {
@@ -1902,6 +4041,22 @@ function DataTable(rawProps) {
1902
4041
  }
1903
4042
  injectComponentStyle("table");
1904
4043
  //#endregion
4044
+ //#region src/components/textarea/index.tsx
4045
+ function Textarea({ value, invalid = false, onValueChange, ...rest }) {
4046
+ const field = useFieldContext();
4047
+ const fieldProps = field?.getTextareaProps() ?? {};
4048
+ return /* @__PURE__ */ jsx("textarea", {
4049
+ ...fieldProps,
4050
+ ...rest,
4051
+ ...value !== void 0 ? { value } : {},
4052
+ "data-scope": "textarea",
4053
+ "data-part": "root",
4054
+ "data-invalid": invalid || field?.invalid ? "" : void 0,
4055
+ onChange: (event) => onValueChange?.(event.target.value)
4056
+ });
4057
+ }
4058
+ injectComponentStyle("textarea");
4059
+ //#endregion
1905
4060
  //#region src/components/timeline/index.tsx
1906
4061
  /** A line of moments: Root is the ordered thread, Item one moment on it,
1907
4062
  * Marker the point where the thread passes, Content what the moment
@@ -2016,6 +4171,299 @@ injectComponentStyle("tooltip");
2016
4171
  const Tour = Tour$1;
2017
4172
  injectComponentStyle("tour");
2018
4173
  //#endregion
4174
+ //#region src/components/transfer/index.tsx
4175
+ const checkGlyph = /* @__PURE__ */ jsx("svg", {
4176
+ viewBox: "0 0 24 24",
4177
+ fill: "none",
4178
+ stroke: "currentColor",
4179
+ strokeWidth: 3,
4180
+ strokeLinecap: "round",
4181
+ strokeLinejoin: "round",
4182
+ "aria-hidden": "true",
4183
+ children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 5 5L19 7" })
4184
+ });
4185
+ function arrowGlyph(direction) {
4186
+ return /* @__PURE__ */ jsx("svg", {
4187
+ viewBox: "0 0 24 24",
4188
+ fill: "none",
4189
+ stroke: "currentColor",
4190
+ strokeWidth: 1.75,
4191
+ strokeLinecap: "round",
4192
+ strokeLinejoin: "round",
4193
+ "aria-hidden": "true",
4194
+ children: /* @__PURE__ */ jsx("path", { d: direction === "right" ? "M5 12h14m-6-6 6 6-6 6" : "M19 12H5m6-6-6 6 6 6" })
4195
+ });
4196
+ }
4197
+ /**
4198
+ * Two ledgers and a crossing: items sit in the source column until the
4199
+ * reader checks them and walks them across — and back, the same way.
4200
+ * `value` is the target column's value list; everything else in `data`
4201
+ * stays on the left. `searchable` adds a filter line to each panel.
4202
+ */
4203
+ function Transfer({ value = [], data, titles = ["Source", "Target"], searchable = false, disabled = false, onValueChange, ...rest }) {
4204
+ const [checkedSource, setCheckedSource] = useState(() => /* @__PURE__ */ new Set());
4205
+ const [checkedTarget, setCheckedTarget] = useState(() => /* @__PURE__ */ new Set());
4206
+ const [sourceQuery, setSourceQuery] = useState("");
4207
+ const [targetQuery, setTargetQuery] = useState("");
4208
+ const target = new Set(value);
4209
+ function panelItems(values, inTarget, query) {
4210
+ return values.filter((item) => item.label.toLowerCase().includes(query.toLowerCase()) && target.has(item.value) === inTarget);
4211
+ }
4212
+ function toggle(side, option) {
4213
+ (side === "source" ? setCheckedSource : setCheckedTarget)((prev) => {
4214
+ const next = new Set(prev);
4215
+ if (next.has(option)) next.delete(option);
4216
+ else next.add(option);
4217
+ return next;
4218
+ });
4219
+ }
4220
+ function move(toTarget) {
4221
+ const moving = toTarget ? checkedSource : checkedTarget;
4222
+ if (moving.size === 0) return;
4223
+ const next = toTarget ? [...value, ...moving].filter((entry, index, all) => all.indexOf(entry) === index) : value.filter((entry) => !moving.has(entry));
4224
+ if (toTarget) setCheckedSource(/* @__PURE__ */ new Set());
4225
+ else setCheckedTarget(/* @__PURE__ */ new Set());
4226
+ onValueChange?.(next);
4227
+ }
4228
+ function panel(side, title, query, onQuery, checked, items) {
4229
+ return /* @__PURE__ */ jsxs("div", {
4230
+ "data-scope": "transfer",
4231
+ "data-part": "panel",
4232
+ "data-side": side,
4233
+ children: [
4234
+ /* @__PURE__ */ jsxs("div", {
4235
+ "data-scope": "transfer",
4236
+ "data-part": "head",
4237
+ children: [/* @__PURE__ */ jsx("span", {
4238
+ "data-scope": "transfer",
4239
+ "data-part": "title",
4240
+ children: title
4241
+ }), /* @__PURE__ */ jsx("span", {
4242
+ "data-scope": "transfer",
4243
+ "data-part": "count",
4244
+ children: items.length
4245
+ })]
4246
+ }),
4247
+ searchable ? /* @__PURE__ */ jsx("div", {
4248
+ "data-scope": "transfer",
4249
+ "data-part": "search",
4250
+ children: /* @__PURE__ */ jsx(Input, {
4251
+ size: "sm",
4252
+ value: query,
4253
+ onValueChange: onQuery,
4254
+ placeholder: "Filter…",
4255
+ "aria-label": `Filter ${title}`
4256
+ })
4257
+ }) : null,
4258
+ /* @__PURE__ */ jsx("div", {
4259
+ "data-scope": "transfer",
4260
+ "data-part": "list",
4261
+ children: items.length === 0 ? /* @__PURE__ */ jsx("p", {
4262
+ "data-scope": "transfer",
4263
+ "data-part": "empty",
4264
+ children: "Nothing here"
4265
+ }) : items.map((item) => {
4266
+ const locked = disabled || item.disabled === true;
4267
+ return /* @__PURE__ */ jsxs(Checkbox$1.Root, {
4268
+ checked: checked.has(item.value),
4269
+ disabled: locked,
4270
+ onCheckedChange: () => toggle(side, item.value),
4271
+ children: [
4272
+ /* @__PURE__ */ jsx(Checkbox$1.Control, { children: /* @__PURE__ */ jsx(Checkbox$1.Indicator, { children: checkGlyph }) }),
4273
+ /* @__PURE__ */ jsx(Checkbox$1.Label, {
4274
+ "data-part": "label",
4275
+ children: item.label
4276
+ }),
4277
+ /* @__PURE__ */ jsx(Checkbox$1.HiddenInput, {})
4278
+ ]
4279
+ }, item.value);
4280
+ })
4281
+ })
4282
+ ]
4283
+ });
4284
+ }
4285
+ const items = panelItems(data, false, sourceQuery);
4286
+ const targetItems = panelItems(data, true, targetQuery);
4287
+ return /* @__PURE__ */ jsxs("div", {
4288
+ ...rest,
4289
+ "data-scope": "transfer",
4290
+ "data-part": "root",
4291
+ children: [
4292
+ panel("source", titles[0], sourceQuery, setSourceQuery, checkedSource, items),
4293
+ /* @__PURE__ */ jsxs("div", {
4294
+ "data-scope": "transfer",
4295
+ "data-part": "operations",
4296
+ children: [/* @__PURE__ */ jsx(Button, {
4297
+ variant: "outline",
4298
+ size: "sm",
4299
+ square: true,
4300
+ disabled: checkedSource.size === 0 || disabled,
4301
+ "aria-label": "Move right",
4302
+ onClick: () => move(true),
4303
+ children: arrowGlyph("right")
4304
+ }), /* @__PURE__ */ jsx(Button, {
4305
+ variant: "outline",
4306
+ size: "sm",
4307
+ square: true,
4308
+ disabled: checkedTarget.size === 0 || disabled,
4309
+ "aria-label": "Move left",
4310
+ onClick: () => move(false),
4311
+ children: arrowGlyph("left")
4312
+ })]
4313
+ }),
4314
+ panel("target", titles[1], targetQuery, setTargetQuery, checkedTarget, targetItems)
4315
+ ]
4316
+ });
4317
+ }
4318
+ injectComponentStyle("transfer");
4319
+ injectComponentStyle("checkbox");
4320
+ //#endregion
4321
+ //#region src/components/tree-select/index.tsx
4322
+ const chevron = /* @__PURE__ */ jsx("svg", {
4323
+ viewBox: "0 0 24 24",
4324
+ fill: "none",
4325
+ stroke: "currentColor",
4326
+ strokeWidth: 2,
4327
+ strokeLinecap: "round",
4328
+ strokeLinejoin: "round",
4329
+ "aria-hidden": "true",
4330
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
4331
+ });
4332
+ const chevronDown = /* @__PURE__ */ jsx("span", {
4333
+ "data-part": "chevron",
4334
+ "aria-hidden": "true",
4335
+ children: chevron
4336
+ });
4337
+ function TreeSelectRow({ node, indexPath }) {
4338
+ return /* @__PURE__ */ jsx(TreeView$1.NodeProvider, {
4339
+ node,
4340
+ indexPath,
4341
+ children: node.children ? /* @__PURE__ */ jsxs(TreeView$1.Branch, { children: [/* @__PURE__ */ jsxs(TreeView$1.BranchControl, { children: [/* @__PURE__ */ jsx(TreeView$1.BranchIndicator, { children: chevron }), /* @__PURE__ */ jsx(TreeView$1.BranchText, { children: node.label })] }), /* @__PURE__ */ jsxs(TreeView$1.BranchContent, { children: [/* @__PURE__ */ jsx(TreeView$1.BranchIndentGuide, {}), node.children.map((child, index) => /* @__PURE__ */ jsx(TreeSelectRow, {
4342
+ node: child,
4343
+ indexPath: [...indexPath, index]
4344
+ }, child.value))] })] }) : /* @__PURE__ */ jsx(TreeView$1.Item, {
4345
+ asChild: true,
4346
+ children: /* @__PURE__ */ jsx("span", {
4347
+ style: {
4348
+ display: "flex",
4349
+ inlineSize: "100%"
4350
+ },
4351
+ children: /* @__PURE__ */ jsx(TreeView$1.ItemText, { children: node.label })
4352
+ })
4353
+ })
4354
+ });
4355
+ }
4356
+ function TreeSelect({ value, data, placeholder = "Select…", filterable = false, disabled = false, onValueChange, ...rest }) {
4357
+ const [open, setOpen] = useState(false);
4358
+ const [query, setQuery] = useState("");
4359
+ const filterFns = useFilter({ sensitivity: "base" });
4360
+ const collection = useMemo(() => createTreeCollection$1({
4361
+ nodeToValue: (node) => node.value,
4362
+ nodeToString: (node) => node.label,
4363
+ rootNode: {
4364
+ value: "ROOT",
4365
+ label: "",
4366
+ children: data
4367
+ }
4368
+ }), [data]);
4369
+ const filtering = filterable && query.trim().length > 0;
4370
+ /** The collection pruned to the matches — each hit keeping its
4371
+ * ancestors, so a deep match still reads inside its hierarchy. */
4372
+ const visibleCollection = useMemo(() => filtering ? collection.filter((node) => filterFns.contains(node.label, query.trim())) : collection, [
4373
+ filtering,
4374
+ collection,
4375
+ filterFns,
4376
+ query
4377
+ ]);
4378
+ const firstLevel = data.map((node) => node.value);
4379
+ const expandedWhileFiltering = useMemo(() => {
4380
+ if (!filtering) return void 0;
4381
+ const values = [];
4382
+ const walk = (nodes) => {
4383
+ for (const node of nodes) if (node.children?.length) {
4384
+ values.push(node.value);
4385
+ walk(node.children);
4386
+ }
4387
+ };
4388
+ walk(visibleCollection.rootNode.children ?? []);
4389
+ return values;
4390
+ }, [filtering, visibleCollection]);
4391
+ let found;
4392
+ const walk = (nodes) => {
4393
+ for (const node of nodes ?? []) {
4394
+ if (node.value === value) found = node.label;
4395
+ walk(node.children);
4396
+ }
4397
+ };
4398
+ walk(data);
4399
+ const label = found;
4400
+ function pick(details) {
4401
+ const [selected] = details.selectedValue;
4402
+ if (selected == null) return;
4403
+ onValueChange?.(selected);
4404
+ setOpen(false);
4405
+ }
4406
+ return /* @__PURE__ */ jsxs(Popover$1.Root, {
4407
+ ...rest,
4408
+ open,
4409
+ onOpenChange: (details) => {
4410
+ setOpen(details.open);
4411
+ if (!details.open) setQuery("");
4412
+ },
4413
+ positioning: {
4414
+ sameWidth: true,
4415
+ placement: "bottom-start"
4416
+ },
4417
+ children: [/* @__PURE__ */ jsx(Popover$1.Trigger, {
4418
+ asChild: true,
4419
+ disabled,
4420
+ children: /* @__PURE__ */ jsxs("button", {
4421
+ type: "button",
4422
+ "data-scope": "tree-select",
4423
+ "data-part": "control",
4424
+ "data-open": open ? "" : void 0,
4425
+ "data-placeholder": label == null ? "" : void 0,
4426
+ disabled,
4427
+ children: [label ?? placeholder, chevronDown]
4428
+ })
4429
+ }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsxs(Popover$1.Content, {
4430
+ "data-scope": "tree-select",
4431
+ "data-part": "content",
4432
+ children: [filterable ? /* @__PURE__ */ jsx("div", {
4433
+ "data-scope": "tree-select",
4434
+ "data-part": "search",
4435
+ children: /* @__PURE__ */ jsx(Input, {
4436
+ size: "sm",
4437
+ value: query,
4438
+ onValueChange: setQuery,
4439
+ placeholder: "Filter…",
4440
+ "aria-label": "Filter options"
4441
+ })
4442
+ }) : null, /* @__PURE__ */ jsx("div", {
4443
+ "data-scope": "tree-select",
4444
+ "data-part": "body",
4445
+ children: (visibleCollection.rootNode.children ?? []).length === 0 ? /* @__PURE__ */ jsx("p", {
4446
+ "data-scope": "tree-select",
4447
+ "data-part": "empty",
4448
+ children: "Nothing matches"
4449
+ }) : /* @__PURE__ */ jsx(TreeView$1.Root, {
4450
+ collection: visibleCollection,
4451
+ selectionMode: "single",
4452
+ selectedValue: value ? [value] : [],
4453
+ ...filtering ? { expandedValue: expandedWhileFiltering } : { defaultExpandedValue: firstLevel },
4454
+ onSelectionChange: pick,
4455
+ children: /* @__PURE__ */ jsx(TreeView$1.Tree, { children: visibleCollection.rootNode.children?.map((node, index) => /* @__PURE__ */ jsx(TreeSelectRow, {
4456
+ node,
4457
+ indexPath: [index]
4458
+ }, node.value)) })
4459
+ })
4460
+ })]
4461
+ }) }) })]
4462
+ });
4463
+ }
4464
+ injectComponentStyle("tree-select");
4465
+ injectComponentStyle("tree-view");
4466
+ //#endregion
2019
4467
  //#region src/components/tree-view/index.tsx
2020
4468
  /** Ark's TreeView, dressed in the paper-and-ink system: quiet rows where
2021
4469
  * selection is pure light on the paper, one hairline plumb line per depth,
@@ -2027,4 +4475,80 @@ injectComponentStyle("tour");
2027
4475
  const TreeView = TreeView$1;
2028
4476
  injectComponentStyle("tree-view");
2029
4477
  //#endregion
2030
- export { Accordion, Action, Ai, Alert, AngleSlider, Avatar, AvatarGroup, Badge, Banner, Breadcrumb, Button, Calendar, Card, Carousel, Checkbox, Chip, Clipboard, Collapsible, ColorPicker, Combobox, DataTable, DateInput, DatePicker, Dialog, Drawer, Editable, Empty, Field, Fieldset, FileUpload, FlexRender, FloatingPanel, Format, Frame, Highlight, HoverCard, ImageCropper, JsonTreeView, Kbd, Link, Listbox, Marquee, Menu, Message, Meter, NavigationMenu, NumberInput, PageHeader, Pagination, PasswordInput, PinInput, Popover, Progress, PromptInput, QrCode, RadioGroup, RatingGroup, Reasoning, Response, ScrollArea, SegmentGroup, Select, Separator, SignaturePad, Skeleton, Slider, Source, Spinner, Splitter, Stat, Steps, Suggestion, Swap, Switch, Tabs, TagsInput, Timeline, Timer, Toast, Toaster, Toc, Toggle, ToggleGroup, Tool, Tooltip, Tour, TreeView, Typography, createColumnHelper, createListCollection, createToaster, useTour };
4478
+ //#region src/components/watermark/index.tsx
4479
+ /** Font-relative measures ride the root's own measure; absolute units
4480
+ * pass through untouched — the canvas needs a number, the prop may
4481
+ * speak in CSS. */
4482
+ function toPx(size) {
4483
+ const root = Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
4484
+ const value = Number.parseFloat(size) || 0;
4485
+ if (size.includes("em")) return value * root;
4486
+ return value;
4487
+ }
4488
+ /**
4489
+ * The paper bears its seal beneath the content: a canvas draws the
4490
+ * text once into a tile (rotated, faint, crisp at the screen's pixel
4491
+ * density) and the marks layer repeats it over the children. The seal
4492
+ * redraws itself when a prop turns — and it never takes a pointer.
4493
+ */
4494
+ function Watermark({ content, opacity = .06, rotate = -20, fontSize = "0.875rem", children, ...rest }) {
4495
+ const [root, setRoot] = useState(null);
4496
+ const [tile, setTile] = useState("");
4497
+ useEffect(() => {
4498
+ if (!root) return;
4499
+ const canvas = document.createElement("canvas");
4500
+ const paint = canvas.getContext("2d");
4501
+ if (!paint) return;
4502
+ const styles = getComputedStyle(root);
4503
+ const fontSizePx = toPx(fontSize);
4504
+ const font = `${styles.fontWeight} ${fontSizePx}px ${styles.fontFamily}`;
4505
+ paint.font = font;
4506
+ const textWidth = paint.measureText(content).width;
4507
+ const margin = fontSizePx;
4508
+ const boxWidth = Math.ceil(textWidth + margin * 2);
4509
+ const boxHeight = Math.ceil(fontSizePx + margin * 2);
4510
+ const radian = rotate * Math.PI / 180;
4511
+ const cos = Math.cos(radian);
4512
+ const sin = Math.sin(radian);
4513
+ const width = Math.ceil(Math.abs(boxWidth * cos) + Math.abs(boxHeight * sin));
4514
+ const height = Math.ceil(Math.abs(boxWidth * sin) + Math.abs(boxHeight * cos));
4515
+ const dpr = window.devicePixelRatio || 1;
4516
+ canvas.width = width * dpr;
4517
+ canvas.height = height * dpr;
4518
+ paint.scale(dpr, dpr);
4519
+ paint.translate(width / 2, height / 2);
4520
+ paint.rotate(radian);
4521
+ paint.globalAlpha = opacity;
4522
+ paint.font = font;
4523
+ paint.textAlign = "center";
4524
+ paint.textBaseline = "middle";
4525
+ paint.fillStyle = styles.getPropertyValue("--bs-color-text-tertiary").trim() || styles.color;
4526
+ paint.fillText(content, 0, 0);
4527
+ setTile(`url(${canvas.toDataURL()})`);
4528
+ }, [
4529
+ root,
4530
+ content,
4531
+ opacity,
4532
+ rotate,
4533
+ fontSize
4534
+ ]);
4535
+ return /* @__PURE__ */ jsxs("div", {
4536
+ ...rest,
4537
+ ref: setRoot,
4538
+ "data-scope": "watermark",
4539
+ "data-part": "root",
4540
+ children: [/* @__PURE__ */ jsx("div", {
4541
+ "data-scope": "watermark",
4542
+ "data-part": "content",
4543
+ children
4544
+ }), /* @__PURE__ */ jsx("div", {
4545
+ "data-scope": "watermark",
4546
+ "data-part": "marks",
4547
+ "aria-hidden": "true",
4548
+ style: { backgroundImage: tile }
4549
+ })]
4550
+ });
4551
+ }
4552
+ injectComponentStyle("watermark");
4553
+ //#endregion
4554
+ export { Accordion, Action, Action as AiAction, Affix, Ai, Attachment as AiAttachment, Attachment, Attachments as AiAttachments, Attachments, Message as AiMessage, Message, PromptInput as AiPromptInput, PromptInput, Reasoning as AiReasoning, Reasoning, Response as AiResponse, Response, Source as AiSource, Source, Sources as AiSources, Sources, Suggestion as AiSuggestion, Suggestion, Tool as AiTool, Tool, Alert, AngleSlider, AspectRatio, AutoComplete, Avatar, AvatarGroup, BackTop, Badge, Banner, Breadcrumb, Button, ButtonGroup, Calendar, Card, Carousel, CascadeSelect, Checkbox, CheckboxGroup, Chip, ClientOnly, Clipboard, Collapsible, ColorPicker, Combobox, Command, Comment, ConfigProvider, Container, DataTable, DateInput, DatePicker, Descriptions, DescriptionsRoot, Dialog, DownloadTrigger, Drawer, DynamicInput, Editable, Ellipsis, Empty, FLOAT_BUTTON_CONTEXT, Field, Fieldset, FileUpload, FlexRender, FloatButton, FloatingPanel, FocusTrap, Form, FormField, Format, Frame, Grid, Highlight, HoverCard, Icon, Image, ImageCropper, ImageViewer, Input, InputGroup, JsonTreeView, Kbd, Layout, Link, List, Listbox, Marquee, Masonry, Mentions, MentionsVessel, Menu, Menubar, Meter, NavigationMenu, NumberInput, PageHeader, Pagination, PasswordInput, PinInput, Popconfirm, Popover, Presence, PresenceProvider, Progress, ProgressGroup, QrCode, RadioGroup, RatingGroup, ScrollArea, SegmentGroup, Select, Separator, SignaturePad, Skeleton, Slider, Spinner, SplitButton, Splitter, Stack, Stat, Steps, Swap, Switch, Tabs, TagsInput, Textarea, Timeline, Timer, Toast, Toaster, Toc, Toggle, ToggleGroup, Tooltip, Tour, Transfer, TreeSelect, TreeView, Typography, Watermark, createColumnHelper, createFileTreeCollection, createGridCollection, createToaster, createTreeCollection, useAsyncList, useConfig, useDownload, useListCollection, useListSelection, useMentions, usePresence, usePresenceContext, useTour };