@estiva-app/ui 0.12.0 → 0.12.2

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 (54) hide show
  1. package/dist/AppShell.d.ts.map +1 -1
  2. package/dist/CollapsibleSection.d.ts +3 -1
  3. package/dist/CollapsibleSection.d.ts.map +1 -1
  4. package/dist/DialogShell.d.ts +14 -2
  5. package/dist/DialogShell.d.ts.map +1 -1
  6. package/dist/Field.d.ts +36 -0
  7. package/dist/Field.d.ts.map +1 -1
  8. package/dist/Popover.d.ts +11 -2
  9. package/dist/Popover.d.ts.map +1 -1
  10. package/dist/ScrollArea.d.ts +14 -2
  11. package/dist/ScrollArea.d.ts.map +1 -1
  12. package/dist/SectionHeader.d.ts +10 -1
  13. package/dist/SectionHeader.d.ts.map +1 -1
  14. package/dist/Toast.d.ts +17 -7
  15. package/dist/Toast.d.ts.map +1 -1
  16. package/dist/cn.d.ts.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +138 -99
  20. package/dist/index.js.map +4 -4
  21. package/package.json +1 -1
  22. package/src/AppShell.mdx +15 -0
  23. package/src/AppShell.stories.tsx +15 -0
  24. package/src/AppShell.test.tsx +39 -0
  25. package/src/AppShell.tsx +22 -7
  26. package/src/CollapsibleSection.tsx +4 -2
  27. package/src/DialogShell.mdx +9 -3
  28. package/src/DialogShell.stories.tsx +29 -0
  29. package/src/DialogShell.test.tsx +35 -0
  30. package/src/DialogShell.tsx +31 -4
  31. package/src/EmptyState.mdx +5 -4
  32. package/src/Field.mdx +3 -2
  33. package/src/Field.tsx +57 -2
  34. package/src/FieldLine.mdx +57 -0
  35. package/src/FieldLine.stories.tsx +80 -0
  36. package/src/FieldLine.test.tsx +56 -0
  37. package/src/Popover.mdx +3 -1
  38. package/src/Popover.stories.tsx +15 -0
  39. package/src/Popover.test.tsx +20 -0
  40. package/src/Popover.tsx +12 -3
  41. package/src/ScrollArea.mdx +5 -0
  42. package/src/ScrollArea.test.tsx +27 -1
  43. package/src/ScrollArea.tsx +16 -2
  44. package/src/SectionHeader.mdx +4 -0
  45. package/src/SectionHeader.stories.tsx +10 -1
  46. package/src/SectionHeader.test.tsx +25 -0
  47. package/src/SectionHeader.tsx +14 -1
  48. package/src/Toast.mdx +8 -2
  49. package/src/Toast.stories.tsx +13 -3
  50. package/src/Toast.tsx +44 -11
  51. package/src/cn.ts +1 -1
  52. package/src/index.ts +1 -1
  53. package/stories/Choosing.mdx +2 -0
  54. package/tailwind-preset.js +4 -0
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var FONT_SIZE_TOKENS = [
20
20
  "chip"
21
21
  ];
22
22
  var BOX_SHADOW_TOKENS = ["sm", "md", "lg", "focus-ring", "glow-warning", "glow-success", "glow-accent", "highlight-inset"];
23
- var DROP_SHADOW_TOKENS = ["glow-success", "glow-accent"];
23
+ var DROP_SHADOW_TOKENS = ["glow-success", "glow-accent", "glow-warning"];
24
24
  var twMerge = extendTailwindMerge({
25
25
  extend: {
26
26
  classGroups: {
@@ -34,11 +34,38 @@ function cn(...inputs) {
34
34
  return twMerge(clsx(inputs));
35
35
  }
36
36
 
37
- // src/TopBar.tsx
37
+ // src/ScrollArea.tsx
38
+ import { ScrollArea as BaseScrollArea } from "@base-ui/react/scroll-area";
38
39
  import { jsx, jsxs } from "react/jsx-runtime";
40
+ var BAR = "flex touch-none select-none rounded-full opacity-0 transition-opacity delay-300 data-[hovering]:opacity-100 data-[hovering]:delay-0 data-[scrolling]:opacity-100 data-[scrolling]:delay-0";
41
+ var THUMB = "rounded-full bg-border-strong";
42
+ function ScrollArea({ orientation = "vertical", className, viewportClassName, contentClassName, viewportRef, onScroll, children }) {
43
+ const vertical = orientation !== "horizontal";
44
+ const horizontal = orientation !== "vertical";
45
+ return /* @__PURE__ */ jsxs(BaseScrollArea.Root, { className: cn("relative min-h-0 min-w-0", className), children: [
46
+ /* @__PURE__ */ jsx(
47
+ BaseScrollArea.Viewport,
48
+ {
49
+ ref: viewportRef,
50
+ onScroll,
51
+ className: cn(
52
+ "h-full w-full outline-none data-[has-overflow-x]:overscroll-x-contain data-[has-overflow-y]:overscroll-y-contain",
53
+ viewportClassName
54
+ ),
55
+ children: /* @__PURE__ */ jsx(BaseScrollArea.Content, { className: contentClassName, style: horizontal ? void 0 : { minWidth: 0 }, children })
56
+ }
57
+ ),
58
+ vertical && /* @__PURE__ */ jsx(BaseScrollArea.Scrollbar, { orientation: "vertical", className: cn(BAR, "w-1.5 justify-center py-0.5 pr-0.5"), children: /* @__PURE__ */ jsx(BaseScrollArea.Thumb, { className: cn(THUMB, "w-full") }) }),
59
+ horizontal && /* @__PURE__ */ jsx(BaseScrollArea.Scrollbar, { orientation: "horizontal", className: cn(BAR, "h-1.5 flex-col justify-center px-0.5 pb-0.5"), children: /* @__PURE__ */ jsx(BaseScrollArea.Thumb, { className: cn(THUMB, "h-full") }) }),
60
+ vertical && horizontal && /* @__PURE__ */ jsx(BaseScrollArea.Corner, {})
61
+ ] });
62
+ }
63
+
64
+ // src/TopBar.tsx
65
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
39
66
  function TopBar({ variant = "solid", menu, logo, search, right, className }) {
40
67
  const floating = variant === "floating";
41
- return /* @__PURE__ */ jsxs(
68
+ return /* @__PURE__ */ jsxs2(
42
69
  "header",
43
70
  {
44
71
  className: cn(
@@ -47,27 +74,27 @@ function TopBar({ variant = "solid", menu, logo, search, right, className }) {
47
74
  className
48
75
  ),
49
76
  children: [
50
- (menu || logo) && /* @__PURE__ */ jsxs("div", { className: cn("flex shrink-0 items-center gap-2", floating && "pointer-events-auto"), children: [
77
+ (menu || logo) && /* @__PURE__ */ jsxs2("div", { className: cn("flex shrink-0 items-center gap-2", floating && "pointer-events-auto"), children: [
51
78
  menu,
52
- logo && /* @__PURE__ */ jsx("div", { className: "flex items-center text-body-2-strong text-text-primary", children: logo })
79
+ logo && /* @__PURE__ */ jsx2("div", { className: "flex items-center text-body-2-strong text-text-primary", children: logo })
53
80
  ] }),
54
- /* @__PURE__ */ jsx("div", { className: cn("flex min-w-0 flex-1 items-center justify-center", floating && "pointer-events-auto"), children: search }),
55
- /* @__PURE__ */ jsx("div", { className: cn("flex shrink-0 items-center gap-[6px]", floating && "pointer-events-auto"), children: right })
81
+ /* @__PURE__ */ jsx2("div", { className: cn("flex min-w-0 flex-1 items-center justify-center", floating && "pointer-events-auto"), children: search }),
82
+ /* @__PURE__ */ jsx2("div", { className: cn("flex shrink-0 items-center gap-[6px]", floating && "pointer-events-auto"), children: right })
56
83
  ]
57
84
  }
58
85
  );
59
86
  }
60
87
 
61
88
  // src/AppShell.tsx
62
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
89
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
63
90
  function AppShell({ variant = "solid", menu, logo, search, identity, banner, nav, children }) {
64
- const bar = /* @__PURE__ */ jsx2(TopBar, { variant, menu, logo, search, right: identity });
91
+ const bar = /* @__PURE__ */ jsx3(TopBar, { variant, menu, logo, search, right: identity });
65
92
  if (variant === "floating") {
66
- return /* @__PURE__ */ jsxs2("div", { className: "relative h-full min-h-0 overflow-hidden bg-bg-base", children: [
93
+ return /* @__PURE__ */ jsxs3("div", { className: "relative h-full min-h-0 overflow-hidden bg-bg-base", children: [
67
94
  bar,
68
- /* @__PURE__ */ jsxs2("div", { className: "flex h-full pb-4 pr-4 pt-[52px]", children: [
95
+ /* @__PURE__ */ jsxs3("div", { className: "flex h-full pb-4 pr-4 pt-[52px]", children: [
69
96
  nav,
70
- /* @__PURE__ */ jsxs2(
97
+ /* @__PURE__ */ jsxs3(
71
98
  "div",
72
99
  {
73
100
  className: cn(
@@ -76,7 +103,7 @@ function AppShell({ variant = "solid", menu, logo, search, identity, banner, nav
76
103
  ),
77
104
  children: [
78
105
  banner,
79
- /* @__PURE__ */ jsx2("main", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children })
106
+ /* @__PURE__ */ jsx3("main", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", children })
80
107
  ]
81
108
  }
82
109
  )
@@ -84,20 +111,22 @@ function AppShell({ variant = "solid", menu, logo, search, identity, banner, nav
84
111
  ] });
85
112
  }
86
113
  return (
87
- /* `relative overflow-hidden` is the seal the floating manner already has,
114
+ /* `relative overflow-hidden` is the seal the floating manner already has
115
+ (and the ScrollArea's own root is `relative`, so an absolutely placed
116
+ stray inside a page now belongs to the region and scrolls with it),
88
117
  and it takes both halves: an absolutely positioned descendant with no
89
118
  positioned ancestor belongs to the *viewport*, so a scroll container
90
119
  never clips it and the document itself gains its position as scroll
91
120
  range — the whole page scrolls, navigation and top bar included.
92
121
  `relative` claims such strays for the shell; `overflow-hidden` clips
93
122
  them at its edge. Either alone seals nothing. */
94
- /* @__PURE__ */ jsxs2("div", { className: "relative flex h-full min-h-0 flex-col overflow-hidden bg-bg-base text-text-primary", children: [
123
+ /* @__PURE__ */ jsxs3("div", { className: "relative flex h-full min-h-0 flex-col overflow-hidden bg-bg-base text-text-primary", children: [
95
124
  bar,
96
- /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 flex-1", children: [
125
+ /* @__PURE__ */ jsxs3("div", { className: "flex min-h-0 flex-1", children: [
97
126
  nav,
98
- /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [
127
+ /* @__PURE__ */ jsxs3("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [
99
128
  banner,
100
- /* @__PURE__ */ jsx2("main", { className: "min-w-0 flex-1 overflow-y-auto", children })
129
+ /* @__PURE__ */ jsx3(ScrollArea, { className: "min-h-0 flex-1", contentClassName: "flex min-h-full flex-col", children: /* @__PURE__ */ jsx3("main", { className: "flex min-w-0 flex-1 flex-col", children }) })
101
130
  ] })
102
131
  ] })
103
132
  ] })
@@ -107,7 +136,7 @@ function AppShell({ variant = "solid", menu, logo, search, identity, banner, nav
107
136
  // src/Avatar.tsx
108
137
  import { useState } from "react";
109
138
  import { IconUserFilled } from "@tabler/icons-react";
110
- import { jsx as jsx3 } from "react/jsx-runtime";
139
+ import { jsx as jsx4 } from "react/jsx-runtime";
111
140
  var HUES = ["#56c8ff", "#ff8f6b", "#4ade8c", "#b18cff", "#ffc94d", "#ff7eb0", "#7ea8ff", "#5fdfd6"];
112
141
  var hueFor = (key) => {
113
142
  let h = 0;
@@ -123,13 +152,13 @@ function Avatar({ src, name, alt = "", size = 36, label: spoken, className }) {
123
152
  const [broken, setBroken] = useState(false);
124
153
  const label = name || alt;
125
154
  const picture = src && !broken ? src : void 0;
126
- return /* @__PURE__ */ jsx3(
155
+ return /* @__PURE__ */ jsx4(
127
156
  "div",
128
157
  {
129
158
  ...spoken ? { role: "img", "aria-label": spoken } : { "aria-hidden": true },
130
159
  className: cn("rounded-sm overflow-hidden shrink-0 bg-bg-inset", className),
131
160
  style: { width: size, height: size },
132
- children: picture ? /* @__PURE__ */ jsx3("img", { src: picture, alt: "", className: "w-full h-full object-cover", onError: () => setBroken(true) }) : label ? /* @__PURE__ */ jsx3(
161
+ children: picture ? /* @__PURE__ */ jsx4("img", { src: picture, alt: "", className: "w-full h-full object-cover", onError: () => setBroken(true) }) : label ? /* @__PURE__ */ jsx4(
133
162
  "div",
134
163
  {
135
164
  className: "w-full h-full flex items-center justify-center font-semibold leading-none",
@@ -143,29 +172,29 @@ function Avatar({ src, name, alt = "", size = 36, label: spoken, className }) {
143
172
  },
144
173
  children: initialsFor(label)
145
174
  }
146
- ) : /* @__PURE__ */ jsx3("div", { className: "w-full h-full bg-accent-muted flex items-center justify-center text-text-muted", children: /* @__PURE__ */ jsx3(IconUserFilled, { size: Math.round(size * 0.5) }) })
175
+ ) : /* @__PURE__ */ jsx4("div", { className: "w-full h-full bg-accent-muted flex items-center justify-center text-text-muted", children: /* @__PURE__ */ jsx4(IconUserFilled, { size: Math.round(size * 0.5) }) })
147
176
  }
148
177
  );
149
178
  }
150
179
 
151
180
  // src/AvatarGroup.tsx
152
- import { jsx as jsx4 } from "react/jsx-runtime";
181
+ import { jsx as jsx5 } from "react/jsx-runtime";
153
182
  function AvatarGroup({ members, size = 24 }) {
154
183
  const visible = members.slice(0, 3);
155
184
  const overlap = Math.round(size / 3);
156
185
  const ring = size / 12;
157
- return /* @__PURE__ */ jsx4("div", { className: "flex items-center", style: { paddingRight: overlap }, children: visible.map((member, i) => (
186
+ return /* @__PURE__ */ jsx5("div", { className: "flex items-center", style: { paddingRight: overlap }, children: visible.map((member, i) => (
158
187
  /*
159
188
  The wrapper carries the overlap and the ring; the face carries
160
189
  neither. It must never clip — see the note at the top of the file —
161
190
  so it has the avatar's own radius and no overflow of its own.
162
191
  */
163
- /* @__PURE__ */ jsx4(
192
+ /* @__PURE__ */ jsx5(
164
193
  "span",
165
194
  {
166
195
  className: "relative flex rounded-sm",
167
196
  style: { marginRight: -overlap, boxShadow: `0 0 0 ${ring}px var(--bg-surface)` },
168
- children: /* @__PURE__ */ jsx4(Avatar, { size, name: member.name, src: member.picture, label: member.name })
197
+ children: /* @__PURE__ */ jsx5(Avatar, { size, name: member.name, src: member.picture, label: member.name })
169
198
  },
170
199
  i
171
200
  )
@@ -182,9 +211,9 @@ import { Button as BaseButton } from "@base-ui/react/button";
182
211
  import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
183
212
 
184
213
  // src/Kbd.tsx
185
- import { jsx as jsx5 } from "react/jsx-runtime";
214
+ import { jsx as jsx6 } from "react/jsx-runtime";
186
215
  function Kbd({ children, className }) {
187
- return /* @__PURE__ */ jsx5(
216
+ return /* @__PURE__ */ jsx6(
188
217
  "kbd",
189
218
  {
190
219
  className: cn(
@@ -199,21 +228,21 @@ function Kbd({ children, className }) {
199
228
  }
200
229
 
201
230
  // src/Tooltip.tsx
202
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
231
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
203
232
  var OPEN_DELAY = 300;
204
233
  function TooltipProvider({ delay = OPEN_DELAY, closeDelay, timeout, children }) {
205
- return /* @__PURE__ */ jsx6(BaseTooltip.Provider, { delay, closeDelay, timeout, children });
234
+ return /* @__PURE__ */ jsx7(BaseTooltip.Provider, { delay, closeDelay, timeout, children });
206
235
  }
207
236
  function Tooltip({ label, shortcut, className, ...props }) {
208
- 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), ...props, children: [
209
- /* @__PURE__ */ jsx6("span", { className: "text-caption text-text-primary whitespace-nowrap", children: label }),
210
- shortcut && /* @__PURE__ */ jsx6(Kbd, { children: shortcut })
237
+ 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: [
238
+ /* @__PURE__ */ jsx7("span", { className: "text-caption text-text-primary whitespace-nowrap", children: label }),
239
+ shortcut && /* @__PURE__ */ jsx7(Kbd, { children: shortcut })
211
240
  ] });
212
241
  }
213
242
  var GAP = 6;
214
243
  var VIEWPORT_PAD = 8;
215
244
  function TooltipSurface({ label, shortcut, placement }) {
216
- return /* @__PURE__ */ jsx6(BaseTooltip.Portal, { children: /* @__PURE__ */ jsx6(
245
+ return /* @__PURE__ */ jsx7(BaseTooltip.Portal, { children: /* @__PURE__ */ jsx7(
217
246
  BaseTooltip.Positioner,
218
247
  {
219
248
  side: placement,
@@ -222,10 +251,10 @@ function TooltipSurface({ label, shortcut, placement }) {
222
251
  collisionPadding: VIEWPORT_PAD,
223
252
  positionMethod: "fixed",
224
253
  className: "pointer-events-none z-[9999]",
225
- children: /* @__PURE__ */ jsx6(
254
+ children: /* @__PURE__ */ jsx7(
226
255
  BaseTooltip.Popup,
227
256
  {
228
- render: /* @__PURE__ */ jsx6(
257
+ render: /* @__PURE__ */ jsx7(
229
258
  Tooltip,
230
259
  {
231
260
  label,
@@ -245,20 +274,20 @@ function TooltipSurface({ label, shortcut, placement }) {
245
274
  ) });
246
275
  }
247
276
  function WithTooltip({ label, shortcut, placement = "top", wrapperClassName, children }) {
248
- return /* @__PURE__ */ jsxs3(BaseTooltip.Root, { disableHoverablePopup: true, children: [
249
- /* @__PURE__ */ jsx6(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: /* @__PURE__ */ jsx6("div", { className: cn("inline-flex shrink-0", wrapperClassName) }), children }),
250
- /* @__PURE__ */ jsx6(TooltipSurface, { label, shortcut, placement })
277
+ return /* @__PURE__ */ jsxs4(BaseTooltip.Root, { disableHoverablePopup: true, children: [
278
+ /* @__PURE__ */ jsx7(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: /* @__PURE__ */ jsx7("div", { className: cn("inline-flex shrink-0", wrapperClassName) }), children }),
279
+ /* @__PURE__ */ jsx7(TooltipSurface, { label, shortcut, placement })
251
280
  ] });
252
281
  }
253
282
  function TooltipTrigger({ label, shortcut, placement = "top", children }) {
254
- return /* @__PURE__ */ jsxs3(BaseTooltip.Root, { disableHoverablePopup: true, children: [
255
- /* @__PURE__ */ jsx6(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: children }),
256
- /* @__PURE__ */ jsx6(TooltipSurface, { label, shortcut, placement })
283
+ return /* @__PURE__ */ jsxs4(BaseTooltip.Root, { disableHoverablePopup: true, children: [
284
+ /* @__PURE__ */ jsx7(BaseTooltip.Trigger, { delay: OPEN_DELAY, render: children }),
285
+ /* @__PURE__ */ jsx7(TooltipSurface, { label, shortcut, placement })
257
286
  ] });
258
287
  }
259
288
 
260
289
  // src/IconButton.tsx
261
- import { jsx as jsx7 } from "react/jsx-runtime";
290
+ import { jsx as jsx8 } from "react/jsx-runtime";
262
291
  function IconButton({
263
292
  variant = "muted",
264
293
  className,
@@ -271,7 +300,7 @@ function IconButton({
271
300
  type = "button",
272
301
  ...props
273
302
  }) {
274
- const button = /* @__PURE__ */ jsx7(
303
+ const button = /* @__PURE__ */ jsx8(
275
304
  BaseButton,
276
305
  {
277
306
  type,
@@ -299,13 +328,13 @@ function IconButton({
299
328
  );
300
329
  const label = disabledReason ?? tooltip;
301
330
  if (label) {
302
- return /* @__PURE__ */ jsx7(TooltipTrigger, { label, shortcut: disabledReason ? void 0 : tooltipShortcut, placement: tooltipPlacement, children: button });
331
+ return /* @__PURE__ */ jsx8(TooltipTrigger, { label, shortcut: disabledReason ? void 0 : tooltipShortcut, placement: tooltipPlacement, children: button });
303
332
  }
304
333
  return button;
305
334
  }
306
335
 
307
336
  // src/Banner.tsx
308
- import { jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
337
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
309
338
  var TONE_STYLES = {
310
339
  ok: "bg-success-muted text-success-default",
311
340
  error: "bg-error-muted text-error-default",
@@ -315,17 +344,17 @@ var TONE_STYLES = {
315
344
  function Banner({ tone, children, onDismiss, dismissLabel = "Dismiss", className }) {
316
345
  const role = tone === "error" ? "alert" : "status";
317
346
  if (!onDismiss) {
318
- return /* @__PURE__ */ jsx8("div", { role, className: cn("px-4 py-2 text-body-2", TONE_STYLES[tone], className), children });
347
+ return /* @__PURE__ */ jsx9("div", { role, className: cn("px-4 py-2 text-body-2", TONE_STYLES[tone], className), children });
319
348
  }
320
- return /* @__PURE__ */ jsxs4("div", { role, className: cn("flex items-center gap-3 px-4 py-2 text-body-2", TONE_STYLES[tone], className), children: [
321
- /* @__PURE__ */ jsx8("span", { className: "min-w-0 flex-1", children }),
322
- /* @__PURE__ */ jsx8(IconButton, { "aria-label": dismissLabel, onClick: onDismiss, className: "-mr-1 shrink-0 text-current hover:text-current", children: /* @__PURE__ */ jsx8(IconX, { size: 16, stroke: 1.5 }) })
349
+ return /* @__PURE__ */ jsxs5("div", { role, className: cn("flex items-center gap-3 px-4 py-2 text-body-2", TONE_STYLES[tone], className), children: [
350
+ /* @__PURE__ */ jsx9("span", { className: "min-w-0 flex-1", children }),
351
+ /* @__PURE__ */ jsx9(IconButton, { "aria-label": dismissLabel, onClick: onDismiss, className: "-mr-1 shrink-0 text-current hover:text-current", children: /* @__PURE__ */ jsx9(IconX, { size: 16, stroke: 1.5 }) })
323
352
  ] });
324
353
  }
325
354
 
326
355
  // src/Button.tsx
327
356
  import { Button as BaseButton2 } from "@base-ui/react/button";
328
- import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
357
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
329
358
  function Button({
330
359
  variant = "muted",
331
360
  size = "default",
@@ -338,7 +367,7 @@ function Button({
338
367
  ...props
339
368
  }) {
340
369
  const hasLeadingIcon = !!leadingIcon;
341
- const button = /* @__PURE__ */ jsxs5(
370
+ const button = /* @__PURE__ */ jsxs6(
342
371
  BaseButton2,
343
372
  {
344
373
  type,
@@ -372,13 +401,13 @@ function Button({
372
401
  ]
373
402
  }
374
403
  );
375
- return disabledReason ? /* @__PURE__ */ jsx9(TooltipTrigger, { label: disabledReason, children: button }) : button;
404
+ return disabledReason ? /* @__PURE__ */ jsx10(TooltipTrigger, { label: disabledReason, children: button }) : button;
376
405
  }
377
406
 
378
407
  // src/Checkbox.tsx
379
408
  import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
380
409
  import { IconCheck } from "@tabler/icons-react";
381
- import { jsx as jsx10 } from "react/jsx-runtime";
410
+ import { jsx as jsx11 } from "react/jsx-runtime";
382
411
  function squareClasses(checked, disabled, interactive, className) {
383
412
  return cn(
384
413
  "inline-flex items-center justify-center size-4 shrink-0 rounded-[4px] border transition-colors",
@@ -391,9 +420,9 @@ function squareClasses(checked, disabled, interactive, className) {
391
420
  var tickClasses = (checked) => cn("flex", !checked && "invisible");
392
421
  function Checkbox({ checked, onChange, disabled = false, className, ...aria }) {
393
422
  if (!onChange) {
394
- return /* @__PURE__ */ jsx10("span", { "aria-hidden": "true", className: squareClasses(checked, disabled, false, className), children: /* @__PURE__ */ jsx10("span", { className: tickClasses(checked), children: /* @__PURE__ */ jsx10(IconCheck, { size: 12, stroke: 3 }) }) });
423
+ return /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", className: squareClasses(checked, disabled, false, className), children: /* @__PURE__ */ jsx11("span", { className: tickClasses(checked), children: /* @__PURE__ */ jsx11(IconCheck, { size: 12, stroke: 3 }) }) });
395
424
  }
396
- return /* @__PURE__ */ jsx10(
425
+ return /* @__PURE__ */ jsx11(
397
426
  BaseCheckbox.Root,
398
427
  {
399
428
  checked,
@@ -403,13 +432,13 @@ function Checkbox({ checked, onChange, disabled = false, className, ...aria }) {
403
432
  onCheckedChange: (next) => onChange(next),
404
433
  onClick: (e) => e.stopPropagation(),
405
434
  className: (state) => squareClasses(state.checked, state.disabled, true, className),
406
- children: /* @__PURE__ */ jsx10(BaseCheckbox.Indicator, { keepMounted: true, className: (state) => tickClasses(state.checked), children: /* @__PURE__ */ jsx10(IconCheck, { size: 12, stroke: 3 }) })
435
+ children: /* @__PURE__ */ jsx11(BaseCheckbox.Indicator, { keepMounted: true, className: (state) => tickClasses(state.checked), children: /* @__PURE__ */ jsx11(IconCheck, { size: 12, stroke: 3 }) })
407
436
  }
408
437
  );
409
438
  }
410
439
 
411
440
  // src/Chip.tsx
412
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
441
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
413
442
  var typeStyles = {
414
443
  neutral: "bg-bg-inset text-text-primary",
415
444
  brand: "bg-accent-muted text-accent-primary signal:border signal:border-accent-outline",
@@ -419,8 +448,8 @@ var typeStyles = {
419
448
  error: "bg-error-muted text-error-default signal:border signal:border-error-outline"
420
449
  };
421
450
  function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className }) {
422
- return /* @__PURE__ */ jsxs6("div", { className: cn("inline-flex min-w-0 items-center justify-center gap-1.5 rounded-full max-h-[20px] px-2 py-1", typeStyles[type], className), children: [
423
- leadingIcon && /* @__PURE__ */ jsx11("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
451
+ return /* @__PURE__ */ jsxs7("div", { className: cn("inline-flex min-w-0 items-center justify-center gap-1.5 rounded-full max-h-[20px] px-2 py-1", typeStyles[type], className), children: [
452
+ leadingIcon && /* @__PURE__ */ jsx12("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
424
453
  label && // A chip given a `max-w-*` cuts a long label with an ellipsis instead
425
454
  // of growing past it — Ship's project chip on an issue row
426
455
  // (2026-09-09). Clipped sideways only: `overflow-x-clip`, not
@@ -429,8 +458,8 @@ function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className })
429
458
  // the screenshot diff caught every descender cut off. `clip` is the
430
459
  // one overflow that leaves the other axis visible. A chip with no cap
431
460
  // draws exactly as before.
432
- /* @__PURE__ */ jsx11("span", { className: "min-w-0 overflow-x-clip text-ellipsis whitespace-nowrap text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums", children: label }),
433
- trailingIcon && /* @__PURE__ */ jsx11("span", { className: "flex size-3 shrink-0 items-center justify-center", children: trailingIcon })
461
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 overflow-x-clip text-ellipsis whitespace-nowrap text-chip signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums", children: label }),
462
+ trailingIcon && /* @__PURE__ */ jsx12("span", { className: "flex size-3 shrink-0 items-center justify-center", children: trailingIcon })
434
463
  ] });
435
464
  }
436
465
 
@@ -467,31 +496,6 @@ function triggerDisabled(trigger) {
467
496
  return !!(props.disabled || props.disabledReason);
468
497
  }
469
498
 
470
- // src/ScrollArea.tsx
471
- import { ScrollArea as BaseScrollArea } from "@base-ui/react/scroll-area";
472
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
473
- var BAR = "flex touch-none select-none rounded-full opacity-0 transition-opacity delay-300 data-[hovering]:opacity-100 data-[hovering]:delay-0 data-[scrolling]:opacity-100 data-[scrolling]:delay-0";
474
- var THUMB = "rounded-full bg-border-strong";
475
- function ScrollArea({ orientation = "vertical", className, viewportClassName, contentClassName, children }) {
476
- const vertical = orientation !== "horizontal";
477
- const horizontal = orientation !== "vertical";
478
- return /* @__PURE__ */ jsxs7(BaseScrollArea.Root, { className: cn("relative min-h-0 min-w-0", className), children: [
479
- /* @__PURE__ */ jsx12(
480
- BaseScrollArea.Viewport,
481
- {
482
- className: cn(
483
- "h-full w-full outline-none data-[has-overflow-x]:overscroll-x-contain data-[has-overflow-y]:overscroll-y-contain",
484
- viewportClassName
485
- ),
486
- children: /* @__PURE__ */ jsx12(BaseScrollArea.Content, { className: contentClassName, style: horizontal ? void 0 : { minWidth: 0 }, children })
487
- }
488
- ),
489
- vertical && /* @__PURE__ */ jsx12(BaseScrollArea.Scrollbar, { orientation: "vertical", className: cn(BAR, "w-1.5 justify-center py-0.5 pr-0.5"), children: /* @__PURE__ */ jsx12(BaseScrollArea.Thumb, { className: cn(THUMB, "w-full") }) }),
490
- horizontal && /* @__PURE__ */ jsx12(BaseScrollArea.Scrollbar, { orientation: "horizontal", className: cn(BAR, "h-1.5 flex-col justify-center px-0.5 pb-0.5"), children: /* @__PURE__ */ jsx12(BaseScrollArea.Thumb, { className: cn(THUMB, "h-full") }) }),
491
- vertical && horizontal && /* @__PURE__ */ jsx12(BaseScrollArea.Corner, {})
492
- ] });
493
- }
494
-
495
499
  // src/SectionLabel.tsx
496
500
  import { jsx as jsx13 } from "react/jsx-runtime";
497
501
  function SectionLabel({ children, className }) {
@@ -1088,6 +1092,11 @@ function Note({ children }) {
1088
1092
  import { cloneElement, isValidElement } from "react";
1089
1093
  import { Field as BaseField } from "@base-ui/react/field";
1090
1094
  import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
1095
+ var LINE_STYLES = {
1096
+ helper: "text-caption text-text-muted",
1097
+ warning: "text-caption text-warning-default",
1098
+ error: "text-caption text-error-default"
1099
+ };
1091
1100
  function Field({ label, required = false, helper, error, children }) {
1092
1101
  const control = required && isValidElement(children) ? cloneElement(children, {
1093
1102
  "aria-required": children.props["aria-required"] ?? true
@@ -1099,10 +1108,13 @@ function Field({ label, required = false, helper, error, children }) {
1099
1108
  ] }),
1100
1109
  /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-1.5", children: [
1101
1110
  control,
1102
- error != null ? /* @__PURE__ */ jsx20(BaseField.Error, { match: true, className: "text-caption text-error-default", children: error }) : helper != null ? /* @__PURE__ */ jsx20(BaseField.Description, { className: "text-caption text-text-muted", children: helper }) : null
1111
+ error != null ? /* @__PURE__ */ jsx20(BaseField.Error, { match: true, className: LINE_STYLES.error, children: error }) : helper != null ? /* @__PURE__ */ jsx20(BaseField.Description, { className: LINE_STYLES.helper, children: helper }) : null
1103
1112
  ] })
1104
1113
  ] });
1105
1114
  }
1115
+ function FieldLine({ tone = "helper", children, className }) {
1116
+ return /* @__PURE__ */ jsx20("p", { role: tone === "error" ? "alert" : "status", className: cn(LINE_STYLES[tone], className), children });
1117
+ }
1106
1118
 
1107
1119
  // src/Select.tsx
1108
1120
  import { IconCheck as IconCheck2, IconChevronDown as IconChevronDown2 } from "@tabler/icons-react";
@@ -1257,7 +1269,7 @@ import { Dialog } from "@base-ui/react/dialog";
1257
1269
  import { AlertDialog } from "@base-ui/react/alert-dialog";
1258
1270
  import { IconX as IconX3 } from "@tabler/icons-react";
1259
1271
  import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
1260
- function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, width = 502, alert = false }) {
1272
+ function DialogShell({ title, onClose, headerContent, footer, children, bodyClassName, bodyMaxHeight, width = 502, alert = false }) {
1261
1273
  const Parts = alert ? AlertDialog : Dialog;
1262
1274
  const popupRef = useRef3(null);
1263
1275
  return /* @__PURE__ */ jsx24(
@@ -1287,7 +1299,15 @@ function DialogShell({ title, onClose, headerContent, footer, children, bodyClas
1287
1299
  }
1288
1300
  )
1289
1301
  ] }),
1290
- /* @__PURE__ */ jsx24("div", { className: cn("pl-5 pr-4 py-4", footer != null && "border-b border-border-subtle", bodyClassName), children }),
1302
+ bodyMaxHeight ? /* @__PURE__ */ jsx24(
1303
+ ScrollArea,
1304
+ {
1305
+ className: cn(footer != null && "border-b border-border-subtle"),
1306
+ viewportClassName: bodyMaxHeight,
1307
+ contentClassName: cn("pl-5 pr-4 py-4", bodyClassName),
1308
+ children
1309
+ }
1310
+ ) : /* @__PURE__ */ jsx24("div", { className: cn("pl-5 pr-4 py-4", footer != null && "border-b border-border-subtle", bodyClassName), children }),
1291
1311
  footer != null && /* @__PURE__ */ jsx24("div", { className: "h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0", children: footer })
1292
1312
  ]
1293
1313
  }
@@ -1573,7 +1593,7 @@ function Popover({ trigger, anchor, align = "left", side = "bottom", open, onOpe
1573
1593
  {
1574
1594
  anchor: anchorTarget,
1575
1595
  side,
1576
- align: align === "right" ? "end" : "start",
1596
+ align: align === "right" ? "end" : align === "center" ? "center" : "start",
1577
1597
  sideOffset: GAP5,
1578
1598
  collisionPadding: VIEWPORT_PAD4,
1579
1599
  className: "z-50 data-[anchor-hidden]:hidden",
@@ -1843,7 +1863,7 @@ function SearchInput({ shortcut, className, placeholder = "Search\u2026", ...pro
1843
1863
  import { IconChevronRight as IconChevronRight2 } from "@tabler/icons-react";
1844
1864
  import { useRender } from "@base-ui/react/use-render";
1845
1865
  import { Fragment as Fragment5, jsx as jsx41, jsxs as jsxs28 } from "react/jsx-runtime";
1846
- function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, actions, showActions = "hover", render, className }) {
1866
+ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, trailing, actions, showActions = "hover", render, className }) {
1847
1867
  const titleElement = useRender({
1848
1868
  render: render ?? (chevron ? /* @__PURE__ */ jsx41("button", { type: "button", onClick: onToggle, "aria-expanded": isExpanded }) : /* @__PURE__ */ jsx41("span", {})),
1849
1869
  props: {
@@ -1877,6 +1897,7 @@ function SectionHeader({ title, chevron = false, isExpanded = true, onToggle, ac
1877
1897
  ),
1878
1898
  children: [
1879
1899
  titleElement,
1900
+ trailing != null && /* @__PURE__ */ jsx41("div", { className: "flex shrink-0 items-center", children: trailing }),
1880
1901
  actions && actions.length > 0 && /* @__PURE__ */ jsx41(
1881
1902
  "div",
1882
1903
  {
@@ -1914,7 +1935,7 @@ function writeStored(key, open) {
1914
1935
  } catch {
1915
1936
  }
1916
1937
  }
1917
- function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenChange, storageKey, actions, showActions, children, className, contentClassName }) {
1938
+ function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenChange, storageKey, trailing, actions, showActions, children, className, contentClassName }) {
1918
1939
  const [openState, setOpenState] = useState6(() => readStored(storageKey) ?? defaultOpen);
1919
1940
  const open = openProp ?? openState;
1920
1941
  const setOpen = (next) => {
@@ -1923,7 +1944,7 @@ function CollapsibleSection({ title, defaultOpen = true, open: openProp, onOpenC
1923
1944
  onOpenChange?.(next);
1924
1945
  };
1925
1946
  return /* @__PURE__ */ jsxs29(Collapsible.Root, { open, onOpenChange: setOpen, className: cn("flex flex-col", className), children: [
1926
- /* @__PURE__ */ jsx42(SectionHeader, { title, chevron: true, isExpanded: open, actions, showActions, className: "shrink-0", render: /* @__PURE__ */ jsx42(Collapsible.Trigger, {}) }),
1947
+ /* @__PURE__ */ jsx42(SectionHeader, { title, chevron: true, isExpanded: open, trailing, actions, showActions, className: "shrink-0", render: /* @__PURE__ */ jsx42(Collapsible.Trigger, {}) }),
1927
1948
  /* @__PURE__ */ jsx42(
1928
1949
  Collapsible.Panel,
1929
1950
  {
@@ -1984,25 +2005,42 @@ function Tabs({ tabs, active, onChange, size = "default", className, ...aria })
1984
2005
  // src/Toast.tsx
1985
2006
  import { createContext as createContext2, useCallback as useCallback3, useContext as useContext2, useEffect as useEffect3, useMemo as useMemo3, useState as useState7 } from "react";
1986
2007
  import { createPortal as createPortal2 } from "react-dom";
1987
- import { IconCircleCheck } from "@tabler/icons-react";
2008
+ import { IconAlertCircle, IconCircleCheck, IconCircleX } from "@tabler/icons-react";
1988
2009
  import { jsx as jsx44, jsxs as jsxs31 } from "react/jsx-runtime";
1989
2010
  var SURFACE_STYLES = {
1990
2011
  success: "bg-success-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
1991
2012
  brand: "bg-accent-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
1992
- neutral: "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]"
2013
+ neutral: "bg-bg-inset border border-border-subtle signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
2014
+ warning: "bg-warning-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]",
2015
+ error: "bg-error-muted signal:bg-bg-inset signal:border signal:border-border-default signal:shadow-[shadow:var(--shadow-md)]"
2016
+ };
2017
+ var ICONS = {
2018
+ success: IconCircleCheck,
2019
+ brand: IconCircleCheck,
2020
+ neutral: IconCircleCheck,
2021
+ warning: IconAlertCircle,
2022
+ error: IconCircleX
1993
2023
  };
1994
2024
  var ICON_STYLES = {
1995
2025
  success: "signal:text-success-default signal:drop-shadow-glow-success",
1996
2026
  brand: "signal:text-text-interactive signal:drop-shadow-glow-accent",
1997
- neutral: "signal:text-text-secondary"
2027
+ neutral: "signal:text-text-secondary",
2028
+ warning: "signal:text-warning-default signal:drop-shadow-glow-warning",
2029
+ // No glow: the theme defines `--glow-warning`, `--glow-success` and
2030
+ // `--glow-accent` and no error glow, and a colour with no token is not
2031
+ // approximated here (D16).
2032
+ error: "signal:text-error-default"
1998
2033
  };
1999
2034
  var ACTION_BORDER_STYLES = {
2000
2035
  success: "signal:border signal:border-border-default signal:hover:border-border-strong",
2001
2036
  brand: "signal:border signal:border-border-default signal:hover:border-border-strong",
2002
- neutral: "border border-border-default"
2037
+ neutral: "border border-border-default",
2038
+ warning: "signal:border signal:border-border-default signal:hover:border-border-strong",
2039
+ error: "signal:border signal:border-border-default signal:hover:border-border-strong"
2003
2040
  };
2004
2041
  function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAction, className }) {
2005
2042
  const hasAction = !!(actionLabel && onAction);
2043
+ const LeadingIcon = ICONS[type];
2006
2044
  return /* @__PURE__ */ jsxs31(
2007
2045
  "div",
2008
2046
  {
@@ -2016,7 +2054,7 @@ function Toast({ label, type = "neutral", leadingIcon = true, actionLabel, onAct
2016
2054
  ),
2017
2055
  children: [
2018
2056
  /* @__PURE__ */ jsxs31("div", { className: "flex items-center gap-2 shrink-0", children: [
2019
- leadingIcon && /* @__PURE__ */ jsx44(IconCircleCheck, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) }),
2057
+ leadingIcon && /* @__PURE__ */ jsx44(LeadingIcon, { size: 16, stroke: 1.5, className: cn("text-text-primary shrink-0", ICON_STYLES[type]) }),
2020
2058
  /* @__PURE__ */ jsx44("span", { className: "font-normal text-[14px] leading-[1.4] text-text-primary whitespace-nowrap", children: label })
2021
2059
  ] }),
2022
2060
  hasAction && /* @__PURE__ */ jsx44(
@@ -2100,6 +2138,7 @@ export {
2100
2138
  EmptyState,
2101
2139
  EnterHint,
2102
2140
  Field,
2141
+ FieldLine,
2103
2142
  IconButton,
2104
2143
  IdentityMenu,
2105
2144
  IdentityPanel,