@evinvest/uikit 0.32.0 → 0.33.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 (288) hide show
  1. package/README.md +21 -10
  2. package/dist/components/atomic/accordion.js +2 -1
  3. package/dist/components/atomic/accordion.js.map +1 -1
  4. package/dist/components/atomic/alert.js +4 -4
  5. package/dist/components/atomic/alert.js.map +1 -1
  6. package/dist/components/atomic/badge.js +4 -3
  7. package/dist/components/atomic/badge.js.map +1 -1
  8. package/dist/components/atomic/breadcrumb.js +7 -9
  9. package/dist/components/atomic/breadcrumb.js.map +1 -1
  10. package/dist/components/atomic/button.js +4 -3
  11. package/dist/components/atomic/button.js.map +1 -1
  12. package/dist/components/atomic/calendar.js +10 -25
  13. package/dist/components/atomic/calendar.js.map +1 -1
  14. package/dist/components/atomic/checkbox.js +2 -1
  15. package/dist/components/atomic/checkbox.js.map +1 -1
  16. package/dist/components/atomic/command.js +5 -20
  17. package/dist/components/atomic/command.js.map +1 -1
  18. package/dist/components/atomic/context-menu.js +6 -13
  19. package/dist/components/atomic/context-menu.js.map +1 -1
  20. package/dist/components/atomic/dialog.js +4 -2
  21. package/dist/components/atomic/dialog.js.map +1 -1
  22. package/dist/components/atomic/dropdown-menu.js +6 -14
  23. package/dist/components/atomic/dropdown-menu.js.map +1 -1
  24. package/dist/components/atomic/hover-card.js.map +1 -1
  25. package/dist/components/atomic/info-tip.js +8 -9
  26. package/dist/components/atomic/info-tip.js.map +1 -1
  27. package/dist/components/atomic/input-otp.js +2 -1
  28. package/dist/components/atomic/input-otp.js.map +1 -1
  29. package/dist/components/atomic/key-value.js +5 -4
  30. package/dist/components/atomic/key-value.js.map +1 -1
  31. package/dist/components/atomic/menubar.js +7 -18
  32. package/dist/components/atomic/menubar.js.map +1 -1
  33. package/dist/components/atomic/native-select.js +1 -1
  34. package/dist/components/atomic/native-select.js.map +1 -1
  35. package/dist/components/atomic/navigation-menu.js +6 -14
  36. package/dist/components/atomic/navigation-menu.js.map +1 -1
  37. package/dist/components/atomic/pagination.js +18 -19
  38. package/dist/components/atomic/pagination.js.map +1 -1
  39. package/dist/components/atomic/popover.js.map +1 -1
  40. package/dist/components/atomic/radio-group.js +5 -4
  41. package/dist/components/atomic/radio-group.js.map +1 -1
  42. package/dist/components/atomic/section-descriptor.js +5 -8
  43. package/dist/components/atomic/section-descriptor.js.map +1 -1
  44. package/dist/components/atomic/select-listbox.js +2 -2
  45. package/dist/components/atomic/select-listbox.js.map +1 -1
  46. package/dist/components/atomic/select.js +5 -13
  47. package/dist/components/atomic/select.js.map +1 -1
  48. package/dist/components/atomic/sheet.js +4 -2
  49. package/dist/components/atomic/sheet.js.map +1 -1
  50. package/dist/components/atomic/sidebar.js +7 -26
  51. package/dist/components/atomic/sidebar.js.map +1 -1
  52. package/dist/components/atomic/slider.js.map +1 -1
  53. package/dist/components/atomic/sonner.js +20 -18
  54. package/dist/components/atomic/sonner.js.map +1 -1
  55. package/dist/components/atomic/spinner.js +6 -3
  56. package/dist/components/atomic/spinner.js.map +1 -1
  57. package/dist/components/atomic/tabs.js.map +1 -1
  58. package/dist/components/atomic/toggle-group.js +2 -2
  59. package/dist/components/atomic/toggle-group.js.map +1 -1
  60. package/dist/components/atomic/toggle.js +3 -6
  61. package/dist/components/atomic/toggle.js.map +1 -1
  62. package/dist/components/atomic/tooltip.js.map +1 -1
  63. package/dist/components/derived/carousel.js +6 -6
  64. package/dist/components/derived/carousel.js.map +1 -1
  65. package/dist/components/derived/command-palette.js +8 -7
  66. package/dist/components/derived/command-palette.js.map +1 -1
  67. package/dist/components/derived/confirm-dialog.js +8 -13
  68. package/dist/components/derived/confirm-dialog.js.map +1 -1
  69. package/dist/components/derived/date-time-picker.js +10 -14
  70. package/dist/components/derived/date-time-picker.js.map +1 -1
  71. package/dist/components/derived/field.js.map +1 -1
  72. package/dist/components/derived/form.js +0 -1
  73. package/dist/components/derived/form.js.map +1 -1
  74. package/dist/components/derived/icon-button.js.map +1 -1
  75. package/dist/components/derived/item.js +0 -1
  76. package/dist/components/derived/item.js.map +1 -1
  77. package/dist/components/derived/popover-form.js +4 -3
  78. package/dist/components/derived/popover-form.js.map +1 -1
  79. package/dist/components/derived/settings.js +4 -4
  80. package/dist/components/derived/settings.js.map +1 -1
  81. package/dist/components/shell/account-menu-profile.js +1 -1
  82. package/dist/components/shell/account-menu-profile.js.map +1 -1
  83. package/dist/components/shell/account-settings-dialog.js +3 -2
  84. package/dist/components/shell/account-settings-dialog.js.map +1 -1
  85. package/dist/components/shell/aside.js +6 -4
  86. package/dist/components/shell/aside.js.map +1 -1
  87. package/dist/components/shell/auth-wall.js +3 -5
  88. package/dist/components/shell/auth-wall.js.map +1 -1
  89. package/dist/components/shell/bottom-tab-bar.js +3 -3
  90. package/dist/components/shell/bottom-tab-bar.js.map +1 -1
  91. package/dist/components/shell/chat-composer.js +9 -7
  92. package/dist/components/shell/chat-composer.js.map +1 -1
  93. package/dist/components/shell/chat-message.js.map +1 -1
  94. package/dist/components/shell/chat-typing.js +2 -1
  95. package/dist/components/shell/chat-typing.js.map +1 -1
  96. package/dist/components/shell/footer.js +26 -24
  97. package/dist/components/shell/footer.js.map +1 -1
  98. package/dist/components/shell/header-controls.js +17 -15
  99. package/dist/components/shell/header-controls.js.map +1 -1
  100. package/dist/components/shell/hotbar.js +158 -0
  101. package/dist/components/shell/hotbar.js.map +1 -0
  102. package/dist/components/shell/logo.js +3 -2
  103. package/dist/components/shell/logo.js.map +1 -1
  104. package/dist/components/shell/mobile-app-bar.js +4 -4
  105. package/dist/components/shell/mobile-app-bar.js.map +1 -1
  106. package/dist/components/shell/nav-collapse.js +6 -5
  107. package/dist/components/shell/nav-collapse.js.map +1 -1
  108. package/dist/components/shell/resource-error.js +2 -2
  109. package/dist/components/shell/resource-error.js.map +1 -1
  110. package/dist/components/shell/section-nav.js +3 -3
  111. package/dist/components/shell/section-nav.js.map +1 -1
  112. package/dist/components/shell/shell-icons.js +7 -22
  113. package/dist/components/shell/shell-icons.js.map +1 -1
  114. package/dist/components/shell/shell-nav.js +4 -4
  115. package/dist/components/shell/shell-nav.js.map +1 -1
  116. package/dist/components/shell/smart-header.js.map +1 -1
  117. package/dist/components/shell/status-screen.js +35 -35
  118. package/dist/components/shell/status-screen.js.map +1 -1
  119. package/dist/components/shell/system-banner.js +6 -6
  120. package/dist/components/shell/system-banner.js.map +1 -1
  121. package/dist/components/shell/workspace-tab.js +2 -1
  122. package/dist/components/shell/workspace-tab.js.map +1 -1
  123. package/dist/generated/accent.js +10 -10
  124. package/dist/generated/accent.js.map +1 -1
  125. package/dist/generated/accordion.js +6 -2
  126. package/dist/generated/accordion.js.map +1 -1
  127. package/dist/generated/account-list.js +4 -4
  128. package/dist/generated/account-list.js.map +1 -1
  129. package/dist/generated/account-settings-dialog.js +6 -2
  130. package/dist/generated/account-settings-dialog.js.map +1 -1
  131. package/dist/generated/alert-dialog.js +1 -1
  132. package/dist/generated/alert-dialog.js.map +1 -1
  133. package/dist/generated/alert.js +3 -10
  134. package/dist/generated/alert.js.map +1 -1
  135. package/dist/generated/aside.js +7 -0
  136. package/dist/generated/aside.js.map +1 -1
  137. package/dist/generated/badge.js +2 -11
  138. package/dist/generated/badge.js.map +1 -1
  139. package/dist/generated/band.js +10 -10
  140. package/dist/generated/band.js.map +1 -1
  141. package/dist/generated/breadcrumb.js +1 -1
  142. package/dist/generated/breadcrumb.js.map +1 -1
  143. package/dist/generated/button-group.js +3 -3
  144. package/dist/generated/button-group.js.map +1 -1
  145. package/dist/generated/button.js +4 -20
  146. package/dist/generated/button.js.map +1 -1
  147. package/dist/generated/calendar.js +5 -5
  148. package/dist/generated/calendar.js.map +1 -1
  149. package/dist/generated/card.js +1 -1
  150. package/dist/generated/card.js.map +1 -1
  151. package/dist/generated/carousel.js +1 -1
  152. package/dist/generated/carousel.js.map +1 -1
  153. package/dist/generated/chat-composer.js +9 -2
  154. package/dist/generated/chat-composer.js.map +1 -1
  155. package/dist/generated/chat-message.js +25 -19
  156. package/dist/generated/chat-message.js.map +1 -1
  157. package/dist/generated/checkbox.js +1 -1
  158. package/dist/generated/checkbox.js.map +1 -1
  159. package/dist/generated/command-palette.js +10 -1
  160. package/dist/generated/command-palette.js.map +1 -1
  161. package/dist/generated/command.js +8 -6
  162. package/dist/generated/command.js.map +1 -1
  163. package/dist/generated/confirm-dialog.js +8 -8
  164. package/dist/generated/confirm-dialog.js.map +1 -1
  165. package/dist/generated/context-menu.js +7 -5
  166. package/dist/generated/context-menu.js.map +1 -1
  167. package/dist/generated/copy.js +97 -0
  168. package/dist/generated/copy.js.map +1 -0
  169. package/dist/generated/date-time-picker.js +10 -0
  170. package/dist/generated/date-time-picker.js.map +1 -1
  171. package/dist/generated/dialog.js +2 -2
  172. package/dist/generated/dialog.js.map +1 -1
  173. package/dist/generated/drawer.js +6 -6
  174. package/dist/generated/drawer.js.map +1 -1
  175. package/dist/generated/dropdown-menu.js +7 -5
  176. package/dist/generated/dropdown-menu.js.map +1 -1
  177. package/dist/generated/empty.js +5 -5
  178. package/dist/generated/empty.js.map +1 -1
  179. package/dist/generated/field.js +9 -7
  180. package/dist/generated/field.js.map +1 -1
  181. package/dist/generated/footer.js +45 -0
  182. package/dist/generated/footer.js.map +1 -0
  183. package/dist/generated/header-controls.js +5 -10
  184. package/dist/generated/header-controls.js.map +1 -1
  185. package/dist/generated/hover-card.js +1 -1
  186. package/dist/generated/hover-card.js.map +1 -1
  187. package/dist/generated/icon-button.js +4 -2
  188. package/dist/generated/icon-button.js.map +1 -1
  189. package/dist/generated/icons.js +36 -0
  190. package/dist/generated/icons.js.map +1 -0
  191. package/dist/generated/info-tip.js +5 -0
  192. package/dist/generated/info-tip.js.map +1 -0
  193. package/dist/generated/input-group.js +10 -10
  194. package/dist/generated/input-group.js.map +1 -1
  195. package/dist/generated/input-otp.js +2 -2
  196. package/dist/generated/input-otp.js.map +1 -1
  197. package/dist/generated/input.js +4 -4
  198. package/dist/generated/input.js.map +1 -1
  199. package/dist/generated/item.js +11 -11
  200. package/dist/generated/item.js.map +1 -1
  201. package/dist/generated/kbd.js +1 -1
  202. package/dist/generated/kbd.js.map +1 -1
  203. package/dist/generated/key-value.js +7 -1
  204. package/dist/generated/key-value.js.map +1 -1
  205. package/dist/generated/label.js +1 -1
  206. package/dist/generated/label.js.map +1 -1
  207. package/dist/generated/logo.js +9 -0
  208. package/dist/generated/logo.js.map +1 -0
  209. package/dist/generated/menu.js +7 -0
  210. package/dist/generated/menu.js.map +1 -0
  211. package/dist/generated/menubar.js +10 -8
  212. package/dist/generated/menubar.js.map +1 -1
  213. package/dist/generated/navigation-menu.js +11 -5
  214. package/dist/generated/navigation-menu.js.map +1 -1
  215. package/dist/generated/orientation.js +5 -0
  216. package/dist/generated/orientation.js.map +1 -0
  217. package/dist/generated/pagination.js +15 -0
  218. package/dist/generated/pagination.js.map +1 -0
  219. package/dist/generated/popover-form.js +5 -0
  220. package/dist/generated/popover-form.js.map +1 -1
  221. package/dist/generated/popover.js +1 -1
  222. package/dist/generated/popover.js.map +1 -1
  223. package/dist/generated/primitives.js +7 -0
  224. package/dist/generated/primitives.js.map +1 -0
  225. package/dist/generated/radio-group.js +5 -1
  226. package/dist/generated/radio-group.js.map +1 -1
  227. package/dist/generated/section-descriptor.js +7 -0
  228. package/dist/generated/section-descriptor.js.map +1 -0
  229. package/dist/generated/select.js +16 -10
  230. package/dist/generated/select.js.map +1 -1
  231. package/dist/generated/settings.js +2 -7
  232. package/dist/generated/settings.js.map +1 -1
  233. package/dist/generated/sheet.js +1 -1
  234. package/dist/generated/sheet.js.map +1 -1
  235. package/dist/generated/sidebar.js +24 -12
  236. package/dist/generated/sidebar.js.map +1 -1
  237. package/dist/generated/size.js +12 -0
  238. package/dist/generated/size.js.map +1 -0
  239. package/dist/generated/skeleton.js +1 -1
  240. package/dist/generated/skeleton.js.map +1 -1
  241. package/dist/generated/slider.js +2 -2
  242. package/dist/generated/slider.js.map +1 -1
  243. package/dist/generated/slot.js +5 -0
  244. package/dist/generated/slot.js.map +1 -0
  245. package/dist/generated/smart-header.js +2 -0
  246. package/dist/generated/smart-header.js.map +1 -1
  247. package/dist/generated/sonner.js +11 -13
  248. package/dist/generated/sonner.js.map +1 -1
  249. package/dist/generated/spinner.js +5 -0
  250. package/dist/generated/spinner.js.map +1 -0
  251. package/dist/generated/status-screen.js +27 -0
  252. package/dist/generated/status-screen.js.map +1 -0
  253. package/dist/generated/switch.js +2 -2
  254. package/dist/generated/switch.js.map +1 -1
  255. package/dist/generated/system-banner.js +6 -16
  256. package/dist/generated/system-banner.js.map +1 -1
  257. package/dist/generated/table.js +9 -9
  258. package/dist/generated/table.js.map +1 -1
  259. package/dist/generated/tabs.js +2 -2
  260. package/dist/generated/tabs.js.map +1 -1
  261. package/dist/generated/textarea.js +1 -1
  262. package/dist/generated/textarea.js.map +1 -1
  263. package/dist/generated/toggle-group.js +9 -0
  264. package/dist/generated/toggle-group.js.map +1 -0
  265. package/dist/generated/toggle.js +1 -9
  266. package/dist/generated/toggle.js.map +1 -1
  267. package/dist/generated/tone.js +12 -0
  268. package/dist/generated/tone.js.map +1 -0
  269. package/dist/generated/tooltip.js +1 -1
  270. package/dist/generated/tooltip.js.map +1 -1
  271. package/dist/generated/workspace-tab.js +3 -3
  272. package/dist/generated/workspace-tab.js.map +1 -1
  273. package/dist/icons/icon-shapes.js +10 -0
  274. package/dist/icons/icon-shapes.js.map +1 -0
  275. package/dist/icons/select-chevron.js +2 -1
  276. package/dist/icons/select-chevron.js.map +1 -1
  277. package/dist/icons/ui-icons.js +3 -57
  278. package/dist/icons/ui-icons.js.map +1 -1
  279. package/dist/index.d.ts +203 -155
  280. package/dist/index.js +1 -0
  281. package/dist/lib/cn.js +1 -0
  282. package/dist/lib/cn.js.map +1 -1
  283. package/dist/primitives/use-floating.js.map +1 -1
  284. package/dist/primitives/use-roving-focus.js.map +1 -1
  285. package/package.json +1 -1
  286. package/styles/ev.css +13 -76
  287. package/styles/theme.css +133 -66
  288. package/styles/tokens.css +157 -149
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/shell/hotbar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { toneClasses } from \"../../generated/tone\";\nimport { iconShapes } from \"../../icons/icon-shapes\";\nimport { Button } from \"../atomic/button\";\nimport { Checkbox } from \"../atomic/checkbox\";\nimport { Kbd } from \"../atomic/kbd\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../atomic/popover\";\nimport { useAppleKeys } from \"./header-controls\";\nimport { TriangleAlertIcon } from \"./shell-icons\";\n\ntype Letter = \"A\" | \"B\" | \"C\" | \"D\" | \"E\" | \"F\" | \"G\" | \"H\" | \"I\" | \"J\" | \"K\" | \"L\" | \"M\" | \"N\" | \"O\" | \"P\" | \"Q\" | \"R\" | \"S\" | \"T\" | \"U\" | \"V\" | \"W\" | \"X\" | \"Y\" | \"Z\";\ntype Digit = \"0\" | \"1\" | \"2\" | \"3\" | \"4\" | \"5\" | \"6\" | \"7\" | \"8\" | \"9\";\ntype HotbarKey = Letter | Digit;\n\nexport interface HotbarAction {\n id: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n /** `\"N\"` alone, or `\"mod+K\"` — ⌘ on Apple, Ctrl elsewhere. Shown in a `Kbd` and bound while the bar is mounted, pinned or not. */\n shortcut?: HotbarKey | `mod+${HotbarKey}`;\n onSelect: () => void;\n /** A toggle that is on — the agent connected, a map layer shown. The app owns the state. */\n pressed?: boolean;\n /** `false` lists it only under Customize until the user pins it. Default pinned. */\n pinned?: boolean;\n}\n\nexport interface HotbarLabels {\n /** Accessible name of the strip. Default `\"Hotbar\"`. */\n region?: string;\n /** The customize button. Default `\"Customize\"`. */\n customize?: string;\n /** Default `\"Customize hotbar\"`. */\n title?: string;\n /** Default `\"Pinned actions show in the bar and in the mobile chip row.\"`. */\n description?: string;\n /** Default `\"Reset to default\"`. */\n reset?: string;\n /** Default `\"Done\"`. */\n done?: string;\n}\n\nexport interface HotbarProps extends Omit<React.ComponentProps<\"section\">, \"children\"> {\n /** Every action the app offers, in bar order. */\n actions: readonly HotbarAction[];\n /** `StatusItem`s at the end of the wide strip. */\n status?: React.ReactNode;\n /** An urgent level: replaces the actions with its message until the app clears it. */\n takeover?: { tone: \"warn\" | \"error\"; message: React.ReactNode; action?: React.ReactNode };\n labels?: HotbarLabels;\n}\n\ninterface Shortcut {\n mod: boolean;\n code: string;\n key: HotbarKey;\n}\n\nfunction parseShortcut(shortcut: NonNullable<HotbarAction[\"shortcut\"]>): Shortcut {\n const mod = shortcut.startsWith(\"mod+\");\n const key = (mod ? shortcut.slice(4) : shortcut) as HotbarKey;\n return { mod, key, code: /\\d/.test(key) ? `Digit${key}` : `Key${key}` };\n}\n\nconst STORAGE_KEY = \"ev-hotbar\";\n/** The user's pins that differ from an action's default, by action id. */\ntype Overrides = Readonly<Record<string, boolean>>;\nconst NO_OVERRIDES: Overrides = {};\nlet snapshot: { raw: string | null; value: Overrides } = { raw: null, value: NO_OVERRIDES };\nconst listeners = new Set<() => void>();\n\nfunction readOverrides(): Overrides {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (raw === snapshot.raw) return snapshot.value;\n const value: unknown = raw === null ? NO_OVERRIDES : JSON.parse(raw);\n if (typeof value !== \"object\" || value === null || Array.isArray(value) || Object.values(value).some(v => typeof v !== \"boolean\"))\n throw new Error(`localStorage[\"${STORAGE_KEY}\"] is not an {actionId: boolean} map: ${raw}`);\n snapshot = { raw, value: value as Overrides };\n return snapshot.value;\n}\n\nfunction writeOverrides(next: Overrides) {\n if (Object.keys(next).length === 0) localStorage.removeItem(STORAGE_KEY);\n else localStorage.setItem(STORAGE_KEY, JSON.stringify(next));\n listeners.forEach(l => l());\n}\n\nfunction subscribeOverrides(listener: () => void) {\n const onStorage = (e: StorageEvent) => {\n if (e.key === STORAGE_KEY || e.key === null) listener();\n };\n listeners.add(listener);\n window.addEventListener(\"storage\", onStorage);\n return () => {\n listeners.delete(listener);\n window.removeEventListener(\"storage\", onStorage);\n };\n}\n\nconst EDITABLE = \"input, textarea, select, [contenteditable]:not([contenteditable='false'])\";\n\n/**\n * The bottom strip of a terminal-style app: pinned actions with bound shortcuts,\n * a customize popover, live status at the end. Position-agnostic — the app docks\n * it. Below a 64rem strip it turns into a sideways-scrolling chip row with no\n * status. Which actions are pinned is the user's, kept in this browser.\n */\nexport function Hotbar({ actions, status, takeover, labels = {}, className, ...props }: HotbarProps) {\n const parsed = React.useMemo(() => {\n const ids = new Set<string>();\n const codes = new Set<string>();\n return actions.map(action => {\n if (ids.has(action.id)) throw new Error(`Hotbar: two actions have the id \"${action.id}\"`);\n ids.add(action.id);\n if (action.shortcut === undefined) return { action, shortcut: undefined };\n const shortcut = parseShortcut(action.shortcut);\n const chord = `${shortcut.mod}:${shortcut.code}`;\n if (codes.has(chord)) throw new Error(`Hotbar: two actions are bound to ${action.shortcut}`);\n codes.add(chord);\n return { action, shortcut };\n });\n }, [actions]);\n\n const latest = React.useRef(parsed);\n React.useEffect(() => {\n latest.current = parsed;\n });\n React.useEffect(() => {\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.defaultPrevented || e.altKey || e.shiftKey) return;\n const mod = e.metaKey || e.ctrlKey;\n const hit = latest.current.find(({ shortcut }) => shortcut?.code === e.code && shortcut.mod === mod);\n if (hit === undefined) return;\n if (!mod && e.target instanceof Element && e.target.closest(EDITABLE)) return;\n e.preventDefault();\n // A held key would fire the action once per repeat.\n if (!e.repeat) hit.action.onSelect();\n };\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, []);\n\n const overrides = React.useSyncExternalStore(subscribeOverrides, readOverrides, () => NO_OVERRIDES);\n const isPinned = (action: HotbarAction) => overrides[action.id] ?? action.pinned ?? true;\n const setPinned = (action: HotbarAction, pinned: boolean) => {\n const next: Record<string, boolean> = { ...overrides };\n delete next[action.id];\n if (pinned !== (action.pinned ?? true)) next[action.id] = pinned;\n writeOverrides(next);\n };\n\n const apple = useAppleKeys();\n const kbd = (s: Shortcut) => (s.mod ? (apple ? `⌘${s.key}` : `Ctrl ${s.key}`) : s.key);\n const [customizing, setCustomizing] = React.useState(false);\n\n return (\n <section\n data-slot=\"hotbar\"\n aria-label={labels.region ?? \"Hotbar\"}\n className={cn(\n \"@container border-t border-border-subtle bg-background\",\n takeover && [toneClasses[takeover.tone], \"border-(color:--tone-edge) bg-[image:linear-gradient(var(--tone-wash),var(--tone-wash))]\"],\n className,\n )}\n {...props}\n >\n <div className=\"flex h-(--control-h-touch) items-center gap-2 overflow-x-auto px-3 @5xl:h-(--control-h) @5xl:gap-1.5 @5xl:overflow-visible\">\n {takeover ? (\n <div data-slot=\"hotbar-takeover\" role=\"alert\" className=\"flex min-w-0 items-center gap-2 text-body-s font-medium text-(color:--tone-ink)\">\n <TriangleAlertIcon className=\"size-4 shrink-0\" />\n <span className=\"truncate\">{takeover.message}</span>\n {takeover.action}\n </div>\n ) : (\n <>\n {parsed.filter(({ action }) => isPinned(action)).map(({ action, shortcut }) => (\n <button\n key={action.id}\n type=\"button\"\n data-slot=\"hotbar-action\"\n aria-pressed={action.pressed}\n onClick={action.onSelect}\n className={cn(ITEM, action.pressed && PRESSED)}\n >\n <action.icon className=\"size-4 shrink-0\" />\n {action.label}\n {shortcut && <Kbd className=\"hidden @5xl:inline-flex\">{kbd(shortcut)}</Kbd>}\n </button>\n ))}\n <span aria-hidden className=\"hidden h-4 w-px shrink-0 bg-border @5xl:block\" />\n <Popover open={customizing} onOpenChange={setCustomizing}>\n <PopoverTrigger type=\"button\" data-slot=\"hotbar-customize\" className={cn(ITEM, \"@5xl:w-(--control-h-2xs) @5xl:justify-center @5xl:px-0\")}>\n <svg className=\"size-4 shrink-0\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {iconShapes(\"sliders-horizontal\")}\n </svg>\n <span className=\"@5xl:sr-only\">{labels.customize ?? \"Customize\"}</span>\n </PopoverTrigger>\n <PopoverContent side=\"top\" align=\"start\" className=\"w-80 p-0\">\n <div className=\"flex flex-col gap-0.5 px-3 pt-3 pb-2\">\n <p className=\"text-body-l font-medium\">{labels.title ?? \"Customize hotbar\"}</p>\n <p className=\"text-caption text-ink-soft\">\n {labels.description ?? \"Pinned actions show in the bar and in the mobile chip row.\"}\n </p>\n </div>\n <ul className=\"flex flex-col px-1 pb-1\">\n {parsed.map(({ action, shortcut }) => (\n <li key={action.id}>\n <label className=\"flex h-(--control-h-sm) cursor-pointer items-center gap-2.5 rounded-control px-2 text-body text-ink-mid hover:bg-hover\">\n <Checkbox checked={isPinned(action)} onCheckedChange={pinned => setPinned(action, pinned)} />\n <action.icon className=\"size-4 shrink-0\" />\n <span className=\"flex-1 truncate\">{action.label}</span>\n {shortcut && <Kbd>{kbd(shortcut)}</Kbd>}\n </label>\n </li>\n ))}\n </ul>\n <div className=\"flex items-center justify-between border-t border-border px-3 py-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => writeOverrides(NO_OVERRIDES)}>\n {labels.reset ?? \"Reset to default\"}\n </Button>\n <Button size=\"sm\" onClick={() => setCustomizing(false)}>\n {labels.done ?? \"Done\"}\n </Button>\n </div>\n </PopoverContent>\n </Popover>\n </>\n )}\n {status != null && (\n <div data-slot=\"hotbar-status\" className=\"ml-auto hidden shrink-0 items-center @5xl:flex\">\n {status}\n </div>\n )}\n </div>\n </section>\n );\n}\n\n// A chip below the container breakpoint, a ghost row item from it up.\nconst ITEM =\n \"inline-flex h-(--control-h-xs) shrink-0 items-center gap-1.5 whitespace-nowrap rounded-chip border border-border bg-secondary pr-3 pl-2 text-body-s text-ink-mid transition-colors outline-none hover:text-ink focus-visible:ring-2 focus-visible:ring-ring \" +\n \"@5xl:h-(--control-h-2xs) @5xl:rounded-control @5xl:border-transparent @5xl:bg-transparent @5xl:px-2 @5xl:hover:bg-hover\";\nconst PRESSED = `${toneClasses.primary} border-(color:--tone-edge) bg-(color:--tone-fill) text-(color:--tone-ink) hover:text-(color:--tone-ink) @5xl:border-(color:--tone-edge) @5xl:bg-(color:--tone-fill)`;\n"],"mappings":";AA2KU,SAMA,UALE,KADF;AAzKV,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,WAAW;AACpB,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAkDlC,SAAS,cAAc,UAA2D;AAChF,QAAM,MAAM,SAAS,WAAW,MAAM;AACtC,QAAM,MAAO,MAAM,SAAS,MAAM,CAAC,IAAI;AACvC,SAAO,EAAE,KAAK,KAAK,MAAM,KAAK,KAAK,GAAG,IAAI,QAAQ,GAAG,KAAK,MAAM,GAAG,GAAG;AACxE;AAEA,MAAM,cAAc;AAGpB,MAAM,eAA0B,CAAC;AACjC,IAAI,WAAqD,EAAE,KAAK,MAAM,OAAO,aAAa;AAC1F,MAAM,YAAY,oBAAI,IAAgB;AAEtC,SAAS,gBAA2B;AAClC,QAAM,MAAM,aAAa,QAAQ,WAAW;AAC5C,MAAI,QAAQ,SAAS,IAAK,QAAO,SAAS;AAC1C,QAAM,QAAiB,QAAQ,OAAO,eAAe,KAAK,MAAM,GAAG;AACnE,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,KAAK,OAAO,OAAO,KAAK,EAAE,KAAK,OAAK,OAAO,MAAM,SAAS;AAC9H,UAAM,IAAI,MAAM,iBAAiB,WAAW,yCAAyC,GAAG,EAAE;AAC5F,aAAW,EAAE,KAAK,MAA0B;AAC5C,SAAO,SAAS;AAClB;AAEA,SAAS,eAAe,MAAiB;AACvC,MAAI,OAAO,KAAK,IAAI,EAAE,WAAW,EAAG,cAAa,WAAW,WAAW;AAAA,MAClE,cAAa,QAAQ,aAAa,KAAK,UAAU,IAAI,CAAC;AAC3D,YAAU,QAAQ,OAAK,EAAE,CAAC;AAC5B;AAEA,SAAS,mBAAmB,UAAsB;AAChD,QAAM,YAAY,CAAC,MAAoB;AACrC,QAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,KAAM,UAAS;AAAA,EACxD;AACA,YAAU,IAAI,QAAQ;AACtB,SAAO,iBAAiB,WAAW,SAAS;AAC5C,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AACzB,WAAO,oBAAoB,WAAW,SAAS;AAAA,EACjD;AACF;AAEA,MAAM,WAAW;AAQV,SAAS,OAAO,EAAE,SAAS,QAAQ,UAAU,SAAS,CAAC,GAAG,WAAW,GAAG,MAAM,GAAgB;AACnG,QAAM,SAAS,MAAM,QAAQ,MAAM;AACjC,UAAM,MAAM,oBAAI,IAAY;AAC5B,UAAM,QAAQ,oBAAI,IAAY;AAC9B,WAAO,QAAQ,IAAI,YAAU;AAC3B,UAAI,IAAI,IAAI,OAAO,EAAE,EAAG,OAAM,IAAI,MAAM,oCAAoC,OAAO,EAAE,GAAG;AACxF,UAAI,IAAI,OAAO,EAAE;AACjB,UAAI,OAAO,aAAa,OAAW,QAAO,EAAE,QAAQ,UAAU,OAAU;AACxE,YAAM,WAAW,cAAc,OAAO,QAAQ;AAC9C,YAAM,QAAQ,GAAG,SAAS,GAAG,IAAI,SAAS,IAAI;AAC9C,UAAI,MAAM,IAAI,KAAK,EAAG,OAAM,IAAI,MAAM,oCAAoC,OAAO,QAAQ,EAAE;AAC3F,YAAM,IAAI,KAAK;AACf,aAAO,EAAE,QAAQ,SAAS;AAAA,IAC5B,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,SAAS,MAAM,OAAO,MAAM;AAClC,QAAM,UAAU,MAAM;AACpB,WAAO,UAAU;AAAA,EACnB,CAAC;AACD,QAAM,UAAU,MAAM;AACpB,UAAM,YAAY,CAAC,MAAqB;AACtC,UAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,SAAU;AAClD,YAAM,MAAM,EAAE,WAAW,EAAE;AAC3B,YAAM,MAAM,OAAO,QAAQ,KAAK,CAAC,EAAE,SAAS,MAAM,UAAU,SAAS,EAAE,QAAQ,SAAS,QAAQ,GAAG;AACnG,UAAI,QAAQ,OAAW;AACvB,UAAI,CAAC,OAAO,EAAE,kBAAkB,WAAW,EAAE,OAAO,QAAQ,QAAQ,EAAG;AACvE,QAAE,eAAe;AAEjB,UAAI,CAAC,EAAE,OAAQ,KAAI,OAAO,SAAS;AAAA,IACrC;AACA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM,SAAS,oBAAoB,WAAW,SAAS;AAAA,EAChE,GAAG,CAAC,CAAC;AAEL,QAAM,YAAY,MAAM,qBAAqB,oBAAoB,eAAe,MAAM,YAAY;AAClG,QAAM,WAAW,CAAC,WAAyB,UAAU,OAAO,EAAE,KAAK,OAAO,UAAU;AACpF,QAAM,YAAY,CAAC,QAAsB,WAAoB;AAC3D,UAAM,OAAgC,EAAE,GAAG,UAAU;AACrD,WAAO,KAAK,OAAO,EAAE;AACrB,QAAI,YAAY,OAAO,UAAU,MAAO,MAAK,OAAO,EAAE,IAAI;AAC1D,mBAAe,IAAI;AAAA,EACrB;AAEA,QAAM,QAAQ,aAAa;AAC3B,QAAM,MAAM,CAAC,MAAiB,EAAE,MAAO,QAAQ,SAAI,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG,KAAM,EAAE;AAClF,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,KAAK;AAE1D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,OAAO,UAAU;AAAA,MAC7B,WAAW;AAAA,QACT;AAAA,QACA,YAAY,CAAC,YAAY,SAAS,IAAI,GAAG,0FAA0F;AAAA,QACnI;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ,+BAAC,SAAI,WAAU,8HACZ;AAAA,mBACC,qBAAC,SAAI,aAAU,mBAAkB,MAAK,SAAQ,WAAU,mFACtD;AAAA,8BAAC,qBAAkB,WAAU,mBAAkB;AAAA,UAC/C,oBAAC,UAAK,WAAU,YAAY,mBAAS,SAAQ;AAAA,UAC5C,SAAS;AAAA,WACZ,IAEA,iCACG;AAAA,iBAAO,OAAO,CAAC,EAAE,OAAO,MAAM,SAAS,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,SAAS,MACvE;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,aAAU;AAAA,cACV,gBAAc,OAAO;AAAA,cACrB,SAAS,OAAO;AAAA,cAChB,WAAW,GAAG,MAAM,OAAO,WAAW,OAAO;AAAA,cAE7C;AAAA,oCAAC,OAAO,MAAP,EAAY,WAAU,mBAAkB;AAAA,gBACxC,OAAO;AAAA,gBACP,YAAY,oBAAC,OAAI,WAAU,2BAA2B,cAAI,QAAQ,GAAE;AAAA;AAAA;AAAA,YAThE,OAAO;AAAA,UAUd,CACD;AAAA,UACD,oBAAC,UAAK,eAAW,MAAC,WAAU,iDAAgD;AAAA,UAC5E,qBAAC,WAAQ,MAAM,aAAa,cAAc,gBACxC;AAAA,iCAAC,kBAAe,MAAK,UAAS,aAAU,oBAAmB,WAAW,GAAG,MAAM,wDAAwD,GACrI;AAAA,kCAAC,SAAI,WAAU,mBAAkB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAa,GAAG,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MAC5J,qBAAW,oBAAoB,GAClC;AAAA,cACA,oBAAC,UAAK,WAAU,gBAAgB,iBAAO,aAAa,aAAY;AAAA,eAClE;AAAA,YACA,qBAAC,kBAAe,MAAK,OAAM,OAAM,SAAQ,WAAU,YACjD;AAAA,mCAAC,SAAI,WAAU,wCACb;AAAA,oCAAC,OAAE,WAAU,2BAA2B,iBAAO,SAAS,oBAAmB;AAAA,gBAC3E,oBAAC,OAAE,WAAU,8BACV,iBAAO,eAAe,8DACzB;AAAA,iBACF;AAAA,cACA,oBAAC,QAAG,WAAU,2BACX,iBAAO,IAAI,CAAC,EAAE,QAAQ,SAAS,MAC9B,oBAAC,QACC,+BAAC,WAAM,WAAU,0HACf;AAAA,oCAAC,YAAS,SAAS,SAAS,MAAM,GAAG,iBAAiB,YAAU,UAAU,QAAQ,MAAM,GAAG;AAAA,gBAC3F,oBAAC,OAAO,MAAP,EAAY,WAAU,mBAAkB;AAAA,gBACzC,oBAAC,UAAK,WAAU,mBAAmB,iBAAO,OAAM;AAAA,gBAC/C,YAAY,oBAAC,OAAK,cAAI,QAAQ,GAAE;AAAA,iBACnC,KANO,OAAO,EAOhB,CACD,GACH;AAAA,cACA,qBAAC,SAAI,WAAU,sEACb;AAAA,oCAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,eAAe,YAAY,GACzE,iBAAO,SAAS,oBACnB;AAAA,gBACA,oBAAC,UAAO,MAAK,MAAK,SAAS,MAAM,eAAe,KAAK,GAClD,iBAAO,QAAQ,QAClB;AAAA,iBACF;AAAA,eACF;AAAA,aACF;AAAA,WACF;AAAA,QAED,UAAU,QACT,oBAAC,SAAI,aAAU,iBAAgB,WAAU,kDACtC,kBACH;AAAA,SAEJ;AAAA;AAAA,EACF;AAEJ;AAGA,MAAM,OACJ;AAEF,MAAM,UAAU,GAAG,YAAY,OAAO;","names":[]}
@@ -1,6 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "../../lib/cn.js";
4
+ import { LOGO_BACKGROUND, LOGO_MARK, LOGO_MARK_INSET } from "../../generated/logo.js";
4
5
  const maskStyle = {
5
6
  backgroundColor: "currentColor",
6
7
  maskImage: "var(--brand-mark)",
@@ -22,7 +23,7 @@ function Logo({ className, withBackground = false }) {
22
23
  "data-slot": "logo",
23
24
  "aria-hidden": true,
24
25
  style: maskStyle,
25
- className: cn("inline-block", withBackground ? "w-3/5 h-3/5" : className)
26
+ className: cn(LOGO_MARK, withBackground ? LOGO_MARK_INSET : className)
26
27
  }
27
28
  );
28
29
  if (!withBackground) return mark;
@@ -30,7 +31,7 @@ function Logo({ className, withBackground = false }) {
30
31
  "span",
31
32
  {
32
33
  "data-slot": "logo-background",
33
- className: cn("inline-flex items-center justify-center bg-brand", className),
34
+ className: cn(LOGO_BACKGROUND, className),
34
35
  children: mark
35
36
  }
36
37
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/logo.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\n\n/**\n * The consumer's mark, painted as a CSS mask (`backgroundColor: currentColor` +\n * `maskImage`) so a monochrome lockup follows the surrounding text colour\n * instead of the artwork's baked-in fill. `withBackground` seats it on the brand\n * field, keeping a chip consistent with a favicon.\n *\n * The kit ships no artwork. The mark is a design token like every colour: the\n * consumer declares `--brand-mark` — a `url()` at its own asset — and\n * `--brand-aspect` in the same sheet it declares the palette in (see\n * `styles/tokens.css`). Undeclared, `maskImage` is invalid at computed-value time\n * and the span paints as a solid block: loud, and nobody else's logo.\n *\n * Decorative by design (`aria-hidden`): every real call site has the brand name\n * in adjacent text or on the wrapping link, and a mark announced twice is worse\n * than one announced once.\n */\nexport interface LogoProps {\n className?: string;\n withBackground?: boolean;\n}\n\nconst maskStyle: React.CSSProperties = {\n backgroundColor: \"currentColor\",\n maskImage: \"var(--brand-mark)\",\n WebkitMaskImage: \"var(--brand-mark)\",\n maskRepeat: \"no-repeat\",\n WebkitMaskRepeat: \"no-repeat\",\n maskPosition: \"center\",\n WebkitMaskPosition: \"center\",\n maskSize: \"contain\",\n WebkitMaskSize: \"contain\",\n // the ratio's fallback is what makes a missing mark *visible*: a masked span\n // has no intrinsic size, so `h-N w-auto` without it is zero-width\n aspectRatio: \"var(--brand-aspect, 1)\",\n};\n\nexport function Logo({ className, withBackground = false }: LogoProps) {\n const mark = (\n <span\n data-slot=\"logo\"\n aria-hidden\n style={maskStyle}\n className={cn(\"inline-block\", withBackground ? \"w-3/5 h-3/5\" : className)}\n />\n );\n\n if (!withBackground) return mark;\n\n return (\n <span\n data-slot=\"logo-background\"\n className={cn(\"inline-flex items-center justify-center bg-brand\", className)}\n >\n {mark}\n </span>\n );\n}\n"],"mappings":"AAyCI;AAzCJ,YAAY,WAAW;AACvB,SAAS,UAAU;AAuBnB,MAAM,YAAiC;AAAA,EACrC,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,gBAAgB;AAAA;AAAA;AAAA,EAGhB,aAAa;AACf;AAEO,SAAS,KAAK,EAAE,WAAW,iBAAiB,MAAM,GAAc;AACrE,QAAM,OACJ;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAW;AAAA,MACX,OAAO;AAAA,MACP,WAAW,GAAG,gBAAgB,iBAAiB,gBAAgB,SAAS;AAAA;AAAA,EAC1E;AAGF,MAAI,CAAC,eAAgB,QAAO;AAE5B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,oDAAoD,SAAS;AAAA,MAE1E;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/shell/logo.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { LOGO_BACKGROUND, LOGO_MARK, LOGO_MARK_INSET } from \"../../generated/logo\";\n\n/**\n * The consumer's mark, painted as a CSS mask (`backgroundColor: currentColor` +\n * `maskImage`) so a monochrome lockup follows the surrounding text colour\n * instead of the artwork's baked-in fill. `withBackground` seats it on the brand\n * field, keeping a chip consistent with a favicon.\n *\n * The kit ships no artwork. The mark is a design token like every colour: the\n * consumer declares `--brand-mark` — a `url()` at its own asset — and\n * `--brand-aspect` in the same sheet it declares the palette in (see\n * `styles/tokens.css`). Undeclared, `maskImage` is invalid at computed-value time\n * and the span paints as a solid block: loud, and nobody else's logo.\n *\n * Decorative by design (`aria-hidden`): every real call site has the brand name\n * in adjacent text or on the wrapping link, and a mark announced twice is worse\n * than one announced once.\n */\nexport interface LogoProps {\n className?: string;\n withBackground?: boolean;\n}\n\nconst maskStyle: React.CSSProperties = {\n backgroundColor: \"currentColor\",\n maskImage: \"var(--brand-mark)\",\n WebkitMaskImage: \"var(--brand-mark)\",\n maskRepeat: \"no-repeat\",\n WebkitMaskRepeat: \"no-repeat\",\n maskPosition: \"center\",\n WebkitMaskPosition: \"center\",\n maskSize: \"contain\",\n WebkitMaskSize: \"contain\",\n // the ratio's fallback is what makes a missing mark *visible*: a masked span\n // has no intrinsic size, so `h-N w-auto` without it is zero-width\n aspectRatio: \"var(--brand-aspect, 1)\",\n};\n\nexport function Logo({ className, withBackground = false }: LogoProps) {\n const mark = (\n <span\n data-slot=\"logo\"\n aria-hidden\n style={maskStyle}\n className={cn(LOGO_MARK, withBackground ? LOGO_MARK_INSET : className)}\n />\n );\n\n if (!withBackground) return mark;\n\n return (\n <span\n data-slot=\"logo-background\"\n className={cn(LOGO_BACKGROUND, className)}\n >\n {mark}\n </span>\n );\n}\n"],"mappings":"AA0CI;AA1CJ,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,iBAAiB,WAAW,uBAAuB;AAuB5D,MAAM,YAAiC;AAAA,EACrC,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,gBAAgB;AAAA;AAAA;AAAA,EAGhB,aAAa;AACf;AAEO,SAAS,KAAK,EAAE,WAAW,iBAAiB,MAAM,GAAc;AACrE,QAAM,OACJ;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAW;AAAA,MACX,OAAO;AAAA,MACP,WAAW,GAAG,WAAW,iBAAiB,kBAAkB,SAAS;AAAA;AAAA,EACvE;AAGF,MAAI,CAAC,eAAgB,QAAO;AAE5B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iBAAiB,SAAS;AAAA,MAEvC;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../lib/cn.js";
5
5
  import { ChevronLeftIcon } from "./shell-icons.js";
6
- const BACK = "-ml-1 flex size-6 shrink-0 items-center justify-center rounded-md text-ink outline-none focus-visible:ring-2 focus-visible:ring-ring";
6
+ const BACK = "-ml-1 flex size-(--control-h-2xs) shrink-0 items-center justify-center rounded-control text-ink outline-none focus-visible:ring-2 focus-visible:ring-ring";
7
7
  const HIDE_FROM = { md: "md:hidden", lg: "lg:hidden" };
8
8
  function MobileAppBar({
9
9
  title,
@@ -23,13 +23,13 @@ function MobileAppBar({
23
23
  "data-slot": "mobile-app-bar",
24
24
  "data-enter": "rise",
25
25
  className: cn(
26
- "sticky top-[var(--ev-shell-offset,0px)] z-30 flex items-center gap-2 border-b border-border bg-secondary px-4 pb-3.5 pt-4",
26
+ "sticky top-[var(--ev-shell-offset,0px)] z-30 flex items-center gap-2 border-b border-border bg-card px-4 pb-3.5 pt-4",
27
27
  hideFrom && HIDE_FROM[hideFrom],
28
28
  className
29
29
  ),
30
30
  ...props,
31
31
  children: [
32
- back && ("href" in back ? /* @__PURE__ */ jsx(Link, { href: back.href, "aria-label": backLabel, className: BACK, children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "size-6" }) }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: back.onClick, "aria-label": backLabel, className: BACK, children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "size-6" }) })),
32
+ back && ("href" in back ? /* @__PURE__ */ jsx(Link, { href: back.href, "aria-label": backLabel, className: BACK, children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "size-full" }) }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: back.onClick, "aria-label": backLabel, className: BACK, children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "size-full" }) })),
33
33
  /* @__PURE__ */ jsx(
34
34
  "h1",
35
35
  {
@@ -40,7 +40,7 @@ function MobileAppBar({
40
40
  children: title
41
41
  }
42
42
  ),
43
- back && right === void 0 ? /* @__PURE__ */ jsx("span", { className: "size-6 shrink-0", "aria-hidden": true }) : right
43
+ back && right === void 0 ? /* @__PURE__ */ jsx("span", { className: "size-(--control-h-2xs) shrink-0", "aria-hidden": true }) : right
44
44
  ]
45
45
  }
46
46
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/mobile-app-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport type { ShellBreakpoint } from \"./app-shell\";\nimport { ChevronLeftIcon } from \"./shell-icons\";\n\nexport interface MobileAppBarLabels {\n /** Accessible name of the back control. Default `\"Back\"`. */\n back?: string;\n}\n\nexport type MobileAppBarBack = { href: string } | { onClick: () => void };\n\nexport interface MobileAppBarProps extends Omit<React.ComponentProps<\"header\">, \"title\"> {\n title: React.ReactNode;\n /** A pushed screen: a back link (`href`) or a back button (`onClick`); centres the title. */\n back?: MobileAppBarBack;\n /** Trailing action — Save, a menu. */\n right?: React.ReactNode;\n linkComponent?: React.ElementType;\n /** Hidden from this breakpoint up, where the page heading takes over. Default `lg`; `false` never hides. */\n hideFrom?: ShellBreakpoint | false;\n labels?: MobileAppBarLabels;\n}\n\n// A bare glyph has no border of its own, so it needs a rounded box for the ring to trace.\nconst BACK =\n \"-ml-1 flex size-6 shrink-0 items-center justify-center rounded-md text-ink outline-none focus-visible:ring-2 focus-visible:ring-ring\";\n\nconst HIDE_FROM = { md: \"md:hidden\", lg: \"lg:hidden\" } as const;\n\n/**\n * The bar that titles a screen on a phone. A root screen puts the title flush\n * left with an optional action; a pushed one (`back`) centres it between the\n * back control and a matching spacer. Sticks under `--ev-shell-offset` and\n * arrives with the screen (`data-enter=\"rise\"`), one step ahead of a\n * `PageFrame`'s sections.\n */\nexport function MobileAppBar({\n title,\n back,\n right,\n linkComponent,\n hideFrom = \"lg\",\n labels = {},\n className,\n ...props\n}: MobileAppBarProps) {\n const backLabel = labels.back ?? \"Back\";\n const Link = linkComponent ?? \"a\";\n return (\n <header\n data-slot=\"mobile-app-bar\"\n data-enter=\"rise\"\n className={cn(\n \"sticky top-[var(--ev-shell-offset,0px)] z-30 flex items-center gap-2 border-b border-border bg-secondary px-4 pb-3.5 pt-4\",\n hideFrom && HIDE_FROM[hideFrom],\n className,\n )}\n {...props}\n >\n {back &&\n (\"href\" in back ? (\n <Link href={back.href} aria-label={backLabel} className={BACK}>\n <ChevronLeftIcon className=\"size-6\" />\n </Link>\n ) : (\n <button type=\"button\" onClick={back.onClick} aria-label={backLabel} className={BACK}>\n <ChevronLeftIcon className=\"size-6\" />\n </button>\n ))}\n <h1\n className={cn(\n \"min-w-0 flex-1 truncate font-semibold text-ink\",\n back ? \"text-center text-base\" : \"text-lg\",\n )}\n >\n {title}\n </h1>\n {back && right === undefined ? <span className=\"size-6 shrink-0\" aria-hidden /> : right}\n </header>\n );\n}\n"],"mappings":";AAoDI,SAaQ,KAbR;AAlDJ,YAAY,WAAW;AACvB,SAAS,UAAU;AAEnB,SAAS,uBAAuB;AAsBhC,MAAM,OACJ;AAEF,MAAM,YAAY,EAAE,IAAI,aAAa,IAAI,YAAY;AAS9C,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,SAAS,CAAC;AAAA,EACV;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,QAAM,YAAY,OAAO,QAAQ;AACjC,QAAM,OAAO,iBAAiB;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAW;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA,YAAY,UAAU,QAAQ;AAAA,QAC9B;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,iBACE,UAAU,OACT,oBAAC,QAAK,MAAM,KAAK,MAAM,cAAY,WAAW,WAAW,MACvD,8BAAC,mBAAgB,WAAU,UAAS,GACtC,IAEA,oBAAC,YAAO,MAAK,UAAS,SAAS,KAAK,SAAS,cAAY,WAAW,WAAW,MAC7E,8BAAC,mBAAgB,WAAU,UAAS,GACtC;AAAA,QAEJ;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,OAAO,0BAA0B;AAAA,YACnC;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QACC,QAAQ,UAAU,SAAY,oBAAC,UAAK,WAAU,mBAAkB,eAAW,MAAC,IAAK;AAAA;AAAA;AAAA,EACpF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/shell/mobile-app-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport type { ShellBreakpoint } from \"./app-shell\";\nimport { ChevronLeftIcon } from \"./shell-icons\";\n\nexport interface MobileAppBarLabels {\n /** Accessible name of the back control. Default `\"Back\"`. */\n back?: string;\n}\n\nexport type MobileAppBarBack = { href: string } | { onClick: () => void };\n\nexport interface MobileAppBarProps extends Omit<React.ComponentProps<\"header\">, \"title\"> {\n title: React.ReactNode;\n /** A pushed screen: a back link (`href`) or a back button (`onClick`); centres the title. */\n back?: MobileAppBarBack;\n /** Trailing action — Save, a menu. */\n right?: React.ReactNode;\n linkComponent?: React.ElementType;\n /** Hidden from this breakpoint up, where the page heading takes over. Default `lg`; `false` never hides. */\n hideFrom?: ShellBreakpoint | false;\n labels?: MobileAppBarLabels;\n}\n\n// A bare glyph has no border of its own, so it needs a rounded box for the ring to trace.\nconst BACK =\n \"-ml-1 flex size-(--control-h-2xs) shrink-0 items-center justify-center rounded-control text-ink outline-none focus-visible:ring-2 focus-visible:ring-ring\";\n\nconst HIDE_FROM = { md: \"md:hidden\", lg: \"lg:hidden\" } as const;\n\n/**\n * The bar that titles a screen on a phone. A root screen puts the title flush\n * left with an optional action; a pushed one (`back`) centres it between the\n * back control and a matching spacer. Sticks under `--ev-shell-offset` and\n * arrives with the screen (`data-enter=\"rise\"`), one step ahead of a\n * `PageFrame`'s sections.\n */\nexport function MobileAppBar({\n title,\n back,\n right,\n linkComponent,\n hideFrom = \"lg\",\n labels = {},\n className,\n ...props\n}: MobileAppBarProps) {\n const backLabel = labels.back ?? \"Back\";\n const Link = linkComponent ?? \"a\";\n return (\n <header\n data-slot=\"mobile-app-bar\"\n data-enter=\"rise\"\n className={cn(\n \"sticky top-[var(--ev-shell-offset,0px)] z-30 flex items-center gap-2 border-b border-border bg-card px-4 pb-3.5 pt-4\",\n hideFrom && HIDE_FROM[hideFrom],\n className,\n )}\n {...props}\n >\n {back &&\n (\"href\" in back ? (\n <Link href={back.href} aria-label={backLabel} className={BACK}>\n <ChevronLeftIcon className=\"size-full\" />\n </Link>\n ) : (\n <button type=\"button\" onClick={back.onClick} aria-label={backLabel} className={BACK}>\n <ChevronLeftIcon className=\"size-full\" />\n </button>\n ))}\n <h1\n className={cn(\n \"min-w-0 flex-1 truncate font-semibold text-ink\",\n back ? \"text-center text-base\" : \"text-lg\",\n )}\n >\n {title}\n </h1>\n {back && right === undefined ? <span className=\"size-(--control-h-2xs) shrink-0\" aria-hidden /> : right}\n </header>\n );\n}\n"],"mappings":";AAoDI,SAaQ,KAbR;AAlDJ,YAAY,WAAW;AACvB,SAAS,UAAU;AAEnB,SAAS,uBAAuB;AAsBhC,MAAM,OACJ;AAEF,MAAM,YAAY,EAAE,IAAI,aAAa,IAAI,YAAY;AAS9C,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,SAAS,CAAC;AAAA,EACV;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,QAAM,YAAY,OAAO,QAAQ;AACjC,QAAM,OAAO,iBAAiB;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAW;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA,YAAY,UAAU,QAAQ;AAAA,QAC9B;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,iBACE,UAAU,OACT,oBAAC,QAAK,MAAM,KAAK,MAAM,cAAY,WAAW,WAAW,MACvD,8BAAC,mBAAgB,WAAU,aAAY,GACzC,IAEA,oBAAC,YAAO,MAAK,UAAS,SAAS,KAAK,SAAS,cAAY,WAAW,WAAW,MAC7E,8BAAC,mBAAgB,WAAU,aAAY,GACzC;AAAA,QAEJ;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,OAAO,0BAA0B;AAAA,YACnC;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QACC,QAAQ,UAAU,SAAY,oBAAC,UAAK,WAAU,mCAAkC,eAAW,MAAC,IAAK;AAAA;AAAA;AAAA,EACpG;AAEJ;","names":[]}
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../lib/cn.js";
5
5
  import { useControllableState } from "../../primitives/use-controllable-state.js";
6
+ import { iconShapes } from "../../icons/icon-shapes.js";
6
7
  function useNavGroupOpen(group, containsCurrent) {
7
8
  const [open, setOpen] = useControllableState({
8
9
  value: group.open,
@@ -65,11 +66,11 @@ function Chevron({ open }) {
65
66
  strokeLinecap: "round",
66
67
  strokeLinejoin: "round",
67
68
  className: cn("size-4 transition-transform", MOTION, open && "rotate-90"),
68
- children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
69
+ children: iconShapes("chevron-right")
69
70
  }
70
71
  );
71
72
  }
72
- const ROW = "flex min-h-9 items-center rounded-(--control-radius) text-body font-medium text-ink transition-colors motion-reduce:transition-none";
73
+ const ROW = "flex min-h-(--control-h-md) items-center rounded-row text-body font-medium text-ink transition-colors motion-reduce:transition-none";
73
74
  const FOCUS = "outline-none focus-visible:ring-2 focus-visible:ring-ring";
74
75
  function NavGroupHeader({
75
76
  label,
@@ -84,9 +85,9 @@ function NavGroupHeader({
84
85
  className
85
86
  }) {
86
87
  const chevronId = React.useId();
87
- const row = cn(ROW, open ? "bg-active" : "hover:bg-hover", className);
88
+ const row = cn(ROW, open ? "bg-selected" : "hover:bg-hover", className);
88
89
  const chip = cn(
89
- "grid size-7 shrink-0 place-items-center rounded-md text-ink-mid",
90
+ "grid size-(--control-h-xs) shrink-0 place-items-center rounded-control text-ink-mid",
90
91
  open && "bg-selected"
91
92
  );
92
93
  if (href === void 0) {
@@ -114,7 +115,7 @@ function NavGroupHeader({
114
115
  id: labelId,
115
116
  href,
116
117
  "aria-current": current ? "page" : void 0,
117
- className: cn("min-w-0 flex-1 truncate rounded-(--control-radius) py-2 pl-3", FOCUS),
118
+ className: cn("min-w-0 flex-1 truncate rounded-row py-2 pl-3", FOCUS),
118
119
  children: label
119
120
  }
120
121
  ),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/nav-collapse.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { useControllableState } from \"../../primitives/use-controllable-state\";\n\n/** What a nav group takes to become a section that opens and closes. */\nexport interface NavGroupCollapse {\n /**\n * Draws the group's `label` as a header with a chevron and lets the rows fold\n * away. Needs a `label`. Off by default so an existing rail keeps its static\n * eyebrow.\n */\n collapsible?: boolean;\n /** Open on mount. Default: open exactly when the group holds the current item. */\n defaultOpen?: boolean;\n /** Controlled open state; pair with `onOpenChange`. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** A landing page: the label navigates to it and only the chevron toggles. */\n href?: string;\n}\n\nexport interface NavCollapseLabels {\n /** Spoken before the group's name on a closed section's chevron. Default `\"Expand\"`. */\n expand?: string;\n /** Spoken before the group's name on an open section's chevron. Default `\"Collapse\"`. */\n collapse?: string;\n}\n\n/**\n * Open state of one collapsible group. A route change that lands inside a closed\n * group opens it; a user who closes the group holding the current row is not\n * fought, because only the move INTO the group opens it.\n */\nexport function useNavGroupOpen(\n group: NavGroupCollapse,\n containsCurrent: boolean,\n): readonly [open: boolean, setOpen: (next: boolean) => void] {\n const [open, setOpen] = useControllableState<boolean>({\n value: group.open,\n defaultValue: group.defaultOpen ?? containsCurrent,\n onChange: group.onOpenChange,\n });\n const hadCurrent = React.useRef(containsCurrent);\n React.useEffect(() => {\n if (containsCurrent && !hadCurrent.current && !open) setOpen(true);\n hadCurrent.current = containsCurrent;\n }, [containsCurrent, open, setOpen]);\n return [open, setOpen] as const;\n}\n\nconst MOTION =\n \"duration-(--ev-dur-fast) ease-(--ev-ease-in-out) motion-reduce:transition-none\";\n\n/**\n * The folding body. Height runs on a `0fr → 1fr` grid track (no measuring), the\n * content fades, and `visibility` flips at the END of the close so the hidden\n * rows leave the tab order without cutting the fade. `inert` is set at once so\n * a closing region cannot take focus or be read while it fades.\n */\nexport function NavCollapse({\n open,\n id,\n children,\n}: {\n open: boolean;\n id: string;\n children: React.ReactNode;\n}) {\n const ref = React.useRef<HTMLDivElement>(null);\n // `inert` as a React prop is a boolean in 19 and unknown in 18; the DOM\n // attribute works in both.\n React.useLayoutEffect(() => {\n ref.current?.toggleAttribute(\"inert\", !open);\n }, [open]);\n return (\n <div\n ref={ref}\n id={id}\n data-slot=\"nav-collapse\"\n data-state={open ? \"open\" : \"closed\"}\n className={cn(\n // minmax(0, …): a plain `1fr` column grows to the widest label and the rail overflows.\n \"grid grid-cols-[minmax(0,1fr)] transition-[grid-template-rows]\",\n MOTION,\n open ? \"grid-rows-[1fr]\" : \"grid-rows-[0fr]\",\n )}\n >\n {/* `overflow-y-clip` keeps the x axis visible so a focus ring on a row is not cut. */}\n <div\n className={cn(\n \"min-h-0 overflow-y-clip transition-[opacity,visibility]\",\n MOTION,\n open ? \"visible opacity-100\" : \"invisible opacity-0\",\n )}\n >\n {children}\n </div>\n </div>\n );\n}\n\nfunction Chevron({ open }: { open: boolean }) {\n return (\n <svg\n aria-hidden\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(\"size-4 transition-transform\", MOTION, open && \"rotate-90\")}\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n );\n}\n\nconst ROW = \"flex min-h-9 items-center rounded-(--control-radius) text-body font-medium text-ink transition-colors motion-reduce:transition-none\";\nconst FOCUS = \"outline-none focus-visible:ring-2 focus-visible:ring-ring\";\n\nexport interface NavGroupHeaderProps {\n label: React.ReactNode;\n labelId: string;\n regionId: string;\n open: boolean;\n onToggle: () => void;\n href?: string | undefined;\n linkComponent?: React.ElementType | undefined;\n /** The landing page is the current page. */\n current?: boolean | undefined;\n labels?: NavCollapseLabels | undefined;\n className?: string | undefined;\n}\n\n/**\n * The section's header row. With an `href` the label is a link and the chevron\n * is its own button; without one the whole row is the button and the chevron is\n * a picture of its state.\n */\nexport function NavGroupHeader({\n label,\n labelId,\n regionId,\n open,\n onToggle,\n href,\n linkComponent,\n current,\n labels,\n className,\n}: NavGroupHeaderProps) {\n const chevronId = React.useId();\n const row = cn(ROW, open ? \"bg-active\" : \"hover:bg-hover\", className);\n const chip = cn(\n \"grid size-7 shrink-0 place-items-center rounded-md text-ink-mid\",\n open && \"bg-selected\",\n );\n if (href === undefined) {\n return (\n <button\n type=\"button\"\n data-slot=\"nav-group-header\"\n aria-expanded={open}\n aria-controls={regionId}\n onClick={onToggle}\n className={cn(row, FOCUS, \"w-full gap-2 py-1 pl-3 pr-1.5 text-left\")}\n >\n <span id={labelId} className=\"min-w-0 flex-1 truncate\">\n {label}\n </span>\n <span className={chip}>\n <Chevron open={open} />\n </span>\n </button>\n );\n }\n const Link: React.ElementType = linkComponent ?? \"a\";\n return (\n <div data-slot=\"nav-group-header\" className={cn(row, \"gap-1 pr-1.5\")}>\n <Link\n id={labelId}\n href={href}\n aria-current={current ? \"page\" : undefined}\n className={cn(\"min-w-0 flex-1 truncate rounded-(--control-radius) py-2 pl-3\", FOCUS)}\n >\n {label}\n </Link>\n {/* Named \"Expand Guides\": the button's own aria-label, then the label's text. */}\n <button\n type=\"button\"\n id={chevronId}\n aria-label={open ? (labels?.collapse ?? \"Collapse\") : (labels?.expand ?? \"Expand\")}\n aria-labelledby={`${chevronId} ${labelId}`}\n aria-expanded={open}\n aria-controls={regionId}\n onClick={onToggle}\n className={cn(chip, FOCUS, !open && \"hover:bg-hover\")}\n >\n <Chevron open={open} />\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/shell/nav-collapse.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { useControllableState } from \"../../primitives/use-controllable-state\";\nimport { iconShapes } from \"../../icons/icon-shapes\";\n\n/** What a nav group takes to become a section that opens and closes. */\nexport interface NavGroupCollapse {\n /**\n * Draws the group's `label` as a header with a chevron and lets the rows fold\n * away. Needs a `label`. Off by default so an existing rail keeps its static\n * eyebrow.\n */\n collapsible?: boolean;\n /** Open on mount. Default: open exactly when the group holds the current item. */\n defaultOpen?: boolean;\n /** Controlled open state; pair with `onOpenChange`. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** A landing page: the label navigates to it and only the chevron toggles. */\n href?: string;\n}\n\nexport interface NavCollapseLabels {\n /** Spoken before the group's name on a closed section's chevron. Default `\"Expand\"`. */\n expand?: string;\n /** Spoken before the group's name on an open section's chevron. Default `\"Collapse\"`. */\n collapse?: string;\n}\n\n/**\n * Open state of one collapsible group. A route change that lands inside a closed\n * group opens it; a user who closes the group holding the current row is not\n * fought, because only the move INTO the group opens it.\n */\nexport function useNavGroupOpen(\n group: NavGroupCollapse,\n containsCurrent: boolean,\n): readonly [open: boolean, setOpen: (next: boolean) => void] {\n const [open, setOpen] = useControllableState<boolean>({\n value: group.open,\n defaultValue: group.defaultOpen ?? containsCurrent,\n onChange: group.onOpenChange,\n });\n const hadCurrent = React.useRef(containsCurrent);\n React.useEffect(() => {\n if (containsCurrent && !hadCurrent.current && !open) setOpen(true);\n hadCurrent.current = containsCurrent;\n }, [containsCurrent, open, setOpen]);\n return [open, setOpen] as const;\n}\n\nconst MOTION =\n \"duration-(--ev-dur-fast) ease-(--ev-ease-in-out) motion-reduce:transition-none\";\n\n/**\n * The folding body. Height runs on a `0fr → 1fr` grid track (no measuring), the\n * content fades, and `visibility` flips at the END of the close so the hidden\n * rows leave the tab order without cutting the fade. `inert` is set at once so\n * a closing region cannot take focus or be read while it fades.\n */\nexport function NavCollapse({\n open,\n id,\n children,\n}: {\n open: boolean;\n id: string;\n children: React.ReactNode;\n}) {\n const ref = React.useRef<HTMLDivElement>(null);\n // `inert` as a React prop is a boolean in 19 and unknown in 18; the DOM\n // attribute works in both.\n React.useLayoutEffect(() => {\n ref.current?.toggleAttribute(\"inert\", !open);\n }, [open]);\n return (\n <div\n ref={ref}\n id={id}\n data-slot=\"nav-collapse\"\n data-state={open ? \"open\" : \"closed\"}\n className={cn(\n // minmax(0, …): a plain `1fr` column grows to the widest label and the rail overflows.\n \"grid grid-cols-[minmax(0,1fr)] transition-[grid-template-rows]\",\n MOTION,\n open ? \"grid-rows-[1fr]\" : \"grid-rows-[0fr]\",\n )}\n >\n {/* `overflow-y-clip` keeps the x axis visible so a focus ring on a row is not cut. */}\n <div\n className={cn(\n \"min-h-0 overflow-y-clip transition-[opacity,visibility]\",\n MOTION,\n open ? \"visible opacity-100\" : \"invisible opacity-0\",\n )}\n >\n {children}\n </div>\n </div>\n );\n}\n\nfunction Chevron({ open }: { open: boolean }) {\n return (\n <svg\n aria-hidden\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(\"size-4 transition-transform\", MOTION, open && \"rotate-90\")}\n >\n {iconShapes(\"chevron-right\")}\n </svg>\n );\n}\n\nconst ROW = \"flex min-h-(--control-h-md) items-center rounded-row text-body font-medium text-ink transition-colors motion-reduce:transition-none\";\nconst FOCUS = \"outline-none focus-visible:ring-2 focus-visible:ring-ring\";\n\nexport interface NavGroupHeaderProps {\n label: React.ReactNode;\n labelId: string;\n regionId: string;\n open: boolean;\n onToggle: () => void;\n href?: string | undefined;\n linkComponent?: React.ElementType | undefined;\n /** The landing page is the current page. */\n current?: boolean | undefined;\n labels?: NavCollapseLabels | undefined;\n className?: string | undefined;\n}\n\n/**\n * The section's header row. With an `href` the label is a link and the chevron\n * is its own button; without one the whole row is the button and the chevron is\n * a picture of its state.\n */\nexport function NavGroupHeader({\n label,\n labelId,\n regionId,\n open,\n onToggle,\n href,\n linkComponent,\n current,\n labels,\n className,\n}: NavGroupHeaderProps) {\n const chevronId = React.useId();\n const row = cn(ROW, open ? \"bg-selected\" : \"hover:bg-hover\", className);\n const chip = cn(\n \"grid size-(--control-h-xs) shrink-0 place-items-center rounded-control text-ink-mid\",\n open && \"bg-selected\",\n );\n if (href === undefined) {\n return (\n <button\n type=\"button\"\n data-slot=\"nav-group-header\"\n aria-expanded={open}\n aria-controls={regionId}\n onClick={onToggle}\n className={cn(row, FOCUS, \"w-full gap-2 py-1 pl-3 pr-1.5 text-left\")}\n >\n <span id={labelId} className=\"min-w-0 flex-1 truncate\">\n {label}\n </span>\n <span className={chip}>\n <Chevron open={open} />\n </span>\n </button>\n );\n }\n const Link: React.ElementType = linkComponent ?? \"a\";\n return (\n <div data-slot=\"nav-group-header\" className={cn(row, \"gap-1 pr-1.5\")}>\n <Link\n id={labelId}\n href={href}\n aria-current={current ? \"page\" : undefined}\n className={cn(\"min-w-0 flex-1 truncate rounded-row py-2 pl-3\", FOCUS)}\n >\n {label}\n </Link>\n {/* Named \"Expand Guides\": the button's own aria-label, then the label's text. */}\n <button\n type=\"button\"\n id={chevronId}\n aria-label={open ? (labels?.collapse ?? \"Collapse\") : (labels?.expand ?? \"Expand\")}\n aria-labelledby={`${chevronId} ${labelId}`}\n aria-expanded={open}\n aria-controls={regionId}\n onClick={onToggle}\n className={cn(chip, FOCUS, !open && \"hover:bg-hover\")}\n >\n <Chevron open={open} />\n </button>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -8,7 +8,7 @@ import { RefreshIcon, TriangleAlertIcon } from "./shell-icons.js";
8
8
  function ResourceError(props) {
9
9
  if (props.variant === "alert") {
10
10
  const { message: message2, title, labels: _labels, variant: _variant2, className: className2, ...rest2 } = props;
11
- return /* @__PURE__ */ jsx("div", { "data-slot": "resource-error", "data-variant": "alert", className: className2, ...rest2, children: /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
11
+ return /* @__PURE__ */ jsx("div", { "data-slot": "resource-error", "data-variant": "alert", className: className2, ...rest2, children: /* @__PURE__ */ jsxs(Alert, { tone: "error", children: [
12
12
  /* @__PURE__ */ jsx(TriangleAlertIcon, { className: "size-4" }),
13
13
  title !== void 0 && /* @__PURE__ */ jsx(AlertTitle, { children: title }),
14
14
  /* @__PURE__ */ jsx(AlertDescription, { children: message2 })
@@ -27,7 +27,7 @@ function ResourceError(props) {
27
27
  "data-variant": "inline",
28
28
  role: "alert",
29
29
  className: cn(
30
- onRetry && "flex flex-wrap items-center gap-3 rounded-lg border border-accent-error/40 bg-accent-error/10 px-3 py-2.5",
30
+ onRetry && "flex flex-wrap items-center gap-3 rounded-panel border border-tint-error-edge bg-accent-error/(--tint-hover) px-3 py-2.5",
31
31
  className
32
32
  ),
33
33
  ...rest,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/resource-error.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { Alert, AlertDescription, AlertTitle } from \"../atomic/alert\";\nimport { Button } from \"../atomic/button\";\nimport { RefreshIcon, TriangleAlertIcon } from \"./shell-icons\";\n\nexport interface ResourceErrorLabels {\n /** The retry button. Default `\"Try again\"`. */\n retry?: string;\n}\n\ninterface ResourceErrorBase extends Omit<React.ComponentProps<\"div\">, \"title\" | \"children\"> {\n /** What failed, already in the reader's words — the kit formats no errors. */\n message: React.ReactNode;\n labels?: ResourceErrorLabels;\n}\n\n// The two forms take different props and the union says so: an `alert` has no\n// retry and an `inline` line has no heading, so asking for either is a type\n// error rather than a prop that silently does nothing.\nexport type ResourceErrorProps = ResourceErrorBase &\n (\n | {\n /** One line under the page header — for dense screens where a block would push everything down. */\n variant?: \"inline\";\n title?: never;\n /** Re-run the read; boxes the line so the button and the message read as one thing. */\n onRetry?: () => void;\n /** The retry is in flight: the button is disabled and its icon spins. */\n retrying?: boolean;\n }\n | {\n /** The kit's `Alert` with a heading — for screens that address the reader. */\n variant: \"alert\";\n title?: React.ReactNode;\n onRetry?: never;\n retrying?: never;\n }\n );\n\n/**\n * What a screen shows when a read it needed never arrived. Whether there is\n * anything to report stays with the caller — only it knows whether stale data is\n * still on screen, and a failed refresh over figures the reader can see belongs\n * beside them, not in place of them.\n */\nexport function ResourceError(props: ResourceErrorProps) {\n if (props.variant === \"alert\") {\n const { message, title, labels: _labels, variant: _variant, className, ...rest } = props;\n return (\n <div data-slot=\"resource-error\" data-variant=\"alert\" className={className} {...rest}>\n <Alert variant=\"destructive\">\n <TriangleAlertIcon className=\"size-4\" />\n {title !== undefined && <AlertTitle>{title}</AlertTitle>}\n <AlertDescription>{message}</AlertDescription>\n </Alert>\n </div>\n );\n }\n const { message, onRetry, retrying = false, labels = {}, variant: _variant, title: _title, className, ...rest } = props;\n const line = (\n <p className=\"flex min-w-0 items-center gap-2 text-sm text-accent-error\">\n {/* Holds its size: beside the button it is the first thing squeezed, and a\n half-width triangle reads as a rendering fault. */}\n <TriangleAlertIcon className=\"size-4 shrink-0\" /> {message}\n </p>\n );\n return (\n <div\n data-slot=\"resource-error\"\n data-variant=\"inline\"\n role=\"alert\"\n className={cn(\n onRetry &&\n \"flex flex-wrap items-center gap-3 rounded-lg border border-accent-error/40 bg-accent-error/10 px-3 py-2.5\",\n className,\n )}\n {...rest}\n >\n {line}\n {onRetry && (\n <Button type=\"button\" variant=\"outline\" size=\"xs\" disabled={retrying} onClick={onRetry}>\n <RefreshIcon className={cn(\"size-4\", retrying && \"motion-safe:animate-spin\")} />\n {labels.retry ?? \"Try again\"}\n </Button>\n )}\n </div>\n );\n}\n"],"mappings":";AAqDQ,SACE,KADF;AAnDR,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,cAAc;AACvB,SAAS,aAAa,yBAAyB;AA0CxC,SAAS,cAAc,OAA2B;AACvD,MAAI,MAAM,YAAY,SAAS;AAC7B,UAAM,EAAE,SAAAA,UAAS,OAAO,QAAQ,SAAS,SAASC,WAAU,WAAAC,YAAW,GAAGC,MAAK,IAAI;AACnF,WACE,oBAAC,SAAI,aAAU,kBAAiB,gBAAa,SAAQ,WAAWD,YAAY,GAAGC,OAC7E,+BAAC,SAAM,SAAQ,eACb;AAAA,0BAAC,qBAAkB,WAAU,UAAS;AAAA,MACrC,UAAU,UAAa,oBAAC,cAAY,iBAAM;AAAA,MAC3C,oBAAC,oBAAkB,UAAAH,UAAQ;AAAA,OAC7B,GACF;AAAA,EAEJ;AACA,QAAM,EAAE,SAAS,SAAS,WAAW,OAAO,SAAS,CAAC,GAAG,SAAS,UAAU,OAAO,QAAQ,WAAW,GAAG,KAAK,IAAI;AAClH,QAAM,OACJ,qBAAC,OAAE,WAAU,6DAGX;AAAA,wBAAC,qBAAkB,WAAU,mBAAkB;AAAA,IAAE;AAAA,IAAE;AAAA,KACrD;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAa;AAAA,MACb,MAAK;AAAA,MACL,WAAW;AAAA,QACT,WACE;AAAA,QACF;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACA,WACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,UAAU,UAAU,SAAS,SAC7E;AAAA,8BAAC,eAAY,WAAW,GAAG,UAAU,YAAY,0BAA0B,GAAG;AAAA,UAC7E,OAAO,SAAS;AAAA,WACnB;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":["message","_variant","className","rest"]}
1
+ {"version":3,"sources":["../../../src/components/shell/resource-error.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { Alert, AlertDescription, AlertTitle } from \"../atomic/alert\";\nimport { Button } from \"../atomic/button\";\nimport { RefreshIcon, TriangleAlertIcon } from \"./shell-icons\";\n\nexport interface ResourceErrorLabels {\n /** The retry button. Default `\"Try again\"`. */\n retry?: string;\n}\n\ninterface ResourceErrorBase extends Omit<React.ComponentProps<\"div\">, \"title\" | \"children\"> {\n /** What failed, already in the reader's words — the kit formats no errors. */\n message: React.ReactNode;\n labels?: ResourceErrorLabels;\n}\n\n// The two forms take different props and the union says so: an `alert` has no\n// retry and an `inline` line has no heading, so asking for either is a type\n// error rather than a prop that silently does nothing.\nexport type ResourceErrorProps = ResourceErrorBase &\n (\n | {\n /** One line under the page header — for dense screens where a block would push everything down. */\n variant?: \"inline\";\n title?: never;\n /** Re-run the read; boxes the line so the button and the message read as one thing. */\n onRetry?: () => void;\n /** The retry is in flight: the button is disabled and its icon spins. */\n retrying?: boolean;\n }\n | {\n /** The kit's `Alert` with a heading — for screens that address the reader. */\n variant: \"alert\";\n title?: React.ReactNode;\n onRetry?: never;\n retrying?: never;\n }\n );\n\n/**\n * What a screen shows when a read it needed never arrived. Whether there is\n * anything to report stays with the caller — only it knows whether stale data is\n * still on screen, and a failed refresh over figures the reader can see belongs\n * beside them, not in place of them.\n */\nexport function ResourceError(props: ResourceErrorProps) {\n if (props.variant === \"alert\") {\n const { message, title, labels: _labels, variant: _variant, className, ...rest } = props;\n return (\n <div data-slot=\"resource-error\" data-variant=\"alert\" className={className} {...rest}>\n <Alert tone=\"error\">\n <TriangleAlertIcon className=\"size-4\" />\n {title !== undefined && <AlertTitle>{title}</AlertTitle>}\n <AlertDescription>{message}</AlertDescription>\n </Alert>\n </div>\n );\n }\n const { message, onRetry, retrying = false, labels = {}, variant: _variant, title: _title, className, ...rest } = props;\n const line = (\n <p className=\"flex min-w-0 items-center gap-2 text-sm text-accent-error\">\n {/* Holds its size: beside the button it is the first thing squeezed, and a\n half-width triangle reads as a rendering fault. */}\n <TriangleAlertIcon className=\"size-4 shrink-0\" /> {message}\n </p>\n );\n return (\n <div\n data-slot=\"resource-error\"\n data-variant=\"inline\"\n role=\"alert\"\n className={cn(\n onRetry &&\n \"flex flex-wrap items-center gap-3 rounded-panel border border-tint-error-edge bg-accent-error/(--tint-hover) px-3 py-2.5\",\n className,\n )}\n {...rest}\n >\n {line}\n {onRetry && (\n <Button type=\"button\" variant=\"outline\" size=\"xs\" disabled={retrying} onClick={onRetry}>\n <RefreshIcon className={cn(\"size-4\", retrying && \"motion-safe:animate-spin\")} />\n {labels.retry ?? \"Try again\"}\n </Button>\n )}\n </div>\n );\n}\n"],"mappings":";AAqDQ,SACE,KADF;AAnDR,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,cAAc;AACvB,SAAS,aAAa,yBAAyB;AA0CxC,SAAS,cAAc,OAA2B;AACvD,MAAI,MAAM,YAAY,SAAS;AAC7B,UAAM,EAAE,SAAAA,UAAS,OAAO,QAAQ,SAAS,SAASC,WAAU,WAAAC,YAAW,GAAGC,MAAK,IAAI;AACnF,WACE,oBAAC,SAAI,aAAU,kBAAiB,gBAAa,SAAQ,WAAWD,YAAY,GAAGC,OAC7E,+BAAC,SAAM,MAAK,SACV;AAAA,0BAAC,qBAAkB,WAAU,UAAS;AAAA,MACrC,UAAU,UAAa,oBAAC,cAAY,iBAAM;AAAA,MAC3C,oBAAC,oBAAkB,UAAAH,UAAQ;AAAA,OAC7B,GACF;AAAA,EAEJ;AACA,QAAM,EAAE,SAAS,SAAS,WAAW,OAAO,SAAS,CAAC,GAAG,SAAS,UAAU,OAAO,QAAQ,WAAW,GAAG,KAAK,IAAI;AAClH,QAAM,OACJ,qBAAC,OAAE,WAAU,6DAGX;AAAA,wBAAC,qBAAkB,WAAU,mBAAkB;AAAA,IAAE;AAAA,IAAE;AAAA,KACrD;AAEF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAa;AAAA,MACb,MAAK;AAAA,MACL,WAAW;AAAA,QACT,WACE;AAAA,QACF;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACA,WACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,UAAU,UAAU,SAAS,SAC7E;AAAA,8BAAC,eAAY,WAAW,GAAG,UAAU,YAAY,0BAA0B,GAAG;AAAA,UAC7E,OAAO,SAAS;AAAA,WACnB;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":["message","_variant","className","rest"]}
@@ -8,7 +8,7 @@ import {
8
8
  useNavGroupOpen
9
9
  } from "./nav-collapse.js";
10
10
  import { SectionLabel } from "./page-frame.js";
11
- const ITEM = "flex h-8 w-full items-center gap-2.5 rounded-(--control-radius) px-2.5 text-left text-body outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-40";
11
+ const ITEM = "flex h-(--control-h-sm) w-full items-center gap-2.5 rounded-row px-2.5 text-left text-body outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-(--disabled-opacity)";
12
12
  function SectionNav({
13
13
  groups,
14
14
  value,
@@ -100,8 +100,8 @@ function SectionNavRow({
100
100
  const Icon = item.icon;
101
101
  const className = cn(
102
102
  ITEM,
103
- active ? "bg-active text-ink" : "text-ink-mid hover:bg-hover",
104
- nested && "h-9"
103
+ active ? "bg-selected text-ink" : "text-ink-mid hover:bg-hover",
104
+ nested && "h-(--control-h-md)"
105
105
  );
106
106
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
107
107
  Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0 text-ink-soft" }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/section-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport {\n NavCollapse,\n NavGroupHeader,\n useNavGroupOpen,\n type NavCollapseLabels,\n type NavGroupCollapse,\n} from \"./nav-collapse\";\nimport { SectionLabel } from \"./page-frame\";\n\nexport interface SectionNavItem {\n id: string;\n label: React.ReactNode;\n icon?: React.ComponentType<{ className?: string }>;\n /** A link (`?section=…`, a sub-route) instead of a button calling `onValueChange`. */\n href?: string;\n /** After the label — a count, a status chip. */\n trailing?: React.ReactNode;\n disabled?: boolean;\n}\n\nexport interface SectionNavGroup extends NavGroupCollapse {\n id: string;\n label?: React.ReactNode;\n /** One line under the label saying what kind of thing lives here. */\n description?: React.ReactNode;\n items: readonly SectionNavItem[];\n}\n\nexport interface SectionNavLabels extends NavCollapseLabels {\n /** Accessible name of the nav. Default `\"Sections\"`. */\n nav?: string;\n}\n\nexport interface SectionNavProps extends Omit<React.ComponentProps<\"nav\">, \"children\"> {\n groups: readonly SectionNavGroup[];\n /** The current section's id. */\n value: string;\n /** Called by button items (those without `href`), and by link items on click. */\n onValueChange?: (id: string) => void;\n linkComponent?: React.ElementType;\n labels?: SectionNavLabels;\n /** Makes every labelled group collapsible; a group's own `collapsible` wins. Default `false`. */\n collapsible?: boolean;\n}\n\nconst ITEM =\n \"flex h-8 w-full items-center gap-2.5 rounded-(--control-radius) px-2.5 text-left text-body outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-40\";\n\n/**\n * A screen's own section rail — settings panes, a document's chapters: grouped\n * rows, one current (`aria-current`), each a button or a link. The shell's rail\n * is `ShellNav`; this one lives inside a page, so it carries a tint rather than\n * the rail's solid marker and does not slide.\n */\nexport function SectionNav({\n groups,\n value,\n onValueChange,\n linkComponent,\n labels = {},\n collapsible = false,\n className,\n ...props\n}: SectionNavProps) {\n return (\n <nav\n data-slot=\"section-nav\"\n aria-label={labels.nav ?? \"Sections\"}\n className={cn(\"flex w-64 shrink-0 flex-col gap-0.5\", className)}\n {...props}\n >\n {groups.map((group) => (\n <SectionNavGroupView\n key={group.id}\n group={group}\n value={value}\n onSelect={onValueChange}\n linkComponent={linkComponent}\n labels={labels}\n collapsible={collapsible}\n />\n ))}\n </nav>\n );\n}\n\nfunction SectionNavGroupView({\n group,\n value,\n onSelect,\n linkComponent,\n labels,\n collapsible,\n}: {\n group: SectionNavGroup;\n value: string;\n onSelect: ((id: string) => void) | undefined;\n linkComponent: React.ElementType | undefined;\n labels: SectionNavLabels;\n collapsible: boolean;\n}) {\n const labelId = React.useId();\n const regionId = React.useId();\n const [open, setOpen] = useNavGroupOpen(group, group.items.some((i) => i.id === value));\n const folds = (group.collapsible ?? collapsible) && group.label !== undefined;\n const rows = group.items.map((item) => (\n <SectionNavRow\n key={item.id}\n item={item}\n active={item.id === value}\n nested={folds}\n onSelect={onSelect}\n linkComponent={linkComponent}\n />\n ));\n if (folds) {\n return (\n <div className=\"flex flex-col pt-1.5 first:pt-0\">\n <NavGroupHeader\n label={group.label}\n labelId={labelId}\n regionId={regionId}\n open={open}\n onToggle={() => setOpen(!open)}\n href={group.href}\n linkComponent={linkComponent}\n labels={labels}\n />\n <NavCollapse open={open} id={regionId}>\n <div className=\"flex flex-col gap-0.5 pt-0.5 pl-3\">{rows}</div>\n </NavCollapse>\n </div>\n );\n }\n return (\n <div className=\"flex flex-col gap-0.5 pt-3.5 first:pt-0\">\n {(group.label !== undefined || group.description !== undefined) && (\n <div className=\"flex flex-col\">\n {group.label !== undefined && <SectionLabel className=\"px-2 py-1\">{group.label}</SectionLabel>}\n {group.description !== undefined && (\n <p className=\"px-2 text-caption text-ink-soft\">{group.description}</p>\n )}\n </div>\n )}\n {rows}\n </div>\n );\n}\n\nfunction SectionNavRow({\n item,\n active,\n nested,\n onSelect,\n linkComponent,\n}: {\n item: SectionNavItem;\n active: boolean;\n nested: boolean;\n onSelect: ((id: string) => void) | undefined;\n linkComponent: React.ElementType | undefined;\n}) {\n const Icon = item.icon;\n const className = cn(\n ITEM,\n active ? \"bg-active text-ink\" : \"text-ink-mid hover:bg-hover\",\n nested && \"h-9\",\n );\n const content = (\n <>\n {Icon && <Icon className=\"size-4 shrink-0 text-ink-soft\" />}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {item.trailing}\n </>\n );\n if (item.href !== undefined && !item.disabled) {\n const Link = linkComponent ?? \"a\";\n return (\n <Link\n href={item.href}\n aria-current={active ? \"page\" : undefined}\n className={className}\n onClick={() => onSelect?.(item.id)}\n >\n {content}\n </Link>\n );\n }\n return (\n <button\n type=\"button\"\n disabled={item.disabled}\n aria-current={active ? \"true\" : undefined}\n className={className}\n onClick={() => onSelect?.(item.id)}\n >\n {content}\n </button>\n );\n}\n"],"mappings":";AA4EQ,SAiGJ,UAjGI,KA6CF,YA7CE;AA1ER,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,oBAAoB;AAsC7B,MAAM,OACJ;AAQK,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,CAAC;AAAA,EACV,cAAc;AAAA,EACd;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY,OAAO,OAAO;AAAA,MAC1B,WAAW,GAAG,uCAAuC,SAAS;AAAA,MAC7D,GAAG;AAAA,MAEH,iBAAO,IAAI,CAAC,UACX;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA;AAAA;AAAA,QANK,MAAM;AAAA,MAOb,CACD;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,UAAU,MAAM,MAAM;AAC5B,QAAM,WAAW,MAAM,MAAM;AAC7B,QAAM,CAAC,MAAM,OAAO,IAAI,gBAAgB,OAAO,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,CAAC;AACtF,QAAM,SAAS,MAAM,eAAe,gBAAgB,MAAM,UAAU;AACpE,QAAM,OAAO,MAAM,MAAM,IAAI,CAAC,SAC5B;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA,QAAQ,KAAK,OAAO;AAAA,MACpB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA;AAAA,IALK,KAAK;AAAA,EAMZ,CACD;AACD,MAAI,OAAO;AACT,WACE,qBAAC,SAAI,WAAU,mCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,MAAM;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU,MAAM,QAAQ,CAAC,IAAI;AAAA,UAC7B,MAAM,MAAM;AAAA,UACZ;AAAA,UACA;AAAA;AAAA,MACF;AAAA,MACA,oBAAC,eAAY,MAAY,IAAI,UAC3B,8BAAC,SAAI,WAAU,qCAAqC,gBAAK,GAC3D;AAAA,OACF;AAAA,EAEJ;AACA,SACE,qBAAC,SAAI,WAAU,2CACX;AAAA,WAAM,UAAU,UAAa,MAAM,gBAAgB,WACnD,qBAAC,SAAI,WAAU,iBACZ;AAAA,YAAM,UAAU,UAAa,oBAAC,gBAAa,WAAU,aAAa,gBAAM,OAAM;AAAA,MAC9E,MAAM,gBAAgB,UACrB,oBAAC,OAAE,WAAU,mCAAmC,gBAAM,aAAY;AAAA,OAEtE;AAAA,IAED;AAAA,KACH;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,OAAO,KAAK;AAClB,QAAM,YAAY;AAAA,IAChB;AAAA,IACA,SAAS,uBAAuB;AAAA,IAChC,UAAU;AAAA,EACZ;AACA,QAAM,UACJ,iCACG;AAAA,YAAQ,oBAAC,QAAK,WAAU,iCAAgC;AAAA,IACzD,oBAAC,UAAK,WAAU,2BAA2B,eAAK,OAAM;AAAA,IACrD,KAAK;AAAA,KACR;AAEF,MAAI,KAAK,SAAS,UAAa,CAAC,KAAK,UAAU;AAC7C,UAAM,OAAO,iBAAiB;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,KAAK;AAAA,QACX,gBAAc,SAAS,SAAS;AAAA,QAChC;AAAA,QACA,SAAS,MAAM,WAAW,KAAK,EAAE;AAAA,QAEhC;AAAA;AAAA,IACH;AAAA,EAEJ;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU,KAAK;AAAA,MACf,gBAAc,SAAS,SAAS;AAAA,MAChC;AAAA,MACA,SAAS,MAAM,WAAW,KAAK,EAAE;AAAA,MAEhC;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/shell/section-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport {\n NavCollapse,\n NavGroupHeader,\n useNavGroupOpen,\n type NavCollapseLabels,\n type NavGroupCollapse,\n} from \"./nav-collapse\";\nimport { SectionLabel } from \"./page-frame\";\n\nexport interface SectionNavItem {\n id: string;\n label: React.ReactNode;\n icon?: React.ComponentType<{ className?: string }>;\n /** A link (`?section=…`, a sub-route) instead of a button calling `onValueChange`. */\n href?: string;\n /** After the label — a count, a status chip. */\n trailing?: React.ReactNode;\n disabled?: boolean;\n}\n\nexport interface SectionNavGroup extends NavGroupCollapse {\n id: string;\n label?: React.ReactNode;\n /** One line under the label saying what kind of thing lives here. */\n description?: React.ReactNode;\n items: readonly SectionNavItem[];\n}\n\nexport interface SectionNavLabels extends NavCollapseLabels {\n /** Accessible name of the nav. Default `\"Sections\"`. */\n nav?: string;\n}\n\nexport interface SectionNavProps extends Omit<React.ComponentProps<\"nav\">, \"children\"> {\n groups: readonly SectionNavGroup[];\n /** The current section's id. */\n value: string;\n /** Called by button items (those without `href`), and by link items on click. */\n onValueChange?: (id: string) => void;\n linkComponent?: React.ElementType;\n labels?: SectionNavLabels;\n /** Makes every labelled group collapsible; a group's own `collapsible` wins. Default `false`. */\n collapsible?: boolean;\n}\n\nconst ITEM =\n \"flex h-(--control-h-sm) w-full items-center gap-2.5 rounded-row px-2.5 text-left text-body outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-(--disabled-opacity)\";\n\n/**\n * A screen's own section rail — settings panes, a document's chapters: grouped\n * rows, one current (`aria-current`), each a button or a link. The shell's rail\n * is `ShellNav`; this one lives inside a page, so it carries a tint rather than\n * the rail's solid marker and does not slide.\n */\nexport function SectionNav({\n groups,\n value,\n onValueChange,\n linkComponent,\n labels = {},\n collapsible = false,\n className,\n ...props\n}: SectionNavProps) {\n return (\n <nav\n data-slot=\"section-nav\"\n aria-label={labels.nav ?? \"Sections\"}\n className={cn(\"flex w-64 shrink-0 flex-col gap-0.5\", className)}\n {...props}\n >\n {groups.map((group) => (\n <SectionNavGroupView\n key={group.id}\n group={group}\n value={value}\n onSelect={onValueChange}\n linkComponent={linkComponent}\n labels={labels}\n collapsible={collapsible}\n />\n ))}\n </nav>\n );\n}\n\nfunction SectionNavGroupView({\n group,\n value,\n onSelect,\n linkComponent,\n labels,\n collapsible,\n}: {\n group: SectionNavGroup;\n value: string;\n onSelect: ((id: string) => void) | undefined;\n linkComponent: React.ElementType | undefined;\n labels: SectionNavLabels;\n collapsible: boolean;\n}) {\n const labelId = React.useId();\n const regionId = React.useId();\n const [open, setOpen] = useNavGroupOpen(group, group.items.some((i) => i.id === value));\n const folds = (group.collapsible ?? collapsible) && group.label !== undefined;\n const rows = group.items.map((item) => (\n <SectionNavRow\n key={item.id}\n item={item}\n active={item.id === value}\n nested={folds}\n onSelect={onSelect}\n linkComponent={linkComponent}\n />\n ));\n if (folds) {\n return (\n <div className=\"flex flex-col pt-1.5 first:pt-0\">\n <NavGroupHeader\n label={group.label}\n labelId={labelId}\n regionId={regionId}\n open={open}\n onToggle={() => setOpen(!open)}\n href={group.href}\n linkComponent={linkComponent}\n labels={labels}\n />\n <NavCollapse open={open} id={regionId}>\n <div className=\"flex flex-col gap-0.5 pt-0.5 pl-3\">{rows}</div>\n </NavCollapse>\n </div>\n );\n }\n return (\n <div className=\"flex flex-col gap-0.5 pt-3.5 first:pt-0\">\n {(group.label !== undefined || group.description !== undefined) && (\n <div className=\"flex flex-col\">\n {group.label !== undefined && <SectionLabel className=\"px-2 py-1\">{group.label}</SectionLabel>}\n {group.description !== undefined && (\n <p className=\"px-2 text-caption text-ink-soft\">{group.description}</p>\n )}\n </div>\n )}\n {rows}\n </div>\n );\n}\n\nfunction SectionNavRow({\n item,\n active,\n nested,\n onSelect,\n linkComponent,\n}: {\n item: SectionNavItem;\n active: boolean;\n nested: boolean;\n onSelect: ((id: string) => void) | undefined;\n linkComponent: React.ElementType | undefined;\n}) {\n const Icon = item.icon;\n const className = cn(\n ITEM,\n active ? \"bg-selected text-ink\" : \"text-ink-mid hover:bg-hover\",\n nested && \"h-(--control-h-md)\",\n );\n const content = (\n <>\n {Icon && <Icon className=\"size-4 shrink-0 text-ink-soft\" />}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {item.trailing}\n </>\n );\n if (item.href !== undefined && !item.disabled) {\n const Link = linkComponent ?? \"a\";\n return (\n <Link\n href={item.href}\n aria-current={active ? \"page\" : undefined}\n className={className}\n onClick={() => onSelect?.(item.id)}\n >\n {content}\n </Link>\n );\n }\n return (\n <button\n type=\"button\"\n disabled={item.disabled}\n aria-current={active ? \"true\" : undefined}\n className={className}\n onClick={() => onSelect?.(item.id)}\n >\n {content}\n </button>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { iconShapes } from "../../icons/icon-shapes.js";
3
4
  function Glyph({ className, children }) {
4
5
  return /* @__PURE__ */ jsx(
5
6
  "svg",
@@ -17,35 +18,19 @@ function Glyph({ className, children }) {
17
18
  );
18
19
  }
19
20
  function ChevronLeftIcon({ className }) {
20
- return /* @__PURE__ */ jsx(Glyph, { className, children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) });
21
+ return /* @__PURE__ */ jsx(Glyph, { className, children: iconShapes("chevron-left") });
21
22
  }
22
23
  function TriangleAlertIcon({ className }) {
23
- return /* @__PURE__ */ jsxs(Glyph, { className, children: [
24
- /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
25
- /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
26
- /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
27
- ] });
24
+ return /* @__PURE__ */ jsx(Glyph, { className, children: iconShapes("triangle-alert") });
28
25
  }
29
26
  function InfoIcon({ className }) {
30
- return /* @__PURE__ */ jsxs(Glyph, { className, children: [
31
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
32
- /* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
33
- /* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
34
- ] });
27
+ return /* @__PURE__ */ jsx(Glyph, { className, children: iconShapes("info") });
35
28
  }
36
29
  function RefreshIcon({ className }) {
37
- return /* @__PURE__ */ jsxs(Glyph, { className, children: [
38
- /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
39
- /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" }),
40
- /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
41
- /* @__PURE__ */ jsx("path", { d: "M8 16H3v5" })
42
- ] });
30
+ return /* @__PURE__ */ jsx(Glyph, { className, children: iconShapes("refresh") });
43
31
  }
44
32
  function CloseIcon({ className }) {
45
- return /* @__PURE__ */ jsxs(Glyph, { className, children: [
46
- /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
47
- /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
48
- ] });
33
+ return /* @__PURE__ */ jsx(Glyph, { className, children: iconShapes("close") });
49
34
  }
50
35
  export {
51
36
  ChevronLeftIcon,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/shell-icons.tsx"],"sourcesContent":["import * as React from \"react\";\n\n// lucide glyphs the shell needs, inlined so the kit keeps its zero-icon-dep footprint.\nfunction Glyph({ className, children }: { className?: string | undefined; children: React.ReactNode }) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n {children}\n </svg>\n );\n}\n\nexport function ChevronLeftIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"m15 18-6-6 6-6\" />\n </Glyph>\n );\n}\n\nexport function TriangleAlertIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </Glyph>\n );\n}\n\nexport function InfoIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </Glyph>\n );\n}\n\nexport function RefreshIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8\" />\n <path d=\"M21 3v5h-5\" />\n <path d=\"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16\" />\n <path d=\"M8 16H3v5\" />\n </Glyph>\n );\n}\n\nexport function CloseIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </Glyph>\n );\n}\n"],"mappings":"AAKI,cAyBA,YAzBA;AALJ,YAAY,WAAW;AAGvB,SAAS,MAAM,EAAE,WAAW,SAAS,GAAkE;AACrG,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa;AAAA,MACb,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAW;AAAA,MAEV;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,gBAAgB,EAAE,UAAU,GAAuC;AACjF,SACE,oBAAC,SAAM,WACL,8BAAC,UAAK,GAAE,kBAAiB,GAC3B;AAEJ;AAEO,SAAS,kBAAkB,EAAE,UAAU,GAAuC;AACnF,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,UAAK,GAAE,4EAA2E;AAAA,IACnF,oBAAC,UAAK,GAAE,WAAU;AAAA,IAClB,oBAAC,UAAK,GAAE,cAAa;AAAA,KACvB;AAEJ;AAEO,SAAS,SAAS,EAAE,UAAU,GAAuC;AAC1E,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,IAC/B,oBAAC,UAAK,GAAE,aAAY;AAAA,IACpB,oBAAC,UAAK,GAAE,aAAY;AAAA,KACtB;AAEJ;AAEO,SAAS,YAAY,EAAE,UAAU,GAAuC;AAC7E,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,UAAK,GAAE,sDAAqD;AAAA,IAC7D,oBAAC,UAAK,GAAE,cAAa;AAAA,IACrB,oBAAC,UAAK,GAAE,uDAAsD;AAAA,IAC9D,oBAAC,UAAK,GAAE,aAAY;AAAA,KACtB;AAEJ;AAEO,SAAS,UAAU,EAAE,UAAU,GAAuC;AAC3E,SACE,qBAAC,SAAM,WACL;AAAA,wBAAC,UAAK,GAAE,cAAa;AAAA,IACrB,oBAAC,UAAK,GAAE,cAAa;AAAA,KACvB;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/shell/shell-icons.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { iconShapes } from \"../../icons/icon-shapes\";\n\nfunction Glyph({ className, children }: { className?: string | undefined; children: React.ReactNode }) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n {children}\n </svg>\n );\n}\n\nexport function ChevronLeftIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n {iconShapes(\"chevron-left\")}\n </Glyph>\n );\n}\n\nexport function TriangleAlertIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n {iconShapes(\"triangle-alert\")}\n </Glyph>\n );\n}\n\nexport function InfoIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n {iconShapes(\"info\")}\n </Glyph>\n );\n}\n\nexport function RefreshIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n {iconShapes(\"refresh\")}\n </Glyph>\n );\n}\n\nexport function CloseIcon({ className }: { className?: string | undefined }) {\n return (\n <Glyph className={className}>\n {iconShapes(\"close\")}\n </Glyph>\n );\n}\n"],"mappings":"AAKI;AALJ,YAAY,WAAW;AACvB,SAAS,kBAAkB;AAE3B,SAAS,MAAM,EAAE,WAAW,SAAS,GAAkE;AACrG,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa;AAAA,MACb,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAW;AAAA,MAEV;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,gBAAgB,EAAE,UAAU,GAAuC;AACjF,SACE,oBAAC,SAAM,WACJ,qBAAW,cAAc,GAC5B;AAEJ;AAEO,SAAS,kBAAkB,EAAE,UAAU,GAAuC;AACnF,SACE,oBAAC,SAAM,WACJ,qBAAW,gBAAgB,GAC9B;AAEJ;AAEO,SAAS,SAAS,EAAE,UAAU,GAAuC;AAC1E,SACE,oBAAC,SAAM,WACJ,qBAAW,MAAM,GACpB;AAEJ;AAEO,SAAS,YAAY,EAAE,UAAU,GAAuC;AAC7E,SACE,oBAAC,SAAM,WACJ,qBAAW,SAAS,GACvB;AAEJ;AAEO,SAAS,UAAU,EAAE,UAAU,GAAuC;AAC3E,SACE,oBAAC,SAAM,WACJ,qBAAW,OAAO,GACrB;AAEJ;","names":[]}
@@ -11,7 +11,7 @@ import {
11
11
  } from "./nav-model.js";
12
12
  import { usePendingNavigation, useSlideMarker } from "./use-shell-nav.js";
13
13
  const NAV_FOCUS = "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary";
14
- const ROW = "flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors";
14
+ const ROW = "flex items-center gap-3 rounded-row px-3 py-2 text-sm transition-colors";
15
15
  function ShellNav({
16
16
  groups,
17
17
  footerGroups = [],
@@ -48,7 +48,7 @@ function ShellNav({
48
48
  {
49
49
  "data-slot": "shell-nav",
50
50
  className: cn(
51
- "flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-secondary px-4.5 pb-5 pt-6",
51
+ "flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-card px-4.5 pb-5 pt-6",
52
52
  className
53
53
  ),
54
54
  ...props,
@@ -85,7 +85,7 @@ function ShellNavSection({
85
85
  {
86
86
  "data-slot": "shell-nav-marker",
87
87
  "aria-hidden": true,
88
- className: "pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary"
88
+ className: "pointer-events-none absolute left-0 top-0 -z-10 rounded-row bg-primary"
89
89
  }
90
90
  ),
91
91
  /* @__PURE__ */ jsx(
@@ -159,7 +159,7 @@ function ShellNavRow({
159
159
  ROW,
160
160
  NAV_FOCUS,
161
161
  state.active ? "font-semibold text-on-primary" : cn("font-medium hover:bg-hover", nested ? "text-ink-mid" : "text-ink"),
162
- item.disabled && "cursor-not-allowed opacity-50 hover:bg-transparent"
162
+ item.disabled && "cursor-not-allowed opacity-(--disabled-opacity) hover:bg-transparent"
163
163
  ),
164
164
  children: row.renderItem ? row.renderItem(item, state) : /* @__PURE__ */ jsxs(Fragment, { children: [
165
165
  Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4.5 shrink-0" }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/shell-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { NavBadge } from \"./nav-badge\";\nimport { labelTitle, NavLink, type NavLinkCallbacks } from \"./nav-link\";\nimport { NavCollapse, NavGroupHeader, useNavGroupOpen, type NavCollapseLabels } from \"./nav-collapse\";\nimport {\n navItemMatch,\n resolveActiveNavItem,\n type NavGroup,\n type NavItem,\n type NavItemState,\n} from \"./nav-model\";\nimport { usePendingNavigation, useSlideMarker } from \"./use-shell-nav\";\n\nexport interface ShellNavLabels extends NavCollapseLabels {\n /** Accessible name of the nav holding `groups`. Default `\"Main\"`. */\n primary?: string;\n /** Accessible name of the nav holding `footerGroups`. Default `\"Account\"`. */\n footer?: string;\n /** Spoken text of a numeric badge, given the real count. Default `\"{n} new\"`. */\n badge?: (count: number) => string;\n}\n\nexport interface ShellNavProps\n extends Omit<React.ComponentProps<\"aside\">, \"children\">,\n NavLinkCallbacks {\n groups: readonly NavGroup[];\n /** Pinned to the bottom of the rail (profile, notifications, settings). */\n footerGroups?: readonly NavGroup[];\n /** The router's current path — `usePathname()` in Next. */\n pathname: string;\n /** Your router's link (`next/link`); defaults to a plain `<a>`. */\n linkComponent?: React.ElementType;\n /** Above the groups — the brand lock-up. */\n header?: React.ReactNode;\n /** Below the footer groups — the signed-in account, a sign-out. */\n footer?: React.ReactNode;\n /** Replaces a row's content (icon, label, badge); the link, `aria-current` and the marker stay the kit's. */\n renderItem?: (item: NavItem, state: NavItemState) => React.ReactNode;\n labels?: ShellNavLabels;\n /** Makes every labelled group collapsible; a group's own `collapsible` wins. Default `false`. */\n collapsible?: boolean;\n}\n\n// The ring is the solid token, never a tint — at 50% it composites under the 3:1\n// non-text floor. The offset keeps it legible around the marked row, whose fill\n// is that same hue.\nconst NAV_FOCUS =\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary\";\nconst ROW = \"flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors\";\n\n/**\n * The app's left rail: data-driven groups, a footer block pinned to the bottom,\n * one sliding marker per group, and an optimistic mark on click so the row\n * answers the click rather than the router's round-trip.\n *\n * Exactly one row across the rail is current — the strongest claim on the path\n * (see {@link resolveActiveNavItem}) — so overlapping `prefix` items are safe.\n * Visibility (roles, flags) is the caller's: pass only the items to show.\n */\nexport function ShellNav({\n groups,\n footerGroups = [],\n pathname,\n linkComponent,\n header,\n footer,\n renderItem,\n onItemIntent,\n onNavigate,\n labels = {},\n collapsible = false,\n className,\n ...props\n}: ShellNavProps) {\n const [pendingId, markPending] = usePendingNavigation(pathname);\n const all = [...groups, ...footerGroups].flatMap((g) => g.items);\n const currentId =\n (pendingId !== null && all.some((i) => i.id === pendingId) ? pendingId : undefined) ??\n resolveActiveNavItem(all, pathname)?.id;\n const section = (group: NavGroup) => (\n <ShellNavSection\n key={group.id}\n group={group}\n currentId={currentId}\n pendingId={pendingId}\n pathname={pathname}\n labels={labels}\n collapsible={collapsible}\n row={{ linkComponent, markPending, onItemIntent, onNavigate, renderItem, badgeLabel: labels.badge }}\n />\n );\n return (\n <aside\n data-slot=\"shell-nav\"\n className={cn(\n \"flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-secondary px-4.5 pb-5 pt-6\",\n className,\n )}\n {...props}\n >\n {header}\n <nav aria-label={labels.primary ?? \"Main\"} className=\"flex flex-col gap-4.5\">\n {groups.map(section)}\n </nav>\n <div className=\"flex-1\" />\n {footerGroups.length > 0 && (\n <nav aria-label={labels.footer ?? \"Account\"} className=\"flex flex-col gap-4.5\">\n {footerGroups.map(section)}\n </nav>\n )}\n {footer}\n </aside>\n );\n}\n\ninterface RowContext extends NavLinkCallbacks {\n linkComponent: React.ElementType | undefined;\n markPending: (id: string) => void;\n renderItem: ShellNavProps[\"renderItem\"];\n badgeLabel: ((count: number) => string) | undefined;\n}\n\n// One marker node per SECTION, mounted once and moved — not one per rail: a\n// marker travelling between groups would cross headings that have nothing to do\n// with either row. Crossing groups unmounts one marker and mounts another (a\n// fade). `isolate` is load-bearing: the marker sits on a negative z-index and\n// without its own stacking context would land behind the rail's background.\n// The marker lives in the BODY, below the header, so a folding section clips it\n// together with its rows instead of leaving it painted over the next group.\nfunction ShellNavSection({\n group,\n currentId,\n pendingId,\n pathname,\n row,\n labels,\n collapsible,\n}: {\n group: NavGroup;\n currentId: string | undefined;\n pendingId: string | null;\n pathname: string;\n row: RowContext;\n labels: ShellNavLabels;\n collapsible: boolean;\n}) {\n const body = React.useRef<HTMLDivElement>(null);\n const labelId = React.useId();\n const regionId = React.useId();\n const at = group.items.some((i) => i.id === currentId) ? (currentId ?? null) : null;\n const [open, setOpen] = useNavGroupOpen(group, at !== null);\n useSlideMarker(body, at);\n const folds = (group.collapsible ?? collapsible) && group.label !== undefined;\n const landing =\n group.href !== undefined &&\n navItemMatch({ id: group.id, href: group.href, label: \"\", match: \"exact\" }, pathname) !== null;\n const content = (\n <div ref={body} className={cn(\"relative isolate\", folds && \"pt-1\")}>\n {/* First in DOM order: the pre-hydration fill in motion.css reaches the marked\n row through a sibling combinator, which only looks forward. */}\n {at !== null && (\n <span\n data-slot=\"shell-nav-marker\"\n aria-hidden\n className=\"pointer-events-none absolute left-0 top-0 -z-10 rounded-lg bg-primary\"\n />\n )}\n <ul\n aria-labelledby={group.label !== undefined ? labelId : undefined}\n className={cn(\"flex flex-col gap-1\", folds && \"pl-3\")}\n >\n {group.items.map((item) => (\n <li key={item.id} className=\"flex flex-col\">\n <ShellNavRow\n item={item}\n nested={folds}\n state={{ active: item.id === currentId, pending: item.id === pendingId }}\n row={row}\n />\n </li>\n ))}\n </ul>\n </div>\n );\n if (!folds) {\n return (\n <div\n data-slot=\"shell-nav-section\"\n className={cn(\"flex flex-col gap-1\", group.label !== undefined && \"pl-1\")}\n >\n {group.label !== undefined && (\n <p id={labelId} className=\"mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft\">\n {group.label}\n </p>\n )}\n {content}\n </div>\n );\n }\n return (\n <div data-slot=\"shell-nav-section\" data-state={open ? \"open\" : \"closed\"} className=\"flex flex-col\">\n <NavGroupHeader\n label={group.label}\n labelId={labelId}\n regionId={regionId}\n open={open}\n onToggle={() => setOpen(!open)}\n href={group.href}\n linkComponent={row.linkComponent}\n current={landing}\n labels={labels}\n />\n <NavCollapse open={open} id={regionId}>\n {content}\n </NavCollapse>\n </div>\n );\n}\n\nfunction ShellNavRow({\n item,\n state,\n row,\n nested,\n}: {\n item: NavItem;\n state: NavItemState;\n row: RowContext;\n nested: boolean;\n}) {\n const Icon = item.icon;\n const trailing = typeof item.trailing === \"function\" ? item.trailing(state) : item.trailing;\n return (\n <NavLink\n item={item}\n current={state.active}\n pending={state.pending}\n linkComponent={row.linkComponent}\n markPending={row.markPending}\n onItemIntent={row.onItemIntent}\n onNavigate={row.onNavigate}\n title={labelTitle(item.label)}\n className={cn(\n ROW,\n NAV_FOCUS,\n state.active\n ? \"font-semibold text-on-primary\"\n : cn(\"font-medium hover:bg-hover\", nested ? \"text-ink-mid\" : \"text-ink\"),\n item.disabled && \"cursor-not-allowed opacity-50 hover:bg-transparent\",\n )}\n >\n {row.renderItem ? (\n row.renderItem(item, state)\n ) : (\n <>\n {Icon && <Icon className=\"size-4.5 shrink-0\" />}\n {/* `min-w-0`: without it the label refuses to shrink below its own text and\n the rail widens to the longest translation instead of truncating. */}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {typeof item.badge === \"number\" ? (\n <NavBadge\n count={item.badge}\n active={state.active}\n {...(row.badgeLabel ? { label: row.badgeLabel } : {})}\n />\n ) : (\n item.badge\n )}\n {trailing}\n </>\n )}\n </NavLink>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/shell/shell-nav.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { NavBadge } from \"./nav-badge\";\nimport { labelTitle, NavLink, type NavLinkCallbacks } from \"./nav-link\";\nimport { NavCollapse, NavGroupHeader, useNavGroupOpen, type NavCollapseLabels } from \"./nav-collapse\";\nimport {\n navItemMatch,\n resolveActiveNavItem,\n type NavGroup,\n type NavItem,\n type NavItemState,\n} from \"./nav-model\";\nimport { usePendingNavigation, useSlideMarker } from \"./use-shell-nav\";\n\nexport interface ShellNavLabels extends NavCollapseLabels {\n /** Accessible name of the nav holding `groups`. Default `\"Main\"`. */\n primary?: string;\n /** Accessible name of the nav holding `footerGroups`. Default `\"Account\"`. */\n footer?: string;\n /** Spoken text of a numeric badge, given the real count. Default `\"{n} new\"`. */\n badge?: (count: number) => string;\n}\n\nexport interface ShellNavProps\n extends Omit<React.ComponentProps<\"aside\">, \"children\">,\n NavLinkCallbacks {\n groups: readonly NavGroup[];\n /** Pinned to the bottom of the rail (profile, notifications, settings). */\n footerGroups?: readonly NavGroup[];\n /** The router's current path — `usePathname()` in Next. */\n pathname: string;\n /** Your router's link (`next/link`); defaults to a plain `<a>`. */\n linkComponent?: React.ElementType;\n /** Above the groups — the brand lock-up. */\n header?: React.ReactNode;\n /** Below the footer groups — the signed-in account, a sign-out. */\n footer?: React.ReactNode;\n /** Replaces a row's content (icon, label, badge); the link, `aria-current` and the marker stay the kit's. */\n renderItem?: (item: NavItem, state: NavItemState) => React.ReactNode;\n labels?: ShellNavLabels;\n /** Makes every labelled group collapsible; a group's own `collapsible` wins. Default `false`. */\n collapsible?: boolean;\n}\n\n// The ring is the solid token, never a tint — at 50% it composites under the 3:1\n// non-text floor. The offset keeps it legible around the marked row, whose fill\n// is that same hue.\nconst NAV_FOCUS =\n \"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-secondary\";\nconst ROW = \"flex items-center gap-3 rounded-row px-3 py-2 text-sm transition-colors\";\n\n/**\n * The app's left rail: data-driven groups, a footer block pinned to the bottom,\n * one sliding marker per group, and an optimistic mark on click so the row\n * answers the click rather than the router's round-trip.\n *\n * Exactly one row across the rail is current — the strongest claim on the path\n * (see {@link resolveActiveNavItem}) — so overlapping `prefix` items are safe.\n * Visibility (roles, flags) is the caller's: pass only the items to show.\n */\nexport function ShellNav({\n groups,\n footerGroups = [],\n pathname,\n linkComponent,\n header,\n footer,\n renderItem,\n onItemIntent,\n onNavigate,\n labels = {},\n collapsible = false,\n className,\n ...props\n}: ShellNavProps) {\n const [pendingId, markPending] = usePendingNavigation(pathname);\n const all = [...groups, ...footerGroups].flatMap((g) => g.items);\n const currentId =\n (pendingId !== null && all.some((i) => i.id === pendingId) ? pendingId : undefined) ??\n resolveActiveNavItem(all, pathname)?.id;\n const section = (group: NavGroup) => (\n <ShellNavSection\n key={group.id}\n group={group}\n currentId={currentId}\n pendingId={pendingId}\n pathname={pathname}\n labels={labels}\n collapsible={collapsible}\n row={{ linkComponent, markPending, onItemIntent, onNavigate, renderItem, badgeLabel: labels.badge }}\n />\n );\n return (\n <aside\n data-slot=\"shell-nav\"\n className={cn(\n \"flex h-full w-(--shell-rail-w) flex-col gap-7 overflow-y-auto border-r border-border bg-card px-4.5 pb-5 pt-6\",\n className,\n )}\n {...props}\n >\n {header}\n <nav aria-label={labels.primary ?? \"Main\"} className=\"flex flex-col gap-4.5\">\n {groups.map(section)}\n </nav>\n <div className=\"flex-1\" />\n {footerGroups.length > 0 && (\n <nav aria-label={labels.footer ?? \"Account\"} className=\"flex flex-col gap-4.5\">\n {footerGroups.map(section)}\n </nav>\n )}\n {footer}\n </aside>\n );\n}\n\ninterface RowContext extends NavLinkCallbacks {\n linkComponent: React.ElementType | undefined;\n markPending: (id: string) => void;\n renderItem: ShellNavProps[\"renderItem\"];\n badgeLabel: ((count: number) => string) | undefined;\n}\n\n// One marker node per SECTION, mounted once and moved — not one per rail: a\n// marker travelling between groups would cross headings that have nothing to do\n// with either row. Crossing groups unmounts one marker and mounts another (a\n// fade). `isolate` is load-bearing: the marker sits on a negative z-index and\n// without its own stacking context would land behind the rail's background.\n// The marker lives in the BODY, below the header, so a folding section clips it\n// together with its rows instead of leaving it painted over the next group.\nfunction ShellNavSection({\n group,\n currentId,\n pendingId,\n pathname,\n row,\n labels,\n collapsible,\n}: {\n group: NavGroup;\n currentId: string | undefined;\n pendingId: string | null;\n pathname: string;\n row: RowContext;\n labels: ShellNavLabels;\n collapsible: boolean;\n}) {\n const body = React.useRef<HTMLDivElement>(null);\n const labelId = React.useId();\n const regionId = React.useId();\n const at = group.items.some((i) => i.id === currentId) ? (currentId ?? null) : null;\n const [open, setOpen] = useNavGroupOpen(group, at !== null);\n useSlideMarker(body, at);\n const folds = (group.collapsible ?? collapsible) && group.label !== undefined;\n const landing =\n group.href !== undefined &&\n navItemMatch({ id: group.id, href: group.href, label: \"\", match: \"exact\" }, pathname) !== null;\n const content = (\n <div ref={body} className={cn(\"relative isolate\", folds && \"pt-1\")}>\n {/* First in DOM order: the pre-hydration fill in motion.css reaches the marked\n row through a sibling combinator, which only looks forward. */}\n {at !== null && (\n <span\n data-slot=\"shell-nav-marker\"\n aria-hidden\n className=\"pointer-events-none absolute left-0 top-0 -z-10 rounded-row bg-primary\"\n />\n )}\n <ul\n aria-labelledby={group.label !== undefined ? labelId : undefined}\n className={cn(\"flex flex-col gap-1\", folds && \"pl-3\")}\n >\n {group.items.map((item) => (\n <li key={item.id} className=\"flex flex-col\">\n <ShellNavRow\n item={item}\n nested={folds}\n state={{ active: item.id === currentId, pending: item.id === pendingId }}\n row={row}\n />\n </li>\n ))}\n </ul>\n </div>\n );\n if (!folds) {\n return (\n <div\n data-slot=\"shell-nav-section\"\n className={cn(\"flex flex-col gap-1\", group.label !== undefined && \"pl-1\")}\n >\n {group.label !== undefined && (\n <p id={labelId} className=\"mb-0.5 text-xs font-semibold uppercase tracking-widest text-ink-soft\">\n {group.label}\n </p>\n )}\n {content}\n </div>\n );\n }\n return (\n <div data-slot=\"shell-nav-section\" data-state={open ? \"open\" : \"closed\"} className=\"flex flex-col\">\n <NavGroupHeader\n label={group.label}\n labelId={labelId}\n regionId={regionId}\n open={open}\n onToggle={() => setOpen(!open)}\n href={group.href}\n linkComponent={row.linkComponent}\n current={landing}\n labels={labels}\n />\n <NavCollapse open={open} id={regionId}>\n {content}\n </NavCollapse>\n </div>\n );\n}\n\nfunction ShellNavRow({\n item,\n state,\n row,\n nested,\n}: {\n item: NavItem;\n state: NavItemState;\n row: RowContext;\n nested: boolean;\n}) {\n const Icon = item.icon;\n const trailing = typeof item.trailing === \"function\" ? item.trailing(state) : item.trailing;\n return (\n <NavLink\n item={item}\n current={state.active}\n pending={state.pending}\n linkComponent={row.linkComponent}\n markPending={row.markPending}\n onItemIntent={row.onItemIntent}\n onNavigate={row.onNavigate}\n title={labelTitle(item.label)}\n className={cn(\n ROW,\n NAV_FOCUS,\n state.active\n ? \"font-semibold text-on-primary\"\n : cn(\"font-medium hover:bg-hover\", nested ? \"text-ink-mid\" : \"text-ink\"),\n item.disabled && \"cursor-not-allowed opacity-(--disabled-opacity) hover:bg-transparent\",\n )}\n >\n {row.renderItem ? (\n row.renderItem(item, state)\n ) : (\n <>\n {Icon && <Icon className=\"size-4.5 shrink-0\" />}\n {/* `min-w-0`: without it the label refuses to shrink below its own text and\n the rail widens to the longest translation instead of truncating. */}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n {typeof item.badge === \"number\" ? (\n <NavBadge\n count={item.badge}\n active={state.active}\n {...(row.badgeLabel ? { label: row.badgeLabel } : {})}\n />\n ) : (\n item.badge\n )}\n {trailing}\n </>\n )}\n </NavLink>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/shell/smart-header.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport {\n SMART_HEADER_BAR,\n SMART_HEADER_BASE,\n SMART_HEADER_CONTEXT,\n SMART_HEADER_TOOLBAR,\n} from \"../../generated/smart-header\";\nimport { TopBar } from \"./top-bar\";\n\nexport type SmartHeaderState = \"default\" | \"collapsed\" | \"banner\" | \"search\";\n\nexport interface SmartHeaderProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /**\n * `default` — bar, toolbar and context line; `collapsed` — the toolbar folds\n * into the bar as `collapsed`; `banner` — a banner row above one bar;\n * `search` — one bar with the `palette` under it.\n */\n state?: SmartHeaderState;\n /** Brand, workspace tabs: the left side of the bar. */\n start?: React.ReactNode;\n /** The right side of the bar: icon actions, balance, bell, account menu. */\n end?: React.ReactNode;\n /** The folded toolbar, between `start` and `end`; shown in `collapsed` only. */\n collapsed?: React.ReactNode;\n /** A `SystemBanner`; shown in `banner` only. */\n banner?: React.ReactNode;\n /** The second row, 44 high; shown in `default` only. */\n toolbar?: React.ReactNode;\n /** The slim line on the page fill under the toolbar; shown in `default` only. */\n context?: React.ReactNode;\n /** A `CommandPalette` or any panel that drops from the bar; shown in `search` only. */\n palette?: React.ReactNode;\n}\n\n/**\n * A page header that adapts to scroll and alerts. The bar is the same in every\n * state, so switching never moves the brand or the account: only the rows\n * around it come and go. Layout only; the caller owns the state and its trigger\n * (a scroll observer, a feed status, the search button).\n */\nexport function SmartHeader({\n state = \"default\",\n start,\n end,\n collapsed,\n banner,\n toolbar,\n context,\n palette,\n className,\n ...props\n}: SmartHeaderProps) {\n return (\n <div\n data-slot=\"smart-header\"\n data-state={state}\n className={cn(SMART_HEADER_BASE, className)}\n {...props}\n >\n {state === \"banner\" && banner}\n <TopBar\n className={SMART_HEADER_BAR}\n start={\n <>\n {start}\n {state === \"collapsed\" && collapsed}\n </>\n }\n end={end}\n />\n {state === \"default\" && toolbar != null && (\n <div data-slot=\"smart-header-toolbar\" className={SMART_HEADER_TOOLBAR}>\n {toolbar}\n </div>\n )}\n {state === \"default\" && context != null && (\n <div data-slot=\"smart-header-context\" className={SMART_HEADER_CONTEXT}>\n {context}\n </div>\n )}\n {state === \"search\" && palette != null && <div data-slot=\"smart-header-palette\">{palette}</div>}\n </div>\n );\n}\n"],"mappings":"AA6DM,SAGI,UAHJ,KAGI,YAHJ;AA7DN,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc;AAiChB,SAAS,YAAY;AAAA,EAC1B,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAqB;AACnB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW,GAAG,mBAAmB,SAAS;AAAA,MACzC,GAAG;AAAA,MAEH;AAAA,kBAAU,YAAY;AAAA,QACvB;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,YACX,OACE,iCACG;AAAA;AAAA,cACA,UAAU,eAAe;AAAA,eAC5B;AAAA,YAEF;AAAA;AAAA,QACF;AAAA,QACC,UAAU,aAAa,WAAW,QACjC,oBAAC,SAAI,aAAU,wBAAuB,WAAW,sBAC9C,mBACH;AAAA,QAED,UAAU,aAAa,WAAW,QACjC,oBAAC,SAAI,aAAU,wBAAuB,WAAW,sBAC9C,mBACH;AAAA,QAED,UAAU,YAAY,WAAW,QAAQ,oBAAC,SAAI,aAAU,wBAAwB,mBAAQ;AAAA;AAAA;AAAA,EAC3F;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/shell/smart-header.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport {\n SMART_HEADER_BAR,\n SMART_HEADER_BASE,\n SMART_HEADER_CONTEXT,\n SMART_HEADER_TOOLBAR,\n} from \"../../generated/smart-header\";\nimport { TopBar } from \"./top-bar\";\nimport type { SmartHeaderState } from \"../../generated/smart-header\";\n\nexport type { SmartHeaderState };\n\nexport interface SmartHeaderProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n /**\n * `default` — bar, toolbar and context line; `collapsed` — the toolbar folds\n * into the bar as `collapsed`; `banner` — a banner row above one bar;\n * `search` — one bar with the `palette` under it.\n */\n state?: SmartHeaderState;\n /** Brand, workspace tabs: the left side of the bar. */\n start?: React.ReactNode;\n /** The right side of the bar: icon actions, balance, bell, account menu. */\n end?: React.ReactNode;\n /** The folded toolbar, between `start` and `end`; shown in `collapsed` only. */\n collapsed?: React.ReactNode;\n /** A `SystemBanner`; shown in `banner` only. */\n banner?: React.ReactNode;\n /** The second row, 44 high; shown in `default` only. */\n toolbar?: React.ReactNode;\n /** The slim line on the page fill under the toolbar; shown in `default` only. */\n context?: React.ReactNode;\n /** A `CommandPalette` or any panel that drops from the bar; shown in `search` only. */\n palette?: React.ReactNode;\n}\n\n/**\n * A page header that adapts to scroll and alerts. The bar is the same in every\n * state, so switching never moves the brand or the account: only the rows\n * around it come and go. Layout only; the caller owns the state and its trigger\n * (a scroll observer, a feed status, the search button).\n */\nexport function SmartHeader({\n state = \"default\",\n start,\n end,\n collapsed,\n banner,\n toolbar,\n context,\n palette,\n className,\n ...props\n}: SmartHeaderProps) {\n return (\n <div\n data-slot=\"smart-header\"\n data-state={state}\n className={cn(SMART_HEADER_BASE, className)}\n {...props}\n >\n {state === \"banner\" && banner}\n <TopBar\n className={SMART_HEADER_BAR}\n start={\n <>\n {start}\n {state === \"collapsed\" && collapsed}\n </>\n }\n end={end}\n />\n {state === \"default\" && toolbar != null && (\n <div data-slot=\"smart-header-toolbar\" className={SMART_HEADER_TOOLBAR}>\n {toolbar}\n </div>\n )}\n {state === \"default\" && context != null && (\n <div data-slot=\"smart-header-context\" className={SMART_HEADER_CONTEXT}>\n {context}\n </div>\n )}\n {state === \"search\" && palette != null && <div data-slot=\"smart-header-palette\">{palette}</div>}\n </div>\n );\n}\n"],"mappings":"AA8DM,SAGI,UAHJ,KAGI,YAHJ;AA9DN,YAAY,WAAW;AACvB,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc;AAkChB,SAAS,YAAY;AAAA,EAC1B,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAqB;AACnB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW,GAAG,mBAAmB,SAAS;AAAA,MACzC,GAAG;AAAA,MAEH;AAAA,kBAAU,YAAY;AAAA,QACvB;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,YACX,OACE,iCACG;AAAA;AAAA,cACA,UAAU,eAAe;AAAA,eAC5B;AAAA,YAEF;AAAA;AAAA,QACF;AAAA,QACC,UAAU,aAAa,WAAW,QACjC,oBAAC,SAAI,aAAU,wBAAuB,WAAW,sBAC9C,mBACH;AAAA,QAED,UAAU,aAAa,WAAW,QACjC,oBAAC,SAAI,aAAU,wBAAuB,WAAW,sBAC9C,mBACH;AAAA,QAED,UAAU,YAAY,WAAW,QAAQ,oBAAC,SAAI,aAAU,wBAAwB,mBAAQ;AAAA;AAAA;AAAA,EAC3F;AAEJ;","names":[]}