@nebutra/ui 0.1.0 → 0.2.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 (39) hide show
  1. package/dist/components/index.d.ts +380 -0
  2. package/dist/components/index.js +2398 -0
  3. package/dist/components/index.js.map +1 -0
  4. package/dist/hooks/index.d.ts +106 -0
  5. package/dist/hooks/index.js +182 -0
  6. package/dist/hooks/index.js.map +1 -0
  7. package/dist/icons/index.d.ts +3 -0
  8. package/dist/icons/index.js +5 -0
  9. package/dist/icons/index.js.map +1 -0
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.js +31 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/layout/index.d.ts +296 -0
  14. package/dist/layout/index.js +1303 -0
  15. package/dist/layout/index.js.map +1 -0
  16. package/dist/layouts/index.d.ts +161 -0
  17. package/dist/layouts/index.js +611 -0
  18. package/dist/layouts/index.js.map +1 -0
  19. package/dist/patterns/index.d.ts +363 -0
  20. package/dist/patterns/index.js +3004 -0
  21. package/dist/patterns/index.js.map +1 -0
  22. package/dist/primitives/index.d.ts +9588 -0
  23. package/dist/primitives/index.js +26779 -0
  24. package/dist/primitives/index.js.map +1 -0
  25. package/dist/styles/brand-override.css +61 -0
  26. package/dist/tailwind.preset.d.ts +251 -0
  27. package/dist/tailwind.preset.js +273 -0
  28. package/dist/tailwind.preset.js.map +1 -0
  29. package/dist/theme/index.d.ts +150 -0
  30. package/dist/theme/index.js +28 -0
  31. package/dist/theme/index.js.map +1 -0
  32. package/dist/typography/fonts.css +110 -0
  33. package/dist/typography/index.d.ts +425 -0
  34. package/dist/typography/index.js +374 -0
  35. package/dist/typography/index.js.map +1 -0
  36. package/dist/utils/index.d.ts +61 -0
  37. package/dist/utils/index.js +55 -0
  38. package/dist/utils/index.js.map +1 -0
  39. package/package.json +16 -6
@@ -0,0 +1,1303 @@
1
+ "use client";
2
+ import { Cross, Warning, External, Menu } from '@nebutra/icons';
3
+ import * as React from 'react';
4
+ import { createContext, useState, useEffect, useMemo, useCallback } from 'react';
5
+ import { Dialog } from '@base-ui/react/dialog';
6
+ import { cva } from 'class-variance-authority';
7
+ import { colors } from '@nebutra/brand';
8
+ import { clsx } from 'clsx';
9
+ import { twMerge } from 'tailwind-merge';
10
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
+ import Link from 'next/link';
12
+
13
+ // src/layout/app-shell.tsx
14
+ ({
15
+ // Nebutra Blue (云毓蓝) — technology & trust — VI: #0033FE
16
+ blue50: colors.primary[50],
17
+ blue100: colors.primary[100],
18
+ blue200: colors.primary[200],
19
+ blue300: colors.primary[300],
20
+ blue400: colors.primary[400],
21
+ blue500: colors.primary[500],
22
+ // brand primary
23
+ blue600: colors.primary[600],
24
+ blue700: colors.primary[700],
25
+ blue800: colors.primary[800],
26
+ blue900: colors.primary[900],
27
+ blue950: colors.primary[950],
28
+ // Nebutra Cyan (云毓青) — data flow & intelligence — VI: #0BF1C3
29
+ cyan50: colors.accent[50],
30
+ cyan100: colors.accent[100],
31
+ cyan200: colors.accent[200],
32
+ cyan300: colors.accent[300],
33
+ cyan400: colors.accent[400],
34
+ cyan500: colors.accent[500],
35
+ // brand secondary
36
+ cyan600: colors.accent[600],
37
+ cyan700: colors.accent[700],
38
+ cyan800: colors.accent[800],
39
+ cyan900: colors.accent[900],
40
+ cyan950: colors.accent[950],
41
+ // Neutral (blue-undertone gray scale)
42
+ neutral50: colors.neutral[50],
43
+ neutral100: colors.neutral[100],
44
+ neutral200: colors.neutral[200],
45
+ neutral300: colors.neutral[300],
46
+ neutral400: colors.neutral[400],
47
+ neutral500: colors.neutral[500],
48
+ neutral600: colors.neutral[600],
49
+ neutral700: colors.neutral[700],
50
+ neutral800: colors.neutral[800],
51
+ neutral900: colors.neutral[900],
52
+ neutral950: colors.neutral[950],
53
+ white: colors.white,
54
+ black: colors.black
55
+ });
56
+ ({
57
+ /** Hero sections, primary CTA buttons, logo fills — VI signature gradient */
58
+ primary: colors.gradient.primary,
59
+ /** Hover states, secondary gradient elements */
60
+ reverse: colors.gradient.primaryReverse,
61
+ /** Vertical layout dividers, page section separators */
62
+ vertical: colors.gradient.primaryVertical,
63
+ /** Background halos, focus glow effects, radial emphasis */
64
+ radial: colors.gradient.primaryRadial});
65
+ var primitiveSpacing = {
66
+ 1: 4,
67
+ 2: 8,
68
+ 3: 12,
69
+ 4: 16,
70
+ 5: 20,
71
+ 6: 24,
72
+ 8: 32,
73
+ 10: 40,
74
+ 12: 48,
75
+ 14: 56,
76
+ 16: 64,
77
+ 24: 96
78
+ };
79
+ var primitiveSizing = {
80
+ sm: 32,
81
+ lg: 48,
82
+ "2xl": 80
83
+ };
84
+ var primitiveRadius = {
85
+ md: 6,
86
+ // Geist default — slightly more rounded than 4px
87
+ lg: 8,
88
+ "2xl": 16};
89
+ var primitiveFontSize = {
90
+ xs: 12,
91
+ sm: 14,
92
+ base: 16,
93
+ lg: 18};
94
+ var primitiveFontWeight = {
95
+ semibold: 600};
96
+ var primitiveTransition = {
97
+ duration: {
98
+ // standard state transitions (default)
99
+ reveal: 300},
100
+ easing: {
101
+ inOut: "cubic-bezier(0.4, 0, 0.2, 1)"
102
+ }
103
+ };
104
+
105
+ // src/tokens/components/sheet.ts
106
+ var sheetTokens = {
107
+ inset: primitiveSpacing[3],
108
+ sideWidth: primitiveSizing["2xl"] * 6 + primitiveSpacing[8],
109
+ // 512px
110
+ edgeHeight: primitiveSizing["2xl"] * 4,
111
+ // 320px
112
+ paddingX: primitiveSpacing[6],
113
+ paddingY: primitiveSpacing[5],
114
+ bodyPaddingY: primitiveSpacing[4],
115
+ gap: primitiveSpacing[4],
116
+ headerGap: primitiveSpacing[1] + 2,
117
+ footerGap: primitiveSpacing[2],
118
+ radius: primitiveRadius["2xl"],
119
+ close: {
120
+ size: primitiveSizing.sm,
121
+ iconSize: primitiveFontSize.base,
122
+ offset: primitiveSpacing[4],
123
+ radius: primitiveRadius.md
124
+ },
125
+ overlay: {
126
+ background: "hsl(var(--background) / 0.68)",
127
+ blur: "blur(6px)"
128
+ },
129
+ surface: {
130
+ background: "hsl(var(--popover))",
131
+ shadow: "var(--elevation-xl)"
132
+ },
133
+ motion: {
134
+ duration: primitiveTransition.duration.reveal,
135
+ easing: primitiveTransition.easing.inOut
136
+ }
137
+ };
138
+ function cn(...inputs) {
139
+ return twMerge(clsx(inputs));
140
+ }
141
+ function getSheetStyle(style) {
142
+ return {
143
+ "--sheet-inset": `${sheetTokens.inset}px`,
144
+ "--sheet-side-width": `${sheetTokens.sideWidth}px`,
145
+ "--sheet-edge-height": `${sheetTokens.edgeHeight}px`,
146
+ "--sheet-padding-x": `${sheetTokens.paddingX}px`,
147
+ "--sheet-padding-y": `${sheetTokens.paddingY}px`,
148
+ "--sheet-body-padding-y": `${sheetTokens.bodyPaddingY}px`,
149
+ "--sheet-gap": `${sheetTokens.gap}px`,
150
+ "--sheet-header-gap": `${sheetTokens.headerGap}px`,
151
+ "--sheet-footer-gap": `${sheetTokens.footerGap}px`,
152
+ "--sheet-radius": `${sheetTokens.radius}px`,
153
+ "--sheet-close-size": `${sheetTokens.close.size}px`,
154
+ "--sheet-close-icon-size": `${sheetTokens.close.iconSize}px`,
155
+ "--sheet-close-offset": `${sheetTokens.close.offset}px`,
156
+ "--sheet-close-radius": `${sheetTokens.close.radius}px`,
157
+ "--sheet-overlay-background": sheetTokens.overlay.background,
158
+ "--sheet-overlay-blur": sheetTokens.overlay.blur,
159
+ "--sheet-background": sheetTokens.surface.background,
160
+ "--sheet-shadow": sheetTokens.surface.shadow,
161
+ "--sheet-duration": `${sheetTokens.motion.duration}ms`,
162
+ "--sheet-easing": sheetTokens.motion.easing,
163
+ ...style
164
+ };
165
+ }
166
+ function Sheet({ modal = "trap-focus", disablePointerDismissal = true, ...props }) {
167
+ return /* @__PURE__ */ jsx(
168
+ Dialog.Root,
169
+ {
170
+ "data-slot": "sheet",
171
+ modal,
172
+ disablePointerDismissal,
173
+ ...props
174
+ }
175
+ );
176
+ }
177
+ Sheet.displayName = "Sheet";
178
+ var SheetTrigger = React.forwardRef(
179
+ ({ asChild, children, ...props }, ref) => {
180
+ if (asChild && React.isValidElement(children)) {
181
+ return /* @__PURE__ */ jsx(
182
+ Dialog.Trigger,
183
+ {
184
+ ref,
185
+ "data-slot": "sheet-trigger",
186
+ ...props,
187
+ render: children
188
+ }
189
+ );
190
+ }
191
+ return /* @__PURE__ */ jsx(Dialog.Trigger, { ref, "data-slot": "sheet-trigger", ...props, children });
192
+ }
193
+ );
194
+ SheetTrigger.displayName = "SheetTrigger";
195
+ var SheetClose = React.forwardRef(
196
+ ({ asChild, children, ...props }, ref) => {
197
+ if (asChild && React.isValidElement(children)) {
198
+ return /* @__PURE__ */ jsx(
199
+ Dialog.Close,
200
+ {
201
+ ref,
202
+ "data-slot": "sheet-close",
203
+ ...props,
204
+ render: children
205
+ }
206
+ );
207
+ }
208
+ return /* @__PURE__ */ jsx(Dialog.Close, { ref, "data-slot": "sheet-close", ...props, children });
209
+ }
210
+ );
211
+ SheetClose.displayName = "SheetClose";
212
+ function SheetPortal(props) {
213
+ return /* @__PURE__ */ jsx(Dialog.Portal, { "data-slot": "sheet-portal", ...props });
214
+ }
215
+ SheetPortal.displayName = "SheetPortal";
216
+ var SheetOverlay = React.forwardRef(
217
+ ({ className, style, ...props }, ref) => /* @__PURE__ */ jsx(
218
+ Dialog.Backdrop,
219
+ {
220
+ ref,
221
+ "data-slot": "sheet-overlay",
222
+ className: cn(
223
+ "fixed inset-0 z-50 bg-[var(--sheet-overlay-background)] backdrop-blur-[var(--sheet-overlay-blur)]",
224
+ "transition-[opacity,backdrop-filter,display] duration-[var(--sheet-duration)] ease-[var(--sheet-easing)]",
225
+ "data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none",
226
+ className
227
+ ),
228
+ style: getSheetStyle(style),
229
+ ...props
230
+ }
231
+ )
232
+ );
233
+ SheetOverlay.displayName = "SheetOverlay";
234
+ var sheetVariants = cva(
235
+ [
236
+ "fixed z-50 flex flex-col overflow-hidden border border-border bg-[var(--sheet-background)] text-foreground shadow-[var(--sheet-shadow)] outline-none",
237
+ "transition-[opacity,transform,display] duration-[var(--sheet-duration)] ease-[var(--sheet-easing)]",
238
+ "data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none"
239
+ ].join(" "),
240
+ {
241
+ variants: {
242
+ side: {
243
+ top: "inset-x-[var(--sheet-inset)] top-[var(--sheet-inset)] max-h-[min(var(--sheet-edge-height),calc(100dvh_-_var(--sheet-inset)_-_var(--sheet-inset)))] rounded-[var(--sheet-radius)] data-ending-style:-translate-y-full data-starting-style:-translate-y-full",
244
+ bottom: "inset-x-[var(--sheet-inset)] bottom-[var(--sheet-inset)] max-h-[min(var(--sheet-edge-height),calc(100dvh_-_var(--sheet-inset)_-_var(--sheet-inset)))] rounded-[var(--sheet-radius)] data-ending-style:translate-y-full data-starting-style:translate-y-full",
245
+ left: "inset-y-[var(--sheet-inset)] left-[var(--sheet-inset)] h-[calc(100dvh_-_var(--sheet-inset)_-_var(--sheet-inset))] w-[min(var(--sheet-side-width),calc(100vw_-_var(--sheet-inset)_-_var(--sheet-inset)))] rounded-[var(--sheet-radius)] data-ending-style:-translate-x-full data-starting-style:-translate-x-full",
246
+ right: "inset-y-[var(--sheet-inset)] right-[var(--sheet-inset)] h-[calc(100dvh_-_var(--sheet-inset)_-_var(--sheet-inset))] w-[min(var(--sheet-side-width),calc(100vw_-_var(--sheet-inset)_-_var(--sheet-inset)))] rounded-[var(--sheet-radius)] data-ending-style:translate-x-full data-starting-style:translate-x-full"
247
+ }
248
+ },
249
+ defaultVariants: {
250
+ side: "right"
251
+ }
252
+ }
253
+ );
254
+ var SheetContent = React.forwardRef(
255
+ ({
256
+ side = "right",
257
+ className,
258
+ children,
259
+ overlay = true,
260
+ noOverlay = false,
261
+ showClose,
262
+ close,
263
+ style,
264
+ ...props
265
+ }, ref) => {
266
+ const shouldRenderClose = close ?? showClose ?? true;
267
+ const shouldRenderOverlay = overlay && !noOverlay;
268
+ return /* @__PURE__ */ jsxs(SheetPortal, { children: [
269
+ shouldRenderOverlay ? /* @__PURE__ */ jsx(SheetOverlay, {}) : null,
270
+ /* @__PURE__ */ jsxs(
271
+ Dialog.Popup,
272
+ {
273
+ ref,
274
+ "data-slot": "sheet-content",
275
+ className: cn(sheetVariants({ side }), className),
276
+ style: getSheetStyle(style),
277
+ ...props,
278
+ children: [
279
+ children,
280
+ shouldRenderClose ? /* @__PURE__ */ jsxs(
281
+ SheetClose,
282
+ {
283
+ "aria-label": "Close",
284
+ className: cn(
285
+ "absolute right-[var(--sheet-close-offset)] top-[var(--sheet-close-offset)] inline-flex size-[var(--sheet-close-size)] items-center justify-center rounded-[var(--sheet-close-radius)] text-muted-foreground",
286
+ "transition-[background-color,color,box-shadow] duration-[var(--sheet-duration)] ease-[var(--sheet-easing)] hover:bg-muted hover:text-foreground",
287
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none motion-reduce:transition-none"
288
+ ),
289
+ children: [
290
+ /* @__PURE__ */ jsx(Cross, { "aria-hidden": "true", className: "size-[var(--sheet-close-icon-size)]" }),
291
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
292
+ ]
293
+ }
294
+ ) : null
295
+ ]
296
+ }
297
+ )
298
+ ] });
299
+ }
300
+ );
301
+ SheetContent.displayName = "SheetContent";
302
+ var SheetHeader = React.forwardRef(
303
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
304
+ "div",
305
+ {
306
+ ref,
307
+ "data-slot": "sheet-header",
308
+ className: cn(
309
+ "grid gap-[var(--sheet-header-gap)] border-b border-border px-[var(--sheet-padding-x)] py-[var(--sheet-padding-y)] pr-[calc(var(--sheet-padding-x)_+_var(--sheet-close-size))] text-left",
310
+ className
311
+ ),
312
+ ...props
313
+ }
314
+ )
315
+ );
316
+ SheetHeader.displayName = "SheetHeader";
317
+ var SheetBody = React.forwardRef(
318
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
319
+ "div",
320
+ {
321
+ ref,
322
+ "data-slot": "sheet-body",
323
+ className: cn(
324
+ "min-h-0 flex-1 overflow-y-auto overscroll-contain px-[var(--sheet-padding-x)] py-[var(--sheet-body-padding-y)]",
325
+ className
326
+ ),
327
+ ...props
328
+ }
329
+ )
330
+ );
331
+ SheetBody.displayName = "SheetBody";
332
+ var SheetFooter = React.forwardRef(
333
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
334
+ "div",
335
+ {
336
+ ref,
337
+ "data-slot": "sheet-footer",
338
+ className: cn(
339
+ "mt-auto flex flex-col-reverse gap-[var(--sheet-footer-gap)] border-t border-border px-[var(--sheet-padding-x)] py-[var(--sheet-padding-y)] sm:flex-row sm:justify-end",
340
+ className
341
+ ),
342
+ ...props
343
+ }
344
+ )
345
+ );
346
+ SheetFooter.displayName = "SheetFooter";
347
+ var SheetTitle = React.forwardRef(
348
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
349
+ Dialog.Title,
350
+ {
351
+ ref,
352
+ "data-slot": "sheet-title",
353
+ className: cn("font-semibold text-base leading-6 text-foreground", className),
354
+ ...props
355
+ }
356
+ )
357
+ );
358
+ SheetTitle.displayName = "SheetTitle";
359
+ var SheetDescription = React.forwardRef(
360
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
361
+ Dialog.Description,
362
+ {
363
+ ref,
364
+ "data-slot": "sheet-description",
365
+ className: cn("text-muted-foreground text-sm leading-5", className),
366
+ ...props
367
+ }
368
+ )
369
+ );
370
+ SheetDescription.displayName = "SheetDescription";
371
+ var DEFAULT_SIDEBAR_WIDTH = 224;
372
+ var DEFAULT_COLLAPSED_WIDTH = 52;
373
+ var DEFAULT_HEADER_HEIGHT = 48;
374
+ function AppShell({
375
+ sidebar,
376
+ header,
377
+ children,
378
+ sidebarWidth = DEFAULT_SIDEBAR_WIDTH,
379
+ sidebarCollapsedWidth = DEFAULT_COLLAPSED_WIDTH,
380
+ collapsed,
381
+ defaultCollapsed = false,
382
+ onCollapsedChange,
383
+ headerHeight = DEFAULT_HEADER_HEIGHT,
384
+ contentClassName,
385
+ className
386
+ }) {
387
+ const isControlled = collapsed !== void 0;
388
+ const [internalCollapsed] = React.useState(defaultCollapsed);
389
+ const isCollapsed = isControlled ? collapsed : internalCollapsed;
390
+ const lastReportedRef = React.useRef(isCollapsed);
391
+ React.useEffect(() => {
392
+ if (lastReportedRef.current !== isCollapsed) {
393
+ lastReportedRef.current = isCollapsed;
394
+ onCollapsedChange?.(isCollapsed);
395
+ }
396
+ }, [isCollapsed, onCollapsedChange]);
397
+ const [mobileOpen, setMobileOpen] = React.useState(false);
398
+ const railWidth = isCollapsed ? sidebarCollapsedWidth : sidebarWidth;
399
+ return /* @__PURE__ */ jsxs(
400
+ "div",
401
+ {
402
+ className: cn("relative flex min-h-screen w-full bg-background text-foreground", className),
403
+ children: [
404
+ /* @__PURE__ */ jsx(
405
+ "aside",
406
+ {
407
+ "aria-label": "Primary",
408
+ className: cn(
409
+ "hidden shrink-0 overflow-hidden border-r border-neutral-4 bg-neutral-2/80 md:block",
410
+ "transition-[width] duration-200 ease-out"
411
+ ),
412
+ style: { width: railWidth },
413
+ children: /* @__PURE__ */ jsx(
414
+ "div",
415
+ {
416
+ className: "h-full overflow-y-auto",
417
+ style: { width: isCollapsed ? sidebarCollapsedWidth : sidebarWidth },
418
+ children: sidebar
419
+ }
420
+ )
421
+ }
422
+ ),
423
+ /* @__PURE__ */ jsx(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: /* @__PURE__ */ jsx(SheetContent, { side: "left", className: "w-72 border-r border-neutral-4 bg-neutral-2 p-0", children: /* @__PURE__ */ jsx("div", { className: "h-full overflow-y-auto", children: sidebar }) }) }),
424
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
425
+ header !== void 0 && /* @__PURE__ */ jsxs(
426
+ "header",
427
+ {
428
+ className: cn(
429
+ "sticky top-0 z-30 flex items-center gap-2 border-b border-border",
430
+ "bg-background/85 px-3 backdrop-blur supports-[backdrop-filter]:bg-background/70 sm:px-4"
431
+ ),
432
+ style: { height: headerHeight },
433
+ children: [
434
+ /* @__PURE__ */ jsx(
435
+ "button",
436
+ {
437
+ type: "button",
438
+ "aria-label": "Open navigation menu",
439
+ onClick: () => setMobileOpen(true),
440
+ className: cn(
441
+ "relative inline-flex size-8 items-center justify-center rounded-md text-foreground/80 after:absolute after:-inset-1.5 after:content-['']",
442
+ "hover:bg-muted hover:text-foreground md:hidden",
443
+ "focus:outline-none focus:ring-2 focus:ring-offset-1"
444
+ ),
445
+ children: /* @__PURE__ */ jsx(Menu, { className: "size-4", "aria-hidden": "true" })
446
+ }
447
+ ),
448
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: header })
449
+ ]
450
+ }
451
+ ),
452
+ header === void 0 && /* @__PURE__ */ jsx("div", { className: "flex items-center border-b border-border bg-background px-4 py-2 md:hidden", children: /* @__PURE__ */ jsx(
453
+ "button",
454
+ {
455
+ type: "button",
456
+ "aria-label": "Open navigation menu",
457
+ onClick: () => setMobileOpen(true),
458
+ className: cn(
459
+ "relative inline-flex size-8 items-center justify-center rounded-md text-foreground/80 after:absolute after:-inset-1.5 after:content-['']",
460
+ "hover:bg-muted hover:text-foreground",
461
+ "focus:outline-none focus:ring-2 focus:ring-offset-1"
462
+ ),
463
+ children: /* @__PURE__ */ jsx(Menu, { className: "size-4", "aria-hidden": "true" })
464
+ }
465
+ ) }),
466
+ /* @__PURE__ */ jsx(
467
+ "main",
468
+ {
469
+ className: cn("w-full max-w-none px-3 py-4 sm:px-4 md:px-5 2xl:px-6", contentClassName),
470
+ children
471
+ }
472
+ )
473
+ ] })
474
+ ]
475
+ }
476
+ );
477
+ }
478
+ function Card({ children, isInteractive = false, className }) {
479
+ return /* @__PURE__ */ jsx(
480
+ "div",
481
+ {
482
+ className: cn(
483
+ "rounded-lg border border-[color:var(--neutral-7)] bg-[color:var(--neutral-1)] p-4 text-[color:var(--neutral-12)] shadow-sm dark:border-white/10 dark:bg-black/40 dark:text-white",
484
+ isInteractive && "cursor-pointer transition-shadow hover:shadow-md",
485
+ className
486
+ ),
487
+ children
488
+ }
489
+ );
490
+ }
491
+ var sizeClasses = {
492
+ sm: "max-w-screen-sm",
493
+ md: "max-w-screen-md",
494
+ lg: "max-w-screen-lg",
495
+ xl: "max-w-screen-xl",
496
+ full: "max-w-full"
497
+ };
498
+ function Container({ children, size = "lg", className }) {
499
+ return /* @__PURE__ */ jsx("div", { className: cn("mx-auto w-full px-4", sizeClasses[size], className), children });
500
+ }
501
+ var DesignSystemContext = createContext(null);
502
+ function DesignSystemProvider({
503
+ children,
504
+ defaultMode = "system",
505
+ storageKey = "nebutra-theme-mode",
506
+ enableSystemPreference = true
507
+ }) {
508
+ const [mode, setModeState] = useState(defaultMode);
509
+ const [systemPreference, setSystemPreference] = useState("light");
510
+ useEffect(() => {
511
+ if (typeof window === "undefined" || !enableSystemPreference) return;
512
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
513
+ setSystemPreference(mediaQuery.matches ? "dark" : "light");
514
+ const handler = (e) => {
515
+ setSystemPreference(e.matches ? "dark" : "light");
516
+ };
517
+ mediaQuery.addEventListener("change", handler);
518
+ return () => mediaQuery.removeEventListener("change", handler);
519
+ }, [enableSystemPreference]);
520
+ useEffect(() => {
521
+ if (typeof window === "undefined") return;
522
+ const stored = localStorage.getItem(storageKey);
523
+ if (stored && ["light", "dark", "system"].includes(stored)) {
524
+ setModeState(stored);
525
+ }
526
+ }, [storageKey]);
527
+ const resolvedMode = useMemo(() => {
528
+ if (mode === "system") {
529
+ return systemPreference;
530
+ }
531
+ return mode;
532
+ }, [mode, systemPreference]);
533
+ const setMode = useCallback(
534
+ (newMode) => {
535
+ setModeState(newMode);
536
+ if (typeof window !== "undefined") {
537
+ localStorage.setItem(storageKey, newMode);
538
+ }
539
+ },
540
+ [storageKey]
541
+ );
542
+ const toggleMode = useCallback(() => {
543
+ setMode(resolvedMode === "light" ? "dark" : "light");
544
+ }, [resolvedMode, setMode]);
545
+ const contextValue = useMemo(
546
+ () => ({
547
+ mode,
548
+ setMode,
549
+ toggleMode,
550
+ resolvedMode
551
+ }),
552
+ [mode, resolvedMode, setMode, toggleMode]
553
+ );
554
+ return /* @__PURE__ */ jsx(DesignSystemContext.Provider, { value: contextValue, children });
555
+ }
556
+ var SIZE_MAP = {
557
+ sm: { box: "h-8 w-8", halo: "h-16 w-16 -inset-4", glyph: "h-3.5 w-3.5" },
558
+ md: { box: "h-10 w-10", halo: "h-20 w-20 -inset-5", glyph: "h-4 w-4" },
559
+ lg: { box: "h-14 w-14", halo: "h-28 w-28 -inset-7", glyph: "h-5 w-5" },
560
+ xl: { box: "h-20 w-20", halo: "h-40 w-40 -inset-10", glyph: "h-7 w-7" }
561
+ };
562
+ function BrandMark({
563
+ children,
564
+ variant = "gradient",
565
+ size = "md",
566
+ className,
567
+ halo = false,
568
+ ariaHidden = true
569
+ }) {
570
+ const sizes = SIZE_MAP[size];
571
+ return /* @__PURE__ */ jsxs(
572
+ "span",
573
+ {
574
+ "aria-hidden": ariaHidden,
575
+ className: cn("relative inline-flex shrink-0 items-center justify-center", className),
576
+ children: [
577
+ halo && /* @__PURE__ */ jsx(
578
+ "span",
579
+ {
580
+ "aria-hidden": "true",
581
+ className: cn(
582
+ "pointer-events-none absolute rounded-full opacity-30 blur-2xl",
583
+ sizes.halo
584
+ ),
585
+ style: { background: "var(--brand-gradient)" }
586
+ }
587
+ ),
588
+ /* @__PURE__ */ jsx(
589
+ "span",
590
+ {
591
+ className: cn(
592
+ "relative inline-flex items-center justify-center rounded-2xl",
593
+ sizes.box,
594
+ variant === "gradient" && "text-white shadow-sm",
595
+ variant === "soft" && "bg-blue-2 text-blue-11 dark:bg-blue-2/20 dark:text-blue-9",
596
+ variant === "outline" && "border border-neutral-7 bg-neutral-1 text-neutral-12 dark:border-white/10 dark:bg-white/[0.04] dark:text-white"
597
+ ),
598
+ style: variant === "gradient" ? { background: "var(--brand-gradient)" } : void 0,
599
+ children: children ?? /* @__PURE__ */ jsx(DefaultGlyph, { className: sizes.glyph })
600
+ }
601
+ )
602
+ ]
603
+ }
604
+ );
605
+ }
606
+ function DefaultGlyph({ className }) {
607
+ return /* @__PURE__ */ jsxs(
608
+ "svg",
609
+ {
610
+ viewBox: "0 0 16 16",
611
+ fill: "none",
612
+ "aria-hidden": "true",
613
+ className,
614
+ xmlns: "http://www.w3.org/2000/svg",
615
+ children: [
616
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "3", fill: "currentColor" }),
617
+ /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeOpacity: "0.35" })
618
+ ]
619
+ }
620
+ );
621
+ }
622
+
623
+ // src/tokens/components/empty-state.ts
624
+ var emptyStateTokens = {
625
+ root: {
626
+ minHeight: {
627
+ sm: primitiveSpacing[16] * 2,
628
+ md: primitiveSpacing[24] * 2,
629
+ lg: primitiveSpacing[16] * 4
630
+ },
631
+ paddingX: {
632
+ sm: primitiveSpacing[4],
633
+ md: primitiveSpacing[6],
634
+ lg: primitiveSpacing[8]
635
+ },
636
+ paddingY: {
637
+ sm: primitiveSpacing[8],
638
+ md: primitiveSpacing[10],
639
+ lg: primitiveSpacing[14]
640
+ },
641
+ radius: primitiveRadius.lg
642
+ },
643
+ content: {
644
+ maxWidth: primitiveSpacing[14] * 8,
645
+ descriptionMaxWidth: primitiveSpacing[12] * 8,
646
+ stackGap: primitiveSpacing[4],
647
+ copyGap: primitiveSpacing[2],
648
+ actionsGap: primitiveSpacing[2]
649
+ },
650
+ icon: {
651
+ size: primitiveSizing.lg,
652
+ radius: primitiveRadius.lg
653
+ },
654
+ typography: {
655
+ title: {
656
+ sm: primitiveFontSize.sm,
657
+ md: primitiveFontSize.base,
658
+ lg: primitiveFontSize.lg
659
+ },
660
+ description: {
661
+ sm: primitiveFontSize.xs,
662
+ md: primitiveFontSize.sm,
663
+ lg: primitiveFontSize.sm
664
+ },
665
+ titleWeight: primitiveFontWeight.semibold,
666
+ titleLineHeight: primitiveSpacing[6],
667
+ descriptionLineHeight: primitiveSpacing[6]
668
+ }
669
+ };
670
+ var variantClassName = {
671
+ "blank-slate": "border border-dashed border-border bg-background",
672
+ informational: "border border-border bg-card",
673
+ educational: "border border-border bg-card",
674
+ guide: "border border-border bg-muted/30",
675
+ "no-results": "border border-dashed border-border bg-background",
676
+ cleared: "border border-dashed border-border bg-muted/20",
677
+ permission: "border border-border bg-card",
678
+ error: "border border-destructive/25 bg-destructive/5"
679
+ };
680
+ var iconToneClassName = {
681
+ "blank-slate": "text-muted-foreground",
682
+ informational: "text-primary",
683
+ educational: "text-primary",
684
+ guide: "text-primary",
685
+ "no-results": "text-muted-foreground",
686
+ cleared: "text-muted-foreground",
687
+ permission: "text-muted-foreground",
688
+ error: "text-destructive"
689
+ };
690
+ var emptyStateCssVars = (size) => ({
691
+ "--empty-state-min-height": `${emptyStateTokens.root.minHeight[size]}px`,
692
+ "--empty-state-padding-x": `${emptyStateTokens.root.paddingX[size]}px`,
693
+ "--empty-state-padding-y": `${emptyStateTokens.root.paddingY[size]}px`,
694
+ "--empty-state-radius": `${emptyStateTokens.root.radius}px`,
695
+ "--empty-state-content-max-width": `${emptyStateTokens.content.maxWidth}px`,
696
+ "--empty-state-description-max-width": `${emptyStateTokens.content.descriptionMaxWidth}px`,
697
+ "--empty-state-stack-gap": `${emptyStateTokens.content.stackGap}px`,
698
+ "--empty-state-copy-gap": `${emptyStateTokens.content.copyGap}px`,
699
+ "--empty-state-actions-gap": `${emptyStateTokens.content.actionsGap}px`,
700
+ "--empty-state-icon-size": `${emptyStateTokens.icon.size}px`,
701
+ "--empty-state-icon-radius": `${emptyStateTokens.icon.radius}px`,
702
+ "--empty-state-title-size": `${emptyStateTokens.typography.title[size]}px`,
703
+ "--empty-state-description-size": `${emptyStateTokens.typography.description[size]}px`,
704
+ "--empty-state-title-weight": `${emptyStateTokens.typography.titleWeight}`,
705
+ "--empty-state-title-line-height": `${emptyStateTokens.typography.titleLineHeight}px`,
706
+ "--empty-state-description-line-height": `${emptyStateTokens.typography.descriptionLineHeight}px`
707
+ });
708
+ var EmptyStateIcon = React.forwardRef(
709
+ ({ className, icon, ...props }, ref) => /* @__PURE__ */ jsx(
710
+ "div",
711
+ {
712
+ ref,
713
+ "aria-hidden": "true",
714
+ className: cn(
715
+ "flex size-[var(--empty-state-icon-size)] shrink-0 items-center justify-center rounded-[var(--empty-state-icon-radius)] bg-muted",
716
+ className
717
+ ),
718
+ ...props,
719
+ children: icon
720
+ }
721
+ )
722
+ );
723
+ EmptyStateIcon.displayName = "EmptyState.Icon";
724
+ var EmptyStateRoot = React.forwardRef(
725
+ ({
726
+ className,
727
+ title,
728
+ description,
729
+ icon,
730
+ children,
731
+ action,
732
+ link,
733
+ style,
734
+ variant = "blank-slate",
735
+ size = "md",
736
+ align = "center",
737
+ live = false,
738
+ ...props
739
+ }, ref) => {
740
+ const actions = children ?? [action, link].filter(Boolean);
741
+ const hasActions = React.Children.toArray(actions).length > 0;
742
+ return /* @__PURE__ */ jsx(
743
+ "section",
744
+ {
745
+ ref,
746
+ "aria-live": live ? "polite" : void 0,
747
+ className: cn(
748
+ "flex min-h-[var(--empty-state-min-height)] w-full flex-col justify-center rounded-[var(--empty-state-radius)] px-[var(--empty-state-padding-x)] py-[var(--empty-state-padding-y)]",
749
+ variantClassName[variant],
750
+ align === "center" ? "items-center text-center" : "items-start text-left",
751
+ className
752
+ ),
753
+ style: { ...emptyStateCssVars(size), ...style },
754
+ ...props,
755
+ children: /* @__PURE__ */ jsxs(
756
+ "div",
757
+ {
758
+ className: cn(
759
+ "flex max-w-[var(--empty-state-content-max-width)] flex-col gap-[var(--empty-state-stack-gap)]",
760
+ align === "center" ? "items-center" : "items-start"
761
+ ),
762
+ children: [
763
+ icon ? /* @__PURE__ */ jsx("div", { className: cn("[&>div]:bg-muted", iconToneClassName[variant]), children: icon }) : null,
764
+ /* @__PURE__ */ jsxs("div", { className: "space-y-[var(--empty-state-copy-gap)]", children: [
765
+ /* @__PURE__ */ jsx("h3", { className: "font-[var(--empty-state-title-weight)] text-[length:var(--empty-state-title-size)] leading-[var(--empty-state-title-line-height)] text-foreground", children: title }),
766
+ description ? /* @__PURE__ */ jsx("p", { className: "max-w-[var(--empty-state-description-max-width)] text-[length:var(--empty-state-description-size)] leading-[var(--empty-state-description-line-height)] text-muted-foreground", children: description }) : null
767
+ ] }),
768
+ hasActions ? /* @__PURE__ */ jsx(
769
+ "div",
770
+ {
771
+ className: cn(
772
+ "flex flex-wrap gap-[var(--empty-state-actions-gap)]",
773
+ align === "center" ? "justify-center" : "justify-start"
774
+ ),
775
+ children: actions
776
+ }
777
+ ) : null
778
+ ]
779
+ }
780
+ )
781
+ }
782
+ );
783
+ }
784
+ );
785
+ EmptyStateRoot.displayName = "EmptyState.Root";
786
+ var EmptyState = {
787
+ Root: EmptyStateRoot,
788
+ Icon: EmptyStateIcon
789
+ };
790
+ var toneVariant = {
791
+ default: "blank-slate",
792
+ branded: "informational",
793
+ subtle: "guide"
794
+ };
795
+ function EmptyState2({
796
+ title,
797
+ description,
798
+ mascot,
799
+ icon,
800
+ action,
801
+ secondaryAction,
802
+ tone = "default",
803
+ size = "md",
804
+ className
805
+ }) {
806
+ const visual = mascot ?? icon ?? (tone === "branded" ? /* @__PURE__ */ jsx(BrandMark, { size: size === "lg" ? "lg" : "md" }) : null);
807
+ return /* @__PURE__ */ jsx(
808
+ EmptyState.Root,
809
+ {
810
+ action,
811
+ className: cn(tone === "subtle" && "border-0 bg-transparent", className),
812
+ description,
813
+ icon: visual ? /* @__PURE__ */ jsx(EmptyState.Icon, { icon: visual }) : void 0,
814
+ link: secondaryAction,
815
+ size,
816
+ title,
817
+ variant: toneVariant[tone]
818
+ }
819
+ );
820
+ }
821
+ var Slot = React.forwardRef(
822
+ ({ children, className, style, ...props }, ref) => {
823
+ const childArray = React.Children.toArray(children);
824
+ const child = childArray.length === 1 ? childArray[0] : null;
825
+ if (child == null || !React.isValidElement(child)) {
826
+ return /* @__PURE__ */ jsx("span", { ref, className, style, ...props, children });
827
+ }
828
+ const childProps = child.props;
829
+ const mergedRef = (node) => {
830
+ if (typeof ref === "function") ref(node);
831
+ else if (ref) ref.current = node;
832
+ const childRef = child.ref;
833
+ if (typeof childRef === "function") childRef(node);
834
+ else if (childRef && "current" in childRef) {
835
+ childRef.current = node;
836
+ }
837
+ };
838
+ const mergedClass = [className, childProps.className].filter(Boolean).join(" ");
839
+ const merged = {
840
+ ...childProps,
841
+ ...props,
842
+ ref: mergedRef,
843
+ className: mergedClass,
844
+ style: { ...childProps.style, ...style }
845
+ };
846
+ return React.cloneElement(
847
+ child,
848
+ merged
849
+ );
850
+ }
851
+ );
852
+ Slot.displayName = "Slot";
853
+ var buttonVariants = cva(
854
+ [
855
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap",
856
+ "rounded-[var(--radius-md)] text-sm font-medium",
857
+ "transition-colors duration-150 ease-out",
858
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
859
+ "disabled:pointer-events-none disabled:opacity-50",
860
+ "aria-busy:cursor-wait"
861
+ ].join(" "),
862
+ {
863
+ variants: {
864
+ variant: {
865
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
866
+ ink: "bg-[var(--neutral-12)] text-[var(--neutral-1)] ring-1 ring-inset ring-white/[0.04] hover:bg-[var(--neutral-11)] hover:-translate-y-px active:translate-y-0 transition-[transform,background-color] duration-150",
867
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
868
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
869
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
870
+ ghost: "hover:bg-accent hover:text-accent-foreground",
871
+ link: "text-primary underline-offset-4 hover:underline",
872
+ tertiary: "border border-transparent bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground hover:border-input",
873
+ warning: "bg-warning text-warning-foreground hover:bg-warning/90"
874
+ },
875
+ size: {
876
+ tiny: "h-6 rounded-[var(--radius-sm)] px-2 text-[11px]",
877
+ // 24px
878
+ sm: "h-8 rounded-[var(--radius-sm)] px-3 text-xs",
879
+ // 32px
880
+ default: "h-10 px-4 py-2",
881
+ // 40px
882
+ lg: "h-12 rounded-[var(--radius-lg)] px-5 text-base",
883
+ // 48px
884
+ icon: "h-10 w-10"
885
+ // 40×40px (kept for backward compat)
886
+ },
887
+ shape: {
888
+ default: "",
889
+ square: "",
890
+ circle: ""
891
+ }
892
+ },
893
+ compoundVariants: [
894
+ // shape="square" — width = height, no horizontal padding
895
+ { shape: "square", size: "tiny", className: "w-6 px-0" },
896
+ { shape: "square", size: "sm", className: "w-8 px-0" },
897
+ { shape: "square", size: "default", className: "w-10 px-0" },
898
+ { shape: "square", size: "lg", className: "w-12 px-0" },
899
+ // shape="circle" — width = height + rounded-full
900
+ { shape: "circle", size: "tiny", className: "w-6 px-0 rounded-full" },
901
+ { shape: "circle", size: "sm", className: "w-8 px-0 rounded-full" },
902
+ { shape: "circle", size: "default", className: "w-10 px-0 rounded-full" },
903
+ { shape: "circle", size: "lg", className: "w-12 px-0 rounded-full" }
904
+ ],
905
+ defaultVariants: {
906
+ variant: "default",
907
+ size: "default",
908
+ shape: "default"
909
+ }
910
+ }
911
+ );
912
+ function Spinner({ className }) {
913
+ return /* @__PURE__ */ jsx(
914
+ "span",
915
+ {
916
+ "aria-hidden": "true",
917
+ className: cn(
918
+ "block rounded-full border-2 border-current border-t-transparent animate-[spinner_0.6s_linear_infinite]",
919
+ className
920
+ )
921
+ }
922
+ );
923
+ }
924
+ function getIconSizeClass(size) {
925
+ switch (size) {
926
+ case "tiny":
927
+ return "[&>svg]:size-3";
928
+ // 12px
929
+ case "sm":
930
+ return "[&>svg]:size-3.5";
931
+ // 14px
932
+ case "lg":
933
+ return "[&>svg]:size-4.5";
934
+ // 18px
935
+ default:
936
+ return "[&>svg]:size-4";
937
+ }
938
+ }
939
+ function getSpinnerSizeClass(size) {
940
+ switch (size) {
941
+ case "tiny":
942
+ return "size-3";
943
+ // 12px
944
+ case "sm":
945
+ return "size-3.5";
946
+ // 14px
947
+ case "lg":
948
+ return "size-4.5";
949
+ // 18px
950
+ default:
951
+ return "size-4";
952
+ }
953
+ }
954
+ var SHADOW_CLASSES = {
955
+ sm: "shadow-sm",
956
+ md: "shadow-md",
957
+ lg: "shadow-lg"
958
+ };
959
+ function resolveShadowClass(shadow) {
960
+ if (!shadow) return void 0;
961
+ const level = shadow === true ? "md" : shadow;
962
+ return SHADOW_CLASSES[level];
963
+ }
964
+ function ButtonContent({ loading, prefix, suffix, size, children }) {
965
+ const iconSizeClass = getIconSizeClass(size);
966
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
967
+ loading && /* @__PURE__ */ jsx(Spinner, { className: getSpinnerSizeClass(size) }),
968
+ prefix != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("shrink-0", iconSizeClass), children: prefix }),
969
+ children,
970
+ suffix != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("shrink-0", iconSizeClass), children: suffix })
971
+ ] });
972
+ }
973
+ var Button = React.forwardRef(
974
+ ({
975
+ className,
976
+ variant,
977
+ size,
978
+ shape,
979
+ asChild = false,
980
+ loading = false,
981
+ disabled,
982
+ prefix,
983
+ suffix,
984
+ shadow,
985
+ children,
986
+ ...props
987
+ }, ref) => {
988
+ const Comp = asChild ? Slot : "button";
989
+ const isDisabled = disabled ?? loading;
990
+ const shadowClass = resolveShadowClass(shadow);
991
+ return /* @__PURE__ */ jsx(
992
+ Comp,
993
+ {
994
+ ref,
995
+ className: cn(buttonVariants({ variant, size, shape }), shadowClass, className),
996
+ disabled: isDisabled,
997
+ "aria-busy": loading || void 0,
998
+ ...props,
999
+ children: asChild ? children : /* @__PURE__ */ jsx(ButtonContent, { loading, prefix, suffix, size, children })
1000
+ }
1001
+ );
1002
+ }
1003
+ );
1004
+ Button.displayName = "Button";
1005
+ var ButtonLink = React.forwardRef(
1006
+ ({
1007
+ className,
1008
+ variant,
1009
+ size,
1010
+ shape,
1011
+ prefix,
1012
+ suffix,
1013
+ shadow,
1014
+ loading = false,
1015
+ children,
1016
+ ...props
1017
+ }, ref) => {
1018
+ const shadowClass = resolveShadowClass(shadow);
1019
+ const loadingProps = loading ? {
1020
+ "aria-busy": "true",
1021
+ "aria-disabled": "true",
1022
+ tabIndex: -1
1023
+ } : {};
1024
+ return /* @__PURE__ */ jsx(
1025
+ "a",
1026
+ {
1027
+ ref,
1028
+ className: cn(
1029
+ buttonVariants({ variant, size, shape }),
1030
+ shadowClass,
1031
+ loading && "pointer-events-none opacity-50",
1032
+ className
1033
+ ),
1034
+ ...loadingProps,
1035
+ ...props,
1036
+ children: /* @__PURE__ */ jsx(ButtonContent, { loading, prefix, suffix, size, children })
1037
+ }
1038
+ );
1039
+ }
1040
+ );
1041
+ ButtonLink.displayName = "ButtonLink";
1042
+ var textSize = {
1043
+ small: "text-xs",
1044
+ medium: "text-sm",
1045
+ large: "text-base"
1046
+ };
1047
+ var iconSize = {
1048
+ small: 12,
1049
+ medium: 14,
1050
+ large: 16
1051
+ };
1052
+ var surfaceSize = {
1053
+ small: "gap-2 rounded-[var(--radius-md)] p-3 text-sm",
1054
+ medium: "gap-3 rounded-[var(--radius-lg)] p-4 text-sm",
1055
+ large: "gap-3 rounded-[var(--radius-lg)] p-5 text-base"
1056
+ };
1057
+ var surfaceIconSize = {
1058
+ small: 14,
1059
+ medium: 16,
1060
+ large: 18
1061
+ };
1062
+ function getActionTarget(link) {
1063
+ if (link?.startsWith("http")) {
1064
+ return { target: "_blank", rel: "noopener noreferrer" };
1065
+ }
1066
+ return {};
1067
+ }
1068
+ var ErrorMessage = React.forwardRef(
1069
+ ({ children, label, size = "medium", error, className }, ref) => {
1070
+ const sz = size;
1071
+ const message = error?.message ?? children;
1072
+ return /* @__PURE__ */ jsxs(
1073
+ "span",
1074
+ {
1075
+ ref,
1076
+ role: "alert",
1077
+ className: cn("inline-flex items-center gap-1.5 text-destructive", textSize[sz], className),
1078
+ children: [
1079
+ /* @__PURE__ */ jsx(Warning, { size: iconSize[sz], "aria-hidden": "true", className: "shrink-0" }),
1080
+ /* @__PURE__ */ jsxs("span", { children: [
1081
+ label && /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
1082
+ label,
1083
+ ":\xA0"
1084
+ ] }),
1085
+ message,
1086
+ error?.action && error.link && /* @__PURE__ */ jsxs(Fragment, { children: [
1087
+ " ",
1088
+ /* @__PURE__ */ jsxs(
1089
+ "a",
1090
+ {
1091
+ href: error.link,
1092
+ className: "underline underline-offset-2 hover:no-underline",
1093
+ target: "_blank",
1094
+ rel: "noopener noreferrer",
1095
+ children: [
1096
+ error.action,
1097
+ /* @__PURE__ */ jsx(External, { size: 10, "aria-hidden": "true", className: "ml-0.5 inline" })
1098
+ ]
1099
+ }
1100
+ )
1101
+ ] })
1102
+ ] })
1103
+ ]
1104
+ }
1105
+ );
1106
+ }
1107
+ );
1108
+ ErrorMessage.displayName = "ErrorMessage";
1109
+ var ErrorSurface = React.forwardRef(
1110
+ ({
1111
+ children,
1112
+ label,
1113
+ showLabel = true,
1114
+ title,
1115
+ size = "medium",
1116
+ error,
1117
+ errorId,
1118
+ errorIdLabel = "Request ID",
1119
+ live = "polite",
1120
+ action,
1121
+ link,
1122
+ onAction,
1123
+ actionLoading = false,
1124
+ className,
1125
+ role,
1126
+ ...props
1127
+ }, ref) => {
1128
+ const message = error?.message ?? children;
1129
+ const resolvedTitle = title ?? error?.title;
1130
+ const resolvedAction = action ?? error?.action;
1131
+ const resolvedLink = link ?? error?.link;
1132
+ const resolvedErrorId = errorId ?? error?.id;
1133
+ const resolvedRole = role ?? (live === "assertive" ? "alert" : "status");
1134
+ const ariaLive = live === "off" ? void 0 : live;
1135
+ return /* @__PURE__ */ jsxs(
1136
+ "div",
1137
+ {
1138
+ ref,
1139
+ role: resolvedRole,
1140
+ "aria-live": ariaLive,
1141
+ className: cn(
1142
+ "flex w-full border border-destructive/25 bg-destructive/5 text-foreground",
1143
+ surfaceSize[size],
1144
+ className
1145
+ ),
1146
+ ...props,
1147
+ children: [
1148
+ /* @__PURE__ */ jsx(
1149
+ "span",
1150
+ {
1151
+ "aria-hidden": "true",
1152
+ className: "mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-[var(--radius-sm)] border border-destructive/20 bg-background text-destructive",
1153
+ children: /* @__PURE__ */ jsx(Warning, { size: surfaceIconSize[size] })
1154
+ }
1155
+ ),
1156
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1157
+ label && showLabel ? /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs font-medium uppercase tracking-normal text-destructive", children: label }) : null,
1158
+ resolvedTitle ? /* @__PURE__ */ jsx("p", { className: "font-semibold leading-6 text-foreground", children: resolvedTitle }) : null,
1159
+ message ? /* @__PURE__ */ jsx("p", { className: cn("leading-5 text-muted-foreground", resolvedTitle && "mt-1"), children: message }) : null,
1160
+ resolvedErrorId ? /* @__PURE__ */ jsxs("details", { className: "mt-3 text-xs text-muted-foreground", children: [
1161
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer select-none text-foreground", children: "Diagnostic details" }),
1162
+ /* @__PURE__ */ jsxs("code", { className: "mt-2 block overflow-x-auto rounded-[var(--radius-sm)] border bg-background px-2 py-1 font-mono text-xs text-muted-foreground", children: [
1163
+ errorIdLabel,
1164
+ ": ",
1165
+ resolvedErrorId,
1166
+ error?.code ? ` (${error.code})` : ""
1167
+ ] })
1168
+ ] }) : null,
1169
+ resolvedAction ? /* @__PURE__ */ jsx("div", { className: "mt-4 flex flex-wrap gap-2", children: resolvedLink ? /* @__PURE__ */ jsx(
1170
+ ButtonLink,
1171
+ {
1172
+ href: resolvedLink,
1173
+ size: "sm",
1174
+ variant: "outline",
1175
+ ...getActionTarget(resolvedLink),
1176
+ children: resolvedAction
1177
+ }
1178
+ ) : /* @__PURE__ */ jsx(
1179
+ Button,
1180
+ {
1181
+ type: "button",
1182
+ size: "sm",
1183
+ variant: "outline",
1184
+ loading: actionLoading,
1185
+ onClick: onAction,
1186
+ children: resolvedAction
1187
+ }
1188
+ ) }) : null
1189
+ ] })
1190
+ ]
1191
+ }
1192
+ );
1193
+ }
1194
+ );
1195
+ ErrorSurface.displayName = "Error";
1196
+ function ErrorState({
1197
+ title = "Couldn\u2019t Load Resource",
1198
+ message,
1199
+ onRetry,
1200
+ errorId
1201
+ }) {
1202
+ const stableIdProps = errorId === void 0 ? {} : { errorId };
1203
+ return /* @__PURE__ */ jsx("div", { className: "px-4 py-6", children: onRetry ? /* @__PURE__ */ jsx(ErrorSurface, { title, action: "Try Again", onAction: onRetry, ...stableIdProps, children: message }) : /* @__PURE__ */ jsx(ErrorSurface, { title, ...stableIdProps, children: message }) });
1204
+ }
1205
+ function FullPageStatus({
1206
+ code,
1207
+ title,
1208
+ description,
1209
+ primaryAction,
1210
+ secondaryAction,
1211
+ meta,
1212
+ variant = "viewport"
1213
+ }) {
1214
+ const isSection = variant === "section";
1215
+ const Wrapper = isSection ? "section" : "main";
1216
+ const wrapperClass = isSection ? "flex min-h-[60vh] items-center justify-center px-6 py-12 text-neutral-12" : "flex min-h-[100dvh] items-center justify-center bg-neutral-1 px-6 text-neutral-12";
1217
+ return /* @__PURE__ */ jsx(Wrapper, { role: isSection ? "alert" : "main", className: wrapperClass, children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-[480px]", children: [
1218
+ /* @__PURE__ */ jsx("div", { className: "mb-4 font-mono text-xs font-medium uppercase tracking-[0.08em] text-neutral-9", children: code }),
1219
+ /* @__PURE__ */ jsx("h1", { className: "mb-3 text-[clamp(28px,5vw,36px)] font-semibold leading-[1.15] tracking-[-0.02em]", children: title }),
1220
+ /* @__PURE__ */ jsx("p", { className: "mb-8 max-w-[52ch] text-base leading-[1.6] text-neutral-11", children: description }),
1221
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-3", children: [
1222
+ /* @__PURE__ */ jsx(ActionButton, { action: primaryAction, variant: "primary" }),
1223
+ secondaryAction && /* @__PURE__ */ jsx(ActionButton, { action: secondaryAction, variant: "secondary" })
1224
+ ] }),
1225
+ meta && (meta.errorId || meta.statusUrl) && /* @__PURE__ */ jsxs("div", { className: "mt-10 flex flex-wrap justify-between gap-4 border-t border-neutral-6 pt-5 font-mono text-xs text-neutral-10", children: [
1226
+ meta.errorId && /* @__PURE__ */ jsxs("span", { children: [
1227
+ "Error ID: ",
1228
+ meta.errorId
1229
+ ] }),
1230
+ meta.statusUrl && /* @__PURE__ */ jsxs(
1231
+ "a",
1232
+ {
1233
+ href: `https://${meta.statusUrl}`,
1234
+ className: "text-neutral-10 hover:text-neutral-12",
1235
+ rel: "noreferrer",
1236
+ children: [
1237
+ meta.statusUrl,
1238
+ " \u2192"
1239
+ ]
1240
+ }
1241
+ )
1242
+ ] })
1243
+ ] }) });
1244
+ }
1245
+ function ActionButton({
1246
+ action,
1247
+ variant
1248
+ }) {
1249
+ const buttonVariant = variant === "primary" ? "ink" : "outline";
1250
+ if (action.href) {
1251
+ return /* @__PURE__ */ jsx(Button, { asChild: true, variant: buttonVariant, children: /* @__PURE__ */ jsx(Link, { href: action.href, children: action.label }) });
1252
+ }
1253
+ return /* @__PURE__ */ jsx(Button, { type: "button", onClick: action.onClick, variant: buttonVariant, children: action.label });
1254
+ }
1255
+ FullPageStatus.Accent = function Accent({ children }) {
1256
+ return /* @__PURE__ */ jsx("span", { className: "text-neutral-12", children });
1257
+ };
1258
+ var spinnerSize = {
1259
+ small: "h-4 w-4 border-2",
1260
+ medium: "h-6 w-6 border-2",
1261
+ large: "h-8 w-8 border-4"
1262
+ };
1263
+ function LoadingState({ message, size = "large" }) {
1264
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-10", children: [
1265
+ /* @__PURE__ */ jsx(
1266
+ "span",
1267
+ {
1268
+ className: cn(
1269
+ "inline-block animate-spin rounded-full border-solid border-[color:var(--blue-9)] border-r-transparent align-[-0.125em]",
1270
+ spinnerSize[size]
1271
+ ),
1272
+ role: "status",
1273
+ "aria-label": message ?? "Loading"
1274
+ }
1275
+ ),
1276
+ message && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-[color:var(--neutral-11)] dark:text-white/70", children: message })
1277
+ ] });
1278
+ }
1279
+ function PageHeader({ title, description, actions, className }) {
1280
+ return /* @__PURE__ */ jsxs(
1281
+ "div",
1282
+ {
1283
+ className: cn(
1284
+ "flex flex-col gap-1 pb-6 sm:flex-row sm:items-start sm:justify-between",
1285
+ className
1286
+ ),
1287
+ children: [
1288
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
1289
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight text-[color:var(--neutral-12)] dark:text-white", children: title }),
1290
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-[color:var(--neutral-11)] dark:text-white/70", children: description })
1291
+ ] }),
1292
+ actions && /* @__PURE__ */ jsx("div", { className: "mt-2 flex shrink-0 gap-2 sm:mt-0", children: actions })
1293
+ ]
1294
+ }
1295
+ );
1296
+ }
1297
+ function Section({ children, label, className }) {
1298
+ return /* @__PURE__ */ jsx("section", { "aria-label": label, className: cn("py-8", className), children });
1299
+ }
1300
+
1301
+ export { AppShell, Card, Container, DesignSystemProvider, EmptyState2 as EmptyState, ErrorState, FullPageStatus, LoadingState, PageHeader, Section };
1302
+ //# sourceMappingURL=index.js.map
1303
+ //# sourceMappingURL=index.js.map