@estiva-app/ui 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +28 -0
  2. package/dist/Avatar.d.ts +1 -1
  3. package/dist/Avatar.d.ts.map +1 -1
  4. package/dist/AvatarGroup.d.ts.map +1 -1
  5. package/dist/ChipInput.d.ts +14 -1
  6. package/dist/ChipInput.d.ts.map +1 -1
  7. package/dist/Divider.d.ts +5 -2
  8. package/dist/Divider.d.ts.map +1 -1
  9. package/dist/EmptyState.d.ts +3 -0
  10. package/dist/EmptyState.d.ts.map +1 -1
  11. package/dist/Property.d.ts +7 -0
  12. package/dist/Property.d.ts.map +1 -1
  13. package/dist/Reaction.d.ts +12 -6
  14. package/dist/Reaction.d.ts.map +1 -1
  15. package/dist/TextInput.d.ts +10 -1
  16. package/dist/TextInput.d.ts.map +1 -1
  17. package/dist/Toast.d.ts +19 -6
  18. package/dist/Toast.d.ts.map +1 -1
  19. package/dist/Toolbar.d.ts +1 -1
  20. package/dist/Toolbar.d.ts.map +1 -1
  21. package/dist/eslint/escape.d.ts +61 -0
  22. package/dist/eslint/escape.d.ts.map +1 -0
  23. package/dist/eslint/index.d.ts +45 -0
  24. package/dist/eslint/index.d.ts.map +1 -0
  25. package/dist/eslint/index.js +118 -0
  26. package/dist/eslint/index.js.map +7 -0
  27. package/dist/eslint/no-raw-button.d.ts +11 -0
  28. package/dist/eslint/no-raw-button.d.ts.map +1 -0
  29. package/dist/index.js +276 -249
  30. package/dist/index.js.map +3 -3
  31. package/package.json +5 -1
  32. package/src/AttachmentCard.tsx +14 -14
  33. package/src/Avatar.mdx +7 -2
  34. package/src/Avatar.picture.test.tsx +60 -0
  35. package/src/Avatar.stories.tsx +1 -1
  36. package/src/Avatar.tsx +50 -14
  37. package/src/AvatarGroup.tsx +1 -0
  38. package/src/Breadcrumb.tsx +2 -2
  39. package/src/Checkbox.tsx +1 -1
  40. package/src/Chip.tsx +1 -1
  41. package/src/ChipInput.mdx +9 -0
  42. package/src/ChipInput.stories.tsx +18 -0
  43. package/src/ChipInput.test.tsx +18 -0
  44. package/src/ChipInput.tsx +18 -4
  45. package/src/DialogShell.stories.tsx +1 -1
  46. package/src/Divider.mdx +3 -2
  47. package/src/Divider.test.tsx +15 -0
  48. package/src/Divider.tsx +11 -9
  49. package/src/EditableText.stories.tsx +2 -2
  50. package/src/EmptyState.mdx +3 -2
  51. package/src/EmptyState.stories.tsx +7 -3
  52. package/src/EmptyState.test.tsx +15 -0
  53. package/src/EmptyState.tsx +4 -1
  54. package/src/IconButton.stories.tsx +1 -1
  55. package/src/Kbd.tsx +3 -3
  56. package/src/Menu.stories.tsx +2 -2
  57. package/src/Menu.tsx +3 -3
  58. package/src/Popover.stories.tsx +1 -1
  59. package/src/PreviewCard.stories.tsx +5 -5
  60. package/src/Property.mdx +8 -0
  61. package/src/Property.stories.tsx +4 -4
  62. package/src/Property.test.tsx +44 -0
  63. package/src/Property.tsx +23 -8
  64. package/src/RailItem.tsx +1 -1
  65. package/src/Reaction.mdx +14 -3
  66. package/src/Reaction.test.tsx +62 -0
  67. package/src/Reaction.tsx +21 -12
  68. package/src/ReactionPicker.tsx +1 -1
  69. package/src/SectionLabel.stories.tsx +1 -1
  70. package/src/SectionLabel.tsx +1 -1
  71. package/src/Select.test.tsx +20 -3
  72. package/src/Select.tsx +3 -3
  73. package/src/TextInput.mdx +6 -0
  74. package/src/TextInput.stories.tsx +45 -0
  75. package/src/TextInput.test.tsx +38 -0
  76. package/src/TextInput.tsx +16 -4
  77. package/src/Textarea.tsx +1 -1
  78. package/src/Toast.mdx +28 -4
  79. package/src/Toast.stories.tsx +12 -1
  80. package/src/Toast.test.tsx +137 -0
  81. package/src/Toast.tsx +127 -83
  82. package/src/Toolbar.tsx +4 -2
  83. package/src/cn.ts +1 -1
  84. package/src/eslint/escape.ts +112 -0
  85. package/src/eslint/index.test.ts +82 -0
  86. package/src/eslint/index.ts +100 -0
  87. package/src/eslint/no-raw-button.test.ts +118 -0
  88. package/src/eslint/no-raw-button.ts +39 -0
  89. package/stories/Choosing.mdx +1 -0
  90. package/stories/TokensPage.tsx +6 -1
  91. package/tailwind-preset.js +7 -0
package/dist/index.js CHANGED
@@ -17,7 +17,8 @@ var FONT_SIZE_TOKENS = [
17
17
  "input-label",
18
18
  "input-value",
19
19
  "input-helper",
20
- "chip"
20
+ "chip",
21
+ "small"
21
22
  ];
22
23
  var BOX_SHADOW_TOKENS = ["sm", "md", "lg", "focus-ring", "glow-warning", "glow-success", "glow-accent", "highlight-inset"];
23
24
  var DROP_SHADOW_TOKENS = ["glow-success", "glow-accent", "glow-warning"];
@@ -253,9 +254,9 @@ function Kbd({ children, className }) {
253
254
  "kbd",
254
255
  {
255
256
  className: cn(
256
- "inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset px-1 py-px font-sans text-[12px] leading-[120%] font-normal text-text-secondary",
257
- "signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-bg-wash signal:font-mono signal:text-[10px]",
258
- "ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-bg-wash ship:font-mono ship:text-[10px]",
257
+ "inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-sm border border-border-strong bg-bg-inset px-1 py-px font-sans text-caption text-text-secondary",
258
+ "signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-bg-wash signal:font-mono signal:text-small",
259
+ "ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-bg-wash ship:font-mono ship:text-small",
259
260
  className
260
261
  ),
261
262
  children
@@ -410,9 +411,9 @@ var formatBytes = (bytes) => {
410
411
  return `${(kb / 1024).toFixed(1)} MB`;
411
412
  };
412
413
  var typeLabelOf = (name) => extensionOf(name).toUpperCase() || "FILE";
413
- var TILE = "size-9 rounded-md bg-bg-active flex items-center justify-center shrink-0 text-text-secondary";
414
- var NAME = "text-[12px] font-medium leading-[1.3] text-text-primary truncate";
415
- var NOTE = "text-[10px] leading-[1.2] truncate";
414
+ var TILE_CLASSES = "size-9 rounded-md bg-bg-active flex items-center justify-center shrink-0 text-text-secondary";
415
+ var NAME_CLASSES = "text-caption font-medium text-text-primary truncate";
416
+ var NOTE_CLASSES = "text-small tracking-wide leading-tight truncate";
416
417
  function TypeIcon({ name }) {
417
418
  const Icon = ICON_BY_EXTENSION[extensionOf(name)] ?? IconFile;
418
419
  return /* @__PURE__ */ jsx9(Icon, { size: 20, stroke: 1.5 });
@@ -469,15 +470,15 @@ function AttachmentCard({
469
470
  className: cn("group relative flex items-center gap-2 w-[200px] p-1.5 pr-3", failed ? "border-error-default" : warning && "border-warning-default", className),
470
471
  ...props,
471
472
  children: [
472
- /* @__PURE__ */ jsx9("div", { className: cn(TILE, "overflow-hidden"), children: state === "uploading" ? /* @__PURE__ */ jsx9(IconLoader2, { size: 16, stroke: 1.5, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx9(IconAlertCircle, { size: 16, stroke: 1.5, className: "text-error-default" }) : warning ? /* @__PURE__ */ jsx9(IconAlertTriangle, { size: 16, stroke: 1.5, className: "text-warning-default" }) : image && src ? /* @__PURE__ */ jsx9("img", { src, alt: name, className: "size-full object-cover" }) : /* @__PURE__ */ jsx9("span", { className: "text-[9px] font-semibold", children: typeLabelOf(name) }) }),
473
+ /* @__PURE__ */ jsx9("div", { className: cn(TILE_CLASSES, "overflow-hidden"), children: state === "uploading" ? /* @__PURE__ */ jsx9(IconLoader2, { size: 16, stroke: 1.5, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx9(IconAlertCircle, { size: 16, stroke: 1.5, className: "text-error-default" }) : warning ? /* @__PURE__ */ jsx9(IconAlertTriangle, { size: 16, stroke: 1.5, className: "text-warning-default" }) : image && src ? /* @__PURE__ */ jsx9("img", { src, alt: name, className: "size-full object-cover" }) : /* @__PURE__ */ jsx9("span", { className: "text-menu font-semibold", children: typeLabelOf(name) }) }),
473
474
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-[1px] min-w-0", children: [
474
- /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME }),
475
+ /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME_CLASSES }),
475
476
  /* @__PURE__ */ jsx9(
476
477
  Truncating,
477
478
  {
478
479
  text: (failed || warning ? note : state === "uploading" ? note ?? "Uploading\u2026" : note ?? sizeText) ?? "",
479
480
  hint: noteHint,
480
- className: cn(NOTE, failed ? "text-error-default" : warning ? "text-warning-default" : "text-text-secondary")
481
+ className: cn(NOTE_CLASSES, failed ? "text-error-default" : warning ? "text-warning-default" : "text-text-secondary")
481
482
  }
482
483
  )
483
484
  ] }),
@@ -521,10 +522,10 @@ function AttachmentCard({
521
522
  }
522
523
  if (state === "unreadable") {
523
524
  return /* @__PURE__ */ jsxs5(Card, { fill: "inset", unreadable: true, className: cn("flex items-center gap-2 w-[240px] p-1.5 opacity-70", className), ...props, children: [
524
- /* @__PURE__ */ jsx9("div", { className: TILE, children: /* @__PURE__ */ jsx9(TypeIcon, { name }) }),
525
+ /* @__PURE__ */ jsx9("div", { className: TILE_CLASSES, children: /* @__PURE__ */ jsx9(TypeIcon, { name }) }),
525
526
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-[1px] min-w-0 text-left", children: [
526
- /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME }),
527
- /* @__PURE__ */ jsx9("span", { className: cn(NOTE, "text-text-secondary"), children: note ?? "Could not be loaded" })
527
+ /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME_CLASSES }),
528
+ /* @__PURE__ */ jsx9("span", { className: cn(NOTE_CLASSES, "text-text-secondary"), children: note ?? "Could not be loaded" })
528
529
  ] })
529
530
  ] });
530
531
  }
@@ -545,16 +546,16 @@ function AttachmentCard({
545
546
  }
546
547
  ) : /* @__PURE__ */ jsx9("div", { className: "block w-full", children: picture }),
547
548
  /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1 pl-2 pr-1 py-1 min-w-0", children: [
548
- /* @__PURE__ */ jsx9(Truncating, { text: name, className: "flex-1 text-[12px] leading-[1.3] text-text-primary truncate", wrapperClassName: "flex-1" }),
549
+ /* @__PURE__ */ jsx9(Truncating, { text: name, className: "flex-1 text-caption text-text-primary truncate", wrapperClassName: "flex-1" }),
549
550
  download
550
551
  ] })
551
552
  ] });
552
553
  }
553
554
  const body = /* @__PURE__ */ jsxs5(Fragment, { children: [
554
- /* @__PURE__ */ jsx9("div", { className: TILE, children: /* @__PURE__ */ jsx9(TypeIcon, { name }) }),
555
+ /* @__PURE__ */ jsx9("div", { className: TILE_CLASSES, children: /* @__PURE__ */ jsx9(TypeIcon, { name }) }),
555
556
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-[1px] min-w-0 text-left", children: [
556
- /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME }),
557
- /* @__PURE__ */ jsx9("span", { className: cn(NOTE, "text-text-secondary"), children: note ?? `${typeLabelOf(name)} \xB7 ${sizeText}` })
557
+ /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME_CLASSES }),
558
+ /* @__PURE__ */ jsx9("span", { className: cn(NOTE_CLASSES, "text-text-secondary"), children: note ?? `${typeLabelOf(name)} \xB7 ${sizeText}` })
558
559
  ] })
559
560
  ] });
560
561
  return /* @__PURE__ */ jsxs5(Card, { fill: "inset", hover: href ? "hairline" : "none", className: cn("group flex items-center gap-2 w-[240px] p-1.5 pr-1", !href && "opacity-70", className), ...props, children: [
@@ -564,9 +565,9 @@ function AttachmentCard({
564
565
  }
565
566
 
566
567
  // src/Avatar.tsx
567
- import { useState as useState2 } from "react";
568
+ import { Avatar as BaseAvatar } from "@base-ui/react/avatar";
568
569
  import { IconUserFilled } from "@tabler/icons-react";
569
- import { jsx as jsx10 } from "react/jsx-runtime";
570
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
570
571
  var HUES = ["#56c8ff", "#ff8f6b", "#4ade8c", "#b18cff", "#ffc94d", "#ff7eb0", "#7ea8ff", "#5fdfd6"];
571
572
  var hueFor = (key) => {
572
573
  let h = 0;
@@ -579,30 +580,45 @@ var initialsFor = (name) => {
579
580
  return initials.toUpperCase();
580
581
  };
581
582
  function Avatar({ src, name, alt = "", size = 36, label: spoken, className }) {
582
- const [broken, setBroken] = useState2(false);
583
583
  const label = name || alt;
584
- const picture = src && !broken ? src : void 0;
585
- return /* @__PURE__ */ jsx10(
586
- "div",
584
+ return /* @__PURE__ */ jsxs6(
585
+ BaseAvatar.Root,
587
586
  {
587
+ render: /* @__PURE__ */ jsx10("div", {}),
588
588
  ...spoken ? { role: "img", "aria-label": spoken } : { "aria-hidden": true },
589
589
  className: cn("rounded-sm overflow-hidden shrink-0 bg-bg-inset", className),
590
590
  style: { width: size, height: size },
591
- children: picture ? /* @__PURE__ */ jsx10("img", { src: picture, alt: "", className: "w-full h-full object-cover", onError: () => setBroken(true) }) : label ? /* @__PURE__ */ jsx10(
592
- "div",
593
- {
594
- className: "w-full h-full flex items-center justify-center font-semibold leading-none",
595
- style: {
596
- // The one ink colour that reads on all eight hues, which are a
597
- // palette rather than tokens (see the note at the top) — so its
598
- // ink cannot be a token either.
599
- color: "#08121c",
600
- fontSize: Math.round(size * 0.36),
601
- background: `linear-gradient(160deg, color-mix(in srgb, ${hueFor(label)} 92%, #fff) 0%, color-mix(in srgb, ${hueFor(label)} 70%, #0b0d11) 100%)`
602
- },
603
- children: initialsFor(label)
604
- }
605
- ) : /* @__PURE__ */ jsx10("div", { className: "w-full h-full bg-accent-muted flex items-center justify-center text-text-muted", children: /* @__PURE__ */ jsx10(IconUserFilled, { size: Math.round(size * 0.5) }) })
591
+ children: [
592
+ src && /* @__PURE__ */ jsx10(
593
+ BaseAvatar.Image,
594
+ {
595
+ src,
596
+ alt: "",
597
+ keepMounted: true,
598
+ className: "w-full h-full object-cover data-[error]:hidden"
599
+ }
600
+ ),
601
+ label ? /* @__PURE__ */ jsx10(
602
+ BaseAvatar.Fallback,
603
+ {
604
+ render: /* @__PURE__ */ jsx10("div", {}),
605
+ className: "w-full h-full flex items-center justify-center font-semibold leading-none",
606
+ style: {
607
+ /* eslint-disable no-restricted-syntax -- @estiva-escape: the per-person palette (the note at
608
+ the top): eight hues picked from the name, the one ink that reads on all
609
+ of them, and a size that follows `size`. None of it can be a token. */
610
+ // The one ink colour that reads on all eight hues, which are a
611
+ // palette rather than tokens (see the note at the top) — so its
612
+ // ink cannot be a token either.
613
+ color: "#08121c",
614
+ fontSize: Math.round(size * 0.36),
615
+ background: `linear-gradient(160deg, color-mix(in srgb, ${hueFor(label)} 92%, #fff) 0%, color-mix(in srgb, ${hueFor(label)} 70%, #0b0d11) 100%)`
616
+ /* eslint-enable no-restricted-syntax */
617
+ },
618
+ children: /* @__PURE__ */ jsx10("span", { className: "[text-box:trim-both_cap_alphabetic]", children: initialsFor(label) })
619
+ }
620
+ ) : /* @__PURE__ */ jsx10(BaseAvatar.Fallback, { render: /* @__PURE__ */ jsx10("div", {}), className: "w-full h-full bg-accent-muted flex items-center justify-center text-text-muted", children: /* @__PURE__ */ jsx10(IconUserFilled, { size: Math.round(size * 0.5) }) })
621
+ ]
606
622
  }
607
623
  );
608
624
  }
@@ -633,7 +649,7 @@ function AvatarGroup({ members, size = 24 }) {
633
649
 
634
650
  // src/Banner.tsx
635
651
  import { IconX as IconX2 } from "@tabler/icons-react";
636
- import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
652
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
637
653
  var TONE_STYLES = {
638
654
  ok: "bg-success-muted text-success-default",
639
655
  error: "bg-error-muted text-error-default",
@@ -645,7 +661,7 @@ function Banner({ tone, children, onDismiss, dismissLabel = "Dismiss", className
645
661
  if (!onDismiss) {
646
662
  return /* @__PURE__ */ jsx12("div", { role, className: cn("px-4 py-2 text-body-2", TONE_STYLES[tone], className), children });
647
663
  }
648
- return /* @__PURE__ */ jsxs6("div", { role, className: cn("flex items-center gap-3 px-4 py-2 text-body-2", TONE_STYLES[tone], className), children: [
664
+ return /* @__PURE__ */ jsxs7("div", { role, className: cn("flex items-center gap-3 px-4 py-2 text-body-2", TONE_STYLES[tone], className), children: [
649
665
  /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1", children }),
650
666
  /* @__PURE__ */ jsx12(IconButton, { "aria-label": dismissLabel, onClick: onDismiss, className: "-mr-1 shrink-0 text-current hover:text-current", children: /* @__PURE__ */ jsx12(IconX2, { size: 16, stroke: 1.5 }) })
651
667
  ] });
@@ -653,7 +669,7 @@ function Banner({ tone, children, onDismiss, dismissLabel = "Dismiss", className
653
669
 
654
670
  // src/Button.tsx
655
671
  import { Button as BaseButton3 } from "@base-ui/react/button";
656
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
672
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
657
673
  function Button({
658
674
  variant = "muted",
659
675
  size = "default",
@@ -666,7 +682,7 @@ function Button({
666
682
  ...props
667
683
  }) {
668
684
  const hasLeadingIcon = !!leadingIcon;
669
- const button = /* @__PURE__ */ jsxs7(
685
+ const button = /* @__PURE__ */ jsxs8(
670
686
  BaseButton3,
671
687
  {
672
688
  type,
@@ -720,7 +736,7 @@ import { IconCheck } from "@tabler/icons-react";
720
736
  import { jsx as jsx14 } from "react/jsx-runtime";
721
737
  function squareClasses(checked, disabled, interactive, className) {
722
738
  return cn(
723
- "inline-flex items-center justify-center size-4 shrink-0 rounded-[4px] border transition-colors",
739
+ "inline-flex items-center justify-center size-4 shrink-0 rounded-sm border transition-colors",
724
740
  checked ? "bg-accent-primary border-accent-primary text-text-inverse" : "bg-transparent border-border-strong hover:border-text-muted",
725
741
  disabled && "opacity-50 pointer-events-none",
726
742
  interactive ? "cursor-pointer" : "pointer-events-none",
@@ -748,7 +764,7 @@ function Checkbox({ checked, onChange, disabled = false, className, ...aria }) {
748
764
  }
749
765
 
750
766
  // src/Chip.tsx
751
- import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
767
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
752
768
  var typeStyles = {
753
769
  neutral: "bg-bg-inset text-text-primary",
754
770
  brand: "bg-accent-muted text-accent-primary signal:border signal:border-accent-outline",
@@ -758,7 +774,7 @@ var typeStyles = {
758
774
  error: "bg-error-muted text-error-default signal:border signal:border-error-outline"
759
775
  };
760
776
  function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className }) {
761
- return /* @__PURE__ */ jsxs8("div", { className: cn("inline-flex min-w-0 items-center justify-center gap-1.5 rounded-full max-h-[20px] px-2 py-1", typeStyles[type], className), children: [
777
+ return /* @__PURE__ */ jsxs9("div", { className: cn("inline-flex min-w-0 items-center justify-center gap-1.5 rounded-full max-h-[20px] px-2 py-1", typeStyles[type], className), children: [
762
778
  leadingIcon && /* @__PURE__ */ jsx15("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
763
779
  label && // A chip given a `max-w-*` cuts a long label with an ellipsis instead
764
780
  // of growing past it — Ship's project chip on an issue row
@@ -768,13 +784,13 @@ function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className })
768
784
  // the screenshot diff caught every descender cut off. `clip` is the
769
785
  // one overflow that leaves the other axis visible. A chip with no cap
770
786
  // draws exactly as before.
771
- /* @__PURE__ */ jsx15("span", { className: "min-w-0 overflow-x-clip text-ellipsis whitespace-nowrap text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums", children: label }),
787
+ /* @__PURE__ */ jsx15("span", { className: "min-w-0 overflow-x-clip text-ellipsis whitespace-nowrap text-chip signal:font-mono signal:text-small signal:font-semibold signal:tracking-wide signal:tabular-nums", children: label }),
772
788
  trailingIcon && /* @__PURE__ */ jsx15("span", { className: "flex size-3 shrink-0 items-center justify-center", children: trailingIcon })
773
789
  ] });
774
790
  }
775
791
 
776
792
  // src/ChipInput.tsx
777
- import { useMemo, useState as useState3 } from "react";
793
+ import { useMemo, useState as useState2 } from "react";
778
794
  import { Button as BaseButton4 } from "@base-ui/react/button";
779
795
  import { Combobox } from "@base-ui/react/combobox";
780
796
  import { IconX as IconX3 } from "@tabler/icons-react";
@@ -797,7 +813,7 @@ function SectionLabel({ children, className }) {
797
813
  "span",
798
814
  {
799
815
  className: cn(
800
- "text-[12px] leading-[12px] font-medium text-text-primary signal:font-mono signal:text-[10px] signal:uppercase signal:tracking-[0.14em]",
816
+ "text-h5 leading-3 text-text-primary signal:font-mono signal:text-small signal:uppercase signal:tracking-widest",
801
817
  className
802
818
  ),
803
819
  children
@@ -806,7 +822,7 @@ function SectionLabel({ children, className }) {
806
822
  }
807
823
 
808
824
  // src/Menu.tsx
809
- import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
825
+ import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
810
826
  var MenuContext = createContext(null);
811
827
  function MenuPanel({ children, className, ...props }) {
812
828
  return /* @__PURE__ */ jsx17(
@@ -839,7 +855,7 @@ var VIEWPORT_PAD2 = 8;
839
855
  var HOVER_OPEN_DELAY = 0;
840
856
  var HOVER_CLOSE_DELAY = 150;
841
857
  function Menu({ trigger, align = "left", openOnHover = false, open, onOpenChange, actionsRef, children, className, contentClassName }) {
842
- return /* @__PURE__ */ jsxs9(
858
+ return /* @__PURE__ */ jsxs10(
843
859
  BaseMenu.Root,
844
860
  {
845
861
  open,
@@ -887,7 +903,7 @@ function MenuSub({ label, leading, selected, children, className }) {
887
903
  '[@estiva-app/ui] Menu: `openOnHover` and `MenuSub` cannot be used together \u2014 leaving the submenu panel strands the menu open. Open this menu on a press instead. See Menu.mdx, "Hover-opened menus".'
888
904
  );
889
905
  }
890
- return /* @__PURE__ */ jsxs9(BaseMenu.SubmenuRoot, { children: [
906
+ return /* @__PURE__ */ jsxs10(BaseMenu.SubmenuRoot, { children: [
891
907
  /* @__PURE__ */ jsx17(
892
908
  BaseMenu.SubmenuTrigger,
893
909
  {
@@ -944,17 +960,17 @@ function menuItemClassName({ size, selected, className }) {
944
960
  }
945
961
  function MenuItemBody({ label, children, size = "default", description, leading, trailing, hint, shortcut, submenu, destructive, selected }) {
946
962
  const edge = trailing ?? (shortcut ? /* @__PURE__ */ jsx17(Kbd, { children: shortcut }) : submenu ? /* @__PURE__ */ jsx17(IconChevronRight, { size: 16, stroke: 1.5, className: "shrink-0 text-text-muted" }) : null);
947
- return /* @__PURE__ */ jsxs9(Fragment2, { children: [
963
+ return /* @__PURE__ */ jsxs10(Fragment2, { children: [
948
964
  leading && /* @__PURE__ */ jsx17("span", { className: "flex shrink-0 items-center", children: leading }),
949
- children ?? /* @__PURE__ */ jsxs9("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
950
- /* @__PURE__ */ jsx17("span", { className: cn("truncate text-[14px] leading-[140%]", destructive ? "text-error-default" : "text-text-primary"), children: label }),
951
- description && /* @__PURE__ */ jsx17("span", { className: "truncate text-[12px] leading-[120%] text-text-secondary", children: description })
965
+ children ?? /* @__PURE__ */ jsxs10("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
966
+ /* @__PURE__ */ jsx17("span", { className: cn("truncate text-body-2", destructive ? "text-error-default" : "text-text-primary"), children: label }),
967
+ description && /* @__PURE__ */ jsx17("span", { className: "truncate text-caption text-text-secondary", children: description })
952
968
  ] }),
953
969
  (edge || hint) && // One grid cell holding both, right-aligned: the slot is as wide as
954
970
  // the wider of the two and never changes, so revealing the hint moves
955
971
  // nothing. No transition here either — the hint switches on the same
956
972
  // :hover / `selected` as the fill, in the same frame.
957
- /* @__PURE__ */ jsxs9("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
973
+ /* @__PURE__ */ jsxs10("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
958
974
  edge && /* @__PURE__ */ jsx17("span", { className: cn("flex items-center", hint && "group-hover:opacity-0 group-data-[highlighted]:opacity-0", hint && selected && "opacity-0"), children: edge }),
959
975
  hint && /* @__PURE__ */ jsx17("span", { className: cn("flex items-center opacity-0 group-hover:opacity-100 group-data-[highlighted]:opacity-100", selected && "opacity-100"), children: hint })
960
976
  ] })
@@ -993,21 +1009,21 @@ function MenuItem({ label, children, size = "default", description, leading, tra
993
1009
  );
994
1010
  }
995
1011
  function EnterHint({ target }) {
996
- return /* @__PURE__ */ jsxs9("span", { className: "flex shrink-0 items-center gap-1.5 text-text-muted", children: [
1012
+ return /* @__PURE__ */ jsxs10("span", { className: "flex shrink-0 items-center gap-1.5 text-text-muted", children: [
997
1013
  /* @__PURE__ */ jsx17(Kbd, { children: "\u21A9 Enter" }),
998
- target && /* @__PURE__ */ jsx17("span", { className: "text-[9px] font-medium leading-[115%] signal:font-mono signal:text-[9.5px] signal:tracking-[0.04em]", children: target })
1014
+ target && /* @__PURE__ */ jsx17("span", { className: "text-menu signal:font-mono signal:text-small signal:tracking-wider", children: target })
999
1015
  ] });
1000
1016
  }
1001
1017
  function MenuSection({ label, children, className }) {
1002
1018
  const inMenu = useContext(MenuContext) !== null;
1003
1019
  const heading = /* @__PURE__ */ jsx17("div", { className: cn("flex h-8 items-center px-2", className), children: /* @__PURE__ */ jsx17(SectionLabel, { className: "text-text-secondary", children: label }) });
1004
1020
  if (!inMenu) {
1005
- return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col", children: [
1021
+ return /* @__PURE__ */ jsxs10("div", { className: "flex flex-col", children: [
1006
1022
  heading,
1007
1023
  children
1008
1024
  ] });
1009
1025
  }
1010
- return /* @__PURE__ */ jsxs9(BaseMenu.Group, { className: "flex flex-col", children: [
1026
+ return /* @__PURE__ */ jsxs10(BaseMenu.Group, { className: "flex flex-col", children: [
1011
1027
  /* @__PURE__ */ jsx17(BaseMenu.GroupLabel, { render: heading }),
1012
1028
  children
1013
1029
  ] });
@@ -1017,7 +1033,7 @@ function MenuRow({ children, className }) {
1017
1033
  }
1018
1034
 
1019
1035
  // src/ChipInput.tsx
1020
- import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
1036
+ import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
1021
1037
  var CHIP_BOX = (
1022
1038
  // Curved, not a pill (Katerina, 2026-09-01): the Avatar keeps its own
1023
1039
  // rounded-sm corners — never a forced circle — and the chip's corner
@@ -1027,10 +1043,10 @@ var CHIP_BOX = (
1027
1043
  var CHIP_LABEL = "text-caption font-medium text-text-primary";
1028
1044
  var CHIP_REMOVE = "size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary";
1029
1045
  var chipPadding = (leading, removable) => cn(leading ? "pl-[2px]" : "pl-2", removable ? "pr-1" : "pr-2");
1030
- function InputChip({ label, leading, onRemove, className }) {
1031
- return /* @__PURE__ */ jsxs10("div", { className: cn(CHIP_BOX, chipPadding(!!leading, !!onRemove), className), children: [
1046
+ function InputChip({ label, leading, onRemove, removeLabel, truncate, className }) {
1047
+ return /* @__PURE__ */ jsxs11("div", { className: cn(CHIP_BOX, chipPadding(!!leading, !!onRemove), className), children: [
1032
1048
  leading && /* @__PURE__ */ jsx18("span", { className: "flex shrink-0 items-center", children: leading }),
1033
- /* @__PURE__ */ jsx18("span", { className: CHIP_LABEL, children: label }),
1049
+ /* @__PURE__ */ jsx18("span", { className: cn(CHIP_LABEL, truncate && "min-w-0 truncate"), children: label }),
1034
1050
  onRemove && /* @__PURE__ */ jsx18(
1035
1051
  BaseButton4,
1036
1052
  {
@@ -1039,8 +1055,8 @@ function InputChip({ label, leading, onRemove, className }) {
1039
1055
  e.stopPropagation();
1040
1056
  onRemove();
1041
1057
  },
1042
- className: CHIP_REMOVE,
1043
- "aria-label": `Remove ${label}`,
1058
+ className: cn(CHIP_REMOVE, truncate && "shrink-0"),
1059
+ "aria-label": removeLabel ?? `Remove ${label}`,
1044
1060
  children: /* @__PURE__ */ jsx18(IconX3, { size: 10, stroke: 1.5 })
1045
1061
  }
1046
1062
  )
@@ -1057,8 +1073,8 @@ function ChipInput({
1057
1073
  rowLeading,
1058
1074
  ...aria
1059
1075
  }) {
1060
- const [query, setQuery] = useState3("");
1061
- const [box, setBox] = useState3(null);
1076
+ const [query, setQuery] = useState2("");
1077
+ const [box, setBox] = useState2(null);
1062
1078
  const available = useMemo(() => {
1063
1079
  const chosen = new Set(value.map((o) => o.id));
1064
1080
  const excluded = new Set(excludeIds);
@@ -1094,7 +1110,7 @@ function ChipInput({
1094
1110
  setQuery("");
1095
1111
  }
1096
1112
  }
1097
- return /* @__PURE__ */ jsxs10(
1113
+ return /* @__PURE__ */ jsxs11(
1098
1114
  Combobox.Root,
1099
1115
  {
1100
1116
  multiple: true,
@@ -1111,8 +1127,8 @@ function ChipInput({
1111
1127
  filter,
1112
1128
  itemToStringLabel: (option) => option.label,
1113
1129
  children: [
1114
- /* @__PURE__ */ jsxs10(Combobox.Chips, { ref: setBox, className: "bg-bg-inset border border-border-default hover:border-border-strong focus-within:border-border-focus focus-within:hover:border-border-focus rounded-lg px-3 py-1.5 flex flex-wrap items-center gap-1.5 transition-colors min-h-[38px] cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring", children: [
1115
- value.map((option) => /* @__PURE__ */ jsxs10(Combobox.Chip, { className: cn(CHIP_BOX, chipPadding(!!chipLeading, true)), children: [
1130
+ /* @__PURE__ */ jsxs11(Combobox.Chips, { ref: setBox, className: "bg-bg-inset border border-border-default hover:border-border-strong focus-within:border-border-focus focus-within:hover:border-border-focus rounded-lg px-3 py-1.5 flex flex-wrap items-center gap-1.5 transition-colors min-h-[38px] cursor-text signal:transition-shadow signal:focus-within:shadow-focus-ring", children: [
1131
+ value.map((option) => /* @__PURE__ */ jsxs11(Combobox.Chip, { className: cn(CHIP_BOX, chipPadding(!!chipLeading, true)), children: [
1116
1132
  chipLeading && /* @__PURE__ */ jsx18("span", { className: "flex shrink-0 items-center", children: chipLeading(option) }),
1117
1133
  /* @__PURE__ */ jsx18("span", { className: CHIP_LABEL, children: option.label }),
1118
1134
  /* @__PURE__ */ jsx18(Combobox.ChipRemove, { className: CHIP_REMOVE, "aria-label": `Remove ${option.label}`, children: /* @__PURE__ */ jsx18(IconX3, { size: 10, stroke: 1.5 }) })
@@ -1162,7 +1178,7 @@ var GAP3 = 4;
1162
1178
  var VIEWPORT_PAD3 = 8;
1163
1179
 
1164
1180
  // src/InlineChip.tsx
1165
- import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
1181
+ import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
1166
1182
  var INLINE_CHIP_CLASSES = "inline-flex h-[1.4em] items-center gap-1 rounded-sm px-1 mx-0.5 align-top text-body-2 font-normal select-none";
1167
1183
  var INLINE_CHIP_TONE_CLASSES = {
1168
1184
  /** A thing or a place — anything that is not a person. */
@@ -1184,7 +1200,7 @@ function inlineChipClassName(tone, className) {
1184
1200
  return cn(INLINE_CHIP_CLASSES, INLINE_CHIP_TONE_CLASSES[tone], className);
1185
1201
  }
1186
1202
  function InlineChip({ tone = "neutral", icon, href, className, children, ...props }) {
1187
- const body = /* @__PURE__ */ jsxs11(Fragment3, { children: [
1203
+ const body = /* @__PURE__ */ jsxs12(Fragment3, { children: [
1188
1204
  icon && /* @__PURE__ */ jsx19("span", { className: "flex size-4 shrink-0 items-center justify-center text-text-secondary", children: icon }),
1189
1205
  children
1190
1206
  ] });
@@ -1199,15 +1215,15 @@ function InlineChip({ tone = "neutral", icon, href, className, children, ...prop
1199
1215
  import { useCallback, useRef as useRef2 } from "react";
1200
1216
 
1201
1217
  // src/Divider.tsx
1202
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
1218
+ import { Separator } from "@base-ui/react/separator";
1219
+ import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
1203
1220
  function Divider({ orientation = "horizontal", label, tone = "default", className }) {
1204
1221
  if (label && orientation === "horizontal") {
1205
1222
  const line = tone === "warning" ? "bg-warning-muted" : "bg-border-subtle";
1206
- return /* @__PURE__ */ jsxs12(
1207
- "div",
1223
+ return /* @__PURE__ */ jsxs13(
1224
+ Separator,
1208
1225
  {
1209
- role: "separator",
1210
- "aria-orientation": "horizontal",
1226
+ orientation: "horizontal",
1211
1227
  "aria-label": label,
1212
1228
  className: cn("flex shrink-0 items-center gap-2 mx-3", className),
1213
1229
  children: [
@@ -1219,23 +1235,22 @@ function Divider({ orientation = "horizontal", label, tone = "default", classNam
1219
1235
  );
1220
1236
  }
1221
1237
  return /* @__PURE__ */ jsx20(
1222
- "div",
1238
+ Separator,
1223
1239
  {
1224
- role: "separator",
1225
- "aria-orientation": orientation,
1240
+ orientation,
1226
1241
  className: cn("shrink-0 bg-border-subtle", orientation === "horizontal" ? "h-px mx-3" : "w-px self-stretch", className)
1227
1242
  }
1228
1243
  );
1229
1244
  }
1230
1245
 
1231
1246
  // src/Person.tsx
1232
- import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
1247
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
1233
1248
  function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
1234
1249
  return (
1235
1250
  // gap-2: the face-to-name distance is one rule (8px) wherever a person
1236
1251
  // appears — Katerina, 2026-09-01, set on Person so PersonTrigger and every
1237
1252
  // row agree by construction.
1238
- /* @__PURE__ */ jsxs13("span", { className: cn("flex min-w-0 items-center gap-2", className), children: [
1253
+ /* @__PURE__ */ jsxs14("span", { className: cn("flex min-w-0 items-center gap-2", className), children: [
1239
1254
  /* @__PURE__ */ jsx21(Avatar, { name, src: picture, size }),
1240
1255
  /* @__PURE__ */ jsx21("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
1241
1256
  ] })
@@ -1245,7 +1260,7 @@ function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
1245
1260
  // src/PersonTrigger.tsx
1246
1261
  import { Button as BaseButton5 } from "@base-ui/react/button";
1247
1262
  import { IconChevronDown } from "@tabler/icons-react";
1248
- import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1263
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
1249
1264
  function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }) {
1250
1265
  if (compact) {
1251
1266
  return /* @__PURE__ */ jsx22(
@@ -1261,7 +1276,7 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
1261
1276
  }
1262
1277
  );
1263
1278
  }
1264
- return /* @__PURE__ */ jsxs14(
1279
+ return /* @__PURE__ */ jsxs15(
1265
1280
  BaseButton5,
1266
1281
  {
1267
1282
  type: "button",
@@ -1287,7 +1302,7 @@ function PersonTrigger({ name, picture, fallback, size, open = false, compact =
1287
1302
  }
1288
1303
 
1289
1304
  // src/IdentityMenu.tsx
1290
- import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
1305
+ import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
1291
1306
  function IdentityPanel({ className, onClose = () => {
1292
1307
  }, ...rest }) {
1293
1308
  return /* @__PURE__ */ jsx23(MenuPanel, { className: cn("w-72", className), children: /* @__PURE__ */ jsx23(IdentityRows, { ...rest, onClose }) });
@@ -1298,23 +1313,23 @@ function IdentityRows({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, on
1298
1313
  action();
1299
1314
  };
1300
1315
  const appRows = typeof children === "function" ? children(onClose) : children;
1301
- return /* @__PURE__ */ jsxs15(Fragment4, { children: [
1302
- /* @__PURE__ */ jsxs15(MenuSection, { label: signedIn ? "Signed in as" : "Acting as", children: [
1316
+ return /* @__PURE__ */ jsxs16(Fragment4, { children: [
1317
+ /* @__PURE__ */ jsxs16(MenuSection, { label: signedIn ? "Signed in as" : "Acting as", children: [
1303
1318
  /* @__PURE__ */ jsx23(MenuRow, { children: /* @__PURE__ */ jsx23(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
1304
1319
  /* @__PURE__ */ jsx23(Note, { children: signedIn ? "Your Estiva ID. The same person you are in every Estiva app." : "A key held by this browser only. Nobody else knows who you are." }),
1305
- me.email && /* @__PURE__ */ jsxs15(Note, { children: [
1320
+ me.email && /* @__PURE__ */ jsxs16(Note, { children: [
1306
1321
  me.email,
1307
1322
  " \xB7 known to Estiva, never published to the relay"
1308
1323
  ] })
1309
1324
  ] }),
1310
- relayUrl && /* @__PURE__ */ jsxs15(Fragment4, { children: [
1325
+ relayUrl && /* @__PURE__ */ jsxs16(Fragment4, { children: [
1311
1326
  /* @__PURE__ */ jsx23(Divider, { className: "mx-0 my-2" }),
1312
- /* @__PURE__ */ jsxs15(MenuSection, { label: "Workspace", children: [
1327
+ /* @__PURE__ */ jsxs16(MenuSection, { label: "Workspace", children: [
1313
1328
  /* @__PURE__ */ jsx23(MenuRow, { children: /* @__PURE__ */ jsx23("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
1314
1329
  /* @__PURE__ */ jsx23(Note, { children: "Everyone on this relay shares this workspace, in every app." })
1315
1330
  ] })
1316
1331
  ] }),
1317
- appRows && /* @__PURE__ */ jsxs15(Fragment4, { children: [
1332
+ appRows && /* @__PURE__ */ jsxs16(Fragment4, { children: [
1318
1333
  /* @__PURE__ */ jsx23(Divider, { className: "mx-0 my-2" }),
1319
1334
  appRows
1320
1335
  ] }),
@@ -1379,7 +1394,7 @@ function Note({ children }) {
1379
1394
  // src/Field.tsx
1380
1395
  import { cloneElement, isValidElement } from "react";
1381
1396
  import { Field as BaseField } from "@base-ui/react/field";
1382
- import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
1397
+ import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
1383
1398
  var LINE_STYLES = {
1384
1399
  helper: "text-caption text-text-muted",
1385
1400
  warning: "text-caption text-warning-default",
@@ -1389,12 +1404,12 @@ function Field({ label, required = false, helper, error, children }) {
1389
1404
  const control = required && isValidElement(children) ? cloneElement(children, {
1390
1405
  "aria-required": children.props["aria-required"] ?? true
1391
1406
  }) : children;
1392
- return /* @__PURE__ */ jsxs16(BaseField.Root, { invalid: !!error, className: "flex flex-col gap-2", children: [
1393
- /* @__PURE__ */ jsxs16(BaseField.Label, { className: cn("text-input-label text-text-primary", required && "flex items-center"), children: [
1407
+ return /* @__PURE__ */ jsxs17(BaseField.Root, { invalid: !!error, className: "flex flex-col gap-2", children: [
1408
+ /* @__PURE__ */ jsxs17(BaseField.Label, { className: cn("text-input-label text-text-primary", required && "flex items-center"), children: [
1394
1409
  label,
1395
1410
  required && /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", className: "text-error-default ml-0.5", children: "*" })
1396
1411
  ] }),
1397
- /* @__PURE__ */ jsxs16("div", { className: "flex flex-col gap-1.5", children: [
1412
+ /* @__PURE__ */ jsxs17("div", { className: "flex flex-col gap-1.5", children: [
1398
1413
  control,
1399
1414
  error != null ? /* @__PURE__ */ jsx24(BaseField.Error, { match: true, className: LINE_STYLES.error, children: error }) : helper != null ? /* @__PURE__ */ jsx24(BaseField.Description, { className: LINE_STYLES.helper, children: helper }) : null
1400
1415
  ] })
@@ -1407,12 +1422,12 @@ function FieldLine({ tone = "helper", children, className }) {
1407
1422
  // src/Select.tsx
1408
1423
  import { IconCheck as IconCheck2, IconChevronDown as IconChevronDown2 } from "@tabler/icons-react";
1409
1424
  import { Select as BaseSelect } from "@base-ui/react/select";
1410
- import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
1425
+ import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
1411
1426
  var GAP4 = 4;
1412
1427
  var VIEWPORT_PAD4 = 8;
1413
1428
  function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className, ...aria }) {
1414
1429
  const selected = options.find((o) => o.value === value);
1415
- return /* @__PURE__ */ jsxs17(
1430
+ return /* @__PURE__ */ jsxs18(
1416
1431
  BaseSelect.Root,
1417
1432
  {
1418
1433
  value,
@@ -1420,7 +1435,7 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
1420
1435
  disabled,
1421
1436
  modal: false,
1422
1437
  children: [
1423
- /* @__PURE__ */ jsxs17(
1438
+ /* @__PURE__ */ jsxs18(
1424
1439
  BaseSelect.Trigger,
1425
1440
  {
1426
1441
  "aria-label": ariaLabel,
@@ -1442,12 +1457,12 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
1442
1457
  "hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
1443
1458
  "focus-visible:border-border-focus aria-expanded:border-border-focus",
1444
1459
  "signal:transition-shadow signal:focus-visible:shadow-focus-ring",
1445
- size === "default" && "px-3 py-2 text-[14px] leading-[1.4] font-normal",
1446
- size === "small" && "h-6 px-2 text-[12px] leading-[1.4] font-normal",
1460
+ size === "default" && "px-3 py-2 text-input-value",
1461
+ size === "small" && "h-6 px-2 text-caption",
1447
1462
  className
1448
1463
  ),
1449
1464
  children: [
1450
- /* @__PURE__ */ jsxs17("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
1465
+ /* @__PURE__ */ jsxs18("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
1451
1466
  selected?.leading && /* @__PURE__ */ jsx25("span", { className: "flex shrink-0 items-center", children: selected.leading }),
1452
1467
  /* @__PURE__ */ jsx25(BaseSelect.Value, { className: "truncate", children: () => selected?.label ?? placeholder })
1453
1468
  ] }),
@@ -1474,15 +1489,15 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
1474
1489
  {
1475
1490
  render: /* @__PURE__ */ jsx25(MenuPanel, {}),
1476
1491
  className: "min-w-[var(--anchor-width)] p-0",
1477
- children: /* @__PURE__ */ jsx25(ScrollArea, { viewportClassName: "max-h-[min(288px,var(--available-height))]", contentClassName: "flex flex-col p-2", children: options.map((option) => /* @__PURE__ */ jsxs17(
1492
+ children: /* @__PURE__ */ jsx25(ScrollArea, { viewportClassName: "max-h-[min(288px,var(--available-height))]", contentClassName: "flex flex-col p-2", children: options.map((option) => /* @__PURE__ */ jsxs18(
1478
1493
  BaseSelect.Item,
1479
1494
  {
1480
1495
  value: option.value,
1481
1496
  className: cn(menuItemClassName({ size: "default" }), "justify-between data-[selected]:font-medium"),
1482
1497
  children: [
1483
- /* @__PURE__ */ jsxs17("span", { className: "flex min-w-0 items-center gap-2", children: [
1498
+ /* @__PURE__ */ jsxs18("span", { className: "flex min-w-0 items-center gap-2", children: [
1484
1499
  option.leading && /* @__PURE__ */ jsx25("span", { className: "flex shrink-0 items-center", children: option.leading }),
1485
- /* @__PURE__ */ jsx25(BaseSelect.ItemText, { className: "truncate text-[14px] leading-[140%] text-text-primary", children: option.label })
1500
+ /* @__PURE__ */ jsx25(BaseSelect.ItemText, { className: "truncate text-body-2 text-text-primary", children: option.label })
1486
1501
  ] }),
1487
1502
  /* @__PURE__ */ jsx25(
1488
1503
  BaseSelect.ItemIndicator,
@@ -1507,15 +1522,18 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
1507
1522
  import { forwardRef } from "react";
1508
1523
  import { Input } from "@base-ui/react/input";
1509
1524
  import { jsx as jsx26 } from "react/jsx-runtime";
1510
- var TextInput = forwardRef(function TextInput2({ className, type = "text", ...props }, ref) {
1525
+ var TextInput = forwardRef(function TextInput2({ className, type = "text", size = "default", ...props }, ref) {
1511
1526
  return /* @__PURE__ */ jsx26(
1512
1527
  Input,
1513
1528
  {
1514
1529
  ref,
1515
1530
  type,
1516
1531
  className: cn(
1517
- "bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
1518
- "text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted",
1532
+ "bg-bg-inset border border-border-default focus:border-border-focus rounded-lg",
1533
+ // The small size is the small Select's trigger, class for class.
1534
+ size === "default" && "px-3 py-2 text-input-value",
1535
+ size === "small" && "h-6 px-2 text-caption",
1536
+ "text-text-primary placeholder:text-text-muted",
1519
1537
  "outline-none transition-colors",
1520
1538
  "disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
1521
1539
  "signal:transition-shadow signal:focus:shadow-focus-ring",
@@ -1538,7 +1556,7 @@ var Textarea = forwardRef2(function Textarea2({ className, ...props }, ref) {
1538
1556
  render: /* @__PURE__ */ jsx27("textarea", {}),
1539
1557
  className: cn(
1540
1558
  "bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
1541
- "text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted",
1559
+ "text-input-value text-text-primary placeholder:text-text-muted",
1542
1560
  "resize-none outline-none transition-colors",
1543
1561
  "disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
1544
1562
  "signal:transition-shadow signal:focus:shadow-focus-ring",
@@ -1550,14 +1568,14 @@ var Textarea = forwardRef2(function Textarea2({ className, ...props }, ref) {
1550
1568
  });
1551
1569
 
1552
1570
  // src/ConfirmDialog.tsx
1553
- import { useState as useState4 } from "react";
1571
+ import { useState as useState3 } from "react";
1554
1572
 
1555
1573
  // src/DialogShell.tsx
1556
1574
  import { useRef as useRef3 } from "react";
1557
1575
  import { Dialog } from "@base-ui/react/dialog";
1558
1576
  import { AlertDialog } from "@base-ui/react/alert-dialog";
1559
1577
  import { IconX as IconX4 } from "@tabler/icons-react";
1560
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1578
+ import { jsx as jsx28, jsxs as jsxs19 } from "react/jsx-runtime";
1561
1579
  function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, bodyMaxHeight, width = 502, alert = false }) {
1562
1580
  const Parts = alert ? AlertDialog : Dialog;
1563
1581
  const popupRef = useRef3(null);
@@ -1568,9 +1586,9 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
1568
1586
  onOpenChange: (open) => {
1569
1587
  if (!open) onClose();
1570
1588
  },
1571
- children: /* @__PURE__ */ jsxs18(Parts.Portal, { children: [
1589
+ children: /* @__PURE__ */ jsxs19(Parts.Portal, { children: [
1572
1590
  /* @__PURE__ */ jsx28(Parts.Backdrop, { className: "fixed inset-0 z-40 bg-scrim" }),
1573
- /* @__PURE__ */ jsx28("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs18(
1591
+ /* @__PURE__ */ jsx28("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs19(
1574
1592
  Parts.Popup,
1575
1593
  {
1576
1594
  ref: popupRef,
@@ -1579,7 +1597,7 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
1579
1597
  className: "bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden outline-none",
1580
1598
  style: { width },
1581
1599
  children: [
1582
- /* @__PURE__ */ jsxs18("div", { className: "h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0", children: [
1600
+ /* @__PURE__ */ jsxs19("div", { className: "h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0", children: [
1583
1601
  headerContent ?? /* @__PURE__ */ jsx28(Parts.Title, { className: "text-h4 text-text-primary", render: /* @__PURE__ */ jsx28("span", {}), children: title }),
1584
1602
  /* @__PURE__ */ jsx28(
1585
1603
  Parts.Close,
@@ -1607,9 +1625,9 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
1607
1625
  }
1608
1626
 
1609
1627
  // src/ConfirmDialog.tsx
1610
- import { Fragment as Fragment5, jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
1628
+ import { Fragment as Fragment5, jsx as jsx29, jsxs as jsxs20 } from "react/jsx-runtime";
1611
1629
  function ConfirmDialog({ title, children, confirmLabel, destructive = false, onConfirm, onClose }) {
1612
- const [busy, setBusy] = useState4(false);
1630
+ const [busy, setBusy] = useState3(false);
1613
1631
  const confirm = async () => {
1614
1632
  setBusy(true);
1615
1633
  try {
@@ -1626,7 +1644,7 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
1626
1644
  title,
1627
1645
  onClose,
1628
1646
  bodyClassName: "flex flex-col gap-3 text-body-2 text-text-primary",
1629
- footer: /* @__PURE__ */ jsxs19(Fragment5, { children: [
1647
+ footer: /* @__PURE__ */ jsxs20(Fragment5, { children: [
1630
1648
  /* @__PURE__ */ jsx29(Button, { variant: "muted", onClick: onClose, disabled: busy, children: "Cancel" }),
1631
1649
  /* @__PURE__ */ jsx29(Button, { variant: destructive ? "destructive" : "primary", onClick: () => void confirm(), disabled: busy, children: confirmLabel })
1632
1650
  ] }),
@@ -1636,14 +1654,14 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
1636
1654
  }
1637
1655
 
1638
1656
  // src/EditableText.tsx
1639
- import { useEffect, useRef as useRef4, useState as useState5 } from "react";
1657
+ import { useEffect, useRef as useRef4, useState as useState4 } from "react";
1640
1658
  import { Field as BaseField3 } from "@base-ui/react/field";
1641
1659
  import { Input as Input2 } from "@base-ui/react/input";
1642
1660
  import { jsx as jsx30 } from "react/jsx-runtime";
1643
1661
  function EditableText({ value, display, displayNode, placeholder, onCommit, multiline = false, className, label, readOnly = false }) {
1644
- const [editing, setEditing] = useState5(false);
1645
- const [draft, setDraft] = useState5(value);
1646
- const [busy, setBusy] = useState5(false);
1662
+ const [editing, setEditing] = useState4(false);
1663
+ const [draft, setDraft] = useState4(value);
1664
+ const [busy, setBusy] = useState4(false);
1647
1665
  const fieldRef = useRef4(null);
1648
1666
  useEffect(() => {
1649
1667
  if (editing) {
@@ -1753,12 +1771,12 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
1753
1771
 
1754
1772
  // src/EmptyState.tsx
1755
1773
  import { IconMessage2 } from "@tabler/icons-react";
1756
- import { jsx as jsx31, jsxs as jsxs20 } from "react/jsx-runtime";
1774
+ import { jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
1757
1775
  function EmptyState({ icon, message, scope = "page", className }) {
1758
1776
  if (scope === "section") {
1759
- return /* @__PURE__ */ jsx31("p", { className: cn("text-body-2 text-text-secondary", className), children: message });
1777
+ return /* @__PURE__ */ jsx31("p", { className: cn("text-caption text-text-muted", className), children: message });
1760
1778
  }
1761
- return /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-1 flex-col items-center justify-center gap-2", className), children: [
1779
+ return /* @__PURE__ */ jsxs21("div", { className: cn("flex flex-1 flex-col items-center justify-center gap-2", className), children: [
1762
1780
  /* @__PURE__ */ jsx31("span", { className: "text-text-secondary", children: icon ?? /* @__PURE__ */ jsx31(IconMessage2, { size: 16, stroke: 1.5 }) }),
1763
1781
  /* @__PURE__ */ jsx31("p", { className: "text-body-2 text-text-secondary text-center", children: message })
1764
1782
  ] });
@@ -1784,13 +1802,13 @@ function ProgressBar({ value, max, label, variant = "default", className }) {
1784
1802
  }
1785
1803
 
1786
1804
  // src/Skeleton.tsx
1787
- import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
1805
+ import { jsx as jsx33, jsxs as jsxs22 } from "react/jsx-runtime";
1788
1806
  function SkeletonBar({ className, style }) {
1789
1807
  return /* @__PURE__ */ jsx33("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
1790
1808
  }
1791
1809
  var ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160];
1792
1810
  function SkeletonRow({ barWidth = 130 }) {
1793
- return /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
1811
+ return /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
1794
1812
  /* @__PURE__ */ jsx33(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
1795
1813
  /* @__PURE__ */ jsx33(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
1796
1814
  ] });
@@ -1800,12 +1818,12 @@ function SkeletonList({ rows = 8, className }) {
1800
1818
  }
1801
1819
 
1802
1820
  // src/Breadcrumb.tsx
1803
- import { Fragment as Fragment6, useCallback as useCallback2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState6 } from "react";
1804
- import { jsx as jsx34, jsxs as jsxs22 } from "react/jsx-runtime";
1821
+ import { Fragment as Fragment6, useCallback as useCallback2, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState5 } from "react";
1822
+ import { jsx as jsx34, jsxs as jsxs23 } from "react/jsx-runtime";
1805
1823
  function Breadcrumb({ items, className }) {
1806
1824
  const navRef = useRef5(null);
1807
1825
  const labelRefs = useRef5([]);
1808
- const [truncated, setTruncated] = useState6(/* @__PURE__ */ new Set());
1826
+ const [truncated, setTruncated] = useState5(/* @__PURE__ */ new Set());
1809
1827
  const measure = useCallback2(() => {
1810
1828
  const next = /* @__PURE__ */ new Set();
1811
1829
  labelRefs.current.forEach((el, index) => {
@@ -1821,7 +1839,7 @@ function Breadcrumb({ items, className }) {
1821
1839
  observer.observe(nav);
1822
1840
  return () => observer.disconnect();
1823
1841
  }, [measure, items]);
1824
- return /* @__PURE__ */ jsx34("nav", { ref: navRef, "aria-label": "Breadcrumb", className: cn("flex min-w-0 items-center gap-1.5 text-[14px] leading-[140%]", className), children: items.map((item, index) => {
1842
+ return /* @__PURE__ */ jsx34("nav", { ref: navRef, "aria-label": "Breadcrumb", className: cn("flex min-w-0 items-center gap-1.5 text-body-2", className), children: items.map((item, index) => {
1825
1843
  const last = index === items.length - 1;
1826
1844
  const tone = item.muted || last && item.mono ? "text-text-muted" : last ? "text-text-primary" : "text-text-secondary";
1827
1845
  const text = cn(
@@ -1829,14 +1847,14 @@ function Breadcrumb({ items, className }) {
1829
1847
  // A mono crumb is a ref — the identity. It never gives up width to a
1830
1848
  // long name beside it (the LongName story always claimed "the ref
1831
1849
  // stays"; flexbox was squeezing it anyway until this line).
1832
- item.mono && "shrink-0 font-mono text-[12px] leading-[120%]",
1850
+ item.mono && "shrink-0 font-mono text-caption",
1833
1851
  tone
1834
1852
  );
1835
1853
  const setLabelRef = (el) => {
1836
1854
  labelRefs.current[index] = el;
1837
1855
  };
1838
1856
  const crumb = item.href ? /* @__PURE__ */ jsx34("a", { ref: setLabelRef, href: item.href, onClick: item.onClick, className: cn(text, "hover:text-text-primary"), children: item.label }) : /* @__PURE__ */ jsx34("span", { ref: setLabelRef, className: text, "aria-current": last ? "page" : void 0, children: item.label });
1839
- return /* @__PURE__ */ jsxs22(Fragment6, { children: [
1857
+ return /* @__PURE__ */ jsxs23(Fragment6, { children: [
1840
1858
  index > 0 && /* @__PURE__ */ jsx34("span", { "aria-hidden": "true", className: "shrink-0 text-text-muted", children: "/" }),
1841
1859
  item.icon && /* @__PURE__ */ jsx34("span", { "aria-hidden": "true", className: cn("flex shrink-0", tone), children: item.icon }),
1842
1860
  truncated.has(index) ? (
@@ -1850,9 +1868,9 @@ function Breadcrumb({ items, className }) {
1850
1868
  }
1851
1869
 
1852
1870
  // src/NavItem.tsx
1853
- import { jsx as jsx35, jsxs as jsxs23 } from "react/jsx-runtime";
1871
+ import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
1854
1872
  function NavItem({ label, href, count, countLabel, active = false, icon, className, ...props }) {
1855
- return /* @__PURE__ */ jsxs23(
1873
+ return /* @__PURE__ */ jsxs24(
1856
1874
  "a",
1857
1875
  {
1858
1876
  href,
@@ -1878,7 +1896,7 @@ function NavItem({ label, href, count, countLabel, active = false, icon, classNa
1878
1896
  // src/Popover.tsx
1879
1897
  import { useMemo as useMemo2 } from "react";
1880
1898
  import { Popover as BasePopover } from "@base-ui/react/popover";
1881
- import { jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
1899
+ import { jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
1882
1900
  var GAP5 = 4;
1883
1901
  var VIEWPORT_PAD5 = 8;
1884
1902
  function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpenChange, finalFocus, actionsRef, ariaLabel, children, className, contentClassName, maxHeight }) {
@@ -1887,7 +1905,7 @@ function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpe
1887
1905
  if (anchor instanceof Element) return anchor;
1888
1906
  return { getBoundingClientRect: () => anchor };
1889
1907
  }, [anchor]);
1890
- return /* @__PURE__ */ jsxs24(
1908
+ return /* @__PURE__ */ jsxs25(
1891
1909
  BasePopover.Root,
1892
1910
  {
1893
1911
  open,
@@ -1958,8 +1976,8 @@ function ToolbarButton({ ref, disabled, disabledReason, ...props }) {
1958
1976
  }
1959
1977
  );
1960
1978
  }
1961
- function ToolbarInput(props) {
1962
- return /* @__PURE__ */ jsx37(BaseToolbar.Input, { render: /* @__PURE__ */ jsx37(TextInput, {}), ...props });
1979
+ function ToolbarInput({ size, ...props }) {
1980
+ return /* @__PURE__ */ jsx37(BaseToolbar.Input, { render: /* @__PURE__ */ jsx37(TextInput, { size }), ...props });
1963
1981
  }
1964
1982
  function ToolbarSeparator({ className }) {
1965
1983
  return /* @__PURE__ */ jsx37(
@@ -1990,7 +2008,7 @@ function ReactionPicker({ options, onSelect, "aria-label": ariaLabel = "Reaction
1990
2008
  "aria-label": option.label,
1991
2009
  tooltip: option.label,
1992
2010
  onClick: () => onSelect(option.emoji),
1993
- className: "size-7 text-[18px] leading-none",
2011
+ className: "size-7 text-h3 leading-none",
1994
2012
  children: /* @__PURE__ */ jsx38("span", { "aria-hidden": "true", children: option.emoji })
1995
2013
  },
1996
2014
  option.emoji
@@ -2000,13 +2018,13 @@ function ReactionPicker({ options, onSelect, "aria-label": ariaLabel = "Reaction
2000
2018
 
2001
2019
  // src/PreviewCard.tsx
2002
2020
  import { PreviewCard as BasePreviewCard } from "@base-ui/react/preview-card";
2003
- import { jsx as jsx39, jsxs as jsxs25 } from "react/jsx-runtime";
2021
+ import { jsx as jsx39, jsxs as jsxs26 } from "react/jsx-runtime";
2004
2022
  var GAP6 = 12;
2005
2023
  var VIEWPORT_PAD6 = 8;
2006
2024
  var OPEN_DELAY2 = 350;
2007
2025
  var CLOSE_DELAY = 200;
2008
2026
  function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, closeDelay = CLOSE_DELAY, className, wrapperClassName }) {
2009
- return /* @__PURE__ */ jsxs25(BasePreviewCard.Root, { children: [
2027
+ return /* @__PURE__ */ jsxs26(BasePreviewCard.Root, { children: [
2010
2028
  /* @__PURE__ */ jsx39(
2011
2029
  BasePreviewCard.Trigger,
2012
2030
  {
@@ -2044,9 +2062,9 @@ function Rail({ "aria-label": ariaLabel = "Navigation", children, className }) {
2044
2062
  }
2045
2063
 
2046
2064
  // src/RailItem.tsx
2047
- import { jsx as jsx41, jsxs as jsxs26 } from "react/jsx-runtime";
2065
+ import { jsx as jsx41, jsxs as jsxs27 } from "react/jsx-runtime";
2048
2066
  function RailItem({ href, icon, label, active = false, className, ...props }) {
2049
- return /* @__PURE__ */ jsxs26(
2067
+ return /* @__PURE__ */ jsxs27(
2050
2068
  "a",
2051
2069
  {
2052
2070
  href,
@@ -2073,7 +2091,7 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
2073
2091
  )
2074
2092
  }
2075
2093
  ),
2076
- /* @__PURE__ */ jsx41("span", { className: cn("text-center text-[9px] font-medium leading-[115%]", active ? "text-text-primary" : "text-text-secondary"), children: label })
2094
+ /* @__PURE__ */ jsx41("span", { className: cn("text-center text-menu", active ? "text-text-primary" : "text-text-secondary"), children: label })
2077
2095
  ]
2078
2096
  }
2079
2097
  );
@@ -2093,33 +2111,34 @@ function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className })
2093
2111
  }
2094
2112
 
2095
2113
  // src/Property.tsx
2096
- import { jsx as jsx43, jsxs as jsxs27 } from "react/jsx-runtime";
2114
+ import { jsx as jsx43, jsxs as jsxs28 } from "react/jsx-runtime";
2115
+ var VALUE_CLASSES = "contents";
2097
2116
  function Property({ label, layout = "row", children, className }) {
2098
2117
  if (layout === "stacked") {
2099
2118
  return (
2100
2119
  // gap-3 — the one label-above-content distance (Katerina, 2026-09-01):
2101
2120
  // Peek's topic-details sections already sit at 12px, Ship's rail was
2102
2121
  // 6px, and unifying means the bigger, calmer one.
2103
- /* @__PURE__ */ jsxs27("div", { className: cn("flex flex-col gap-3", className), children: [
2104
- /* @__PURE__ */ jsx43("span", { className: "text-menu uppercase tracking-[0.08em] text-text-secondary", children: label }),
2105
- children
2122
+ /* @__PURE__ */ jsxs28("dl", { className: cn("flex flex-col gap-3", className), children: [
2123
+ /* @__PURE__ */ jsx43("dt", { className: "text-menu uppercase tracking-widest text-text-secondary", children: label }),
2124
+ /* @__PURE__ */ jsx43("dd", { className: VALUE_CLASSES, children })
2106
2125
  ] })
2107
2126
  );
2108
2127
  }
2109
- return /* @__PURE__ */ jsxs27("div", { className: cn("flex items-center gap-2", className), children: [
2110
- /* @__PURE__ */ jsx43("span", { className: "w-[68px] shrink-0 text-caption text-text-secondary", children: label }),
2111
- children
2128
+ return /* @__PURE__ */ jsxs28("dl", { className: cn("flex items-center gap-2", className), children: [
2129
+ /* @__PURE__ */ jsx43("dt", { className: "w-[68px] shrink-0 text-caption text-text-secondary", children: label }),
2130
+ /* @__PURE__ */ jsx43("dd", { className: VALUE_CLASSES, children })
2112
2131
  ] });
2113
2132
  }
2114
2133
 
2115
2134
  // src/Reaction.tsx
2116
- import { jsx as jsx44, jsxs as jsxs28 } from "react/jsx-runtime";
2117
- function Reaction({ emoji, count, pressed = false, className, type, ...props }) {
2118
- return /* @__PURE__ */ jsxs28(
2119
- "button",
2135
+ import { Toggle } from "@base-ui/react/toggle";
2136
+ import { jsx as jsx44, jsxs as jsxs29 } from "react/jsx-runtime";
2137
+ function Reaction({ emoji, count, pressed = false, className, ...props }) {
2138
+ return /* @__PURE__ */ jsxs29(
2139
+ Toggle,
2120
2140
  {
2121
- type: type ?? "button",
2122
- "aria-pressed": pressed,
2141
+ pressed,
2123
2142
  className: cn(
2124
2143
  // Chip's pill at a control's height, so a reaction and a status chip
2125
2144
  // read as the same family — 24px matches Button `small`.
@@ -2138,8 +2157,8 @@ function Reaction({ emoji, count, pressed = false, className, type, ...props })
2138
2157
  ),
2139
2158
  ...props,
2140
2159
  children: [
2141
- /* @__PURE__ */ jsx44("span", { "aria-hidden": "true", className: "shrink-0 text-[16px] leading-none", children: emoji }),
2142
- /* @__PURE__ */ jsx44("span", { className: "text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tabular-nums", children: count })
2160
+ /* @__PURE__ */ jsx44("span", { "aria-hidden": "true", className: "shrink-0 text-body-1 leading-none", children: emoji }),
2161
+ /* @__PURE__ */ jsx44("span", { className: "text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums", children: count })
2143
2162
  ]
2144
2163
  }
2145
2164
  );
@@ -2148,9 +2167,9 @@ function Reaction({ emoji, count, pressed = false, className, type, ...props })
2148
2167
  // src/SearchInput.tsx
2149
2168
  import "react";
2150
2169
  import { Input as Input3 } from "@base-ui/react/input";
2151
- import { jsx as jsx45, jsxs as jsxs29 } from "react/jsx-runtime";
2170
+ import { jsx as jsx45, jsxs as jsxs30 } from "react/jsx-runtime";
2152
2171
  function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...props }) {
2153
- return /* @__PURE__ */ jsxs29(
2172
+ return /* @__PURE__ */ jsxs30(
2154
2173
  "div",
2155
2174
  {
2156
2175
  className: cn(
@@ -2177,7 +2196,7 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
2177
2196
  // src/SectionHeader.tsx
2178
2197
  import { IconChevronRight as IconChevronRight2 } from "@tabler/icons-react";
2179
2198
  import { useRender } from "@base-ui/react/use-render";
2180
- import { Fragment as Fragment7, jsx as jsx46, jsxs as jsxs30 } from "react/jsx-runtime";
2199
+ import { Fragment as Fragment7, jsx as jsx46, jsxs as jsxs31 } from "react/jsx-runtime";
2181
2200
  function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = "hover", render, className }) {
2182
2201
  const titleElement = useRender({
2183
2202
  render: render ?? (chevron ? /* @__PURE__ */ jsx46("button", { type: "button", onClick: onToggle, "aria-expanded": isExpanded }) : /* @__PURE__ */ jsx46("span", {})),
@@ -2186,7 +2205,7 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
2186
2205
  // whole row up to the actions is the hit target, as it was when the
2187
2206
  // row itself carried the click.
2188
2207
  className: "flex h-full min-w-0 flex-1 items-center gap-1 text-left",
2189
- children: /* @__PURE__ */ jsxs30(Fragment7, { children: [
2208
+ children: /* @__PURE__ */ jsxs31(Fragment7, { children: [
2190
2209
  chevron && /* @__PURE__ */ jsx46(
2191
2210
  IconChevronRight2,
2192
2211
  {
@@ -2199,7 +2218,7 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
2199
2218
  ] })
2200
2219
  }
2201
2220
  });
2202
- return /* @__PURE__ */ jsxs30(
2221
+ return /* @__PURE__ */ jsxs31(
2203
2222
  "div",
2204
2223
  {
2205
2224
  className: cn(
@@ -2229,9 +2248,9 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
2229
2248
  }
2230
2249
 
2231
2250
  // src/CollapsibleSection.tsx
2232
- import { useState as useState7 } from "react";
2251
+ import { useState as useState6 } from "react";
2233
2252
  import { Collapsible } from "@base-ui/react/collapsible";
2234
- import { jsx as jsx47, jsxs as jsxs31 } from "react/jsx-runtime";
2253
+ import { jsx as jsx47, jsxs as jsxs32 } from "react/jsx-runtime";
2235
2254
  var OPEN = "open";
2236
2255
  var CLOSED = "closed";
2237
2256
  function readStored(key) {
@@ -2251,14 +2270,14 @@ function writeStored(key, open) {
2251
2270
  }
2252
2271
  }
2253
2272
  function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenChange, storageKey, trailing, actions, showActions, children, className, contentClassName }) {
2254
- const [openState, setOpenState] = useState7(() => readStored(storageKey) ?? defaultOpen);
2273
+ const [openState, setOpenState] = useState6(() => readStored(storageKey) ?? defaultOpen);
2255
2274
  const open = openProp ?? openState;
2256
2275
  const setOpen = (next) => {
2257
2276
  setOpenState(next);
2258
2277
  writeStored(storageKey, next);
2259
2278
  onOpenChange?.(next);
2260
2279
  };
2261
- return /* @__PURE__ */ jsxs31(Collapsible.Root, { open, onOpenChange: setOpen, className: cn("flex flex-col", className), children: [
2280
+ return /* @__PURE__ */ jsxs32(Collapsible.Root, { open, onOpenChange: setOpen, className: cn("flex flex-col", className), children: [
2262
2281
  /* @__PURE__ */ jsx47(SectionHeader, { title, chevron: true, isExpanded: open, trailing, actions, showActions, className: "shrink-0", render: /* @__PURE__ */ jsx47(Collapsible.Trigger, {}) }),
2263
2282
  /* @__PURE__ */ jsx47(
2264
2283
  Collapsible.Panel,
@@ -2273,7 +2292,7 @@ function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenC
2273
2292
 
2274
2293
  // src/Tabs.tsx
2275
2294
  import { Tabs as BaseTabs } from "@base-ui/react/tabs";
2276
- import { jsx as jsx48, jsxs as jsxs32 } from "react/jsx-runtime";
2295
+ import { jsx as jsx48, jsxs as jsxs33 } from "react/jsx-runtime";
2277
2296
  function Tabs({ tabs, active, onChange, size = "default", className, ...aria }) {
2278
2297
  return /* @__PURE__ */ jsx48(
2279
2298
  BaseTabs.Root,
@@ -2290,7 +2309,7 @@ function Tabs({ tabs, active, onChange, size = "default", className, ...aria })
2290
2309
  "aria-label": aria["aria-label"],
2291
2310
  "aria-labelledby": aria["aria-labelledby"],
2292
2311
  className: "flex items-center gap-2",
2293
- children: tabs.map((tab) => /* @__PURE__ */ jsxs32(
2312
+ children: tabs.map((tab) => /* @__PURE__ */ jsxs33(
2294
2313
  BaseTabs.Tab,
2295
2314
  {
2296
2315
  value: tab.id,
@@ -2303,7 +2322,7 @@ function Tabs({ tabs, active, onChange, size = "default", className, ...aria })
2303
2322
  ),
2304
2323
  children: [
2305
2324
  tab.icon,
2306
- /* @__PURE__ */ jsxs32("span", { className: "flex items-baseline", children: [
2325
+ /* @__PURE__ */ jsxs33("span", { className: "flex items-baseline", children: [
2307
2326
  tab.label,
2308
2327
  tab.count !== void 0 ? /* @__PURE__ */ jsx48("span", { className: "ml-2.5 font-mono text-caption tabular-nums text-text-secondary", children: tab.count }) : null
2309
2328
  ] })
@@ -2318,16 +2337,16 @@ function Tabs({ tabs, active, onChange, size = "default", className, ...aria })
2318
2337
  }
2319
2338
 
2320
2339
  // src/Toast.tsx
2321
- import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect2, useMemo as useMemo3, useState as useState8 } from "react";
2322
- import { createPortal } from "react-dom";
2340
+ import { createContext as createContext2, useContext as useContext2, useMemo as useMemo3 } from "react";
2341
+ import { Toast as BaseToast } from "@base-ui/react/toast";
2323
2342
  import { IconAlertCircle as IconAlertCircle2, IconCircleCheck, IconCircleX } from "@tabler/icons-react";
2324
- import { jsx as jsx49, jsxs as jsxs33 } from "react/jsx-runtime";
2343
+ import { jsx as jsx49, jsxs as jsxs34 } from "react/jsx-runtime";
2325
2344
  var SURFACE_STYLES = {
2326
- success: "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
2327
- brand: "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
2328
- neutral: "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
2329
- warning: "bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
2330
- error: "bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]"
2345
+ success: "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
2346
+ brand: "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
2347
+ neutral: "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-md",
2348
+ warning: "bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md",
2349
+ error: "bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-md"
2331
2350
  };
2332
2351
  var ICONS = {
2333
2352
  success: IconCircleCheck,
@@ -2353,82 +2372,90 @@ var ACTION_BORDER_STYLES = {
2353
2372
  warning: "signal:border signal:border-border-default signal:hover:border-border-strong",
2354
2373
  error: "signal:border signal:border-border-default signal:hover:border-border-strong"
2355
2374
  };
2375
+ var LABEL_CLASSES = "text-body-2 text-text-primary whitespace-nowrap";
2376
+ var pillClassName = (type, hasAction, className) => cn(
2377
+ "inline-flex items-center min-h-[32px] pl-2 py-1 rounded-lg shadow-lg",
2378
+ // Without an action the label needs real right padding; the action
2379
+ // button brings its own edge, so the tight pr-1 only applies there.
2380
+ hasAction ? "pr-1 gap-[46px]" : "pr-3",
2381
+ SURFACE_STYLES[type],
2382
+ className
2383
+ );
2384
+ var actionClassName = (type) => cn(
2385
+ "h-6 flex items-center justify-center gap-1 px-1 py-1 rounded-md shrink-0 transition-colors",
2386
+ ACTION_BORDER_STYLES[type],
2387
+ type === "neutral" ? "hover:border-border-strong" : "hover:opacity-80"
2388
+ );
2389
+ function LeadingIcon({ type }) {
2390
+ const Icon = ICONS[type];
2391
+ return /* @__PURE__ */ jsx49(Icon, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) });
2392
+ }
2393
+ var ACTION_LABEL_CLASSES = "text-btn-small text-text-primary whitespace-nowrap";
2356
2394
  function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAction, className }) {
2357
2395
  const hasAction = !!(actionLabel && onAction);
2358
- const LeadingIcon = ICONS[type];
2359
- return /* @__PURE__ */ jsxs33(
2360
- "div",
2361
- {
2362
- className: cn(
2363
- "inline-flex items-center min-h-[32px] pl-2 py-1 rounded-lg shadow-lg",
2364
- // Without an action the label needs real right padding; the action
2365
- // button brings its own edge, so the tight pr-1 only applies there.
2366
- hasAction ? "pr-1 gap-[46px]" : "pr-3",
2367
- SURFACE_STYLES[type],
2368
- className
2369
- ),
2370
- children: [
2371
- /* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-2 shrink-0", children: [
2372
- leadingIcon && /* @__PURE__ */ jsx49(LeadingIcon, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) }),
2373
- /* @__PURE__ */ jsx49("span", { className: "font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap", children: label })
2374
- ] }),
2375
- hasAction && /* @__PURE__ */ jsx49(
2376
- "button",
2377
- {
2378
- type: "button",
2379
- onClick: onAction,
2380
- className: cn(
2381
- "h-6 flex items-center justify-center gap-1 px-1 py-1 rounded-md shrink-0 transition-colors",
2382
- ACTION_BORDER_STYLES[type],
2383
- type === "neutral" ? "hover:border-border-strong" : "hover:opacity-80"
2384
- ),
2385
- children: /* @__PURE__ */ jsx49("span", { className: "font-medium text-[12px] leading-[12px] text-text-primary whitespace-nowrap", children: actionLabel })
2386
- }
2387
- )
2388
- ]
2389
- }
2390
- );
2396
+ return /* @__PURE__ */ jsxs34("div", { className: pillClassName(type, hasAction, className), children: [
2397
+ /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2 shrink-0", children: [
2398
+ leadingIcon && /* @__PURE__ */ jsx49(LeadingIcon, { type }),
2399
+ /* @__PURE__ */ jsx49("span", { className: LABEL_CLASSES, children: label })
2400
+ ] }),
2401
+ hasAction && /* @__PURE__ */ jsx49("button", { type: "button", onClick: onAction, className: actionClassName(type), children: /* @__PURE__ */ jsx49("span", { className: ACTION_LABEL_CLASSES, children: actionLabel }) })
2402
+ ] });
2391
2403
  }
2392
2404
  var ToastContext = createContext2(null);
2393
- var toastSeq = 0;
2405
+ var VISIBLE_TOASTS = 3;
2394
2406
  function ToastProvider({ children }) {
2395
- const [toast, setToast] = useState8(null);
2396
- const showToast = useCallback3((opts) => {
2397
- setToast({ ...opts, id: ++toastSeq });
2398
- }, []);
2399
- const dismissToast = useCallback3(() => {
2400
- setToast(null);
2401
- }, []);
2402
- useEffect2(() => {
2403
- if (!toast) return;
2404
- const duration = toast.durationMs ?? 5e3;
2405
- if (duration <= 0) return;
2406
- const timer = setTimeout(() => {
2407
- setToast((current) => current?.id === toast.id ? null : current);
2408
- }, duration);
2409
- return () => clearTimeout(timer);
2410
- }, [toast]);
2411
- const value = useMemo3(() => ({ showToast, dismissToast }), [showToast, dismissToast]);
2412
- return /* @__PURE__ */ jsxs33(ToastContext.Provider, { value, children: [
2407
+ const manager = useMemo3(() => BaseToast.createToastManager(), []);
2408
+ const value = useMemo3(
2409
+ () => ({
2410
+ showToast: ({ label, type = "neutral", leadingIcon = true, actionLabel, onAction, durationMs = 5e3 }) => manager.add({
2411
+ title: label,
2412
+ type,
2413
+ timeout: durationMs,
2414
+ // A failure interrupts; everything else waits for a pause.
2415
+ priority: type === "error" ? "high" : "low",
2416
+ data: { leadingIcon, actionLabel, onAction }
2417
+ }),
2418
+ dismissToast: (id) => manager.close(id)
2419
+ }),
2420
+ [manager]
2421
+ );
2422
+ return /* @__PURE__ */ jsx49(ToastContext.Provider, { value, children: /* @__PURE__ */ jsxs34(BaseToast.Provider, { toastManager: manager, limit: VISIBLE_TOASTS, children: [
2413
2423
  children,
2414
- toast && createPortal(
2415
- /* @__PURE__ */ jsx49("div", { className: "fixed bottom-4 left-4 z-[100] pointer-events-none", children: /* @__PURE__ */ jsx49(
2416
- Toast,
2417
- {
2418
- className: "pointer-events-auto",
2419
- label: toast.label,
2420
- type: toast.type ?? "neutral",
2421
- leadingIcon: toast.leadingIcon ?? true,
2422
- actionLabel: toast.actionLabel,
2423
- onAction: toast.onAction ? () => {
2424
- toast.onAction?.();
2425
- dismissToast();
2426
- } : void 0
2427
- }
2428
- ) }),
2429
- document.body
2430
- )
2431
- ] });
2424
+ /* @__PURE__ */ jsx49(BaseToast.Portal, { children: /* @__PURE__ */ jsx49(BaseToast.Viewport, { className: "fixed bottom-4 left-4 z-[100] flex flex-col-reverse items-start gap-2 pointer-events-none", children: /* @__PURE__ */ jsx49(ToastList, {}) }) })
2425
+ ] }) });
2426
+ }
2427
+ function ToastList() {
2428
+ const { toasts, close } = BaseToast.useToastManager();
2429
+ return toasts.map((toast) => {
2430
+ const type = toast.type ?? "neutral";
2431
+ const { leadingIcon = true, actionLabel, onAction } = toast.data ?? {};
2432
+ return /* @__PURE__ */ jsxs34(
2433
+ BaseToast.Root,
2434
+ {
2435
+ toast,
2436
+ swipeDirection: ["left", "down"],
2437
+ className: pillClassName(type, !!actionLabel, "pointer-events-auto data-[limited]:hidden"),
2438
+ children: [
2439
+ /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2 shrink-0", children: [
2440
+ leadingIcon && /* @__PURE__ */ jsx49(LeadingIcon, { type }),
2441
+ /* @__PURE__ */ jsx49(BaseToast.Title, { render: /* @__PURE__ */ jsx49("span", {}), className: LABEL_CLASSES })
2442
+ ] }),
2443
+ actionLabel && /* @__PURE__ */ jsx49(
2444
+ BaseToast.Action,
2445
+ {
2446
+ onClick: () => {
2447
+ onAction?.();
2448
+ close(toast.id);
2449
+ },
2450
+ className: actionClassName(type),
2451
+ children: /* @__PURE__ */ jsx49("span", { className: ACTION_LABEL_CLASSES, children: actionLabel })
2452
+ }
2453
+ )
2454
+ ]
2455
+ },
2456
+ toast.id
2457
+ );
2458
+ });
2432
2459
  }
2433
2460
  function useToast() {
2434
2461
  const ctx = useContext2(ToastContext);