@estiva-app/ui 0.6.0 → 0.8.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 (71) hide show
  1. package/README.md +23 -6
  2. package/dist/Button.d.ts +12 -5
  3. package/dist/Button.d.ts.map +1 -1
  4. package/dist/Checkbox.d.ts +19 -5
  5. package/dist/Checkbox.d.ts.map +1 -1
  6. package/dist/IconButton.d.ts +9 -2
  7. package/dist/IconButton.d.ts.map +1 -1
  8. package/dist/PersonTrigger.d.ts +2 -1
  9. package/dist/PersonTrigger.d.ts.map +1 -1
  10. package/dist/Reaction.d.ts +54 -0
  11. package/dist/Reaction.d.ts.map +1 -0
  12. package/dist/Tabs.d.ts +4 -1
  13. package/dist/Tabs.d.ts.map +1 -1
  14. package/dist/cn.d.ts +10 -1
  15. package/dist/cn.d.ts.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +383 -329
  19. package/dist/index.js.map +4 -4
  20. package/package.json +17 -3
  21. package/src/AppShell.stories.tsx +7 -1
  22. package/src/AppShell.tsx +1 -1
  23. package/src/Avatar.stories.tsx +3 -1
  24. package/src/Banner.stories.tsx +6 -1
  25. package/src/Banner.tsx +2 -2
  26. package/src/Breadcrumb.stories.tsx +3 -0
  27. package/src/Button.mdx +17 -3
  28. package/src/Button.stories.tsx +4 -1
  29. package/src/Button.test.tsx +119 -0
  30. package/src/Button.tsx +37 -23
  31. package/src/Checkbox.mdx +17 -6
  32. package/src/Checkbox.stories.tsx +10 -5
  33. package/src/Checkbox.test.tsx +73 -0
  34. package/src/Checkbox.tsx +52 -25
  35. package/src/Chip.stories.tsx +4 -0
  36. package/src/Chip.tsx +5 -5
  37. package/src/ChipInput.stories.tsx +4 -0
  38. package/src/DialogShell.tsx +1 -1
  39. package/src/EditableText.stories.tsx +6 -1
  40. package/src/IconButton.mdx +12 -1
  41. package/src/IconButton.stories.tsx +9 -4
  42. package/src/IconButton.test.tsx +102 -0
  43. package/src/IconButton.tsx +30 -17
  44. package/src/IdentityMenu.stories.tsx +5 -1
  45. package/src/Kbd.tsx +2 -2
  46. package/src/NavItem.stories.tsx +3 -0
  47. package/src/Person.stories.tsx +3 -0
  48. package/src/PersonTrigger.mdx +9 -1
  49. package/src/PersonTrigger.stories.tsx +3 -0
  50. package/src/PersonTrigger.test.tsx +52 -0
  51. package/src/PersonTrigger.tsx +8 -9
  52. package/src/Reaction.mdx +49 -0
  53. package/src/Reaction.stories.tsx +49 -0
  54. package/src/Reaction.tsx +86 -0
  55. package/src/Select.stories.tsx +7 -2
  56. package/src/Sidebar.stories.tsx +6 -0
  57. package/src/Tabs.mdx +13 -4
  58. package/src/Tabs.test.tsx +117 -0
  59. package/src/Tabs.tsx +45 -34
  60. package/src/Toast.tsx +8 -8
  61. package/src/TopBar.stories.tsx +7 -1
  62. package/src/cn.test.ts +20 -1
  63. package/src/cn.ts +18 -2
  64. package/src/index.ts +1 -0
  65. package/stories/Choosing.mdx +101 -0
  66. package/stories/DesignTokens.mdx +10 -0
  67. package/stories/GettingStarted.mdx +108 -0
  68. package/stories/Introduction.mdx +36 -0
  69. package/stories/TokensPage.tsx +293 -0
  70. package/tailwind-preset.js +17 -0
  71. package/tokens.css +48 -0
package/dist/index.js CHANGED
@@ -19,8 +19,16 @@ var FONT_SIZE_TOKENS = [
19
19
  "input-helper",
20
20
  "chip"
21
21
  ];
22
+ var BOX_SHADOW_TOKENS = ["sm", "md", "lg", "focus-ring", "glow-warning", "highlight-inset"];
23
+ var DROP_SHADOW_TOKENS = ["glow-success", "glow-accent"];
22
24
  var twMerge = extendTailwindMerge({
23
- extend: { classGroups: { "font-size": [{ text: FONT_SIZE_TOKENS }] } }
25
+ extend: {
26
+ classGroups: {
27
+ "font-size": [{ text: FONT_SIZE_TOKENS }],
28
+ shadow: [{ shadow: BOX_SHADOW_TOKENS }],
29
+ "drop-shadow": [{ "drop-shadow": DROP_SHADOW_TOKENS }]
30
+ }
31
+ }
24
32
  });
25
33
  function cn(...inputs) {
26
34
  return twMerge(clsx(inputs));
@@ -64,7 +72,7 @@ function AppShell({ variant = "solid", menu, logo, search, identity, banner, nav
64
72
  {
65
73
  className: cn(
66
74
  "flex min-w-0 flex-1 flex-col overflow-hidden rounded-2xl bg-bg-surface",
67
- "signal:border signal:border-border-subtle signal:shadow-[inset_0_1px_0_rgba(255,255,255,0.035)]"
75
+ "signal:border signal:border-border-subtle signal:shadow-highlight-inset"
68
76
  ),
69
77
  children: [
70
78
  banner,
@@ -155,18 +163,83 @@ function AvatarGroup({ members, size = 24 }) {
155
163
 
156
164
  // src/Banner.tsx
157
165
  import { jsx as jsx5 } from "react/jsx-runtime";
158
- var TONE = {
166
+ var TONE_STYLES = {
159
167
  ok: "bg-success-muted text-success-default",
160
168
  error: "bg-error-muted text-error-default",
161
169
  info: "bg-info-muted text-info-default",
162
170
  warning: "bg-warning-muted text-warning-default"
163
171
  };
164
172
  function Banner({ tone, children, className }) {
165
- return /* @__PURE__ */ jsx5("div", { role: tone === "error" ? "alert" : "status", className: cn("px-4 py-2 text-body-2", TONE[tone], className), children });
173
+ return /* @__PURE__ */ jsx5("div", { role: tone === "error" ? "alert" : "status", className: cn("px-4 py-2 text-body-2", TONE_STYLES[tone], className), children });
174
+ }
175
+
176
+ // src/Button.tsx
177
+ import { Button as BaseButton } from "@base-ui/react/button";
178
+
179
+ // src/Tooltip.tsx
180
+ import { useCallback, useLayoutEffect, useRef, useState as useState2 } from "react";
181
+ import { createPortal } from "react-dom";
182
+
183
+ // src/Kbd.tsx
184
+ import { jsx as jsx6 } from "react/jsx-runtime";
185
+ function Kbd({ children, className }) {
186
+ return /* @__PURE__ */ jsx6(
187
+ "kbd",
188
+ {
189
+ className: cn(
190
+ "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",
191
+ "signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-bg-wash signal:font-mono signal:text-[10px]",
192
+ "ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-bg-wash ship:font-mono ship:text-[10px]",
193
+ className
194
+ ),
195
+ children
196
+ }
197
+ );
198
+ }
199
+
200
+ // src/Tooltip.tsx
201
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
202
+ function Tooltip({ label, shortcut, className }) {
203
+ return /* @__PURE__ */ jsxs3("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), children: [
204
+ /* @__PURE__ */ jsx7("span", { className: "text-caption text-text-primary whitespace-nowrap", children: label }),
205
+ shortcut && /* @__PURE__ */ jsx7(Kbd, { children: shortcut })
206
+ ] });
207
+ }
208
+ var GAP = 6;
209
+ var VIEWPORT_PAD = 8;
210
+ function WithTooltip({ label, shortcut, placement = "top", wrapperClassName, children }) {
211
+ const [show, setShow] = useState2(false);
212
+ const ref = useRef(null);
213
+ const tooltipRef = useRef(null);
214
+ const [style, setStyle] = useState2({ position: "fixed", zIndex: 9999, pointerEvents: "none", visibility: "hidden" });
215
+ const reposition = useCallback(() => {
216
+ const trigger = ref.current;
217
+ const tip = tooltipRef.current;
218
+ if (!trigger || !tip) return;
219
+ const triggerRect = trigger.getBoundingClientRect();
220
+ const tipRect = tip.getBoundingClientRect();
221
+ let top = placement === "bottom" ? triggerRect.bottom + GAP : triggerRect.top - tipRect.height - GAP;
222
+ if (placement === "top" && top < VIEWPORT_PAD) top = triggerRect.bottom + GAP;
223
+ else if (placement === "bottom" && top + tipRect.height > window.innerHeight - VIEWPORT_PAD) top = triggerRect.top - tipRect.height - GAP;
224
+ top = Math.max(VIEWPORT_PAD, Math.min(top, window.innerHeight - tipRect.height - VIEWPORT_PAD));
225
+ let left = triggerRect.left + triggerRect.width / 2 - tipRect.width / 2;
226
+ left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - tipRect.width - VIEWPORT_PAD));
227
+ setStyle({ position: "fixed", top, left, zIndex: 9999, pointerEvents: "none", visibility: "visible" });
228
+ }, [placement]);
229
+ useLayoutEffect(() => {
230
+ if (show) reposition();
231
+ }, [show, reposition]);
232
+ return /* @__PURE__ */ jsxs3("div", { ref, className: cn("inline-flex shrink-0", wrapperClassName), onMouseEnter: () => setShow(true), onMouseLeave: () => setShow(false), children: [
233
+ children,
234
+ show && createPortal(
235
+ /* @__PURE__ */ jsx7("div", { ref: tooltipRef, style, children: /* @__PURE__ */ jsx7(Tooltip, { label, shortcut }) }),
236
+ document.body
237
+ )
238
+ ] });
166
239
  }
167
240
 
168
241
  // src/Button.tsx
169
- import { jsxs as jsxs3 } from "react/jsx-runtime";
242
+ import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
170
243
  function Button({
171
244
  variant = "muted",
172
245
  size = "default",
@@ -174,30 +247,32 @@ function Button({
174
247
  className,
175
248
  children,
176
249
  disabled,
250
+ disabledReason,
177
251
  type = "button",
178
252
  ...props
179
253
  }) {
180
254
  const hasLeadingIcon = !!leadingIcon;
181
- return /* @__PURE__ */ jsxs3(
182
- "button",
255
+ const button = /* @__PURE__ */ jsxs4(
256
+ BaseButton,
183
257
  {
184
258
  type,
185
- className: cn(
259
+ disabled: disabled || !!disabledReason,
260
+ focusableWhenDisabled: !!disabledReason,
261
+ className: (state) => cn(
186
262
  "inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium",
187
- size === "default" && "h-8 text-[14px] leading-[14px]",
188
- size === "small" && "h-6 text-[12px] leading-[12px]",
263
+ size === "default" && "h-8 text-btn-default",
264
+ size === "small" && "h-6 text-btn-small",
189
265
  // Extra right padding beside a leading icon, for optical balance.
190
266
  size === "default" && (hasLeadingIcon ? "pl-2 pr-3" : "px-2"),
191
267
  size === "small" && (hasLeadingIcon ? "pl-1.5 pr-2" : "px-1.5"),
192
- !disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold",
193
- !disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer",
194
- !disabled && variant === "muted" && "hover:bg-bg-hover text-text-primary cursor-pointer",
195
- !disabled && variant === "destructive" && "hover:bg-error-muted text-error-default cursor-pointer",
196
- disabled && "bg-bg-disabled text-text-disabled pointer-events-none",
197
- disabled && variant === "outlined" && "border border-border-default",
268
+ !state.disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold",
269
+ !state.disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer",
270
+ !state.disabled && variant === "muted" && "hover:bg-bg-hover text-text-primary cursor-pointer",
271
+ !state.disabled && variant === "destructive" && "hover:bg-error-muted text-error-default cursor-pointer",
272
+ state.disabled && "bg-bg-disabled text-text-disabled pointer-events-none",
273
+ state.disabled && variant === "outlined" && "border border-border-default",
198
274
  className
199
275
  ),
200
- disabled,
201
276
  ...props,
202
277
  children: [
203
278
  leadingIcon,
@@ -205,62 +280,67 @@ function Button({
205
280
  ]
206
281
  }
207
282
  );
283
+ return disabledReason ? /* @__PURE__ */ jsx8(WithTooltip, { label: disabledReason, children: button }) : button;
208
284
  }
209
285
 
210
286
  // src/Checkbox.tsx
287
+ import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
211
288
  import { IconCheck } from "@tabler/icons-react";
212
- import { jsx as jsx6 } from "react/jsx-runtime";
289
+ import { jsx as jsx9 } from "react/jsx-runtime";
290
+ function squareClasses(checked, disabled, interactive, className) {
291
+ return cn(
292
+ "inline-flex items-center justify-center size-4 shrink-0 rounded-[4px] border transition-colors",
293
+ checked ? "bg-accent-primary border-accent-primary text-text-inverse" : "bg-transparent border-border-strong hover:border-text-muted",
294
+ disabled && "opacity-50 pointer-events-none",
295
+ interactive ? "cursor-pointer" : "pointer-events-none",
296
+ className
297
+ );
298
+ }
299
+ var tickClasses = (checked) => cn("flex", !checked && "invisible");
213
300
  function Checkbox({ checked, onChange, disabled = false, className, ...aria }) {
214
- return /* @__PURE__ */ jsx6(
215
- "button",
301
+ if (!onChange) {
302
+ return /* @__PURE__ */ jsx9("span", { "aria-hidden": "true", className: squareClasses(checked, disabled, false, className), children: /* @__PURE__ */ jsx9("span", { className: tickClasses(checked), children: /* @__PURE__ */ jsx9(IconCheck, { size: 12, stroke: 3 }) }) });
303
+ }
304
+ return /* @__PURE__ */ jsx9(
305
+ BaseCheckbox.Root,
216
306
  {
217
- type: "button",
218
- role: "checkbox",
219
- "aria-checked": checked,
220
- "aria-label": aria["aria-label"],
307
+ checked,
221
308
  disabled,
222
- onClick: (e) => {
223
- e.stopPropagation();
224
- onChange?.(!checked);
225
- },
226
- className: cn(
227
- "inline-flex items-center justify-center size-4 shrink-0 rounded-[4px] border transition-colors",
228
- checked ? "bg-accent-primary border-accent-primary text-text-inverse" : "bg-transparent border-border-strong hover:border-text-muted",
229
- disabled && "opacity-50 pointer-events-none",
230
- onChange ? "cursor-pointer" : "pointer-events-none",
231
- className
232
- ),
233
- children: checked && /* @__PURE__ */ jsx6(IconCheck, { size: 12, stroke: 3 })
309
+ "aria-label": aria["aria-label"],
310
+ onCheckedChange: (next) => onChange(next),
311
+ onClick: (e) => e.stopPropagation(),
312
+ className: (state) => squareClasses(state.checked, state.disabled, true, className),
313
+ children: /* @__PURE__ */ jsx9(BaseCheckbox.Indicator, { keepMounted: true, className: (state) => tickClasses(state.checked), children: /* @__PURE__ */ jsx9(IconCheck, { size: 12, stroke: 3 }) })
234
314
  }
235
315
  );
236
316
  }
237
317
 
238
318
  // src/Chip.tsx
239
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
319
+ import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
240
320
  var typeStyles = {
241
321
  neutral: "bg-bg-inset text-text-primary",
242
- brand: "bg-accent-muted text-accent-primary signal:border signal:border-[rgba(86,200,255,0.3)]",
243
- info: "bg-info-muted text-info-default signal:border signal:border-[rgba(86,200,255,0.3)]",
244
- warning: "bg-warning-muted text-warning-default signal:border signal:border-[rgba(255,176,32,0.3)] signal:shadow-[shadow:0_0_5px_rgba(255,176,32,0.4)]",
245
- success: "bg-success-muted text-success-default signal:border signal:border-[rgba(63,222,140,0.3)]",
246
- error: "bg-error-muted text-error-default signal:border signal:border-[rgba(255,107,107,0.3)]"
322
+ brand: "bg-accent-muted text-accent-primary signal:border signal:border-accent-outline",
323
+ info: "bg-info-muted text-info-default signal:border signal:border-info-outline",
324
+ warning: "bg-warning-muted text-warning-default signal:border signal:border-warning-outline signal:shadow-glow-warning",
325
+ success: "bg-success-muted text-success-default signal:border signal:border-success-outline",
326
+ error: "bg-error-muted text-error-default signal:border signal:border-error-outline"
247
327
  };
248
328
  function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className }) {
249
- return /* @__PURE__ */ jsxs4("div", { className: cn("inline-flex items-center justify-center gap-1.5 rounded-full max-h-[20px] min-w-[16px] px-2 py-1", typeStyles[type], className), children: [
250
- leadingIcon && /* @__PURE__ */ jsx7("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
251
- label && /* @__PURE__ */ jsx7("span", { className: "text-chip whitespace-nowrap signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums", children: label }),
252
- trailingIcon && /* @__PURE__ */ jsx7("span", { className: "flex size-3 shrink-0 items-center justify-center", children: trailingIcon })
329
+ return /* @__PURE__ */ jsxs5("div", { className: cn("inline-flex items-center justify-center gap-1.5 rounded-full max-h-[20px] min-w-[16px] px-2 py-1", typeStyles[type], className), children: [
330
+ leadingIcon && /* @__PURE__ */ jsx10("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
331
+ label && /* @__PURE__ */ jsx10("span", { className: "text-chip whitespace-nowrap signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums", children: label }),
332
+ trailingIcon && /* @__PURE__ */ jsx10("span", { className: "flex size-3 shrink-0 items-center justify-center", children: trailingIcon })
253
333
  ] });
254
334
  }
255
335
 
256
336
  // src/ChipInput.tsx
257
- import { useState as useState3, useRef as useRef2, useMemo, useEffect as useEffect2, useLayoutEffect as useLayoutEffect2 } from "react";
258
- import { createPortal as createPortal2 } from "react-dom";
337
+ import { useState as useState4, useRef as useRef3, useMemo, useEffect as useEffect2, useLayoutEffect as useLayoutEffect3 } from "react";
338
+ import { createPortal as createPortal3 } from "react-dom";
259
339
  import { IconX } from "@tabler/icons-react";
260
340
 
261
341
  // src/fit.ts
262
342
  var MARGIN = 8;
263
- var GAP = 4;
343
+ var GAP2 = 4;
264
344
  function fitMenu({
265
345
  anchor,
266
346
  menu,
@@ -268,11 +348,11 @@ function fitMenu({
268
348
  cap = Number.POSITIVE_INFINITY
269
349
  }) {
270
350
  const left = Math.max(MARGIN, Math.min(anchor.left, viewport.width - menu.width - MARGIN));
271
- const below = viewport.height - anchor.bottom - GAP - MARGIN;
272
- const above = anchor.top - GAP - MARGIN;
351
+ const below = viewport.height - anchor.bottom - GAP2 - MARGIN;
352
+ const above = anchor.top - GAP2 - MARGIN;
273
353
  const openUp = below < Math.min(menu.contentHeight, cap) && above > below;
274
354
  const maxHeight = Math.max(Math.min(cap, openUp ? above : below), 120);
275
- return openUp ? { left, bottom: viewport.height - anchor.top + GAP, maxHeight } : { left, top: anchor.bottom + GAP, maxHeight };
355
+ return openUp ? { left, bottom: viewport.height - anchor.top + GAP2, maxHeight } : { left, top: anchor.bottom + GAP2, maxHeight };
276
356
  }
277
357
  function clampBox({
278
358
  box,
@@ -288,38 +368,21 @@ function fitSubmenu({
288
368
  panel,
289
369
  viewport
290
370
  }) {
291
- const fitsRight = row.right + GAP + panel.width <= viewport.width - MARGIN;
292
- const left = Math.max(MARGIN, fitsRight ? row.right + GAP : row.left - GAP - panel.width);
371
+ const fitsRight = row.right + GAP2 + panel.width <= viewport.width - MARGIN;
372
+ const left = Math.max(MARGIN, fitsRight ? row.right + GAP2 : row.left - GAP2 - panel.width);
293
373
  const top = Math.max(MARGIN, Math.min(row.top, viewport.height - panel.height - MARGIN));
294
374
  return { left, top };
295
375
  }
296
376
 
297
377
  // src/Menu.tsx
298
- import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState as useState2 } from "react";
378
+ import { createContext, useCallback as useCallback2, useContext, useEffect, useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState as useState3 } from "react";
299
379
  import { IconChevronRight } from "@tabler/icons-react";
300
- import { createPortal } from "react-dom";
301
-
302
- // src/Kbd.tsx
303
- import { jsx as jsx8 } from "react/jsx-runtime";
304
- function Kbd({ children, className }) {
305
- return /* @__PURE__ */ jsx8(
306
- "kbd",
307
- {
308
- className: cn(
309
- "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",
310
- "signal:border-b-2 signal:pt-[2px] signal:pb-px signal:bg-[rgba(255,255,255,.05)] signal:font-mono signal:text-[10px]",
311
- "ship:border-b-2 ship:pt-[2px] ship:pb-px ship:bg-[rgba(255,255,255,.05)] ship:font-mono ship:text-[10px]",
312
- className
313
- ),
314
- children
315
- }
316
- );
317
- }
380
+ import { createPortal as createPortal2 } from "react-dom";
318
381
 
319
382
  // src/SectionLabel.tsx
320
- import { jsx as jsx9 } from "react/jsx-runtime";
383
+ import { jsx as jsx11 } from "react/jsx-runtime";
321
384
  function SectionLabel({ children, className }) {
322
- return /* @__PURE__ */ jsx9(
385
+ return /* @__PURE__ */ jsx11(
323
386
  "span",
324
387
  {
325
388
  className: cn(
@@ -332,10 +395,10 @@ function SectionLabel({ children, className }) {
332
395
  }
333
396
 
334
397
  // src/Menu.tsx
335
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
398
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
336
399
  var MenuHoverContext = createContext(null);
337
400
  function MenuPanel({ children, className, ...props }) {
338
- return /* @__PURE__ */ jsx10(
401
+ return /* @__PURE__ */ jsx12(
339
402
  "div",
340
403
  {
341
404
  className: cn("flex flex-col rounded-lg border border-border-default bg-bg-elevated p-2 shadow-lg", className),
@@ -345,17 +408,17 @@ function MenuPanel({ children, className, ...props }) {
345
408
  );
346
409
  }
347
410
  function Menu({ onClose, anchor, align = "left", position, closeOnLeave = false, children, className }) {
348
- const ref = useRef(null);
349
- const leaveTimer = useRef(void 0);
350
- const hold = useCallback(() => clearTimeout(leaveTimer.current), []);
351
- const release = useCallback(() => {
411
+ const ref = useRef2(null);
412
+ const leaveTimer = useRef2(void 0);
413
+ const hold = useCallback2(() => clearTimeout(leaveTimer.current), []);
414
+ const release = useCallback2(() => {
352
415
  if (!closeOnLeave) return;
353
416
  clearTimeout(leaveTimer.current);
354
417
  leaveTimer.current = setTimeout(onClose, 150);
355
418
  }, [closeOnLeave, onClose]);
356
419
  useEffect(() => () => clearTimeout(leaveTimer.current), []);
357
420
  const portalled = Boolean(anchor || position);
358
- const [placed, setPlaced] = useState2(null);
421
+ const [placed, setPlaced] = useState3(null);
359
422
  useEffect(() => {
360
423
  const onDown = (event) => {
361
424
  if (!ref.current?.contains(event.target)) onClose();
@@ -373,7 +436,7 @@ function Menu({ onClose, anchor, align = "left", position, closeOnLeave = false,
373
436
  const posLeft = position && "left" in position ? position.left : void 0;
374
437
  const posRight = position && "right" in position ? position.right : void 0;
375
438
  const posTop = position?.top;
376
- useLayoutEffect(() => {
439
+ useLayoutEffect2(() => {
377
440
  if (!portalled || !ref.current) return;
378
441
  const viewport = { width: window.innerWidth, height: window.innerHeight };
379
442
  const menu = ref.current;
@@ -409,7 +472,7 @@ function Menu({ onClose, anchor, align = "left", position, closeOnLeave = false,
409
472
  // The provisional render: measured by the layout effect, never seen.
410
473
  { left: 0, top: 0, visibility: "hidden" }
411
474
  ) : void 0;
412
- const node = /* @__PURE__ */ jsx10(MenuHoverContext.Provider, { value: { hold, release }, children: /* @__PURE__ */ jsx10(
475
+ const node = /* @__PURE__ */ jsx12(MenuHoverContext.Provider, { value: { hold, release }, children: /* @__PURE__ */ jsx12(
413
476
  MenuPanel,
414
477
  {
415
478
  ref,
@@ -427,14 +490,14 @@ function Menu({ onClose, anchor, align = "left", position, closeOnLeave = false,
427
490
  children
428
491
  }
429
492
  ) });
430
- return portalled ? createPortal(node, document.body) : node;
493
+ return portalled ? createPortal2(node, document.body) : node;
431
494
  }
432
495
  function MenuSub({ label, leading, selected, children, className }) {
433
- const [open, setOpen] = useState2(false);
434
- const rowRef = useRef(null);
435
- const panelRef = useRef(null);
436
- const closeTimer = useRef(void 0);
437
- const [placed, setPlaced] = useState2(null);
496
+ const [open, setOpen] = useState3(false);
497
+ const rowRef = useRef2(null);
498
+ const panelRef = useRef2(null);
499
+ const closeTimer = useRef2(void 0);
500
+ const [placed, setPlaced] = useState3(null);
438
501
  const menuHover = useContext(MenuHoverContext);
439
502
  const enter = () => {
440
503
  clearTimeout(closeTimer.current);
@@ -446,7 +509,7 @@ function MenuSub({ label, leading, selected, children, className }) {
446
509
  menuHover?.release();
447
510
  };
448
511
  useEffect(() => () => clearTimeout(closeTimer.current), []);
449
- useLayoutEffect(() => {
512
+ useLayoutEffect2(() => {
450
513
  if (!open || !rowRef.current || !panelRef.current) {
451
514
  setPlaced(null);
452
515
  return;
@@ -460,10 +523,10 @@ function MenuSub({ label, leading, selected, children, className }) {
460
523
  })
461
524
  );
462
525
  }, [open]);
463
- return /* @__PURE__ */ jsxs5("div", { ref: rowRef, onMouseEnter: enter, onMouseLeave: leave, children: [
464
- /* @__PURE__ */ jsx10(MenuItem, { label, leading, selected, submenu: true }),
465
- open && createPortal(
466
- /* @__PURE__ */ jsx10(
526
+ return /* @__PURE__ */ jsxs6("div", { ref: rowRef, onMouseEnter: enter, onMouseLeave: leave, children: [
527
+ /* @__PURE__ */ jsx12(MenuItem, { label, leading, selected, submenu: true }),
528
+ open && createPortal2(
529
+ /* @__PURE__ */ jsx12(
467
530
  MenuPanel,
468
531
  {
469
532
  ref: panelRef,
@@ -481,8 +544,8 @@ function MenuSub({ label, leading, selected, children, className }) {
481
544
  ] });
482
545
  }
483
546
  function MenuItem({ label, children, size = "default", description, leading, trailing, hint, shortcut, submenu, destructive, selected, className, ...props }) {
484
- const edge = trailing ?? (shortcut ? /* @__PURE__ */ jsx10(Kbd, { children: shortcut }) : submenu ? /* @__PURE__ */ jsx10(IconChevronRight, { size: 16, stroke: 1.5, className: "shrink-0 text-text-muted" }) : null);
485
- return /* @__PURE__ */ jsxs5(
547
+ const edge = trailing ?? (shortcut ? /* @__PURE__ */ jsx12(Kbd, { children: shortcut }) : submenu ? /* @__PURE__ */ jsx12(IconChevronRight, { size: 16, stroke: 1.5, className: "shrink-0 text-text-muted" }) : null);
548
+ return /* @__PURE__ */ jsxs6(
486
549
  "button",
487
550
  {
488
551
  type: "button",
@@ -513,43 +576,43 @@ function MenuItem({ label, children, size = "default", description, leading, tra
513
576
  ),
514
577
  ...props,
515
578
  children: [
516
- leading && /* @__PURE__ */ jsx10("span", { className: "flex shrink-0 items-center", children: leading }),
517
- children ?? /* @__PURE__ */ jsxs5("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
518
- /* @__PURE__ */ jsx10("span", { className: cn("truncate text-[14px] leading-[140%]", destructive ? "text-error-default" : "text-text-primary"), children: label }),
519
- description && /* @__PURE__ */ jsx10("span", { className: "truncate text-[12px] leading-[120%] text-text-secondary", children: description })
579
+ leading && /* @__PURE__ */ jsx12("span", { className: "flex shrink-0 items-center", children: leading }),
580
+ children ?? /* @__PURE__ */ jsxs6("span", { className: cn("flex min-w-0 flex-1 flex-col", size === "tall" && "gap-[2px]"), children: [
581
+ /* @__PURE__ */ jsx12("span", { className: cn("truncate text-[14px] leading-[140%]", destructive ? "text-error-default" : "text-text-primary"), children: label }),
582
+ description && /* @__PURE__ */ jsx12("span", { className: "truncate text-[12px] leading-[120%] text-text-secondary", children: description })
520
583
  ] }),
521
584
  (edge || hint) && // One grid cell holding both, right-aligned: the slot is as wide as
522
585
  // the wider of the two and never changes, so revealing the hint moves
523
586
  // nothing. No transition here either — the hint switches on the same
524
587
  // :hover / `selected` as the fill, in the same frame.
525
- /* @__PURE__ */ jsxs5("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
526
- edge && /* @__PURE__ */ jsx10("span", { className: cn("flex items-center", hint && "group-hover:opacity-0", hint && selected && "opacity-0"), children: edge }),
527
- hint && /* @__PURE__ */ jsx10("span", { className: cn("flex items-center opacity-0 group-hover:opacity-100", selected && "opacity-100"), children: hint })
588
+ /* @__PURE__ */ jsxs6("span", { className: "grid shrink-0 items-center justify-items-end [&>*]:col-start-1 [&>*]:row-start-1", children: [
589
+ edge && /* @__PURE__ */ jsx12("span", { className: cn("flex items-center", hint && "group-hover:opacity-0", hint && selected && "opacity-0"), children: edge }),
590
+ hint && /* @__PURE__ */ jsx12("span", { className: cn("flex items-center opacity-0 group-hover:opacity-100", selected && "opacity-100"), children: hint })
528
591
  ] })
529
592
  ]
530
593
  }
531
594
  );
532
595
  }
533
596
  function EnterHint({ target }) {
534
- return /* @__PURE__ */ jsxs5("span", { className: "flex shrink-0 items-center gap-1.5 text-text-muted", children: [
535
- /* @__PURE__ */ jsx10(Kbd, { children: "\u21A9 Enter" }),
536
- target && /* @__PURE__ */ jsx10("span", { className: "text-[9px] font-medium leading-[115%] signal:font-mono signal:text-[9.5px] signal:tracking-[0.04em]", children: target })
597
+ return /* @__PURE__ */ jsxs6("span", { className: "flex shrink-0 items-center gap-1.5 text-text-muted", children: [
598
+ /* @__PURE__ */ jsx12(Kbd, { children: "\u21A9 Enter" }),
599
+ target && /* @__PURE__ */ jsx12("span", { className: "text-[9px] font-medium leading-[115%] signal:font-mono signal:text-[9.5px] signal:tracking-[0.04em]", children: target })
537
600
  ] });
538
601
  }
539
602
  function MenuSection({ label, children, className }) {
540
- return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col", children: [
541
- /* @__PURE__ */ jsx10("div", { className: cn("flex h-8 items-center px-2", className), children: /* @__PURE__ */ jsx10(SectionLabel, { className: "text-text-secondary", children: label }) }),
603
+ return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col", children: [
604
+ /* @__PURE__ */ jsx12("div", { className: cn("flex h-8 items-center px-2", className), children: /* @__PURE__ */ jsx12(SectionLabel, { className: "text-text-secondary", children: label }) }),
542
605
  children
543
606
  ] });
544
607
  }
545
608
  function MenuRow({ children, className }) {
546
- return /* @__PURE__ */ jsx10("div", { className: cn("flex items-center gap-2 px-2 py-1.5", className), children });
609
+ return /* @__PURE__ */ jsx12("div", { className: cn("flex items-center gap-2 px-2 py-1.5", className), children });
547
610
  }
548
611
 
549
612
  // src/ChipInput.tsx
550
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
613
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
551
614
  function InputChip({ label, leading, onRemove, className }) {
552
- return /* @__PURE__ */ jsxs6(
615
+ return /* @__PURE__ */ jsxs7(
553
616
  "div",
554
617
  {
555
618
  className: cn(
@@ -565,9 +628,9 @@ function InputChip({ label, leading, onRemove, className }) {
565
628
  className
566
629
  ),
567
630
  children: [
568
- leading && /* @__PURE__ */ jsx11("span", { className: "flex shrink-0 items-center", children: leading }),
569
- /* @__PURE__ */ jsx11("span", { className: "text-caption font-medium text-text-primary", children: label }),
570
- onRemove && /* @__PURE__ */ jsx11(
631
+ leading && /* @__PURE__ */ jsx13("span", { className: "flex shrink-0 items-center", children: leading }),
632
+ /* @__PURE__ */ jsx13("span", { className: "text-caption font-medium text-text-primary", children: label }),
633
+ onRemove && /* @__PURE__ */ jsx13(
571
634
  "button",
572
635
  {
573
636
  type: "button",
@@ -577,7 +640,7 @@ function InputChip({ label, leading, onRemove, className }) {
577
640
  },
578
641
  className: "size-4 flex items-center justify-center rounded-full hover:bg-bg-hover text-text-secondary",
579
642
  "aria-label": `Remove ${label}`,
580
- children: /* @__PURE__ */ jsx11(IconX, { size: 10, stroke: 1.5 })
643
+ children: /* @__PURE__ */ jsx13(IconX, { size: 10, stroke: 1.5 })
581
644
  }
582
645
  )
583
646
  ]
@@ -594,12 +657,12 @@ function ChipInput({
594
657
  chipLeading,
595
658
  rowLeading
596
659
  }) {
597
- const [query, setQuery] = useState3("");
598
- const [highlight, setHighlight] = useState3(0);
599
- const [isFocused, setIsFocused] = useState3(false);
600
- const [anchorRect, setAnchorRect] = useState3(null);
601
- const wrapperRef = useRef2(null);
602
- const inputRef = useRef2(null);
660
+ const [query, setQuery] = useState4("");
661
+ const [highlight, setHighlight] = useState4(0);
662
+ const [isFocused, setIsFocused] = useState4(false);
663
+ const [anchorRect, setAnchorRect] = useState4(null);
664
+ const wrapperRef = useRef3(null);
665
+ const inputRef = useRef3(null);
603
666
  const matches = useMemo(() => {
604
667
  const selectedIds = new Set(value.map((o) => o.id));
605
668
  const excludedIds = new Set(excludeIds);
@@ -615,7 +678,7 @@ function ChipInput({
615
678
  setHighlight(0);
616
679
  }, [query, matches.length]);
617
680
  const showDropdown = isFocused && query.trim().length > 0 && matches.length > 0;
618
- useLayoutEffect2(() => {
681
+ useLayoutEffect3(() => {
619
682
  if (!showDropdown) return;
620
683
  const update = () => {
621
684
  if (wrapperRef.current) setAnchorRect(wrapperRef.current.getBoundingClientRect());
@@ -665,16 +728,16 @@ function ChipInput({
665
728
  }
666
729
  }
667
730
  }
668
- return /* @__PURE__ */ jsxs6("div", { className: "relative", children: [
669
- /* @__PURE__ */ jsxs6(
731
+ return /* @__PURE__ */ jsxs7("div", { className: "relative", children: [
732
+ /* @__PURE__ */ jsxs7(
670
733
  "div",
671
734
  {
672
735
  ref: wrapperRef,
673
736
  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",
674
737
  onClick: () => inputRef.current?.focus(),
675
738
  children: [
676
- value.map((o) => /* @__PURE__ */ jsx11(InputChip, { label: o.label, leading: chipLeading?.(o), onRemove: () => removeOption(o.id) }, o.id)),
677
- /* @__PURE__ */ jsx11(
739
+ value.map((o) => /* @__PURE__ */ jsx13(InputChip, { label: o.label, leading: chipLeading?.(o), onRemove: () => removeOption(o.id) }, o.id)),
740
+ /* @__PURE__ */ jsx13(
678
741
  "input",
679
742
  {
680
743
  ref: inputRef,
@@ -694,8 +757,8 @@ function ChipInput({
694
757
  ]
695
758
  }
696
759
  ),
697
- showDropdown && anchorRect && createPortal2(
698
- /* @__PURE__ */ jsx11(
760
+ showDropdown && anchorRect && createPortal3(
761
+ /* @__PURE__ */ jsx13(
699
762
  "div",
700
763
  {
701
764
  className: "fixed z-[60] overflow-y-auto bg-bg-elevated border border-border-default rounded-lg shadow-lg",
@@ -708,7 +771,7 @@ function ChipInput({
708
771
  }),
709
772
  width: anchorRect.width
710
773
  },
711
- children: matches.map((o, i) => /* @__PURE__ */ jsx11(
774
+ children: matches.map((o, i) => /* @__PURE__ */ jsx13(
712
775
  MenuItem,
713
776
  {
714
777
  size: "tall",
@@ -732,84 +795,45 @@ function ChipInput({
732
795
  ] });
733
796
  }
734
797
 
735
- // src/Tooltip.tsx
736
- import { useCallback as useCallback2, useLayoutEffect as useLayoutEffect3, useRef as useRef3, useState as useState4 } from "react";
737
- import { createPortal as createPortal3 } from "react-dom";
738
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
739
- function Tooltip({ label, shortcut, className }) {
740
- return /* @__PURE__ */ jsxs7("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), children: [
741
- /* @__PURE__ */ jsx12("span", { className: "text-caption text-text-primary whitespace-nowrap", children: label }),
742
- shortcut && /* @__PURE__ */ jsx12(Kbd, { children: shortcut })
743
- ] });
744
- }
745
- var GAP2 = 6;
746
- var VIEWPORT_PAD = 8;
747
- function WithTooltip({ label, shortcut, placement = "top", wrapperClassName, children }) {
748
- const [show, setShow] = useState4(false);
749
- const ref = useRef3(null);
750
- const tooltipRef = useRef3(null);
751
- const [style, setStyle] = useState4({ position: "fixed", zIndex: 9999, pointerEvents: "none", visibility: "hidden" });
752
- const reposition = useCallback2(() => {
753
- const trigger = ref.current;
754
- const tip = tooltipRef.current;
755
- if (!trigger || !tip) return;
756
- const triggerRect = trigger.getBoundingClientRect();
757
- const tipRect = tip.getBoundingClientRect();
758
- let top = placement === "bottom" ? triggerRect.bottom + GAP2 : triggerRect.top - tipRect.height - GAP2;
759
- if (placement === "top" && top < VIEWPORT_PAD) top = triggerRect.bottom + GAP2;
760
- else if (placement === "bottom" && top + tipRect.height > window.innerHeight - VIEWPORT_PAD) top = triggerRect.top - tipRect.height - GAP2;
761
- top = Math.max(VIEWPORT_PAD, Math.min(top, window.innerHeight - tipRect.height - VIEWPORT_PAD));
762
- let left = triggerRect.left + triggerRect.width / 2 - tipRect.width / 2;
763
- left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - tipRect.width - VIEWPORT_PAD));
764
- setStyle({ position: "fixed", top, left, zIndex: 9999, pointerEvents: "none", visibility: "visible" });
765
- }, [placement]);
766
- useLayoutEffect3(() => {
767
- if (show) reposition();
768
- }, [show, reposition]);
769
- return /* @__PURE__ */ jsxs7("div", { ref, className: cn("inline-flex shrink-0", wrapperClassName), onMouseEnter: () => setShow(true), onMouseLeave: () => setShow(false), children: [
770
- children,
771
- show && createPortal3(
772
- /* @__PURE__ */ jsx12("div", { ref: tooltipRef, style, children: /* @__PURE__ */ jsx12(Tooltip, { label, shortcut }) }),
773
- document.body
774
- )
775
- ] });
776
- }
777
-
778
798
  // src/IconButton.tsx
779
- import { jsx as jsx13 } from "react/jsx-runtime";
799
+ import { Button as BaseButton2 } from "@base-ui/react/button";
800
+ import { jsx as jsx14 } from "react/jsx-runtime";
780
801
  function IconButton({
781
802
  variant = "muted",
782
803
  className,
783
804
  children,
784
805
  disabled,
806
+ disabledReason,
785
807
  tooltip,
786
808
  tooltipShortcut,
787
809
  tooltipPlacement,
788
810
  type = "button",
789
811
  ...props
790
812
  }) {
791
- const button = /* @__PURE__ */ jsx13(
792
- "button",
813
+ const button = /* @__PURE__ */ jsx14(
814
+ BaseButton2,
793
815
  {
794
816
  type,
795
- className: cn(
817
+ disabled: disabled || !!disabledReason,
818
+ focusableWhenDisabled: !!disabledReason,
819
+ className: (state) => cn(
796
820
  "flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 cursor-pointer",
797
- !disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
798
- !disabled && variant === "muted" && "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
799
- !disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
800
- disabled && variant === "primary" && "bg-bg-disabled text-text-disabled",
801
- disabled && variant === "muted" && "text-text-disabled",
802
- disabled && variant === "outlined" && "border border-border-default text-text-disabled",
803
- disabled && "pointer-events-none cursor-not-allowed",
821
+ !state.disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
822
+ !state.disabled && variant === "muted" && "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
823
+ !state.disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
824
+ state.disabled && variant === "primary" && "bg-bg-disabled text-text-disabled",
825
+ state.disabled && variant === "muted" && "text-text-disabled",
826
+ state.disabled && variant === "outlined" && "border border-border-default text-text-disabled",
827
+ state.disabled && "pointer-events-none cursor-not-allowed",
804
828
  className
805
829
  ),
806
- disabled,
807
830
  ...props,
808
831
  children
809
832
  }
810
833
  );
811
- if (tooltip) {
812
- return /* @__PURE__ */ jsx13(WithTooltip, { label: tooltip, shortcut: tooltipShortcut, placement: tooltipPlacement, children: button });
834
+ const label = disabledReason ?? tooltip;
835
+ if (label) {
836
+ return /* @__PURE__ */ jsx14(WithTooltip, { label, shortcut: disabledReason ? void 0 : tooltipShortcut, placement: tooltipPlacement, children: button });
813
837
  }
814
838
  return button;
815
839
  }
@@ -818,9 +842,9 @@ function IconButton({
818
842
  import { useRef as useRef4, useState as useState5 } from "react";
819
843
 
820
844
  // src/Divider.tsx
821
- import { jsx as jsx14 } from "react/jsx-runtime";
845
+ import { jsx as jsx15 } from "react/jsx-runtime";
822
846
  function Divider({ orientation = "horizontal", className }) {
823
- return /* @__PURE__ */ jsx14(
847
+ return /* @__PURE__ */ jsx15(
824
848
  "div",
825
849
  {
826
850
  role: "separator",
@@ -831,61 +855,59 @@ function Divider({ orientation = "horizontal", className }) {
831
855
  }
832
856
 
833
857
  // src/Person.tsx
834
- import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
858
+ import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
835
859
  function Person({ name, picture, fallback = "\u2014", size = 20, className }) {
836
860
  return (
837
861
  // gap-2: the face-to-name distance is one rule (8px) wherever a person
838
862
  // appears — Katerina, 2026-09-01, set on Person so PersonTrigger and every
839
863
  // row agree by construction.
840
864
  /* @__PURE__ */ jsxs8("span", { className: cn("flex min-w-0 items-center gap-2", className), children: [
841
- /* @__PURE__ */ jsx15(Avatar, { name, src: picture, size }),
842
- /* @__PURE__ */ jsx15("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
865
+ /* @__PURE__ */ jsx16(Avatar, { name, src: picture, size }),
866
+ /* @__PURE__ */ jsx16("span", { className: cn("truncate", !name && "text-text-muted"), children: name ?? fallback })
843
867
  ] })
844
868
  );
845
869
  }
846
870
 
847
871
  // src/PersonTrigger.tsx
872
+ import { Button as BaseButton3 } from "@base-ui/react/button";
848
873
  import { IconChevronDown } from "@tabler/icons-react";
849
- import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
874
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
850
875
  function PersonTrigger({ name, picture, fallback, size, open = false, compact = false, className, ...props }) {
851
876
  if (compact) {
852
- return /* @__PURE__ */ jsx16(
853
- "button",
877
+ return /* @__PURE__ */ jsx17(
878
+ BaseButton3,
854
879
  {
855
880
  type: "button",
856
881
  "aria-haspopup": "menu",
857
882
  "aria-expanded": open,
858
883
  className: cn("cursor-pointer rounded-full focus:outline-none", className),
859
884
  ...props,
860
- children: /* @__PURE__ */ jsx16(Avatar, { name, src: picture, size: size ?? 36 })
885
+ children: /* @__PURE__ */ jsx17(Avatar, { name, src: picture, size: size ?? 36 })
861
886
  }
862
887
  );
863
888
  }
864
889
  return /* @__PURE__ */ jsxs9(
865
- "button",
890
+ BaseButton3,
866
891
  {
867
892
  type: "button",
868
893
  "aria-haspopup": "menu",
869
894
  "aria-expanded": open,
870
895
  className: cn(
871
- // The size is an arbitrary value (the body-2 token): this list goes through
872
- // cn(), and tw-merge silently drops a custom text-{size} once a
873
- // text-{colour} follows it. Measured: the name rendered 16px.
874
- "flex h-8 cursor-pointer items-center gap-1.5 rounded-md pl-1.5 pr-1.5 text-[14px] leading-[140%] text-text-primary transition-colors hover:bg-bg-hover",
896
+ "flex h-8 cursor-pointer items-center gap-1.5 rounded-md pl-1.5 pr-1.5 text-body-2 text-text-primary transition-colors hover:bg-bg-hover",
875
897
  open && "bg-bg-hover",
876
898
  className
877
899
  ),
878
900
  ...props,
879
901
  children: [
880
- /* @__PURE__ */ jsx16(Person, { name, picture, fallback, size: size ?? 22 }),
881
- /* @__PURE__ */ jsx16(IconChevronDown, { size: 14, stroke: 1.5, className: "shrink-0 text-text-muted" })
902
+ /* @__PURE__ */ jsx17(Person, { name, picture, fallback, size: size ?? 22 }),
903
+ /* @__PURE__ */ jsx17(IconChevronDown, { size: 14, stroke: 1.5, className: "shrink-0 text-text-muted" })
882
904
  ]
883
905
  }
884
906
  );
885
907
  }
886
908
 
887
909
  // src/IdentityMenu.tsx
888
- import { Fragment, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
910
+ import { Fragment, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
889
911
  function IdentityPanel({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, onClose, anchor, className, children }) {
890
912
  const act = (action) => () => {
891
913
  onClose();
@@ -894,41 +916,41 @@ function IdentityPanel({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, o
894
916
  const appRows = typeof children === "function" ? children(onClose) : children;
895
917
  return /* @__PURE__ */ jsxs10(Menu, { onClose, anchor, align: "right", className: cn("w-72", className), children: [
896
918
  /* @__PURE__ */ jsxs10(MenuSection, { label: signedIn ? "Signed in as" : "Acting as", children: [
897
- /* @__PURE__ */ jsx17(MenuRow, { children: /* @__PURE__ */ jsx17(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
898
- /* @__PURE__ */ jsx17(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." }),
919
+ /* @__PURE__ */ jsx18(MenuRow, { children: /* @__PURE__ */ jsx18(Person, { name: me.name, picture: me.picture, fallback: "Anonymous", size: 28, className: "text-body-2-strong" }) }),
920
+ /* @__PURE__ */ jsx18(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." }),
899
921
  me.email && /* @__PURE__ */ jsxs10(Note, { children: [
900
922
  me.email,
901
923
  " \xB7 known to Estiva, never published to the relay"
902
924
  ] })
903
925
  ] }),
904
926
  relayUrl && /* @__PURE__ */ jsxs10(Fragment, { children: [
905
- /* @__PURE__ */ jsx17(Divider, { className: "mx-0 my-2" }),
927
+ /* @__PURE__ */ jsx18(Divider, { className: "mx-0 my-2" }),
906
928
  /* @__PURE__ */ jsxs10(MenuSection, { label: "Workspace", children: [
907
- /* @__PURE__ */ jsx17(MenuRow, { children: /* @__PURE__ */ jsx17("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
908
- /* @__PURE__ */ jsx17(Note, { children: "Everyone on this relay shares this workspace, in every app." })
929
+ /* @__PURE__ */ jsx18(MenuRow, { children: /* @__PURE__ */ jsx18("span", { className: "break-all font-mono text-caption text-text-primary", children: relayUrl }) }),
930
+ /* @__PURE__ */ jsx18(Note, { children: "Everyone on this relay shares this workspace, in every app." })
909
931
  ] })
910
932
  ] }),
911
933
  appRows && /* @__PURE__ */ jsxs10(Fragment, { children: [
912
- /* @__PURE__ */ jsx17(Divider, { className: "mx-0 my-2" }),
934
+ /* @__PURE__ */ jsx18(Divider, { className: "mx-0 my-2" }),
913
935
  appRows
914
936
  ] }),
915
- signedIn && idBase || onCopyKey || idBase && onSignOut ? /* @__PURE__ */ jsx17(Divider, { className: "mx-0 my-2" }) : null,
916
- signedIn && idBase && /* @__PURE__ */ jsx17(
937
+ signedIn && idBase || onCopyKey || idBase && onSignOut ? /* @__PURE__ */ jsx18(Divider, { className: "mx-0 my-2" }) : null,
938
+ signedIn && idBase && /* @__PURE__ */ jsx18(
917
939
  MenuItem,
918
940
  {
919
941
  label: "Edit your profile in Estiva ID",
920
942
  onClick: act(() => window.open(idBase, "_blank", "noopener,noreferrer"))
921
943
  }
922
944
  ),
923
- onCopyKey && /* @__PURE__ */ jsx17(MenuItem, { label: "Copy public key", onClick: act(onCopyKey) }),
924
- idBase && onSignOut && /* @__PURE__ */ jsx17(MenuItem, { label: "Sign out", onClick: act(onSignOut) })
945
+ onCopyKey && /* @__PURE__ */ jsx18(MenuItem, { label: "Copy public key", onClick: act(onCopyKey) }),
946
+ idBase && onSignOut && /* @__PURE__ */ jsx18(MenuItem, { label: "Sign out", onClick: act(onSignOut) })
925
947
  ] });
926
948
  }
927
949
  function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, compact = false, className, children }) {
928
950
  const [open, setOpen] = useState5(false);
929
951
  const anchorRef = useRef4(null);
930
952
  return /* @__PURE__ */ jsxs10("div", { ref: anchorRef, className: cn("relative", className), children: [
931
- /* @__PURE__ */ jsx17(
953
+ /* @__PURE__ */ jsx18(
932
954
  PersonTrigger,
933
955
  {
934
956
  name: me.name,
@@ -942,7 +964,7 @@ function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, co
942
964
  "aria-label": compact ? "Account menu" : void 0
943
965
  }
944
966
  ),
945
- open && /* @__PURE__ */ jsx17(
967
+ open && /* @__PURE__ */ jsx18(
946
968
  IdentityPanel,
947
969
  {
948
970
  me,
@@ -959,12 +981,12 @@ function IdentityMenu({ me, signedIn, relayUrl, idBase, onCopyKey, onSignOut, co
959
981
  ] });
960
982
  }
961
983
  function Note({ children }) {
962
- return /* @__PURE__ */ jsx17("span", { className: "px-2 pb-1.5 text-caption text-text-secondary", children });
984
+ return /* @__PURE__ */ jsx18("span", { className: "px-2 pb-1.5 text-caption text-text-secondary", children });
963
985
  }
964
986
 
965
987
  // src/Field.tsx
966
988
  import { createContext as createContext2, useContext as useContext2, useId } from "react";
967
- import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
989
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
968
990
  var FieldControlIdContext = createContext2(void 0);
969
991
  function useFieldControlId(ownId) {
970
992
  const fromField = useContext2(FieldControlIdContext);
@@ -981,11 +1003,11 @@ function Field({ label, required = false, htmlFor, children }) {
981
1003
  className: `text-input-label text-text-primary${required ? " flex items-center" : ""}`,
982
1004
  children: [
983
1005
  label,
984
- required && /* @__PURE__ */ jsx18("span", { className: "text-error-default ml-0.5", children: "*" })
1006
+ required && /* @__PURE__ */ jsx19("span", { className: "text-error-default ml-0.5", children: "*" })
985
1007
  ]
986
1008
  }
987
1009
  ),
988
- /* @__PURE__ */ jsx18(FieldControlIdContext.Provider, { value: id, children })
1010
+ /* @__PURE__ */ jsx19(FieldControlIdContext.Provider, { value: id, children })
989
1011
  ] });
990
1012
  }
991
1013
 
@@ -993,7 +1015,7 @@ function Field({ label, required = false, htmlFor, children }) {
993
1015
  import { IconCheck as IconCheck2, IconChevronDown as IconChevronDown2 } from "@tabler/icons-react";
994
1016
  import { useCallback as useCallback3, useEffect as useEffect3, useId as useId2, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState6 } from "react";
995
1017
  import { createPortal as createPortal4 } from "react-dom";
996
- import { Fragment as Fragment2, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
1018
+ import { Fragment as Fragment2, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
997
1019
  function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className }) {
998
1020
  const id = useId2();
999
1021
  const triggerRef = useRef5(null);
@@ -1133,15 +1155,15 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
1133
1155
  ),
1134
1156
  children: [
1135
1157
  /* @__PURE__ */ jsxs12("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
1136
- selected?.leading && /* @__PURE__ */ jsx19("span", { className: "flex shrink-0 items-center", children: selected.leading }),
1137
- /* @__PURE__ */ jsx19("span", { className: "truncate", children: selected?.label ?? placeholder })
1158
+ selected?.leading && /* @__PURE__ */ jsx20("span", { className: "flex shrink-0 items-center", children: selected.leading }),
1159
+ /* @__PURE__ */ jsx20("span", { className: "truncate", children: selected?.label ?? placeholder })
1138
1160
  ] }),
1139
- /* @__PURE__ */ jsx19(IconChevronDown2, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
1161
+ /* @__PURE__ */ jsx20(IconChevronDown2, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
1140
1162
  ]
1141
1163
  }
1142
1164
  ),
1143
1165
  open && rect && createPortal4(
1144
- /* @__PURE__ */ jsx19(
1166
+ /* @__PURE__ */ jsx20(
1145
1167
  "div",
1146
1168
  {
1147
1169
  ref: menuRef,
@@ -1174,10 +1196,10 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
1174
1196
  ),
1175
1197
  children: [
1176
1198
  /* @__PURE__ */ jsxs12("span", { className: "flex min-w-0 items-center gap-2", children: [
1177
- option.leading && /* @__PURE__ */ jsx19("span", { className: "flex shrink-0 items-center", children: option.leading }),
1178
- /* @__PURE__ */ jsx19("span", { className: "truncate", children: option.label })
1199
+ option.leading && /* @__PURE__ */ jsx20("span", { className: "flex shrink-0 items-center", children: option.leading }),
1200
+ /* @__PURE__ */ jsx20("span", { className: "truncate", children: option.label })
1179
1201
  ] }),
1180
- option.value === value && /* @__PURE__ */ jsx19(IconCheck2, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
1202
+ option.value === value && /* @__PURE__ */ jsx20(IconCheck2, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
1181
1203
  ]
1182
1204
  },
1183
1205
  option.value
@@ -1191,10 +1213,10 @@ function Select({ value, onChange, options, size = "default", ariaLabel, placeho
1191
1213
 
1192
1214
  // src/TextInput.tsx
1193
1215
  import { forwardRef } from "react";
1194
- import { jsx as jsx20 } from "react/jsx-runtime";
1216
+ import { jsx as jsx21 } from "react/jsx-runtime";
1195
1217
  var TextInput = forwardRef(function TextInput2({ className, type = "text", id, ...props }, ref) {
1196
1218
  const controlId = useFieldControlId(id);
1197
- return /* @__PURE__ */ jsx20(
1219
+ return /* @__PURE__ */ jsx21(
1198
1220
  "input",
1199
1221
  {
1200
1222
  ref,
@@ -1215,10 +1237,10 @@ var TextInput = forwardRef(function TextInput2({ className, type = "text", id, .
1215
1237
 
1216
1238
  // src/Textarea.tsx
1217
1239
  import { forwardRef as forwardRef2 } from "react";
1218
- import { jsx as jsx21 } from "react/jsx-runtime";
1240
+ import { jsx as jsx22 } from "react/jsx-runtime";
1219
1241
  var Textarea = forwardRef2(function Textarea2({ className, id, ...props }, ref) {
1220
1242
  const controlId = useFieldControlId(id);
1221
- return /* @__PURE__ */ jsx21(
1243
+ return /* @__PURE__ */ jsx22(
1222
1244
  "textarea",
1223
1245
  {
1224
1246
  ref,
@@ -1243,7 +1265,7 @@ import { useState as useState7 } from "react";
1243
1265
  import { useEffect as useEffect4 } from "react";
1244
1266
  import { createPortal as createPortal5 } from "react-dom";
1245
1267
  import { IconX as IconX2 } from "@tabler/icons-react";
1246
- import { Fragment as Fragment3, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1268
+ import { Fragment as Fragment3, jsx as jsx23, jsxs as jsxs13 } from "react/jsx-runtime";
1247
1269
  function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, width = 502 }) {
1248
1270
  useEffect4(() => {
1249
1271
  const onKey = (event) => {
@@ -1254,8 +1276,8 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
1254
1276
  }, [onClose]);
1255
1277
  return createPortal5(
1256
1278
  /* @__PURE__ */ jsxs13(Fragment3, { children: [
1257
- /* @__PURE__ */ jsx22("div", { className: "fixed inset-0 z-40 bg-black/50", onClick: onClose }),
1258
- /* @__PURE__ */ jsx22("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs13(
1279
+ /* @__PURE__ */ jsx23("div", { className: "fixed inset-0 z-40 bg-scrim", onClick: onClose }),
1280
+ /* @__PURE__ */ jsx23("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs13(
1259
1281
  "div",
1260
1282
  {
1261
1283
  role: "dialog",
@@ -1265,11 +1287,11 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
1265
1287
  style: { width },
1266
1288
  children: [
1267
1289
  /* @__PURE__ */ jsxs13("div", { className: "h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0", children: [
1268
- headerContent ?? /* @__PURE__ */ jsx22("span", { className: "text-h4 text-text-primary", children: title }),
1269
- /* @__PURE__ */ jsx22(IconButton, { tooltip: "Close", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx22(IconX2, { size: 16, stroke: 1.5 }) })
1290
+ headerContent ?? /* @__PURE__ */ jsx23("span", { className: "text-h4 text-text-primary", children: title }),
1291
+ /* @__PURE__ */ jsx23(IconButton, { tooltip: "Close", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx23(IconX2, { size: 16, stroke: 1.5 }) })
1270
1292
  ] }),
1271
- /* @__PURE__ */ jsx22("div", { className: cn("pl-5 pr-4 py-4", footer != null && "border-b border-border-subtle", bodyClassName), children }),
1272
- footer != null && /* @__PURE__ */ jsx22("div", { className: "h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0", children: footer })
1293
+ /* @__PURE__ */ jsx23("div", { className: cn("pl-5 pr-4 py-4", footer != null && "border-b border-border-subtle", bodyClassName), children }),
1294
+ footer != null && /* @__PURE__ */ jsx23("div", { className: "h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0", children: footer })
1273
1295
  ]
1274
1296
  }
1275
1297
  ) })
@@ -1279,7 +1301,7 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
1279
1301
  }
1280
1302
 
1281
1303
  // src/ConfirmDialog.tsx
1282
- import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
1304
+ import { Fragment as Fragment4, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
1283
1305
  function ConfirmDialog({ title, children, confirmLabel, destructive = false, onConfirm, onClose }) {
1284
1306
  const [busy, setBusy] = useState7(false);
1285
1307
  const confirm = async () => {
@@ -1291,15 +1313,15 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
1291
1313
  setBusy(false);
1292
1314
  }
1293
1315
  };
1294
- return /* @__PURE__ */ jsx23(
1316
+ return /* @__PURE__ */ jsx24(
1295
1317
  DialogShell,
1296
1318
  {
1297
1319
  title,
1298
1320
  onClose,
1299
1321
  bodyClassName: "flex flex-col gap-3 text-body-2 text-text-primary",
1300
1322
  footer: /* @__PURE__ */ jsxs14(Fragment4, { children: [
1301
- /* @__PURE__ */ jsx23(Button, { variant: "muted", onClick: onClose, disabled: busy, children: "Cancel" }),
1302
- /* @__PURE__ */ jsx23(Button, { variant: destructive ? "destructive" : "primary", onClick: () => void confirm(), disabled: busy, children: confirmLabel })
1323
+ /* @__PURE__ */ jsx24(Button, { variant: "muted", onClick: onClose, disabled: busy, children: "Cancel" }),
1324
+ /* @__PURE__ */ jsx24(Button, { variant: destructive ? "destructive" : "primary", onClick: () => void confirm(), disabled: busy, children: confirmLabel })
1303
1325
  ] }),
1304
1326
  children
1305
1327
  }
@@ -1308,7 +1330,7 @@ function ConfirmDialog({ title, children, confirmLabel, destructive = false, onC
1308
1330
 
1309
1331
  // src/EditableText.tsx
1310
1332
  import { useEffect as useEffect5, useRef as useRef6, useState as useState8 } from "react";
1311
- import { jsx as jsx24 } from "react/jsx-runtime";
1333
+ import { jsx as jsx25 } from "react/jsx-runtime";
1312
1334
  function EditableText({ value, display, displayNode, placeholder, onCommit, multiline = false, className, label, readOnly = false }) {
1313
1335
  const [editing, setEditing] = useState8(false);
1314
1336
  const [draft, setDraft] = useState8(value);
@@ -1359,7 +1381,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
1359
1381
  className
1360
1382
  );
1361
1383
  if (readOnly) {
1362
- return /* @__PURE__ */ jsx24(
1384
+ return /* @__PURE__ */ jsx25(
1363
1385
  "div",
1364
1386
  {
1365
1387
  "aria-label": label,
@@ -1369,7 +1391,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
1369
1391
  );
1370
1392
  }
1371
1393
  if (editing) {
1372
- return multiline ? /* @__PURE__ */ jsx24(
1394
+ return multiline ? /* @__PURE__ */ jsx25(
1373
1395
  "textarea",
1374
1396
  {
1375
1397
  ref: fieldRef,
@@ -1382,7 +1404,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
1382
1404
  onBlur: () => void commit(),
1383
1405
  className: fieldClass
1384
1406
  }
1385
- ) : /* @__PURE__ */ jsx24(
1407
+ ) : /* @__PURE__ */ jsx25(
1386
1408
  "input",
1387
1409
  {
1388
1410
  ref: fieldRef,
@@ -1396,7 +1418,7 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
1396
1418
  }
1397
1419
  );
1398
1420
  }
1399
- return /* @__PURE__ */ jsx24(
1421
+ return /* @__PURE__ */ jsx25(
1400
1422
  "button",
1401
1423
  {
1402
1424
  type: "button",
@@ -1416,33 +1438,33 @@ function EditableText({ value, display, displayNode, placeholder, onCommit, mult
1416
1438
 
1417
1439
  // src/EmptyState.tsx
1418
1440
  import { IconMessage2 } from "@tabler/icons-react";
1419
- import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
1441
+ import { jsx as jsx26, jsxs as jsxs15 } from "react/jsx-runtime";
1420
1442
  function EmptyState({ icon, message, className }) {
1421
1443
  return /* @__PURE__ */ jsxs15("div", { className: cn("flex flex-col gap-2 items-center", className), children: [
1422
- /* @__PURE__ */ jsx25("span", { className: "text-text-secondary", children: icon ?? /* @__PURE__ */ jsx25(IconMessage2, { size: 16, stroke: 1.5 }) }),
1423
- /* @__PURE__ */ jsx25("p", { className: "text-body-2 text-text-secondary text-center", children: message })
1444
+ /* @__PURE__ */ jsx26("span", { className: "text-text-secondary", children: icon ?? /* @__PURE__ */ jsx26(IconMessage2, { size: 16, stroke: 1.5 }) }),
1445
+ /* @__PURE__ */ jsx26("p", { className: "text-body-2 text-text-secondary text-center", children: message })
1424
1446
  ] });
1425
1447
  }
1426
1448
 
1427
1449
  // src/Skeleton.tsx
1428
- import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
1450
+ import { jsx as jsx27, jsxs as jsxs16 } from "react/jsx-runtime";
1429
1451
  function SkeletonBar({ className, style }) {
1430
- return /* @__PURE__ */ jsx26("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
1452
+ return /* @__PURE__ */ jsx27("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
1431
1453
  }
1432
1454
  var ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160];
1433
1455
  function SkeletonRow({ barWidth = 130 }) {
1434
1456
  return /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
1435
- /* @__PURE__ */ jsx26(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
1436
- /* @__PURE__ */ jsx26(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
1457
+ /* @__PURE__ */ jsx27(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
1458
+ /* @__PURE__ */ jsx27(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
1437
1459
  ] });
1438
1460
  }
1439
1461
  function SkeletonList({ rows = 8, className }) {
1440
- return /* @__PURE__ */ jsx26("div", { className: cn("flex flex-col gap-0.5 animate-skeleton-in", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx26(SkeletonRow, { barWidth: ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length] }, i)) });
1462
+ return /* @__PURE__ */ jsx27("div", { className: cn("flex flex-col gap-0.5 animate-skeleton-in", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx27(SkeletonRow, { barWidth: ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length] }, i)) });
1441
1463
  }
1442
1464
 
1443
1465
  // src/Breadcrumb.tsx
1444
1466
  import { Fragment as Fragment5, useCallback as useCallback4, useLayoutEffect as useLayoutEffect5, useRef as useRef7, useState as useState9 } from "react";
1445
- import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
1467
+ import { jsx as jsx28, jsxs as jsxs17 } from "react/jsx-runtime";
1446
1468
  function Breadcrumb({ items, className }) {
1447
1469
  const navRef = useRef7(null);
1448
1470
  const labelRefs = useRef7([]);
@@ -1462,7 +1484,7 @@ function Breadcrumb({ items, className }) {
1462
1484
  observer.observe(nav);
1463
1485
  return () => observer.disconnect();
1464
1486
  }, [measure, items]);
1465
- return /* @__PURE__ */ jsx27("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) => {
1487
+ return /* @__PURE__ */ jsx28("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) => {
1466
1488
  const last = index === items.length - 1;
1467
1489
  const text = cn(
1468
1490
  "truncate",
@@ -1475,21 +1497,21 @@ function Breadcrumb({ items, className }) {
1475
1497
  const setLabelRef = (el) => {
1476
1498
  labelRefs.current[index] = el;
1477
1499
  };
1478
- const crumb = item.href ? /* @__PURE__ */ jsx27("a", { ref: setLabelRef, href: item.href, className: cn(text, "hover:text-text-primary"), children: item.label }) : /* @__PURE__ */ jsx27("span", { ref: setLabelRef, className: text, "aria-current": last ? "page" : void 0, children: item.label });
1500
+ const crumb = item.href ? /* @__PURE__ */ jsx28("a", { ref: setLabelRef, href: item.href, className: cn(text, "hover:text-text-primary"), children: item.label }) : /* @__PURE__ */ jsx28("span", { ref: setLabelRef, className: text, "aria-current": last ? "page" : void 0, children: item.label });
1479
1501
  return /* @__PURE__ */ jsxs17(Fragment5, { children: [
1480
- index > 0 && /* @__PURE__ */ jsx27("span", { "aria-hidden": "true", className: "shrink-0 text-text-muted", children: "/" }),
1502
+ index > 0 && /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", className: "shrink-0 text-text-muted", children: "/" }),
1481
1503
  truncated.has(index) ? (
1482
1504
  // `min-w-0 shrink` undoes the wrapper's own shrink-0 — the crumb
1483
1505
  // must keep truncating inside it, or wrapping it would widen the
1484
1506
  // trail and the tooltip would never be needed again.
1485
- /* @__PURE__ */ jsx27(WithTooltip, { label: item.label, wrapperClassName: "min-w-0 shrink", children: crumb })
1507
+ /* @__PURE__ */ jsx28(WithTooltip, { label: item.label, wrapperClassName: "min-w-0 shrink", children: crumb })
1486
1508
  ) : crumb
1487
1509
  ] }, `${item.label}-${index}`);
1488
1510
  }) });
1489
1511
  }
1490
1512
 
1491
1513
  // src/NavItem.tsx
1492
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1514
+ import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
1493
1515
  function NavItem({ label, href, count, countLabel, active = false, icon, className, ...props }) {
1494
1516
  return /* @__PURE__ */ jsxs18(
1495
1517
  "a",
@@ -1506,22 +1528,22 @@ function NavItem({ label, href, count, countLabel, active = false, icon, classNa
1506
1528
  ),
1507
1529
  ...props,
1508
1530
  children: [
1509
- icon && /* @__PURE__ */ jsx28("span", { className: "flex shrink-0 items-center", children: icon }),
1510
- /* @__PURE__ */ jsx28("span", { className: "flex-1 truncate", children: label }),
1511
- count ? /* @__PURE__ */ jsx28(WithTooltip, { label: countLabel ?? `${count} open`, children: /* @__PURE__ */ jsx28("span", { className: "shrink-0 font-mono text-caption tabular-nums text-text-muted", children: count }) }) : null
1531
+ icon && /* @__PURE__ */ jsx29("span", { className: "flex shrink-0 items-center", children: icon }),
1532
+ /* @__PURE__ */ jsx29("span", { className: "flex-1 truncate", children: label }),
1533
+ count ? /* @__PURE__ */ jsx29(WithTooltip, { label: countLabel ?? `${count} open`, children: /* @__PURE__ */ jsx29("span", { className: "shrink-0 font-mono text-caption tabular-nums text-text-muted", children: count }) }) : null
1512
1534
  ]
1513
1535
  }
1514
1536
  );
1515
1537
  }
1516
1538
 
1517
1539
  // src/Rail.tsx
1518
- import { jsx as jsx29 } from "react/jsx-runtime";
1540
+ import { jsx as jsx30 } from "react/jsx-runtime";
1519
1541
  function Rail({ "aria-label": ariaLabel = "Navigation", children, className }) {
1520
- return /* @__PURE__ */ jsx29("nav", { "aria-label": ariaLabel, className: cn("flex w-16 shrink-0 flex-col items-start gap-2 px-2 py-3", className), children });
1542
+ return /* @__PURE__ */ jsx30("nav", { "aria-label": ariaLabel, className: cn("flex w-16 shrink-0 flex-col items-start gap-2 px-2 py-3", className), children });
1521
1543
  }
1522
1544
 
1523
1545
  // src/RailItem.tsx
1524
- import { jsx as jsx30, jsxs as jsxs19 } from "react/jsx-runtime";
1546
+ import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
1525
1547
  function RailItem({ href, icon, label, active = false, className, ...props }) {
1526
1548
  return /* @__PURE__ */ jsxs19(
1527
1549
  "a",
@@ -1531,14 +1553,14 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
1531
1553
  className: cn("group flex w-full shrink-0 flex-col items-center gap-0.5 px-2 py-0.5", className),
1532
1554
  ...props,
1533
1555
  children: [
1534
- /* @__PURE__ */ jsx30(
1556
+ /* @__PURE__ */ jsx31(
1535
1557
  "div",
1536
1558
  {
1537
1559
  className: cn(
1538
1560
  "flex items-center justify-center rounded-lg p-2 transition-colors",
1539
1561
  active ? "bg-bg-selected" : "group-hover:bg-bg-hover"
1540
1562
  ),
1541
- children: /* @__PURE__ */ jsx30(
1563
+ children: /* @__PURE__ */ jsx31(
1542
1564
  "span",
1543
1565
  {
1544
1566
  className: cn(
@@ -1550,16 +1572,16 @@ function RailItem({ href, icon, label, active = false, className, ...props }) {
1550
1572
  )
1551
1573
  }
1552
1574
  ),
1553
- /* @__PURE__ */ jsx30("span", { className: cn("text-center text-[9px] font-medium leading-[115%]", active ? "text-text-primary" : "text-text-secondary"), children: label })
1575
+ /* @__PURE__ */ jsx31("span", { className: cn("text-center text-[9px] font-medium leading-[115%]", active ? "text-text-primary" : "text-text-secondary"), children: label })
1554
1576
  ]
1555
1577
  }
1556
1578
  );
1557
1579
  }
1558
1580
 
1559
1581
  // src/Sidebar.tsx
1560
- import { jsx as jsx31 } from "react/jsx-runtime";
1582
+ import { jsx as jsx32 } from "react/jsx-runtime";
1561
1583
  function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className }) {
1562
- return /* @__PURE__ */ jsx31(
1584
+ return /* @__PURE__ */ jsx32(
1563
1585
  "nav",
1564
1586
  {
1565
1587
  "aria-label": ariaLabel,
@@ -1573,7 +1595,7 @@ function Sidebar({ "aria-label": ariaLabel = "Workspace", children, className })
1573
1595
  }
1574
1596
 
1575
1597
  // src/Property.tsx
1576
- import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
1598
+ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1577
1599
  function Property({ label, layout = "row", children, className }) {
1578
1600
  if (layout === "stacked") {
1579
1601
  return (
@@ -1581,22 +1603,48 @@ function Property({ label, layout = "row", children, className }) {
1581
1603
  // Peek's topic-details sections already sit at 12px, Ship's rail was
1582
1604
  // 6px, and unifying means the bigger, calmer one.
1583
1605
  /* @__PURE__ */ jsxs20("div", { className: cn("flex flex-col gap-3", className), children: [
1584
- /* @__PURE__ */ jsx32("span", { className: "text-menu uppercase tracking-[0.08em] text-text-secondary", children: label }),
1606
+ /* @__PURE__ */ jsx33("span", { className: "text-menu uppercase tracking-[0.08em] text-text-secondary", children: label }),
1585
1607
  children
1586
1608
  ] })
1587
1609
  );
1588
1610
  }
1589
1611
  return /* @__PURE__ */ jsxs20("div", { className: cn("flex items-center gap-2", className), children: [
1590
- /* @__PURE__ */ jsx32("span", { className: "w-[68px] shrink-0 text-caption text-text-secondary", children: label }),
1612
+ /* @__PURE__ */ jsx33("span", { className: "w-[68px] shrink-0 text-caption text-text-secondary", children: label }),
1591
1613
  children
1592
1614
  ] });
1593
1615
  }
1594
1616
 
1617
+ // src/Reaction.tsx
1618
+ import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1619
+ function Reaction({ emoji, count, pressed = false, className, type, ...props }) {
1620
+ return /* @__PURE__ */ jsxs21(
1621
+ "button",
1622
+ {
1623
+ type: type ?? "button",
1624
+ "aria-pressed": pressed,
1625
+ className: cn(
1626
+ // Chip's pill at a control's height, so a reaction and a status chip
1627
+ // read as the same family — 24px matches Button `small`.
1628
+ "inline-flex h-6 items-center justify-center gap-1.5 rounded-full px-2",
1629
+ "border transition-colors",
1630
+ "disabled:cursor-not-allowed disabled:opacity-50",
1631
+ pressed ? "border-accent-primary bg-accent-muted text-accent-primary hover:border-accent-hover" : "border-border-default bg-bg-inset text-text-primary hover:border-border-strong hover:bg-bg-hover",
1632
+ className
1633
+ ),
1634
+ ...props,
1635
+ children: [
1636
+ /* @__PURE__ */ jsx34("span", { "aria-hidden": "true", className: "shrink-0 text-[16px] leading-none", children: emoji }),
1637
+ /* @__PURE__ */ jsx34("span", { className: "text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tabular-nums", children: count })
1638
+ ]
1639
+ }
1640
+ );
1641
+ }
1642
+
1595
1643
  // src/SearchInput.tsx
1596
1644
  import "react";
1597
- import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
1645
+ import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
1598
1646
  function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...props }) {
1599
- return /* @__PURE__ */ jsxs21(
1647
+ return /* @__PURE__ */ jsxs22(
1600
1648
  "div",
1601
1649
  {
1602
1650
  className: cn(
@@ -1606,7 +1654,7 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
1606
1654
  className
1607
1655
  ),
1608
1656
  children: [
1609
- /* @__PURE__ */ jsx33(
1657
+ /* @__PURE__ */ jsx35(
1610
1658
  "input",
1611
1659
  {
1612
1660
  className: "flex-1 min-w-0 bg-transparent text-input-value text-text-primary placeholder:text-text-muted outline-none",
@@ -1614,7 +1662,7 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
1614
1662
  ...props
1615
1663
  }
1616
1664
  ),
1617
- shortcut && /* @__PURE__ */ jsx33(Kbd, { children: shortcut })
1665
+ shortcut && /* @__PURE__ */ jsx35(Kbd, { children: shortcut })
1618
1666
  ]
1619
1667
  }
1620
1668
  );
@@ -1623,10 +1671,10 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
1623
1671
  // src/SectionHeader.tsx
1624
1672
  import { useState as useState10 } from "react";
1625
1673
  import { IconChevronRight as IconChevronRight2 } from "@tabler/icons-react";
1626
- import { jsx as jsx34, jsxs as jsxs22 } from "react/jsx-runtime";
1674
+ import { jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
1627
1675
  function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, actions, showActions = "hover", className }) {
1628
1676
  const [isHovered, setIsHovered] = useState10(false);
1629
- return /* @__PURE__ */ jsxs22(
1677
+ return /* @__PURE__ */ jsxs23(
1630
1678
  "div",
1631
1679
  {
1632
1680
  className: cn(
@@ -1639,8 +1687,8 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
1639
1687
  onMouseEnter: () => setIsHovered(true),
1640
1688
  onMouseLeave: () => setIsHovered(false),
1641
1689
  children: [
1642
- /* @__PURE__ */ jsxs22("div", { className: "flex shrink-0 items-center gap-1", children: [
1643
- chevron && /* @__PURE__ */ jsx34(
1690
+ /* @__PURE__ */ jsxs23("div", { className: "flex shrink-0 items-center gap-1", children: [
1691
+ chevron && /* @__PURE__ */ jsx36(
1644
1692
  IconChevronRight2,
1645
1693
  {
1646
1694
  size: 12,
@@ -1648,9 +1696,9 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
1648
1696
  className: cn("text-text-secondary transition-transform duration-150", isExpanded && "rotate-90")
1649
1697
  }
1650
1698
  ),
1651
- /* @__PURE__ */ jsx34(SectionLabel, { children: title })
1699
+ /* @__PURE__ */ jsx36(SectionLabel, { children: title })
1652
1700
  ] }),
1653
- (showActions === "always" || isHovered) && actions && actions.length > 0 && /* @__PURE__ */ jsx34("div", { className: "flex items-center gap-1", children: actions.map((action) => /* @__PURE__ */ jsx34(
1701
+ (showActions === "always" || isHovered) && actions && actions.length > 0 && /* @__PURE__ */ jsx36("div", { className: "flex items-center gap-1", children: actions.map((action) => /* @__PURE__ */ jsx36(
1654
1702
  IconButton,
1655
1703
  {
1656
1704
  tooltip: action.tooltip,
@@ -1669,60 +1717,65 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
1669
1717
  }
1670
1718
 
1671
1719
  // src/Tabs.tsx
1672
- import { jsx as jsx35, jsxs as jsxs23 } from "react/jsx-runtime";
1720
+ import { Tabs as BaseTabs } from "@base-ui/react/tabs";
1721
+ import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
1673
1722
  function Tabs({ tabs, active, onChange, size = "default", className }) {
1674
- return /* @__PURE__ */ jsx35("div", { role: "tablist", className: cn("flex items-center gap-2", className), children: tabs.map((tab) => /* @__PURE__ */ jsxs23(
1675
- "button",
1723
+ return /* @__PURE__ */ jsx37(
1724
+ BaseTabs.Root,
1676
1725
  {
1677
- type: "button",
1678
- role: "tab",
1679
- "aria-selected": active === tab.id,
1680
- onClick: () => onChange(tab.id),
1681
- className: cn(
1682
- "flex cursor-pointer items-center transition-colors",
1683
- // Arbitrary sizes (the body-2 and caption tokens): the colour branch below
1684
- // follows them through cn(), and tw-merge drops a custom text-{size}
1685
- // once a text-{colour} lands after it. Measured: tabs rendered 16px.
1686
- // gap: default is Ship's 6px; small keeps Peek's original 4px, or
1687
- // "small is Peek's geometry" stops being true.
1688
- size === "default" ? "gap-1.5 rounded-md px-2 py-1 text-[14px] leading-[140%]" : "gap-1 rounded px-1.5 py-0.5 text-[12px] leading-[120%]",
1689
- active === tab.id ? "bg-bg-active text-text-primary" : "text-text-secondary hover:bg-bg-hover"
1690
- ),
1691
- children: [
1692
- tab.icon,
1693
- /* @__PURE__ */ jsxs23("span", { className: "flex items-baseline", children: [
1694
- tab.label,
1695
- tab.count !== void 0 ? /* @__PURE__ */ jsx35("span", { className: "ml-2.5 font-mono text-caption tabular-nums text-text-secondary", children: tab.count }) : null
1696
- ] })
1697
- ]
1698
- },
1699
- tab.id
1700
- )) });
1726
+ value: active,
1727
+ onValueChange: (value, details) => {
1728
+ if (details.reason === "none") onChange(value);
1729
+ },
1730
+ className,
1731
+ children: /* @__PURE__ */ jsx37(BaseTabs.List, { activateOnFocus: true, className: "flex items-center gap-2", children: tabs.map((tab) => /* @__PURE__ */ jsxs24(
1732
+ BaseTabs.Tab,
1733
+ {
1734
+ value: tab.id,
1735
+ className: (state) => cn(
1736
+ "flex cursor-pointer items-center transition-colors",
1737
+ // gap: default is Ship's 6px; small keeps Peek's original 4px, or
1738
+ // "small is Peek's geometry" stops being true.
1739
+ size === "default" ? "gap-1.5 rounded-md px-2 py-1 text-body-2" : "gap-1 rounded px-1.5 py-0.5 text-caption",
1740
+ state.active ? "bg-bg-active text-text-primary" : "text-text-secondary hover:bg-bg-hover"
1741
+ ),
1742
+ children: [
1743
+ tab.icon,
1744
+ /* @__PURE__ */ jsxs24("span", { className: "flex items-baseline", children: [
1745
+ tab.label,
1746
+ tab.count !== void 0 ? /* @__PURE__ */ jsx37("span", { className: "ml-2.5 font-mono text-caption tabular-nums text-text-secondary", children: tab.count }) : null
1747
+ ] })
1748
+ ]
1749
+ },
1750
+ tab.id
1751
+ )) })
1752
+ }
1753
+ );
1701
1754
  }
1702
1755
 
1703
1756
  // src/Toast.tsx
1704
1757
  import { createContext as createContext3, useCallback as useCallback5, useContext as useContext3, useEffect as useEffect6, useMemo as useMemo3, useState as useState11 } from "react";
1705
1758
  import { createPortal as createPortal6 } from "react-dom";
1706
1759
  import { IconCircleCheck } from "@tabler/icons-react";
1707
- import { jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
1708
- var SURFACE_BY_TYPE = {
1760
+ import { jsx as jsx38, jsxs as jsxs25 } from "react/jsx-runtime";
1761
+ var SURFACE_STYLES = {
1709
1762
  success: "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
1710
1763
  brand: "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
1711
1764
  neutral: "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]"
1712
1765
  };
1713
- var ICON_BY_TYPE = {
1714
- success: "signal:text-success-default signal:drop-shadow-[0_0_5px_rgba(63,222,140,0.7)]",
1715
- brand: "signal:text-text-interactive signal:drop-shadow-[0_0_5px_rgba(86,200,255,0.6)]",
1766
+ var ICON_STYLES = {
1767
+ success: "signal:text-success-default signal:drop-shadow-glow-success",
1768
+ brand: "signal:text-text-interactive signal:drop-shadow-glow-accent",
1716
1769
  neutral: "signal:text-text-secondary"
1717
1770
  };
1718
- var ACTION_BORDER_BY_TYPE = {
1771
+ var ACTION_BORDER_STYLES = {
1719
1772
  success: "signal:border signal:border-border-default signal:hover:border-border-strong",
1720
1773
  brand: "signal:border signal:border-border-default signal:hover:border-border-strong",
1721
1774
  neutral: "border border-border-default"
1722
1775
  };
1723
1776
  function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAction, className }) {
1724
1777
  const hasAction = !!(actionLabel && onAction);
1725
- return /* @__PURE__ */ jsxs24(
1778
+ return /* @__PURE__ */ jsxs25(
1726
1779
  "div",
1727
1780
  {
1728
1781
  className: cn(
@@ -1730,25 +1783,25 @@ function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAct
1730
1783
  // Without an action the label needs real right padding; the action
1731
1784
  // button brings its own edge, so the tight pr-1 only applies there.
1732
1785
  hasAction ? "pr-1 gap-[46px]" : "pr-3",
1733
- SURFACE_BY_TYPE[type],
1786
+ SURFACE_STYLES[type],
1734
1787
  className
1735
1788
  ),
1736
1789
  children: [
1737
- /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-2 shrink-0", children: [
1738
- leadingIcon && /* @__PURE__ */ jsx36(IconCircleCheck, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_BY_TYPE[type]) }),
1739
- /* @__PURE__ */ jsx36("span", { className: "font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap", children: label })
1790
+ /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 shrink-0", children: [
1791
+ leadingIcon && /* @__PURE__ */ jsx38(IconCircleCheck, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) }),
1792
+ /* @__PURE__ */ jsx38("span", { className: "font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap", children: label })
1740
1793
  ] }),
1741
- hasAction && /* @__PURE__ */ jsx36(
1794
+ hasAction && /* @__PURE__ */ jsx38(
1742
1795
  "button",
1743
1796
  {
1744
1797
  type: "button",
1745
1798
  onClick: onAction,
1746
1799
  className: cn(
1747
1800
  "h-6 flex items-center justify-center gap-1 px-1 py-1 rounded-md shrink-0 transition-colors",
1748
- ACTION_BORDER_BY_TYPE[type],
1801
+ ACTION_BORDER_STYLES[type],
1749
1802
  type === "neutral" ? "hover:border-border-strong" : "hover:opacity-80"
1750
1803
  ),
1751
- children: /* @__PURE__ */ jsx36("span", { className: "font-medium text-[12px] leading-[12px] text-text-primary whitespace-nowrap", children: actionLabel })
1804
+ children: /* @__PURE__ */ jsx38("span", { className: "font-medium text-[12px] leading-[12px] text-text-primary whitespace-nowrap", children: actionLabel })
1752
1805
  }
1753
1806
  )
1754
1807
  ]
@@ -1775,10 +1828,10 @@ function ToastProvider({ children }) {
1775
1828
  return () => clearTimeout(timer);
1776
1829
  }, [toast]);
1777
1830
  const value = useMemo3(() => ({ showToast, dismissToast }), [showToast, dismissToast]);
1778
- return /* @__PURE__ */ jsxs24(ToastContext.Provider, { value, children: [
1831
+ return /* @__PURE__ */ jsxs25(ToastContext.Provider, { value, children: [
1779
1832
  children,
1780
1833
  toast && createPortal6(
1781
- /* @__PURE__ */ jsx36("div", { className: "fixed bottom-4 left-4 z-[100] pointer-events-none", children: /* @__PURE__ */ jsx36(
1834
+ /* @__PURE__ */ jsx38("div", { className: "fixed bottom-4 left-4 z-[100] pointer-events-none", children: /* @__PURE__ */ jsx38(
1782
1835
  Toast,
1783
1836
  {
1784
1837
  className: "pointer-events-auto",
@@ -1835,6 +1888,7 @@ export {
1835
1888
  Property,
1836
1889
  Rail,
1837
1890
  RailItem,
1891
+ Reaction,
1838
1892
  SearchInput,
1839
1893
  SectionHeader,
1840
1894
  SectionLabel,