@estiva-app/ui 0.23.0 → 0.24.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 (98) hide show
  1. package/dist/AppShell.d.ts.map +1 -1
  2. package/dist/AttachmentCard.d.ts.map +1 -1
  3. package/dist/Form.d.ts.map +1 -1
  4. package/dist/IconButton.d.ts +9 -1
  5. package/dist/IconButton.d.ts.map +1 -1
  6. package/dist/PreviewCard.d.ts +9 -2
  7. package/dist/PreviewCard.d.ts.map +1 -1
  8. package/dist/SectionHeader.d.ts +7 -1
  9. package/dist/SectionHeader.d.ts.map +1 -1
  10. package/dist/Select.d.ts +11 -3
  11. package/dist/Select.d.ts.map +1 -1
  12. package/dist/Tooltip.d.ts +13 -1
  13. package/dist/Tooltip.d.ts.map +1 -1
  14. package/dist/eslint/index.js +2 -1
  15. package/dist/eslint/index.js.map +2 -2
  16. package/dist/eslint/no-restyled-part.d.ts.map +1 -1
  17. package/dist/index.js +127 -108
  18. package/dist/index.js.map +2 -2
  19. package/package.json +1 -1
  20. package/registry.json +51 -8
  21. package/src/AppShell.mdx +10 -0
  22. package/src/AppShell.test.tsx +16 -0
  23. package/src/AppShell.tsx +4 -1
  24. package/src/AttachmentCard.mdx +9 -0
  25. package/src/AttachmentCard.test.tsx +10 -0
  26. package/src/AttachmentCard.tsx +8 -5
  27. package/src/Avatar.mdx +13 -3
  28. package/src/AvatarGroup.mdx +4 -0
  29. package/src/Banner.mdx +13 -3
  30. package/src/Breadcrumb.mdx +10 -0
  31. package/src/Button.mdx +14 -3
  32. package/src/Card.mdx +9 -0
  33. package/src/Checkbox.mdx +10 -0
  34. package/src/Chip.mdx +4 -0
  35. package/src/ChipInput.mdx +12 -0
  36. package/src/CollapsibleSection.mdx +9 -0
  37. package/src/CommandPalette.mdx +14 -2
  38. package/src/ConfirmDialog.mdx +11 -0
  39. package/src/ContainerHeader.mdx +4 -0
  40. package/src/DialogShell.mdx +14 -0
  41. package/src/Divider.mdx +9 -1
  42. package/src/EditableText.mdx +11 -0
  43. package/src/EmptyState.mdx +15 -4
  44. package/src/Field.mdx +10 -1
  45. package/src/FieldLine.mdx +9 -1
  46. package/src/FilePicker.mdx +8 -0
  47. package/src/Form.mdx +10 -0
  48. package/src/Form.test.tsx +26 -0
  49. package/src/Form.tsx +7 -0
  50. package/src/IconButton.mdx +16 -2
  51. package/src/IconButton.stories.tsx +3 -0
  52. package/src/IconButton.test.tsx +25 -0
  53. package/src/IconButton.tsx +61 -46
  54. package/src/IdentityMenu.mdx +9 -0
  55. package/src/InlineChip.mdx +8 -0
  56. package/src/Kbd.mdx +4 -0
  57. package/src/Link.mdx +10 -0
  58. package/src/Menu.mdx +16 -1
  59. package/src/MenuItem.mdx +12 -2
  60. package/src/NavItem.mdx +8 -0
  61. package/src/Person.mdx +4 -0
  62. package/src/PersonTrigger.mdx +9 -0
  63. package/src/Popover.mdx +21 -0
  64. package/src/PreviewCard.mdx +19 -4
  65. package/src/PreviewCard.tsx +11 -4
  66. package/src/ProgressBar.mdx +8 -0
  67. package/src/Property.mdx +4 -0
  68. package/src/Rail.mdx +9 -1
  69. package/src/RailItem.mdx +8 -0
  70. package/src/Reaction.mdx +9 -0
  71. package/src/ReactionPicker.mdx +8 -0
  72. package/src/ScrollArea.mdx +13 -2
  73. package/src/SearchInput.mdx +9 -0
  74. package/src/SectionHeader.mdx +13 -0
  75. package/src/SectionHeader.stories.tsx +9 -0
  76. package/src/SectionHeader.test.tsx +9 -0
  77. package/src/SectionHeader.tsx +8 -2
  78. package/src/SectionLabel.mdx +4 -0
  79. package/src/Select.mdx +21 -2
  80. package/src/Select.stories.tsx +4 -1
  81. package/src/Select.test.tsx +17 -0
  82. package/src/Select.tsx +34 -13
  83. package/src/Sidebar.mdx +8 -0
  84. package/src/Skeleton.mdx +4 -0
  85. package/src/Tabs.mdx +12 -1
  86. package/src/TextInput.mdx +8 -0
  87. package/src/Textarea.mdx +8 -0
  88. package/src/Toast.mdx +12 -1
  89. package/src/Toolbar.mdx +11 -0
  90. package/src/Tooltip.mdx +26 -2
  91. package/src/Tooltip.stories.tsx +26 -0
  92. package/src/Tooltip.test.tsx +29 -0
  93. package/src/Tooltip.tsx +17 -4
  94. package/src/TopBar.mdx +4 -0
  95. package/src/eslint/no-rebuilt-behaviour.ts +1 -1
  96. package/src/eslint/no-restyled-part.ts +1 -0
  97. package/src/pages.test.ts +142 -0
  98. package/tailwind-preset.js +22 -1
@@ -1 +1 @@
1
- {"version":3,"file":"no-restyled-part.d.ts","sourceRoot":"","sources":["../../src/eslint/no-restyled-part.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAGzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,6FAA6F;AAC7F,eAAO,MAAM,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAUxD,CAAA;AASD,oGAAoG;AACpG,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAajF;AAED,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIlD;AAOD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAgCpD,CAAA;AAOD,UAAU,IAAI;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,GAAG,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACnC,MAAM,CAAC,EAAE,IAAI,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAwBD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AA8UD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,UAAQ,EAAE,IAAI,YAAkB,GAAG,MAAM,EAAE,CAqDtJ;AAOD,eAAO,MAAM,cAAc,EAAE,IAAI,CAAC,UAwEjC,CAAA"}
1
+ {"version":3,"file":"no-restyled-part.d.ts","sourceRoot":"","sources":["../../src/eslint/no-restyled-part.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAGzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,6FAA6F;AAC7F,eAAO,MAAM,SAAS,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAUxD,CAAA;AASD,oGAAoG;AACpG,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAajF;AAED,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIlD;AAOD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAiCpD,CAAA;AAOD,UAAU,IAAI;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,GAAG,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACnC,MAAM,CAAC,EAAE,IAAI,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAwBD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AA8UD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,UAAQ,EAAE,IAAI,YAAkB,GAAG,MAAM,EAAE,CAqDtJ;AAOD,eAAO,MAAM,cAAc,EAAE,IAAI,CAAC,UAwEjC,CAAA"}
package/dist/index.js CHANGED
@@ -91,25 +91,30 @@ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
91
91
  function AppShell({ variant = "solid", menu, logo, search, identity, banner, nav, children }) {
92
92
  const bar = /* @__PURE__ */ jsx3(TopBar, { variant, menu, logo, search, right: identity });
93
93
  if (variant === "floating") {
94
- return /* @__PURE__ */ jsxs3("div", { className: "relative h-full min-h-0 overflow-hidden bg-bg-base", children: [
95
- bar,
96
- /* @__PURE__ */ jsxs3("div", { className: "flex h-full pb-4 pr-4 pt-[52px]", children: [
97
- nav,
98
- /* @__PURE__ */ jsxs3(
99
- "div",
100
- {
101
- className: cn(
102
- "flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl bg-bg-surface",
103
- "signal:border signal:border-border-subtle signal:shadow-highlight-inset"
104
- ),
105
- children: [
106
- banner,
107
- /* @__PURE__ */ jsx3("main", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children })
108
- ]
109
- }
110
- )
94
+ return (
95
+ // `signal-canvas`: in the Signal theme, the preset draws the control-room dot
96
+ // grid behind everything in the frame (Peek's, moved here with the frame:
97
+ // UIG-14, Katerina, 19 September — the canvas is the theme's, not an app's).
98
+ /* @__PURE__ */ jsxs3("div", { className: "signal-canvas relative h-full min-h-0 overflow-hidden bg-bg-base", children: [
99
+ bar,
100
+ /* @__PURE__ */ jsxs3("div", { className: "flex h-full pb-4 pr-4 pt-[52px]", children: [
101
+ nav,
102
+ /* @__PURE__ */ jsxs3(
103
+ "div",
104
+ {
105
+ className: cn(
106
+ "flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl bg-bg-surface",
107
+ "signal:border signal:border-border-subtle signal:shadow-highlight-inset"
108
+ ),
109
+ children: [
110
+ banner,
111
+ /* @__PURE__ */ jsx3("main", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children })
112
+ ]
113
+ }
114
+ )
115
+ ] })
111
116
  ] })
112
- ] });
117
+ );
113
118
  }
114
119
  return (
115
120
  /* `relative overflow-hidden` is the seal the floating manner already has
@@ -283,8 +288,8 @@ function TooltipProvider({ delay = OPEN_DELAY, closeDelay, timeout, children })
283
288
  return /* @__PURE__ */ jsx7(BaseTooltip.Provider, { delay, closeDelay, timeout, children });
284
289
  }
285
290
  function Tooltip({ label, shortcut, className, ...props }) {
286
- return /* @__PURE__ */ jsxs4("div", { role: "tooltip", className: cn("bg-bg-elevated border border-border-default rounded-lg h-[30px] flex items-center justify-center gap-1.5 px-2 shadow-lg", className), ...props, children: [
287
- /* @__PURE__ */ jsx7("span", { className: "text-caption text-text-primary whitespace-nowrap", children: label }),
291
+ return /* @__PURE__ */ jsxs4("div", { role: "tooltip", className: cn("bg-bg-elevated border border-border-default rounded-lg min-h-[30px] max-w-80 flex items-center justify-center gap-1.5 px-2 py-1 shadow-lg", className), ...props, children: [
292
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 text-caption text-text-primary break-words", children: label }),
288
293
  shortcut && /* @__PURE__ */ jsx7(Kbd, { children: shortcut })
289
294
  ] });
290
295
  }
@@ -316,9 +321,10 @@ function TooltipSurface({ label, shortcut, placement }) {
316
321
  }
317
322
  ) });
318
323
  }
319
- function WithTooltip({ label, shortcut, placement = "top", wrapperClassName, children }) {
324
+ function WithTooltip({ label, shortcut, placement = "top", wrapperClassName, inline = false, children }) {
325
+ const Wrapper = inline ? "span" : "div";
320
326
  return /* @__PURE__ */ jsxs4(BaseTooltip.Root, { disableHoverablePopup: true, children: [
321
- /* @__PURE__ */ jsx7(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: /* @__PURE__ */ jsx7("div", { className: cn("inline-flex shrink-0", wrapperClassName) }), children }),
327
+ /* @__PURE__ */ jsx7(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: /* @__PURE__ */ jsx7(Wrapper, { className: cn("inline-flex shrink-0", wrapperClassName) }), children }),
322
328
  /* @__PURE__ */ jsx7(TooltipSurface, { label, shortcut, placement })
323
329
  ] });
324
330
  }
@@ -343,48 +349,51 @@ function IconButton({
343
349
  tooltipShortcut,
344
350
  tooltipPlacement,
345
351
  type = "button",
352
+ href,
346
353
  ...props
347
354
  }) {
348
355
  const formBusy = useFormBusy();
349
- const button = /* @__PURE__ */ jsx8(
356
+ const unusable = disabled || !!disabledReason || formBusy;
357
+ const look = (off) => cn(
358
+ // `shrink-0` stops a flex parent squashing the button; `self-center`
359
+ // stops one stretching it. Two different failures, and both have
360
+ // happened here: a row with no `items-*` drew this 24 wide and 228
361
+ // tall in Peek's TopicMoreMenu story (Katerina, 2026-09-11). A button
362
+ // is the size of its icon and its padding, whatever box it lands in.
363
+ //
364
+ // This is the one of the two that needs it: an IconButton states no
365
+ // height, so its cross size is `auto` and a stretching parent takes
366
+ // it. Measured in the same 260px row on 2026-09-12: 24px with this
367
+ // class, 226px without. `Button` states `h-8`/`h-6`, so it cannot be
368
+ // stretched and carries no `self-center` — see the note there.
369
+ "flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 self-center cursor-pointer",
370
+ !off && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
371
+ !off && (variant === "muted" || variant === "resolve") && "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
372
+ !off && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
373
+ !off && variant === "current" && "hover:bg-bg-hover",
374
+ off && variant === "primary" && "bg-bg-disabled text-text-disabled",
375
+ off && (variant === "muted" || variant === "resolve") && "text-text-disabled",
376
+ off && variant === "outlined" && "border border-border-default text-text-disabled",
377
+ // Always, disabled too: the colour of where it sits, and Resolve's green.
378
+ variant === "current" && "text-current",
379
+ variant === "resolve" && "signal:hover:bg-success-muted signal:hover:text-success-default",
380
+ pressed && "bg-bg-active text-text-primary",
381
+ glow && "signal:shadow-glow-accent",
382
+ // `pointer-events-none` only where the button is truly out of reach:
383
+ // with a `disabledReason` the button IS the tooltip's trigger, and a
384
+ // trigger the pointer cannot land on never opens one. Base UI already
385
+ // swallows the click.
386
+ off && !disabledReason && "pointer-events-none",
387
+ off && "cursor-not-allowed",
388
+ className
389
+ );
390
+ const button = href && !unusable ? /* @__PURE__ */ jsx8("a", { href, className: look(false), ...props, children }) : /* @__PURE__ */ jsx8(
350
391
  BaseButton,
351
392
  {
352
393
  type,
353
- disabled: disabled || !!disabledReason || formBusy,
394
+ disabled: unusable,
354
395
  focusableWhenDisabled: !!disabledReason,
355
- className: (state) => cn(
356
- // `shrink-0` stops a flex parent squashing the button; `self-center`
357
- // stops one stretching it. Two different failures, and both have
358
- // happened here: a row with no `items-*` drew this 24 wide and 228
359
- // tall in Peek's TopicMoreMenu story (Katerina, 2026-09-11). A button
360
- // is the size of its icon and its padding, whatever box it lands in.
361
- //
362
- // This is the one of the two that needs it: an IconButton states no
363
- // height, so its cross size is `auto` and a stretching parent takes
364
- // it. Measured in the same 260px row on 2026-09-12: 24px with this
365
- // class, 226px without. `Button` states `h-8`/`h-6`, so it cannot be
366
- // stretched and carries no `self-center` — see the note there.
367
- "flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 self-center cursor-pointer",
368
- !state.disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
369
- !state.disabled && (variant === "muted" || variant === "resolve") && "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
370
- !state.disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
371
- !state.disabled && variant === "current" && "hover:bg-bg-hover",
372
- state.disabled && variant === "primary" && "bg-bg-disabled text-text-disabled",
373
- state.disabled && (variant === "muted" || variant === "resolve") && "text-text-disabled",
374
- state.disabled && variant === "outlined" && "border border-border-default text-text-disabled",
375
- // Always, disabled too: the colour of where it sits, and Resolve's green.
376
- variant === "current" && "text-current",
377
- variant === "resolve" && "signal:hover:bg-success-muted signal:hover:text-success-default",
378
- pressed && "bg-bg-active text-text-primary",
379
- glow && "signal:shadow-glow-accent",
380
- // `pointer-events-none` only where the button is truly out of reach:
381
- // with a `disabledReason` the button IS the tooltip's trigger, and a
382
- // trigger the pointer cannot land on never opens one. Base UI already
383
- // swallows the click.
384
- state.disabled && !disabledReason && "pointer-events-none",
385
- state.disabled && "cursor-not-allowed",
386
- className
387
- ),
396
+ className: (state) => look(state.disabled),
388
397
  "aria-pressed": pressed,
389
398
  ...props,
390
399
  children
@@ -434,7 +443,7 @@ function TypeIcon({ name }) {
434
443
  const Icon = ICON_BY_EXTENSION[extensionOf(name)] ?? IconFile;
435
444
  return /* @__PURE__ */ jsx9(Icon, { size: 20, stroke: 1.5 });
436
445
  }
437
- function Truncating({ text, hint, className, wrapperClassName }) {
446
+ function Truncating({ text, hint, className, wrapperClassName, placement }) {
438
447
  const ref = useRef(null);
439
448
  const [cut, setCut] = useState(false);
440
449
  useLayoutEffect(() => {
@@ -456,7 +465,7 @@ function Truncating({ text, hint, className, wrapperClassName }) {
456
465
  }, [text, cut]);
457
466
  const line = /* @__PURE__ */ jsx9("span", { ref, className, children: text });
458
467
  if (!hint && !cut) return line;
459
- return /* @__PURE__ */ jsx9(WithTooltip, { label: hint ?? text, wrapperClassName: cn("min-w-0 flex-col", wrapperClassName), children: line });
468
+ return /* @__PURE__ */ jsx9(WithTooltip, { label: hint ?? text, placement, wrapperClassName: cn("min-w-0 flex-col", wrapperClassName), children: line });
460
469
  }
461
470
  function AttachmentCard({
462
471
  name,
@@ -490,7 +499,7 @@ function AttachmentCard({
490
499
  children: [
491
500
  /* @__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) }) }),
492
501
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-[1px] min-w-0", children: [
493
- /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME_CLASSES }),
502
+ /* @__PURE__ */ jsx9(Truncating, { text: name, className: NAME_CLASSES, placement: onRemove ? "bottom" : void 0 }),
494
503
  /* @__PURE__ */ jsx9(
495
504
  Truncating,
496
505
  {
@@ -505,7 +514,7 @@ function AttachmentCard({
505
514
  {
506
515
  type: "button",
507
516
  "aria-label": `Remove ${name}`,
508
- className: "absolute -top-1.5 -right-1.5 size-5 rounded-full bg-bg-elevated border border-border-strong flex items-center justify-center text-text-secondary hover:text-text-primary opacity-0 group-hover:opacity-100 transition-opacity",
517
+ className: "absolute -top-1.5 -right-1.5 size-5 rounded-full bg-bg-elevated border border-border-strong flex items-center justify-center text-text-secondary hover:text-text-primary opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100 transition-opacity",
509
518
  onClick: (event) => {
510
519
  event.stopPropagation();
511
520
  onRemove();
@@ -1406,6 +1415,7 @@ function Form({ onSubmit, busy: ownBusy = false, enterSends = true, className, c
1406
1415
  },
1407
1416
  onKeyDown,
1408
1417
  onSubmit: (event) => {
1418
+ if (event.target !== event.currentTarget) return;
1409
1419
  event.preventDefault();
1410
1420
  if (busyNow.current) return;
1411
1421
  void onSubmit();
@@ -1944,56 +1954,65 @@ import { Select as BaseSelect } from "@base-ui/react/select";
1944
1954
  import { jsx as jsx30, jsxs as jsxs22 } from "react/jsx-runtime";
1945
1955
  var GAP4 = 4;
1946
1956
  var VIEWPORT_PAD4 = 8;
1947
- function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className, ...aria }) {
1957
+ function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, disabledReason, className, ...aria }) {
1948
1958
  const selected = options.find((o) => o.value === value);
1959
+ const held = Boolean(disabledReason) && !disabled;
1960
+ const trigger = /* @__PURE__ */ jsxs22(
1961
+ BaseSelect.Trigger,
1962
+ {
1963
+ "aria-label": ariaLabel,
1964
+ "aria-required": aria["aria-required"],
1965
+ "aria-disabled": held || void 0,
1966
+ className: cn(
1967
+ /*
1968
+ * `min-w-0 max-w-full`: a trigger must never outgrow its container
1969
+ * (Katerina, 2026-09-01 — a long label stretched the files panel's
1970
+ * Lead row past its card). In a flex row the default min-width:auto
1971
+ * forbids shrinking below the label's width, which is what kept
1972
+ * `truncate` from ever engaging; in a block container max-w-full is
1973
+ * the cap. Full-width callers are unaffected.
1974
+ */
1975
+ "flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left",
1976
+ "border-border-default text-text-primary outline-none transition-colors",
1977
+ // The focused border survives a hover: hover alone strengthens the
1978
+ // hairline, but hover while focused must not grey the focus colour —
1979
+ // the stacked variant outranks plain hover by specificity.
1980
+ !held && "hover:border-border-strong focus-visible:hover:border-border-focus aria-expanded:hover:border-border-focus",
1981
+ "disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
1982
+ held && "cursor-not-allowed bg-bg-disabled text-text-disabled",
1983
+ "focus-visible:border-border-focus aria-expanded:border-border-focus",
1984
+ "signal:transition-shadow signal:focus-visible:shadow-focus-ring",
1985
+ size === "default" && "px-3 py-2 text-input-value",
1986
+ size === "small" && "h-6 px-2 text-caption",
1987
+ className
1988
+ ),
1989
+ children: [
1990
+ /* @__PURE__ */ jsxs22("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
1991
+ selected?.leading && /* @__PURE__ */ jsx30("span", { className: "flex shrink-0 items-center", children: selected.leading }),
1992
+ /* @__PURE__ */ jsx30(BaseSelect.Value, { className: "truncate", children: () => selected?.label ?? placeholder })
1993
+ ] }),
1994
+ /* @__PURE__ */ jsx30(
1995
+ BaseSelect.Icon,
1996
+ {
1997
+ render: /* @__PURE__ */ jsx30(IconChevronDown2, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
1998
+ }
1999
+ )
2000
+ ]
2001
+ }
2002
+ );
1949
2003
  return /* @__PURE__ */ jsxs22(
1950
2004
  BaseSelect.Root,
1951
2005
  {
1952
2006
  value,
1953
- onValueChange: (next) => onChange(next),
2007
+ onValueChange: (next) => {
2008
+ if (!held) onChange(next);
2009
+ },
2010
+ ...held ? { open: false, onOpenChange: () => {
2011
+ } } : {},
1954
2012
  disabled,
1955
2013
  modal: false,
1956
2014
  children: [
1957
- /* @__PURE__ */ jsxs22(
1958
- BaseSelect.Trigger,
1959
- {
1960
- "aria-label": ariaLabel,
1961
- "aria-required": aria["aria-required"],
1962
- className: cn(
1963
- /*
1964
- * `min-w-0 max-w-full`: a trigger must never outgrow its container
1965
- * (Katerina, 2026-09-01 — a long label stretched the files panel's
1966
- * Lead row past its card). In a flex row the default min-width:auto
1967
- * forbids shrinking below the label's width, which is what kept
1968
- * `truncate` from ever engaging; in a block container max-w-full is
1969
- * the cap. Full-width callers are unaffected.
1970
- */
1971
- "flex w-full min-w-0 max-w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left",
1972
- "border-border-default text-text-primary outline-none transition-colors",
1973
- // The focused border survives a hover: hover alone strengthens the
1974
- // hairline, but hover while focused must not grey the focus colour —
1975
- // the stacked variant outranks plain hover by specificity.
1976
- "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",
1977
- "focus-visible:border-border-focus aria-expanded:border-border-focus",
1978
- "signal:transition-shadow signal:focus-visible:shadow-focus-ring",
1979
- size === "default" && "px-3 py-2 text-input-value",
1980
- size === "small" && "h-6 px-2 text-caption",
1981
- className
1982
- ),
1983
- children: [
1984
- /* @__PURE__ */ jsxs22("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
1985
- selected?.leading && /* @__PURE__ */ jsx30("span", { className: "flex shrink-0 items-center", children: selected.leading }),
1986
- /* @__PURE__ */ jsx30(BaseSelect.Value, { className: "truncate", children: () => selected?.label ?? placeholder })
1987
- ] }),
1988
- /* @__PURE__ */ jsx30(
1989
- BaseSelect.Icon,
1990
- {
1991
- render: /* @__PURE__ */ jsx30(IconChevronDown2, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
1992
- }
1993
- )
1994
- ]
1995
- }
1996
- ),
2015
+ held ? /* @__PURE__ */ jsx30(TooltipTrigger, { label: disabledReason ?? "", children: trigger }) : trigger,
1997
2016
  /* @__PURE__ */ jsx30(BaseSelect.Portal, { children: /* @__PURE__ */ jsx30(
1998
2017
  BaseSelect.Positioner,
1999
2018
  {
@@ -2521,7 +2540,7 @@ var GAP6 = 12;
2521
2540
  var VIEWPORT_PAD6 = 8;
2522
2541
  var OPEN_DELAY2 = 350;
2523
2542
  var CLOSE_DELAY = 200;
2524
- function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, closeDelay = CLOSE_DELAY, className, wrapperClassName }) {
2543
+ function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, closeDelay = CLOSE_DELAY, className, contentClassName, wrapperClassName }) {
2525
2544
  return /* @__PURE__ */ jsxs28(BasePreviewCard.Root, { children: [
2526
2545
  /* @__PURE__ */ jsx42(
2527
2546
  BasePreviewCard.Trigger,
@@ -2543,9 +2562,9 @@ function PreviewCard({ content, children, side = "right", delay = OPEN_DELAY2, c
2543
2562
  children: /* @__PURE__ */ jsx42(
2544
2563
  BasePreviewCard.Popup,
2545
2564
  {
2546
- className: cn("w-[360px] p-3 outline-none", className),
2565
+ className: cn("w-[360px] p-0 outline-none", className),
2547
2566
  render: /* @__PURE__ */ jsx42(MenuPanel, {}),
2548
- children: /* @__PURE__ */ jsx42(ScrollArea, { viewportClassName: "max-h-[calc(min(300px,var(--available-height))_-_1.5rem)]", contentClassName: "flex flex-col gap-3", children: content })
2567
+ children: /* @__PURE__ */ jsx42(ScrollArea, { viewportClassName: "max-h-[min(300px,var(--available-height))]", contentClassName: cn("flex flex-col gap-3 p-3", contentClassName), children: content })
2549
2568
  }
2550
2569
  )
2551
2570
  }
@@ -2697,7 +2716,7 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
2697
2716
  import { IconChevronRight as IconChevronRight2 } from "@tabler/icons-react";
2698
2717
  import { useRender } from "@base-ui/react/use-render";
2699
2718
  import { Fragment as Fragment8, jsx as jsx49, jsxs as jsxs33 } from "react/jsx-runtime";
2700
- function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = "hover", render, className }) {
2719
+ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = "hover", hover = "fill", render, className }) {
2701
2720
  const titleElement = useRender({
2702
2721
  render: render ?? (chevron ? /* @__PURE__ */ jsx49("button", { type: "button", onClick: onToggle, "aria-expanded": isExpanded }) : /* @__PURE__ */ jsx49("span", {})),
2703
2722
  props: {
@@ -2726,7 +2745,7 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, tr
2726
2745
  // The fill says "this does something": a row with a toggle or actions
2727
2746
  // lights up, a fixed heading over rows does not (2026-09-09, the
2728
2747
  // Sidebar's fixed group).
2729
- (chevron || actions && actions.length > 0) && "hover:bg-bg-hover",
2748
+ hover === "fill" && (chevron || actions && actions.length > 0) && "hover:bg-bg-hover",
2730
2749
  className
2731
2750
  ),
2732
2751
  children: [