@devalok/shilp-sutra 0.38.0 → 0.40.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 (361) hide show
  1. package/AGENTS.md +151 -0
  2. package/MIGRATION.md +255 -0
  3. package/README.md +3 -0
  4. package/dist/_chunks/badge-group.js +76 -75
  5. package/dist/_chunks/badge-group.js.map +1 -1
  6. package/dist/_chunks/chart-container.js +50 -0
  7. package/dist/_chunks/chart-container.js.map +1 -0
  8. package/dist/_chunks/chat.js +236 -228
  9. package/dist/_chunks/chat.js.map +1 -1
  10. package/dist/_chunks/colors.js +30 -0
  11. package/dist/_chunks/colors.js.map +1 -0
  12. package/dist/_chunks/document-preview.js +2 -2
  13. package/dist/_chunks/document-preview.js.map +1 -1
  14. package/dist/_chunks/emoji-data.js +45 -0
  15. package/dist/_chunks/emoji-data.js.map +1 -0
  16. package/dist/_chunks/grid-lines.js +72 -0
  17. package/dist/_chunks/grid-lines.js.map +1 -0
  18. package/dist/_chunks/image-preview.js +2 -2
  19. package/dist/_chunks/image-preview.js.map +1 -1
  20. package/dist/_chunks/mention-suggestion.js +35 -263
  21. package/dist/_chunks/mention-suggestion.js.map +1 -1
  22. package/dist/_chunks/normalize-icon.js +18 -0
  23. package/dist/_chunks/normalize-icon.js.map +1 -0
  24. package/dist/_chunks/oauth-button.js +227 -0
  25. package/dist/_chunks/oauth-button.js.map +1 -0
  26. package/dist/_chunks/popover.js +3 -3
  27. package/dist/_chunks/popover.js.map +1 -1
  28. package/dist/_chunks/primitives.js +107 -107
  29. package/dist/_chunks/primitives.js.map +1 -1
  30. package/dist/_chunks/shared.js +5 -5
  31. package/dist/_chunks/shared.js.map +1 -1
  32. package/dist/_chunks/{text.js → success.js} +26 -63
  33. package/dist/_chunks/success.js.map +1 -0
  34. package/dist/_chunks/tiptap.js +1952 -1949
  35. package/dist/_chunks/tiptap.js.map +1 -1
  36. package/dist/_chunks/tooltip.js +58 -0
  37. package/dist/_chunks/tooltip.js.map +1 -0
  38. package/dist/_chunks/tree-view.js +101 -96
  39. package/dist/_chunks/tree-view.js.map +1 -1
  40. package/dist/_chunks/use-calendar.js +5 -5
  41. package/dist/_chunks/use-calendar.js.map +1 -1
  42. package/dist/ai/ai-command-provider.d.ts +3 -2
  43. package/dist/ai/ai-command-provider.d.ts.map +1 -1
  44. package/dist/ai/ai-command-provider.js.map +1 -1
  45. package/dist/ai/block-renderer.js +11 -9
  46. package/dist/ai/block-renderer.js.map +1 -1
  47. package/dist/ai/blocks/error.js +31 -0
  48. package/dist/ai/blocks/error.js.map +1 -0
  49. package/dist/ai/blocks/index.d.ts +0 -2
  50. package/dist/ai/blocks/index.d.ts.map +1 -1
  51. package/dist/ai/blocks/index.js +2 -2
  52. package/dist/ai/blocks/text.js +19 -0
  53. package/dist/ai/blocks/text.js.map +1 -0
  54. package/dist/ai/command-bar.d.ts.map +1 -1
  55. package/dist/ai/command-bar.js +198 -193
  56. package/dist/ai/command-bar.js.map +1 -1
  57. package/dist/ai/conversation.d.ts +2 -1
  58. package/dist/ai/conversation.d.ts.map +1 -1
  59. package/dist/ai/conversation.js +92 -87
  60. package/dist/ai/conversation.js.map +1 -1
  61. package/dist/ai/index.d.ts +0 -4
  62. package/dist/ai/index.d.ts.map +1 -1
  63. package/dist/ai/index.js +45 -46
  64. package/dist/ai/index.js.map +1 -1
  65. package/dist/composed/activity-feed.d.ts +2 -1
  66. package/dist/composed/activity-feed.d.ts.map +1 -1
  67. package/dist/composed/activity-feed.js +5 -5
  68. package/dist/composed/activity-feed.js.map +1 -1
  69. package/dist/composed/avatar-group.js +4 -4
  70. package/dist/composed/avatar-group.js.map +1 -1
  71. package/dist/composed/bulk-action-bar.d.ts +2 -2
  72. package/dist/composed/bulk-action-bar.d.ts.map +1 -1
  73. package/dist/composed/bulk-action-bar.js +12 -12
  74. package/dist/composed/bulk-action-bar.js.map +1 -1
  75. package/dist/composed/command-palette.d.ts +2 -1
  76. package/dist/composed/command-palette.d.ts.map +1 -1
  77. package/dist/composed/command-palette.js +106 -101
  78. package/dist/composed/command-palette.js.map +1 -1
  79. package/dist/composed/content-card.js +1 -1
  80. package/dist/composed/content-card.js.map +1 -1
  81. package/dist/composed/date-picker/index.js +747 -3
  82. package/dist/composed/date-picker/index.js.map +1 -0
  83. package/dist/composed/emoji-picker.js +4 -4
  84. package/dist/composed/emoji-picker.js.map +1 -1
  85. package/dist/composed/empty-state.d.ts +3 -3
  86. package/dist/composed/empty-state.d.ts.map +1 -1
  87. package/dist/composed/empty-state.js +40 -31
  88. package/dist/composed/empty-state.js.map +1 -1
  89. package/dist/composed/error-boundary.js +3 -3
  90. package/dist/composed/error-boundary.js.map +1 -1
  91. package/dist/composed/extensions/emoji-node.js +81 -0
  92. package/dist/composed/extensions/emoji-node.js.map +1 -0
  93. package/dist/composed/extensions/emoji-suggestion.js +117 -0
  94. package/dist/composed/extensions/emoji-suggestion.js.map +1 -0
  95. package/dist/composed/file-preview.js +498 -2
  96. package/dist/composed/file-preview.js.map +1 -0
  97. package/dist/composed/filter-bar.js +1 -1
  98. package/dist/composed/filter-bar.js.map +1 -1
  99. package/dist/composed/index.d.ts +0 -15
  100. package/dist/composed/index.d.ts.map +1 -1
  101. package/dist/composed/index.js +16 -24
  102. package/dist/composed/inline-edit.js +1 -1
  103. package/dist/composed/inline-edit.js.map +1 -1
  104. package/dist/composed/loading-skeleton.js +9 -9
  105. package/dist/composed/loading-skeleton.js.map +1 -1
  106. package/dist/composed/markdown-viewer.js +3 -3
  107. package/dist/composed/markdown-viewer.js.map +1 -1
  108. package/dist/composed/multi-select-popover.js +1 -1
  109. package/dist/composed/multi-select-popover.js.map +1 -1
  110. package/dist/composed/page-header.js +1 -1
  111. package/dist/composed/page-header.js.map +1 -1
  112. package/dist/composed/page-skeletons.js +15 -15
  113. package/dist/composed/page-skeletons.js.map +1 -1
  114. package/dist/composed/priority-indicator.js +3 -3
  115. package/dist/composed/priority-indicator.js.map +1 -1
  116. package/dist/composed/rich-chat-input.js +2073 -2
  117. package/dist/composed/rich-chat-input.js.map +1 -0
  118. package/dist/composed/rich-text-editor.js +75 -72
  119. package/dist/composed/rich-text-editor.js.map +1 -1
  120. package/dist/composed/schedule-view.js +3 -3
  121. package/dist/composed/schedule-view.js.map +1 -1
  122. package/dist/composed/status-badge.d.ts +3 -2
  123. package/dist/composed/status-badge.d.ts.map +1 -1
  124. package/dist/composed/status-badge.js +58 -53
  125. package/dist/composed/status-badge.js.map +1 -1
  126. package/dist/shell/app-command-palette.d.ts +2 -1
  127. package/dist/shell/app-command-palette.d.ts.map +1 -1
  128. package/dist/shell/app-command-palette.js.map +1 -1
  129. package/dist/shell/bottom-navbar.d.ts +3 -1
  130. package/dist/shell/bottom-navbar.d.ts.map +1 -1
  131. package/dist/shell/bottom-navbar.js +94 -86
  132. package/dist/shell/bottom-navbar.js.map +1 -1
  133. package/dist/shell/command-registry.d.ts +2 -1
  134. package/dist/shell/command-registry.d.ts.map +1 -1
  135. package/dist/shell/command-registry.js.map +1 -1
  136. package/dist/shell/notification-center.js +8 -8
  137. package/dist/shell/notification-center.js.map +1 -1
  138. package/dist/shell/notification-preferences.js +1 -1
  139. package/dist/shell/notification-preferences.js.map +1 -1
  140. package/dist/shell/sidebar.d.ts +7 -4
  141. package/dist/shell/sidebar.d.ts.map +1 -1
  142. package/dist/shell/sidebar.js +148 -134
  143. package/dist/shell/sidebar.js.map +1 -1
  144. package/dist/shell/top-bar.d.ts +4 -3
  145. package/dist/shell/top-bar.d.ts.map +1 -1
  146. package/dist/shell/top-bar.js +116 -108
  147. package/dist/shell/top-bar.js.map +1 -1
  148. package/dist/tokens/semantic.css +82 -9
  149. package/dist/ui/accordion.js +1 -1
  150. package/dist/ui/accordion.js.map +1 -1
  151. package/dist/ui/alert-dialog.js +3 -3
  152. package/dist/ui/alert-dialog.js.map +1 -1
  153. package/dist/ui/alert.js +2 -2
  154. package/dist/ui/alert.js.map +1 -1
  155. package/dist/ui/autocomplete.js +2 -2
  156. package/dist/ui/autocomplete.js.map +1 -1
  157. package/dist/ui/avatar.js +11 -11
  158. package/dist/ui/avatar.js.map +1 -1
  159. package/dist/ui/badge-indicator.js +1 -1
  160. package/dist/ui/badge-indicator.js.map +1 -1
  161. package/dist/ui/badge.d.ts +3 -2
  162. package/dist/ui/badge.d.ts.map +1 -1
  163. package/dist/ui/banner.js +1 -1
  164. package/dist/ui/banner.js.map +1 -1
  165. package/dist/ui/breadcrumb.js +1 -1
  166. package/dist/ui/breadcrumb.js.map +1 -1
  167. package/dist/ui/button.d.ts +5 -4
  168. package/dist/ui/button.d.ts.map +1 -1
  169. package/dist/ui/button.js +66 -65
  170. package/dist/ui/button.js.map +1 -1
  171. package/dist/ui/card.js +5 -5
  172. package/dist/ui/card.js.map +1 -1
  173. package/dist/ui/charts/area-chart.js +177 -0
  174. package/dist/ui/charts/area-chart.js.map +1 -0
  175. package/dist/ui/charts/bar-chart.js +127 -0
  176. package/dist/ui/charts/bar-chart.js.map +1 -0
  177. package/dist/ui/charts/chart-container.js +3 -0
  178. package/dist/ui/charts/gauge-chart.js +72 -0
  179. package/dist/ui/charts/gauge-chart.js.map +1 -0
  180. package/dist/ui/charts/index.js +10 -1035
  181. package/dist/ui/charts/line-chart.js +135 -0
  182. package/dist/ui/charts/line-chart.js.map +1 -0
  183. package/dist/ui/charts/pie-chart.js +111 -0
  184. package/dist/ui/charts/pie-chart.js.map +1 -0
  185. package/dist/ui/charts/radar-chart.js +170 -0
  186. package/dist/ui/charts/radar-chart.js.map +1 -0
  187. package/dist/ui/charts/sparkline.js +119 -0
  188. package/dist/ui/charts/sparkline.js.map +1 -0
  189. package/dist/ui/chat/message.d.ts +3 -3
  190. package/dist/ui/chat/message.d.ts.map +1 -1
  191. package/dist/ui/chat/system-message.d.ts +2 -1
  192. package/dist/ui/chat/system-message.d.ts.map +1 -1
  193. package/dist/ui/checkbox.js +1 -1
  194. package/dist/ui/checkbox.js.map +1 -1
  195. package/dist/ui/code.js +2 -2
  196. package/dist/ui/code.js.map +1 -1
  197. package/dist/ui/color-input.js +10 -10
  198. package/dist/ui/color-input.js.map +1 -1
  199. package/dist/ui/color-swatch.js +3 -3
  200. package/dist/ui/color-swatch.js.map +1 -1
  201. package/dist/ui/combobox.d.ts +2 -1
  202. package/dist/ui/combobox.d.ts.map +1 -1
  203. package/dist/ui/combobox.js +100 -95
  204. package/dist/ui/combobox.js.map +1 -1
  205. package/dist/ui/container.d.ts +6 -1
  206. package/dist/ui/container.d.ts.map +1 -1
  207. package/dist/ui/container.js +2 -1
  208. package/dist/ui/container.js.map +1 -1
  209. package/dist/ui/context-menu.js +6 -6
  210. package/dist/ui/context-menu.js.map +1 -1
  211. package/dist/ui/data-table-body.js +1 -1
  212. package/dist/ui/data-table-body.js.map +1 -1
  213. package/dist/ui/data-table-bulk-actions.js +2 -2
  214. package/dist/ui/data-table-bulk-actions.js.map +1 -1
  215. package/dist/ui/data-table-card.js +2 -2
  216. package/dist/ui/data-table-card.js.map +1 -1
  217. package/dist/ui/data-table-header.js +2 -2
  218. package/dist/ui/data-table-header.js.map +1 -1
  219. package/dist/ui/data-table-pagination.js +3 -3
  220. package/dist/ui/data-table-pagination.js.map +1 -1
  221. package/dist/ui/data-table-toolbar.js +1 -1
  222. package/dist/ui/data-table-toolbar.js.map +1 -1
  223. package/dist/ui/data-table.js +1 -1
  224. package/dist/ui/data-table.js.map +1 -1
  225. package/dist/ui/devalok-grain.d.ts +1 -1
  226. package/dist/ui/devalok-grain.js.map +1 -1
  227. package/dist/ui/dialog.js +2 -2
  228. package/dist/ui/dialog.js.map +1 -1
  229. package/dist/ui/dropdown-menu.js +6 -6
  230. package/dist/ui/dropdown-menu.js.map +1 -1
  231. package/dist/ui/file-upload.js +4 -4
  232. package/dist/ui/file-upload.js.map +1 -1
  233. package/dist/ui/hover-card.js +1 -1
  234. package/dist/ui/hover-card.js.map +1 -1
  235. package/dist/ui/icon-button.d.ts +9 -2
  236. package/dist/ui/icon-button.d.ts.map +1 -1
  237. package/dist/ui/icon-button.js +14 -13
  238. package/dist/ui/icon-button.js.map +1 -1
  239. package/dist/ui/index.d.ts +1 -3
  240. package/dist/ui/index.d.ts.map +1 -1
  241. package/dist/ui/index.js +31 -33
  242. package/dist/ui/index.js.map +1 -1
  243. package/dist/ui/input-otp.js +1 -1
  244. package/dist/ui/input-otp.js.map +1 -1
  245. package/dist/ui/input.js +2 -2
  246. package/dist/ui/input.js.map +1 -1
  247. package/dist/ui/lib/icon-input.d.ts +42 -0
  248. package/dist/ui/lib/icon-input.d.ts.map +1 -0
  249. package/dist/ui/lib/normalize-icon.d.ts +39 -0
  250. package/dist/ui/lib/normalize-icon.d.ts.map +1 -0
  251. package/dist/ui/link.js +1 -1
  252. package/dist/ui/link.js.map +1 -1
  253. package/dist/ui/menubar.js +8 -8
  254. package/dist/ui/menubar.js.map +1 -1
  255. package/dist/ui/navigation-menu.js +3 -3
  256. package/dist/ui/navigation-menu.js.map +1 -1
  257. package/dist/ui/number-input.js +3 -3
  258. package/dist/ui/number-input.js.map +1 -1
  259. package/dist/ui/oauth-button/index.d.ts +3 -0
  260. package/dist/ui/oauth-button/index.d.ts.map +1 -0
  261. package/dist/ui/oauth-button/index.js +3 -0
  262. package/dist/ui/oauth-button/oauth-button.d.ts +208 -0
  263. package/dist/ui/oauth-button/oauth-button.d.ts.map +1 -0
  264. package/dist/ui/pagination.js +1 -1
  265. package/dist/ui/pagination.js.map +1 -1
  266. package/dist/ui/progress.js +2 -2
  267. package/dist/ui/progress.js.map +1 -1
  268. package/dist/ui/radio.js +1 -1
  269. package/dist/ui/radio.js.map +1 -1
  270. package/dist/ui/segmented-control.d.ts +3 -4
  271. package/dist/ui/segmented-control.d.ts.map +1 -1
  272. package/dist/ui/segmented-control.js +53 -45
  273. package/dist/ui/segmented-control.js.map +1 -1
  274. package/dist/ui/select.js +3 -3
  275. package/dist/ui/select.js.map +1 -1
  276. package/dist/ui/sheet.js +2 -2
  277. package/dist/ui/sheet.js.map +1 -1
  278. package/dist/ui/sidebar.js +11 -11
  279. package/dist/ui/sidebar.js.map +1 -1
  280. package/dist/ui/skeleton.js +9 -9
  281. package/dist/ui/skeleton.js.map +1 -1
  282. package/dist/ui/slider.js +2 -2
  283. package/dist/ui/slider.js.map +1 -1
  284. package/dist/ui/split-button.js +7 -7
  285. package/dist/ui/split-button.js.map +1 -1
  286. package/dist/ui/stack.d.ts +6 -1
  287. package/dist/ui/stack.d.ts.map +1 -1
  288. package/dist/ui/stack.js +2 -1
  289. package/dist/ui/stack.js.map +1 -1
  290. package/dist/ui/stat-card.d.ts +2 -3
  291. package/dist/ui/stat-card.d.ts.map +1 -1
  292. package/dist/ui/stat-card.js +118 -116
  293. package/dist/ui/stat-card.js.map +1 -1
  294. package/dist/ui/status-dot.js +2 -2
  295. package/dist/ui/status-dot.js.map +1 -1
  296. package/dist/ui/stepper.d.ts +2 -1
  297. package/dist/ui/stepper.d.ts.map +1 -1
  298. package/dist/ui/stepper.js +74 -69
  299. package/dist/ui/stepper.js.map +1 -1
  300. package/dist/ui/switch.js +2 -2
  301. package/dist/ui/switch.js.map +1 -1
  302. package/dist/ui/tabs.js +3 -3
  303. package/dist/ui/tabs.js.map +1 -1
  304. package/dist/ui/text.d.ts +7 -2
  305. package/dist/ui/text.d.ts.map +1 -1
  306. package/dist/ui/text.js +2 -1
  307. package/dist/ui/text.js.map +1 -1
  308. package/dist/ui/textarea.js +1 -1
  309. package/dist/ui/textarea.js.map +1 -1
  310. package/dist/ui/toast.js +8 -8
  311. package/dist/ui/toast.js.map +1 -1
  312. package/dist/ui/toaster.d.ts +11 -2
  313. package/dist/ui/toaster.d.ts.map +1 -1
  314. package/dist/ui/toaster.js.map +1 -1
  315. package/dist/ui/toggle.js +1 -1
  316. package/dist/ui/toggle.js.map +1 -1
  317. package/dist/ui/tooltip.js +12 -12
  318. package/dist/ui/tooltip.js.map +1 -1
  319. package/dist/ui/tree-view/tree-item.d.ts +3 -2
  320. package/dist/ui/tree-view/tree-item.d.ts.map +1 -1
  321. package/dist/ui/tree-view/use-tree.d.ts +2 -1
  322. package/dist/ui/tree-view/use-tree.d.ts.map +1 -1
  323. package/docs/components/_header.md +90 -1
  324. package/docs/components/ui/oauth-button.md +86 -0
  325. package/docs/recipes/customize-brand.md +100 -4
  326. package/docs/recipes/index.md +5 -1
  327. package/docs/recipes/install-astro.md +15 -0
  328. package/docs/recipes/install-next-app-router.md +21 -5
  329. package/docs/recipes/install-next-pages.md +2 -0
  330. package/docs/recipes/install-remix.md +15 -0
  331. package/docs/recipes/install-tanstack-start.md +15 -0
  332. package/docs/recipes/install-vite.md +15 -0
  333. package/docs/recipes/troubleshoot.md +22 -0
  334. package/llms-full.txt +177 -2
  335. package/llms-quick.txt +247 -0
  336. package/llms.txt +116 -3
  337. package/package.json +80 -2
  338. package/scripts/welcome.mjs +219 -0
  339. package/skill/README.md +99 -0
  340. package/skill/SKILL.md +163 -0
  341. package/skill/install.sh +59 -0
  342. package/skill/references/components-full.md +7083 -0
  343. package/skill/references/components.md +778 -0
  344. package/skill/references/customize-brand.md +314 -0
  345. package/skill/references/server-components.md +211 -0
  346. package/skill/references/setup-astro.md +195 -0
  347. package/skill/references/setup-next-app-router.md +248 -0
  348. package/skill/references/setup-next-pages.md +127 -0
  349. package/skill/references/setup-remix.md +188 -0
  350. package/skill/references/setup-tanstack-start.md +160 -0
  351. package/skill/references/setup-vite.md +187 -0
  352. package/skill/references/troubleshoot.md +241 -0
  353. package/dist/_chunks/date-picker.js +0 -748
  354. package/dist/_chunks/date-picker.js.map +0 -1
  355. package/dist/_chunks/file-preview.js +0 -499
  356. package/dist/_chunks/file-preview.js.map +0 -1
  357. package/dist/_chunks/rich-chat-input.js +0 -2071
  358. package/dist/_chunks/rich-chat-input.js.map +0 -1
  359. package/dist/_chunks/text.js.map +0 -1
  360. package/dist/ui/charts/index.js.map +0 -1
  361. /package/{LICENSE → skill/LICENSE} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"app-command-palette.js","names":[],"sources":["../../src/shell/app-command-palette.tsx"],"sourcesContent":["'use client'\n\n/**\n * AppCommandPalette -- Application-level command palette wrapper.\n *\n * Props-driven: accepts navigation items, search callbacks, and user\n * info instead of reading from Zustand stores or Remix hooks.\n *\n * Depends on the CommandPalette shared component being available.\n */\nimport { IconFileText } from '@tabler/icons-react'\nimport * as React from 'react'\nimport { useCallback, useMemo } from 'react'\n\nimport { type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, type FooterHint } from '../composed/command-palette'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { useCommandRegistry } from './command-registry'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface SearchResult {\n id: string\n title: string\n snippet?: string\n entityType: string\n projectId?: string | null\n metadata?: Record<string, unknown>\n /** Custom icon for this result. Overrides the default entity-type icon. */\n icon?: React.ReactNode\n /** Relevance score for sorting (higher = more relevant). */\n rank?: number\n /** Keyboard shortcut hint to display on this result. */\n shortcut?: string\n /** Navigation href. Used as fallback when onSearchResultSelect is not provided. */\n href?: string\n}\n\n/** A group of search results with a label. */\nexport interface SearchResultGroup {\n label: string\n results: SearchResult[]\n}\n\nexport interface AppCommandPaletteUser {\n name: string\n role?: string\n}\n\nexport interface AppCommandPaletteProps\n extends Omit<React.ComponentPropsWithRef<'div'>, 'onSearch'> {\n /** Current user (used to determine admin access) */\n user?: AppCommandPaletteUser | null\n /** When true, shows admin command groups regardless of user.role. Takes precedence over role-based detection. */\n isAdmin?: boolean\n /** Additional command groups to show */\n extraGroups?: CommandGroup[]\n /** Called when the user selects a navigation target */\n onNavigate?: (path: string) => void\n /** Called when the search input changes (for server-side search) */\n onSearch?: (query: string) => void\n /** Flat search results (displayed in a single \"Search Results\" group).\n * For grouped results, use `searchResultGroups` instead. */\n searchResults?: SearchResult[]\n /** Grouped search results — displayed as multiple labeled sections.\n * Takes precedence over `searchResults` when provided. */\n searchResultGroups?: SearchResultGroup[]\n /** Whether a search is currently in progress */\n isSearching?: boolean\n /** Called when the user selects a search result. When provided, the component\n * does NOT perform internal navigation — the consumer owns routing entirely. */\n onSearchResultSelect?: (result: SearchResult) => void\n /** Custom label for the search results group. Overrides \"Search Results\" / \"Searching...\".\n * Can be a string or a function receiving the result count. */\n searchResultsLabel?: string | ((count: number) => string)\n // -- Pass-through CommandPalette props --\n /** Controlled open state. */\n open?: boolean\n /** Default open state. */\n defaultOpen?: boolean\n /** Called when the open state changes. */\n onOpenChange?: (open: boolean) => void\n /** Keybinding(s) to toggle. Pass `false` to disable. Default: 'mod+k'. */\n keybinding?: CommandPaletteProps['keybinding']\n /** Max height of results. Default '320px'. */\n maxHeight?: CommandPaletteProps['maxHeight']\n /** Custom empty state. */\n emptyState?: React.ReactNode\n /** Custom footer hints. Pass `false` to hide. */\n footerHints?: FooterHint[] | false\n}\n\n// -----------------------------------------------------------------------\n// Constants\n// -----------------------------------------------------------------------\n\n// -----------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------\n\n/** Convert a SearchResult into a CommandItem. */\nfunction searchResultToCommandItem(\n r: SearchResult,\n onSearchResultSelect: ((result: SearchResult) => void) | undefined,\n nav: (to: string) => void,\n): CommandItem {\n return {\n id: `search-${r.entityType}-${r.id}`,\n label: r.title,\n filterValue: r.title,\n description: r.snippet,\n icon: r.icon ?? <Icon icon={IconFileText} />,\n shortcut: r.shortcut,\n onSelect: () => {\n if (onSearchResultSelect) {\n onSearchResultSelect(r)\n } else if (r.href) {\n nav(r.href)\n }\n },\n }\n}\n\n// -----------------------------------------------------------------------\n// AppCommandPalette\n// -----------------------------------------------------------------------\n\nconst AppCommandPalette = React.forwardRef<HTMLDivElement, AppCommandPaletteProps>(\n function AppCommandPalette(\n {\n user,\n isAdmin: isAdminProp,\n extraGroups = [],\n onNavigate,\n onSearch,\n searchResults = [],\n searchResultGroups,\n isSearching = false,\n onSearchResultSelect,\n searchResultsLabel,\n open,\n defaultOpen,\n onOpenChange,\n keybinding,\n maxHeight,\n emptyState,\n footerHints,\n className,\n ...props\n },\n ref,\n ) {\n const isAdmin =\n isAdminProp ?? (user?.role === 'Admin' || user?.role === 'SuperAdmin')\n\n const registry = useCommandRegistry()\n\n const nav = useCallback(\n (to: string) => {\n onNavigate?.(to)\n },\n [onNavigate],\n )\n\n // -- Static groups ---------------------------------------------------\n\n const pagesGroup: CommandGroup = useMemo(\n () => ({\n label: 'Pages',\n items: registry\n ? registry.pages.map((p) => ({\n id: p.id,\n label: p.label,\n icon: p.icon,\n keywords: p.keywords,\n onSelect: () => nav(p.path),\n }))\n : [], // Consumer provides pages via CommandRegistryProvider\n }),\n [nav, registry],\n )\n\n const adminGroup: CommandGroup = useMemo(\n () => ({\n label: 'Admin',\n items: registry\n ? registry.adminPages.map((p) => ({\n id: p.id,\n label: p.label,\n icon: p.icon,\n keywords: p.keywords,\n onSelect: () => nav(p.path),\n }))\n : [], // Consumer provides admin pages via CommandRegistryProvider\n }),\n [nav, registry],\n )\n\n // -- Dynamic search results groups ------------------------------------\n\n const searchGroups: CommandGroup[] = useMemo(() => {\n // Grouped results take precedence (#2)\n if (searchResultGroups && searchResultGroups.length > 0) {\n return searchResultGroups.map((group) => {\n // Sort by rank if provided\n const sorted = group.results.some(r => r.rank != null)\n ? [...group.results].sort((a, b) => (b.rank ?? 0) - (a.rank ?? 0))\n : group.results\n\n return {\n label: group.label,\n items: sorted.map(r => searchResultToCommandItem(r, onSearchResultSelect, nav)),\n }\n })\n }\n\n // Flat results (legacy behavior)\n if (searchResults.length === 0) return []\n\n // Sort by rank if any result has one\n const sorted = searchResults.some(r => r.rank != null)\n ? [...searchResults].sort((a, b) => (b.rank ?? 0) - (a.rank ?? 0))\n : searchResults\n\n const items = sorted.map(r => searchResultToCommandItem(r, onSearchResultSelect, nav))\n\n // Resolve the label (#10)\n let label: string\n if (searchResultsLabel) {\n label = typeof searchResultsLabel === 'function'\n ? searchResultsLabel(items.length)\n : searchResultsLabel\n } else {\n label = isSearching ? 'Searching...' : 'Search Results'\n }\n\n return [{ label, items }]\n }, [searchResults, searchResultGroups, isSearching, nav, onSearchResultSelect, searchResultsLabel])\n\n // -- Assemble groups -------------------------------------------------\n\n const groups: CommandGroup[] = useMemo(() => {\n const g: CommandGroup[] = []\n g.push(...searchGroups)\n g.push(pagesGroup)\n if (isAdmin) g.push(adminGroup)\n g.push(...extraGroups)\n return g\n }, [searchGroups, pagesGroup, isAdmin, adminGroup, extraGroups])\n\n // -- Handle search ---------------------------------------------------\n\n const handleSearch = useCallback(\n (query: string) => {\n onSearch?.(query)\n },\n [onSearch],\n )\n\n return (\n <CommandPalette\n ref={ref}\n groups={groups}\n placeholder=\"Search or jump to...\"\n onSearch={handleSearch}\n emptyMessage=\"No results found. Try a different search term.\"\n open={open}\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n keybinding={keybinding}\n maxHeight={maxHeight}\n emptyState={emptyState}\n footerHints={footerHints}\n className={cn(className)}\n {...props}\n />\n )\n },\n)\n\nAppCommandPalette.displayName = 'AppCommandPalette'\n\nexport { AppCommandPalette }\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-command-palette.js","names":[],"sources":["../../src/shell/app-command-palette.tsx"],"sourcesContent":["'use client'\n\n/**\n * AppCommandPalette -- Application-level command palette wrapper.\n *\n * Props-driven: accepts navigation items, search callbacks, and user\n * info instead of reading from Zustand stores or Remix hooks.\n *\n * Depends on the CommandPalette shared component being available.\n */\nimport { IconFileText } from '@tabler/icons-react'\nimport * as React from 'react'\nimport { useCallback, useMemo } from 'react'\n\nimport { type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, type FooterHint } from '../composed/command-palette'\nimport { Icon } from '../ui/icon'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { cn } from '../ui/lib/utils'\nimport { useCommandRegistry } from './command-registry'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface SearchResult {\n id: string\n title: string\n snippet?: string\n entityType: string\n projectId?: string | null\n metadata?: Record<string, unknown>\n /** Custom icon for this result. Overrides the default entity-type icon. */\n icon?: IconInput\n /** Relevance score for sorting (higher = more relevant). */\n rank?: number\n /** Keyboard shortcut hint to display on this result. */\n shortcut?: string\n /** Navigation href. Used as fallback when onSearchResultSelect is not provided. */\n href?: string\n}\n\n/** A group of search results with a label. */\nexport interface SearchResultGroup {\n label: string\n results: SearchResult[]\n}\n\nexport interface AppCommandPaletteUser {\n name: string\n role?: string\n}\n\nexport interface AppCommandPaletteProps\n extends Omit<React.ComponentPropsWithRef<'div'>, 'onSearch'> {\n /** Current user (used to determine admin access) */\n user?: AppCommandPaletteUser | null\n /** When true, shows admin command groups regardless of user.role. Takes precedence over role-based detection. */\n isAdmin?: boolean\n /** Additional command groups to show */\n extraGroups?: CommandGroup[]\n /** Called when the user selects a navigation target */\n onNavigate?: (path: string) => void\n /** Called when the search input changes (for server-side search) */\n onSearch?: (query: string) => void\n /** Flat search results (displayed in a single \"Search Results\" group).\n * For grouped results, use `searchResultGroups` instead. */\n searchResults?: SearchResult[]\n /** Grouped search results — displayed as multiple labeled sections.\n * Takes precedence over `searchResults` when provided. */\n searchResultGroups?: SearchResultGroup[]\n /** Whether a search is currently in progress */\n isSearching?: boolean\n /** Called when the user selects a search result. When provided, the component\n * does NOT perform internal navigation — the consumer owns routing entirely. */\n onSearchResultSelect?: (result: SearchResult) => void\n /** Custom label for the search results group. Overrides \"Search Results\" / \"Searching...\".\n * Can be a string or a function receiving the result count. */\n searchResultsLabel?: string | ((count: number) => string)\n // -- Pass-through CommandPalette props --\n /** Controlled open state. */\n open?: boolean\n /** Default open state. */\n defaultOpen?: boolean\n /** Called when the open state changes. */\n onOpenChange?: (open: boolean) => void\n /** Keybinding(s) to toggle. Pass `false` to disable. Default: 'mod+k'. */\n keybinding?: CommandPaletteProps['keybinding']\n /** Max height of results. Default '320px'. */\n maxHeight?: CommandPaletteProps['maxHeight']\n /** Custom empty state. */\n emptyState?: React.ReactNode\n /** Custom footer hints. Pass `false` to hide. */\n footerHints?: FooterHint[] | false\n}\n\n// -----------------------------------------------------------------------\n// Constants\n// -----------------------------------------------------------------------\n\n// -----------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------\n\n/** Convert a SearchResult into a CommandItem. */\nfunction searchResultToCommandItem(\n r: SearchResult,\n onSearchResultSelect: ((result: SearchResult) => void) | undefined,\n nav: (to: string) => void,\n): CommandItem {\n return {\n id: `search-${r.entityType}-${r.id}`,\n label: r.title,\n filterValue: r.title,\n description: r.snippet,\n icon: r.icon ?? <Icon icon={IconFileText} />,\n shortcut: r.shortcut,\n onSelect: () => {\n if (onSearchResultSelect) {\n onSearchResultSelect(r)\n } else if (r.href) {\n nav(r.href)\n }\n },\n }\n}\n\n// -----------------------------------------------------------------------\n// AppCommandPalette\n// -----------------------------------------------------------------------\n\nconst AppCommandPalette = React.forwardRef<HTMLDivElement, AppCommandPaletteProps>(\n function AppCommandPalette(\n {\n user,\n isAdmin: isAdminProp,\n extraGroups = [],\n onNavigate,\n onSearch,\n searchResults = [],\n searchResultGroups,\n isSearching = false,\n onSearchResultSelect,\n searchResultsLabel,\n open,\n defaultOpen,\n onOpenChange,\n keybinding,\n maxHeight,\n emptyState,\n footerHints,\n className,\n ...props\n },\n ref,\n ) {\n const isAdmin =\n isAdminProp ?? (user?.role === 'Admin' || user?.role === 'SuperAdmin')\n\n const registry = useCommandRegistry()\n\n const nav = useCallback(\n (to: string) => {\n onNavigate?.(to)\n },\n [onNavigate],\n )\n\n // -- Static groups ---------------------------------------------------\n\n const pagesGroup: CommandGroup = useMemo(\n () => ({\n label: 'Pages',\n items: registry\n ? registry.pages.map((p) => ({\n id: p.id,\n label: p.label,\n icon: p.icon,\n keywords: p.keywords,\n onSelect: () => nav(p.path),\n }))\n : [], // Consumer provides pages via CommandRegistryProvider\n }),\n [nav, registry],\n )\n\n const adminGroup: CommandGroup = useMemo(\n () => ({\n label: 'Admin',\n items: registry\n ? registry.adminPages.map((p) => ({\n id: p.id,\n label: p.label,\n icon: p.icon,\n keywords: p.keywords,\n onSelect: () => nav(p.path),\n }))\n : [], // Consumer provides admin pages via CommandRegistryProvider\n }),\n [nav, registry],\n )\n\n // -- Dynamic search results groups ------------------------------------\n\n const searchGroups: CommandGroup[] = useMemo(() => {\n // Grouped results take precedence (#2)\n if (searchResultGroups && searchResultGroups.length > 0) {\n return searchResultGroups.map((group) => {\n // Sort by rank if provided\n const sorted = group.results.some(r => r.rank != null)\n ? [...group.results].sort((a, b) => (b.rank ?? 0) - (a.rank ?? 0))\n : group.results\n\n return {\n label: group.label,\n items: sorted.map(r => searchResultToCommandItem(r, onSearchResultSelect, nav)),\n }\n })\n }\n\n // Flat results (legacy behavior)\n if (searchResults.length === 0) return []\n\n // Sort by rank if any result has one\n const sorted = searchResults.some(r => r.rank != null)\n ? [...searchResults].sort((a, b) => (b.rank ?? 0) - (a.rank ?? 0))\n : searchResults\n\n const items = sorted.map(r => searchResultToCommandItem(r, onSearchResultSelect, nav))\n\n // Resolve the label (#10)\n let label: string\n if (searchResultsLabel) {\n label = typeof searchResultsLabel === 'function'\n ? searchResultsLabel(items.length)\n : searchResultsLabel\n } else {\n label = isSearching ? 'Searching...' : 'Search Results'\n }\n\n return [{ label, items }]\n }, [searchResults, searchResultGroups, isSearching, nav, onSearchResultSelect, searchResultsLabel])\n\n // -- Assemble groups -------------------------------------------------\n\n const groups: CommandGroup[] = useMemo(() => {\n const g: CommandGroup[] = []\n g.push(...searchGroups)\n g.push(pagesGroup)\n if (isAdmin) g.push(adminGroup)\n g.push(...extraGroups)\n return g\n }, [searchGroups, pagesGroup, isAdmin, adminGroup, extraGroups])\n\n // -- Handle search ---------------------------------------------------\n\n const handleSearch = useCallback(\n (query: string) => {\n onSearch?.(query)\n },\n [onSearch],\n )\n\n return (\n <CommandPalette\n ref={ref}\n groups={groups}\n placeholder=\"Search or jump to...\"\n onSearch={handleSearch}\n emptyMessage=\"No results found. Try a different search term.\"\n open={open}\n defaultOpen={defaultOpen}\n onOpenChange={onOpenChange}\n keybinding={keybinding}\n maxHeight={maxHeight}\n emptyState={emptyState}\n footerHints={footerHints}\n className={cn(className)}\n {...props}\n />\n )\n },\n)\n\nAppCommandPalette.displayName = 'AppCommandPalette'\n\nexport { AppCommandPalette }\n"],"mappings":"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"}
@@ -1,9 +1,11 @@
1
1
  "use client";
2
+ import { IconInput } from '../ui/lib/icon-input';
2
3
  import * as React from 'react';
3
4
  export interface BottomNavItem {
4
5
  title: string;
5
6
  href: string;
6
- icon: React.ReactNode;
7
+ /** Icon for this nav item. Accepts any `IconInput`. */
8
+ icon: IconInput;
7
9
  /** When true, the route matches only when the path is exactly equal */
8
10
  exact?: boolean;
9
11
  /** Notification badge count. 0 or undefined = hidden, 1–99 = shown, >99 = "99+" */
@@ -1 +1 @@
1
- {"version":3,"file":"bottom-navbar.d.ts","sourceRoot":"","sources":["../../src/shell/bottom-navbar.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAY9B,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,KAAK,CAAC,SAAS,CAAA;IACrB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;IACzC,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC9B,6EAA6E;IAC7E,YAAY,CAAC,EAAE,aAAa,EAAE,CAAA;IAC9B,yDAAyD;IACzD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;IAC3B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4ED,QAAA,MAAM,YAAY,uFA6JjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"bottom-navbar.d.ts","sourceRoot":"","sources":["../../src/shell/bottom-navbar.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAUrD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,IAAI,EAAE,SAAS,CAAA;IACf,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;IACzC,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC9B,6EAA6E;IAC7E,YAAY,CAAC,EAAE,aAAa,EAAE,CAAA;IAC9B,yDAAyD;IACzD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;IAC3B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4ED,QAAA,MAAM,YAAY,uFA6JjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -1,145 +1,153 @@
1
1
  "use client";
2
- import { springs as e } from "../ui/lib/motion.js";
3
- import { cn as t } from "../ui/lib/utils.js";
4
- import { Icon as n } from "../ui/icon.js";
5
- import { n as r } from "../_chunks/link-context.js";
2
+ import { IconProvider as e } from "../ui/icon-context.js";
3
+ import { springs as t } from "../ui/lib/motion.js";
4
+ import { cn as n } from "../ui/lib/utils.js";
5
+ import { Icon as r } from "../ui/icon.js";
6
+ import { t as i } from "../_chunks/normalize-icon.js";
7
+ import { n as a } from "../_chunks/link-context.js";
6
8
  import "./link-context.js";
7
- import * as i from "react";
8
- import { useCallback as a, useEffect as o, useRef as s, useState as c } from "react";
9
- import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
10
- import { IconDots as f, IconX as p } from "@tabler/icons-react";
11
- import { AnimatePresence as m, motion as h, useReducedMotion as g } from "framer-motion";
9
+ import * as o from "react";
10
+ import { useCallback as s, useEffect as c, useRef as l, useState as u } from "react";
11
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
12
+ import { IconDots as m, IconX as h } from "@tabler/icons-react";
13
+ import { AnimatePresence as g, motion as _, useReducedMotion as v } from "framer-motion";
12
14
  //#region src/shell/bottom-navbar.tsx
13
- function _({ count: e }) {
15
+ function y({ count: e }) {
14
16
  if (!e || e <= 0) return null;
15
- let n = e > 99 ? "99+" : String(e), r = e >= 10;
16
- return /* @__PURE__ */ u("span", {
17
+ let t = e > 99 ? "99+" : String(e), r = e >= 10;
18
+ return /* @__PURE__ */ f("span", {
17
19
  "aria-label": `${e} notifications`,
18
- className: t("absolute -right-1 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-error-9 text-[10px] font-semibold leading-none text-error-fg animate-in zoom-in-75", r ? "px-0.5" : ""),
19
- children: n
20
+ className: n("absolute -right-1 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-pill bg-error-9 text-[10px] font-semibold leading-none text-error-fg animate-in zoom-in-75", r ? "px-0.5" : ""),
21
+ children: t
20
22
  });
21
23
  }
22
- function v({ item: n, isActive: i, onClick: a }) {
23
- let o = r(), s = g();
24
- return /* @__PURE__ */ u(h.div, {
25
- whileTap: s ? void 0 : { y: -2 },
26
- transition: s ? { duration: 0 } : e.snappy,
24
+ function b({ item: r, isActive: o, onClick: s }) {
25
+ let c = a(), l = v();
26
+ return /* @__PURE__ */ f(_.div, {
27
+ whileTap: l ? void 0 : { y: -2 },
28
+ transition: l ? { duration: 0 } : t.snappy,
27
29
  className: "flex max-w-[70px] flex-1",
28
- children: /* @__PURE__ */ u(o, {
29
- href: n.href,
30
- onClick: a,
31
- "aria-label": n.title,
32
- "aria-current": i ? "page" : void 0,
33
- className: t("flex h-16 w-full cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm transition-colors duration-fast-02 ease-productive-standard", i ? "font-semibold text-accent-11" : "text-surface-fg-subtle"),
34
- children: /* @__PURE__ */ d("div", {
30
+ children: /* @__PURE__ */ f(c, {
31
+ href: r.href,
32
+ onClick: s,
33
+ "aria-label": r.title,
34
+ "aria-current": o ? "page" : void 0,
35
+ className: n("flex h-16 w-full cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm transition-colors duration-fast-02 ease-productive-standard", o ? "font-semibold text-accent-11" : "text-surface-fg-subtle"),
36
+ children: /* @__PURE__ */ p("div", {
35
37
  className: "relative flex w-full flex-col items-center gap-ds-02",
36
38
  children: [
37
- i && /* @__PURE__ */ u(h.div, {
39
+ o && /* @__PURE__ */ f(_.div, {
38
40
  layoutId: "bottom-nav-indicator",
39
- className: "absolute top-0 h-[3px] w-full rounded-b-ds-sm bg-accent-9 p-0",
41
+ className: "absolute top-0 h-[3px] w-full rounded-b-control-inner bg-accent-9 p-0",
40
42
  "aria-hidden": "true",
41
- transition: s ? { duration: 0 } : e.snappy
43
+ transition: l ? { duration: 0 } : t.snappy
42
44
  }),
43
- /* @__PURE__ */ d("div", {
45
+ /* @__PURE__ */ p("div", {
44
46
  className: "relative p-ds-03",
45
- children: [/* @__PURE__ */ u("span", {
47
+ children: [/* @__PURE__ */ f("span", {
46
48
  className: "[&>svg]:h-ico-md [&>svg]:w-ico-md",
47
49
  "aria-hidden": "true",
48
- children: n.icon
49
- }), n.badge != null && /* @__PURE__ */ u(_, { count: n.badge })]
50
+ children: /* @__PURE__ */ f(e, {
51
+ size: "md",
52
+ children: i(r.icon)
53
+ })
54
+ }), r.badge != null && /* @__PURE__ */ f(y, { count: r.badge })]
50
55
  }),
51
- /* @__PURE__ */ u("span", {
56
+ /* @__PURE__ */ f("span", {
52
57
  className: "text-center",
53
- children: n.title
58
+ children: r.title
54
59
  })
55
60
  ]
56
61
  })
57
62
  })
58
63
  });
59
64
  }
60
- var y = i.forwardRef(({ currentPath: i = "/", user: _, primaryItems: y = [], moreItems: b = [], className: x, ...S }, C) => {
61
- let w = r(), T = g(), [E, D] = c(!1), O = s(null), k = a(() => D(!1), []);
62
- o(() => {
63
- E && O.current && O.current.querySelector("a, button")?.focus();
64
- }, [E]);
65
- let A = (e, t = !1) => t || e === "/" ? i === e : i.startsWith(e), j = b.some((e) => A(e.href, e.exact));
66
- return /* @__PURE__ */ d(l, { children: [/* @__PURE__ */ u(m, { children: E && /* @__PURE__ */ d("div", {
65
+ var x = o.forwardRef(({ currentPath: o = "/", user: y, primaryItems: x = [], moreItems: S = [], className: C, ...w }, T) => {
66
+ let E = a(), D = v(), [O, k] = u(!1), A = l(null), j = s(() => k(!1), []);
67
+ c(() => {
68
+ O && A.current && A.current.querySelector("a, button")?.focus();
69
+ }, [O]);
70
+ let M = (e, t = !1) => t || e === "/" ? o === e : o.startsWith(e), N = S.some((e) => M(e.href, e.exact));
71
+ return /* @__PURE__ */ p(d, { children: [/* @__PURE__ */ f(g, { children: O && /* @__PURE__ */ p("div", {
67
72
  className: "fixed inset-0 z-overlay md:hidden",
68
- onClick: () => D(!1),
69
- children: [/* @__PURE__ */ u("div", { className: "absolute inset-0 bg-overlay" }), /* @__PURE__ */ d(h.div, {
70
- ref: O,
73
+ onClick: () => k(!1),
74
+ children: [/* @__PURE__ */ f("div", { className: "absolute inset-0 bg-overlay" }), /* @__PURE__ */ p(_.div, {
75
+ ref: A,
71
76
  role: "dialog",
72
77
  "aria-label": "More navigation",
73
78
  initial: { y: "100%" },
74
79
  animate: { y: 0 },
75
80
  exit: { y: "100%" },
76
- transition: T ? { duration: 0 } : e.smooth,
77
- className: "absolute bottom-[72px] left-0 right-0 rounded-t-ds-2xl border-t border-surface-border-strong bg-surface-overlay p-ds-05 pb-ds-03",
81
+ transition: D ? { duration: 0 } : t.smooth,
82
+ className: "absolute bottom-[72px] left-0 right-0 rounded-t-bubble border-t border-surface-border-strong bg-surface-overlay p-ds-05 pb-ds-03",
78
83
  onClick: (e) => e.stopPropagation(),
79
84
  onKeyDown: (e) => {
80
- e.stopPropagation(), e.key === "Escape" && k();
85
+ e.stopPropagation(), e.key === "Escape" && j();
81
86
  },
82
- children: [/* @__PURE__ */ d("div", {
87
+ children: [/* @__PURE__ */ p("div", {
83
88
  className: "mb-ds-04 flex items-center justify-between",
84
- children: [/* @__PURE__ */ u("span", {
89
+ children: [/* @__PURE__ */ f("span", {
85
90
  className: "text-ds-md font-semibold text-surface-fg",
86
91
  children: "More"
87
- }), /* @__PURE__ */ u("button", {
88
- onClick: () => D(!1),
92
+ }), /* @__PURE__ */ f("button", {
93
+ onClick: () => k(!1),
89
94
  "aria-label": "Close more menu",
90
- className: "flex h-ds-sm w-ds-sm items-center justify-center rounded-ds-full hover:bg-surface-raised-hover",
91
- children: /* @__PURE__ */ u(n, {
92
- icon: p,
95
+ className: "flex h-ds-sm w-ds-sm items-center justify-center rounded-pill hover:bg-surface-raised-hover",
96
+ children: /* @__PURE__ */ f(r, {
97
+ icon: h,
93
98
  size: "sm",
94
99
  className: "text-surface-fg-muted"
95
100
  })
96
101
  })]
97
- }), /* @__PURE__ */ u("div", {
102
+ }), /* @__PURE__ */ f("div", {
98
103
  className: "grid grid-cols-4 gap-ds-03",
99
- children: b.map((e) => /* @__PURE__ */ d(w, {
100
- href: e.href,
101
- onClick: () => D(!1),
102
- className: t("flex flex-col items-center gap-ds-02b rounded-ds-xl p-ds-04 text-ds-sm transition-colors ease-productive-standard", A(e.href, e.exact) ? "bg-surface-raised-hover text-accent-11" : "text-surface-fg-subtle hover:bg-surface-raised-hover"),
103
- children: [/* @__PURE__ */ u("span", {
104
+ children: S.map((t) => /* @__PURE__ */ p(E, {
105
+ href: t.href,
106
+ onClick: () => k(!1),
107
+ className: n("flex flex-col items-center gap-ds-02b rounded-overlay-lg p-ds-04 text-ds-sm transition-colors ease-productive-standard", M(t.href, t.exact) ? "bg-surface-raised-hover text-accent-11" : "text-surface-fg-subtle hover:bg-surface-raised-hover"),
108
+ children: [/* @__PURE__ */ f("span", {
104
109
  className: "[&>svg]:h-ico-md [&>svg]:w-ico-md",
105
- children: e.icon
106
- }), /* @__PURE__ */ u("span", {
110
+ children: /* @__PURE__ */ f(e, {
111
+ size: "md",
112
+ children: i(t.icon)
113
+ })
114
+ }), /* @__PURE__ */ f("span", {
107
115
  className: "text-center",
108
- children: e.title
116
+ children: t.title
109
117
  })]
110
- }, e.href))
118
+ }, t.href))
111
119
  })]
112
120
  })]
113
- }) }), /* @__PURE__ */ d("nav", {
114
- ...S,
115
- ref: C,
121
+ }) }), /* @__PURE__ */ p("nav", {
122
+ ...w,
123
+ ref: T,
116
124
  "aria-label": "Mobile navigation",
117
- className: t("fixed bottom-0 left-0 right-0 z-sticky flex w-full flex-row items-start justify-between border-t border-surface-border-strong bg-surface-raised px-ds-05 pb-safe pt-0 md:hidden", x),
118
- children: [y.map((e) => /* @__PURE__ */ u(v, {
125
+ className: n("fixed bottom-0 left-0 right-0 z-sticky flex w-full flex-row items-start justify-between border-t border-surface-border-strong bg-surface-raised px-ds-05 pb-safe pt-0 md:hidden", C),
126
+ children: [x.map((e) => /* @__PURE__ */ f(b, {
119
127
  item: e,
120
- isActive: A(e.href, e.exact)
121
- }, e.href)), b.length > 0 && /* @__PURE__ */ u(h.button, {
128
+ isActive: M(e.href, e.exact)
129
+ }, e.href)), S.length > 0 && /* @__PURE__ */ f(_.button, {
122
130
  type: "button",
123
- onClick: () => D(!E),
131
+ onClick: () => k(!O),
124
132
  "aria-label": "More navigation options",
125
- "aria-expanded": E,
126
- whileTap: T ? void 0 : { y: -2 },
127
- transition: T ? { duration: 0 } : e.snappy,
128
- className: t("flex h-16 max-w-[70px] flex-1 cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm", E || j ? "font-semibold text-accent-11" : "text-surface-fg-subtle"),
129
- children: /* @__PURE__ */ d("div", {
133
+ "aria-expanded": O,
134
+ whileTap: D ? void 0 : { y: -2 },
135
+ transition: D ? { duration: 0 } : t.snappy,
136
+ className: n("flex h-16 max-w-[70px] flex-1 cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm", O || N ? "font-semibold text-accent-11" : "text-surface-fg-subtle"),
137
+ children: /* @__PURE__ */ p("div", {
130
138
  className: "relative flex w-full flex-col items-center gap-ds-02",
131
139
  children: [
132
- (E || j) && /* @__PURE__ */ u(h.div, {
140
+ (O || N) && /* @__PURE__ */ f(_.div, {
133
141
  layoutId: "bottom-nav-indicator",
134
- className: "absolute top-0 h-[3px] w-full rounded-b-ds-sm bg-accent-9 p-0",
142
+ className: "absolute top-0 h-[3px] w-full rounded-b-control-inner bg-accent-9 p-0",
135
143
  "aria-hidden": "true",
136
- transition: T ? { duration: 0 } : e.snappy
144
+ transition: D ? { duration: 0 } : t.snappy
137
145
  }),
138
- /* @__PURE__ */ u("div", {
146
+ /* @__PURE__ */ f("div", {
139
147
  className: "p-ds-03",
140
- children: /* @__PURE__ */ u(n, { icon: f })
148
+ children: /* @__PURE__ */ f(r, { icon: m })
141
149
  }),
142
- /* @__PURE__ */ u("span", {
150
+ /* @__PURE__ */ f("span", {
143
151
  className: "text-center",
144
152
  children: "More"
145
153
  })
@@ -148,6 +156,6 @@ var y = i.forwardRef(({ currentPath: i = "/", user: _, primaryItems: y = [], mor
148
156
  })]
149
157
  })] });
150
158
  });
151
- y.displayName = "BottomNavbar";
159
+ x.displayName = "BottomNavbar";
152
160
  //#endregion
153
- export { y as BottomNavbar };
161
+ export { x as BottomNavbar };
@@ -1 +1 @@
1
- {"version":3,"file":"bottom-navbar.js","names":[],"sources":["../../src/shell/bottom-navbar.tsx"],"sourcesContent":["'use client'\n\n/**\n * BottomNavbar -- Mobile bottom navigation bar.\n *\n * Props-driven: accepts currentPath, user, navItems instead of\n * reading from Remix hooks or Zustand stores.\n */\nimport { IconDots, IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\nimport { useCallback,useEffect, useRef, useState } from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { springs } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport { useLink } from './link-context'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface BottomNavItem {\n title: string\n href: string\n icon: React.ReactNode\n /** When true, the route matches only when the path is exactly equal */\n exact?: boolean\n /** Notification badge count. 0 or undefined = hidden, 1–99 = shown, >99 = \"99+\" */\n badge?: number\n}\n\nexport interface BottomNavbarUser {\n name: string\n role?: string\n}\n\nexport interface BottomNavbarProps\n extends React.HTMLAttributes<HTMLElement> {\n /** Currently active pathname */\n currentPath?: string\n /** User information (used to determine admin status, presence) */\n user?: BottomNavbarUser | null\n /** Primary nav items shown directly in the bottom bar (max 4 recommended) */\n primaryItems?: BottomNavItem[]\n /** Additional items shown in the \"More\" overflow menu */\n moreItems?: BottomNavItem[]\n /** Additional className for the nav element */\n className?: string\n}\n\n// -----------------------------------------------------------------------\n// NavBadge (internal)\n// -----------------------------------------------------------------------\n\nfunction NavBadge({ count }: { count: number }) {\n if (!count || count <= 0) return null\n const display = count > 99 ? '99+' : String(count)\n const isMultiDigit = count >= 10\n return (\n <span\n aria-label={`${count} notifications`}\n className={cn(\n 'absolute -right-1 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-full bg-error-9 text-[10px] font-semibold leading-none text-error-fg animate-in zoom-in-75',\n isMultiDigit ? 'px-0.5' : '',\n )}\n >\n {display}\n </span>\n )\n}\n\n// -----------------------------------------------------------------------\n// BottomNavLink (internal)\n// -----------------------------------------------------------------------\n\nfunction BottomNavLink({\n item,\n isActive,\n onClick,\n}: {\n item: BottomNavItem\n isActive: boolean\n onClick?: () => void\n}) {\n const Link = useLink()\n const prefersReducedMotion = useReducedMotion()\n return (\n <motion.div whileTap={prefersReducedMotion ? undefined : { y: -2 }} transition={prefersReducedMotion ? { duration: 0 } : springs.snappy} className=\"flex max-w-[70px] flex-1\">\n <Link\n href={item.href}\n onClick={onClick}\n aria-label={item.title}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n 'flex h-16 w-full cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm transition-colors duration-fast-02 ease-productive-standard',\n isActive\n ? 'font-semibold text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n <div className=\"relative flex w-full flex-col items-center gap-ds-02\">\n {isActive && (\n <motion.div\n layoutId=\"bottom-nav-indicator\"\n className=\"absolute top-0 h-[3px] w-full rounded-b-ds-sm bg-accent-9 p-0\"\n aria-hidden=\"true\"\n transition={prefersReducedMotion ? { duration: 0 } : springs.snappy}\n />\n )}\n <div className=\"relative p-ds-03\">\n <span className=\"[&>svg]:h-ico-md [&>svg]:w-ico-md\" aria-hidden=\"true\">{item.icon}</span>\n {item.badge != null && <NavBadge count={item.badge} />}\n </div>\n <span className=\"text-center\">{item.title}</span>\n </div>\n </Link>\n </motion.div>\n )\n}\n\n// -----------------------------------------------------------------------\n// BottomNavbar\n// -----------------------------------------------------------------------\n\nconst BottomNavbar = React.forwardRef<HTMLElement, BottomNavbarProps>(\n (\n {\n currentPath = '/',\n user: _user,\n primaryItems = [],\n moreItems = [],\n className,\n ...props\n },\n ref,\n ) => {\n const Link = useLink()\n const prefersReducedMotion = useReducedMotion()\n const [showMore, setShowMore] = useState(false)\n const overlayRef = useRef<HTMLDivElement>(null)\n\n const closeMore = useCallback(() => setShowMore(false), [])\n\n // Focus first focusable item when overlay opens\n useEffect(() => {\n if (showMore && overlayRef.current) {\n const firstFocusable = overlayRef.current.querySelector<HTMLElement>('a, button')\n firstFocusable?.focus()\n }\n }, [showMore])\n\n const isActive = (path: string, exact = false) => {\n if (exact || path === '/') {\n return currentPath === path\n }\n return currentPath.startsWith(path)\n }\n\n // Check if any \"more\" item is active\n const isMoreActive = moreItems.some((item) =>\n isActive(item.href, item.exact),\n )\n\n return (\n <>\n {/* More Menu Overlay */}\n <AnimatePresence>\n {showMore && (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events -- backdrop overlay, dismiss via mouse only; keyboard users close via Escape\n <div\n className=\"fixed inset-0 z-overlay md:hidden\"\n onClick={() => setShowMore(false)}\n >\n <div className=\"absolute inset-0 bg-overlay\" />\n { }\n <motion.div\n ref={overlayRef}\n role=\"dialog\"\n aria-label=\"More navigation\"\n initial={{ y: '100%' }}\n animate={{ y: 0 }}\n exit={{ y: '100%' }}\n transition={prefersReducedMotion ? { duration: 0 } : springs.smooth}\n className=\"absolute bottom-[72px] left-0 right-0 rounded-t-ds-2xl border-t border-surface-border-strong bg-surface-overlay p-ds-05 pb-ds-03\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => {\n e.stopPropagation()\n if (e.key === 'Escape') closeMore()\n }}\n >\n <div className=\"mb-ds-04 flex items-center justify-between\">\n <span className=\"text-ds-md font-semibold text-surface-fg\">\n More\n </span>\n <button\n onClick={() => setShowMore(false)}\n aria-label=\"Close more menu\"\n className=\"flex h-ds-sm w-ds-sm items-center justify-center rounded-ds-full hover:bg-surface-raised-hover\"\n >\n <Icon icon={IconX} size=\"sm\" className=\"text-surface-fg-muted\" />\n </button>\n </div>\n <div className=\"grid grid-cols-4 gap-ds-03\">\n {moreItems.map((item) => (\n <Link\n key={item.href}\n href={item.href}\n onClick={() => setShowMore(false)}\n className={cn(\n 'flex flex-col items-center gap-ds-02b rounded-ds-xl p-ds-04 text-ds-sm transition-colors ease-productive-standard',\n isActive(item.href, item.exact)\n ? 'bg-surface-raised-hover text-accent-11'\n : 'text-surface-fg-subtle hover:bg-surface-raised-hover',\n )}\n >\n <span className=\"[&>svg]:h-ico-md [&>svg]:w-ico-md\">{item.icon}</span>\n <span className=\"text-center\">\n {item.title}\n </span>\n </Link>\n ))}\n </div>\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n\n {/* Bottom Navigation Bar */}\n <nav\n {...props}\n ref={ref}\n aria-label=\"Mobile navigation\"\n className={cn(\n 'fixed bottom-0 left-0 right-0 z-sticky flex w-full flex-row items-start justify-between border-t border-surface-border-strong bg-surface-raised px-ds-05 pb-safe pt-0 md:hidden',\n className,\n )}\n >\n {primaryItems.map((item) => (\n <BottomNavLink\n key={item.href}\n item={item}\n isActive={isActive(item.href, item.exact)}\n />\n ))}\n\n {/* More Button */}\n {moreItems.length > 0 && (\n <motion.button\n type=\"button\"\n onClick={() => setShowMore(!showMore)}\n aria-label=\"More navigation options\"\n aria-expanded={showMore}\n whileTap={prefersReducedMotion ? undefined : { y: -2 }}\n transition={prefersReducedMotion ? { duration: 0 } : springs.snappy}\n className={cn(\n 'flex h-16 max-w-[70px] flex-1 cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm',\n showMore || isMoreActive\n ? 'font-semibold text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n <div className=\"relative flex w-full flex-col items-center gap-ds-02\">\n {(showMore || isMoreActive) && (\n <motion.div\n layoutId=\"bottom-nav-indicator\"\n className=\"absolute top-0 h-[3px] w-full rounded-b-ds-sm bg-accent-9 p-0\"\n aria-hidden=\"true\"\n transition={prefersReducedMotion ? { duration: 0 } : springs.snappy}\n />\n )}\n <div className=\"p-ds-03\">\n <Icon icon={IconDots} />\n </div>\n <span className=\"text-center\">More</span>\n </div>\n </motion.button>\n )}\n </nav>\n </>\n )\n },\n)\nBottomNavbar.displayName = 'BottomNavbar'\n\nexport { BottomNavbar }\n"],"mappings":"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"}
1
+ {"version":3,"file":"bottom-navbar.js","names":[],"sources":["../../src/shell/bottom-navbar.tsx"],"sourcesContent":["'use client'\n\n/**\n * BottomNavbar -- Mobile bottom navigation bar.\n *\n * Props-driven: accepts currentPath, user, navItems instead of\n * reading from Remix hooks or Zustand stores.\n */\nimport { IconDots, IconX } from '@tabler/icons-react'\nimport { AnimatePresence, motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\nimport { useCallback,useEffect, useRef, useState } from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { IconProvider } from '../ui/icon-context'\nimport type { IconInput } from '../ui/lib/icon-input'\nimport { springs } from '../ui/lib/motion'\nimport { normalizeIcon } from '../ui/lib/normalize-icon'\nimport { cn } from '../ui/lib/utils'\nimport { useLink } from './link-context'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface BottomNavItem {\n title: string\n href: string\n /** Icon for this nav item. Accepts any `IconInput`. */\n icon: IconInput\n /** When true, the route matches only when the path is exactly equal */\n exact?: boolean\n /** Notification badge count. 0 or undefined = hidden, 1–99 = shown, >99 = \"99+\" */\n badge?: number\n}\n\nexport interface BottomNavbarUser {\n name: string\n role?: string\n}\n\nexport interface BottomNavbarProps\n extends React.HTMLAttributes<HTMLElement> {\n /** Currently active pathname */\n currentPath?: string\n /** User information (used to determine admin status, presence) */\n user?: BottomNavbarUser | null\n /** Primary nav items shown directly in the bottom bar (max 4 recommended) */\n primaryItems?: BottomNavItem[]\n /** Additional items shown in the \"More\" overflow menu */\n moreItems?: BottomNavItem[]\n /** Additional className for the nav element */\n className?: string\n}\n\n// -----------------------------------------------------------------------\n// NavBadge (internal)\n// -----------------------------------------------------------------------\n\nfunction NavBadge({ count }: { count: number }) {\n if (!count || count <= 0) return null\n const display = count > 99 ? '99+' : String(count)\n const isMultiDigit = count >= 10\n return (\n <span\n aria-label={`${count} notifications`}\n className={cn(\n 'absolute -right-1 -top-0.5 flex h-4 min-w-4 items-center justify-center rounded-pill bg-error-9 text-[10px] font-semibold leading-none text-error-fg animate-in zoom-in-75',\n isMultiDigit ? 'px-0.5' : '',\n )}\n >\n {display}\n </span>\n )\n}\n\n// -----------------------------------------------------------------------\n// BottomNavLink (internal)\n// -----------------------------------------------------------------------\n\nfunction BottomNavLink({\n item,\n isActive,\n onClick,\n}: {\n item: BottomNavItem\n isActive: boolean\n onClick?: () => void\n}) {\n const Link = useLink()\n const prefersReducedMotion = useReducedMotion()\n return (\n <motion.div whileTap={prefersReducedMotion ? undefined : { y: -2 }} transition={prefersReducedMotion ? { duration: 0 } : springs.snappy} className=\"flex max-w-[70px] flex-1\">\n <Link\n href={item.href}\n onClick={onClick}\n aria-label={item.title}\n aria-current={isActive ? 'page' : undefined}\n className={cn(\n 'flex h-16 w-full cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm transition-colors duration-fast-02 ease-productive-standard',\n isActive\n ? 'font-semibold text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n <div className=\"relative flex w-full flex-col items-center gap-ds-02\">\n {isActive && (\n <motion.div\n layoutId=\"bottom-nav-indicator\"\n className=\"absolute top-0 h-[3px] w-full rounded-b-control-inner bg-accent-9 p-0\"\n aria-hidden=\"true\"\n transition={prefersReducedMotion ? { duration: 0 } : springs.snappy}\n />\n )}\n <div className=\"relative p-ds-03\">\n <span className=\"[&>svg]:h-ico-md [&>svg]:w-ico-md\" aria-hidden=\"true\"><IconProvider size=\"md\">{normalizeIcon(item.icon)}</IconProvider></span>\n {item.badge != null && <NavBadge count={item.badge} />}\n </div>\n <span className=\"text-center\">{item.title}</span>\n </div>\n </Link>\n </motion.div>\n )\n}\n\n// -----------------------------------------------------------------------\n// BottomNavbar\n// -----------------------------------------------------------------------\n\nconst BottomNavbar = React.forwardRef<HTMLElement, BottomNavbarProps>(\n (\n {\n currentPath = '/',\n user: _user,\n primaryItems = [],\n moreItems = [],\n className,\n ...props\n },\n ref,\n ) => {\n const Link = useLink()\n const prefersReducedMotion = useReducedMotion()\n const [showMore, setShowMore] = useState(false)\n const overlayRef = useRef<HTMLDivElement>(null)\n\n const closeMore = useCallback(() => setShowMore(false), [])\n\n // Focus first focusable item when overlay opens\n useEffect(() => {\n if (showMore && overlayRef.current) {\n const firstFocusable = overlayRef.current.querySelector<HTMLElement>('a, button')\n firstFocusable?.focus()\n }\n }, [showMore])\n\n const isActive = (path: string, exact = false) => {\n if (exact || path === '/') {\n return currentPath === path\n }\n return currentPath.startsWith(path)\n }\n\n // Check if any \"more\" item is active\n const isMoreActive = moreItems.some((item) =>\n isActive(item.href, item.exact),\n )\n\n return (\n <>\n {/* More Menu Overlay */}\n <AnimatePresence>\n {showMore && (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events -- backdrop overlay, dismiss via mouse only; keyboard users close via Escape\n <div\n className=\"fixed inset-0 z-overlay md:hidden\"\n onClick={() => setShowMore(false)}\n >\n <div className=\"absolute inset-0 bg-overlay\" />\n { }\n <motion.div\n ref={overlayRef}\n role=\"dialog\"\n aria-label=\"More navigation\"\n initial={{ y: '100%' }}\n animate={{ y: 0 }}\n exit={{ y: '100%' }}\n transition={prefersReducedMotion ? { duration: 0 } : springs.smooth}\n className=\"absolute bottom-[72px] left-0 right-0 rounded-t-bubble border-t border-surface-border-strong bg-surface-overlay p-ds-05 pb-ds-03\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => {\n e.stopPropagation()\n if (e.key === 'Escape') closeMore()\n }}\n >\n <div className=\"mb-ds-04 flex items-center justify-between\">\n <span className=\"text-ds-md font-semibold text-surface-fg\">\n More\n </span>\n <button\n onClick={() => setShowMore(false)}\n aria-label=\"Close more menu\"\n className=\"flex h-ds-sm w-ds-sm items-center justify-center rounded-pill hover:bg-surface-raised-hover\"\n >\n <Icon icon={IconX} size=\"sm\" className=\"text-surface-fg-muted\" />\n </button>\n </div>\n <div className=\"grid grid-cols-4 gap-ds-03\">\n {moreItems.map((item) => (\n <Link\n key={item.href}\n href={item.href}\n onClick={() => setShowMore(false)}\n className={cn(\n 'flex flex-col items-center gap-ds-02b rounded-overlay-lg p-ds-04 text-ds-sm transition-colors ease-productive-standard',\n isActive(item.href, item.exact)\n ? 'bg-surface-raised-hover text-accent-11'\n : 'text-surface-fg-subtle hover:bg-surface-raised-hover',\n )}\n >\n <span className=\"[&>svg]:h-ico-md [&>svg]:w-ico-md\"><IconProvider size=\"md\">{normalizeIcon(item.icon)}</IconProvider></span>\n <span className=\"text-center\">\n {item.title}\n </span>\n </Link>\n ))}\n </div>\n </motion.div>\n </div>\n )}\n </AnimatePresence>\n\n {/* Bottom Navigation Bar */}\n <nav\n {...props}\n ref={ref}\n aria-label=\"Mobile navigation\"\n className={cn(\n 'fixed bottom-0 left-0 right-0 z-sticky flex w-full flex-row items-start justify-between border-t border-surface-border-strong bg-surface-raised px-ds-05 pb-safe pt-0 md:hidden',\n className,\n )}\n >\n {primaryItems.map((item) => (\n <BottomNavLink\n key={item.href}\n item={item}\n isActive={isActive(item.href, item.exact)}\n />\n ))}\n\n {/* More Button */}\n {moreItems.length > 0 && (\n <motion.button\n type=\"button\"\n onClick={() => setShowMore(!showMore)}\n aria-label=\"More navigation options\"\n aria-expanded={showMore}\n whileTap={prefersReducedMotion ? undefined : { y: -2 }}\n transition={prefersReducedMotion ? { duration: 0 } : springs.snappy}\n className={cn(\n 'flex h-16 max-w-[70px] flex-1 cursor-pointer flex-col items-center gap-ds-02 p-ds-02 pt-0 text-ds-sm',\n showMore || isMoreActive\n ? 'font-semibold text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n <div className=\"relative flex w-full flex-col items-center gap-ds-02\">\n {(showMore || isMoreActive) && (\n <motion.div\n layoutId=\"bottom-nav-indicator\"\n className=\"absolute top-0 h-[3px] w-full rounded-b-control-inner bg-accent-9 p-0\"\n aria-hidden=\"true\"\n transition={prefersReducedMotion ? { duration: 0 } : springs.snappy}\n />\n )}\n <div className=\"p-ds-03\">\n <Icon icon={IconDots} />\n </div>\n <span className=\"text-center\">More</span>\n </div>\n </motion.button>\n )}\n </nav>\n </>\n )\n },\n)\nBottomNavbar.displayName = 'BottomNavbar'\n\nexport { BottomNavbar }\n"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  "use client";
2
+ import { IconInput } from '../ui/lib/icon-input';
2
3
  import * as React from 'react';
3
4
  export interface CommandPageItem {
4
5
  id: string;
5
6
  label: string;
6
- icon: React.ReactNode;
7
+ icon: IconInput;
7
8
  path: string;
8
9
  keywords?: string[];
9
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"command-registry.d.ts","sourceRoot":"","sources":["../../src/shell/command-registry.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,KAAK,CAAC,SAAS,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,UAAU,EAAE,eAAe,EAAE,CAAA;CAC9B;AAYD,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,QAAQ,GACT,EAAE,4BAA4B,2CAM9B;yBATe,uBAAuB;;;AAiBvC,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAE3D"}
1
+ {"version":3,"file":"command-registry.d.ts","sourceRoot":"","sources":["../../src/shell/command-registry.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAMrD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,UAAU,EAAE,eAAe,EAAE,CAAA;CAC9B;AAYD,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,QAAQ,GACT,EAAE,4BAA4B,2CAM9B;yBATe,uBAAuB;;;AAiBvC,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAE3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"command-registry.js","names":[],"sources":["../../src/shell/command-registry.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface CommandPageItem {\n id: string\n label: string\n icon: React.ReactNode\n path: string\n keywords?: string[]\n}\n\nexport interface CommandRegistry {\n pages: CommandPageItem[]\n adminPages: CommandPageItem[]\n}\n\n// -----------------------------------------------------------------------\n// Context\n// -----------------------------------------------------------------------\n\nconst CommandRegistryContext = React.createContext<CommandRegistry | null>(null)\n\n// -----------------------------------------------------------------------\n// Provider\n// -----------------------------------------------------------------------\n\nexport interface CommandRegistryProviderProps {\n children: React.ReactNode\n registry: CommandRegistry\n}\n\nexport function CommandRegistryProvider({\n children,\n registry,\n}: CommandRegistryProviderProps) {\n return (\n <CommandRegistryContext.Provider value={registry}>\n {children}\n </CommandRegistryContext.Provider>\n )\n}\n\nCommandRegistryProvider.displayName = 'CommandRegistryProvider'\n\n// -----------------------------------------------------------------------\n// Hook\n// -----------------------------------------------------------------------\n\nexport function useCommandRegistry(): CommandRegistry | null {\n return React.useContext(CommandRegistryContext)\n}\n"],"mappings":";;;;AAyBA,IAAM,IAAyB,EAAM,cAAsC,KAAK;AAWhF,SAAgB,EAAwB,EACtC,aACA,eAC+B;AAC/B,QACE,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;EACrC;EAC+B,CAAA;;AAItC,EAAwB,cAAc;AAMtC,SAAgB,IAA6C;AAC3D,QAAO,EAAM,WAAW,EAAuB"}
1
+ {"version":3,"file":"command-registry.js","names":[],"sources":["../../src/shell/command-registry.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport type { IconInput } from '../ui/lib/icon-input'\n\n// -----------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------\n\nexport interface CommandPageItem {\n id: string\n label: string\n icon: IconInput\n path: string\n keywords?: string[]\n}\n\nexport interface CommandRegistry {\n pages: CommandPageItem[]\n adminPages: CommandPageItem[]\n}\n\n// -----------------------------------------------------------------------\n// Context\n// -----------------------------------------------------------------------\n\nconst CommandRegistryContext = React.createContext<CommandRegistry | null>(null)\n\n// -----------------------------------------------------------------------\n// Provider\n// -----------------------------------------------------------------------\n\nexport interface CommandRegistryProviderProps {\n children: React.ReactNode\n registry: CommandRegistry\n}\n\nexport function CommandRegistryProvider({\n children,\n registry,\n}: CommandRegistryProviderProps) {\n return (\n <CommandRegistryContext.Provider value={registry}>\n {children}\n </CommandRegistryContext.Provider>\n )\n}\n\nCommandRegistryProvider.displayName = 'CommandRegistryProvider'\n\n// -----------------------------------------------------------------------\n// Hook\n// -----------------------------------------------------------------------\n\nexport function useCommandRegistry(): CommandRegistry | null {\n return React.useContext(CommandRegistryContext)\n}\n"],"mappings":";;;;AA2BA,IAAM,IAAyB,EAAM,cAAsC,KAAK;AAWhF,SAAgB,EAAwB,EACtC,aACA,eAC+B;AAC/B,QACE,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;EACrC;EAC+B,CAAA;;AAItC,EAAwB,cAAc;AAMtC,SAAgB,IAA6C;AAC3D,QAAO,EAAM,WAAW,EAAuB"}
@@ -41,7 +41,7 @@ function k({ notification: e, onRead: n, onNavigate: i, getRoute: a, onDismiss:
41
41
  children: [
42
42
  /* @__PURE__ */ b("div", {
43
43
  className: "mt-ds-02b flex shrink-0",
44
- children: /* @__PURE__ */ b("span", { className: t("h-[8px] w-[8px] rounded-ds-full transition-opacity duration-fast-02 ease-productive-standard", O[e.tier] || O.INFO, e.isRead ? "opacity-20" : "opacity-100") })
44
+ children: /* @__PURE__ */ b("span", { className: t("h-[8px] w-[8px] rounded-pill transition-opacity duration-fast-02 ease-productive-standard", O[e.tier] || O.INFO, e.isRead ? "opacity-20" : "opacity-100") })
45
45
  }),
46
46
  /* @__PURE__ */ x("div", {
47
47
  className: "min-w-0 flex-1",
@@ -75,7 +75,7 @@ function k({ notification: e, onRead: n, onNavigate: i, getRoute: a, onDismiss:
75
75
  t.stopPropagation(), n.onClick(e.id);
76
76
  },
77
77
  onKeyDown: (e) => e.stopPropagation(),
78
- className: t("rounded-ds-md px-ds-03 py-ds-01 text-ds-sm font-medium transition-colors duration-fast-02 ease-productive-standard", n.variant === "primary" ? "bg-accent-9 text-accent-fg hover:bg-accent-10" : n.variant === "danger" ? "text-error-11 hover:bg-error-3" : "text-surface-fg-muted hover:bg-surface-raised-hover"),
78
+ className: t("rounded-control px-ds-03 py-ds-01 text-ds-sm font-medium transition-colors duration-fast-02 ease-productive-standard", n.variant === "primary" ? "bg-accent-9 text-accent-fg hover:bg-accent-10" : n.variant === "danger" ? "text-error-11 hover:bg-error-3" : "text-surface-fg-muted hover:bg-surface-raised-hover"),
79
79
  children: n.label
80
80
  }, n.label))
81
81
  })
@@ -88,7 +88,7 @@ function k({ notification: e, onRead: n, onNavigate: i, getRoute: a, onDismiss:
88
88
  t.stopPropagation(), o(e.id);
89
89
  },
90
90
  onKeyDown: (e) => e.stopPropagation(),
91
- className: "absolute right-ds-03 top-ds-03 hidden rounded-ds-sm p-ds-01 text-surface-fg-subtle transition-[color,background-color,opacity] duration-fast-01 ease-productive-standard hover:bg-surface-raised-hover hover:text-surface-fg-muted group-hover:flex group-focus-within:flex",
91
+ className: "absolute right-ds-03 top-ds-03 hidden rounded-control-inner p-ds-01 text-surface-fg-subtle transition-[color,background-color,opacity] duration-fast-01 ease-productive-standard hover:bg-surface-raised-hover hover:text-surface-fg-muted group-hover:flex group-focus-within:flex",
92
92
  children: /* @__PURE__ */ b(r, {
93
93
  icon: w,
94
94
  size: "sm"
@@ -121,7 +121,7 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
121
121
  className: "text-ds-md font-semibold text-surface-fg",
122
122
  children: "Notifications"
123
123
  }), q > 0 && /* @__PURE__ */ b("span", {
124
- className: "flex h-5 min-w-5 items-center justify-center rounded-ds-full bg-accent-2 px-ds-02b text-ds-sm font-semibold text-accent-11",
124
+ className: "flex h-5 min-w-5 items-center justify-center rounded-pill bg-accent-2 px-ds-02b text-ds-sm font-semibold text-accent-11",
125
125
  children: q
126
126
  })]
127
127
  }), /* @__PURE__ */ x("div", {
@@ -145,7 +145,7 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
145
145
  className: "flex flex-col items-center justify-center px-ds-05 py-ds-09",
146
146
  children: [
147
147
  /* @__PURE__ */ b("div", {
148
- className: "flex h-ds-lg w-ds-lg items-center justify-center rounded-ds-full bg-surface-raised",
148
+ className: "flex h-ds-lg w-ds-lg items-center justify-center rounded-pill bg-surface-raised",
149
149
  children: /* @__PURE__ */ b(r, {
150
150
  icon: C,
151
151
  size: "lg",
@@ -198,7 +198,7 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
198
198
  ...H,
199
199
  ref: U,
200
200
  "aria-label": q > 0 ? `Notifications, ${q} unread` : "Notifications",
201
- className: t("relative flex h-ds-sm-plus w-ds-sm-plus items-center justify-center rounded-ds-full border border-surface-border-strong bg-surface-raised text-surface-fg-muted transition-colors ease-productive-standard hover:bg-surface-raised-hover", V),
201
+ className: t("relative flex h-ds-sm-plus w-ds-sm-plus items-center justify-center rounded-pill border border-surface-border-strong bg-surface-raised text-surface-fg-muted transition-colors ease-productive-standard hover:bg-surface-raised-hover", V),
202
202
  children: [/* @__PURE__ */ b(r, {
203
203
  icon: S,
204
204
  size: "sm"
@@ -216,7 +216,7 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
216
216
  duration: e.slow01,
217
217
  ease: "easeInOut"
218
218
  },
219
- className: "absolute -right-ds-01 -top-ds-01 flex h-4 min-w-4 items-center justify-center rounded-ds-full bg-accent-9 px-ds-02 text-ds-xs font-semibold text-accent-fg",
219
+ className: "absolute -right-ds-01 -top-ds-01 flex h-4 min-w-4 items-center justify-center rounded-pill bg-accent-9 px-ds-02 text-ds-xs font-semibold text-accent-fg",
220
220
  children: q > 99 ? "99+" : q
221
221
  })]
222
222
  });
@@ -256,7 +256,7 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
256
256
  align: "center",
257
257
  children: "Notifications"
258
258
  })] }), /* @__PURE__ */ b(o, {
259
- className: t("w-[380px] rounded-ds-xl border border-surface-border-strong bg-surface-overlay p-0 shadow-floating", B),
259
+ className: t("w-[380px] rounded-overlay-lg border border-surface-border-strong bg-surface-overlay p-0 shadow-floating", B),
260
260
  sideOffset: 8,
261
261
  align: "end",
262
262
  children: Q