@mirror-physics/fractal-ui 0.2.0-next.5 → 0.2.0-next.50

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.js CHANGED
@@ -50,7 +50,7 @@ var colorPalette = {
50
50
  var sizeConfig = {
51
51
  xs: { height: "28px", padding: "4px 8px", iconOnly: "28px", fontSize: "0.75rem" },
52
52
  sm: { height: "32px", padding: "6px 12px", iconOnly: "32px", fontSize: "0.75rem" },
53
- md: { height: "38px", padding: "8px 16px", iconOnly: "38px", fontSize: "0.875rem" },
53
+ md: { height: "40px", padding: "8px 16px", iconOnly: "40px", fontSize: "0.875rem" },
54
54
  lg: { height: "44px", padding: "10px 20px", iconOnly: "44px", fontSize: "1rem" },
55
55
  xl: { height: "56px", padding: "14px 28px", iconOnly: "56px", fontSize: "1rem" }
56
56
  };
@@ -165,44 +165,251 @@ var Button = React.forwardRef(
165
165
  );
166
166
  Button.displayName = "Button";
167
167
 
168
+ // src/components/CodeBlock/CodeBlock.tsx
169
+ import { useEffect, useMemo, useRef, useState as useState2 } from "react";
170
+ import CodeMirror from "@uiw/react-codemirror";
171
+ import { HighlightStyle, foldGutter, syntaxHighlighting } from "@codemirror/language";
172
+ import { json } from "@codemirror/lang-json";
173
+ import { EditorView, lineNumbers as lineNumberGutter } from "@codemirror/view";
174
+ import { tags } from "@lezer/highlight";
175
+ import { Check, Copy } from "@mirror-physics/fractal-icons";
176
+ import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
177
+ var codeTheme = EditorView.theme({
178
+ "&": {
179
+ backgroundColor: "transparent",
180
+ color: "var(--fg-primary)",
181
+ fontSize: "0.75rem"
182
+ },
183
+ "&.cm-focused": { outline: "none" },
184
+ ".cm-scroller": {
185
+ fontFamily: "var(--font-mono)",
186
+ lineHeight: "1.55",
187
+ overscrollBehavior: "contain"
188
+ },
189
+ ".cm-content": { padding: "10px 0" },
190
+ ".cm-line": { padding: "0 12px" },
191
+ ".cm-cursor, .cm-dropCursor": { borderLeftColor: "var(--fg-primary)" },
192
+ ".cm-gutters": {
193
+ backgroundColor: "var(--bg-canvas)",
194
+ borderRight: "1px solid var(--border-subtle)",
195
+ color: "var(--fg-tertiary)"
196
+ },
197
+ ".cm-lineNumbers .cm-gutterElement": { padding: "0 10px 0 12px" },
198
+ ".cm-foldGutter .cm-gutterElement": { padding: "0 8px 0 2px" },
199
+ ".cm-activeLine, .cm-activeLineGutter": { backgroundColor: "var(--bg-surface-1)" },
200
+ ".cm-selectionBackground, &.cm-focused .cm-selectionBackground, ::selection": {
201
+ backgroundColor: "var(--accent-surface-stroke) !important"
202
+ },
203
+ ".cm-searchMatch": {
204
+ backgroundColor: "var(--warning-surface)",
205
+ outline: "1px solid var(--warning-surface-stroke)"
206
+ },
207
+ ".cm-searchMatch.cm-searchMatch-selected": { backgroundColor: "var(--warning-subtle)" },
208
+ ".cm-panels": { backgroundColor: "var(--bg-surface-1)", color: "var(--fg-primary)" },
209
+ ".cm-tooltip": {
210
+ borderColor: "var(--border-subtle)",
211
+ backgroundColor: "var(--bg-canvas)",
212
+ color: "var(--fg-primary)"
213
+ }
214
+ });
215
+ var fractalHighlightStyle = HighlightStyle.define([
216
+ { tag: tags.comment, color: "var(--fg-tertiary)", fontStyle: "italic" },
217
+ { tag: [tags.keyword, tags.controlKeyword, tags.operatorKeyword], color: "var(--purple-default)" },
218
+ { tag: [tags.string, tags.special(tags.string)], color: "var(--purple-default)" },
219
+ { tag: [tags.number, tags.bool, tags.null], color: "var(--orange-default)" },
220
+ { tag: [tags.function(tags.variableName), tags.labelName], color: "var(--accent-default)" },
221
+ { tag: [tags.typeName, tags.className, tags.namespace], color: "var(--pink-default)" },
222
+ { tag: [tags.propertyName, tags.attributeName], color: "var(--success-default)" },
223
+ { tag: [tags.operator, tags.punctuation], color: "var(--fg-secondary)" },
224
+ { tag: tags.bracket, color: "var(--accent-default)" },
225
+ { tag: [tags.heading, tags.strong], color: "var(--fg-primary)", fontWeight: "600" },
226
+ { tag: tags.emphasis, fontStyle: "italic" },
227
+ { tag: tags.link, color: "var(--accent-default)", textDecoration: "underline" },
228
+ { tag: tags.invalid, color: "var(--error-default)", textDecoration: "underline wavy" }
229
+ ]);
230
+ var jsonSupport = json();
231
+ var JSON_LANGUAGE_NAMES = /* @__PURE__ */ new Set(["json", "jsonl", "ndjson", "geojson"]);
232
+ function resolveLanguage(language, fileName) {
233
+ const normalized = language?.trim().toLowerCase();
234
+ const extension = fileName?.split(/[?#]/, 1)[0].split(".").pop()?.toLowerCase();
235
+ if (normalized && JSON_LANGUAGE_NAMES.has(normalized) || extension && JSON_LANGUAGE_NAMES.has(extension)) {
236
+ return { label: normalized === "jsonl" || extension === "jsonl" ? "JSONL" : "JSON", support: jsonSupport };
237
+ }
238
+ if (normalized && !["text", "txt", "plaintext", "plain"].includes(normalized)) {
239
+ return { label: language?.trim(), support: null };
240
+ }
241
+ return { support: null };
242
+ }
243
+ function CodeBlock({
244
+ value,
245
+ language,
246
+ fileName,
247
+ label,
248
+ editable = false,
249
+ onChange,
250
+ lineNumbers = true,
251
+ wrap = true,
252
+ copyable = true,
253
+ ariaLabel,
254
+ height,
255
+ minHeight,
256
+ maxHeight = "28rem",
257
+ placeholder,
258
+ autoFocus,
259
+ className,
260
+ ...props
261
+ }) {
262
+ const resolvedLanguage = useMemo(() => resolveLanguage(language, fileName), [fileName, language]);
263
+ const [copyStatus, setCopyStatus] = useState2("idle");
264
+ const copyResetTimer = useRef(null);
265
+ useEffect(() => () => {
266
+ if (copyResetTimer.current) clearTimeout(copyResetTimer.current);
267
+ }, []);
268
+ const extensions = useMemo(() => [
269
+ codeTheme,
270
+ syntaxHighlighting(fractalHighlightStyle),
271
+ ...resolvedLanguage.support ? [resolvedLanguage.support] : [],
272
+ ...lineNumbers ? [foldGutter(), lineNumberGutter()] : [],
273
+ ...wrap ? [EditorView.lineWrapping] : []
274
+ ], [lineNumbers, resolvedLanguage.support, wrap]);
275
+ const languageLabel = resolvedLanguage.label;
276
+ const toolbarLabel = label ?? fileName ?? languageLabel;
277
+ const surfaceLabel = ariaLabel ?? (fileName ? `${fileName} ${editable ? "editor" : "viewer"}` : `Code ${editable ? "editor" : "viewer"}`);
278
+ const copyCode = async () => {
279
+ try {
280
+ await navigator.clipboard.writeText(value);
281
+ setCopyStatus("copied");
282
+ } catch {
283
+ setCopyStatus("error");
284
+ }
285
+ if (copyResetTimer.current) clearTimeout(copyResetTimer.current);
286
+ copyResetTimer.current = setTimeout(() => setCopyStatus("idle"), 1800);
287
+ };
288
+ const showToolbar = toolbarLabel !== void 0 || copyable || editable;
289
+ return /* @__PURE__ */ jsxs2(
290
+ "div",
291
+ {
292
+ className: cn(
293
+ "fractal-code-block",
294
+ editable && "fractal-code-block--editable",
295
+ !lineNumbers && "fractal-code-block--no-line-numbers",
296
+ className
297
+ ),
298
+ ...props,
299
+ children: [
300
+ showToolbar && /* @__PURE__ */ jsxs2("div", { className: "fractal-code-block__toolbar", children: [
301
+ /* @__PURE__ */ jsxs2("div", { className: "fractal-code-block__identity", children: [
302
+ toolbarLabel !== void 0 && /* @__PURE__ */ jsx("span", { className: "fractal-code-block__label", title: typeof toolbarLabel === "string" ? toolbarLabel : void 0, children: toolbarLabel }),
303
+ fileName && languageLabel && /* @__PURE__ */ jsx("span", { className: "fractal-code-block__language", children: languageLabel }),
304
+ editable && /* @__PURE__ */ jsx("span", { className: "fractal-code-block__mode", children: "Editing" })
305
+ ] }),
306
+ copyable && /* @__PURE__ */ jsxs2(
307
+ "button",
308
+ {
309
+ type: "button",
310
+ className: "fractal-code-block__copy",
311
+ "aria-label": copyStatus === "copied" ? "Code copied" : "Copy code",
312
+ onClick: () => void copyCode(),
313
+ children: [
314
+ copyStatus === "copied" ? /* @__PURE__ */ jsx(Check, { size: 14 }) : /* @__PURE__ */ jsx(Copy, { size: 14 }),
315
+ /* @__PURE__ */ jsx("span", { children: copyStatus === "copied" ? "Copied" : copyStatus === "error" ? "Copy failed" : "Copy" })
316
+ ]
317
+ }
318
+ )
319
+ ] }),
320
+ /* @__PURE__ */ jsx("div", { className: "fractal-code-block__surface", children: /* @__PURE__ */ jsx(
321
+ CodeMirror,
322
+ {
323
+ "aria-label": surfaceLabel,
324
+ value,
325
+ editable,
326
+ readOnly: !editable,
327
+ onChange,
328
+ extensions,
329
+ theme: "none",
330
+ basicSetup: {
331
+ lineNumbers: false,
332
+ foldGutter: false,
333
+ drawSelection: editable,
334
+ highlightActiveLine: editable,
335
+ highlightActiveLineGutter: editable,
336
+ highlightSelectionMatches: editable,
337
+ autocompletion: editable,
338
+ history: editable
339
+ },
340
+ indentWithTab: editable,
341
+ height,
342
+ minHeight,
343
+ maxHeight,
344
+ placeholder,
345
+ autoFocus
346
+ }
347
+ ) }),
348
+ /* @__PURE__ */ jsx("span", { className: "fractal-code-block__status", "aria-live": "polite", children: copyStatus === "copied" ? "Code copied to clipboard." : copyStatus === "error" ? "Code could not be copied." : "" })
349
+ ]
350
+ }
351
+ );
352
+ }
353
+
354
+ // src/components/Breadcrumbs/Breadcrumbs.tsx
355
+ import { ChevronRight } from "@mirror-physics/fractal-icons";
356
+ import { jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
357
+ function Breadcrumbs({
358
+ items,
359
+ ariaLabel = "Breadcrumbs",
360
+ separator = /* @__PURE__ */ jsx2(ChevronRight, { "aria-hidden": "true", size: 14 }),
361
+ className,
362
+ ...props
363
+ }) {
364
+ if (items.length === 0) return null;
365
+ return /* @__PURE__ */ jsx2("nav", { "aria-label": ariaLabel, className: cn("fractal-breadcrumbs", className), ...props, children: /* @__PURE__ */ jsx2("ol", { className: "fractal-breadcrumbs__list", children: items.map((item, index) => {
366
+ const isCurrent = index === items.length - 1;
367
+ const content = isCurrent ? /* @__PURE__ */ jsx2("span", { "aria-current": "page", className: "fractal-breadcrumbs__current", children: item.label }) : /* @__PURE__ */ jsx2("a", { className: "fractal-breadcrumbs__link", href: item.href ?? "#", onClick: item.onClick, children: item.label });
368
+ return /* @__PURE__ */ jsxs3("li", { className: "fractal-breadcrumbs__item", children: [
369
+ index > 0 && /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: "fractal-breadcrumbs__separator", children: separator }),
370
+ content
371
+ ] }, index);
372
+ }) }) });
373
+ }
374
+
168
375
  // src/components/Card/Card.tsx
169
376
  import * as React2 from "react";
170
377
  import { Folder as FolderIcon } from "@mirror-physics/fractal-icons";
171
- import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
378
+ import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
172
379
  var Card = React2.forwardRef(
173
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card", className), ...props })
380
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("fractal-card", className), ...props })
174
381
  );
175
382
  Card.displayName = "Card";
176
383
  var CardHeader = React2.forwardRef(
177
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card-header", className), ...props })
384
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("fractal-card-header", className), ...props })
178
385
  );
179
386
  CardHeader.displayName = "CardHeader";
180
387
  var CardTitle = React2.forwardRef(
181
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("fractal-card-title", className), ...props })
388
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx3("h3", { ref, className: cn("fractal-card-title", className), ...props })
182
389
  );
183
390
  CardTitle.displayName = "CardTitle";
184
391
  var CardDescription = React2.forwardRef(
185
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("fractal-card-description", className), ...props })
392
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx3("p", { ref, className: cn("fractal-card-description", className), ...props })
186
393
  );
187
394
  CardDescription.displayName = "CardDescription";
188
395
  var CardContent = React2.forwardRef(
189
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card-content", className), ...props })
396
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("fractal-card-content", className), ...props })
190
397
  );
191
398
  CardContent.displayName = "CardContent";
192
399
  var CardFooter = React2.forwardRef(
193
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("fractal-card-footer", className), ...props })
400
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx3("div", { ref, className: cn("fractal-card-footer", className), ...props })
194
401
  );
195
402
  CardFooter.displayName = "CardFooter";
196
403
  var FeatureCard = React2.forwardRef(
197
- ({ icon, title, description, className, ...props }, ref) => /* @__PURE__ */ jsxs2("div", { ref, className: cn("fractal-feature-card", className), ...props, children: [
198
- icon && /* @__PURE__ */ jsx("div", { className: "fractal-feature-card__icon", children: icon }),
199
- /* @__PURE__ */ jsx("h3", { className: "fractal-feature-card__title", children: title }),
200
- /* @__PURE__ */ jsx("p", { className: "fractal-feature-card__description", children: description })
404
+ ({ icon, title, description, className, ...props }, ref) => /* @__PURE__ */ jsxs4("div", { ref, className: cn("fractal-feature-card", className), ...props, children: [
405
+ icon && /* @__PURE__ */ jsx3("div", { className: "fractal-feature-card__icon", children: icon }),
406
+ /* @__PURE__ */ jsx3("h3", { className: "fractal-feature-card__title", children: title }),
407
+ /* @__PURE__ */ jsx3("p", { className: "fractal-feature-card__description", children: description })
201
408
  ] })
202
409
  );
203
410
  FeatureCard.displayName = "FeatureCard";
204
411
  var FolderCard = React2.forwardRef(
205
- ({ name, sessionCount, selected, className, onClick, ...props }, ref) => /* @__PURE__ */ jsxs2(
412
+ ({ name, sessionCount, selected, className, onClick, ...props }, ref) => /* @__PURE__ */ jsxs4(
206
413
  "div",
207
414
  {
208
415
  ref,
@@ -218,11 +425,11 @@ var FolderCard = React2.forwardRef(
218
425
  className: cn("fractal-folder-card", selected && "fractal-folder-card--selected", className),
219
426
  ...props,
220
427
  children: [
221
- /* @__PURE__ */ jsxs2("div", { className: "fractal-folder-card__header", children: [
222
- /* @__PURE__ */ jsx(FolderIcon, { size: 16, className: "fractal-folder-card__icon", "aria-hidden": true }),
223
- /* @__PURE__ */ jsx("span", { className: "fractal-folder-card__name", children: name })
428
+ /* @__PURE__ */ jsxs4("div", { className: "fractal-folder-card__header", children: [
429
+ /* @__PURE__ */ jsx3(FolderIcon, { size: 16, className: "fractal-folder-card__icon", "aria-hidden": true }),
430
+ /* @__PURE__ */ jsx3("span", { className: "fractal-folder-card__name", children: name })
224
431
  ] }),
225
- sessionCount !== void 0 && /* @__PURE__ */ jsx("div", { className: "fractal-folder-card__footer", children: /* @__PURE__ */ jsxs2("span", { className: "fractal-folder-card__sessions", children: [
432
+ sessionCount !== void 0 && /* @__PURE__ */ jsx3("div", { className: "fractal-folder-card__footer", children: /* @__PURE__ */ jsxs4("span", { className: "fractal-folder-card__sessions", children: [
226
433
  sessionCount,
227
434
  " ",
228
435
  sessionCount === 1 ? "session" : "sessions"
@@ -235,7 +442,7 @@ FolderCard.displayName = "FolderCard";
235
442
 
236
443
  // src/components/PromptCard/PromptCard.tsx
237
444
  import * as React3 from "react";
238
- import { jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
445
+ import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
239
446
  var titleToPrompt = (title) => typeof title === "string" || typeof title === "number" ? String(title) : "";
240
447
  var PromptCard = React3.forwardRef(
241
448
  ({
@@ -256,7 +463,7 @@ var PromptCard = React3.forwardRef(
256
463
  onPromptSelect?.(prompt ?? titleToPrompt(title));
257
464
  }
258
465
  };
259
- return /* @__PURE__ */ jsxs3(
466
+ return /* @__PURE__ */ jsxs5(
260
467
  "button",
261
468
  {
262
469
  ref,
@@ -271,8 +478,8 @@ var PromptCard = React3.forwardRef(
271
478
  onClick: handleClick,
272
479
  ...props,
273
480
  children: [
274
- icon && /* @__PURE__ */ jsx2("span", { className: "fractal-prompt-card__icon", "aria-hidden": "true", children: icon }),
275
- /* @__PURE__ */ jsx2("span", { className: "fractal-prompt-card__title", children: title })
481
+ icon && /* @__PURE__ */ jsx4("span", { className: "fractal-prompt-card__icon", "aria-hidden": "true", children: icon }),
482
+ /* @__PURE__ */ jsx4("span", { className: "fractal-prompt-card__title", children: title })
276
483
  ]
277
484
  }
278
485
  );
@@ -280,7 +487,7 @@ var PromptCard = React3.forwardRef(
280
487
  );
281
488
  PromptCard.displayName = "PromptCard";
282
489
  var PromptGrid = React3.forwardRef(
283
- ({ compact, padded, className, ...props }, ref) => /* @__PURE__ */ jsx2(
490
+ ({ compact, padded, className, ...props }, ref) => /* @__PURE__ */ jsx4(
284
491
  "div",
285
492
  {
286
493
  ref,
@@ -298,9 +505,9 @@ PromptGrid.displayName = "PromptGrid";
298
505
 
299
506
  // src/components/Input/Input.tsx
300
507
  import * as React4 from "react";
301
- import { jsx as jsx3 } from "react/jsx-runtime";
508
+ import { jsx as jsx5 } from "react/jsx-runtime";
302
509
  var Input = React4.forwardRef(
303
- ({ className, type, ...props }, ref) => /* @__PURE__ */ jsx3(
510
+ ({ className, type, ...props }, ref) => /* @__PURE__ */ jsx5(
304
511
  "input",
305
512
  {
306
513
  type,
@@ -312,11 +519,141 @@ var Input = React4.forwardRef(
312
519
  );
313
520
  Input.displayName = "Input";
314
521
 
315
- // src/components/Label/Label.tsx
522
+ // src/components/NumberInput/NumberInput.tsx
316
523
  import * as React5 from "react";
317
- import { jsx as jsx4 } from "react/jsx-runtime";
318
- var Label = React5.forwardRef(
319
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
524
+ import { Minus, Plus } from "@mirror-physics/fractal-icons";
525
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
526
+ function NumberInput({
527
+ unit,
528
+ className,
529
+ value,
530
+ defaultValue,
531
+ disabled,
532
+ readOnly,
533
+ min,
534
+ max,
535
+ ...props
536
+ }) {
537
+ const inputRef = React5.useRef(null);
538
+ const numericValue = Number(value ?? defaultValue);
539
+ const characterCount = Math.max(String(value ?? defaultValue ?? "").length, 1);
540
+ const controlLabel = typeof props["aria-label"] === "string" ? props["aria-label"] : "value";
541
+ const decrementDisabled = disabled || readOnly || Number.isFinite(numericValue) && min !== void 0 && numericValue <= Number(min);
542
+ const incrementDisabled = disabled || readOnly || Number.isFinite(numericValue) && max !== void 0 && numericValue >= Number(max);
543
+ const changeByStep = (direction) => {
544
+ const input = inputRef.current;
545
+ if (!input) return;
546
+ if (direction === -1) input.stepDown();
547
+ else input.stepUp();
548
+ input.dispatchEvent(new Event("input", { bubbles: true }));
549
+ input.focus();
550
+ };
551
+ return /* @__PURE__ */ jsxs6("div", { className: cn("fractal-number-input", unit && "fractal-number-input--with-unit", className), children: [
552
+ /* @__PURE__ */ jsx6(
553
+ Input,
554
+ {
555
+ ref: inputRef,
556
+ type: "number",
557
+ value,
558
+ defaultValue,
559
+ disabled,
560
+ readOnly,
561
+ min,
562
+ max,
563
+ ...props
564
+ }
565
+ ),
566
+ unit && /* @__PURE__ */ jsx6(
567
+ "span",
568
+ {
569
+ className: "fractal-number-input-unit",
570
+ style: { left: `calc(var(--space-3, 12px) + ${characterCount}ch)` },
571
+ "aria-hidden": "true",
572
+ children: unit
573
+ }
574
+ ),
575
+ /* @__PURE__ */ jsxs6("div", { className: "fractal-number-input-controls", children: [
576
+ /* @__PURE__ */ jsx6(
577
+ "button",
578
+ {
579
+ type: "button",
580
+ className: "fractal-number-input-step",
581
+ "aria-label": `Decrease ${controlLabel}`,
582
+ disabled: decrementDisabled,
583
+ onClick: () => changeByStep(-1),
584
+ children: /* @__PURE__ */ jsx6(Minus, { "aria-hidden": "true", size: 16 })
585
+ }
586
+ ),
587
+ /* @__PURE__ */ jsx6(
588
+ "button",
589
+ {
590
+ type: "button",
591
+ className: "fractal-number-input-step",
592
+ "aria-label": `Increase ${controlLabel}`,
593
+ disabled: incrementDisabled,
594
+ onClick: () => changeByStep(1),
595
+ children: /* @__PURE__ */ jsx6(Plus, { "aria-hidden": "true", size: 16 })
596
+ }
597
+ )
598
+ ] })
599
+ ] });
600
+ }
601
+
602
+ // src/components/PasswordInput/PasswordInput.tsx
603
+ import * as React6 from "react";
604
+ import { Eye, EyeSlash } from "@mirror-physics/fractal-icons";
605
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
606
+ var PasswordInput = React6.forwardRef(
607
+ ({
608
+ className,
609
+ disabled,
610
+ showPasswordLabel = "Show password",
611
+ hidePasswordLabel = "Hide password",
612
+ ...props
613
+ }, ref) => {
614
+ const [passwordVisible, setPasswordVisible] = React6.useState(false);
615
+ const toggleLabel = passwordVisible ? hidePasswordLabel : showPasswordLabel;
616
+ return /* @__PURE__ */ jsxs7(
617
+ "div",
618
+ {
619
+ className: cn("fractal-password-input", className),
620
+ "data-password-visible": passwordVisible ? "" : void 0,
621
+ children: [
622
+ /* @__PURE__ */ jsx7(
623
+ Input,
624
+ {
625
+ ...props,
626
+ ref,
627
+ className: "fractal-password-input-field",
628
+ disabled,
629
+ type: passwordVisible ? "text" : "password"
630
+ }
631
+ ),
632
+ /* @__PURE__ */ jsx7(
633
+ "button",
634
+ {
635
+ type: "button",
636
+ className: "fractal-password-input-toggle",
637
+ "aria-label": toggleLabel,
638
+ "aria-pressed": passwordVisible,
639
+ disabled,
640
+ title: toggleLabel,
641
+ onClick: () => setPasswordVisible((visible) => !visible),
642
+ children: passwordVisible ? /* @__PURE__ */ jsx7(EyeSlash, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ jsx7(Eye, { "aria-hidden": "true", size: 18 })
643
+ }
644
+ )
645
+ ]
646
+ }
647
+ );
648
+ }
649
+ );
650
+ PasswordInput.displayName = "PasswordInput";
651
+
652
+ // src/components/Label/Label.tsx
653
+ import * as React7 from "react";
654
+ import { jsx as jsx8 } from "react/jsx-runtime";
655
+ var Label = React7.forwardRef(
656
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
320
657
  "label",
321
658
  {
322
659
  ref,
@@ -328,44 +665,48 @@ var Label = React5.forwardRef(
328
665
  Label.displayName = "Label";
329
666
 
330
667
  // src/components/Alert/Alert.tsx
331
- import * as React6 from "react";
332
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
668
+ import * as React8 from "react";
669
+ import { CircleCheck, CircleClose, CircleInfo, Warning } from "@mirror-physics/fractal-icons";
670
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
333
671
  var defaultIcons = {
334
- default: "\u2139",
335
- info: "\u2139",
336
- success: "\u2713",
337
- warning: "\u26A0",
338
- error: "\u2715"
672
+ default: CircleInfo,
673
+ info: CircleInfo,
674
+ success: CircleCheck,
675
+ warning: Warning,
676
+ error: CircleClose
339
677
  };
340
- var Alert = React6.forwardRef(
341
- ({ className, variant = "default", icon, children, ...props }, ref) => /* @__PURE__ */ jsxs4(
342
- "div",
343
- {
344
- ref,
345
- role: "alert",
346
- className: cn("fractal-alert", `fractal-alert--${variant}`, className),
347
- ...props,
348
- children: [
349
- /* @__PURE__ */ jsx5("span", { className: "fractal-alert-icon", "aria-hidden": true, children: icon ?? defaultIcons[variant] }),
350
- /* @__PURE__ */ jsx5("div", { className: "fractal-alert-content", children })
351
- ]
352
- }
353
- )
678
+ var Alert = React8.forwardRef(
679
+ ({ className, variant = "default", icon, children, ...props }, ref) => {
680
+ const DefaultIcon = defaultIcons[variant];
681
+ return /* @__PURE__ */ jsxs8(
682
+ "div",
683
+ {
684
+ ref,
685
+ role: "alert",
686
+ className: cn("fractal-alert", `fractal-alert--${variant}`, className),
687
+ ...props,
688
+ children: [
689
+ /* @__PURE__ */ jsx9("span", { className: "fractal-alert-icon", "aria-hidden": true, children: icon ?? /* @__PURE__ */ jsx9(DefaultIcon, { size: 18 }) }),
690
+ /* @__PURE__ */ jsx9("div", { className: "fractal-alert-content", children })
691
+ ]
692
+ }
693
+ );
694
+ }
354
695
  );
355
696
  Alert.displayName = "Alert";
356
- var AlertTitle = React6.forwardRef(
357
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx5("h5", { ref, className: cn("fractal-alert-title", className), ...props })
697
+ var AlertTitle = React8.forwardRef(
698
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx9("h5", { ref, className: cn("fractal-alert-title", className), ...props })
358
699
  );
359
700
  AlertTitle.displayName = "AlertTitle";
360
- var AlertDescription = React6.forwardRef(
361
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx5("div", { ref, className: cn("fractal-alert-description", className), ...props })
701
+ var AlertDescription = React8.forwardRef(
702
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx9("div", { ref, className: cn("fractal-alert-description", className), ...props })
362
703
  );
363
704
  AlertDescription.displayName = "AlertDescription";
364
705
 
365
706
  // src/components/Toggle/Toggle.tsx
366
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
707
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
367
708
  function Toggle({ checked, onChange, label, className }) {
368
- return /* @__PURE__ */ jsxs5(
709
+ return /* @__PURE__ */ jsxs9(
369
710
  "button",
370
711
  {
371
712
  type: "button",
@@ -374,8 +715,8 @@ function Toggle({ checked, onChange, label, className }) {
374
715
  className: cn("fractal-toggle", className),
375
716
  onClick: () => onChange(!checked),
376
717
  children: [
377
- /* @__PURE__ */ jsx6("span", { className: cn("fractal-toggle-track", checked && "fractal-toggle-track--checked"), children: /* @__PURE__ */ jsx6("span", { className: cn("fractal-toggle-thumb", checked && "fractal-toggle-thumb--checked") }) }),
378
- label && /* @__PURE__ */ jsx6("span", { className: "fractal-toggle-label", children: label })
718
+ /* @__PURE__ */ jsx10("span", { className: cn("fractal-toggle-track", checked && "fractal-toggle-track--checked"), children: /* @__PURE__ */ jsx10("span", { className: cn("fractal-toggle-thumb", checked && "fractal-toggle-thumb--checked") }) }),
719
+ label && /* @__PURE__ */ jsx10("span", { className: "fractal-toggle-label", children: label })
379
720
  ]
380
721
  }
381
722
  );
@@ -385,7 +726,7 @@ function Toggle({ checked, onChange, label, className }) {
385
726
  import { createPortal } from "react-dom";
386
727
 
387
728
  // src/hooks/useEscapeDismiss.ts
388
- import { useEffect, useRef } from "react";
729
+ import { useEffect as useEffect2, useRef as useRef3 } from "react";
389
730
  var nextId = 0;
390
731
  var stack = [];
391
732
  function insertSorted(entry) {
@@ -418,9 +759,9 @@ function ensureGlobalListener() {
418
759
  );
419
760
  }
420
761
  function useEscapeDismiss(priority, handler, enabled = true) {
421
- const handlerRef = useRef(handler);
762
+ const handlerRef = useRef3(handler);
422
763
  handlerRef.current = handler;
423
- useEffect(() => {
764
+ useEffect2(() => {
424
765
  if (!enabled) return;
425
766
  ensureGlobalListener();
426
767
  const id = nextId++;
@@ -434,27 +775,84 @@ function useEscapeDismiss(priority, handler, enabled = true) {
434
775
  }, [priority, enabled]);
435
776
  }
436
777
 
778
+ // src/components/UILoader/UILoader.tsx
779
+ import { useId } from "react";
780
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
781
+ var LEFT = "M0 53C0 51 1.6 49.5 3.6 49.5h16.5c1 0 1.9.4 2.5 1L42.1 69.6c.7.7 1.1 1.6 1.1 2.5v16.2c0 2-1.6 3.5-3.6 3.5H23.1c-1 0-1.9-.4-2.5-1L1.1 71.7C.4 71 0 70.1 0 69.2V53Z";
782
+ var CENTER = "M0 3.5C0 1.6 1.6 0 3.6 0h16.5c1 0 1.9.4 2.5 1L92.5 69.6c.7.7 1 1.6 1 2.5v16.2c0 2-1.6 3.5-3.6 3.5H73.4c-1 0-1.9-.4-2.5-1L1.1 22.2C.4 21.6 0 20.7 0 19.7V3.5Z";
783
+ var RIGHT = "M50.4 3.5C50.4 1.6 52 0 54 0h16.5c1 0 1.9.4 2.5 1l69.8 68.6c.7.7 1.1 1.6 1.1 2.5v16.2c0 2-1.6 3.5-3.6 3.5h-16.5c-1 0-1.9-.4-2.5-1L51.4 22.2c-.7-.6-1-1.5-1-2.5V3.5Z";
784
+ function UILoader({ size = 48, tone = "neutral", className, style, label = "Loading", ariaHidden = false }) {
785
+ const rawId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
786
+ const clipId = `fractal-ui-loader-clip-${rawId}`;
787
+ const gradientId = `fractal-ui-loader-gradient-${rawId}`;
788
+ const height = typeof size === "number" ? `${size}px` : size;
789
+ return /* @__PURE__ */ jsx11(
790
+ "span",
791
+ {
792
+ "aria-hidden": ariaHidden || void 0,
793
+ "aria-label": ariaHidden ? void 0 : label,
794
+ className: cn("fractal-ui-loader", `fractal-ui-loader--${tone}`, className),
795
+ role: ariaHidden ? void 0 : "status",
796
+ style,
797
+ children: /* @__PURE__ */ jsxs10("svg", { "aria-hidden": "true", fill: "none", viewBox: "0 0 144 92", style: { height, width: "auto" }, children: [
798
+ /* @__PURE__ */ jsxs10("defs", { children: [
799
+ /* @__PURE__ */ jsxs10("linearGradient", { id: gradientId, x1: "59", x2: "94.3", y1: "0", y2: "89.1", gradientUnits: "userSpaceOnUse", children: [
800
+ /* @__PURE__ */ jsx11("stop", { className: "fractal-ui-loader__stop-start", offset: "0" }),
801
+ /* @__PURE__ */ jsx11("stop", { className: "fractal-ui-loader__stop-end", offset: "1" })
802
+ ] }),
803
+ /* @__PURE__ */ jsxs10("linearGradient", { id: `${gradientId}-shine`, x1: "0", x2: "1", y1: "0", y2: "0", children: [
804
+ /* @__PURE__ */ jsx11("stop", { offset: "0.4", stopColor: "white", stopOpacity: "0" }),
805
+ /* @__PURE__ */ jsx11("stop", { offset: "0.5", stopColor: "white", stopOpacity: "0.62" }),
806
+ /* @__PURE__ */ jsx11("stop", { offset: "0.6", stopColor: "white", stopOpacity: "0" })
807
+ ] }),
808
+ /* @__PURE__ */ jsxs10("clipPath", { id: clipId, children: [
809
+ /* @__PURE__ */ jsx11("path", { d: LEFT }),
810
+ /* @__PURE__ */ jsx11("path", { d: CENTER }),
811
+ /* @__PURE__ */ jsx11("path", { d: RIGHT })
812
+ ] })
813
+ ] }),
814
+ /* @__PURE__ */ jsx11("path", { d: CENTER, fill: `url(#${gradientId})` }),
815
+ /* @__PURE__ */ jsx11("path", { d: RIGHT, fill: `url(#${gradientId})` }),
816
+ /* @__PURE__ */ jsx11("path", { d: LEFT, fill: `url(#${gradientId})` }),
817
+ /* @__PURE__ */ jsx11("g", { clipPath: `url(#${clipId})`, children: /* @__PURE__ */ jsx11("g", { className: "fractal-ui-loader__shine-axis", transform: "translate(72 46) rotate(68.4)", children: /* @__PURE__ */ jsx11("rect", { className: "fractal-ui-loader__shine", x: "-160", y: "-100", width: "320", height: "200", fill: `url(#${gradientId}-shine)` }) }) })
818
+ ] })
819
+ }
820
+ );
821
+ }
822
+
437
823
  // src/components/Modal/Modal.tsx
438
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
439
- var CloseIcon = () => /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx7("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
440
- function Modal({ open, onClose, title, children, danger, warn, footer, noClose, size = "sm", className }) {
441
- useEscapeDismiss(80, onClose, open && !noClose);
824
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
825
+ var CloseIcon = () => /* @__PURE__ */ jsx12("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx12("path", { d: "M12 4L4 12M4 4L12 12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) });
826
+ function Modal({ open, onClose, title, children, danger, warn, footer, noClose, closeDisabled = false, size = "sm", loading = false, loadingLabel = "Loading", scrollable = false, className }) {
827
+ useEscapeDismiss(80, onClose, open && !noClose && !closeDisabled);
442
828
  if (!open) return null;
443
829
  const sizeClass = `fractal-modal-panel--${size}`;
444
830
  const accentClass = danger ? "fractal-modal-panel--danger" : warn ? "fractal-modal-panel--warn" : "";
445
831
  return createPortal(
446
- /* @__PURE__ */ jsx7("div", { className: "fractal-modal-overlay", onClick: noClose ? void 0 : onClose, children: /* @__PURE__ */ jsxs6(
832
+ /* @__PURE__ */ jsx12("div", { className: "fractal-modal-overlay", onClick: noClose || closeDisabled ? void 0 : onClose, children: /* @__PURE__ */ jsxs11(
447
833
  "div",
448
834
  {
449
- className: cn("fractal-modal-panel", sizeClass, accentClass, className),
835
+ className: cn("fractal-modal-panel", sizeClass, accentClass, scrollable && "fractal-modal-panel--scrollable", className),
450
836
  onClick: (e) => e.stopPropagation(),
451
837
  children: [
452
- /* @__PURE__ */ jsxs6("div", { className: "fractal-modal-title-row", children: [
453
- /* @__PURE__ */ jsx7("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
454
- !noClose && /* @__PURE__ */ jsx7("button", { type: "button", onClick: onClose, title: "Close", className: "fractal-modal-close", children: /* @__PURE__ */ jsx7(CloseIcon, {}) })
838
+ /* @__PURE__ */ jsxs11("div", { className: "fractal-modal-title-row", children: [
839
+ /* @__PURE__ */ jsx12("h3", { className: cn("fractal-modal-title", danger && "fractal-modal-title--danger", warn && "fractal-modal-title--warn"), children: title }),
840
+ !noClose && /* @__PURE__ */ jsx12("button", { type: "button", onClick: onClose, title: "Close", disabled: closeDisabled, className: "fractal-modal-close", children: /* @__PURE__ */ jsx12(CloseIcon, {}) })
455
841
  ] }),
456
- /* @__PURE__ */ jsx7("div", { className: cn("fractal-modal-body", size === "lg" && "fractal-modal-body--lg"), children }),
457
- footer && /* @__PURE__ */ jsx7("div", { className: "fractal-modal-footer", children: footer })
842
+ /* @__PURE__ */ jsx12(
843
+ "div",
844
+ {
845
+ "aria-busy": loading || void 0,
846
+ className: cn(
847
+ "fractal-modal-body",
848
+ size === "lg" && "fractal-modal-body--lg",
849
+ loading && "fractal-modal-body--loading",
850
+ scrollable && "fractal-modal-body--scrollable"
851
+ ),
852
+ children: loading ? /* @__PURE__ */ jsx12(UILoader, { label: loadingLabel }) : children
853
+ }
854
+ ),
855
+ footer && /* @__PURE__ */ jsx12("div", { className: cn("fractal-modal-footer", scrollable && "fractal-modal-footer--divided"), children: footer })
458
856
  ]
459
857
  }
460
858
  ) }),
@@ -463,17 +861,17 @@ function Modal({ open, onClose, title, children, danger, warn, footer, noClose,
463
861
  }
464
862
 
465
863
  // src/components/DataTable/DataTable.tsx
466
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
864
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
467
865
  function DataTable({ columns, data, emptyMessage, header, size = "md", className }) {
468
- return /* @__PURE__ */ jsx8("div", { className: cn("fractal-datatable", `fractal-datatable--${size}`, className), children: /* @__PURE__ */ jsxs7("div", { className: "fractal-datatable-surface", children: [
469
- header && /* @__PURE__ */ jsx8("div", { className: "fractal-datatable-header", children: header }),
470
- data.length === 0 ? /* @__PURE__ */ jsx8("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs7("table", { className: "fractal-datatable-table", children: [
471
- /* @__PURE__ */ jsx8("thead", { children: /* @__PURE__ */ jsx8("tr", { className: "fractal-datatable-thead-row", children: columns.map((col) => /* @__PURE__ */ jsx8("th", { className: "fractal-datatable-th", style: { width: col.width }, children: col.header }, col.key)) }) }),
472
- /* @__PURE__ */ jsx8("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx8(
866
+ return /* @__PURE__ */ jsx13("div", { className: cn("fractal-datatable", `fractal-datatable--${size}`, className), children: /* @__PURE__ */ jsxs12("div", { className: "fractal-datatable-surface", children: [
867
+ header && /* @__PURE__ */ jsx13("div", { className: "fractal-datatable-header", children: header }),
868
+ data.length === 0 ? /* @__PURE__ */ jsx13("div", { className: "fractal-datatable-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs12("table", { className: "fractal-datatable-table", children: [
869
+ /* @__PURE__ */ jsx13("thead", { children: /* @__PURE__ */ jsx13("tr", { className: "fractal-datatable-thead-row", children: columns.map((col) => /* @__PURE__ */ jsx13("th", { className: "fractal-datatable-th", style: { width: col.width }, children: col.header }, col.key)) }) }),
870
+ /* @__PURE__ */ jsx13("tbody", { children: data.map((row, rowIndex) => /* @__PURE__ */ jsx13(
473
871
  "tr",
474
872
  {
475
873
  className: "fractal-datatable-row",
476
- children: columns.map((col) => /* @__PURE__ */ jsx8("td", { className: "fractal-datatable-td", style: { width: col.width }, children: col.render(row, rowIndex) }, col.key))
874
+ children: columns.map((col) => /* @__PURE__ */ jsx13("td", { className: "fractal-datatable-td", style: { width: col.width }, children: col.render(row, rowIndex) }, col.key))
477
875
  },
478
876
  rowIndex
479
877
  )) })
@@ -482,9 +880,9 @@ function DataTable({ columns, data, emptyMessage, header, size = "md", className
482
880
  }
483
881
 
484
882
  // src/components/Dropdown/Dropdown.tsx
485
- import * as React7 from "react";
883
+ import * as React9 from "react";
486
884
  import { ChevronDown, ChevronUp } from "@mirror-physics/fractal-icons";
487
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
885
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
488
886
  var MARGIN = 4;
489
887
  function Dropdown({
490
888
  items,
@@ -495,18 +893,20 @@ function Dropdown({
495
893
  align = "left",
496
894
  triggerContent,
497
895
  label,
896
+ iconOnly = false,
498
897
  noChevron = false,
499
898
  size = "md",
500
899
  triggerVariant = "outline",
501
900
  className,
502
901
  triggerClassName,
503
902
  panelClassName,
504
- closeOnSelect = true
903
+ closeOnSelect = true,
904
+ disabled = false
505
905
  }) {
506
- const [open, setOpen] = React7.useState(false);
507
- const triggerRef = React7.useRef(null);
508
- const panelRef = React7.useRef(null);
509
- const [panelStyle, setPanelStyle] = React7.useState({});
906
+ const [open, setOpen] = React9.useState(false);
907
+ const triggerRef = React9.useRef(null);
908
+ const panelRef = React9.useRef(null);
909
+ const [panelStyle, setPanelStyle] = React9.useState({});
510
910
  const visibleItems = items.filter(
511
911
  (item) => item.icon != null || item.description != null && (typeof item.description === "string" ? item.description !== "" : true)
512
912
  );
@@ -515,7 +915,7 @@ function Dropdown({
515
915
  const selected = items.find((i) => i.value === selectedValue && i.kind !== "header") ?? selectableItems[0] ?? visibleItems[0];
516
916
  const isUp = direction === "up";
517
917
  const ChevronIcon = isUp ? ChevronUp : ChevronDown;
518
- React7.useLayoutEffect(() => {
918
+ React9.useLayoutEffect(() => {
519
919
  if (!open || !triggerRef.current || !panelRef.current) return;
520
920
  const triggerRect = triggerRef.current.getBoundingClientRect();
521
921
  const panelRect = panelRef.current.getBoundingClientRect();
@@ -549,7 +949,7 @@ function Dropdown({
549
949
  }
550
950
  setPanelStyle({ top, left, right });
551
951
  }, [open, isUp, align]);
552
- React7.useEffect(() => {
952
+ React9.useEffect(() => {
553
953
  if (!open) return;
554
954
  const handleClickOutside = (e) => {
555
955
  const target = e.target;
@@ -559,12 +959,13 @@ function Dropdown({
559
959
  document.addEventListener("mousedown", handleClickOutside);
560
960
  return () => document.removeEventListener("mousedown", handleClickOutside);
561
961
  }, [open]);
562
- return /* @__PURE__ */ jsxs8("div", { className: cn("fractal-dropdown", className), children: [
563
- /* @__PURE__ */ jsxs8(
962
+ return /* @__PURE__ */ jsxs13("div", { className: cn("fractal-dropdown", className), children: [
963
+ /* @__PURE__ */ jsxs13(
564
964
  "button",
565
965
  {
566
966
  ref: triggerRef,
567
967
  type: "button",
968
+ disabled,
568
969
  "aria-haspopup": "listbox",
569
970
  "aria-expanded": open,
570
971
  "aria-label": triggerLabel,
@@ -573,18 +974,19 @@ function Dropdown({
573
974
  "fractal-dropdown-trigger",
574
975
  `fractal-dropdown-trigger--${size}`,
575
976
  `fractal-dropdown-trigger--${triggerVariant}`,
977
+ iconOnly && "fractal-dropdown-trigger--icon-only",
576
978
  triggerClassName
577
979
  ),
578
980
  children: [
579
- triggerContent != null ? triggerContent : /* @__PURE__ */ jsxs8(Fragment2, { children: [
580
- selected.icon != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
581
- selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-selected-text", children: selected.description })
981
+ triggerContent != null ? triggerContent : /* @__PURE__ */ jsxs13(Fragment2, { children: [
982
+ selected.icon != null && /* @__PURE__ */ jsx14("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
983
+ selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ jsx14("span", { className: "fractal-dropdown-selected-text", children: selected.description })
582
984
  ] }),
583
- !noChevron && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ jsx9(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
985
+ !noChevron && !iconOnly && /* @__PURE__ */ jsx14("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ jsx14(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
584
986
  ]
585
987
  }
586
988
  ),
587
- open && /* @__PURE__ */ jsxs8(
989
+ open && /* @__PURE__ */ jsxs13(
588
990
  "div",
589
991
  {
590
992
  ref: panelRef,
@@ -593,21 +995,21 @@ function Dropdown({
593
995
  className: cn("fractal-dropdown-panel", `fractal-dropdown-panel--${size}`, panelClassName),
594
996
  style: panelStyle,
595
997
  children: [
596
- label != null && /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-label", children: label }),
998
+ label != null && /* @__PURE__ */ jsx14("div", { className: "fractal-dropdown-label", children: label }),
597
999
  visibleItems.map((item, index) => {
598
- const topDivider = item.border === "top" ? /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-separator" }) : null;
599
- const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx9("div", { className: "fractal-dropdown-separator" }) : null;
1000
+ const topDivider = item.border === "top" ? /* @__PURE__ */ jsx14("div", { className: "fractal-dropdown-separator" }) : null;
1001
+ const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ jsx14("div", { className: "fractal-dropdown-separator" }) : null;
600
1002
  if (item.kind === "header") {
601
- return /* @__PURE__ */ jsxs8(React7.Fragment, { children: [
1003
+ return /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
602
1004
  topDivider,
603
- /* @__PURE__ */ jsx9("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
1005
+ /* @__PURE__ */ jsx14("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
604
1006
  bottomDivider
605
1007
  ] }, `header-${index}`);
606
1008
  }
607
1009
  const isSelected = item.value === selectedValue;
608
- return /* @__PURE__ */ jsxs8(React7.Fragment, { children: [
1010
+ return /* @__PURE__ */ jsxs13(React9.Fragment, { children: [
609
1011
  topDivider,
610
- /* @__PURE__ */ jsxs8(
1012
+ /* @__PURE__ */ jsxs13(
611
1013
  "button",
612
1014
  {
613
1015
  type: "button",
@@ -619,9 +1021,9 @@ function Dropdown({
619
1021
  },
620
1022
  className: "fractal-dropdown-option",
621
1023
  children: [
622
- item.icon != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-icon", children: item.icon }),
623
- item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-option-text", children: item.description }),
624
- item.trailing != null && /* @__PURE__ */ jsx9("span", { className: "fractal-dropdown-trailing", children: item.trailing })
1024
+ item.icon != null && /* @__PURE__ */ jsx14("span", { className: "fractal-dropdown-icon", children: item.icon }),
1025
+ item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ jsx14("span", { className: "fractal-dropdown-option-text", children: item.description }),
1026
+ item.trailing != null && /* @__PURE__ */ jsx14("span", { className: "fractal-dropdown-trailing", children: item.trailing })
625
1027
  ]
626
1028
  }
627
1029
  ),
@@ -635,10 +1037,10 @@ function Dropdown({
635
1037
  }
636
1038
 
637
1039
  // src/components/Link/Link.tsx
638
- import * as React8 from "react";
639
- import { jsx as jsx10 } from "react/jsx-runtime";
640
- var Link = React8.forwardRef(
641
- ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx10(
1040
+ import * as React10 from "react";
1041
+ import { jsx as jsx15 } from "react/jsx-runtime";
1042
+ var Link = React10.forwardRef(
1043
+ ({ className, theme = "default", size = "md", ...props }, ref) => /* @__PURE__ */ jsx15(
642
1044
  "a",
643
1045
  {
644
1046
  ref,
@@ -650,10 +1052,10 @@ var Link = React8.forwardRef(
650
1052
  Link.displayName = "Link";
651
1053
 
652
1054
  // src/components/Chip/Chip.tsx
653
- import * as React9 from "react";
654
- import { jsx as jsx11 } from "react/jsx-runtime";
655
- var Chip = React9.forwardRef(
656
- ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx11(
1055
+ import * as React11 from "react";
1056
+ import { jsx as jsx16 } from "react/jsx-runtime";
1057
+ var Chip = React11.forwardRef(
1058
+ ({ className, tone = "neutral", ...props }, ref) => /* @__PURE__ */ jsx16(
657
1059
  "span",
658
1060
  {
659
1061
  ref,
@@ -664,36 +1066,86 @@ var Chip = React9.forwardRef(
664
1066
  );
665
1067
  Chip.displayName = "Chip";
666
1068
 
1069
+ // src/components/Checkbox/Checkbox.tsx
1070
+ import { Checkbox as CheckboxIcon, CheckboxChecked, Minus as Minus2 } from "@mirror-physics/fractal-icons";
1071
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1072
+ function Checkbox({
1073
+ checked,
1074
+ indeterminate = false,
1075
+ onCheckedChange,
1076
+ className,
1077
+ disabled,
1078
+ onClick,
1079
+ onKeyDown,
1080
+ ...props
1081
+ }) {
1082
+ return /* @__PURE__ */ jsx17(
1083
+ "button",
1084
+ {
1085
+ ...props,
1086
+ className: cn(
1087
+ "fractal-checkbox",
1088
+ (checked || indeterminate) && "fractal-checkbox--checked",
1089
+ indeterminate && "fractal-checkbox--indeterminate",
1090
+ className
1091
+ ),
1092
+ type: "button",
1093
+ role: "checkbox",
1094
+ "aria-checked": indeterminate ? "mixed" : checked,
1095
+ disabled,
1096
+ onClick: (event) => {
1097
+ onClick?.(event);
1098
+ if (!event.defaultPrevented) onCheckedChange(indeterminate || !checked);
1099
+ },
1100
+ onKeyDown: (event) => {
1101
+ onKeyDown?.(event);
1102
+ if (event.key === " " || event.key === "Enter") event.stopPropagation();
1103
+ },
1104
+ children: indeterminate ? /* @__PURE__ */ jsxs14("span", { className: "fractal-checkbox__indeterminate-icon", "aria-hidden": "true", children: [
1105
+ /* @__PURE__ */ jsx17(CheckboxIcon, { size: 18 }),
1106
+ /* @__PURE__ */ jsx17(Minus2, { className: "fractal-checkbox__indeterminate-mark", size: 12 })
1107
+ ] }) : checked ? /* @__PURE__ */ jsx17(CheckboxChecked, { "aria-hidden": "true", size: 18 }) : /* @__PURE__ */ jsx17(CheckboxIcon, { "aria-hidden": "true", size: 18 })
1108
+ }
1109
+ );
1110
+ }
1111
+
667
1112
  // src/components/SortableTable/SortableTable.tsx
668
- import { useMemo, useState as useState3 } from "react";
1113
+ import { useMemo as useMemo2, useState as useState5 } from "react";
669
1114
  import { ArrowUp, ArrowDown, ArrowsUpDown } from "@mirror-physics/fractal-icons";
670
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1115
+ import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
671
1116
  function SortableTable({
672
1117
  columns,
673
1118
  data,
674
1119
  emptyMessage,
675
1120
  header,
676
1121
  size = "md",
1122
+ embedded = false,
677
1123
  className,
1124
+ defaultSort,
678
1125
  defaultSortKey = void 0,
679
1126
  defaultSortDirection = null,
680
1127
  sortKey: controlledKey,
681
1128
  sortDirection: controlledDirection,
682
1129
  onSortChange,
683
1130
  highlightRow,
684
- onRowClick
1131
+ onRowClick,
1132
+ rowAction
685
1133
  }) {
686
- const [internalKey, setInternalKey] = useState3(defaultSortKey ?? null);
687
- const [internalDirection, setInternalDirection] = useState3(defaultSortDirection);
1134
+ const [internalKey, setInternalKey] = useState5(defaultSortKey ?? null);
1135
+ const [internalDirection, setInternalDirection] = useState5(defaultSortDirection);
688
1136
  const isControlled = controlledKey !== void 0 && controlledDirection !== void 0;
689
1137
  const activeKey = isControlled ? controlledKey : internalKey;
690
1138
  const activeDirection = isControlled ? controlledDirection : internalDirection;
691
- const sorted = useMemo(() => {
692
- if (!activeKey || !activeDirection) return data;
693
- const col = columns.find((c) => c.key === activeKey);
1139
+ const baselineKey = defaultSort?.key ?? null;
1140
+ const baselineDirection = defaultSort?.direction ?? null;
1141
+ const effectiveKey = activeKey && activeDirection ? activeKey : baselineKey;
1142
+ const effectiveDirection = activeKey && activeDirection ? activeDirection : baselineDirection;
1143
+ const sorted = useMemo2(() => {
1144
+ if (!effectiveKey || !effectiveDirection) return data;
1145
+ const col = columns.find((c) => c.key === effectiveKey);
694
1146
  if (!col) return data;
695
1147
  const accessor = col.sortAccessor ?? ((row) => {
696
- const value = row[activeKey];
1148
+ const value = row[effectiveKey];
697
1149
  return typeof value === "string" || typeof value === "number" ? value : null;
698
1150
  });
699
1151
  const copy = [...data];
@@ -704,26 +1156,28 @@ function SortableTable({
704
1156
  if (av == null) return 1;
705
1157
  if (bv == null) return -1;
706
1158
  if (typeof av === "number" && typeof bv === "number") {
707
- return activeDirection === "asc" ? av - bv : bv - av;
1159
+ return effectiveDirection === "asc" ? av - bv : bv - av;
708
1160
  }
709
1161
  const as = String(av).toLowerCase();
710
1162
  const bs = String(bv).toLowerCase();
711
- if (as < bs) return activeDirection === "asc" ? -1 : 1;
712
- if (as > bs) return activeDirection === "asc" ? 1 : -1;
1163
+ if (as < bs) return effectiveDirection === "asc" ? -1 : 1;
1164
+ if (as > bs) return effectiveDirection === "asc" ? 1 : -1;
713
1165
  return 0;
714
1166
  });
715
1167
  return copy;
716
- }, [data, columns, activeKey, activeDirection]);
1168
+ }, [data, columns, effectiveKey, effectiveDirection]);
717
1169
  const handleSortClick = (key) => {
718
1170
  let nextDirection;
719
- if (activeKey !== key) {
1171
+ if (!activeKey || !activeDirection) {
1172
+ nextDirection = baselineKey === key && baselineDirection ? baselineDirection : "asc";
1173
+ } else if (activeKey !== key) {
720
1174
  nextDirection = "asc";
1175
+ } else if (baselineKey === key && baselineDirection) {
1176
+ nextDirection = activeDirection === baselineDirection ? baselineDirection === "asc" ? "desc" : "asc" : null;
721
1177
  } else if (activeDirection === "asc") {
722
1178
  nextDirection = "desc";
723
- } else if (activeDirection === "desc") {
724
- nextDirection = null;
725
1179
  } else {
726
- nextDirection = "asc";
1180
+ nextDirection = null;
727
1181
  }
728
1182
  const nextKey = nextDirection === null ? null : key;
729
1183
  if (isControlled) {
@@ -734,13 +1188,13 @@ function SortableTable({
734
1188
  onSortChange?.(nextKey, nextDirection);
735
1189
  }
736
1190
  };
737
- return /* @__PURE__ */ jsx12("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, className), children: /* @__PURE__ */ jsxs9("div", { className: "fractal-sortable-table-surface", children: [
738
- header && /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-header", children: header }),
739
- sorted.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs9("table", { className: "fractal-sortable-table-table", children: [
740
- /* @__PURE__ */ jsx12("thead", { children: /* @__PURE__ */ jsx12("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
1191
+ return /* @__PURE__ */ jsx18("div", { className: cn("fractal-sortable-table", `fractal-sortable-table--${size}`, embedded && "fractal-sortable-table--embedded", className), children: /* @__PURE__ */ jsxs15("div", { className: "fractal-sortable-table-surface", children: [
1192
+ header && /* @__PURE__ */ jsx18("div", { className: "fractal-sortable-table-header", children: header }),
1193
+ sorted.length === 0 ? /* @__PURE__ */ jsx18("div", { className: "fractal-sortable-table-empty", children: emptyMessage || "No data" }) : /* @__PURE__ */ jsxs15("table", { className: "fractal-sortable-table-table", children: [
1194
+ /* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsx18("tr", { className: "fractal-sortable-table-thead-row", children: columns.map((col) => {
741
1195
  const isActive = activeKey === col.key && activeDirection !== null;
742
1196
  if (!col.sortable) {
743
- return /* @__PURE__ */ jsx12(
1197
+ return /* @__PURE__ */ jsx18(
744
1198
  "th",
745
1199
  {
746
1200
  className: "fractal-sortable-table-th",
@@ -750,12 +1204,12 @@ function SortableTable({
750
1204
  col.key
751
1205
  );
752
1206
  }
753
- return /* @__PURE__ */ jsx12(
1207
+ return /* @__PURE__ */ jsx18(
754
1208
  "th",
755
1209
  {
756
1210
  className: "fractal-sortable-table-th fractal-sortable-table-th--sortable",
757
1211
  style: { width: col.width },
758
- children: /* @__PURE__ */ jsxs9(
1212
+ children: /* @__PURE__ */ jsxs15(
759
1213
  "button",
760
1214
  {
761
1215
  type: "button",
@@ -766,8 +1220,8 @@ function SortableTable({
766
1220
  onClick: () => handleSortClick(col.key),
767
1221
  "aria-sort": isActive ? activeDirection === "asc" ? "ascending" : "descending" : "none",
768
1222
  children: [
769
- /* @__PURE__ */ jsx12("span", { children: col.header }),
770
- /* @__PURE__ */ jsx12("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx12(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx12(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx12(ArrowsUpDown, { size: 14 }) })
1223
+ /* @__PURE__ */ jsx18("span", { children: col.header }),
1224
+ /* @__PURE__ */ jsx18("span", { className: "fractal-sortable-table-sort-icon", "aria-hidden": "true", children: isActive && activeDirection === "asc" ? /* @__PURE__ */ jsx18(ArrowUp, { size: 14 }) : isActive && activeDirection === "desc" ? /* @__PURE__ */ jsx18(ArrowDown, { size: 14 }) : /* @__PURE__ */ jsx18(ArrowsUpDown, { size: 14 }) })
771
1225
  ]
772
1226
  }
773
1227
  )
@@ -775,9 +1229,13 @@ function SortableTable({
775
1229
  col.key
776
1230
  );
777
1231
  }) }) }),
778
- /* @__PURE__ */ jsx12("tbody", { children: sorted.map((row, rowIndex) => {
1232
+ /* @__PURE__ */ jsx18("tbody", { children: sorted.map((row, rowIndex) => {
779
1233
  const highlighted = highlightRow?.(row, rowIndex) ?? false;
780
- return /* @__PURE__ */ jsx12(
1234
+ const action = rowAction ? {
1235
+ label: rowAction.label(row, rowIndex),
1236
+ icon: rowAction.icon(row, rowIndex)
1237
+ } : null;
1238
+ return /* @__PURE__ */ jsx18(
781
1239
  "tr",
782
1240
  {
783
1241
  className: cn(
@@ -792,14 +1250,21 @@ function SortableTable({
792
1250
  onRowClick(row, rowIndex);
793
1251
  }
794
1252
  } : void 0,
1253
+ "aria-label": action?.label,
795
1254
  role: onRowClick ? "button" : void 0,
796
1255
  tabIndex: onRowClick ? 0 : void 0,
797
- children: columns.map((col) => /* @__PURE__ */ jsx12(
1256
+ children: columns.map((col, columnIndex) => /* @__PURE__ */ jsxs15(
798
1257
  "td",
799
1258
  {
800
- className: "fractal-sortable-table-td",
1259
+ className: cn(
1260
+ "fractal-sortable-table-td",
1261
+ action && columnIndex === columns.length - 1 && "fractal-sortable-table-td--row-action"
1262
+ ),
801
1263
  style: { width: col.width, textAlign: col.align ?? "left" },
802
- children: col.render(row, rowIndex)
1264
+ children: [
1265
+ col.render(row, rowIndex),
1266
+ action && columnIndex === columns.length - 1 && /* @__PURE__ */ jsx18("span", { className: "fractal-sortable-table-row-action", "aria-hidden": "true", children: /* @__PURE__ */ jsx18("span", { className: "fractal-sortable-table-row-action__icon", children: action.icon }) })
1267
+ ]
803
1268
  },
804
1269
  col.key
805
1270
  ))
@@ -812,23 +1277,40 @@ function SortableTable({
812
1277
  }
813
1278
 
814
1279
  // src/components/Tabs/Tabs.tsx
815
- import { useId, useRef as useRef3, useState as useState4 } from "react";
816
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1280
+ import { useId as useId2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState6 } from "react";
1281
+ import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
817
1282
  function Tabs({
818
1283
  items,
819
1284
  defaultValue,
820
1285
  value: controlledValue,
821
1286
  onValueChange,
822
1287
  className,
823
- ariaLabel
1288
+ ariaLabel,
1289
+ divider = false,
1290
+ variant = "line"
824
1291
  }) {
825
- const reactId = useId();
826
- const [internalValue, setInternalValue] = useState4(
1292
+ const reactId = useId2();
1293
+ const [internalValue, setInternalValue] = useState6(
827
1294
  defaultValue ?? items.find((i) => !i.disabled)?.id ?? items[0]?.id ?? ""
828
1295
  );
829
1296
  const isControlled = controlledValue !== void 0;
830
1297
  const activeId = isControlled ? controlledValue : internalValue;
831
- const tabRefs = useRef3({});
1298
+ const tabListRef = useRef5(null);
1299
+ const tabRefs = useRef5({});
1300
+ const [indicator, setIndicator] = useState6(null);
1301
+ useLayoutEffect2(() => {
1302
+ const updateIndicator = () => {
1303
+ const activeTab = tabRefs.current[activeId];
1304
+ if (!activeTab) return;
1305
+ setIndicator({ offset: activeTab.offsetLeft, width: activeTab.offsetWidth });
1306
+ };
1307
+ updateIndicator();
1308
+ const list = tabListRef.current;
1309
+ if (!list || typeof ResizeObserver === "undefined") return;
1310
+ const observer = new ResizeObserver(updateIndicator);
1311
+ observer.observe(list);
1312
+ return () => observer.disconnect();
1313
+ }, [activeId, items.length]);
832
1314
  const selectTab = (id) => {
833
1315
  if (isControlled) {
834
1316
  onValueChange?.(id);
@@ -838,6 +1320,10 @@ function Tabs({
838
1320
  }
839
1321
  };
840
1322
  const focusable = items.filter((i) => !i.disabled);
1323
+ const indicatorStyle = indicator ? {
1324
+ "--fractal-tabs-indicator-offset": `${indicator.offset}px`,
1325
+ "--fractal-tabs-indicator-width": `${indicator.width}px`
1326
+ } : void 0;
841
1327
  const onKeyDown = (event) => {
842
1328
  const currentIndex = focusable.findIndex((i) => i.id === activeId);
843
1329
  if (currentIndex === -1) return;
@@ -863,41 +1349,52 @@ function Tabs({
863
1349
  selectTab(nextId2);
864
1350
  requestAnimationFrame(() => tabRefs.current[nextId2]?.focus());
865
1351
  };
866
- return /* @__PURE__ */ jsxs10("div", { className: cn("fractal-tabs", className), children: [
867
- /* @__PURE__ */ jsx13("div", { role: "tablist", "aria-label": ariaLabel, className: "fractal-tabs-list", children: items.map((item) => {
868
- const isActive = item.id === activeId;
869
- const tabId = `${reactId}-tab-${item.id}`;
870
- const panelId = `${reactId}-panel-${item.id}`;
871
- return /* @__PURE__ */ jsx13(
872
- "button",
873
- {
874
- ref: (el) => {
875
- tabRefs.current[item.id] = el;
876
- },
877
- id: tabId,
878
- role: "tab",
879
- type: "button",
880
- "aria-selected": isActive,
881
- "aria-controls": panelId,
882
- tabIndex: isActive ? 0 : -1,
883
- disabled: item.disabled,
884
- className: cn(
885
- "fractal-tabs-trigger",
886
- isActive && "fractal-tabs-trigger--active",
887
- item.disabled && "fractal-tabs-trigger--disabled"
888
- ),
889
- onClick: () => !item.disabled && selectTab(item.id),
890
- onKeyDown,
891
- children: item.label
892
- },
893
- item.id
894
- );
895
- }) }),
1352
+ return /* @__PURE__ */ jsxs16("div", { className: cn("fractal-tabs", `fractal-tabs--${variant}`, divider && "fractal-tabs--divider", className), children: [
1353
+ /* @__PURE__ */ jsx19(
1354
+ "div",
1355
+ {
1356
+ ref: tabListRef,
1357
+ role: "tablist",
1358
+ "aria-label": ariaLabel,
1359
+ className: "fractal-tabs-list",
1360
+ "data-indicator-ready": indicator ? "true" : void 0,
1361
+ style: indicatorStyle,
1362
+ children: items.map((item) => {
1363
+ const isActive = item.id === activeId;
1364
+ const tabId = `${reactId}-tab-${item.id}`;
1365
+ const panelId = `${reactId}-panel-${item.id}`;
1366
+ return /* @__PURE__ */ jsx19(
1367
+ "button",
1368
+ {
1369
+ ref: (el) => {
1370
+ tabRefs.current[item.id] = el;
1371
+ },
1372
+ id: tabId,
1373
+ role: "tab",
1374
+ type: "button",
1375
+ "aria-selected": isActive,
1376
+ "aria-controls": panelId,
1377
+ tabIndex: isActive ? 0 : -1,
1378
+ disabled: item.disabled,
1379
+ className: cn(
1380
+ "fractal-tabs-trigger",
1381
+ isActive && "fractal-tabs-trigger--active",
1382
+ item.disabled && "fractal-tabs-trigger--disabled"
1383
+ ),
1384
+ onClick: () => !item.disabled && selectTab(item.id),
1385
+ onKeyDown,
1386
+ children: item.label
1387
+ },
1388
+ item.id
1389
+ );
1390
+ })
1391
+ }
1392
+ ),
896
1393
  items.map((item) => {
897
1394
  const tabId = `${reactId}-tab-${item.id}`;
898
1395
  const panelId = `${reactId}-panel-${item.id}`;
899
1396
  const isActive = item.id === activeId;
900
- return /* @__PURE__ */ jsx13(
1397
+ return /* @__PURE__ */ jsx19(
901
1398
  "div",
902
1399
  {
903
1400
  id: panelId,
@@ -913,10 +1410,112 @@ function Tabs({
913
1410
  ] });
914
1411
  }
915
1412
 
1413
+ // src/components/ContentSwitcher/ContentSwitcher.tsx
1414
+ import * as React12 from "react";
1415
+ import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
1416
+ function ContentSwitcher({ items, value, onValueChange, ariaLabel, fullWidth = false, className }) {
1417
+ const switcherRef = React12.useRef(null);
1418
+ const buttonRefs = React12.useRef([]);
1419
+ const indicatorMotionReadyRef = React12.useRef(false);
1420
+ const activeIndex = items.findIndex((item) => item.value === value);
1421
+ React12.useLayoutEffect(() => {
1422
+ const switcher = switcherRef.current;
1423
+ const activeButton = buttonRefs.current[activeIndex];
1424
+ let motionFrame = 0;
1425
+ if (!switcher || !activeButton) {
1426
+ switcher?.removeAttribute("data-indicator-ready");
1427
+ switcher?.removeAttribute("data-indicator-animate");
1428
+ indicatorMotionReadyRef.current = false;
1429
+ return;
1430
+ }
1431
+ const updateIndicator = () => {
1432
+ const switcherRect = switcher.getBoundingClientRect();
1433
+ const activeButtonRect = activeButton.getBoundingClientRect();
1434
+ switcher.style.setProperty("--fractal-content-switcher-indicator-x", `${activeButtonRect.left - switcherRect.left - switcher.clientLeft}px`);
1435
+ switcher.style.setProperty("--fractal-content-switcher-indicator-y", `${activeButtonRect.top - switcherRect.top - switcher.clientTop}px`);
1436
+ switcher.style.setProperty("--fractal-content-switcher-indicator-width", `${activeButtonRect.width}px`);
1437
+ switcher.style.setProperty("--fractal-content-switcher-indicator-height", `${activeButtonRect.height}px`);
1438
+ switcher.setAttribute("data-indicator-ready", "true");
1439
+ if (!indicatorMotionReadyRef.current && motionFrame === 0) {
1440
+ motionFrame = window.requestAnimationFrame(() => {
1441
+ indicatorMotionReadyRef.current = true;
1442
+ switcher.setAttribute("data-indicator-animate", "true");
1443
+ motionFrame = 0;
1444
+ });
1445
+ }
1446
+ };
1447
+ updateIndicator();
1448
+ if (typeof ResizeObserver === "undefined") {
1449
+ return () => {
1450
+ if (motionFrame !== 0) window.cancelAnimationFrame(motionFrame);
1451
+ };
1452
+ }
1453
+ const resizeObserver = new ResizeObserver(updateIndicator);
1454
+ resizeObserver.observe(switcher);
1455
+ buttonRefs.current.forEach((button) => {
1456
+ if (button) resizeObserver.observe(button);
1457
+ });
1458
+ return () => {
1459
+ resizeObserver.disconnect();
1460
+ if (motionFrame !== 0) window.cancelAnimationFrame(motionFrame);
1461
+ };
1462
+ }, [activeIndex, items.length]);
1463
+ const moveFocus = (index, direction) => {
1464
+ let nextIndex = index;
1465
+ do {
1466
+ nextIndex = (nextIndex + direction + items.length) % items.length;
1467
+ } while (items[nextIndex]?.disabled && nextIndex !== index);
1468
+ if (!items[nextIndex]?.disabled) {
1469
+ buttonRefs.current[nextIndex]?.focus();
1470
+ onValueChange(items[nextIndex].value);
1471
+ }
1472
+ };
1473
+ return /* @__PURE__ */ jsxs17(
1474
+ "div",
1475
+ {
1476
+ className: cn("fractal-content-switcher", fullWidth && "fractal-content-switcher--full-width", className),
1477
+ role: "group",
1478
+ "aria-label": ariaLabel,
1479
+ ref: switcherRef,
1480
+ children: [
1481
+ /* @__PURE__ */ jsx20("span", { className: "fractal-content-switcher__indicator", "aria-hidden": true }),
1482
+ items.map((item, index) => {
1483
+ const active = item.value === value;
1484
+ return /* @__PURE__ */ jsx20(
1485
+ "button",
1486
+ {
1487
+ className: cn("fractal-content-switcher__item", active && "fractal-content-switcher__item--active"),
1488
+ type: "button",
1489
+ "aria-pressed": active,
1490
+ disabled: item.disabled,
1491
+ onClick: () => onValueChange(item.value),
1492
+ onKeyDown: (event) => {
1493
+ if (event.key === "ArrowRight") {
1494
+ event.preventDefault();
1495
+ moveFocus(index, 1);
1496
+ }
1497
+ if (event.key === "ArrowLeft") {
1498
+ event.preventDefault();
1499
+ moveFocus(index, -1);
1500
+ }
1501
+ },
1502
+ ref: (element) => {
1503
+ buttonRefs.current[index] = element;
1504
+ },
1505
+ children: item.label
1506
+ },
1507
+ item.value
1508
+ );
1509
+ })
1510
+ ]
1511
+ }
1512
+ );
1513
+ }
1514
+
916
1515
  // src/components/Disclosure/Disclosure.tsx
917
- import { useId as useId2, useState as useState5 } from "react";
918
- import { ChevronRight, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
919
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1516
+ import { useId as useId3, useState as useState7 } from "react";
1517
+ import { ChevronRight as ChevronRight2, ChevronDown as ChevronDown2 } from "@mirror-physics/fractal-icons";
1518
+ import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
920
1519
  function Disclosure({
921
1520
  title,
922
1521
  meta,
@@ -924,16 +1523,18 @@ function Disclosure({
924
1523
  defaultOpen = false,
925
1524
  open: controlledOpen,
926
1525
  onOpenChange,
1526
+ disabled = false,
927
1527
  className,
928
1528
  variant = "card"
929
1529
  }) {
930
- const reactId = useId2();
1530
+ const reactId = useId3();
931
1531
  const headerId = `${reactId}-header`;
932
1532
  const panelId = `${reactId}-panel`;
933
- const [internalOpen, setInternalOpen] = useState5(defaultOpen);
1533
+ const [internalOpen, setInternalOpen] = useState7(defaultOpen);
934
1534
  const isControlled = controlledOpen !== void 0;
935
1535
  const isOpen = isControlled ? controlledOpen : internalOpen;
936
1536
  const toggle = () => {
1537
+ if (disabled) return;
937
1538
  const next = !isOpen;
938
1539
  if (isControlled) {
939
1540
  onOpenChange?.(next);
@@ -942,17 +1543,18 @@ function Disclosure({
942
1543
  onOpenChange?.(next);
943
1544
  }
944
1545
  };
945
- return /* @__PURE__ */ jsxs11(
1546
+ return /* @__PURE__ */ jsxs18(
946
1547
  "div",
947
1548
  {
948
1549
  className: cn(
949
1550
  "fractal-disclosure",
950
1551
  variant === "card" && "fractal-disclosure--card",
951
1552
  isOpen && "fractal-disclosure--open",
1553
+ disabled && "fractal-disclosure--disabled",
952
1554
  className
953
1555
  ),
954
1556
  children: [
955
- /* @__PURE__ */ jsxs11(
1557
+ /* @__PURE__ */ jsxs18(
956
1558
  "button",
957
1559
  {
958
1560
  type: "button",
@@ -960,15 +1562,16 @@ function Disclosure({
960
1562
  className: "fractal-disclosure-trigger",
961
1563
  "aria-expanded": isOpen,
962
1564
  "aria-controls": panelId,
1565
+ disabled,
963
1566
  onClick: toggle,
964
1567
  children: [
965
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx14(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx14(ChevronRight, { size: 16 }) }),
966
- /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-title", children: title }),
967
- meta && /* @__PURE__ */ jsx14("span", { className: "fractal-disclosure-meta", children: meta })
1568
+ /* @__PURE__ */ jsx21("span", { className: "fractal-disclosure-icon", "aria-hidden": "true", children: isOpen ? /* @__PURE__ */ jsx21(ChevronDown2, { size: 16 }) : /* @__PURE__ */ jsx21(ChevronRight2, { size: 16 }) }),
1569
+ /* @__PURE__ */ jsx21("span", { className: "fractal-disclosure-title", children: title }),
1570
+ meta && /* @__PURE__ */ jsx21("span", { className: "fractal-disclosure-meta", children: meta })
968
1571
  ]
969
1572
  }
970
1573
  ),
971
- /* @__PURE__ */ jsx14(
1574
+ /* @__PURE__ */ jsx21(
972
1575
  "div",
973
1576
  {
974
1577
  id: panelId,
@@ -976,7 +1579,7 @@ function Disclosure({
976
1579
  "aria-labelledby": headerId,
977
1580
  className: "fractal-disclosure-panel",
978
1581
  hidden: !isOpen,
979
- children: /* @__PURE__ */ jsx14("div", { className: "fractal-disclosure-panel-inner", children })
1582
+ children: /* @__PURE__ */ jsx21("div", { className: "fractal-disclosure-panel-inner", children })
980
1583
  }
981
1584
  )
982
1585
  ]
@@ -984,39 +1587,260 @@ function Disclosure({
984
1587
  );
985
1588
  }
986
1589
 
1590
+ // src/components/LatticeLoader/LatticeLoader.tsx
1591
+ import { jsx as jsx22 } from "react/jsx-runtime";
1592
+ var LATTICE_COUNT = 160;
1593
+ var WAVE_PERIOD = 80;
1594
+ function LatticeLoader({ className, label = "Loading", ...props }) {
1595
+ return /* @__PURE__ */ jsx22(
1596
+ "span",
1597
+ {
1598
+ "aria-label": label,
1599
+ className: cn("fractal-lattice-loader", className),
1600
+ role: "progressbar",
1601
+ ...props,
1602
+ children: Array.from({ length: LATTICE_COUNT }, (_, index) => /* @__PURE__ */ jsx22(
1603
+ "span",
1604
+ {
1605
+ "aria-hidden": "true",
1606
+ className: "fractal-lattice-loader__cell",
1607
+ style: { "--fractal-lattice-index": index % WAVE_PERIOD }
1608
+ },
1609
+ index
1610
+ ))
1611
+ }
1612
+ );
1613
+ }
1614
+
1615
+ // src/components/Ghost/Ghost.tsx
1616
+ import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
1617
+ var toCssLength = (value) => typeof value === "number" ? `${value}px` : value;
1618
+ function Ghost({ className, width, height, radius, style, ...props }) {
1619
+ return /* @__PURE__ */ jsx23(
1620
+ "div",
1621
+ {
1622
+ "aria-hidden": "true",
1623
+ className: cn("fractal-ghost", className),
1624
+ style: {
1625
+ width: toCssLength(width),
1626
+ height: toCssLength(height),
1627
+ borderRadius: toCssLength(radius),
1628
+ ...style
1629
+ },
1630
+ ...props
1631
+ }
1632
+ );
1633
+ }
1634
+ function GhostLine({ className, size = "md", width = "100%", ...props }) {
1635
+ return /* @__PURE__ */ jsx23(Ghost, { className: cn("fractal-ghost-line", `fractal-ghost-line--${size}`, className), width, ...props });
1636
+ }
1637
+ function ButtonGhost({ className, size = "md", iconOnly = false, width, ...props }) {
1638
+ return /* @__PURE__ */ jsx23(Ghost, { className: cn("fractal-ghost-button", `fractal-ghost-button--${size}`, iconOnly && "fractal-ghost-button--icon-only", className), width, ...props });
1639
+ }
1640
+ function CardGhost({ className, lines = 3, showHeader = true, ...props }) {
1641
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-card", className), ...props, children: [
1642
+ showHeader && /* @__PURE__ */ jsx23(GhostLine, { width: "38%" }),
1643
+ /* @__PURE__ */ jsx23("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx23(GhostLine, { width: index === lines - 1 ? "62%" : "100%" }, index)) })
1644
+ ] });
1645
+ }
1646
+ function DataTableGhost({
1647
+ className,
1648
+ showHeader = true,
1649
+ headers,
1650
+ columns = 4,
1651
+ rows = 5,
1652
+ columnWidths,
1653
+ size = "md",
1654
+ ...props
1655
+ }) {
1656
+ const cells = Array.from({ length: columns }, (_, index) => index);
1657
+ return /* @__PURE__ */ jsx23("div", { "aria-busy": "true", "aria-label": "Loading table", className: cn("fractal-ghost-table", `fractal-ghost-table--${size}`, className), role: "status", ...props, children: /* @__PURE__ */ jsxs19("table", { className: "fractal-ghost-table__table", children: [
1658
+ (showHeader || headers) && /* @__PURE__ */ jsx23("thead", { children: /* @__PURE__ */ jsx23("tr", { className: "fractal-ghost-table__head-row", children: cells.map((index) => /* @__PURE__ */ jsx23("th", { style: { width: columnWidths?.[index] }, children: headers?.[index] ?? /* @__PURE__ */ jsx23(GhostLine, { size: "sm", width: index === columns - 1 ? "62%" : "76%" }) }, index)) }) }),
1659
+ /* @__PURE__ */ jsx23("tbody", { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx23("tr", { className: "fractal-ghost-table__row", children: cells.map((columnIndex) => /* @__PURE__ */ jsx23("td", { style: { width: columnWidths?.[columnIndex] }, children: /* @__PURE__ */ jsx23(GhostLine, { width: `${[72, 54, 82, 63, 46][(rowIndex + columnIndex) % 5]}%` }) }, columnIndex)) }, rowIndex)) })
1660
+ ] }) });
1661
+ }
1662
+ function InputGhost({ className, labelWidth = "26%", ...props }) {
1663
+ return /* @__PURE__ */ jsx23(FormFieldGhost, { className, labelWidth, ...props });
1664
+ }
1665
+ function TextareaGhost({ className, labelWidth = "26%", ...props }) {
1666
+ return /* @__PURE__ */ jsx23(FormFieldGhost, { className, labelWidth, multiline: true, ...props });
1667
+ }
1668
+ function LabelGhost({ className, width = "26%", ...props }) {
1669
+ return /* @__PURE__ */ jsx23(GhostLine, { className: cn("fractal-ghost-label", className), size: "sm", width, ...props });
1670
+ }
1671
+ function FormFieldGhost({ className, labelWidth = "26%", multiline = false, ...props }) {
1672
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-field", className), ...props, children: [
1673
+ /* @__PURE__ */ jsx23(LabelGhost, { width: labelWidth }),
1674
+ /* @__PURE__ */ jsx23(Ghost, { className: cn("fractal-ghost-field__control", multiline && "fractal-ghost-field__control--multiline") })
1675
+ ] });
1676
+ }
1677
+ function AlertGhost({ className, ...props }) {
1678
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-alert", className), ...props, children: [
1679
+ /* @__PURE__ */ jsx23(GhostLine, { width: "30%" }),
1680
+ /* @__PURE__ */ jsx23(GhostLine, { size: "sm", width: "78%" })
1681
+ ] });
1682
+ }
1683
+ function CheckboxGhost({ className, ...props }) {
1684
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-choice", className), ...props, children: [
1685
+ /* @__PURE__ */ jsx23(Ghost, { className: "fractal-ghost-choice__control" }),
1686
+ /* @__PURE__ */ jsx23(GhostLine, { width: "42%" })
1687
+ ] });
1688
+ }
1689
+ var ToggleGhost = CheckboxGhost;
1690
+ function ChipGhost({ className, width = 74, ...props }) {
1691
+ return /* @__PURE__ */ jsx23(Ghost, { className: cn("fractal-ghost-chip", className), height: 24, width, ...props });
1692
+ }
1693
+ function LinkGhost({ className, width = 96, ...props }) {
1694
+ return /* @__PURE__ */ jsx23(GhostLine, { className: cn("fractal-ghost-link", className), size: "sm", width, ...props });
1695
+ }
1696
+ var TextButtonGhost = LinkGhost;
1697
+ function DropdownGhost({ className, ...props }) {
1698
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropdown", className), ...props, children: [
1699
+ /* @__PURE__ */ jsx23(GhostLine, { width: "38%" }),
1700
+ /* @__PURE__ */ jsx23(Ghost, { className: "fractal-ghost-dropdown__chevron" })
1701
+ ] });
1702
+ }
1703
+ function ContentSwitcherGhost({ className, options = 3, ...props }) {
1704
+ return /* @__PURE__ */ jsx23("div", { "aria-hidden": "true", className: cn("fractal-ghost-switcher", className), ...props, children: Array.from({ length: options }, (_, index) => /* @__PURE__ */ jsx23(Ghost, {}, index)) });
1705
+ }
1706
+ var TabsGhost = ContentSwitcherGhost;
1707
+ function DisclosureGhost({ className, ...props }) {
1708
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-disclosure", className), ...props, children: [
1709
+ /* @__PURE__ */ jsx23(Ghost, { className: "fractal-ghost-disclosure__icon" }),
1710
+ /* @__PURE__ */ jsx23(GhostLine, { width: "42%" })
1711
+ ] });
1712
+ }
1713
+ function PaginationGhost({ className, pages = 5, ...props }) {
1714
+ return /* @__PURE__ */ jsx23("div", { "aria-hidden": "true", className: cn("fractal-ghost-pagination", className), ...props, children: Array.from({ length: pages }, (_, index) => /* @__PURE__ */ jsx23(Ghost, {}, index)) });
1715
+ }
1716
+ function FileDropzoneGhost({ className, ...props }) {
1717
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-dropzone", className), ...props, children: [
1718
+ /* @__PURE__ */ jsx23(Ghost, { className: "fractal-ghost-dropzone__icon" }),
1719
+ /* @__PURE__ */ jsx23(GhostLine, { width: "26%" }),
1720
+ /* @__PURE__ */ jsx23(GhostLine, { size: "sm", width: "46%" })
1721
+ ] });
1722
+ }
1723
+ function PromptCardGhost({ className, ...props }) {
1724
+ return /* @__PURE__ */ jsx23(CardGhost, { className: cn("fractal-ghost-prompt-card", className), lines: 4, ...props });
1725
+ }
1726
+ function ConversationGhost({
1727
+ className,
1728
+ "aria-label": ariaLabel = "Loading conversation",
1729
+ ...props
1730
+ }) {
1731
+ return /* @__PURE__ */ jsxs19(
1732
+ "div",
1733
+ {
1734
+ "aria-busy": "true",
1735
+ "aria-label": ariaLabel,
1736
+ className: cn("fractal-ghost-conversation", className),
1737
+ role: "status",
1738
+ ...props,
1739
+ children: [
1740
+ /* @__PURE__ */ jsx23("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--user", children: /* @__PURE__ */ jsxs19("div", { className: "fractal-ghost-conversation__bubble", children: [
1741
+ /* @__PURE__ */ jsx23(GhostLine, { width: "100%" }),
1742
+ /* @__PURE__ */ jsx23(GhostLine, { width: "86%" }),
1743
+ /* @__PURE__ */ jsx23(GhostLine, { width: "64%" })
1744
+ ] }) }),
1745
+ /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: "fractal-ghost-conversation__message fractal-ghost-conversation__message--assistant", children: [
1746
+ /* @__PURE__ */ jsx23(GhostLine, { width: "92%" }),
1747
+ /* @__PURE__ */ jsx23(GhostLine, { width: "78%" }),
1748
+ /* @__PURE__ */ jsx23(GhostLine, { width: "58%" })
1749
+ ] })
1750
+ ]
1751
+ }
1752
+ );
1753
+ }
1754
+ function SortableTableGhost(props) {
1755
+ return /* @__PURE__ */ jsx23(DataTableGhost, { ...props });
1756
+ }
1757
+ function SidebarGhost({ className, items = 6, ...props }) {
1758
+ return /* @__PURE__ */ jsxs19("aside", { "aria-hidden": "true", className: cn("fractal-ghost-sidebar", className), ...props, children: [
1759
+ /* @__PURE__ */ jsx23(Ghost, { className: "fractal-ghost-sidebar__brand" }),
1760
+ /* @__PURE__ */ jsx23("div", { className: "fractal-ghost-sidebar__items", children: Array.from({ length: items }, (_, index) => /* @__PURE__ */ jsxs19("div", { className: "fractal-ghost-sidebar__item", children: [
1761
+ /* @__PURE__ */ jsx23(Ghost, {}),
1762
+ /* @__PURE__ */ jsx23(GhostLine, { width: `${[54, 68, 44, 61][index % 4]}%` })
1763
+ ] }, index)) })
1764
+ ] });
1765
+ }
1766
+ function ModalGhost({ className, title, lines = 3, ...props }) {
1767
+ return /* @__PURE__ */ jsx23(SurfaceGhost, { className: cn("fractal-ghost-modal", className), title, lines, ...props });
1768
+ }
1769
+ function SidePanelGhost({ className, title, lines = 5, ...props }) {
1770
+ return /* @__PURE__ */ jsx23(SurfaceGhost, { className: cn("fractal-ghost-side-panel", className), title, lines, ...props });
1771
+ }
1772
+ function SurfaceGhost({ className, title, lines = 3, ...props }) {
1773
+ return /* @__PURE__ */ jsxs19("div", { "aria-hidden": "true", className: cn("fractal-ghost-surface", className), ...props, children: [
1774
+ /* @__PURE__ */ jsxs19("div", { className: "fractal-ghost-surface__header", children: [
1775
+ /* @__PURE__ */ jsx23(GhostLine, { width: "42%" }),
1776
+ /* @__PURE__ */ jsx23(Ghost, { className: "fractal-ghost-surface__close" })
1777
+ ] }),
1778
+ title && /* @__PURE__ */ jsx23("div", { className: "fractal-ghost-surface__title", children: title }),
1779
+ /* @__PURE__ */ jsx23("div", { className: "fractal-ghost-stack", children: Array.from({ length: lines }, (_, index) => /* @__PURE__ */ jsx23(GhostLine, { width: index === lines - 1 ? "64%" : "100%" }, index)) })
1780
+ ] });
1781
+ }
1782
+
987
1783
  // src/components/Sidebar/Sidebar.tsx
988
- import * as React10 from "react";
989
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
990
- var Sidebar = React10.forwardRef(
991
- ({ className, width, style, ...props }, ref) => /* @__PURE__ */ jsx15(
1784
+ import * as React13 from "react";
1785
+ import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1786
+ var Sidebar = React13.forwardRef(
1787
+ ({ className, width, compact = false, style, ...props }, ref) => /* @__PURE__ */ jsx24(
992
1788
  "aside",
993
1789
  {
994
1790
  ref,
995
1791
  className: cn("fractal-sidebar", className),
1792
+ "data-compact": compact || void 0,
996
1793
  style: { ...style, ...width ? { "--fractal-sidebar-width": width } : {} },
997
1794
  ...props
998
1795
  }
999
1796
  )
1000
1797
  );
1001
1798
  Sidebar.displayName = "Sidebar";
1002
- var SidebarHeader = React10.forwardRef(
1003
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1799
+ var SidebarHeader = React13.forwardRef(
1800
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx24("div", { ref, className: cn("fractal-sidebar__header", className), ...props })
1004
1801
  );
1005
1802
  SidebarHeader.displayName = "SidebarHeader";
1006
- var SidebarBrand = React10.forwardRef(
1007
- ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx15("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
1803
+ var SidebarBrand = React13.forwardRef(
1804
+ ({ className, type = "button", ...props }, ref) => /* @__PURE__ */ jsx24("button", { ref, type, className: cn("fractal-sidebar__brand", className), ...props })
1008
1805
  );
1009
1806
  SidebarBrand.displayName = "SidebarBrand";
1010
- var SidebarNav = React10.forwardRef(
1011
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1807
+ function SidebarAccountMenu({
1808
+ name,
1809
+ items,
1810
+ onSelect,
1811
+ triggerLabel = "Account menu",
1812
+ icon,
1813
+ className
1814
+ }) {
1815
+ return /* @__PURE__ */ jsx24(
1816
+ Dropdown,
1817
+ {
1818
+ align: "left",
1819
+ className: cn("fractal-sidebar__account", className),
1820
+ items,
1821
+ onSelect,
1822
+ panelClassName: "fractal-sidebar__account-panel",
1823
+ selectedValue: "",
1824
+ triggerClassName: "fractal-sidebar__account-trigger",
1825
+ triggerContent: /* @__PURE__ */ jsxs20(Fragment3, { children: [
1826
+ icon && /* @__PURE__ */ jsx24("span", { className: "fractal-sidebar__account-icon", "aria-hidden": "true", children: icon }),
1827
+ /* @__PURE__ */ jsx24("span", { className: "fractal-sidebar__account-name", children: name })
1828
+ ] }),
1829
+ triggerLabel,
1830
+ triggerVariant: "tertiary"
1831
+ }
1832
+ );
1833
+ }
1834
+ var SidebarNav = React13.forwardRef(
1835
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx24("nav", { ref, className: cn("fractal-sidebar__nav", className), ...props })
1012
1836
  );
1013
1837
  SidebarNav.displayName = "SidebarNav";
1014
- var SidebarSection = React10.forwardRef(
1015
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1838
+ var SidebarSection = React13.forwardRef(
1839
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx24("div", { ref, className: cn("fractal-sidebar__section", className), ...props })
1016
1840
  );
1017
1841
  SidebarSection.displayName = "SidebarSection";
1018
- var SidebarNavItem = React10.forwardRef(
1019
- ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs12(
1842
+ var SidebarNavItem = React13.forwardRef(
1843
+ ({ active = false, icon, endAdornment, className, type = "button", children, ...props }, ref) => /* @__PURE__ */ jsxs20(
1020
1844
  "button",
1021
1845
  {
1022
1846
  ref,
@@ -1026,42 +1850,28 @@ var SidebarNavItem = React10.forwardRef(
1026
1850
  "aria-current": active ? "page" : void 0,
1027
1851
  ...props,
1028
1852
  children: [
1029
- icon && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1030
- /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-label", children }),
1031
- endAdornment && /* @__PURE__ */ jsx15("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1853
+ icon && /* @__PURE__ */ jsx24("span", { className: "fractal-sidebar__nav-item-icon", "aria-hidden": "true", children: icon }),
1854
+ /* @__PURE__ */ jsx24("span", { className: "fractal-sidebar__nav-item-label", children }),
1855
+ endAdornment && /* @__PURE__ */ jsx24("span", { className: "fractal-sidebar__nav-item-end", children: endAdornment })
1032
1856
  ]
1033
1857
  }
1034
1858
  )
1035
1859
  );
1036
1860
  SidebarNavItem.displayName = "SidebarNavItem";
1037
- var SidebarFooter = React10.forwardRef(
1038
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx15("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1861
+ var SidebarFooter = React13.forwardRef(
1862
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx24("div", { ref, className: cn("fractal-sidebar__footer", className), ...props })
1039
1863
  );
1040
1864
  SidebarFooter.displayName = "SidebarFooter";
1041
1865
 
1042
- // src/components/PageHeader/PageHeader.tsx
1043
- import * as React11 from "react";
1044
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1045
- var PageHeader = React11.forwardRef(
1046
- ({ title, description, actions, className, ...props }, ref) => /* @__PURE__ */ jsxs13("div", { ref, className: cn("fractal-page-header", className), ...props, children: [
1047
- /* @__PURE__ */ jsxs13("div", { className: "fractal-page-header__content", children: [
1048
- /* @__PURE__ */ jsx16("h1", { className: "fractal-page-header__title", children: title }),
1049
- description && /* @__PURE__ */ jsx16("p", { className: "fractal-page-header__description", children: description })
1050
- ] }),
1051
- actions && /* @__PURE__ */ jsx16("div", { className: "fractal-page-header__actions", children: actions })
1052
- ] })
1053
- );
1054
- PageHeader.displayName = "PageHeader";
1055
-
1056
1866
  // src/components/SidePanel/SidePanel.tsx
1057
1867
  import { createPortal as createPortal2 } from "react-dom";
1058
1868
  import { Close } from "@mirror-physics/fractal-icons";
1059
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1060
- function SidePanel({ open, onClose, title, description, children, footer, size = "md", className }) {
1869
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
1870
+ function SidePanel({ open, onClose, title, description, children, footer, size = "md", loading = false, loadingLabel = "Loading", className }) {
1061
1871
  useEscapeDismiss(80, onClose, open);
1062
1872
  if (!open) return null;
1063
1873
  return createPortal2(
1064
- /* @__PURE__ */ jsx17("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs14(
1874
+ /* @__PURE__ */ jsx25("div", { className: "fractal-side-panel-overlay", onMouseDown: onClose, children: /* @__PURE__ */ jsxs21(
1065
1875
  "aside",
1066
1876
  {
1067
1877
  className: cn("fractal-side-panel", `fractal-side-panel--${size}`, className),
@@ -1070,15 +1880,15 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
1070
1880
  role: "dialog",
1071
1881
  onMouseDown: (event) => event.stopPropagation(),
1072
1882
  children: [
1073
- /* @__PURE__ */ jsxs14("header", { className: "fractal-side-panel__header", children: [
1074
- /* @__PURE__ */ jsxs14("div", { className: "fractal-side-panel__heading", children: [
1075
- /* @__PURE__ */ jsx17("h2", { className: "fractal-side-panel__title", children: title }),
1076
- description && /* @__PURE__ */ jsx17("p", { className: "fractal-side-panel__description", children: description })
1883
+ /* @__PURE__ */ jsxs21("header", { className: "fractal-side-panel__header", children: [
1884
+ /* @__PURE__ */ jsxs21("div", { className: "fractal-side-panel__heading", children: [
1885
+ typeof title === "string" ? /* @__PURE__ */ jsx25("h2", { className: "fractal-side-panel__title", children: title }) : /* @__PURE__ */ jsx25("div", { className: "fractal-side-panel__title", children: title }),
1886
+ description && /* @__PURE__ */ jsx25("p", { className: "fractal-side-panel__description", children: description })
1077
1887
  ] }),
1078
- /* @__PURE__ */ jsx17("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx17(Close, { size: 18 }) })
1888
+ /* @__PURE__ */ jsx25("button", { type: "button", className: "fractal-side-panel__close", "aria-label": "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx25(Close, { size: 18 }) })
1079
1889
  ] }),
1080
- /* @__PURE__ */ jsx17("div", { className: "fractal-side-panel__body", children }),
1081
- footer && /* @__PURE__ */ jsx17("footer", { className: "fractal-side-panel__footer", children: footer })
1890
+ /* @__PURE__ */ jsx25("div", { "aria-busy": loading || void 0, className: cn("fractal-side-panel__body", loading && "fractal-side-panel__body--loading"), children: loading ? /* @__PURE__ */ jsx25(UILoader, { label: loadingLabel }) : children }),
1891
+ footer && /* @__PURE__ */ jsx25("footer", { className: "fractal-side-panel__footer", children: footer })
1082
1892
  ]
1083
1893
  }
1084
1894
  ) }),
@@ -1087,48 +1897,271 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
1087
1897
  }
1088
1898
 
1089
1899
  // src/components/Textarea/Textarea.tsx
1090
- import * as React12 from "react";
1091
- import { jsx as jsx18 } from "react/jsx-runtime";
1092
- var Textarea = React12.forwardRef(
1093
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx18("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1900
+ import * as React14 from "react";
1901
+ import { jsx as jsx26 } from "react/jsx-runtime";
1902
+ var Textarea = React14.forwardRef(
1903
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx26("textarea", { ref, className: cn("fractal-textarea", className), ...props })
1094
1904
  );
1095
1905
  Textarea.displayName = "Textarea";
1906
+
1907
+ // src/components/TextButton/TextButton.tsx
1908
+ import * as React15 from "react";
1909
+ import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
1910
+ var TextButton = React15.forwardRef(
1911
+ ({ className, icon, iconPosition = "start", size = "md", tone = "primary", children, ...props }, ref) => /* @__PURE__ */ jsxs22(
1912
+ "button",
1913
+ {
1914
+ ref,
1915
+ className: cn("fractal-text-button", `fractal-text-button--${size}`, `fractal-text-button--${tone}`, className),
1916
+ type: "button",
1917
+ ...props,
1918
+ children: [
1919
+ icon && iconPosition === "start" && /* @__PURE__ */ jsx27("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon }),
1920
+ /* @__PURE__ */ jsx27("span", { children }),
1921
+ icon && iconPosition === "end" && /* @__PURE__ */ jsx27("span", { className: "fractal-text-button__icon", "aria-hidden": "true", children: icon })
1922
+ ]
1923
+ }
1924
+ )
1925
+ );
1926
+ TextButton.displayName = "TextButton";
1927
+
1928
+ // src/components/FileDropzone/FileDropzone.tsx
1929
+ import * as React16 from "react";
1930
+ import { Close as Close2, FileArrowUp } from "@mirror-physics/fractal-icons";
1931
+ import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
1932
+ function FileDropzone({
1933
+ files,
1934
+ onFilesChange,
1935
+ accept,
1936
+ multiple = true,
1937
+ disabled = false,
1938
+ title = "Drop files here or click to browse",
1939
+ description = "Files stay attached to this record.",
1940
+ className
1941
+ }) {
1942
+ const inputId = React16.useId();
1943
+ const inputRef = React16.useRef(null);
1944
+ const [isDragging, setIsDragging] = React16.useState(false);
1945
+ const addFiles = (nextFiles) => {
1946
+ if (disabled) return;
1947
+ const additions = Array.from(nextFiles);
1948
+ if (additions.length === 0) return;
1949
+ onFilesChange(multiple ? [...files, ...additions] : additions.slice(0, 1));
1950
+ };
1951
+ return /* @__PURE__ */ jsxs23("div", { className: cn("fractal-file-dropzone", className), children: [
1952
+ /* @__PURE__ */ jsx28(
1953
+ "input",
1954
+ {
1955
+ ref: inputRef,
1956
+ id: inputId,
1957
+ className: "fractal-file-dropzone__input",
1958
+ type: "file",
1959
+ accept,
1960
+ multiple,
1961
+ disabled,
1962
+ onChange: (event) => {
1963
+ if (event.target.files) addFiles(event.target.files);
1964
+ event.target.value = "";
1965
+ }
1966
+ }
1967
+ ),
1968
+ /* @__PURE__ */ jsxs23(
1969
+ "button",
1970
+ {
1971
+ className: "fractal-file-dropzone__target",
1972
+ "data-dragging": isDragging || void 0,
1973
+ type: "button",
1974
+ disabled,
1975
+ onClick: () => inputRef.current?.click(),
1976
+ onDragEnter: (event) => {
1977
+ event.preventDefault();
1978
+ if (!disabled) setIsDragging(true);
1979
+ },
1980
+ onDragOver: (event) => event.preventDefault(),
1981
+ onDragLeave: (event) => {
1982
+ event.preventDefault();
1983
+ setIsDragging(false);
1984
+ },
1985
+ onDrop: (event) => {
1986
+ event.preventDefault();
1987
+ setIsDragging(false);
1988
+ addFiles(event.dataTransfer.files);
1989
+ },
1990
+ children: [
1991
+ /* @__PURE__ */ jsx28(FileArrowUp, { "aria-hidden": "true", size: 20 }),
1992
+ /* @__PURE__ */ jsxs23("span", { className: "fractal-file-dropzone__copy", children: [
1993
+ /* @__PURE__ */ jsx28("span", { className: "fractal-file-dropzone__title", children: title }),
1994
+ description && /* @__PURE__ */ jsx28("span", { className: "fractal-file-dropzone__description", children: description })
1995
+ ] })
1996
+ ]
1997
+ }
1998
+ ),
1999
+ files.length > 0 && /* @__PURE__ */ jsx28("ul", { className: "fractal-file-dropzone__files", "aria-label": "Attached files", children: files.map((file, index) => /* @__PURE__ */ jsxs23("li", { className: "fractal-file-dropzone__file", children: [
2000
+ /* @__PURE__ */ jsx28("span", { className: "fractal-file-dropzone__file-name", children: file.name }),
2001
+ /* @__PURE__ */ jsx28("span", { className: "fractal-file-dropzone__file-meta", children: formatFileSize(file.size) }),
2002
+ /* @__PURE__ */ jsx28(
2003
+ "button",
2004
+ {
2005
+ className: "fractal-file-dropzone__remove",
2006
+ type: "button",
2007
+ "aria-label": `Remove ${file.name}`,
2008
+ disabled,
2009
+ onClick: () => onFilesChange(files.filter((_, fileIndex) => fileIndex !== index)),
2010
+ children: /* @__PURE__ */ jsx28(Close2, { "aria-hidden": "true", size: 14 })
2011
+ }
2012
+ )
2013
+ ] }, `${file.name}-${file.size}-${index}`)) })
2014
+ ] });
2015
+ }
2016
+ function formatFileSize(bytes) {
2017
+ if (bytes < 1024) return `${bytes} B`;
2018
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
2019
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2020
+ }
2021
+
2022
+ // src/components/Pagination/Pagination.tsx
2023
+ import { ArrowLeft, ArrowRight } from "@mirror-physics/fractal-icons";
2024
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2025
+ function Pagination({
2026
+ page,
2027
+ pageSize,
2028
+ totalItems,
2029
+ onPageChange,
2030
+ pageSizeOptions,
2031
+ onPageSizeChange,
2032
+ ariaLabel = "Pagination",
2033
+ className
2034
+ }) {
2035
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
2036
+ const currentPage = Math.min(Math.max(page, 1), totalPages);
2037
+ const start = totalItems === 0 ? 0 : (currentPage - 1) * pageSize + 1;
2038
+ const end = Math.min(currentPage * pageSize, totalItems);
2039
+ const canChangePageSize = pageSizeOptions != null && pageSizeOptions.length > 0 && onPageSizeChange != null;
2040
+ return /* @__PURE__ */ jsxs24("nav", { className: cn("fractal-pagination", className), "aria-label": ariaLabel, children: [
2041
+ /* @__PURE__ */ jsx29("span", { className: "fractal-pagination__summary", children: totalItems === 0 ? "No results" : `${start}-${end} of ${totalItems}` }),
2042
+ /* @__PURE__ */ jsxs24("div", { className: "fractal-pagination__controls", children: [
2043
+ canChangePageSize && /* @__PURE__ */ jsx29(
2044
+ Dropdown,
2045
+ {
2046
+ align: "right",
2047
+ className: "fractal-pagination__page-size",
2048
+ direction: "up",
2049
+ items: pageSizeOptions.map((option) => ({
2050
+ value: String(option),
2051
+ description: `${option} per page`
2052
+ })),
2053
+ onSelect: (value) => onPageSizeChange(Number(value)),
2054
+ selectedValue: String(pageSize),
2055
+ size: "sm",
2056
+ triggerLabel: "Results per page",
2057
+ triggerVariant: "tertiary"
2058
+ }
2059
+ ),
2060
+ /* @__PURE__ */ jsx29(
2061
+ "button",
2062
+ {
2063
+ className: "fractal-pagination__button",
2064
+ type: "button",
2065
+ "aria-label": "Previous page",
2066
+ title: "Previous page",
2067
+ disabled: currentPage === 1 || totalItems === 0,
2068
+ onClick: () => onPageChange(currentPage - 1),
2069
+ children: /* @__PURE__ */ jsx29(ArrowLeft, { "aria-hidden": "true", size: 16 })
2070
+ }
2071
+ ),
2072
+ /* @__PURE__ */ jsxs24("span", { className: "fractal-pagination__page", "aria-current": "page", children: [
2073
+ currentPage,
2074
+ " of ",
2075
+ totalPages
2076
+ ] }),
2077
+ /* @__PURE__ */ jsx29(
2078
+ "button",
2079
+ {
2080
+ className: "fractal-pagination__button",
2081
+ type: "button",
2082
+ "aria-label": "Next page",
2083
+ title: "Next page",
2084
+ disabled: currentPage === totalPages || totalItems === 0,
2085
+ onClick: () => onPageChange(currentPage + 1),
2086
+ children: /* @__PURE__ */ jsx29(ArrowRight, { "aria-hidden": "true", size: 16 })
2087
+ }
2088
+ )
2089
+ ] })
2090
+ ] });
2091
+ }
1096
2092
  export {
1097
2093
  Alert,
1098
2094
  AlertDescription,
2095
+ AlertGhost,
1099
2096
  AlertTitle,
2097
+ Breadcrumbs,
1100
2098
  Button,
2099
+ ButtonGhost,
1101
2100
  Card,
1102
2101
  CardContent,
1103
2102
  CardDescription,
1104
2103
  CardFooter,
2104
+ CardGhost,
1105
2105
  CardHeader,
1106
2106
  CardTitle,
2107
+ Checkbox,
2108
+ CheckboxGhost,
1107
2109
  Chip,
2110
+ ChipGhost,
2111
+ CodeBlock,
2112
+ ContentSwitcher,
2113
+ ContentSwitcherGhost,
2114
+ ConversationGhost,
1108
2115
  DataTable,
2116
+ DataTableGhost,
1109
2117
  Disclosure,
2118
+ DisclosureGhost,
1110
2119
  Dropdown,
2120
+ DropdownGhost,
1111
2121
  FeatureCard,
2122
+ FileDropzone,
2123
+ FileDropzoneGhost,
1112
2124
  FolderCard,
2125
+ Ghost,
2126
+ GhostLine,
1113
2127
  Input,
2128
+ InputGhost,
1114
2129
  Label,
2130
+ LabelGhost,
2131
+ LatticeLoader,
1115
2132
  Link,
2133
+ LinkGhost,
1116
2134
  Modal,
1117
- PageHeader,
2135
+ ModalGhost,
2136
+ NumberInput,
2137
+ Pagination,
2138
+ PaginationGhost,
2139
+ PasswordInput,
1118
2140
  PromptCard,
2141
+ PromptCardGhost,
1119
2142
  PromptGrid,
1120
2143
  SidePanel,
2144
+ SidePanelGhost,
1121
2145
  Sidebar,
2146
+ SidebarAccountMenu,
1122
2147
  SidebarBrand,
1123
2148
  SidebarFooter,
2149
+ SidebarGhost,
1124
2150
  SidebarHeader,
1125
2151
  SidebarNav,
1126
2152
  SidebarNavItem,
1127
2153
  SidebarSection,
1128
2154
  SortableTable,
2155
+ SortableTableGhost,
1129
2156
  Tabs,
2157
+ TabsGhost,
2158
+ TextButton,
2159
+ TextButtonGhost,
1130
2160
  Textarea,
2161
+ TextareaGhost,
1131
2162
  Toggle,
2163
+ ToggleGhost,
2164
+ UILoader,
1132
2165
  cn,
1133
2166
  useEscapeDismiss
1134
2167
  };