@braedonsaunders/appkit-ui 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 (300) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +11 -0
  3. package/account-menu.d.ts +49 -0
  4. package/account-menu.d.ts.map +1 -0
  5. package/account-menu.js +66 -0
  6. package/account-menu.js.map +1 -0
  7. package/alert.d.ts +13 -0
  8. package/alert.d.ts.map +1 -0
  9. package/alert.js +22 -0
  10. package/alert.js.map +1 -0
  11. package/animated-number.d.ts +13 -0
  12. package/animated-number.d.ts.map +1 -0
  13. package/animated-number.js +53 -0
  14. package/animated-number.js.map +1 -0
  15. package/api-reference.d.ts +44 -0
  16. package/api-reference.d.ts.map +1 -0
  17. package/api-reference.js +82 -0
  18. package/api-reference.js.map +1 -0
  19. package/app-shell.d.ts +39 -0
  20. package/app-shell.d.ts.map +1 -0
  21. package/app-shell.js +21 -0
  22. package/app-shell.js.map +1 -0
  23. package/app-sidebar.d.ts +22 -0
  24. package/app-sidebar.d.ts.map +1 -0
  25. package/app-sidebar.js +30 -0
  26. package/app-sidebar.js.map +1 -0
  27. package/avatar.d.ts +9 -0
  28. package/avatar.d.ts.map +1 -0
  29. package/avatar.js +18 -0
  30. package/avatar.js.map +1 -0
  31. package/badge.d.ts +9 -0
  32. package/badge.d.ts.map +1 -0
  33. package/badge.js +23 -0
  34. package/badge.js.map +1 -0
  35. package/button.d.ts +24 -0
  36. package/button.d.ts.map +1 -0
  37. package/button.js +50 -0
  38. package/button.js.map +1 -0
  39. package/card.d.ts +21 -0
  40. package/card.d.ts.map +1 -0
  41. package/card.js +36 -0
  42. package/card.js.map +1 -0
  43. package/checkbox.d.ts +4 -0
  44. package/checkbox.d.ts.map +1 -0
  45. package/checkbox.js +6 -0
  46. package/checkbox.js.map +1 -0
  47. package/confirm-dialog.d.ts +23 -0
  48. package/confirm-dialog.d.ts.map +1 -0
  49. package/confirm-dialog.js +67 -0
  50. package/confirm-dialog.js.map +1 -0
  51. package/context-menu.d.ts +47 -0
  52. package/context-menu.d.ts.map +1 -0
  53. package/context-menu.js +90 -0
  54. package/context-menu.js.map +1 -0
  55. package/dialog.d.ts +25 -0
  56. package/dialog.d.ts.map +1 -0
  57. package/dialog.js +89 -0
  58. package/dialog.js.map +1 -0
  59. package/document-title.d.ts +11 -0
  60. package/document-title.d.ts.map +1 -0
  61. package/document-title.js +17 -0
  62. package/document-title.js.map +1 -0
  63. package/drawer.d.ts +63 -0
  64. package/drawer.d.ts.map +1 -0
  65. package/drawer.js +188 -0
  66. package/drawer.js.map +1 -0
  67. package/empty-state.d.ts +10 -0
  68. package/empty-state.d.ts.map +1 -0
  69. package/empty-state.js +7 -0
  70. package/empty-state.js.map +1 -0
  71. package/file-uploader.d.ts +67 -0
  72. package/file-uploader.d.ts.map +1 -0
  73. package/file-uploader.js +156 -0
  74. package/file-uploader.js.map +1 -0
  75. package/filter-chips.d.ts +47 -0
  76. package/filter-chips.d.ts.map +1 -0
  77. package/filter-chips.js +62 -0
  78. package/filter-chips.js.map +1 -0
  79. package/global-search.d.ts +41 -0
  80. package/global-search.d.ts.map +1 -0
  81. package/global-search.js +116 -0
  82. package/global-search.js.map +1 -0
  83. package/index.d.ts +70 -0
  84. package/index.d.ts.map +1 -0
  85. package/index.js +70 -0
  86. package/index.js.map +1 -0
  87. package/input.d.ts +9 -0
  88. package/input.d.ts.map +1 -0
  89. package/input.js +11 -0
  90. package/input.js.map +1 -0
  91. package/label.d.ts +3 -0
  92. package/label.d.ts.map +1 -0
  93. package/label.js +6 -0
  94. package/label.js.map +1 -0
  95. package/line-grid.d.ts +48 -0
  96. package/line-grid.d.ts.map +1 -0
  97. package/line-grid.js +155 -0
  98. package/line-grid.js.map +1 -0
  99. package/link-context.d.ts +44 -0
  100. package/link-context.d.ts.map +1 -0
  101. package/link-context.js +32 -0
  102. package/link-context.js.map +1 -0
  103. package/list-nav.d.ts +28 -0
  104. package/list-nav.d.ts.map +1 -0
  105. package/list-nav.js +41 -0
  106. package/list-nav.js.map +1 -0
  107. package/list-pagination.d.ts +27 -0
  108. package/list-pagination.d.ts.map +1 -0
  109. package/list-pagination.js +43 -0
  110. package/list-pagination.js.map +1 -0
  111. package/list-params.d.ts +41 -0
  112. package/list-params.d.ts.map +1 -0
  113. package/list-params.js +84 -0
  114. package/list-params.js.map +1 -0
  115. package/list-search-input-state.d.ts +13 -0
  116. package/list-search-input-state.d.ts.map +1 -0
  117. package/list-search-input-state.js +27 -0
  118. package/list-search-input-state.js.map +1 -0
  119. package/list-search-input.d.ts +16 -0
  120. package/list-search-input.d.ts.map +1 -0
  121. package/list-search-input.js +51 -0
  122. package/list-search-input.js.map +1 -0
  123. package/mobile-tab-bar.d.ts +14 -0
  124. package/mobile-tab-bar.d.ts.map +1 -0
  125. package/mobile-tab-bar.js +25 -0
  126. package/mobile-tab-bar.js.map +1 -0
  127. package/multi-select.d.ts +32 -0
  128. package/multi-select.d.ts.map +1 -0
  129. package/multi-select.js +52 -0
  130. package/multi-select.js.map +1 -0
  131. package/nav-overflow.d.ts +12 -0
  132. package/nav-overflow.d.ts.map +1 -0
  133. package/nav-overflow.js +30 -0
  134. package/nav-overflow.js.map +1 -0
  135. package/navigation-config-editor.d.ts +26 -0
  136. package/navigation-config-editor.d.ts.map +1 -0
  137. package/navigation-config-editor.js +57 -0
  138. package/navigation-config-editor.js.map +1 -0
  139. package/navigation-config.d.ts +48 -0
  140. package/navigation-config.d.ts.map +1 -0
  141. package/navigation-config.js +75 -0
  142. package/navigation-config.js.map +1 -0
  143. package/navigation-mode.d.ts +16 -0
  144. package/navigation-mode.d.ts.map +1 -0
  145. package/navigation-mode.js +30 -0
  146. package/navigation-mode.js.map +1 -0
  147. package/notifications-bell.d.ts +22 -0
  148. package/notifications-bell.d.ts.map +1 -0
  149. package/notifications-bell.js +30 -0
  150. package/notifications-bell.js.map +1 -0
  151. package/org-chart.d.ts +85 -0
  152. package/org-chart.d.ts.map +1 -0
  153. package/org-chart.js +193 -0
  154. package/org-chart.js.map +1 -0
  155. package/package.json +61 -0
  156. package/page-header.d.ts +32 -0
  157. package/page-header.d.ts.map +1 -0
  158. package/page-header.js +14 -0
  159. package/page-header.js.map +1 -0
  160. package/page-layout.d.ts +29 -0
  161. package/page-layout.d.ts.map +1 -0
  162. package/page-layout.js +23 -0
  163. package/page-layout.js.map +1 -0
  164. package/page-transition.d.ts +13 -0
  165. package/page-transition.d.ts.map +1 -0
  166. package/page-transition.js +14 -0
  167. package/page-transition.js.map +1 -0
  168. package/paged-table.d.ts +54 -0
  169. package/paged-table.d.ts.map +1 -0
  170. package/paged-table.js +74 -0
  171. package/paged-table.js.map +1 -0
  172. package/popover.d.ts +16 -0
  173. package/popover.d.ts.map +1 -0
  174. package/popover.js +99 -0
  175. package/popover.js.map +1 -0
  176. package/progress.d.ts +7 -0
  177. package/progress.d.ts.map +1 -0
  178. package/progress.js +8 -0
  179. package/progress.js.map +1 -0
  180. package/prompt-dialog.d.ts +21 -0
  181. package/prompt-dialog.d.ts.map +1 -0
  182. package/prompt-dialog.js +109 -0
  183. package/prompt-dialog.js.map +1 -0
  184. package/record-list.d.ts +76 -0
  185. package/record-list.d.ts.map +1 -0
  186. package/record-list.js +51 -0
  187. package/record-list.js.map +1 -0
  188. package/search-select.d.ts +42 -0
  189. package/search-select.d.ts.map +1 -0
  190. package/search-select.js +194 -0
  191. package/search-select.js.map +1 -0
  192. package/select-options.d.ts +16 -0
  193. package/select-options.d.ts.map +1 -0
  194. package/select-options.js +119 -0
  195. package/select-options.js.map +1 -0
  196. package/select.d.ts +24 -0
  197. package/select.d.ts.map +1 -0
  198. package/select.js +65 -0
  199. package/select.js.map +1 -0
  200. package/separator.d.ts +6 -0
  201. package/separator.d.ts.map +1 -0
  202. package/separator.js +7 -0
  203. package/separator.js.map +1 -0
  204. package/settings-layout.d.ts +79 -0
  205. package/settings-layout.d.ts.map +1 -0
  206. package/settings-layout.js +74 -0
  207. package/settings-layout.js.map +1 -0
  208. package/sidebar-nav.d.ts +67 -0
  209. package/sidebar-nav.d.ts.map +1 -0
  210. package/sidebar-nav.js +291 -0
  211. package/sidebar-nav.js.map +1 -0
  212. package/signature-pad.d.ts +16 -0
  213. package/signature-pad.d.ts.map +1 -0
  214. package/signature-pad.js +355 -0
  215. package/signature-pad.js.map +1 -0
  216. package/skeleton.d.ts +9 -0
  217. package/skeleton.d.ts.map +1 -0
  218. package/skeleton.js +12 -0
  219. package/skeleton.js.map +1 -0
  220. package/slider.d.ts +13 -0
  221. package/slider.d.ts.map +1 -0
  222. package/slider.js +15 -0
  223. package/slider.js.map +1 -0
  224. package/sortable-th.d.ts +28 -0
  225. package/sortable-th.d.ts.map +1 -0
  226. package/sortable-th.js +35 -0
  227. package/sortable-th.js.map +1 -0
  228. package/sparkline.d.ts +16 -0
  229. package/sparkline.d.ts.map +1 -0
  230. package/sparkline.js +46 -0
  231. package/sparkline.js.map +1 -0
  232. package/spinner.d.ts +11 -0
  233. package/spinner.d.ts.map +1 -0
  234. package/spinner.js +11 -0
  235. package/spinner.js.map +1 -0
  236. package/styles.css +166 -0
  237. package/subtab-nav.d.ts +17 -0
  238. package/subtab-nav.d.ts.map +1 -0
  239. package/subtab-nav.js +11 -0
  240. package/subtab-nav.js.map +1 -0
  241. package/switch.d.ts +9 -0
  242. package/switch.d.ts.map +1 -0
  243. package/switch.js +11 -0
  244. package/switch.js.map +1 -0
  245. package/tab-content.d.ts +24 -0
  246. package/tab-content.d.ts.map +1 -0
  247. package/tab-content.js +26 -0
  248. package/tab-content.js.map +1 -0
  249. package/table.d.ts +28 -0
  250. package/table.d.ts.map +1 -0
  251. package/table.js +21 -0
  252. package/table.js.map +1 -0
  253. package/tabs.d.ts +18 -0
  254. package/tabs.d.ts.map +1 -0
  255. package/tabs.js +18 -0
  256. package/tabs.js.map +1 -0
  257. package/text-context.d.ts +9 -0
  258. package/text-context.d.ts.map +1 -0
  259. package/text-context.js +12 -0
  260. package/text-context.js.map +1 -0
  261. package/textarea.d.ts +4 -0
  262. package/textarea.d.ts.map +1 -0
  263. package/textarea.js +6 -0
  264. package/textarea.js.map +1 -0
  265. package/theme-script.d.ts +3 -0
  266. package/theme-script.d.ts.map +1 -0
  267. package/theme-script.js +6 -0
  268. package/theme-script.js.map +1 -0
  269. package/theme.d.ts +28 -0
  270. package/theme.d.ts.map +1 -0
  271. package/theme.js +92 -0
  272. package/theme.js.map +1 -0
  273. package/toast.d.ts +52 -0
  274. package/toast.d.ts.map +1 -0
  275. package/toast.js +178 -0
  276. package/toast.js.map +1 -0
  277. package/tooltip.d.ts +12 -0
  278. package/tooltip.d.ts.map +1 -0
  279. package/tooltip.js +14 -0
  280. package/tooltip.js.map +1 -0
  281. package/top-nav.d.ts +18 -0
  282. package/top-nav.d.ts.map +1 -0
  283. package/top-nav.js +160 -0
  284. package/top-nav.js.map +1 -0
  285. package/upload-limits.d.ts +4 -0
  286. package/upload-limits.d.ts.map +1 -0
  287. package/upload-limits.js +21 -0
  288. package/upload-limits.js.map +1 -0
  289. package/use-element-size.d.ts +7 -0
  290. package/use-element-size.d.ts.map +1 -0
  291. package/use-element-size.js +21 -0
  292. package/use-element-size.js.map +1 -0
  293. package/utils.d.ts +4 -0
  294. package/utils.d.ts.map +1 -0
  295. package/utils.js +7 -0
  296. package/utils.js.map +1 -0
  297. package/workspace-launcher.d.ts +50 -0
  298. package/workspace-launcher.d.ts.map +1 -0
  299. package/workspace-launcher.js +127 -0
  300. package/workspace-launcher.js.map +1 -0
package/sidebar-nav.js ADDED
@@ -0,0 +1,291 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Activity, AlertTriangle, Award, BellRing, BookOpen, CalendarDays, Building2, CheckCircle2, ChevronRight, CircleHelp, CircleUser, ClipboardCheck, ClipboardList, Code2, Construction, Database, Download, FileText, Folder, Gauge, GraduationCap, HardHat, HeartPulse, History, KeyRound, Layers, LayoutGrid, LibraryBig, Link2, ListChecks, Mail, MapPin, MessageSquare, NotebookPen, Package, PanelLeft, Plus, QrCode, Radiation, Rss, ScrollText, Settings, ShieldCheck, Sparkles, Star, Tag, Target, Timer, TrendingUp, Truck, Upload, Users, Wallet, Workflow, Wrench, } from 'lucide-react';
5
+ import { Popover } from './popover.js';
6
+ import { defaultLinkRender } from './link-context.js';
7
+ import { cn } from './utils.js';
8
+ // This is the union of the production navigation icon registries.
9
+ // String keys remain serializable across an RSC boundary; `icon` keeps the
10
+ // primitive compatible with callers that already pass a React node.
11
+ const ICONS = {
12
+ activity: Activity,
13
+ alert: AlertTriangle,
14
+ award: Award,
15
+ bell: BellRing,
16
+ book: BookOpen,
17
+ calendar: CalendarDays,
18
+ building: Building2,
19
+ check: CheckCircle2,
20
+ 'circle-help': CircleHelp,
21
+ 'circle-user': CircleUser,
22
+ 'clipboard-check': ClipboardCheck,
23
+ clipboard: ClipboardList,
24
+ code: Code2,
25
+ construction: Construction,
26
+ database: Database,
27
+ download: Download,
28
+ file: FileText,
29
+ folder: Folder,
30
+ gauge: Gauge,
31
+ grad: GraduationCap,
32
+ grid: LayoutGrid,
33
+ 'hard-hat': HardHat,
34
+ 'heart-pulse': HeartPulse,
35
+ history: History,
36
+ journal: NotebookPen,
37
+ key: KeyRound,
38
+ layers: Layers,
39
+ library: LibraryBig,
40
+ link: Link2,
41
+ 'list-checks': ListChecks,
42
+ mail: Mail,
43
+ message: MessageSquare,
44
+ package: Package,
45
+ 'panel-left': PanelLeft,
46
+ pin: MapPin,
47
+ plus: Plus,
48
+ 'qr-code': QrCode,
49
+ radiation: Radiation,
50
+ rss: Rss,
51
+ scroll: ScrollText,
52
+ settings: Settings,
53
+ shield: ShieldCheck,
54
+ sparkles: Sparkles,
55
+ star: Star,
56
+ tag: Tag,
57
+ target: Target,
58
+ timer: Timer,
59
+ 'trending-up': TrendingUp,
60
+ truck: Truck,
61
+ upload: Upload,
62
+ users: Users,
63
+ wallet: Wallet,
64
+ workflow: Workflow,
65
+ wrench: Wrench,
66
+ };
67
+ const EXPANDED_STORAGE_KEY = 'appkit.nav.expanded-workspaces';
68
+ /**
69
+ * Single-item groups are top-level navigation links, not expandable
70
+ * workspaces. This is the same contract TopNav already uses and keeps the
71
+ * sidebar/mobile drawer faithful when an application chooses a flat menu.
72
+ */
73
+ export function isFlatNavigation(groups) {
74
+ return (groups.length > 0 &&
75
+ groups.every((group) => group.items.length === 1 &&
76
+ !group.items[0]?.subgroup &&
77
+ !group.items[0]?.subgroupHref));
78
+ }
79
+ export function findActiveNavHref(pathname, groups) {
80
+ if (!pathname)
81
+ return null;
82
+ let activeHref = null;
83
+ const consider = (href, exact) => {
84
+ if (!matchesNavPath(pathname, href, exact))
85
+ return;
86
+ if (!activeHref || href.length > activeHref.length)
87
+ activeHref = href;
88
+ };
89
+ for (const group of groups) {
90
+ for (const item of group.items) {
91
+ consider(item.href, item.exact);
92
+ if (item.subgroupHref)
93
+ consider(item.subgroupHref);
94
+ }
95
+ }
96
+ return activeHref;
97
+ }
98
+ function matchesNavPath(pathname, href, exact) {
99
+ if (pathname === href)
100
+ return true;
101
+ if (exact || href === '/')
102
+ return false;
103
+ return pathname.startsWith(`${href}/`);
104
+ }
105
+ /**
106
+ * The shared navigation registry rendered as a desktop sidebar. Workspace
107
+ * metadata (`id` + group icon) enables collapsible treatment; simpler groups
108
+ * retain labeled sections. Both use the same active-path, icon, and link contracts.
109
+ */
110
+ export function SidebarNav({ groups, pathname, collapsed = false, linkRender = defaultLinkRender, }) {
111
+ const activeHref = findActiveNavHref(pathname, groups);
112
+ const flatNavigation = isFlatNavigation(groups);
113
+ const workspaceMode = groups.some((group) => group.id || group.iconKey || group.icon);
114
+ const activeGroupId = groupKey(groups.find((group) => groupContainsActiveHref(group, activeHref)), groups);
115
+ const navRef = React.useRef(null);
116
+ const [openGroupIds, setOpenGroupIds] = React.useState(() => new Set([groupKey(groups[0], groups), activeGroupId].filter(Boolean)));
117
+ const openGroupKey = React.useMemo(() => [...openGroupIds].sort().join('|'), [openGroupIds]);
118
+ React.useEffect(() => {
119
+ if (!workspaceMode)
120
+ return;
121
+ try {
122
+ const raw = localStorage.getItem(EXPANDED_STORAGE_KEY);
123
+ if (raw === null)
124
+ return;
125
+ const saved = JSON.parse(raw);
126
+ if (!Array.isArray(saved))
127
+ return;
128
+ const valid = saved.filter((id) => typeof id === 'string' && groups.some((group) => groupKey(group, groups) === id));
129
+ setOpenGroupIds(new Set([...valid, activeGroupId].filter(Boolean)));
130
+ }
131
+ catch {
132
+ // Storage is an enhancement; Home + active workspace remain open.
133
+ }
134
+ }, [activeGroupId, groups, workspaceMode]);
135
+ React.useEffect(() => {
136
+ if (!workspaceMode || !activeGroupId)
137
+ return;
138
+ setOpenGroupIds((current) => {
139
+ if (current.has(activeGroupId))
140
+ return current;
141
+ const next = new Set(current).add(activeGroupId);
142
+ persistExpandedGroups(next);
143
+ return next;
144
+ });
145
+ }, [activeGroupId, workspaceMode]);
146
+ React.useEffect(() => {
147
+ if (collapsed)
148
+ return;
149
+ const frame = window.requestAnimationFrame(() => {
150
+ navRef.current?.querySelector('[aria-current="page"]')?.scrollIntoView({ block: 'nearest' });
151
+ });
152
+ return () => window.cancelAnimationFrame(frame);
153
+ }, [collapsed, openGroupKey, pathname]);
154
+ function toggleGroup(id) {
155
+ setOpenGroupIds((current) => {
156
+ const next = new Set(current);
157
+ if (next.has(id))
158
+ next.delete(id);
159
+ else
160
+ next.add(id);
161
+ persistExpandedGroups(next);
162
+ return next;
163
+ });
164
+ }
165
+ if (flatNavigation) {
166
+ return (_jsx("nav", { ref: navRef, className: cn('app-scroll flex-1 overflow-y-auto px-2 py-3', collapsed && 'space-y-1'), children: groups.map((group, groupIndex) => {
167
+ const item = group.items[0];
168
+ return (_jsx(NavLink, { item: item, active: activeHref === item.href, collapsed: collapsed, linkRender: linkRender }, group.id ?? `${item.href}-${groupIndex}`));
169
+ }) }));
170
+ }
171
+ if (!workspaceMode) {
172
+ return (_jsx("nav", { ref: navRef, className: "app-scroll flex-1 overflow-y-auto px-2 py-3", children: groups.map((group, groupIndex) => (_jsxs("div", { className: "mb-3", children: [collapsed ? (_jsx("div", { className: "mx-2 mb-1 border-t border-border-subtle", "aria-hidden": true })) : (_jsx("div", { className: "px-2 pb-1 text-[10px] font-semibold tracking-wider text-fg-subtle uppercase", children: group.label })), group.items.map((item) => (_jsx(NavLink, { item: item, active: activeHref === item.href, collapsed: collapsed, linkRender: linkRender }, item.href)))] }, `${group.label}-${groupIndex}`))) }));
173
+ }
174
+ return (_jsx("nav", { ref: navRef, className: cn('app-scroll flex-1 overflow-y-auto px-2 py-3', collapsed && 'space-y-1'), children: collapsed
175
+ ? groups.map((group, groupIndex) => (_jsx(CollapsedWorkspace, { group: group, activeHref: activeHref, active: groupKey(group, groups) === activeGroupId, linkRender: linkRender }, groupKey(group, groups) || groupIndex)))
176
+ : groups.map((group, groupIndex) => {
177
+ const id = groupKey(group, groups) || String(groupIndex);
178
+ const open = openGroupIds.has(id);
179
+ const active = id === activeGroupId;
180
+ const panelId = `nav-workspace-${id.replace(/[^a-zA-Z0-9_-]/g, '-')}`;
181
+ return (_jsxs("section", { className: "mb-1", children: [_jsxs("button", { type: "button", "aria-expanded": open, "aria-controls": panelId, onClick: () => toggleGroup(id), className: cn('group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs font-semibold tracking-wide transition-colors', active
182
+ ? 'bg-primary-subtle text-primary'
183
+ : 'text-fg-muted hover:bg-surface-hover hover:text-fg', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40'), children: [_jsx(NavIcon, { iconKey: group.iconKey, icon: group.icon, size: 14, className: "shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-left uppercase", children: group.label }), _jsx(ChevronRight, { size: 13, className: cn('shrink-0 opacity-60 transition-transform duration-150', open && 'rotate-90') })] }), open ? (_jsx("div", { id: panelId, className: "mt-0.5 space-y-0.5", children: _jsx(WorkspaceItems, { items: group.items, activeHref: activeHref, linkRender: linkRender }) })) : null] }, id));
184
+ }) }));
185
+ }
186
+ function groupKey(group, groups) {
187
+ if (!group)
188
+ return '';
189
+ return group.id || `${group.label}-${groups.indexOf(group)}`;
190
+ }
191
+ export function groupContainsActiveHref(group, activeHref) {
192
+ return group.items.some((item) => item.href === activeHref || item.subgroupHref === activeHref);
193
+ }
194
+ function persistExpandedGroups(ids) {
195
+ try {
196
+ localStorage.setItem(EXPANDED_STORAGE_KEY, JSON.stringify([...ids]));
197
+ }
198
+ catch {
199
+ // Navigation remains functional when storage is unavailable.
200
+ }
201
+ }
202
+ function WorkspaceItems({ items, activeHref, linkRender, }) {
203
+ return toBlocks(items).map((block, blockIndex) => block.kind === 'item' ? (_jsx(NavLink, { item: block.item, active: activeHref === block.item.href, linkRender: linkRender }, block.item.href)) : (_jsx(SubgroupSection, { label: block.label, href: block.href, iconKey: block.iconKey, items: block.items, activeHref: activeHref, linkRender: linkRender }, `sub-${block.label}-${blockIndex}`)));
204
+ }
205
+ function CollapsedWorkspace({ group, activeHref, active, linkRender, }) {
206
+ const [open, setOpen] = React.useState(false);
207
+ return (_jsx(Popover, { open: open, onOpenChange: setOpen, side: "right", align: "start", className: "max-h-[min(36rem,calc(100vh-2rem))] w-64 overflow-y-auto py-1.5", trigger: _jsx("button", { type: "button", "aria-label": group.label, "aria-expanded": open, title: group.label, onClick: () => setOpen((value) => !value), className: cn('relative grid h-9 w-full place-items-center rounded-md transition-colors', 'before:absolute before:top-1/2 before:left-0 before:h-5 before:w-[2px] before:-translate-y-1/2 before:rounded-full', active
208
+ ? 'bg-primary-subtle text-primary before:bg-primary'
209
+ : 'text-fg-muted before:bg-transparent hover:bg-surface-hover hover:text-fg', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40'), children: _jsx(NavIcon, { iconKey: group.iconKey, icon: group.icon, size: 17 }) }), children: _jsxs("div", { onClick: (event) => {
210
+ if (event.target.closest('a'))
211
+ setOpen(false);
212
+ }, children: [_jsx("div", { className: "border-b border-border-subtle px-3 py-2 text-xs font-semibold tracking-wide text-fg-muted uppercase", children: group.label }), _jsx("div", { className: "py-1", children: _jsx(WorkspaceItems, { items: group.items, activeHref: activeHref, linkRender: linkRender }) })] }) }));
213
+ }
214
+ export function toBlocks(items) {
215
+ const blocks = [];
216
+ for (const item of items) {
217
+ if (item.subgroup) {
218
+ const existing = blocks.find((block) => block.kind === 'subgroup' && block.label === item.subgroup);
219
+ if (existing) {
220
+ existing.items.push(item);
221
+ if (!existing.href)
222
+ existing.href = item.subgroupHref;
223
+ if (!existing.iconKey)
224
+ existing.iconKey = item.subgroupIconKey;
225
+ }
226
+ else {
227
+ blocks.push({
228
+ kind: 'subgroup',
229
+ label: item.subgroup,
230
+ href: item.subgroupHref,
231
+ iconKey: item.subgroupIconKey,
232
+ items: [item],
233
+ });
234
+ }
235
+ }
236
+ else {
237
+ blocks.push({ kind: 'item', item });
238
+ }
239
+ }
240
+ return blocks;
241
+ }
242
+ function NavLink({ item, active, nested = false, collapsed = false, linkRender, }) {
243
+ return (_jsx(React.Fragment, { children: linkRender({
244
+ href: item.href,
245
+ title: collapsed ? item.label : undefined,
246
+ ariaCurrent: active ? 'page' : undefined,
247
+ dataWalkthrough: `nav:${item.href}`,
248
+ className: cn('group relative flex items-center rounded-md py-1.5 text-sm transition-colors duration-150 ease-out', collapsed ? 'justify-center px-2' : nested ? 'gap-2.5 pr-2 pl-8' : 'gap-2.5 px-2', 'before:absolute before:top-1/2 before:left-0 before:h-5 before:w-[2px] before:-translate-y-1/2 before:rounded-full', 'before:transition-all before:duration-150 before:ease-out', active
249
+ ? 'bg-primary-subtle text-fg before:h-6 before:bg-primary'
250
+ : 'text-fg-muted before:bg-transparent hover:bg-surface-hover hover:text-fg hover:before:bg-border-strong', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40'),
251
+ children: (_jsxs(_Fragment, { children: [_jsx(NavIcon, { iconKey: item.iconKey, icon: item.icon, size: 15, className: cn('shrink-0 transition-colors duration-150', active ? 'text-primary' : 'text-fg-subtle group-hover:text-fg') }), collapsed ? null : _jsx("span", { className: "truncate", children: item.label })] })),
252
+ }) }));
253
+ }
254
+ function SubgroupSection({ label, href, iconKey, items, activeHref, linkRender, }) {
255
+ const selfActive = href != null && activeHref === href;
256
+ const hasActiveChild = items.some((item) => item.href === activeHref) || selfActive;
257
+ const [open, setOpen] = React.useState(hasActiveChild);
258
+ React.useEffect(() => {
259
+ if (hasActiveChild)
260
+ setOpen(true);
261
+ }, [hasActiveChild]);
262
+ const headerClass = cn('group flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors duration-150 ease-out', selfActive
263
+ ? 'bg-primary-subtle text-fg'
264
+ : hasActiveChild && !open
265
+ ? 'text-primary'
266
+ : 'text-fg-muted hover:bg-surface-hover hover:text-fg', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40');
267
+ const chevron = (_jsx(ChevronRight, { size: 15, className: cn('shrink-0 text-fg-subtle transition-transform duration-150', open && 'rotate-90') }));
268
+ return (_jsxs("div", { children: [href ? (_jsxs("div", { className: cn(headerClass, 'p-0'), children: [_jsx("button", { type: "button", "aria-expanded": open, "aria-label": label, onClick: () => setOpen((current) => !current), className: "grid shrink-0 place-items-center self-stretch rounded-l-md pl-2 hover:bg-surface-active", children: chevron }), linkRender({
269
+ href,
270
+ ariaCurrent: selfActive ? 'page' : undefined,
271
+ dataWalkthrough: `nav:${href}`,
272
+ className: 'flex flex-1 items-center gap-2.5 rounded-r-md py-1.5 pr-2',
273
+ children: (_jsxs(_Fragment, { children: [iconKey ? _jsx(NavIcon, { iconKey: iconKey, className: "shrink-0 text-fg-muted" }) : null, label] })),
274
+ })] })) : (_jsxs("button", { type: "button", onClick: () => setOpen((current) => !current), "aria-expanded": open, className: headerClass, children: [chevron, iconKey ? _jsx(NavIcon, { iconKey: iconKey, className: "shrink-0 text-fg-muted" }) : null, _jsx("span", { children: label })] })), open ? (_jsx("div", { className: "mt-0.5 space-y-0.5", children: items.map((item) => (_jsx(NavLink, { item: item, active: activeHref === item.href, nested: true, linkRender: linkRender }, item.href))) })) : null] }));
275
+ }
276
+ export const ICON_KEYS = Object.keys(ICONS).sort();
277
+ export function NavIcon({ iconKey, icon, size = 15, className, }) {
278
+ if (icon) {
279
+ return (_jsx("span", { className: cn('inline-flex shrink-0 items-center justify-center [&_svg]:size-[var(--nav-icon-size)]', className), style: { ['--nav-icon-size']: `${size}px` }, children: icon }));
280
+ }
281
+ const Icon = (iconKey && ICONS[iconKey]) || Gauge;
282
+ return _jsx(Icon, { size: size, className: className });
283
+ }
284
+ /** Mobile shortcut selection: explicitly pinned entries, then registry order. */
285
+ export function selectMobileTabs(groups, count = 4) {
286
+ const unique = groups
287
+ .flatMap((group) => group.items)
288
+ .filter((item, index, items) => items.findIndex((candidate) => candidate.href === item.href) === index);
289
+ return [...unique.filter((item) => item.mobile), ...unique.filter((item) => !item.mobile)].slice(0, count);
290
+ }
291
+ //# sourceMappingURL=sidebar-nav.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar-nav.js","sourceRoot":"","sources":["../src/sidebar-nav.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EACL,QAAQ,EACR,aAAa,EACb,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACd,aAAa,EACb,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,KAAK,EACL,aAAa,EACb,OAAO,EACP,UAAU,EACV,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,UAAU,EACV,KAAK,EACL,UAAU,EACV,IAAI,EACJ,MAAM,EACN,aAAa,EACb,WAAW,EACX,OAAO,EACP,SAAS,EACT,IAAI,EACJ,MAAM,EACN,SAAS,EACT,GAAG,EACH,UAAU,EACV,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,GAAG,EACH,MAAM,EACN,KAAK,EACL,UAAU,EACV,KAAK,EACL,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,MAAM,GAEP,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,iBAAiB,EAAmB,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAA;AAE5B,kEAAkE;AAClE,2EAA2E;AAC3E,oEAAoE;AACpE,MAAM,KAAK,GAA+B;IACxC,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,aAAa;IACpB,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,YAAY;IACtB,QAAQ,EAAE,SAAS;IACnB,KAAK,EAAE,YAAY;IACnB,aAAa,EAAE,UAAU;IACzB,aAAa,EAAE,UAAU;IACzB,iBAAiB,EAAE,cAAc;IACjC,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,KAAK;IACX,YAAY,EAAE,YAAY;IAC1B,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,UAAU;IAChB,UAAU,EAAE,OAAO;IACnB,aAAa,EAAE,UAAU;IACzB,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,WAAW;IACpB,GAAG,EAAE,QAAQ;IACb,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,UAAU;IACnB,IAAI,EAAE,KAAK;IACX,aAAa,EAAE,UAAU;IACzB,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,aAAa;IACtB,OAAO,EAAE,OAAO;IAChB,YAAY,EAAE,SAAS;IACvB,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,SAAS;IACpB,GAAG,EAAE,GAAG;IACR,MAAM,EAAE,UAAU;IAClB,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,WAAW;IACnB,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,GAAG;IACR,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,KAAK;IACZ,aAAa,EAAE,UAAU;IACzB,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,QAAQ;IAClB,MAAM,EAAE,MAAM;CACf,CAAA;AA0BD,MAAM,oBAAoB,GAAG,gCAAgC,CAAA;AAE7D;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAyB;IACxD,OAAO,CACL,MAAM,CAAC,MAAM,GAAG,CAAC;QACjB,MAAM,CAAC,KAAK,CACV,CAAC,KAAK,EAAE,EAAE,CACR,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YACxB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ;YACzB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,YAAY,CAChC,CACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,QAAmC,EACnC,MAAyB;IAEzB,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1B,IAAI,UAAU,GAAkB,IAAI,CAAA;IAEpC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,KAAe,EAAE,EAAE;QACjD,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC;YAAE,OAAM;QAClD,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM;YAAE,UAAU,GAAG,IAAI,CAAA;IACvE,CAAC,CAAA;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;YAC/B,IAAI,IAAI,CAAC,YAAY;gBAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB,EAAE,IAAY,EAAE,KAAe;IACrE,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAClC,IAAI,KAAK,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAA;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,EACR,SAAS,GAAG,KAAK,EACjB,UAAU,GAAG,iBAAiB,GAM/B;IACC,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACtD,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC/C,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,CAAA;IACrF,MAAM,aAAa,GAAG,QAAQ,CAC5B,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAuB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,EAClE,MAAM,CACP,CAAA;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAc,IAAI,CAAC,CAAA;IAC9C,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,QAAQ,CACpD,GAAG,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAC5E,CAAA;IACD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAA;IAE5F,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,aAAa;YAAE,OAAM;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;YACtD,IAAI,GAAG,KAAK,IAAI;gBAAE,OAAM;YACxB,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAM;YACjC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CACxB,CAAC,EAAE,EAAgB,EAAE,CACnB,OAAO,EAAE,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CACnF,CAAA;YACD,eAAe,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,aAAa,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;QACpE,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAA;IAE1C,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,aAAa,IAAI,CAAC,aAAa;YAAE,OAAM;QAC5C,eAAe,CAAC,CAAC,OAAO,EAAE,EAAE;YAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;gBAAE,OAAO,OAAO,CAAA;YAC9C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;YAChD,qBAAqB,CAAC,IAAI,CAAC,CAAA;YAC3B,OAAO,IAAI,CAAA;QACb,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAA;IAElC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,SAAS;YAAE,OAAM;QACrB,MAAM,KAAK,GAAG,MAAM,CAAC,qBAAqB,CAAC,GAAG,EAAE;YAC9C,MAAM,CAAC,OAAO,EAAE,aAAa,CAAc,uBAAuB,CAAC,EAAE,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;QAC3G,CAAC,CAAC,CAAA;QACF,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;IACjD,CAAC,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAA;IAEvC,SAAS,WAAW,CAAC,EAAU;QAC7B,eAAe,CAAC,CAAC,OAAO,EAAE,EAAE;YAC1B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;YAC7B,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;;gBAC5B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACjB,qBAAqB,CAAC,IAAI,CAAC,CAAA;YAC3B,OAAO,IAAI,CAAA;QACb,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,CACL,cACE,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,EAAE,CACX,6CAA6C,EAC7C,SAAS,IAAI,WAAW,CACzB,YAEA,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE;gBAChC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAA;gBAC5B,OAAO,CACL,KAAC,OAAO,IAEN,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC,IAAI,EAChC,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,IAJjB,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAK7C,CACH,CAAA;YACH,CAAC,CAAC,GACE,CACP,CAAA;IACH,CAAC;IAED,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,CACL,cAAK,GAAG,EAAE,MAAM,EAAE,SAAS,EAAC,6CAA6C,YACtE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,CACjC,eAA0C,SAAS,EAAC,MAAM,aACvD,SAAS,CAAC,CAAC,CAAC,CACX,cAAK,SAAS,EAAC,yCAAyC,wBAAe,CACxE,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,6EAA6E,YACzF,KAAK,CAAC,KAAK,GACR,CACP,EACA,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACzB,KAAC,OAAO,IAEN,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC,IAAI,EAChC,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,IAJjB,IAAI,CAAC,IAAI,CAKd,CACH,CAAC,KAhBM,GAAG,KAAK,CAAC,KAAK,IAAI,UAAU,EAAE,CAiBlC,CACP,CAAC,GACE,CACP,CAAA;IACH,CAAC;IAED,OAAO,CACL,cAAK,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,6CAA6C,EAAE,SAAS,IAAI,WAAW,CAAC,YACrG,SAAS;YACR,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC,CAChC,KAAC,kBAAkB,IAEjB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,aAAa,EACjD,UAAU,EAAE,UAAU,IAJjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,UAAU,CAK1C,CACH,CAAC;YACJ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE;gBAC/B,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,CAAA;gBACxD,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBACjC,MAAM,MAAM,GAAG,EAAE,KAAK,aAAa,CAAA;gBACnC,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,EAAE,CAAA;gBACrE,OAAO,CACL,mBAAkB,SAAS,EAAC,MAAM,aAChC,kBACE,IAAI,EAAC,QAAQ,mBACE,IAAI,mBACJ,OAAO,EACtB,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,EAC9B,SAAS,EAAE,EAAE,CACX,mHAAmH,EACnH,MAAM;gCACJ,CAAC,CAAC,gCAAgC;gCAClC,CAAC,CAAC,oDAAoD,EACxD,4EAA4E,CAC7E,aAED,KAAC,OAAO,IAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,UAAU,GAAG,EACpF,eAAM,SAAS,EAAC,6CAA6C,YAAE,KAAK,CAAC,KAAK,GAAQ,EAClF,KAAC,YAAY,IACX,IAAI,EAAE,EAAE,EACR,SAAS,EAAE,EAAE,CAAC,uDAAuD,EAAE,IAAI,IAAI,WAAW,CAAC,GAC3F,IACK,EACR,IAAI,CAAC,CAAC,CAAC,CACN,cAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAC,oBAAoB,YAC9C,KAAC,cAAc,IAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAI,GAClF,CACP,CAAC,CAAC,CAAC,IAAI,KAzBI,EAAE,CA0BN,CACX,CAAA;YACH,CAAC,CAAC,GACF,CACP,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAkC,EAAE,MAAyB;IAC7E,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,OAAO,KAAK,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAA;AAC9D,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAsB,EAAE,UAAyB;IACvF,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,YAAY,KAAK,UAAU,CAAC,CAAA;AACjG,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAgB;IAC7C,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAA;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,EACtB,KAAK,EACL,UAAU,EACV,UAAU,GAKX;IACC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,CAC/C,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CACtB,KAAC,OAAO,IAEN,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,MAAM,EAAE,UAAU,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,EACtC,UAAU,EAAE,UAAU,IAHjB,KAAK,CAAC,IAAI,CAAC,IAAI,CAIpB,CACH,CAAC,CAAC,CAAC,CACF,KAAC,eAAe,IAEd,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,OAAO,EAAE,KAAK,CAAC,OAAO,EACtB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,UAAU,IANjB,OAAO,KAAK,CAAC,KAAK,IAAI,UAAU,EAAE,CAOvC,CACH,CACF,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,EAC1B,KAAK,EACL,UAAU,EACV,MAAM,EACN,UAAU,GAMX;IACC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7C,OAAO,CACL,KAAC,OAAO,IACN,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,OAAO,EACrB,IAAI,EAAC,OAAO,EACZ,KAAK,EAAC,OAAO,EACb,SAAS,EAAC,iEAAiE,EAC3E,OAAO,EACL,iBACE,IAAI,EAAC,QAAQ,gBACD,KAAK,CAAC,KAAK,mBACR,IAAI,EACnB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EACzC,SAAS,EAAE,EAAE,CACX,0EAA0E,EAC1E,oHAAoH,EACpH,MAAM;gBACJ,CAAC,CAAC,kDAAkD;gBACpD,CAAC,CAAC,0EAA0E,EAC9E,4EAA4E,CAC7E,YAED,KAAC,OAAO,IAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAI,GACxD,YAGX,eACE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,IAAK,KAAK,CAAC,MAAsB,CAAC,OAAO,CAAC,GAAG,CAAC;oBAAE,OAAO,CAAC,KAAK,CAAC,CAAA;YAChE,CAAC,aAED,cAAK,SAAS,EAAC,qGAAqG,YACjH,KAAK,CAAC,KAAK,GACR,EACN,cAAK,SAAS,EAAC,MAAM,YACnB,KAAC,cAAc,IAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,GAAI,GAClF,IACF,GACE,CACX,CAAA;AACH,CAAC;AAMD,MAAM,UAAU,QAAQ,CAAC,KAAuB;IAC9C,MAAM,MAAM,GAAe,EAAE,CAAA;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAC1B,CAAC,KAAK,EAAoD,EAAE,CAC1D,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,QAAQ,CAC7D,CAAA;YACD,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACzB,IAAI,CAAC,QAAQ,CAAC,IAAI;oBAAE,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC,YAAY,CAAA;gBACrD,IAAI,CAAC,QAAQ,CAAC,OAAO;oBAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,eAAe,CAAA;YAChE,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,UAAU;oBAChB,KAAK,EAAE,IAAI,CAAC,QAAQ;oBACpB,IAAI,EAAE,IAAI,CAAC,YAAY;oBACvB,OAAO,EAAE,IAAI,CAAC,eAAe;oBAC7B,KAAK,EAAE,CAAC,IAAI,CAAC;iBACd,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QACrC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,OAAO,CAAC,EACf,IAAI,EACJ,MAAM,EACN,MAAM,GAAG,KAAK,EACd,SAAS,GAAG,KAAK,EACjB,UAAU,GAOX;IACC,OAAO,CACL,KAAC,KAAK,CAAC,QAAQ,cACZ,UAAU,CAAC;YACV,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;YACzC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;YACxC,eAAe,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE;YACnC,SAAS,EAAE,EAAE,CACX,oGAAoG,EACpG,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,cAAc,EACjF,oHAAoH,EACpH,2DAA2D,EAC3D,MAAM;gBACJ,CAAC,CAAC,wDAAwD;gBAC1D,CAAC,CAAC,wGAAwG,EAC5G,4EAA4E,CAC7E;YACD,QAAQ,EAAE,CACR,8BACE,KAAC,OAAO,IACN,OAAO,EAAE,IAAI,CAAC,OAAO,EACrB,IAAI,EAAE,IAAI,CAAC,IAAI,EACf,IAAI,EAAE,EAAE,EACR,SAAS,EAAE,EAAE,CACX,yCAAyC,EACzC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,oCAAoC,CAC/D,GACD,EACD,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,UAAU,YAAE,IAAI,CAAC,KAAK,GAAQ,IACjE,CACJ;SACF,CAAC,GACa,CAClB,CAAA;AACH,CAAC;AAED,SAAS,eAAe,CAAC,EACvB,KAAK,EACL,IAAI,EACJ,OAAO,EACP,KAAK,EACL,UAAU,EACV,UAAU,GAQX;IACC,MAAM,UAAU,GAAG,IAAI,IAAI,IAAI,IAAI,UAAU,KAAK,IAAI,CAAA;IACtD,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,UAAU,CAAA;IACnF,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;IAEtD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,cAAc;YAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAA;IAEpB,MAAM,WAAW,GAAG,EAAE,CACpB,+GAA+G,EAC/G,UAAU;QACR,CAAC,CAAC,2BAA2B;QAC7B,CAAC,CAAC,cAAc,IAAI,CAAC,IAAI;YACvB,CAAC,CAAC,cAAc;YAChB,CAAC,CAAC,oDAAoD,EAC1D,4EAA4E,CAC7E,CAAA;IACD,MAAM,OAAO,GAAG,CACd,KAAC,YAAY,IACX,IAAI,EAAE,EAAE,EACR,SAAS,EAAE,EAAE,CAAC,2DAA2D,EAAE,IAAI,IAAI,WAAW,CAAC,GAC/F,CACH,CAAA;IAED,OAAO,CACL,0BACG,IAAI,CAAC,CAAC,CAAC,CACN,eAAK,SAAS,EAAE,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,aACpC,iBACE,IAAI,EAAC,QAAQ,mBACE,IAAI,gBACP,KAAK,EACjB,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAC7C,SAAS,EAAC,yFAAyF,YAElG,OAAO,GACD,EACR,UAAU,CAAC;wBACV,IAAI;wBACJ,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;wBAC5C,eAAe,EAAE,OAAO,IAAI,EAAE;wBAC9B,SAAS,EAAE,2DAA2D;wBACtE,QAAQ,EAAE,CACR,8BACG,OAAO,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAC,wBAAwB,GAAG,CAAC,CAAC,CAAC,IAAI,EACjF,KAAK,IACL,CACJ;qBACF,CAAC,IACE,CACP,CAAC,CAAC,CAAC,CACF,kBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAiB,IAAI,EAAE,SAAS,EAAE,WAAW,aAC7G,OAAO,EACP,OAAO,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAC,wBAAwB,GAAG,CAAC,CAAC,CAAC,IAAI,EAClF,yBAAO,KAAK,GAAQ,IACb,CACV,EACA,IAAI,CAAC,CAAC,CAAC,CACN,cAAK,SAAS,EAAC,oBAAoB,YAChC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,KAAC,OAAO,IAEN,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC,IAAI,EAChC,MAAM,QACN,UAAU,EAAE,UAAU,IAJjB,IAAI,CAAC,IAAI,CAKd,CACH,CAAC,GACE,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAA;AAElD,MAAM,UAAU,OAAO,CAAC,EACtB,OAAO,EACP,IAAI,EACJ,IAAI,GAAG,EAAE,EACT,SAAS,GAMV;IACC,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CAAC,sFAAsF,EAAE,SAAS,CAAC,EAChH,KAAK,EAAE,EAAE,CAAC,iBAA2B,CAAC,EAAE,GAAG,IAAI,IAAI,EAAE,YAEpD,IAAI,GACA,CACR,CAAA;IACH,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAA;IACjD,OAAO,KAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,GAAI,CAAA;AACnD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,MAAyB,EAAE,KAAK,GAAG,CAAC;IACnE,MAAM,MAAM,GAAG,MAAM;SAClB,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC;SAC/B,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAA;IACzG,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;AAC5G,CAAC","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport {\n Activity,\n AlertTriangle,\n Award,\n BellRing,\n BookOpen,\n CalendarDays,\n Building2,\n CheckCircle2,\n ChevronRight,\n CircleHelp,\n CircleUser,\n ClipboardCheck,\n ClipboardList,\n Code2,\n Construction,\n Database,\n Download,\n FileText,\n Folder,\n Gauge,\n GraduationCap,\n HardHat,\n HeartPulse,\n History,\n KeyRound,\n Layers,\n LayoutGrid,\n LibraryBig,\n Link2,\n ListChecks,\n Mail,\n MapPin,\n MessageSquare,\n NotebookPen,\n Package,\n PanelLeft,\n Plus,\n QrCode,\n Radiation,\n Rss,\n ScrollText,\n Settings,\n ShieldCheck,\n Sparkles,\n Star,\n Tag,\n Target,\n Timer,\n TrendingUp,\n Truck,\n Upload,\n Users,\n Wallet,\n Workflow,\n Wrench,\n type LucideIcon,\n} from 'lucide-react'\nimport { Popover } from './popover'\nimport { defaultLinkRender, type LinkRender } from './link-context'\nimport { cn } from './utils'\n\n// This is the union of the production navigation icon registries.\n// String keys remain serializable across an RSC boundary; `icon` keeps the\n// primitive compatible with callers that already pass a React node.\nconst ICONS: Record<string, LucideIcon> = {\n activity: Activity,\n alert: AlertTriangle,\n award: Award,\n bell: BellRing,\n book: BookOpen,\n calendar: CalendarDays,\n building: Building2,\n check: CheckCircle2,\n 'circle-help': CircleHelp,\n 'circle-user': CircleUser,\n 'clipboard-check': ClipboardCheck,\n clipboard: ClipboardList,\n code: Code2,\n construction: Construction,\n database: Database,\n download: Download,\n file: FileText,\n folder: Folder,\n gauge: Gauge,\n grad: GraduationCap,\n grid: LayoutGrid,\n 'hard-hat': HardHat,\n 'heart-pulse': HeartPulse,\n history: History,\n journal: NotebookPen,\n key: KeyRound,\n layers: Layers,\n library: LibraryBig,\n link: Link2,\n 'list-checks': ListChecks,\n mail: Mail,\n message: MessageSquare,\n package: Package,\n 'panel-left': PanelLeft,\n pin: MapPin,\n plus: Plus,\n 'qr-code': QrCode,\n radiation: Radiation,\n rss: Rss,\n scroll: ScrollText,\n settings: Settings,\n shield: ShieldCheck,\n sparkles: Sparkles,\n star: Star,\n tag: Tag,\n target: Target,\n timer: Timer,\n 'trending-up': TrendingUp,\n truck: Truck,\n upload: Upload,\n users: Users,\n wallet: Wallet,\n workflow: Workflow,\n wrench: Wrench,\n}\n\nexport type SidebarNavItem = {\n href: string\n label: string\n /** Built-in message key. Omitted for tenant-authored/custom labels. */\n labelKey?: string\n iconKey?: keyof typeof ICONS | string\n icon?: React.ReactNode\n exact?: boolean\n subgroup?: string\n subgroupHref?: string\n subgroupIconKey?: string\n mobile?: boolean\n}\n\nexport type SidebarNavGroup = {\n /** Optional workspace id; omit it for a simple grouped-list contract. */\n id?: string\n label: string\n labelKey?: string\n iconKey?: string\n icon?: React.ReactNode\n items: SidebarNavItem[]\n}\n\nconst EXPANDED_STORAGE_KEY = 'appkit.nav.expanded-workspaces'\n\n/**\n * Single-item groups are top-level navigation links, not expandable\n * workspaces. This is the same contract TopNav already uses and keeps the\n * sidebar/mobile drawer faithful when an application chooses a flat menu.\n */\nexport function isFlatNavigation(groups: SidebarNavGroup[]): boolean {\n return (\n groups.length > 0 &&\n groups.every(\n (group) =>\n group.items.length === 1 &&\n !group.items[0]?.subgroup &&\n !group.items[0]?.subgroupHref,\n )\n )\n}\n\nexport function findActiveNavHref(\n pathname: string | null | undefined,\n groups: SidebarNavGroup[],\n): string | null {\n if (!pathname) return null\n let activeHref: string | null = null\n\n const consider = (href: string, exact?: boolean) => {\n if (!matchesNavPath(pathname, href, exact)) return\n if (!activeHref || href.length > activeHref.length) activeHref = href\n }\n\n for (const group of groups) {\n for (const item of group.items) {\n consider(item.href, item.exact)\n if (item.subgroupHref) consider(item.subgroupHref)\n }\n }\n return activeHref\n}\n\nfunction matchesNavPath(pathname: string, href: string, exact?: boolean): boolean {\n if (pathname === href) return true\n if (exact || href === '/') return false\n return pathname.startsWith(`${href}/`)\n}\n\n/**\n * The shared navigation registry rendered as a desktop sidebar. Workspace\n * metadata (`id` + group icon) enables collapsible treatment; simpler groups\n * retain labeled sections. Both use the same active-path, icon, and link contracts.\n */\nexport function SidebarNav({\n groups,\n pathname,\n collapsed = false,\n linkRender = defaultLinkRender,\n}: {\n groups: SidebarNavGroup[]\n pathname: string\n collapsed?: boolean\n linkRender?: LinkRender\n}) {\n const activeHref = findActiveNavHref(pathname, groups)\n const flatNavigation = isFlatNavigation(groups)\n const workspaceMode = groups.some((group) => group.id || group.iconKey || group.icon)\n const activeGroupId = groupKey(\n groups.find((group) => groupContainsActiveHref(group, activeHref)),\n groups,\n )\n const navRef = React.useRef<HTMLElement>(null)\n const [openGroupIds, setOpenGroupIds] = React.useState<Set<string>>(\n () => new Set([groupKey(groups[0], groups), activeGroupId].filter(Boolean)),\n )\n const openGroupKey = React.useMemo(() => [...openGroupIds].sort().join('|'), [openGroupIds])\n\n React.useEffect(() => {\n if (!workspaceMode) return\n try {\n const raw = localStorage.getItem(EXPANDED_STORAGE_KEY)\n if (raw === null) return\n const saved: unknown = JSON.parse(raw)\n if (!Array.isArray(saved)) return\n const valid = saved.filter(\n (id): id is string =>\n typeof id === 'string' && groups.some((group) => groupKey(group, groups) === id),\n )\n setOpenGroupIds(new Set([...valid, activeGroupId].filter(Boolean)))\n } catch {\n // Storage is an enhancement; Home + active workspace remain open.\n }\n }, [activeGroupId, groups, workspaceMode])\n\n React.useEffect(() => {\n if (!workspaceMode || !activeGroupId) return\n setOpenGroupIds((current) => {\n if (current.has(activeGroupId)) return current\n const next = new Set(current).add(activeGroupId)\n persistExpandedGroups(next)\n return next\n })\n }, [activeGroupId, workspaceMode])\n\n React.useEffect(() => {\n if (collapsed) return\n const frame = window.requestAnimationFrame(() => {\n navRef.current?.querySelector<HTMLElement>('[aria-current=\"page\"]')?.scrollIntoView({ block: 'nearest' })\n })\n return () => window.cancelAnimationFrame(frame)\n }, [collapsed, openGroupKey, pathname])\n\n function toggleGroup(id: string) {\n setOpenGroupIds((current) => {\n const next = new Set(current)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n persistExpandedGroups(next)\n return next\n })\n }\n\n if (flatNavigation) {\n return (\n <nav\n ref={navRef}\n className={cn(\n 'app-scroll flex-1 overflow-y-auto px-2 py-3',\n collapsed && 'space-y-1',\n )}\n >\n {groups.map((group, groupIndex) => {\n const item = group.items[0]!\n return (\n <NavLink\n key={group.id ?? `${item.href}-${groupIndex}`}\n item={item}\n active={activeHref === item.href}\n collapsed={collapsed}\n linkRender={linkRender}\n />\n )\n })}\n </nav>\n )\n }\n\n if (!workspaceMode) {\n return (\n <nav ref={navRef} className=\"app-scroll flex-1 overflow-y-auto px-2 py-3\">\n {groups.map((group, groupIndex) => (\n <div key={`${group.label}-${groupIndex}`} className=\"mb-3\">\n {collapsed ? (\n <div className=\"mx-2 mb-1 border-t border-border-subtle\" aria-hidden />\n ) : (\n <div className=\"px-2 pb-1 text-[10px] font-semibold tracking-wider text-fg-subtle uppercase\">\n {group.label}\n </div>\n )}\n {group.items.map((item) => (\n <NavLink\n key={item.href}\n item={item}\n active={activeHref === item.href}\n collapsed={collapsed}\n linkRender={linkRender}\n />\n ))}\n </div>\n ))}\n </nav>\n )\n }\n\n return (\n <nav ref={navRef} className={cn('app-scroll flex-1 overflow-y-auto px-2 py-3', collapsed && 'space-y-1')}>\n {collapsed\n ? groups.map((group, groupIndex) => (\n <CollapsedWorkspace\n key={groupKey(group, groups) || groupIndex}\n group={group}\n activeHref={activeHref}\n active={groupKey(group, groups) === activeGroupId}\n linkRender={linkRender}\n />\n ))\n : groups.map((group, groupIndex) => {\n const id = groupKey(group, groups) || String(groupIndex)\n const open = openGroupIds.has(id)\n const active = id === activeGroupId\n const panelId = `nav-workspace-${id.replace(/[^a-zA-Z0-9_-]/g, '-')}`\n return (\n <section key={id} className=\"mb-1\">\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() => toggleGroup(id)}\n className={cn(\n 'group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs font-semibold tracking-wide transition-colors',\n active\n ? 'bg-primary-subtle text-primary'\n : 'text-fg-muted hover:bg-surface-hover hover:text-fg',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n )}\n >\n <NavIcon iconKey={group.iconKey} icon={group.icon} size={14} className=\"shrink-0\" />\n <span className=\"min-w-0 flex-1 truncate text-left uppercase\">{group.label}</span>\n <ChevronRight\n size={13}\n className={cn('shrink-0 opacity-60 transition-transform duration-150', open && 'rotate-90')}\n />\n </button>\n {open ? (\n <div id={panelId} className=\"mt-0.5 space-y-0.5\">\n <WorkspaceItems items={group.items} activeHref={activeHref} linkRender={linkRender} />\n </div>\n ) : null}\n </section>\n )\n })}\n </nav>\n )\n}\n\nfunction groupKey(group: SidebarNavGroup | undefined, groups: SidebarNavGroup[]): string {\n if (!group) return ''\n return group.id || `${group.label}-${groups.indexOf(group)}`\n}\n\nexport function groupContainsActiveHref(group: SidebarNavGroup, activeHref: string | null): boolean {\n return group.items.some((item) => item.href === activeHref || item.subgroupHref === activeHref)\n}\n\nfunction persistExpandedGroups(ids: Set<string>) {\n try {\n localStorage.setItem(EXPANDED_STORAGE_KEY, JSON.stringify([...ids]))\n } catch {\n // Navigation remains functional when storage is unavailable.\n }\n}\n\nfunction WorkspaceItems({\n items,\n activeHref,\n linkRender,\n}: {\n items: SidebarNavItem[]\n activeHref: string | null\n linkRender: LinkRender\n}) {\n return toBlocks(items).map((block, blockIndex) =>\n block.kind === 'item' ? (\n <NavLink\n key={block.item.href}\n item={block.item}\n active={activeHref === block.item.href}\n linkRender={linkRender}\n />\n ) : (\n <SubgroupSection\n key={`sub-${block.label}-${blockIndex}`}\n label={block.label}\n href={block.href}\n iconKey={block.iconKey}\n items={block.items}\n activeHref={activeHref}\n linkRender={linkRender}\n />\n ),\n )\n}\n\nfunction CollapsedWorkspace({\n group,\n activeHref,\n active,\n linkRender,\n}: {\n group: SidebarNavGroup\n activeHref: string | null\n active: boolean\n linkRender: LinkRender\n}) {\n const [open, setOpen] = React.useState(false)\n return (\n <Popover\n open={open}\n onOpenChange={setOpen}\n side=\"right\"\n align=\"start\"\n className=\"max-h-[min(36rem,calc(100vh-2rem))] w-64 overflow-y-auto py-1.5\"\n trigger={\n <button\n type=\"button\"\n aria-label={group.label}\n aria-expanded={open}\n title={group.label}\n onClick={() => setOpen((value) => !value)}\n className={cn(\n 'relative grid h-9 w-full place-items-center rounded-md transition-colors',\n 'before:absolute before:top-1/2 before:left-0 before:h-5 before:w-[2px] before:-translate-y-1/2 before:rounded-full',\n active\n ? 'bg-primary-subtle text-primary before:bg-primary'\n : 'text-fg-muted before:bg-transparent hover:bg-surface-hover hover:text-fg',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n )}\n >\n <NavIcon iconKey={group.iconKey} icon={group.icon} size={17} />\n </button>\n }\n >\n <div\n onClick={(event) => {\n if ((event.target as HTMLElement).closest('a')) setOpen(false)\n }}\n >\n <div className=\"border-b border-border-subtle px-3 py-2 text-xs font-semibold tracking-wide text-fg-muted uppercase\">\n {group.label}\n </div>\n <div className=\"py-1\">\n <WorkspaceItems items={group.items} activeHref={activeHref} linkRender={linkRender} />\n </div>\n </div>\n </Popover>\n )\n}\n\nexport type NavBlock =\n | { kind: 'item'; item: SidebarNavItem }\n | { kind: 'subgroup'; label: string; href?: string; iconKey?: string; items: SidebarNavItem[] }\n\nexport function toBlocks(items: SidebarNavItem[]): NavBlock[] {\n const blocks: NavBlock[] = []\n for (const item of items) {\n if (item.subgroup) {\n const existing = blocks.find(\n (block): block is Extract<NavBlock, { kind: 'subgroup' }> =>\n block.kind === 'subgroup' && block.label === item.subgroup,\n )\n if (existing) {\n existing.items.push(item)\n if (!existing.href) existing.href = item.subgroupHref\n if (!existing.iconKey) existing.iconKey = item.subgroupIconKey\n } else {\n blocks.push({\n kind: 'subgroup',\n label: item.subgroup,\n href: item.subgroupHref,\n iconKey: item.subgroupIconKey,\n items: [item],\n })\n }\n } else {\n blocks.push({ kind: 'item', item })\n }\n }\n return blocks\n}\n\nfunction NavLink({\n item,\n active,\n nested = false,\n collapsed = false,\n linkRender,\n}: {\n item: SidebarNavItem\n active: boolean\n nested?: boolean\n collapsed?: boolean\n linkRender: LinkRender\n}) {\n return (\n <React.Fragment>\n {linkRender({\n href: item.href,\n title: collapsed ? item.label : undefined,\n ariaCurrent: active ? 'page' : undefined,\n dataWalkthrough: `nav:${item.href}`,\n className: cn(\n 'group relative flex items-center rounded-md py-1.5 text-sm transition-colors duration-150 ease-out',\n collapsed ? 'justify-center px-2' : nested ? 'gap-2.5 pr-2 pl-8' : 'gap-2.5 px-2',\n 'before:absolute before:top-1/2 before:left-0 before:h-5 before:w-[2px] before:-translate-y-1/2 before:rounded-full',\n 'before:transition-all before:duration-150 before:ease-out',\n active\n ? 'bg-primary-subtle text-fg before:h-6 before:bg-primary'\n : 'text-fg-muted before:bg-transparent hover:bg-surface-hover hover:text-fg hover:before:bg-border-strong',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n ),\n children: (\n <>\n <NavIcon\n iconKey={item.iconKey}\n icon={item.icon}\n size={15}\n className={cn(\n 'shrink-0 transition-colors duration-150',\n active ? 'text-primary' : 'text-fg-subtle group-hover:text-fg',\n )}\n />\n {collapsed ? null : <span className=\"truncate\">{item.label}</span>}\n </>\n ),\n })}\n </React.Fragment>\n )\n}\n\nfunction SubgroupSection({\n label,\n href,\n iconKey,\n items,\n activeHref,\n linkRender,\n}: {\n label: string\n href?: string\n iconKey?: string\n items: SidebarNavItem[]\n activeHref: string | null\n linkRender: LinkRender\n}) {\n const selfActive = href != null && activeHref === href\n const hasActiveChild = items.some((item) => item.href === activeHref) || selfActive\n const [open, setOpen] = React.useState(hasActiveChild)\n\n React.useEffect(() => {\n if (hasActiveChild) setOpen(true)\n }, [hasActiveChild])\n\n const headerClass = cn(\n 'group flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors duration-150 ease-out',\n selfActive\n ? 'bg-primary-subtle text-fg'\n : hasActiveChild && !open\n ? 'text-primary'\n : 'text-fg-muted hover:bg-surface-hover hover:text-fg',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40',\n )\n const chevron = (\n <ChevronRight\n size={15}\n className={cn('shrink-0 text-fg-subtle transition-transform duration-150', open && 'rotate-90')}\n />\n )\n\n return (\n <div>\n {href ? (\n <div className={cn(headerClass, 'p-0')}>\n <button\n type=\"button\"\n aria-expanded={open}\n aria-label={label}\n onClick={() => setOpen((current) => !current)}\n className=\"grid shrink-0 place-items-center self-stretch rounded-l-md pl-2 hover:bg-surface-active\"\n >\n {chevron}\n </button>\n {linkRender({\n href,\n ariaCurrent: selfActive ? 'page' : undefined,\n dataWalkthrough: `nav:${href}`,\n className: 'flex flex-1 items-center gap-2.5 rounded-r-md py-1.5 pr-2',\n children: (\n <>\n {iconKey ? <NavIcon iconKey={iconKey} className=\"shrink-0 text-fg-muted\" /> : null}\n {label}\n </>\n ),\n })}\n </div>\n ) : (\n <button type=\"button\" onClick={() => setOpen((current) => !current)} aria-expanded={open} className={headerClass}>\n {chevron}\n {iconKey ? <NavIcon iconKey={iconKey} className=\"shrink-0 text-fg-muted\" /> : null}\n <span>{label}</span>\n </button>\n )}\n {open ? (\n <div className=\"mt-0.5 space-y-0.5\">\n {items.map((item) => (\n <NavLink\n key={item.href}\n item={item}\n active={activeHref === item.href}\n nested\n linkRender={linkRender}\n />\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport const ICON_KEYS = Object.keys(ICONS).sort()\n\nexport function NavIcon({\n iconKey,\n icon,\n size = 15,\n className,\n}: {\n iconKey?: string\n icon?: React.ReactNode\n size?: number\n className?: string\n}) {\n if (icon) {\n return (\n <span\n className={cn('inline-flex shrink-0 items-center justify-center [&_svg]:size-[var(--nav-icon-size)]', className)}\n style={{ ['--nav-icon-size' as string]: `${size}px` }}\n >\n {icon}\n </span>\n )\n }\n const Icon = (iconKey && ICONS[iconKey]) || Gauge\n return <Icon size={size} className={className} />\n}\n\n/** Mobile shortcut selection: explicitly pinned entries, then registry order. */\nexport function selectMobileTabs(groups: SidebarNavGroup[], count = 4): SidebarNavItem[] {\n const unique = groups\n .flatMap((group) => group.items)\n .filter((item, index, items) => items.findIndex((candidate) => candidate.href === item.href) === index)\n return [...unique.filter((item) => item.mobile), ...unique.filter((item) => !item.mobile)].slice(0, count)\n}\n"]}
@@ -0,0 +1,16 @@
1
+ export type SignaturePadProps = {
2
+ /** Current value as a PNG data-url, or `null` if empty. */
3
+ value: string | null;
4
+ /** Called with the new data-url after each completed stroke, or `null` on clear. */
5
+ onChange: (value: string | null) => void;
6
+ /** Disable drawing. Renders `value` (if any) as a static image. */
7
+ disabled?: boolean;
8
+ /** Canvas height in CSS pixels. Default 160. */
9
+ height?: number;
10
+ /** Extra classes on the outer wrapper. */
11
+ className?: string;
12
+ /** Accessibility label for the canvas. Default "Signature". */
13
+ ariaLabel?: string;
14
+ };
15
+ export declare function SignaturePad({ value, onChange, disabled, height, className, ariaLabel, }: SignaturePadProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=signature-pad.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature-pad.d.ts","sourceRoot":"","sources":["../src/signature-pad.tsx"],"names":[],"mappings":"AA+BA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,oFAAoF;IACpF,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAWD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,QAAgB,EAChB,MAAY,EACZ,SAAS,EACT,SAAS,GACV,EAAE,iBAAiB,+BAkanB"}