@estiva-app/ui 0.1.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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/dist/Avatar.d.ts +16 -0
  4. package/dist/Avatar.d.ts.map +1 -0
  5. package/dist/Button.d.ts +28 -0
  6. package/dist/Button.d.ts.map +1 -0
  7. package/dist/Chip.d.ts +21 -0
  8. package/dist/Chip.d.ts.map +1 -0
  9. package/dist/DialogShell.d.ts +19 -0
  10. package/dist/DialogShell.d.ts.map +1 -0
  11. package/dist/Divider.d.ts +12 -0
  12. package/dist/Divider.d.ts.map +1 -0
  13. package/dist/EmptyState.d.ts +14 -0
  14. package/dist/EmptyState.d.ts.map +1 -0
  15. package/dist/Field.d.ts +13 -0
  16. package/dist/Field.d.ts.map +1 -0
  17. package/dist/IconButton.d.ts +17 -0
  18. package/dist/IconButton.d.ts.map +1 -0
  19. package/dist/Select.d.ts +30 -0
  20. package/dist/Select.d.ts.map +1 -0
  21. package/dist/Skeleton.d.ts +24 -0
  22. package/dist/Skeleton.d.ts.map +1 -0
  23. package/dist/TextInput.d.ts +11 -0
  24. package/dist/TextInput.d.ts.map +1 -0
  25. package/dist/Textarea.d.ts +8 -0
  26. package/dist/Textarea.d.ts.map +1 -0
  27. package/dist/Tooltip.d.ts +22 -0
  28. package/dist/Tooltip.d.ts.map +1 -0
  29. package/dist/cn.d.ts +10 -0
  30. package/dist/cn.d.ts.map +1 -0
  31. package/dist/index.d.ts +26 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +486 -0
  34. package/dist/index.js.map +7 -0
  35. package/package.json +74 -0
  36. package/src/Avatar.stories.tsx +55 -0
  37. package/src/Avatar.tsx +81 -0
  38. package/src/Button.stories.tsx +53 -0
  39. package/src/Button.tsx +67 -0
  40. package/src/Chip.stories.tsx +37 -0
  41. package/src/Chip.tsx +43 -0
  42. package/src/DialogShell.stories.tsx +97 -0
  43. package/src/DialogShell.tsx +65 -0
  44. package/src/Divider.stories.tsx +44 -0
  45. package/src/Divider.tsx +22 -0
  46. package/src/EmptyState.stories.tsx +18 -0
  47. package/src/EmptyState.tsx +24 -0
  48. package/src/Field.stories.tsx +22 -0
  49. package/src/Field.tsx +24 -0
  50. package/src/IconButton.stories.tsx +44 -0
  51. package/src/IconButton.tsx +60 -0
  52. package/src/Select.stories.tsx +107 -0
  53. package/src/Select.tsx +195 -0
  54. package/src/Skeleton.stories.tsx +33 -0
  55. package/src/Skeleton.tsx +38 -0
  56. package/src/TextInput.stories.tsx +18 -0
  57. package/src/TextInput.tsx +29 -0
  58. package/src/Textarea.stories.tsx +17 -0
  59. package/src/Textarea.tsx +25 -0
  60. package/src/Tooltip.stories.tsx +47 -0
  61. package/src/Tooltip.tsx +70 -0
  62. package/src/cn.ts +13 -0
  63. package/src/index.ts +25 -0
  64. package/tailwind-preset.js +162 -0
  65. package/tokens.css +225 -0
package/dist/index.js ADDED
@@ -0,0 +1,486 @@
1
+ // src/Avatar.tsx
2
+ import { useState } from "react";
3
+ import { IconUserFilled } from "@tabler/icons-react";
4
+
5
+ // src/cn.ts
6
+ import { clsx } from "clsx";
7
+ import { twMerge } from "tailwind-merge";
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+
12
+ // src/Avatar.tsx
13
+ import { jsx } from "react/jsx-runtime";
14
+ var HUES = ["#56c8ff", "#ff8f6b", "#4ade8c", "#b18cff", "#ffc94d", "#ff7eb0", "#7ea8ff", "#5fdfd6"];
15
+ var hueFor = (key) => {
16
+ let h = 0;
17
+ for (let i = 0; i < key.length; i++) h = h * 31 + key.charCodeAt(i) | 0;
18
+ return HUES[Math.abs(h) % HUES.length];
19
+ };
20
+ var initialsFor = (name) => name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
21
+ function Avatar({ src, name, alt = "", size = 36, className }) {
22
+ const [broken, setBroken] = useState(false);
23
+ const label = name || alt;
24
+ const picture = src && !broken ? src : void 0;
25
+ return /* @__PURE__ */ jsx("div", { className: cn("rounded-sm overflow-hidden shrink-0 bg-bg-inset", className), style: { width: size, height: size }, children: picture ? /* @__PURE__ */ jsx("img", { src: picture, alt: alt || name || "", className: "w-full h-full object-cover", onError: () => setBroken(true) }) : label ? /* @__PURE__ */ jsx(
26
+ "div",
27
+ {
28
+ className: "w-full h-full flex items-center justify-center font-semibold",
29
+ style: {
30
+ color: "#08121c",
31
+ fontSize: Math.round(size * 0.36),
32
+ background: `linear-gradient(160deg, color-mix(in srgb, ${hueFor(label)} 92%, #fff) 0%, color-mix(in srgb, ${hueFor(label)} 70%, #0b0d11) 100%)`
33
+ },
34
+ children: initialsFor(label)
35
+ }
36
+ ) : /* @__PURE__ */ jsx("div", { className: "w-full h-full bg-accent-muted flex items-center justify-center text-text-muted", children: /* @__PURE__ */ jsx(IconUserFilled, { size: Math.round(size * 0.5) }) }) });
37
+ }
38
+
39
+ // src/Button.tsx
40
+ import { jsxs } from "react/jsx-runtime";
41
+ function Button({
42
+ variant = "muted",
43
+ size = "default",
44
+ leadingIcon,
45
+ className,
46
+ children,
47
+ disabled,
48
+ type = "button",
49
+ ...props
50
+ }) {
51
+ const hasLeadingIcon = !!leadingIcon;
52
+ return /* @__PURE__ */ jsxs(
53
+ "button",
54
+ {
55
+ type,
56
+ className: cn(
57
+ "inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium",
58
+ size === "default" && "h-8 text-[14px] leading-[14px]",
59
+ size === "small" && "h-6 text-[12px] leading-[12px]",
60
+ // Extra right padding beside a leading icon, for optical balance.
61
+ size === "default" && (hasLeadingIcon ? "pl-2 pr-3" : "px-2"),
62
+ size === "small" && (hasLeadingIcon ? "pl-1.5 pr-2" : "px-1.5"),
63
+ !disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold",
64
+ !disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer",
65
+ !disabled && variant === "muted" && "hover:bg-bg-hover text-text-primary cursor-pointer",
66
+ !disabled && variant === "destructive" && "hover:bg-error-muted text-error-default cursor-pointer",
67
+ disabled && "bg-bg-disabled text-text-disabled pointer-events-none",
68
+ disabled && variant === "outlined" && "border border-border-default",
69
+ className
70
+ ),
71
+ disabled,
72
+ ...props,
73
+ children: [
74
+ leadingIcon,
75
+ children
76
+ ]
77
+ }
78
+ );
79
+ }
80
+
81
+ // src/Chip.tsx
82
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
83
+ var typeStyles = {
84
+ neutral: "bg-bg-inset text-text-primary",
85
+ brand: "bg-accent-muted text-accent-primary signal:border signal:border-[rgba(86,200,255,0.3)]",
86
+ info: "bg-info-muted text-info-default signal:border signal:border-[rgba(86,200,255,0.3)]",
87
+ 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)]",
88
+ success: "bg-success-muted text-success-default signal:border signal:border-[rgba(63,222,140,0.3)]",
89
+ error: "bg-error-muted text-error-default signal:border signal:border-[rgba(255,107,107,0.3)]"
90
+ };
91
+ function Chip({ type = "neutral", label, leadingIcon, trailingIcon, className }) {
92
+ return /* @__PURE__ */ jsxs2("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: [
93
+ leadingIcon && /* @__PURE__ */ jsx2("span", { className: "flex size-3 shrink-0 items-center justify-center", children: leadingIcon }),
94
+ label && /* @__PURE__ */ jsx2("span", { className: "text-chip whitespace-nowrap signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums", children: label }),
95
+ trailingIcon && /* @__PURE__ */ jsx2("span", { className: "flex size-3 shrink-0 items-center justify-center", children: trailingIcon })
96
+ ] });
97
+ }
98
+
99
+ // src/Tooltip.tsx
100
+ import { useCallback, useLayoutEffect, useRef, useState as useState2 } from "react";
101
+ import { createPortal } from "react-dom";
102
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
103
+ function Tooltip({ label, className }) {
104
+ return /* @__PURE__ */ jsx3("div", { role: "tooltip", className: cn("bg-bg-elevated border border-border-default rounded-lg h-[30px] flex items-center justify-center px-2 shadow-lg", className), children: /* @__PURE__ */ jsx3("span", { className: "text-caption text-text-primary whitespace-nowrap", children: label }) });
105
+ }
106
+ var GAP = 6;
107
+ var VIEWPORT_PAD = 8;
108
+ function WithTooltip({ label, placement = "top", children }) {
109
+ const [show, setShow] = useState2(false);
110
+ const ref = useRef(null);
111
+ const tooltipRef = useRef(null);
112
+ const [style, setStyle] = useState2({ position: "fixed", zIndex: 9999, pointerEvents: "none", visibility: "hidden" });
113
+ const reposition = useCallback(() => {
114
+ const trigger = ref.current;
115
+ const tip = tooltipRef.current;
116
+ if (!trigger || !tip) return;
117
+ const triggerRect = trigger.getBoundingClientRect();
118
+ const tipRect = tip.getBoundingClientRect();
119
+ const top = placement === "bottom" ? triggerRect.bottom + GAP : triggerRect.top - tipRect.height - GAP;
120
+ let left = triggerRect.left + triggerRect.width / 2 - tipRect.width / 2;
121
+ left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - tipRect.width - VIEWPORT_PAD));
122
+ setStyle({ position: "fixed", top, left, zIndex: 9999, pointerEvents: "none", visibility: "visible" });
123
+ }, [placement]);
124
+ useLayoutEffect(() => {
125
+ if (show) reposition();
126
+ }, [show, reposition]);
127
+ return /* @__PURE__ */ jsxs3("div", { ref, className: "inline-flex shrink-0", onMouseEnter: () => setShow(true), onMouseLeave: () => setShow(false), children: [
128
+ children,
129
+ show && createPortal(
130
+ /* @__PURE__ */ jsx3("div", { ref: tooltipRef, style, children: /* @__PURE__ */ jsx3(Tooltip, { label }) }),
131
+ document.body
132
+ )
133
+ ] });
134
+ }
135
+
136
+ // src/IconButton.tsx
137
+ import { jsx as jsx4 } from "react/jsx-runtime";
138
+ function IconButton({
139
+ variant = "muted",
140
+ className,
141
+ children,
142
+ disabled,
143
+ tooltip,
144
+ tooltipPlacement,
145
+ type = "button",
146
+ ...props
147
+ }) {
148
+ const button = /* @__PURE__ */ jsx4(
149
+ "button",
150
+ {
151
+ type,
152
+ className: cn(
153
+ "flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 cursor-pointer",
154
+ !disabled && variant === "primary" && "bg-accent-primary hover:bg-accent-hover text-text-inverse",
155
+ !disabled && variant === "muted" && "text-text-secondary hover:bg-bg-hover hover:text-text-primary",
156
+ !disabled && variant === "outlined" && "border border-border-default hover:bg-bg-hover text-text-secondary",
157
+ disabled && variant === "primary" && "bg-bg-disabled text-text-disabled",
158
+ disabled && variant === "muted" && "text-text-disabled",
159
+ disabled && variant === "outlined" && "border border-border-default text-text-disabled",
160
+ disabled && "pointer-events-none cursor-not-allowed",
161
+ className
162
+ ),
163
+ disabled,
164
+ ...props,
165
+ children
166
+ }
167
+ );
168
+ if (tooltip) {
169
+ return /* @__PURE__ */ jsx4(WithTooltip, { label: tooltip, placement: tooltipPlacement, children: button });
170
+ }
171
+ return button;
172
+ }
173
+
174
+ // src/Field.tsx
175
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
176
+ function Field({ label, required = false, children }) {
177
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
178
+ /* @__PURE__ */ jsxs4("label", { className: `text-input-label text-text-primary${required ? " flex items-center" : ""}`, children: [
179
+ label,
180
+ required && /* @__PURE__ */ jsx5("span", { className: "text-error-default ml-0.5", children: "*" })
181
+ ] }),
182
+ children
183
+ ] });
184
+ }
185
+
186
+ // src/Select.tsx
187
+ import { IconCheck, IconChevronDown } from "@tabler/icons-react";
188
+ import { useCallback as useCallback2, useEffect, useId, useMemo, useRef as useRef2, useState as useState3 } from "react";
189
+ import { createPortal as createPortal2 } from "react-dom";
190
+ import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
191
+ function Select({ value, onChange, options, size = "default", ariaLabel, placeholder = "Select\u2026", disabled, className }) {
192
+ const id = useId();
193
+ const triggerRef = useRef2(null);
194
+ const menuRef = useRef2(null);
195
+ const [rect, setRect] = useState3(null);
196
+ const open = rect !== null;
197
+ const selectedIndex = useMemo(() => Math.max(0, options.findIndex((o) => o.value === value)), [options, value]);
198
+ const [activeIndex, setActiveIndex] = useState3(selectedIndex);
199
+ const close = useCallback2((refocus) => {
200
+ setRect(null);
201
+ if (refocus) triggerRef.current?.focus();
202
+ }, []);
203
+ const openMenu = useCallback2(() => {
204
+ setActiveIndex(selectedIndex);
205
+ setRect(triggerRef.current?.getBoundingClientRect() ?? null);
206
+ }, [selectedIndex]);
207
+ useEffect(() => {
208
+ if (!open) return;
209
+ const onDown = (e) => {
210
+ if (menuRef.current?.contains(e.target)) return;
211
+ if (triggerRef.current?.contains(e.target)) return;
212
+ setRect(null);
213
+ };
214
+ const onMove = () => setRect(null);
215
+ document.addEventListener("mousedown", onDown);
216
+ window.addEventListener("resize", onMove);
217
+ window.addEventListener("scroll", onMove, true);
218
+ return () => {
219
+ document.removeEventListener("mousedown", onDown);
220
+ window.removeEventListener("resize", onMove);
221
+ window.removeEventListener("scroll", onMove, true);
222
+ };
223
+ }, [open]);
224
+ const pick = (index) => {
225
+ const option = options[index];
226
+ if (!option) return;
227
+ onChange(option.value);
228
+ close(true);
229
+ };
230
+ const onKeyDown = (e) => {
231
+ if (!open) {
232
+ if (["ArrowDown", "ArrowUp", "Enter", " "].includes(e.key)) {
233
+ e.preventDefault();
234
+ openMenu();
235
+ }
236
+ return;
237
+ }
238
+ switch (e.key) {
239
+ case "Escape":
240
+ e.preventDefault();
241
+ close(true);
242
+ break;
243
+ case "ArrowDown":
244
+ e.preventDefault();
245
+ setActiveIndex((i) => Math.min(options.length - 1, i + 1));
246
+ break;
247
+ case "ArrowUp":
248
+ e.preventDefault();
249
+ setActiveIndex((i) => Math.max(0, i - 1));
250
+ break;
251
+ case "Home":
252
+ e.preventDefault();
253
+ setActiveIndex(0);
254
+ break;
255
+ case "End":
256
+ e.preventDefault();
257
+ setActiveIndex(options.length - 1);
258
+ break;
259
+ case "Enter":
260
+ case " ":
261
+ e.preventDefault();
262
+ pick(activeIndex);
263
+ break;
264
+ case "Tab":
265
+ close(false);
266
+ break;
267
+ }
268
+ };
269
+ const selected = options.find((o) => o.value === value);
270
+ return /* @__PURE__ */ jsxs5(Fragment, { children: [
271
+ /* @__PURE__ */ jsxs5(
272
+ "button",
273
+ {
274
+ ref: triggerRef,
275
+ type: "button",
276
+ disabled,
277
+ "aria-haspopup": "listbox",
278
+ "aria-expanded": open,
279
+ "aria-label": ariaLabel,
280
+ onClick: () => open ? close(false) : openMenu(),
281
+ onKeyDown,
282
+ className: cn(
283
+ "flex w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left",
284
+ "border-border-default text-text-primary outline-none transition-colors",
285
+ "hover:border-border-strong disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
286
+ "focus-visible:border-border-focus aria-expanded:border-border-focus",
287
+ "signal:transition-shadow signal:focus-visible:shadow-focus-ring",
288
+ size === "default" && "px-3 py-2 text-[14px] leading-[1.4] font-normal",
289
+ size === "small" && "h-6 px-2 text-[12px] leading-[1.4] font-normal",
290
+ className
291
+ ),
292
+ children: [
293
+ /* @__PURE__ */ jsxs5("span", { className: cn("flex min-w-0 items-center gap-2", !selected && "text-text-muted"), children: [
294
+ selected?.leading && /* @__PURE__ */ jsx6("span", { className: "flex shrink-0 items-center", children: selected.leading }),
295
+ /* @__PURE__ */ jsx6("span", { className: "truncate", children: selected?.label ?? placeholder })
296
+ ] }),
297
+ /* @__PURE__ */ jsx6(IconChevronDown, { size: size === "small" ? 14 : 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
298
+ ]
299
+ }
300
+ ),
301
+ open && rect && createPortal2(
302
+ /* @__PURE__ */ jsx6(
303
+ "div",
304
+ {
305
+ ref: menuRef,
306
+ role: "listbox",
307
+ "aria-activedescendant": `${id}-${activeIndex}`,
308
+ tabIndex: -1,
309
+ style: { top: rect.bottom + 4, left: rect.left, minWidth: rect.width },
310
+ className: "fixed z-50 max-h-72 overflow-y-auto rounded-lg border border-border-default bg-bg-elevated p-1 shadow-lg",
311
+ children: options.map((option, index) => /* @__PURE__ */ jsxs5(
312
+ "div",
313
+ {
314
+ id: `${id}-${index}`,
315
+ role: "option",
316
+ "aria-selected": option.value === value,
317
+ onMouseEnter: () => setActiveIndex(index),
318
+ onMouseDown: (e) => {
319
+ e.preventDefault();
320
+ pick(index);
321
+ },
322
+ className: cn(
323
+ "flex h-9 cursor-pointer items-center justify-between gap-2 rounded-lg px-3 transition-colors",
324
+ "text-[14px] font-normal leading-[1.4] text-text-primary",
325
+ index === activeIndex && "bg-bg-hover",
326
+ option.value === value && "font-medium"
327
+ ),
328
+ children: [
329
+ /* @__PURE__ */ jsxs5("span", { className: "flex min-w-0 items-center gap-2", children: [
330
+ option.leading && /* @__PURE__ */ jsx6("span", { className: "flex shrink-0 items-center", children: option.leading }),
331
+ /* @__PURE__ */ jsx6("span", { className: "truncate", children: option.label })
332
+ ] }),
333
+ option.value === value && /* @__PURE__ */ jsx6(IconCheck, { size: 16, stroke: 1.5, className: "shrink-0 text-text-secondary" })
334
+ ]
335
+ },
336
+ option.value
337
+ ))
338
+ }
339
+ ),
340
+ document.body
341
+ )
342
+ ] });
343
+ }
344
+
345
+ // src/TextInput.tsx
346
+ import { forwardRef } from "react";
347
+ import { jsx as jsx7 } from "react/jsx-runtime";
348
+ var TextInput = forwardRef(function TextInput2({ className, type = "text", ...props }, ref) {
349
+ return /* @__PURE__ */ jsx7(
350
+ "input",
351
+ {
352
+ ref,
353
+ type,
354
+ className: cn(
355
+ "bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
356
+ "text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted",
357
+ "outline-none transition-colors",
358
+ "disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
359
+ "signal:transition-shadow signal:focus:shadow-focus-ring",
360
+ className
361
+ ),
362
+ ...props
363
+ }
364
+ );
365
+ });
366
+
367
+ // src/Textarea.tsx
368
+ import { forwardRef as forwardRef2 } from "react";
369
+ import { jsx as jsx8 } from "react/jsx-runtime";
370
+ var Textarea = forwardRef2(function Textarea2({ className, ...props }, ref) {
371
+ return /* @__PURE__ */ jsx8(
372
+ "textarea",
373
+ {
374
+ ref,
375
+ className: cn(
376
+ "bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2",
377
+ "text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted",
378
+ "resize-none outline-none transition-colors",
379
+ "disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled",
380
+ "signal:transition-shadow signal:focus:shadow-focus-ring",
381
+ className
382
+ ),
383
+ ...props
384
+ }
385
+ );
386
+ });
387
+
388
+ // src/DialogShell.tsx
389
+ import { useEffect as useEffect2 } from "react";
390
+ import { createPortal as createPortal3 } from "react-dom";
391
+ import { IconX } from "@tabler/icons-react";
392
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
393
+ function DialogShell({ title, onClose, footer, children, bodyClassName, width = 502 }) {
394
+ useEffect2(() => {
395
+ const onKey = (event) => {
396
+ if (event.key === "Escape") onClose();
397
+ };
398
+ document.addEventListener("keydown", onKey);
399
+ return () => document.removeEventListener("keydown", onKey);
400
+ }, [onClose]);
401
+ return createPortal3(
402
+ /* @__PURE__ */ jsxs6(Fragment2, { children: [
403
+ /* @__PURE__ */ jsx9("div", { className: "fixed inset-0 z-40 bg-black/50", onClick: onClose }),
404
+ /* @__PURE__ */ jsx9("div", { className: "fixed inset-0 z-50 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs6(
405
+ "div",
406
+ {
407
+ role: "dialog",
408
+ "aria-modal": "true",
409
+ "aria-label": title,
410
+ className: "bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden",
411
+ style: { width },
412
+ children: [
413
+ /* @__PURE__ */ jsxs6("div", { className: "h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0", children: [
414
+ /* @__PURE__ */ jsx9("span", { className: "text-h4 text-text-primary", children: title }),
415
+ /* @__PURE__ */ jsx9(IconButton, { tooltip: "Close", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(IconX, { size: 16, stroke: 1.5 }) })
416
+ ] }),
417
+ /* @__PURE__ */ jsx9("div", { className: cn("pl-5 pr-4 py-4 border-b border-border-subtle", bodyClassName), children }),
418
+ /* @__PURE__ */ jsx9("div", { className: "h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0", children: footer })
419
+ ]
420
+ }
421
+ ) })
422
+ ] }),
423
+ document.body
424
+ );
425
+ }
426
+
427
+ // src/Divider.tsx
428
+ import { jsx as jsx10 } from "react/jsx-runtime";
429
+ function Divider({ orientation = "horizontal", className }) {
430
+ return /* @__PURE__ */ jsx10(
431
+ "div",
432
+ {
433
+ role: "separator",
434
+ "aria-orientation": orientation,
435
+ className: cn("bg-border-subtle", orientation === "horizontal" ? "h-px mx-3" : "w-px self-stretch shrink-0", className)
436
+ }
437
+ );
438
+ }
439
+
440
+ // src/EmptyState.tsx
441
+ import { IconMessage2 } from "@tabler/icons-react";
442
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
443
+ function EmptyState({ icon, message, className }) {
444
+ return /* @__PURE__ */ jsxs7("div", { className: cn("flex flex-col gap-2 items-center", className), children: [
445
+ /* @__PURE__ */ jsx11("span", { className: "text-text-secondary", children: icon ?? /* @__PURE__ */ jsx11(IconMessage2, { size: 16, stroke: 1.5 }) }),
446
+ /* @__PURE__ */ jsx11("p", { className: "text-body-2 text-text-secondary text-center", children: message })
447
+ ] });
448
+ }
449
+
450
+ // src/Skeleton.tsx
451
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
452
+ function SkeletonBar({ className, style }) {
453
+ return /* @__PURE__ */ jsx12("div", { className: cn("bg-bg-inset rounded-sm animate-pulse", className), style });
454
+ }
455
+ var ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160];
456
+ function SkeletonRow({ barWidth = 130 }) {
457
+ return /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 px-2 h-[32px] rounded-lg", children: [
458
+ /* @__PURE__ */ jsx12(SkeletonBar, { className: "w-4 h-4 shrink-0" }),
459
+ /* @__PURE__ */ jsx12(SkeletonBar, { className: "h-3.5", style: { width: barWidth } })
460
+ ] });
461
+ }
462
+ function SkeletonList({ rows = 8, className }) {
463
+ return /* @__PURE__ */ jsx12("div", { className: cn("flex flex-col gap-0.5 animate-skeleton-in", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx12(SkeletonRow, { barWidth: ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length] }, i)) });
464
+ }
465
+ export {
466
+ Avatar,
467
+ Button,
468
+ Chip,
469
+ DialogShell,
470
+ Divider,
471
+ EmptyState,
472
+ Field,
473
+ IconButton,
474
+ Select,
475
+ SkeletonBar,
476
+ SkeletonList,
477
+ SkeletonRow,
478
+ TextInput,
479
+ Textarea,
480
+ Tooltip,
481
+ WithTooltip,
482
+ cn,
483
+ hueFor,
484
+ initialsFor
485
+ };
486
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/Avatar.tsx", "../src/cn.ts", "../src/Button.tsx", "../src/Chip.tsx", "../src/Tooltip.tsx", "../src/IconButton.tsx", "../src/Field.tsx", "../src/Select.tsx", "../src/TextInput.tsx", "../src/Textarea.tsx", "../src/DialogShell.tsx", "../src/Divider.tsx", "../src/EmptyState.tsx", "../src/Skeleton.tsx"],
4
+ "sourcesContent": ["import { useState } from 'react'\nimport { IconUserFilled } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * A person's face: their picture, or their initials on a colour that is\n * theirs, or \u2014 when there is no name to take initials from \u2014 a silhouette.\n *\n * Peek's Avatar (2026-08-28), made self-contained: Peek's version found the\n * picture itself, by name, from Peek's data (an upload in Convex, else the\n * demo portrait), and switched the initials on only under its Signal theme.\n * Neither belongs in a shared component, so this one takes the picture URL\n * from its caller and draws the initials in every theme. Peek keeps a\n * three-line wrapper that resolves the picture; Ship passes the relay's\n * `kind:0` picture straight in.\n *\n * Initials come from a *name*, never from a key: a component that accepted a\n * pubkey would eventually show one (Ship's ruling, 2026-08-24). An unnamed\n * person is the silhouette \u2014 `?` on a bright gradient read as an error badge\n * rather than a person (Peek, FEE-1).\n *\n * The hue is a fallback palette, not a token: it is per person, chosen\n * from the name so the same person is the same colour everywhere, and a\n * theme has no say in it. Peek's eight, verbatim.\n */\nconst HUES = ['#56c8ff', '#ff8f6b', '#4ade8c', '#b18cff', '#ffc94d', '#ff7eb0', '#7ea8ff', '#5fdfd6']\n\nexport const hueFor = (key: string) => {\n let h = 0\n for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) | 0\n return HUES[Math.abs(h) % HUES.length]\n}\n\n/** Peek's rule: the first letter of the first two words. */\nexport const initialsFor = (name: string) =>\n name\n .split(' ')\n .map((w) => w[0])\n .slice(0, 2)\n .join('')\n .toUpperCase()\n\nexport interface AvatarProps {\n /** The picture URL, resolved by the caller. A picture that fails to load falls back to the initials. */\n src?: string\n /** The person's name \u2014 where the initials and the colour come from. */\n name?: string\n /** Alt text for the picture; the name when absent. Also the initials' source when there is no name. */\n alt?: string\n /** Pixels. Peek's scale: 16 \u00B7 24 \u00B7 32 \u00B7 36 (default). */\n size?: number\n className?: string\n}\n\nexport function Avatar({ src, name, alt = '', size = 36, className }: AvatarProps) {\n const [broken, setBroken] = useState(false)\n const label = name || alt\n const picture = src && !broken ? src : undefined\n return (\n <div className={cn('rounded-sm overflow-hidden shrink-0 bg-bg-inset', className)} style={{ width: size, height: size }}>\n {picture ? (\n <img src={picture} alt={alt || name || ''} className=\"w-full h-full object-cover\" onError={() => setBroken(true)} />\n ) : label ? (\n <div\n className=\"w-full h-full flex items-center justify-center font-semibold\"\n style={{\n color: '#08121c',\n fontSize: Math.round(size * 0.36),\n background: `linear-gradient(160deg, color-mix(in srgb, ${hueFor(label)} 92%, #fff) 0%, color-mix(in srgb, ${hueFor(label)} 70%, #0b0d11) 100%)`,\n }}\n >\n {initialsFor(label)}\n </div>\n ) : (\n <div className=\"w-full h-full bg-accent-muted flex items-center justify-center text-text-muted\">\n <IconUserFilled size={Math.round(size * 0.5)} />\n </div>\n )}\n </div>\n )\n}\n", "import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\n/**\n * Peek's `cn`: clsx for the conditionals, tailwind-merge for the conflicts.\n *\n * Remember the pitfall the README records: a custom text-size class in a\n * merged list is dropped when a `text-{colour}` follows it. Inside this\n * package, sizes are arbitrary values (`text-[12px]`), never the token class.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Button (2026-08-28), verbatim, plus what Ship added and Peek should\n * adopt: a `destructive` variant \u2014 the muted button in the error colour, for\n * \"Delete project\" and its kind; a destructive action is a button like any\n * other, not a dotted link \u2014 and `type=\"button\"` by default, so a button\n * inside a form submits it only when asked to.\n *\n * Three variants and two sizes: 32px default / 24px small, 6px radius, 500\n * weight. Sizes are spelled as arbitrary values for the reason the README\n * records: tailwind-merge drops a custom `text-{size}` that is followed by a\n * `text-{colour}`.\n *\n * The primary reads in `text-inverse` on the accent \u2014 a theme decides what\n * that is (dark on Peek's light accents, light on Ship's dark one). Under\n * Signal it is also semibold, as Peek has it.\n */\nexport type ButtonVariant = 'primary' | 'outlined' | 'muted' | 'destructive'\nexport type ButtonSize = 'default' | 'small'\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant\n size?: ButtonSize\n /** 16px, stroke 1.5 on the default size; 14px on small. */\n leadingIcon?: ReactNode\n children: ReactNode\n}\n\nexport function Button({\n variant = 'muted',\n size = 'default',\n leadingIcon,\n className,\n children,\n disabled,\n type = 'button',\n ...props\n}: ButtonProps) {\n const hasLeadingIcon = !!leadingIcon\n return (\n <button\n type={type}\n className={cn(\n 'inline-flex items-center justify-center gap-1 rounded-md transition-colors font-sans font-medium',\n size === 'default' && 'h-8 text-[14px] leading-[14px]',\n size === 'small' && 'h-6 text-[12px] leading-[12px]',\n // Extra right padding beside a leading icon, for optical balance.\n size === 'default' && (hasLeadingIcon ? 'pl-2 pr-3' : 'px-2'),\n size === 'small' && (hasLeadingIcon ? 'pl-1.5 pr-2' : 'px-1.5'),\n !disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse cursor-pointer signal:font-semibold',\n !disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-primary cursor-pointer',\n !disabled && variant === 'muted' && 'hover:bg-bg-hover text-text-primary cursor-pointer',\n !disabled && variant === 'destructive' && 'hover:bg-error-muted text-error-default cursor-pointer',\n disabled && 'bg-bg-disabled text-text-disabled pointer-events-none',\n disabled && variant === 'outlined' && 'border border-border-default',\n className,\n )}\n disabled={disabled}\n {...props}\n >\n {leadingIcon}\n {children}\n </button>\n )\n}\n", "import type { ReactNode } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Chip (2026-08-28), verbatim: a 20px pill in one of six colour\n * types, with room for a 12px icon either side. Under Signal the label is\n * mono and the coloured types carry a hairline, as Peek has it. The icon\n * slots centre their icon (Ship's addition \u2014 an icon beside text otherwise\n * rides on the baseline).\n *\n * The label is the `chip` type token, 11px / 500; it is a plain class here,\n * never merged, so tailwind-merge cannot drop it.\n */\nexport type ChipType = 'neutral' | 'brand' | 'info' | 'warning' | 'success' | 'error'\n\nexport interface ChipProps {\n type?: ChipType\n label?: string\n leadingIcon?: ReactNode\n trailingIcon?: ReactNode\n className?: string\n}\n\nconst typeStyles: Record<ChipType, string> = {\n neutral: 'bg-bg-inset text-text-primary',\n brand: 'bg-accent-muted text-accent-primary signal:border signal:border-[rgba(86,200,255,0.3)]',\n info: 'bg-info-muted text-info-default signal:border signal:border-[rgba(86,200,255,0.3)]',\n 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)]',\n success: 'bg-success-muted text-success-default signal:border signal:border-[rgba(63,222,140,0.3)]',\n error: 'bg-error-muted text-error-default signal:border signal:border-[rgba(255,107,107,0.3)]',\n}\n\nexport function Chip({ type = 'neutral', label, leadingIcon, trailingIcon, className }: ChipProps) {\n return (\n <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)}>\n {leadingIcon && <span className=\"flex size-3 shrink-0 items-center justify-center\">{leadingIcon}</span>}\n {label && (\n <span className=\"text-chip whitespace-nowrap signal:font-mono signal:text-[10px] signal:font-semibold signal:tracking-[0.02em] signal:tabular-nums\">{label}</span>\n )}\n {trailingIcon && <span className=\"flex size-3 shrink-0 items-center justify-center\">{trailingIcon}</span>}\n </div>\n )\n}\n", "import { useCallback, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\n\n/**\n * Peek's Tooltip and WithTooltip (2026-08-28), verbatim, in one file.\n *\n * `Tooltip` is the surface: a 30px elevated pill with a caption. `WithTooltip`\n * wraps a trigger and portals the surface above or below it on hover, fixed\n * to the viewport and kept 8px inside it. The wrapper is `inline-flex` and\n * shrinks nothing \u2014 wrap a control, never a block (a form inside it\n * collapses to its content width; Ship learnt that).\n */\nexport interface TooltipProps {\n label: string\n className?: string\n}\n\nexport function Tooltip({ label, className }: TooltipProps) {\n return (\n <div role=\"tooltip\" className={cn('bg-bg-elevated border border-border-default rounded-lg h-[30px] flex items-center justify-center px-2 shadow-lg', className)}>\n <span className=\"text-caption text-text-primary whitespace-nowrap\">{label}</span>\n </div>\n )\n}\n\nexport interface WithTooltipProps {\n label: string\n placement?: 'top' | 'bottom'\n children: ReactNode\n}\n\nconst GAP = 6\nconst VIEWPORT_PAD = 8\n\nexport function WithTooltip({ label, placement = 'top', children }: WithTooltipProps) {\n const [show, setShow] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const tooltipRef = useRef<HTMLDivElement>(null)\n const [style, setStyle] = useState<CSSProperties>({ position: 'fixed', zIndex: 9999, pointerEvents: 'none', visibility: 'hidden' })\n\n const reposition = useCallback(() => {\n const trigger = ref.current\n const tip = tooltipRef.current\n if (!trigger || !tip) return\n const triggerRect = trigger.getBoundingClientRect()\n const tipRect = tip.getBoundingClientRect()\n const top = placement === 'bottom' ? triggerRect.bottom + GAP : triggerRect.top - tipRect.height - GAP\n let left = triggerRect.left + triggerRect.width / 2 - tipRect.width / 2\n left = Math.max(VIEWPORT_PAD, Math.min(left, window.innerWidth - tipRect.width - VIEWPORT_PAD))\n setStyle({ position: 'fixed', top, left, zIndex: 9999, pointerEvents: 'none', visibility: 'visible' })\n }, [placement])\n\n useLayoutEffect(() => {\n if (show) reposition()\n }, [show, reposition])\n\n return (\n <div ref={ref} className=\"inline-flex shrink-0\" onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>\n {children}\n {show &&\n createPortal(\n <div ref={tooltipRef} style={style}>\n <Tooltip label={label} />\n </div>,\n document.body,\n )}\n </div>\n )\n}\n", "import type { ButtonHTMLAttributes, ReactNode } from 'react'\nimport { cn } from './cn'\nimport { WithTooltip } from './Tooltip'\n\n/**\n * Peek's IconButton (2026-08-28), verbatim: a square 4px-padded button\n * around a 16px icon, 8px radius, three variants, an optional tooltip that\n * portals above or below. Plus `type=\"button\"` by default (Ship's addition),\n * so it never submits a form by accident.\n */\nexport type IconButtonVariant = 'muted' | 'outlined' | 'primary'\n\nexport interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: IconButtonVariant\n tooltip?: string\n tooltipPlacement?: 'top' | 'bottom'\n /** The icon: 16px, stroke 1.5. */\n children: ReactNode\n}\n\nexport function IconButton({\n variant = 'muted',\n className,\n children,\n disabled,\n tooltip,\n tooltipPlacement,\n type = 'button',\n ...props\n}: IconButtonProps) {\n const button = (\n <button\n type={type}\n className={cn(\n 'flex items-center justify-center p-1 rounded-lg transition-colors shrink-0 cursor-pointer',\n !disabled && variant === 'primary' && 'bg-accent-primary hover:bg-accent-hover text-text-inverse',\n !disabled && variant === 'muted' && 'text-text-secondary hover:bg-bg-hover hover:text-text-primary',\n !disabled && variant === 'outlined' && 'border border-border-default hover:bg-bg-hover text-text-secondary',\n disabled && variant === 'primary' && 'bg-bg-disabled text-text-disabled',\n disabled && variant === 'muted' && 'text-text-disabled',\n disabled && variant === 'outlined' && 'border border-border-default text-text-disabled',\n disabled && 'pointer-events-none cursor-not-allowed',\n className,\n )}\n disabled={disabled}\n {...props}\n >\n {children}\n </button>\n )\n\n if (tooltip) {\n return (\n <WithTooltip label={tooltip} placement={tooltipPlacement}>\n {button}\n </WithTooltip>\n )\n }\n return button\n}\n", "import type { ReactNode } from 'react'\n\n/**\n * Peek's Field (2026-08-28), verbatim: a label over a control, 8px apart,\n * with a red asterisk when required. The label is the `input-label` type\n * token as a plain class, never merged.\n */\nexport interface FieldProps {\n label: string\n required?: boolean\n children: ReactNode\n}\n\nexport function Field({ label, required = false, children }: FieldProps) {\n return (\n <div className=\"flex flex-col gap-2\">\n <label className={`text-input-label text-text-primary${required ? ' flex items-center' : ''}`}>\n {label}\n {required && <span className=\"text-error-default ml-0.5\">*</span>}\n </label>\n {children}\n </div>\n )\n}\n", "import { IconCheck, IconChevronDown } from '@tabler/icons-react'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from './cn'\n\n/**\n * Peek's Select (2026-08-28), verbatim, plus what Ship added: an option may\n * carry a `leading` node \u2014 an avatar beside a person's name \u2014 shown in the\n * trigger and in the list.\n *\n * A button that opens a portalled listbox under itself: arrows move, Home\n * and End jump, Enter and Space pick, Escape closes and returns focus to the\n * trigger, Tab closes, a click outside closes, a scroll or resize closes\n * (the list is fixed to where the trigger was). Two sizes; `disabled`\n * explains nothing by itself \u2014 wrap it in a tooltip that does.\n */\nexport interface SelectOption {\n value: string\n label: string\n /** Drawn before the label, 16px \u2014 an Avatar, an icon. */\n leading?: ReactNode\n}\n\nexport interface SelectProps {\n value: string\n onChange: (value: string) => void\n options: SelectOption[]\n size?: 'default' | 'small'\n ariaLabel?: string\n placeholder?: string\n disabled?: boolean\n className?: string\n}\n\nexport function Select({ value, onChange, options, size = 'default', ariaLabel, placeholder = 'Select\u2026', disabled, className }: SelectProps) {\n const id = useId()\n const triggerRef = useRef<HTMLButtonElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n const [rect, setRect] = useState<DOMRect | null>(null)\n const open = rect !== null\n\n const selectedIndex = useMemo(() => Math.max(0, options.findIndex((o) => o.value === value)), [options, value])\n const [activeIndex, setActiveIndex] = useState(selectedIndex)\n\n const close = useCallback((refocus: boolean) => {\n setRect(null)\n if (refocus) triggerRef.current?.focus()\n }, [])\n\n const openMenu = useCallback(() => {\n setActiveIndex(selectedIndex)\n setRect(triggerRef.current?.getBoundingClientRect() ?? null)\n }, [selectedIndex])\n\n // Outside click and Escape \u2014 the two exits every menu has. `mousedown`\n // rather than `click`, so a press that starts outside dismisses at once.\n useEffect(() => {\n if (!open) return\n const onDown = (e: MouseEvent) => {\n if (menuRef.current?.contains(e.target as Node)) return\n if (triggerRef.current?.contains(e.target as Node)) return\n setRect(null)\n }\n const onMove = () => setRect(null)\n document.addEventListener('mousedown', onDown)\n window.addEventListener('resize', onMove)\n window.addEventListener('scroll', onMove, true)\n return () => {\n document.removeEventListener('mousedown', onDown)\n window.removeEventListener('resize', onMove)\n window.removeEventListener('scroll', onMove, true)\n }\n }, [open])\n\n const pick = (index: number) => {\n const option = options[index]\n if (!option) return\n onChange(option.value)\n close(true)\n }\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (!open) {\n if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {\n e.preventDefault()\n openMenu()\n }\n return\n }\n switch (e.key) {\n case 'Escape':\n e.preventDefault()\n close(true)\n break\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex((i) => Math.min(options.length - 1, i + 1))\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex((i) => Math.max(0, i - 1))\n break\n case 'Home':\n e.preventDefault()\n setActiveIndex(0)\n break\n case 'End':\n e.preventDefault()\n setActiveIndex(options.length - 1)\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n pick(activeIndex)\n break\n case 'Tab':\n close(false)\n break\n }\n }\n\n const selected = options.find((o) => o.value === value)\n\n return (\n <>\n <button\n ref={triggerRef}\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n onClick={() => (open ? close(false) : openMenu())}\n onKeyDown={onKeyDown}\n className={cn(\n 'flex w-full items-center justify-between gap-2 rounded-lg border bg-bg-inset text-left',\n 'border-border-default text-text-primary outline-none transition-colors',\n 'hover:border-border-strong disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'focus-visible:border-border-focus aria-expanded:border-border-focus',\n 'signal:transition-shadow signal:focus-visible:shadow-focus-ring',\n size === 'default' && 'px-3 py-2 text-[14px] leading-[1.4] font-normal',\n size === 'small' && 'h-6 px-2 text-[12px] leading-[1.4] font-normal',\n className,\n )}\n >\n <span className={cn('flex min-w-0 items-center gap-2', !selected && 'text-text-muted')}>\n {selected?.leading && <span className=\"flex shrink-0 items-center\">{selected.leading}</span>}\n <span className=\"truncate\">{selected?.label ?? placeholder}</span>\n </span>\n <IconChevronDown size={size === 'small' ? 14 : 16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />\n </button>\n\n {open &&\n rect &&\n createPortal(\n <div\n ref={menuRef}\n role=\"listbox\"\n aria-activedescendant={`${id}-${activeIndex}`}\n tabIndex={-1}\n style={{ top: rect.bottom + 4, left: rect.left, minWidth: rect.width }}\n className=\"fixed z-50 max-h-72 overflow-y-auto rounded-lg border border-border-default bg-bg-elevated p-1 shadow-lg\"\n >\n {options.map((option, index) => (\n <div\n key={option.value}\n id={`${id}-${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(e) => {\n // The document-level dismiss also listens on mousedown.\n e.preventDefault()\n pick(index)\n }}\n className={cn(\n 'flex h-9 cursor-pointer items-center justify-between gap-2 rounded-lg px-3 transition-colors',\n 'text-[14px] font-normal leading-[1.4] text-text-primary',\n index === activeIndex && 'bg-bg-hover',\n option.value === value && 'font-medium',\n )}\n >\n <span className=\"flex min-w-0 items-center gap-2\">\n {option.leading && <span className=\"flex shrink-0 items-center\">{option.leading}</span>}\n <span className=\"truncate\">{option.label}</span>\n </span>\n {option.value === value && <IconCheck size={16} stroke={1.5} className=\"shrink-0 text-text-secondary\" />}\n </div>\n ))}\n </div>,\n document.body,\n )}\n </>\n )\n}\n", "import { forwardRef, type InputHTMLAttributes } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's TextInput (2026-08-28): the inset field with a 8px radius, 14px\n * text, the focus border in every theme (Katerina, 2026-08-28: Ship's inputs\n * focus like Signal's) \u2014 and Signal's glow on top.\n * Plus a disabled look (Ship's addition): the disabled surface and text,\n * no pointer.\n */\nexport type TextInputProps = InputHTMLAttributes<HTMLInputElement>\n\nexport const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput({ className, type = 'text', ...props }, ref) {\n return (\n <input\n ref={ref}\n type={type}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2',\n 'text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted',\n 'outline-none transition-colors',\n 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'signal:transition-shadow signal:focus:shadow-focus-ring',\n className,\n )}\n {...props}\n />\n )\n})\n", "import { forwardRef, type TextareaHTMLAttributes } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Textarea (2026-08-28), verbatim: TextInput's look on a textarea\n * that does not resize. Plus a disabled look (Ship's addition).\n */\nexport type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(function Textarea({ className, ...props }, ref) {\n return (\n <textarea\n ref={ref}\n className={cn(\n 'bg-bg-inset border border-border-default focus:border-border-focus rounded-lg px-3 py-2',\n 'text-[14px] leading-[1.4] font-normal text-text-primary placeholder:text-text-muted',\n 'resize-none outline-none transition-colors',\n 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',\n 'signal:transition-shadow signal:focus:shadow-focus-ring',\n className,\n )}\n {...props}\n />\n )\n})\n", "import { useEffect, type ReactNode } from 'react'\nimport { createPortal } from 'react-dom'\nimport { IconX } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\n\n/**\n * Peek's DialogShell (2026-08-28), verbatim: the portal, the backdrop, the\n * 502px card and its chrome \u2014 a 48px header with the title and a close\n * button, a body, a 48px footer for the buttons. A dialog is just what goes\n * in the three slots. Plus what Ship added: Escape closes it, and the card\n * says what it is to assistive tech.\n */\nexport interface DialogShellProps {\n title: string\n onClose: () => void\n footer: ReactNode\n children: ReactNode\n /** Extra classes on the body (e.g. `flex flex-col gap-6`, or a max height with `overflow-y-auto`). */\n bodyClassName?: string\n width?: number\n}\n\nexport function DialogShell({ title, onClose, footer, children, bodyClassName, width = 502 }: DialogShellProps) {\n useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose()\n }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [onClose])\n\n return createPortal(\n <>\n {/* Backdrop */}\n <div className=\"fixed inset-0 z-40 bg-black/50\" onClick={onClose} />\n\n {/* Dialog */}\n <div className=\"fixed inset-0 z-50 flex items-center justify-center pointer-events-none\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n className=\"bg-bg-elevated border border-border-subtle rounded-lg shadow-lg pointer-events-auto flex flex-col overflow-hidden\"\n style={{ width }}\n >\n {/* Header */}\n <div className=\"h-12 flex items-center justify-between pl-5 pr-4 border-b border-border-subtle shrink-0\">\n <span className=\"text-h4 text-text-primary\">{title}</span>\n <IconButton tooltip=\"Close\" aria-label=\"Close\" onClick={onClose}>\n <IconX size={16} stroke={1.5} />\n </IconButton>\n </div>\n\n {/* Body */}\n <div className={cn('pl-5 pr-4 py-4 border-b border-border-subtle', bodyClassName)}>{children}</div>\n\n {/* Footer */}\n <div className=\"h-12 flex items-center justify-end gap-2 pl-5 pr-4 shrink-0\">{footer}</div>\n </div>\n </div>\n </>,\n document.body,\n )\n}\n", "import { cn } from './cn'\n\n/**\n * Peek's Divider (2026-08-28): a hairline in `border-subtle`, inset 12px each\n * side. Plus what Ship added: `orientation=\"vertical\"` \u2014 the same hairline\n * standing up, stretching to its row's height, no inset \u2014 and the separator\n * role for assistive tech.\n */\nexport interface DividerProps {\n orientation?: 'horizontal' | 'vertical'\n className?: string\n}\n\nexport function Divider({ orientation = 'horizontal', className }: DividerProps) {\n return (\n <div\n role=\"separator\"\n aria-orientation={orientation}\n className={cn('bg-border-subtle', orientation === 'horizontal' ? 'h-px mx-3' : 'w-px self-stretch shrink-0', className)}\n />\n )\n}\n", "import type { ReactNode } from 'react'\nimport { IconMessage2 } from '@tabler/icons-react'\nimport { cn } from './cn'\n\n/**\n * Peek's EmptyState (2026-08-28), verbatim: a 16px icon over a centred line\n * of secondary text. The message is the caller's \u2014 a shared component has no\n * words of its own for what is missing.\n */\nexport interface EmptyStateProps {\n /** 16px, stroke 1.5. A speech bubble when absent. */\n icon?: ReactNode\n message: string\n className?: string\n}\n\nexport function EmptyState({ icon, message, className }: EmptyStateProps) {\n return (\n <div className={cn('flex flex-col gap-2 items-center', className)}>\n <span className=\"text-text-secondary\">{icon ?? <IconMessage2 size={16} stroke={1.5} />}</span>\n <p className=\"text-body-2 text-text-secondary text-center\">{message}</p>\n </div>\n )\n}\n", "import type { CSSProperties } from 'react'\nimport { cn } from './cn'\n\n/**\n * Peek's Skeleton (2026-08-28), the generic parts only: the bar, a row\n * shaped like a list row, and a list of them. Peek's placeholders shaped\n * like a conversation card or a huddle card know what those are and stay\n * in Peek, built from the bar.\n *\n * A list reveals after 150ms (`animate-skeleton-in`, in the preset) so a\n * fast load never flashes a skeleton \u2014 Peek's rule.\n */\nexport function SkeletonBar({ className, style }: { className?: string; style?: CSSProperties }) {\n return <div className={cn('bg-bg-inset rounded-sm animate-pulse', className)} style={style} />\n}\n\nconst ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160]\n\n/** A 32px row: a 16px square and a bar, like a row with a face and a name. */\nexport function SkeletonRow({ barWidth = 130 }: { barWidth?: number }) {\n return (\n <div className=\"flex items-center gap-2 px-2 h-[32px] rounded-lg\">\n <SkeletonBar className=\"w-4 h-4 shrink-0\" />\n <SkeletonBar className=\"h-3.5\" style={{ width: barWidth }} />\n </div>\n )\n}\n\n/** Rows of varied widths, so the placeholder does not read as a pattern. */\nexport function SkeletonList({ rows = 8, className }: { rows?: number; className?: string }) {\n return (\n <div className={cn('flex flex-col gap-0.5 animate-skeleton-in', className)} aria-hidden>\n {Array.from({ length: rows }, (_, i) => (\n <SkeletonRow key={i} barWidth={ROW_BAR_WIDTHS[i % ROW_BAR_WIDTHS.length]} />\n ))}\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["jsx", "jsxs", "useState", "jsx", "jsxs", "useState", "jsx", "jsx", "jsxs", "useCallback", "useRef", "useState", "createPortal", "jsx", "jsxs", "useRef", "useState", "useCallback", "createPortal", "jsx", "TextInput", "forwardRef", "jsx", "forwardRef", "Textarea", "useEffect", "createPortal", "Fragment", "jsx", "jsxs", "useEffect", "createPortal", "jsx", "jsx", "jsxs", "jsx", "jsxs"]
7
+ }