@nexub/patterns 1.5.1 → 1.7.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 (37) hide show
  1. package/dist/{chunk-Y3TNGDKC.js → chunk-5IORYY2L.js} +8 -7
  2. package/dist/{chunk-TLOLIMPY.js → chunk-5JU6HUVJ.js} +2 -2
  3. package/dist/{chunk-AEYLZU3F.js → chunk-7NYGYETG.js} +4 -4
  4. package/dist/{chunk-ZFHA46UX.js → chunk-H5UTW5ML.js} +5 -5
  5. package/dist/{chunk-AOE6GQSH.js → chunk-HMJRSNJD.js} +4 -4
  6. package/dist/{chunk-YX7UO577.js → chunk-HYC5N5MO.js} +4 -5
  7. package/dist/{chunk-7GYTYFUD.js → chunk-K5EFLRKU.js} +9 -9
  8. package/dist/{chunk-MXPRKZPB.js → chunk-L456OB3Q.js} +6 -6
  9. package/dist/{chunk-OYSWFXRD.js → chunk-LAYYOVIK.js} +14 -19
  10. package/dist/chunk-RWPFCNMJ.js +75 -0
  11. package/dist/{chunk-OTNHVOPC.js → chunk-U3FT7XAU.js} +2 -2
  12. package/dist/{chunk-5QFC4WFD.js → chunk-WIHNK7RT.js} +12 -12
  13. package/dist/{chunk-74JVLMLE.js → chunk-WPNT5JOU.js} +3 -3
  14. package/dist/chunk-WRP73TXG.js +109 -0
  15. package/dist/chunk-WZTDCJYP.js +23 -0
  16. package/dist/{chunk-QMYBXQG4.js → chunk-YB7G4YBU.js} +8 -8
  17. package/dist/{chunk-EOA5C3Q4.js → chunk-Z6VMCMSW.js} +2 -2
  18. package/dist/components/ActivityFeed/index.js +1 -1
  19. package/dist/components/AppHeader/index.js +2 -2
  20. package/dist/components/AppLauncher/index.js +1 -1
  21. package/dist/components/AuthCard/index.js +1 -1
  22. package/dist/components/BentoGrid/index.js +9 -0
  23. package/dist/components/Conversation/index.js +7 -0
  24. package/dist/components/ErrorSummary/index.js +9 -0
  25. package/dist/components/FilterBar/index.js +1 -1
  26. package/dist/components/InfoList/index.js +1 -1
  27. package/dist/components/ListDetail/index.js +1 -1
  28. package/dist/components/NotificationItem/index.js +1 -1
  29. package/dist/components/PlanCard/index.js +1 -1
  30. package/dist/components/SettingsLayout/index.js +1 -1
  31. package/dist/components/SettingsSection/index.js +1 -1
  32. package/dist/components/StatCard/index.js +1 -1
  33. package/dist/components/StepIndicator/index.js +1 -1
  34. package/dist/components/UserCard/index.js +1 -1
  35. package/dist/index.d.ts +88 -5
  36. package/dist/index.js +39 -23
  37. package/package.json +17 -16
@@ -10,8 +10,8 @@ var NotificationItem = React.forwardRef(
10
10
  {
11
11
  ref,
12
12
  className: cn(
13
- "flex items-start gap-3 p-3 rounded-lg transition-colors hover:bg-[var(--color-bg-subtle)]",
14
- !read && "bg-[var(--color-bg-subtle)]",
13
+ "nx-state-layer flex items-start gap-3 p-3 rounded-shape-container",
14
+ !read && "bg-[var(--color-surface-container-low)]",
15
15
  className
16
16
  ),
17
17
  ...props,
@@ -19,15 +19,16 @@ var NotificationItem = React.forwardRef(
19
19
  !read && /* @__PURE__ */ jsx(
20
20
  "span",
21
21
  {
22
- className: "mt-1.5 h-2 w-2 shrink-0 rounded-full bg-[var(--color-brand-primary-500)]",
22
+ className: "mt-1.5 h-2 w-2 shrink-0 rounded-full bg-[var(--color-primary)]",
23
+ role: "img",
23
24
  "aria-label": "Unread"
24
25
  }
25
26
  ),
26
27
  icon && /* @__PURE__ */ jsx("div", { className: "shrink-0 mt-0.5", children: icon }),
27
28
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
28
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-text-primary)] truncate", children: title }),
29
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-0.5 line-clamp-2", children: description }),
30
- timestamp && /* @__PURE__ */ jsx("time", { className: "text-xs text-[var(--color-text-muted)] mt-1 block", children: timestamp })
29
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-on-surface)] truncate", children: title }),
30
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-on-surface-muted)] mt-0.5 line-clamp-2", children: description }),
31
+ timestamp && /* @__PURE__ */ jsx("time", { className: "text-xs text-[var(--color-on-surface-muted)] mt-1 block", children: timestamp })
31
32
  ] }),
32
33
  onDismiss && /* @__PURE__ */ jsx(
33
34
  "button",
@@ -35,7 +36,7 @@ var NotificationItem = React.forwardRef(
35
36
  type: "button",
36
37
  onClick: onDismiss,
37
38
  "aria-label": "Dismiss notification",
38
- className: "shrink-0 text-[var(--color-text-muted)] hover:text-[var(--color-text-primary)] p-1 rounded",
39
+ className: "shrink-0 text-[var(--color-on-surface-muted)] hover:text-[var(--color-on-surface)] p-1 rounded-shape-control",
39
40
  children: "\xD7"
40
41
  }
41
42
  )
@@ -29,7 +29,7 @@ var ListDetail = React.forwardRef(
29
29
  "aria-label": listLabel,
30
30
  className: cn(
31
31
  "min-h-0 w-full shrink-0 flex-col overflow-y-auto",
32
- "[@container(min-width:720px)]:flex [@container(min-width:720px)]:border-r [@container(min-width:720px)]:border-[var(--color-outline-variant)]",
32
+ "[@container(min-width:720px)]:flex [@container(min-width:720px)]:border-e [@container(min-width:720px)]:border-[var(--color-outline-variant)]",
33
33
  widths[listWidth],
34
34
  showDetail ? "hidden" : "flex"
35
35
  ),
@@ -46,7 +46,7 @@ var ListDetail = React.forwardRef(
46
46
  showDetail ? "flex" : "hidden"
47
47
  ),
48
48
  children: [
49
- onBack && /* @__PURE__ */ jsx("div", { className: "p-2 [@container(min-width:720px)]:hidden", children: /* @__PURE__ */ jsx(IconButton, { label: backLabel, onClick: onBack, children: /* @__PURE__ */ jsx(ArrowLeft, { size: "md", "aria-hidden": "true" }) }) }),
49
+ onBack && /* @__PURE__ */ jsx("div", { className: "p-2 [@container(min-width:720px)]:hidden", children: /* @__PURE__ */ jsx(IconButton, { label: backLabel, onClick: onBack, children: /* @__PURE__ */ jsx(ArrowLeft, { size: "md", "aria-hidden": "true", className: "rtl:-scale-x-100" }) }) }),
50
50
  detail
51
51
  ]
52
52
  }
@@ -21,7 +21,7 @@ var UserCard = React.forwardRef(
21
21
  {
22
22
  ref,
23
23
  className: cn(
24
- "flex items-center gap-3 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-default)] p-4",
24
+ "flex items-center gap-3 rounded-shape-container border border-[color-mix(in_srgb,var(--color-outline-variant)_60%,transparent)] bg-[var(--color-surface-elevated)] p-4",
25
25
  className
26
26
  ),
27
27
  ...props,
@@ -36,9 +36,9 @@ var UserCard = React.forwardRef(
36
36
  }
37
37
  ),
38
38
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex flex-col gap-0.5", children: [
39
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[var(--color-text-primary)] truncate", children: name }),
40
- role && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-muted)] truncate", children: role }),
41
- email && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-secondary)] truncate", children: email })
39
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-[var(--color-on-surface)] truncate", children: name }),
40
+ role && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-on-surface-muted)] truncate", children: role }),
41
+ email && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-on-surface-variant)] truncate", children: email })
42
42
  ] }),
43
43
  actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 shrink-0", children: actions })
44
44
  ]
@@ -16,12 +16,12 @@ function AppIcon({ app }) {
16
16
  alt: "",
17
17
  width: 40,
18
18
  height: 40,
19
- className: "h-10 w-10 rounded-xl object-cover",
19
+ className: "h-10 w-10 rounded-[12px] object-cover",
20
20
  onError: () => setFailed(true)
21
21
  }
22
22
  );
23
23
  }
24
- return /* @__PURE__ */ jsx(Avatar, { name: app.name, size: "md", className: "rounded-xl", "aria-hidden": "true" });
24
+ return /* @__PURE__ */ jsx(Avatar, { name: app.name, size: "md", className: "rounded-[12px]", "aria-hidden": "true" });
25
25
  }
26
26
  var AppLauncher = React.forwardRef(
27
27
  ({ apps, onNavigate, surface = "solid", label = "Nexub apps", className }, ref) => {
@@ -75,9 +75,9 @@ var AppLauncher = React.forwardRef(
75
75
  "aria-current": app.current ? "page" : void 0,
76
76
  onClick: (e) => onClick(e, app),
77
77
  className: cn(
78
- "flex flex-col items-center gap-1.5 rounded-lg px-1 py-3 text-center text-xs text-[var(--color-text-primary)] transition-colors",
79
- "hover:bg-[var(--color-bg-subtle)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring-default)]",
80
- app.current && "bg-[var(--color-bg-subtle)] font-semibold"
78
+ "flex flex-col items-center gap-1.5 rounded-[8px] px-1 py-3 text-center text-xs text-[var(--color-on-surface)] transition-colors",
79
+ "hover:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,transparent)]",
80
+ app.current && "bg-[var(--color-surface-container-low)] font-semibold"
81
81
  ),
82
82
  children: [
83
83
  /* @__PURE__ */ jsx(AppIcon, { app }),
@@ -22,11 +22,11 @@ function AuthCard({
22
22
  children: [
23
23
  logo && /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: logo }),
24
24
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 text-center", children: [
25
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold tracking-tight text-[var(--color-text-primary)]", children: title }),
26
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
25
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold tracking-tight text-[var(--color-on-surface)]", children: title }),
26
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-on-surface-muted)]", children: description })
27
27
  ] }),
28
- /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-default)] p-6 flex flex-col gap-4", children }),
29
- footer && /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center gap-2 text-sm text-[var(--color-text-muted)]", children: footer })
28
+ /* @__PURE__ */ jsx("div", { className: "rounded-shape-container border border-[color-mix(in_srgb,var(--color-outline-variant)_60%,transparent)] bg-[var(--color-surface-elevated)] shadow-elevation-1 p-6 flex flex-col gap-4", children }),
29
+ footer && /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center gap-2 text-sm text-[var(--color-on-surface-muted)]", children: footer })
30
30
  ]
31
31
  }
32
32
  );
@@ -9,9 +9,8 @@ var Root = React.forwardRef(
9
9
  "ul",
10
10
  {
11
11
  ref,
12
- role: "feed",
13
12
  "aria-label": "Activity feed",
14
- className: cn("flex flex-col divide-y divide-[var(--color-border-default)]", className),
13
+ className: cn("flex flex-col divide-y divide-[var(--color-outline-variant)]", className),
15
14
  ...props,
16
15
  children
17
16
  }
@@ -21,10 +20,10 @@ Root.displayName = "ActivityFeed.Root";
21
20
  var Item = ({ avatar, title, description, timestamp, className }) => /* @__PURE__ */ jsxs("li", { className: cn("flex items-start gap-3 py-3", className), children: [
22
21
  avatar && /* @__PURE__ */ jsx("div", { className: "shrink-0 mt-0.5", children: avatar }),
23
22
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
24
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-text-primary)]", children: title }),
25
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-0.5", children: description })
23
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-on-surface)]", children: title }),
24
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-on-surface-muted)] mt-0.5", children: description })
26
25
  ] }),
27
- timestamp && /* @__PURE__ */ jsx("time", { className: "shrink-0 text-xs text-[var(--color-text-muted)]", children: timestamp })
26
+ timestamp && /* @__PURE__ */ jsx("time", { className: "shrink-0 text-xs text-[var(--color-on-surface-muted)]", children: timestamp })
28
27
  ] });
29
28
  Item.displayName = "ActivityFeed.Item";
30
29
  var ActivityFeed = { Root, Item };
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/components/StatCard/StatCard.tsx
4
4
  import * as React from "react";
5
- import { cn } from "@nexub/react";
5
+ import { cn, NumberTicker } from "@nexub/react";
6
6
  import { TrendingUp, TrendingDown, Minus } from "@nexub/icons";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  var StatCard = React.forwardRef(
@@ -13,32 +13,32 @@ var StatCard = React.forwardRef(
13
13
  deltaType = "neutral",
14
14
  icon,
15
15
  description,
16
+ animate = false,
16
17
  className,
17
18
  ...props
18
19
  }, ref) => {
19
- const deltaColor = deltaType === "increase" ? "var(--color-success-500)" : deltaType === "decrease" ? "var(--color-error-500)" : "var(--color-text-muted)";
20
+ const deltaColor = deltaType === "increase" ? "text-[var(--color-success)]" : deltaType === "decrease" ? "text-[var(--color-error)]" : "text-[var(--color-on-surface-muted)]";
20
21
  const DeltaIcon = deltaType === "increase" ? TrendingUp : deltaType === "decrease" ? TrendingDown : Minus;
21
22
  return /* @__PURE__ */ jsxs(
22
23
  "div",
23
24
  {
24
25
  ref,
25
26
  className: cn(
26
- "rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-default)] p-6 flex flex-col gap-3",
27
+ "rounded-shape-container border border-[color-mix(in_srgb,var(--color-outline-variant)_60%,transparent)] bg-[var(--color-surface-elevated)] p-6 flex flex-col gap-3",
27
28
  className
28
29
  ),
29
30
  ...props,
30
31
  children: [
31
32
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
32
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--color-text-muted)]", children: label }),
33
- icon && /* @__PURE__ */ jsx("span", { className: "text-[var(--color-text-muted)]", children: icon })
33
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--color-on-surface-muted)]", children: label }),
34
+ icon && /* @__PURE__ */ jsx("span", { className: "text-[var(--color-on-surface-muted)]", children: icon })
34
35
  ] }),
35
- /* @__PURE__ */ jsx("div", { className: "text-3xl font-bold tracking-tight text-[var(--color-text-primary)]", children: value }),
36
+ /* @__PURE__ */ jsx("div", { className: "text-3xl font-bold tracking-tight tabular-nums text-[var(--color-on-surface)]", children: animate && (typeof value === "number" || typeof value === "string") ? /* @__PURE__ */ jsx(NumberTicker, { value, "aria-live": "polite" }) : value }),
36
37
  (delta || description) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
37
38
  delta && /* @__PURE__ */ jsxs(
38
39
  "span",
39
40
  {
40
- className: "inline-flex items-center gap-1 text-xs font-medium",
41
- style: { color: deltaColor },
41
+ className: cn("inline-flex items-center gap-1 text-xs font-medium", deltaColor),
42
42
  "data-delta-type": deltaType,
43
43
  children: [
44
44
  /* @__PURE__ */ jsx(DeltaIcon, { size: "xs", "aria-hidden": "true" }),
@@ -46,7 +46,7 @@ var StatCard = React.forwardRef(
46
46
  ]
47
47
  }
48
48
  ),
49
- description && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-muted)]", children: description })
49
+ description && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-on-surface-muted)]", children: description })
50
50
  ] })
51
51
  ]
52
52
  }
@@ -12,7 +12,7 @@ var StepIndicator = React.forwardRef(
12
12
  index < steps.length - 1 && /* @__PURE__ */ jsx(
13
13
  "div",
14
14
  {
15
- className: "absolute left-4 top-8 h-full w-px bg-[var(--color-border-default)]",
15
+ className: "absolute start-4 top-8 h-full w-px bg-[var(--color-outline-variant)]",
16
16
  "aria-hidden": "true"
17
17
  }
18
18
  ),
@@ -21,17 +21,17 @@ var StepIndicator = React.forwardRef(
21
21
  {
22
22
  className: cn(
23
23
  "relative z-10 flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-xs font-semibold",
24
- status === "complete" && "bg-[var(--color-fill-primary)] text-white",
25
- status === "current" && "border-2 border-[var(--color-brand-primary-500)] text-[var(--color-brand-primary-500)] bg-[var(--color-bg-default)]",
26
- status === "upcoming" && "border-2 border-[var(--color-border-default)] text-[var(--color-text-muted)] bg-[var(--color-bg-default)]"
24
+ status === "complete" && "bg-[var(--color-primary)] text-[var(--color-on-primary)]",
25
+ status === "current" && "border-2 border-[var(--color-primary)] text-[var(--color-primary)] bg-[var(--color-surface)]",
26
+ status === "upcoming" && "border-2 border-[var(--color-outline-variant)] text-[var(--color-on-surface-muted)] bg-[var(--color-surface)]"
27
27
  ),
28
28
  "aria-current": status === "current" ? "step" : void 0,
29
29
  children: status === "complete" ? /* @__PURE__ */ jsx(Check, { size: "xs", "aria-hidden": "true" }) : index + 1
30
30
  }
31
31
  ),
32
32
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 pt-0.5", children: [
33
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-text-primary)]", children: step.label }),
34
- step.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--color-text-muted)]", children: step.description })
33
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-[var(--color-on-surface)]", children: step.label }),
34
+ step.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--color-on-surface-muted)]", children: step.description })
35
35
  ] })
36
36
  ] }, step.label);
37
37
  }) })
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  AppLauncher
4
- } from "./chunk-ZFHA46UX.js";
4
+ } from "./chunk-H5UTW5ML.js";
5
5
 
6
6
  // src/components/AppHeader/AppHeader.tsx
7
7
  import * as React from "react";
@@ -19,7 +19,6 @@ import {
19
19
  import { Building2, ChevronDown, Moon, Search, Sun } from "@nexub/icons";
20
20
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
21
21
  var SEARCH_DEBOUNCE_MS = 200;
22
- var focusRing = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring-default)] focus-visible:ring-offset-2 ring-offset-[var(--color-ring-offset)]";
23
22
  var isPlainClick = (e) => e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;
24
23
  var isTextField = (t) => t instanceof HTMLElement && (t.isContentEditable || t.matches("input, textarea, select"));
25
24
  function HeaderSearch({
@@ -99,13 +98,10 @@ function HeaderSearch({
99
98
  {
100
99
  type: "button",
101
100
  ...triggerProps,
102
- className: cn(
103
- "hidden h-9 w-full max-w-xl items-center gap-2 rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-subtle)] px-3 text-sm text-[var(--color-text-muted)] transition-colors hover:bg-[var(--color-bg-muted)] md:flex",
104
- focusRing
105
- ),
101
+ className: "nx-state-layer hidden h-9 w-full max-w-xl items-center gap-2 rounded-shape-field border border-[var(--color-outline-variant)] bg-[var(--color-surface-container-low)] px-3 text-sm text-[var(--color-on-surface-muted)] md:flex",
106
102
  children: [
107
103
  /* @__PURE__ */ jsx(Search, { size: "sm", "aria-hidden": "true", className: "shrink-0" }),
108
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: placeholder }),
104
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-start", children: placeholder }),
109
105
  /* @__PURE__ */ jsx(Kbd, { "aria-hidden": "true", className: "shrink-0", children: isMac ? "\u2318K" : "Ctrl K" })
110
106
  ]
111
107
  }
@@ -122,7 +118,7 @@ function HeaderSearch({
122
118
  children: [
123
119
  /* @__PURE__ */ jsx(CommandPalette.Input, { placeholder, value: query, onValueChange: setQuery }),
124
120
  /* @__PURE__ */ jsxs(CommandPalette.List, { children: [
125
- loading && /* @__PURE__ */ jsx("div", { role: "status", className: "px-3 py-2 text-sm text-[var(--color-text-muted)]", children: "Searching\u2026" }),
121
+ loading && /* @__PURE__ */ jsx("div", { role: "status", className: "px-3 py-2 text-sm text-[var(--color-on-surface-muted)]", children: "Searching\u2026" }),
126
122
  !loading && /* @__PURE__ */ jsx(CommandPalette.Empty, { children: failed ? "Search failed. Try again." : query ? `No results for \u201C${query}\u201D` : "Type to search" }),
127
123
  groups.map((group) => /* @__PURE__ */ jsx(CommandPalette.Group, { heading: group.label, children: group.items.map((item) => /* @__PURE__ */ jsxs(
128
124
  CommandPalette.Item,
@@ -134,9 +130,9 @@ function HeaderSearch({
134
130
  item.onSelect();
135
131
  },
136
132
  children: [
137
- item.icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "mr-2 flex shrink-0 text-[var(--color-text-muted)]", children: item.icon }),
133
+ item.icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "me-2 flex shrink-0 text-[var(--color-on-surface-muted)]", children: item.icon }),
138
134
  /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: item.label }),
139
- item.hint && /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 pl-3 text-xs text-[var(--color-text-muted)]", children: item.hint })
135
+ item.hint && /* @__PURE__ */ jsx("span", { className: "ms-auto shrink-0 ps-3 text-xs text-[var(--color-on-surface-muted)]", children: item.hint })
140
136
  ]
141
137
  },
142
138
  item.id
@@ -156,7 +152,7 @@ function OrgSwitcher({
156
152
  const current = orgs.find((o) => o.id === currentOrgId) ?? orgs[0];
157
153
  if (!current) return null;
158
154
  if (orgs.length === 1) {
159
- return /* @__PURE__ */ jsx("span", { title: current.name, className: cn("px-2 text-sm font-medium text-[var(--color-text-secondary)]", orgLabelClass), children: current.name });
155
+ return /* @__PURE__ */ jsx("span", { title: current.name, className: cn("px-2 text-sm font-medium text-[var(--color-on-surface-variant)]", orgLabelClass), children: current.name });
160
156
  }
161
157
  return (
162
158
  // modal={false}: Radix's modal menu injects a <style> scroll lock (strict CSP).
@@ -167,14 +163,11 @@ function OrgSwitcher({
167
163
  type: "button",
168
164
  "aria-label": `${current.name}, switch organization`,
169
165
  title: current.name,
170
- className: cn(
171
- "inline-flex h-9 min-w-0 shrink-0 items-center gap-1 rounded-md px-2 text-sm sm:shrink font-medium text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-bg-subtle)]",
172
- focusRing
173
- ),
166
+ className: "nx-state-layer inline-flex h-9 min-w-0 shrink-0 items-center gap-1 rounded-shape-control px-2 text-sm sm:shrink font-medium text-[var(--color-on-surface)]",
174
167
  children: [
175
168
  /* @__PURE__ */ jsx(Building2, { size: "md", "aria-hidden": "true", className: "shrink-0 sm:hidden" }),
176
169
  /* @__PURE__ */ jsx("span", { className: cn("hidden sm:inline", orgLabelClass), children: current.name }),
177
- /* @__PURE__ */ jsx(ChevronDown, { size: "sm", "aria-hidden": "true", className: "shrink-0 text-[var(--color-text-muted)]" })
170
+ /* @__PURE__ */ jsx(ChevronDown, { size: "sm", "aria-hidden": "true", className: "shrink-0 text-[var(--color-on-surface-muted)]" })
178
171
  ]
179
172
  }
180
173
  ) }),
@@ -231,7 +224,7 @@ function AccountMenu({
231
224
  return (
232
225
  // modal={false}: Radix's modal menu injects a <style> scroll lock (strict CSP).
233
226
  /* @__PURE__ */ jsxs(DropdownMenu.Root, { open, onOpenChange: setOpen, modal: false, children: [
234
- /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Account: ${user.name}`, className: cn("ml-1 shrink-0 rounded-full", focusRing), children: /* @__PURE__ */ jsx(Avatar, { name: user.name, src: user.avatarUrl, size: "sm", "aria-hidden": "true" }) }) }),
227
+ /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Account: ${user.name}`, className: "ms-1 shrink-0 rounded-full", children: /* @__PURE__ */ jsx(Avatar, { name: user.name, src: user.avatarUrl, size: "sm", "aria-hidden": "true" }) }) }),
235
228
  /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs(DropdownMenu.Content, { align: "end", className: "w-72 max-w-[calc(100vw-1rem)]", children: [
236
229
  /* @__PURE__ */ jsxs(DropdownMenu.Label, { className: "flex flex-col gap-0.5 py-2", children: [
237
230
  /* @__PURE__ */ jsx("span", { className: "truncate text-title-sm text-[var(--color-on-surface)]", children: user.name }),
@@ -268,6 +261,7 @@ var AppHeader = React.forwardRef(
268
261
  onSignOut,
269
262
  accountMenuItems,
270
263
  surface = "solid",
264
+ condense = false,
271
265
  className,
272
266
  ...props
273
267
  }, ref) => {
@@ -279,6 +273,7 @@ var AppHeader = React.forwardRef(
279
273
  ref,
280
274
  role: "banner",
281
275
  surface,
276
+ "data-condense": condense ? "" : void 0,
282
277
  className: cn("fixed inset-x-0 top-0 h-[var(--app-header-height,64px)] px-2 sm:px-4", className),
283
278
  ...props,
284
279
  children: /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 items-center gap-1 sm:gap-2", children: [
@@ -293,11 +288,11 @@ var AppHeader = React.forwardRef(
293
288
  onNavigate(homeHref);
294
289
  }
295
290
  },
296
- className: cn("flex min-w-0 shrink items-center rounded-md p-1", focusRing),
291
+ className: "flex min-w-0 shrink items-center rounded-shape-min p-1",
297
292
  children: /* @__PURE__ */ jsx(Logo, { size: "sm", product: productName })
298
293
  }
299
294
  ),
300
- /* @__PURE__ */ jsx("div", { className: "ml-auto flex shrink-0 md:ml-0 md:min-w-0 md:flex-1 md:justify-center md:px-2 lg:px-4", children: (onSearch || searchItems) && /* @__PURE__ */ jsx(HeaderSearch, { placeholder, items: searchItems, onSearch }) }),
295
+ /* @__PURE__ */ jsx("div", { className: "ms-auto flex shrink-0 md:ms-0 md:min-w-0 md:flex-1 md:justify-center md:px-2 lg:px-4", children: (onSearch || searchItems) && /* @__PURE__ */ jsx(HeaderSearch, { placeholder, items: searchItems, onSearch }) }),
301
296
  /* @__PURE__ */ jsxs("nav", { "aria-label": "Account and apps", className: "flex shrink-0 items-center gap-0.5 sm:gap-1", children: [
302
297
  orgs.length > 0 && /* @__PURE__ */ jsx(OrgSwitcher, { orgs, currentOrgId, onOrgChange }),
303
298
  showThemeSwitch && /* @__PURE__ */ jsx(ThemeSwitch, {}),
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+
3
+ // src/components/ErrorSummary/ErrorSummary.tsx
4
+ import * as React from "react";
5
+ import { cn } from "@nexub/react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var ErrorSummary = React.forwardRef(
8
+ ({ errors, title = "There is a problem", onFocusField, className, ...props }, forwardedRef) => {
9
+ const ref = React.useRef(null);
10
+ const headingId = React.useId();
11
+ React.useEffect(() => {
12
+ if (errors.length) ref.current?.focus();
13
+ }, [errors]);
14
+ if (!errors.length) return null;
15
+ return /* @__PURE__ */ jsxs(
16
+ "div",
17
+ {
18
+ ref: (node) => {
19
+ ref.current = node;
20
+ if (typeof forwardedRef === "function") forwardedRef(node);
21
+ else if (forwardedRef) forwardedRef.current = node;
22
+ },
23
+ role: "alert",
24
+ "aria-labelledby": headingId,
25
+ tabIndex: -1,
26
+ className: cn(
27
+ "w-full rounded-shape-container border-s-4 border-s-[var(--color-error)] p-4",
28
+ "bg-[var(--color-error-container)] text-[var(--color-on-error-container)]",
29
+ className
30
+ ),
31
+ ...props,
32
+ children: [
33
+ /* @__PURE__ */ jsx("h2", { id: headingId, className: "text-title-md font-semibold", children: title }),
34
+ /* @__PURE__ */ jsx("ul", { className: "mt-2 flex flex-col gap-1 text-body-md", children: errors.map((error) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
35
+ "a",
36
+ {
37
+ href: `#${error.id}`,
38
+ className: "underline underline-offset-[0.2em]",
39
+ onClick: (e) => {
40
+ const field = document.getElementById(error.id);
41
+ if (!field) return;
42
+ e.preventDefault();
43
+ field.scrollIntoView({ block: "center" });
44
+ field.focus({ preventScroll: true });
45
+ onFocusField?.(error.id);
46
+ },
47
+ children: error.message
48
+ }
49
+ ) }, error.id)) })
50
+ ]
51
+ }
52
+ );
53
+ }
54
+ );
55
+ ErrorSummary.displayName = "ErrorSummary";
56
+ function useFormErrors(map) {
57
+ const errors = React.useMemo(
58
+ () => Object.entries(map).flatMap(([id, message]) => message ? [{ id, message }] : []),
59
+ [map]
60
+ );
61
+ return {
62
+ errors,
63
+ fieldProps: (id) => ({
64
+ id,
65
+ "aria-invalid": map[id] ? true : void 0,
66
+ "aria-describedby": map[id] ? `${id}-error` : void 0
67
+ }),
68
+ errorProps: (id) => ({ id: `${id}-error` })
69
+ };
70
+ }
71
+
72
+ export {
73
+ ErrorSummary,
74
+ useFormErrors
75
+ };
@@ -7,8 +7,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  var SettingsSection = React.forwardRef(
8
8
  ({ title, description, actions, children, className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-8", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 md:flex-row md:gap-8", children: [
9
9
  /* @__PURE__ */ jsxs("div", { className: "md:w-1/3 flex flex-col gap-1", children: [
10
- /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--color-text-primary)]", children: title }),
11
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
10
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--color-on-surface)]", children: title }),
11
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-on-surface-muted)]", children: description })
12
12
  ] }),
13
13
  /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4", children: [
14
14
  children,
@@ -36,8 +36,8 @@ var Root = React.forwardRef(
36
36
  }
37
37
  setMenuOpen(false);
38
38
  };
39
- const menuButton = /* @__PURE__ */ jsx(IconButton, { label: `Open ${navLabel.toLowerCase()} menu`, className: "-ml-2 md:hidden", onClick: () => setMenuOpen(true), children: /* @__PURE__ */ jsx(Menu, { size: "md", "aria-hidden": "true" }) });
40
- return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex min-h-screen flex-col bg-[var(--color-bg-default)]", className), ...props, children: [
39
+ const menuButton = /* @__PURE__ */ jsx(IconButton, { label: `Open ${navLabel.toLowerCase()} menu`, className: "-ms-2 md:hidden", onClick: () => setMenuOpen(true), children: /* @__PURE__ */ jsx(Menu, { size: "md", "aria-hidden": "true" }) });
40
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex min-h-screen flex-col bg-[var(--color-surface)]", className), ...props, children: [
41
41
  header ? header(menuButton) : /* @__PURE__ */ jsxs(TopNav.Root, { className: "h-[var(--app-header-height,64px)]", children: [
42
42
  menuButton,
43
43
  logo && /* @__PURE__ */ jsx(TopNav.Logo, { className: "min-w-0 shrink", children: logo }),
@@ -56,8 +56,8 @@ var Root = React.forwardRef(
56
56
  ),
57
57
  /* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 px-4 py-6 md:px-8 md:py-10", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-6", children: [
58
58
  (title || description) && /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
59
- title && /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight text-[var(--color-text-primary)]", children: title }),
60
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
59
+ title && /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight text-[var(--color-on-surface)]", children: title }),
60
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-on-surface-muted)]", children: description })
61
61
  ] }),
62
62
  children
63
63
  ] }) })
@@ -66,7 +66,7 @@ var Root = React.forwardRef(
66
66
  /* @__PURE__ */ jsx(Sheet.Overlay, { className: "md:hidden" }),
67
67
  /* @__PURE__ */ jsxs(Sheet.Content, { side: "left", "aria-describedby": void 0, className: "md:hidden", children: [
68
68
  /* @__PURE__ */ jsx(Sheet.Title, { className: "px-5 pb-2 pt-5 text-base", children: navLabel }),
69
- /* @__PURE__ */ jsx(SectionNav, { items, activeId, onChoose, label: navLabel, className: "w-full border-r-0" })
69
+ /* @__PURE__ */ jsx(SectionNav, { items, activeId, onChoose, label: navLabel, className: "w-full border-e-0" })
70
70
  ] })
71
71
  ] }) })
72
72
  ] });
@@ -75,9 +75,9 @@ var Root = React.forwardRef(
75
75
  Root.displayName = "SettingsLayout";
76
76
  var Section = React.forwardRef(
77
77
  ({ title, description, className, children, ...props }, ref) => /* @__PURE__ */ jsx(Card, { asChild: true, className: cn("px-5 py-1", className), children: /* @__PURE__ */ jsxs("section", { ref, ...props, children: [
78
- (title || description) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 border-b border-[var(--color-border-default)] pb-4 pt-4", children: [
79
- title && /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--color-text-primary)]", children: title }),
80
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
78
+ (title || description) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 border-b border-[var(--color-outline-variant)] pb-4 pt-4", children: [
79
+ title && /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-[var(--color-on-surface)]", children: title }),
80
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-on-surface-muted)]", children: description })
81
81
  ] }),
82
82
  children
83
83
  ] }) })
@@ -89,16 +89,16 @@ var SettingsRow = React.forwardRef(
89
89
  {
90
90
  ref,
91
91
  className: cn(
92
- "flex flex-col gap-2 border-t border-[var(--color-border-default)] py-4 first:border-t-0 sm:flex-row sm:items-center sm:gap-4",
92
+ "flex flex-col gap-2 border-t border-[var(--color-outline-variant)] py-4 first:border-t-0 sm:flex-row sm:items-center sm:gap-4",
93
93
  className
94
94
  ),
95
95
  ...props,
96
96
  children: [
97
97
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5 sm:w-1/3 sm:shrink-0", children: [
98
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--color-text-primary)]", children: label }),
99
- description && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-text-muted)]", children: description })
98
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--color-on-surface)]", children: label }),
99
+ description && /* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--color-on-surface-muted)]", children: description })
100
100
  ] }),
101
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 break-words text-sm text-[var(--color-text-secondary)]", children: value }),
101
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 break-words text-sm text-[var(--color-on-surface-variant)]", children: value }),
102
102
  action && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: action })
103
103
  ]
104
104
  }
@@ -9,7 +9,7 @@ var Root = React.forwardRef(
9
9
  "dl",
10
10
  {
11
11
  ref,
12
- className: cn("divide-y divide-[var(--color-border-default)]", className),
12
+ className: cn("divide-y divide-[var(--color-outline-variant)]", className),
13
13
  ...props,
14
14
  children
15
15
  }
@@ -17,8 +17,8 @@ var Root = React.forwardRef(
17
17
  );
18
18
  Root.displayName = "InfoList.Root";
19
19
  var Item = ({ label, value, className }) => /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between py-3 gap-4", className), children: [
20
- /* @__PURE__ */ jsx("dt", { className: "text-sm text-[var(--color-text-muted)] shrink-0", children: label }),
21
- /* @__PURE__ */ jsx("dd", { className: "text-sm font-medium text-[var(--color-text-primary)] text-right", children: value })
20
+ /* @__PURE__ */ jsx("dt", { className: "text-sm text-[var(--color-on-surface-muted)] shrink-0", children: label }),
21
+ /* @__PURE__ */ jsx("dd", { className: "text-sm font-medium text-[var(--color-on-surface)] text-end", children: value })
22
22
  ] });
23
23
  Item.displayName = "InfoList.Item";
24
24
  var InfoList = { Root, Item };
@@ -0,0 +1,109 @@
1
+ 'use client';
2
+
3
+ // src/components/Conversation/Conversation.tsx
4
+ import * as React from "react";
5
+ import { Button, cn, useNexubMessages } from "@nexub/react";
6
+ import { ArrowDown } from "@nexub/icons";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ var fallbackMessages = {
9
+ jumpToLatest: "Jump to latest",
10
+ conversation: "Conversation"
11
+ };
12
+ var STICK_THRESHOLD = 48;
13
+ function atBottom(el) {
14
+ return el.scrollHeight - el.scrollTop - el.clientHeight <= STICK_THRESHOLD;
15
+ }
16
+ var Conversation = React.forwardRef(
17
+ ({ children, composer, autoScroll = true, announce = false, messages, className, ...props }, ref) => {
18
+ const provided = useNexubMessages();
19
+ const m = {
20
+ jumpToLatest: messages?.jumpToLatest ?? provided.jumpToLatest ?? fallbackMessages.jumpToLatest,
21
+ conversation: messages?.conversation ?? provided.conversation ?? fallbackMessages.conversation
22
+ };
23
+ const scrollRef = React.useRef(null);
24
+ const contentRef = React.useRef(null);
25
+ const pinnedRef = React.useRef(true);
26
+ const lastTop = React.useRef(0);
27
+ const [pinned, setPinned] = React.useState(true);
28
+ const setPin = React.useCallback((value) => {
29
+ pinnedRef.current = value;
30
+ setPinned(value);
31
+ }, []);
32
+ React.useEffect(() => {
33
+ const scroller = scrollRef.current;
34
+ const content = contentRef.current;
35
+ if (!autoScroll || !scroller || !content) return;
36
+ const follow = () => {
37
+ if (pinnedRef.current) scroller.scrollTop = scroller.scrollHeight;
38
+ lastTop.current = scroller.scrollTop;
39
+ };
40
+ follow();
41
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(follow) : void 0;
42
+ ro?.observe(content);
43
+ const mo = new MutationObserver(follow);
44
+ mo.observe(content, { childList: true, subtree: true, characterData: true });
45
+ return () => {
46
+ ro?.disconnect();
47
+ mo.disconnect();
48
+ };
49
+ }, [autoScroll]);
50
+ const onScroll = () => {
51
+ const el = scrollRef.current;
52
+ if (!el) return;
53
+ if (atBottom(el)) {
54
+ if (!pinnedRef.current) setPin(true);
55
+ } else if (el.scrollTop < lastTop.current && pinnedRef.current) {
56
+ setPin(false);
57
+ }
58
+ lastTop.current = el.scrollTop;
59
+ };
60
+ const jump = () => {
61
+ const el = scrollRef.current;
62
+ if (!el) return;
63
+ setPin(true);
64
+ const reduce = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
65
+ const top = el.scrollHeight;
66
+ if (typeof el.scrollTo === "function") el.scrollTo({ top, behavior: reduce ? "auto" : "smooth" });
67
+ else el.scrollTop = top;
68
+ };
69
+ const live = announce ? { role: "log", "aria-live": "polite", "aria-relevant": "additions" } : { role: "region" };
70
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex h-full min-h-0 flex-col", className), ...props, children: [
71
+ /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1", children: [
72
+ /* @__PURE__ */ jsx(
73
+ "div",
74
+ {
75
+ ref: scrollRef,
76
+ onScroll,
77
+ "aria-label": m.conversation,
78
+ tabIndex: 0,
79
+ ...live,
80
+ className: cn(
81
+ "h-full overflow-y-auto",
82
+ composer && "[-webkit-mask-image:linear-gradient(to_bottom,black_calc(100%-24px),transparent)] [mask-image:linear-gradient(to_bottom,black_calc(100%-24px),transparent)]"
83
+ ),
84
+ children: /* @__PURE__ */ jsx("div", { ref: contentRef, className: "mx-auto flex w-full max-w-3xl flex-col gap-6 px-4 pb-8 pt-6", children })
85
+ }
86
+ ),
87
+ autoScroll && !pinned && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-4 flex justify-center", children: /* @__PURE__ */ jsx(
88
+ Button,
89
+ {
90
+ type: "button",
91
+ variant: "tonal",
92
+ size: "sm",
93
+ "data-state": "open",
94
+ onClick: jump,
95
+ leadingIcon: /* @__PURE__ */ jsx(ArrowDown, { size: "sm", "aria-hidden": "true" }),
96
+ className: "nx-motion nx-pop pointer-events-auto shadow-elevation-2",
97
+ children: m.jumpToLatest
98
+ }
99
+ ) })
100
+ ] }),
101
+ composer && /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-3xl shrink-0 px-4 pb-4", children: composer })
102
+ ] });
103
+ }
104
+ );
105
+ Conversation.displayName = "Conversation";
106
+
107
+ export {
108
+ Conversation
109
+ };
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+
3
+ // src/components/BentoGrid/BentoGrid.tsx
4
+ import * as React from "react";
5
+ import { Card, cn } from "@nexub/react";
6
+ import { jsx } from "react/jsx-runtime";
7
+ var BentoGrid = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("[container-type:inline-size]", className), ...props, children: /* @__PURE__ */ jsx("div", { className: "grid grid-flow-row-dense auto-rows-[minmax(10rem,auto)] grid-cols-1 gap-4 [@container(min-width:480px)]:grid-cols-2 [@container(min-width:768px)]:grid-cols-4", children }) }));
8
+ BentoGrid.displayName = "BentoGrid";
9
+ var sizes = {
10
+ "1x1": "",
11
+ "2x1": "[@container(min-width:480px)]:col-span-2",
12
+ "1x2": "row-span-2",
13
+ "2x2": "[@container(min-width:480px)]:col-span-2 row-span-2"
14
+ };
15
+ var BentoTile = React.forwardRef(
16
+ ({ size = "1x1", className, ...props }, ref) => /* @__PURE__ */ jsx(Card, { ref, variant: "surface", className: cn("min-w-0 p-4", sizes[size], className), "data-size": size, ...props })
17
+ );
18
+ BentoTile.displayName = "BentoTile";
19
+
20
+ export {
21
+ BentoGrid,
22
+ BentoTile
23
+ };
@@ -23,27 +23,27 @@ var PlanCard = React.forwardRef(
23
23
  {
24
24
  ref,
25
25
  className: cn(
26
- "relative flex flex-col gap-6 rounded-xl border bg-[var(--color-bg-default)] p-6",
27
- highlighted ? "border-[var(--color-brand-primary-500)] shadow-lg ring-1 ring-[var(--color-brand-primary-500)]" : "border-[var(--color-border-default)]",
26
+ "relative flex flex-col gap-6 rounded-shape-container bg-[var(--color-surface-elevated)] text-[var(--color-on-surface)] p-6",
27
+ highlighted ? "border-2 border-[var(--color-primary)] shadow-elevation-2" : "border border-[color-mix(in_srgb,var(--color-outline-variant)_60%,transparent)] shadow-elevation-1",
28
28
  className
29
29
  ),
30
30
  ...props,
31
31
  children: [
32
32
  badge && /* @__PURE__ */ jsx("div", { className: "absolute -top-3 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsx(Badge, { tone: "info", children: badge }) }),
33
33
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
34
- /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-[var(--color-text-primary)]", children: name }),
34
+ /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-[var(--color-on-surface)]", children: name }),
35
35
  /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1", children: [
36
- /* @__PURE__ */ jsx("span", { className: "text-3xl font-bold text-[var(--color-text-primary)]", children: price }),
37
- period && /* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--color-text-muted)]", children: period })
36
+ /* @__PURE__ */ jsx("span", { className: "text-3xl font-bold text-[var(--color-on-surface)]", children: price }),
37
+ period && /* @__PURE__ */ jsx("span", { className: "text-sm text-[var(--color-on-surface-muted)]", children: period })
38
38
  ] }),
39
- description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-text-muted)]", children: description })
39
+ description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--color-on-surface-muted)]", children: description })
40
40
  ] }),
41
41
  /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", children: features.map((feature) => /* @__PURE__ */ jsxs(
42
42
  "li",
43
43
  {
44
- className: "flex items-center gap-2 text-sm text-[var(--color-text-secondary)]",
44
+ className: "flex items-center gap-2 text-sm text-[var(--color-on-surface-variant)]",
45
45
  children: [
46
- /* @__PURE__ */ jsx(Check, { size: "xs", className: "text-[var(--color-success-500)] shrink-0", "aria-hidden": "true" }),
46
+ /* @__PURE__ */ jsx(Check, { size: "xs", className: "text-[var(--color-success)] shrink-0", "aria-hidden": "true" }),
47
47
  feature
48
48
  ]
49
49
  },
@@ -13,11 +13,11 @@ var FilterBar = React.forwardRef(
13
13
  placeholder: searchPlaceholder ?? "Search\u2026",
14
14
  value: searchValue,
15
15
  onChange: (e) => onSearch?.(e.target.value),
16
- className: "h-9 rounded-md border border-[var(--color-border-default)] bg-[var(--color-bg-default)] px-3 py-2 text-sm placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-primary-500)] w-full sm:w-64"
16
+ className: "h-9 rounded-shape-field border border-[var(--color-outline-variant)] bg-[var(--color-surface-container-lowest)] px-3 py-2 text-sm text-[var(--color-on-surface)] placeholder:text-[var(--color-on-surface-muted)] w-full sm:w-64"
17
17
  }
18
18
  ),
19
19
  filters && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: filters }),
20
- actions && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-2", children: actions })
20
+ actions && /* @__PURE__ */ jsx("div", { className: "ms-auto flex items-center gap-2", children: actions })
21
21
  ] })
22
22
  );
23
23
  FilterBar.displayName = "FilterBar";
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  ActivityFeed
4
- } from "../../chunk-YX7UO577.js";
4
+ } from "../../chunk-HYC5N5MO.js";
5
5
  export {
6
6
  ActivityFeed
7
7
  };
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
  import {
3
3
  AppHeader
4
- } from "../../chunk-OYSWFXRD.js";
5
- import "../../chunk-ZFHA46UX.js";
4
+ } from "../../chunk-LAYYOVIK.js";
5
+ import "../../chunk-H5UTW5ML.js";
6
6
  export {
7
7
  AppHeader
8
8
  };
@@ -2,7 +2,7 @@
2
2
  import "../../chunk-VYMTTYPR.js";
3
3
  import {
4
4
  AppLauncher
5
- } from "../../chunk-ZFHA46UX.js";
5
+ } from "../../chunk-H5UTW5ML.js";
6
6
  export {
7
7
  AppLauncher
8
8
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  AuthCard
4
- } from "../../chunk-AOE6GQSH.js";
4
+ } from "../../chunk-HMJRSNJD.js";
5
5
  export {
6
6
  AuthCard
7
7
  };
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import {
3
+ BentoGrid,
4
+ BentoTile
5
+ } from "../../chunk-WZTDCJYP.js";
6
+ export {
7
+ BentoGrid,
8
+ BentoTile
9
+ };
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+ import {
3
+ Conversation
4
+ } from "../../chunk-WRP73TXG.js";
5
+ export {
6
+ Conversation
7
+ };
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import {
3
+ ErrorSummary,
4
+ useFormErrors
5
+ } from "../../chunk-RWPFCNMJ.js";
6
+ export {
7
+ ErrorSummary,
8
+ useFormErrors
9
+ };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  FilterBar
4
- } from "../../chunk-EOA5C3Q4.js";
4
+ } from "../../chunk-Z6VMCMSW.js";
5
5
  export {
6
6
  FilterBar
7
7
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  InfoList
4
- } from "../../chunk-74JVLMLE.js";
4
+ } from "../../chunk-WPNT5JOU.js";
5
5
  export {
6
6
  InfoList
7
7
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  ListDetail
4
- } from "../../chunk-TLOLIMPY.js";
4
+ } from "../../chunk-5JU6HUVJ.js";
5
5
  export {
6
6
  ListDetail
7
7
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  NotificationItem
4
- } from "../../chunk-Y3TNGDKC.js";
4
+ } from "../../chunk-5IORYY2L.js";
5
5
  export {
6
6
  NotificationItem
7
7
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  PlanCard
4
- } from "../../chunk-QMYBXQG4.js";
4
+ } from "../../chunk-YB7G4YBU.js";
5
5
  export {
6
6
  PlanCard
7
7
  };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  SettingsLayout,
4
4
  SettingsRow
5
- } from "../../chunk-5QFC4WFD.js";
5
+ } from "../../chunk-WIHNK7RT.js";
6
6
  export {
7
7
  SettingsLayout,
8
8
  SettingsRow
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  SettingsSection
4
- } from "../../chunk-OTNHVOPC.js";
4
+ } from "../../chunk-U3FT7XAU.js";
5
5
  export {
6
6
  SettingsSection
7
7
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  StatCard
4
- } from "../../chunk-7GYTYFUD.js";
4
+ } from "../../chunk-K5EFLRKU.js";
5
5
  export {
6
6
  StatCard
7
7
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  StepIndicator
4
- } from "../../chunk-MXPRKZPB.js";
4
+ } from "../../chunk-L456OB3Q.js";
5
5
  export {
6
6
  StepIndicator
7
7
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import {
3
3
  UserCard
4
- } from "../../chunk-AEYLZU3F.js";
4
+ } from "../../chunk-7NYGYETG.js";
5
5
  export {
6
6
  UserCard
7
7
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import * as React from 'react';
2
+ import { CardProps } from '@nexub/react';
3
3
 
4
4
  interface ActivityItemProps {
5
5
  avatar?: React.ReactNode;
@@ -11,7 +11,7 @@ interface ActivityItemProps {
11
11
  declare const ActivityFeed: {
12
12
  Root: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLUListElement> & React.RefAttributes<HTMLUListElement>>;
13
13
  Item: {
14
- ({ avatar, title, description, timestamp, className }: ActivityItemProps): react_jsx_runtime.JSX.Element;
14
+ ({ avatar, title, description, timestamp, className }: ActivityItemProps): React.JSX.Element;
15
15
  displayName: string;
16
16
  };
17
17
  };
@@ -32,6 +32,8 @@ interface StatCardProps extends React.HTMLAttributes<HTMLDivElement> {
32
32
  deltaType?: 'increase' | 'decrease' | 'neutral';
33
33
  icon?: React.ReactNode;
34
34
  description?: string;
35
+ /** Roll digits when a number/string `value` changes (NumberTicker); announced politely. */
36
+ animate?: boolean;
35
37
  }
36
38
  declare const StatCard: React.ForwardRefExoticComponent<StatCardProps & React.RefAttributes<HTMLDivElement>>;
37
39
 
@@ -54,7 +56,7 @@ interface AuthCardProps {
54
56
  footer?: React.ReactNode;
55
57
  className?: string;
56
58
  }
57
- declare function AuthCard({ logo, title, description, children, footer, className, }: AuthCardProps): react_jsx_runtime.JSX.Element;
59
+ declare function AuthCard({ logo, title, description, children, footer, className, }: AuthCardProps): React.JSX.Element;
58
60
  declare namespace AuthCard {
59
61
  var displayName: string;
60
62
  }
@@ -78,7 +80,7 @@ interface InfoListItemProps {
78
80
  declare const InfoList: {
79
81
  Root: React.ForwardRefExoticComponent<InfoListRootProps & React.RefAttributes<HTMLDListElement>>;
80
82
  Item: {
81
- ({ label, value, className }: InfoListItemProps): react_jsx_runtime.JSX.Element;
83
+ ({ label, value, className }: InfoListItemProps): React.JSX.Element;
82
84
  displayName: string;
83
85
  };
84
86
  };
@@ -264,6 +266,8 @@ interface AppHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, 'childr
264
266
  surface?: 'solid' | 'glass';
265
267
  /** `soft`: page content dissolves under the header instead of meeting a divider. */
266
268
  scrollEdge?: 'soft' | 'hard';
269
+ /** Shrink 64 → 52px and lift onto elevation 1 over the first 64px of page scroll (CSS scroll timeline; static where unsupported). */
270
+ condense?: boolean;
267
271
  }
268
272
  /**
269
273
  * The one top header every Nexub app shares. Fixed to the top; renders a
@@ -271,4 +275,83 @@ interface AppHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, 'childr
271
275
  */
272
276
  declare const AppHeader: React.ForwardRefExoticComponent<AppHeaderProps & React.RefAttributes<HTMLElement>>;
273
277
 
274
- export { ActivityFeed, type ActivityItemProps, AppHeader, type AppHeaderOrg, type AppHeaderProps, type AppHeaderSearchGroup, type AppHeaderSearchItem, type AppHeaderUser, AppLauncher, type AppLauncherApp, type AppLauncherProps, AuthCard, type AuthCardProps, FilterBar, type FilterBarProps, InfoList, type InfoListItemProps, type InfoListRootProps, ListDetail, type ListDetailProps, NotificationItem, type NotificationItemProps, PlanCard, type PlanCardProps, SettingsLayout, type SettingsLayoutItem, type SettingsLayoutProps, type SettingsLayoutSectionProps, SettingsRow, type SettingsRowProps, SettingsSection, type SettingsSectionProps, StatCard, type StatCardProps, StepIndicator, type StepIndicatorProps, type StepIndicatorStep, UserCard, type UserCardProps };
278
+ interface BentoGridProps extends React.HTMLAttributes<HTMLDivElement> {
279
+ }
280
+ /**
281
+ * A bento grid that responds to its *own* width (container query): 1 column,
282
+ * 2 at 480px, 4 at 768px. Dense packing back-fills gaps left by large tiles.
283
+ */
284
+ declare const BentoGrid: React.ForwardRefExoticComponent<BentoGridProps & React.RefAttributes<HTMLDivElement>>;
285
+ type BentoTileSize = '1x1' | '2x1' | '1x2' | '2x2';
286
+ interface BentoTileProps extends Omit<CardProps, 'variant'> {
287
+ /** @default '1x1' */
288
+ size?: BentoTileSize;
289
+ }
290
+ /** A Card-styled cell of a BentoGrid. */
291
+ declare const BentoTile: React.ForwardRefExoticComponent<BentoTileProps & React.RefAttributes<HTMLDivElement>>;
292
+
293
+ interface ErrorSummaryItem {
294
+ /** The field's DOM id (the link target). */
295
+ id: string;
296
+ message: string;
297
+ }
298
+ interface ErrorSummaryProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
299
+ errors: ErrorSummaryItem[];
300
+ /** @default 'There is a problem' */
301
+ title?: React.ReactNode;
302
+ /** Called after a link moves focus to its field. */
303
+ onFocusField?: (id: string) => void;
304
+ }
305
+ /**
306
+ * Lists a form's errors after a failed submit (GOV.UK pattern). It takes focus
307
+ * whenever it receives a new `errors` array with entries, so pass a new array per
308
+ * submit (not per keystroke). Each link focuses its field.
309
+ */
310
+ declare const ErrorSummary: React.ForwardRefExoticComponent<ErrorSummaryProps & React.RefAttributes<HTMLDivElement>>;
311
+ type FormErrorMap = Record<string, string | null | undefined | false>;
312
+ /**
313
+ * Turns `{ fieldId: message }` into the ErrorSummary list plus the ARIA wiring
314
+ * for each field and its inline message (`${id}-error`, the same id
315
+ * FormField.Error uses).
316
+ */
317
+ declare function useFormErrors(map: FormErrorMap): {
318
+ errors: ErrorSummaryItem[];
319
+ fieldProps: (id: string) => {
320
+ id: string;
321
+ 'aria-invalid': true | undefined;
322
+ 'aria-describedby': string | undefined;
323
+ };
324
+ errorProps: (id: string) => {
325
+ id: string;
326
+ };
327
+ };
328
+
329
+ interface ConversationMessages {
330
+ /** Floating button shown when the reader has scrolled up. */
331
+ jumpToLatest: string;
332
+ /** Accessible name of the message list. */
333
+ conversation: string;
334
+ }
335
+ interface ConversationProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
336
+ /** The messages. */
337
+ children?: React.ReactNode;
338
+ /** Sticky composer slot under the list, e.g. a PromptInput. */
339
+ composer?: React.ReactNode;
340
+ /** Keep the view pinned to the newest content while the reader is at the bottom. @default true */
341
+ autoScroll?: boolean;
342
+ /**
343
+ * Make the list a polite live log (role="log"). Off by default: token streaming
344
+ * would announce every chunk. Turn on for whole-message updates only.
345
+ * @default false
346
+ */
347
+ announce?: boolean;
348
+ messages?: Partial<ConversationMessages>;
349
+ }
350
+ /**
351
+ * Chat layout: a scrolling message list (max-w-3xl, centred) with a sticky
352
+ * composer at the bottom. Sticks to the bottom as content streams in while the
353
+ * reader is there; scrolling up stops following and offers "Jump to latest".
354
+ */
355
+ declare const Conversation: React.ForwardRefExoticComponent<ConversationProps & React.RefAttributes<HTMLDivElement>>;
356
+
357
+ export { ActivityFeed, type ActivityItemProps, AppHeader, type AppHeaderOrg, type AppHeaderProps, type AppHeaderSearchGroup, type AppHeaderSearchItem, type AppHeaderUser, AppLauncher, type AppLauncherApp, type AppLauncherProps, AuthCard, type AuthCardProps, BentoGrid, type BentoGridProps, BentoTile, type BentoTileProps, type BentoTileSize, Conversation, type ConversationMessages, type ConversationProps, ErrorSummary, type ErrorSummaryItem, type ErrorSummaryProps, FilterBar, type FilterBarProps, type FormErrorMap, InfoList, type InfoListItemProps, type InfoListRootProps, ListDetail, type ListDetailProps, NotificationItem, type NotificationItemProps, PlanCard, type PlanCardProps, SettingsLayout, type SettingsLayoutItem, type SettingsLayoutProps, type SettingsLayoutSectionProps, SettingsRow, type SettingsRowProps, SettingsSection, type SettingsSectionProps, StatCard, type StatCardProps, StepIndicator, type StepIndicatorProps, type StepIndicatorStep, UserCard, type UserCardProps, useFormErrors };
package/dist/index.js CHANGED
@@ -1,53 +1,68 @@
1
1
  'use client';
2
+ import {
3
+ StepIndicator
4
+ } from "./chunk-L456OB3Q.js";
5
+ import {
6
+ UserCard
7
+ } from "./chunk-7NYGYETG.js";
8
+ import {
9
+ FilterBar
10
+ } from "./chunk-Z6VMCMSW.js";
11
+ import {
12
+ InfoList
13
+ } from "./chunk-WPNT5JOU.js";
14
+ import {
15
+ ListDetail
16
+ } from "./chunk-5JU6HUVJ.js";
2
17
  import {
3
18
  NotificationItem
4
- } from "./chunk-Y3TNGDKC.js";
19
+ } from "./chunk-5IORYY2L.js";
5
20
  import {
6
21
  PlanCard
7
- } from "./chunk-QMYBXQG4.js";
22
+ } from "./chunk-YB7G4YBU.js";
8
23
  import {
9
24
  SettingsLayout,
10
25
  SettingsRow
11
- } from "./chunk-5QFC4WFD.js";
26
+ } from "./chunk-WIHNK7RT.js";
12
27
  import {
13
28
  SettingsSection
14
- } from "./chunk-OTNHVOPC.js";
29
+ } from "./chunk-U3FT7XAU.js";
15
30
  import {
16
31
  StatCard
17
- } from "./chunk-7GYTYFUD.js";
32
+ } from "./chunk-K5EFLRKU.js";
18
33
  import {
19
- StepIndicator
20
- } from "./chunk-MXPRKZPB.js";
21
- import {
22
- UserCard
23
- } from "./chunk-AEYLZU3F.js";
34
+ ActivityFeed
35
+ } from "./chunk-HYC5N5MO.js";
24
36
  import {
25
37
  AppHeader
26
- } from "./chunk-OYSWFXRD.js";
27
- import {
28
- ActivityFeed
29
- } from "./chunk-YX7UO577.js";
38
+ } from "./chunk-LAYYOVIK.js";
30
39
  import "./chunk-VYMTTYPR.js";
31
40
  import {
32
41
  AppLauncher
33
- } from "./chunk-ZFHA46UX.js";
42
+ } from "./chunk-H5UTW5ML.js";
34
43
  import {
35
44
  AuthCard
36
- } from "./chunk-AOE6GQSH.js";
45
+ } from "./chunk-HMJRSNJD.js";
37
46
  import {
38
- FilterBar
39
- } from "./chunk-EOA5C3Q4.js";
47
+ BentoGrid,
48
+ BentoTile
49
+ } from "./chunk-WZTDCJYP.js";
40
50
  import {
41
- InfoList
42
- } from "./chunk-74JVLMLE.js";
51
+ Conversation
52
+ } from "./chunk-WRP73TXG.js";
43
53
  import {
44
- ListDetail
45
- } from "./chunk-TLOLIMPY.js";
54
+ ErrorSummary,
55
+ useFormErrors
56
+ } from "./chunk-RWPFCNMJ.js";
46
57
  export {
47
58
  ActivityFeed,
48
59
  AppHeader,
49
60
  AppLauncher,
50
61
  AuthCard,
62
+ BentoGrid,
63
+ BentoTile,
64
+ Conversation,
65
+ ErrorSummary,
51
66
  FilterBar,
52
67
  InfoList,
53
68
  ListDetail,
@@ -58,5 +73,6 @@ export {
58
73
  SettingsSection,
59
74
  StatCard,
60
75
  StepIndicator,
61
- UserCard
76
+ UserCard,
77
+ useFormErrors
62
78
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexub/patterns",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "description": "Composite UI patterns for Nexub Design System — pre-built, production-ready layouts and widgets built on @nexub/react.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -36,9 +36,9 @@
36
36
  "peerDependencies": {
37
37
  "react": ">=18 <20",
38
38
  "react-dom": ">=18 <20",
39
- "@nexub/react": "^1.5.1",
40
- "@nexub/icons": "^1.5.1",
41
- "@nexub/tokens": "^1.5.1"
39
+ "@nexub/icons": "^1.7.0",
40
+ "@nexub/react": "^1.7.0",
41
+ "@nexub/tokens": "^1.7.0"
42
42
  },
43
43
  "peerDependenciesMeta": {
44
44
  "@nexub/icons": {
@@ -49,18 +49,19 @@
49
49
  }
50
50
  },
51
51
  "devDependencies": {
52
- "@testing-library/jest-dom": "^6.6.3",
53
- "@testing-library/react": "^16.1.0",
54
- "@testing-library/user-event": "^14.5.2",
55
- "@types/react": "^19.0.0",
56
- "@types/react-dom": "^19.0.0",
57
- "jsdom": "^25.0.1",
58
- "tsup": "^8.3.5",
59
- "typescript": "^5.6.3",
60
- "vitest": "^2.1.8",
61
- "@nexub/icons": "1.5.1",
62
- "@nexub/react": "1.5.1",
63
- "@nexub/tokens": "1.5.1"
52
+ "@testing-library/jest-dom": "^6.9.1",
53
+ "@testing-library/react": "^16.3.3",
54
+ "@testing-library/user-event": "^14.6.8",
55
+ "@types/react": "^19.3.0",
56
+ "@types/react-dom": "^19.3.0",
57
+ "jsdom": "^30.1.3",
58
+ "tsup": "^8.5.1",
59
+ "typescript": "^5.9.3",
60
+ "vite": "^7.3.7",
61
+ "vitest": "^5.0.5",
62
+ "@nexub/icons": "1.7.0",
63
+ "@nexub/react": "1.7.0",
64
+ "@nexub/tokens": "1.7.0"
64
65
  },
65
66
  "scripts": {
66
67
  "build": "tsup",