@sovereignfs/ui 0.54.0 → 0.89.1

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 (473) hide show
  1. package/LICENSE +373 -661
  2. package/NOTICE +8 -0
  3. package/README.md +4 -6
  4. package/dist/Accordion.module.css +1 -1
  5. package/dist/Alert.module.css +1 -1
  6. package/dist/AppsLauncher.module.css +102 -0
  7. package/dist/Avatar.module.css +1 -1
  8. package/dist/Badge.module.css +30 -7
  9. package/dist/BalanceChip.module.css +1 -1
  10. package/dist/Button.module.css +23 -2
  11. package/dist/Card.module.css +7 -1
  12. package/dist/CardTile.module.css +187 -0
  13. package/dist/CheckableListRow.module.css +8 -0
  14. package/dist/Checkbox.module.css +8 -0
  15. package/dist/CodeTextarea.module.css +7 -1
  16. package/dist/Collapsible.module.css +1 -1
  17. package/dist/ColorPicker.module.css +138 -0
  18. package/dist/Combobox.module.css +8 -2
  19. package/dist/Command.module.css +7 -1
  20. package/dist/ConfirmDialog.module.css +8 -2
  21. package/dist/ConsentPrompt.module.css +21 -0
  22. package/dist/DatePicker.module.css +7 -1
  23. package/dist/Dialog.module.css +130 -21
  24. package/dist/DragHandleRow.module.css +6 -0
  25. package/dist/Drawer.module.css +36 -7
  26. package/dist/FileDropzone.module.css +25 -1
  27. package/dist/Header.module.css +68 -0
  28. package/dist/HeaderFooterLayout.module.css +21 -0
  29. package/dist/IconPicker.module.css +15 -5
  30. package/dist/Input.module.css +7 -1
  31. package/dist/Kbd.module.css +1 -1
  32. package/dist/LegalLinks.module.css +9 -2
  33. package/dist/Markdown.module.css +29 -0
  34. package/dist/Menu.module.css +1 -0
  35. package/dist/Message.module.css +10 -1
  36. package/dist/MessageScroller.module.css +12 -1
  37. package/dist/MobileAppsDrawer.module.css +19 -14
  38. package/dist/MobileFooter.module.css +7 -1
  39. package/dist/MobileHeader.module.css +14 -1
  40. package/dist/NavList.module.css +139 -0
  41. package/dist/NavTabs.module.css +8 -2
  42. package/dist/NotificationsPanel.module.css +268 -0
  43. package/dist/OverlayHeader.module.css +1 -1
  44. package/dist/PageContainer.module.css +62 -0
  45. package/dist/PageHeader.module.css +54 -0
  46. package/dist/PageLayout.module.css +51 -0
  47. package/dist/Popover.module.css +1 -1
  48. package/dist/QrCode.module.css +17 -0
  49. package/dist/QuantityStepper.module.css +5 -3
  50. package/dist/RadioGroup.module.css +6 -0
  51. package/dist/Resizable.module.css +3 -3
  52. package/dist/RootLayout.module.css +12 -0
  53. package/dist/SegmentedControl.module.css +7 -1
  54. package/dist/Select.module.css +7 -1
  55. package/dist/Sheet.module.css +20 -2
  56. package/dist/SplitPane.module.css +5 -5
  57. package/dist/StatusBadge.module.css +1 -1
  58. package/dist/StepStrip.module.css +55 -0
  59. package/dist/SuggestionInput.module.css +14 -3
  60. package/dist/SwipableMobileCarousel.module.css +12 -3
  61. package/dist/SwipableMobileCarouselDots.module.css +21 -0
  62. package/dist/SwipeStack.module.css +101 -0
  63. package/dist/SystemBanner.module.css +16 -0
  64. package/dist/Table.module.css +2 -2
  65. package/dist/Tabs.module.css +7 -1
  66. package/dist/TagInput.module.css +8 -2
  67. package/dist/Textarea.module.css +15 -1
  68. package/dist/ThreeColumnLayout.module.css +35 -0
  69. package/dist/Toast.module.css +67 -3
  70. package/dist/Toggle.module.css +7 -0
  71. package/dist/Tooltip.module.css +7 -1
  72. package/dist/UserMenu.module.css +95 -0
  73. package/dist/fonts/OFL.txt +93 -0
  74. package/dist/fonts/hanken-grotesk-cyrillic-ext-wght-normal.woff2 +0 -0
  75. package/dist/fonts/hanken-grotesk-latin-ext-wght-normal.woff2 +0 -0
  76. package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
  77. package/dist/fonts/hanken-grotesk-vietnamese-wght-normal.woff2 +0 -0
  78. package/dist/index.d.ts +1156 -76
  79. package/dist/index.js +4223 -1513
  80. package/dist/tokens/fonts.css +53 -0
  81. package/dist/tokens/primitives.css +23 -12
  82. package/dist/tokens/semantic.css +32 -0
  83. package/dist/tokens/theme-presets.css +33 -0
  84. package/dist/tokens.css +2 -1
  85. package/package.json +46 -19
  86. package/src/__tests__/motion.test.tsx +0 -105
  87. package/src/components/Accordion/Accordion.module.css +0 -3
  88. package/src/components/Accordion/Accordion.stories.tsx +0 -110
  89. package/src/components/Accordion/Accordion.tsx +0 -53
  90. package/src/components/Accordion/__tests__/Accordion.test.tsx +0 -61
  91. package/src/components/Alert/Alert.module.css +0 -62
  92. package/src/components/Alert/Alert.stories.tsx +0 -96
  93. package/src/components/Alert/Alert.tsx +0 -58
  94. package/src/components/Alert/__tests__/Alert.test.tsx +0 -75
  95. package/src/components/AspectRatio/AspectRatio.module.css +0 -15
  96. package/src/components/AspectRatio/AspectRatio.stories.tsx +0 -78
  97. package/src/components/AspectRatio/AspectRatio.tsx +0 -21
  98. package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +0 -27
  99. package/src/components/Avatar/Avatar.module.css +0 -38
  100. package/src/components/Avatar/Avatar.tsx +0 -42
  101. package/src/components/Badge/Badge.module.css +0 -154
  102. package/src/components/Badge/Badge.stories.tsx +0 -226
  103. package/src/components/Badge/Badge.tsx +0 -81
  104. package/src/components/Badge/__tests__/Badge.test.tsx +0 -88
  105. package/src/components/BalanceChip/BalanceChip.module.css +0 -32
  106. package/src/components/BalanceChip/BalanceChip.stories.tsx +0 -45
  107. package/src/components/BalanceChip/BalanceChip.tsx +0 -33
  108. package/src/components/Breadcrumb/Breadcrumb.module.css +0 -49
  109. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -47
  110. package/src/components/Breadcrumb/Breadcrumb.tsx +0 -57
  111. package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +0 -52
  112. package/src/components/Button/Button.module.css +0 -153
  113. package/src/components/Button/Button.stories.tsx +0 -83
  114. package/src/components/Button/Button.tsx +0 -52
  115. package/src/components/Button/__tests__/Button.test.tsx +0 -66
  116. package/src/components/ButtonGroup/ButtonGroup.module.css +0 -29
  117. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -57
  118. package/src/components/ButtonGroup/ButtonGroup.tsx +0 -27
  119. package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +0 -29
  120. package/src/components/Calendar/Calendar.module.css +0 -126
  121. package/src/components/Calendar/Calendar.tsx +0 -213
  122. package/src/components/Calendar/__tests__/Calendar.test.tsx +0 -98
  123. package/src/components/Calendar/dateUtils.ts +0 -97
  124. package/src/components/Card/Card.module.css +0 -42
  125. package/src/components/Card/Card.tsx +0 -34
  126. package/src/components/CheckableListRow/CheckableListRow.module.css +0 -95
  127. package/src/components/CheckableListRow/CheckableListRow.stories.tsx +0 -99
  128. package/src/components/CheckableListRow/CheckableListRow.tsx +0 -89
  129. package/src/components/Checkbox/Checkbox.module.css +0 -111
  130. package/src/components/Checkbox/Checkbox.tsx +0 -78
  131. package/src/components/CodeTextarea/CodeTextarea.module.css +0 -53
  132. package/src/components/CodeTextarea/CodeTextarea.stories.tsx +0 -77
  133. package/src/components/CodeTextarea/CodeTextarea.tsx +0 -36
  134. package/src/components/Collapsible/Collapsible.module.css +0 -70
  135. package/src/components/Collapsible/Collapsible.stories.tsx +0 -62
  136. package/src/components/Collapsible/Collapsible.tsx +0 -66
  137. package/src/components/Collapsible/__tests__/Collapsible.test.tsx +0 -71
  138. package/src/components/Combobox/Combobox.module.css +0 -122
  139. package/src/components/Combobox/Combobox.stories.tsx +0 -79
  140. package/src/components/Combobox/Combobox.tsx +0 -186
  141. package/src/components/Combobox/__tests__/Combobox.test.tsx +0 -158
  142. package/src/components/Command/Command.module.css +0 -80
  143. package/src/components/Command/Command.stories.tsx +0 -104
  144. package/src/components/Command/Command.tsx +0 -168
  145. package/src/components/Command/__tests__/Command.test.tsx +0 -162
  146. package/src/components/ConfirmDialog/ConfirmDialog.module.css +0 -113
  147. package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -124
  148. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +0 -146
  149. package/src/components/ContextMenu/ContextMenu.stories.tsx +0 -81
  150. package/src/components/ContextMenu/ContextMenu.tsx +0 -100
  151. package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +0 -87
  152. package/src/components/CurrencyInput/CurrencyInput.stories.tsx +0 -50
  153. package/src/components/CurrencyInput/CurrencyInput.tsx +0 -60
  154. package/src/components/DataTable/DataTable.module.css +0 -28
  155. package/src/components/DataTable/DataTable.stories.tsx +0 -74
  156. package/src/components/DataTable/DataTable.tsx +0 -154
  157. package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -123
  158. package/src/components/DatePicker/DatePicker.module.css +0 -61
  159. package/src/components/DatePicker/DatePicker.tsx +0 -103
  160. package/src/components/DatePicker/__tests__/DatePicker.test.tsx +0 -89
  161. package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +0 -58
  162. package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +0 -45
  163. package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +0 -100
  164. package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +0 -119
  165. package/src/components/Dialog/Dialog.module.css +0 -226
  166. package/src/components/Dialog/Dialog.stories.tsx +0 -147
  167. package/src/components/Dialog/Dialog.tsx +0 -160
  168. package/src/components/Dialog/__tests__/Dialog.test.tsx +0 -137
  169. package/src/components/DragHandleRow/DragHandleRow.module.css +0 -57
  170. package/src/components/DragHandleRow/DragHandleRow.tsx +0 -61
  171. package/src/components/Drawer/Drawer.module.css +0 -92
  172. package/src/components/Drawer/Drawer.stories.tsx +0 -132
  173. package/src/components/Drawer/Drawer.tsx +0 -176
  174. package/src/components/Drawer/__tests__/Drawer.test.tsx +0 -199
  175. package/src/components/EmptyState/EmptyState.module.css +0 -27
  176. package/src/components/EmptyState/EmptyState.tsx +0 -27
  177. package/src/components/FileDropzone/FileDropzone.module.css +0 -63
  178. package/src/components/FileDropzone/FileDropzone.tsx +0 -109
  179. package/src/components/FileDropzone/__tests__/FileDropzone.test.tsx +0 -67
  180. package/src/components/FormField/FormField.module.css +0 -33
  181. package/src/components/FormField/FormField.tsx +0 -78
  182. package/src/components/FormField/__tests__/FormField.test.tsx +0 -83
  183. package/src/components/HoverCard/HoverCard.module.css +0 -3
  184. package/src/components/HoverCard/HoverCard.stories.tsx +0 -66
  185. package/src/components/HoverCard/HoverCard.tsx +0 -111
  186. package/src/components/HoverCard/__tests__/HoverCard.test.tsx +0 -99
  187. package/src/components/Icon/Icon.module.css +0 -27
  188. package/src/components/Icon/Icon.stories.tsx +0 -95
  189. package/src/components/Icon/Icon.tsx +0 -43
  190. package/src/components/Icon/__tests__/Icon.test.tsx +0 -40
  191. package/src/components/Icon/icons/activity.tsx +0 -20
  192. package/src/components/Icon/icons/alert-triangle.tsx +0 -22
  193. package/src/components/Icon/icons/apple.tsx +0 -21
  194. package/src/components/Icon/icons/banana.tsx +0 -21
  195. package/src/components/Icon/icons/beef.tsx +0 -22
  196. package/src/components/Icon/icons/beer.tsx +0 -24
  197. package/src/components/Icon/icons/bell.tsx +0 -21
  198. package/src/components/Icon/icons/calendar.tsx +0 -23
  199. package/src/components/Icon/icons/candy.tsx +0 -24
  200. package/src/components/Icon/icons/carrot.tsx +0 -22
  201. package/src/components/Icon/icons/check.tsx +0 -20
  202. package/src/components/Icon/icons/chevron-down.tsx +0 -20
  203. package/src/components/Icon/icons/chevron-left.tsx +0 -20
  204. package/src/components/Icon/icons/chevron-right.tsx +0 -20
  205. package/src/components/Icon/icons/chevron-up.tsx +0 -20
  206. package/src/components/Icon/icons/circle-check.tsx +0 -21
  207. package/src/components/Icon/icons/circle-x.tsx +0 -22
  208. package/src/components/Icon/icons/coffee.tsx +0 -23
  209. package/src/components/Icon/icons/cookie.tsx +0 -25
  210. package/src/components/Icon/icons/croissant.tsx +0 -24
  211. package/src/components/Icon/icons/cup-soda.tsx +0 -23
  212. package/src/components/Icon/icons/drumstick.tsx +0 -21
  213. package/src/components/Icon/icons/egg.tsx +0 -20
  214. package/src/components/Icon/icons/ellipsis-vertical.tsx +0 -22
  215. package/src/components/Icon/icons/external-link.tsx +0 -22
  216. package/src/components/Icon/icons/eye-off.tsx +0 -23
  217. package/src/components/Icon/icons/eye.tsx +0 -21
  218. package/src/components/Icon/icons/file.tsx +0 -21
  219. package/src/components/Icon/icons/fish.tsx +0 -25
  220. package/src/components/Icon/icons/grid-2x2.tsx +0 -22
  221. package/src/components/Icon/icons/house.tsx +0 -21
  222. package/src/components/Icon/icons/index.ts +0 -234
  223. package/src/components/Icon/icons/info.tsx +0 -22
  224. package/src/components/Icon/icons/lock.tsx +0 -21
  225. package/src/components/Icon/icons/log-out.tsx +0 -22
  226. package/src/components/Icon/icons/mail.tsx +0 -21
  227. package/src/components/Icon/icons/menu.tsx +0 -22
  228. package/src/components/Icon/icons/milk.tsx +0 -22
  229. package/src/components/Icon/icons/package.tsx +0 -23
  230. package/src/components/Icon/icons/pencil.tsx +0 -21
  231. package/src/components/Icon/icons/pizza.tsx +0 -24
  232. package/src/components/Icon/icons/plus.tsx +0 -21
  233. package/src/components/Icon/icons/rotate-ccw.tsx +0 -21
  234. package/src/components/Icon/icons/salad.tsx +0 -24
  235. package/src/components/Icon/icons/search.tsx +0 -21
  236. package/src/components/Icon/icons/settings.tsx +0 -21
  237. package/src/components/Icon/icons/shield.tsx +0 -20
  238. package/src/components/Icon/icons/shopping-basket.tsx +0 -26
  239. package/src/components/Icon/icons/sliders-horizontal.tsx +0 -28
  240. package/src/components/Icon/icons/smartphone.tsx +0 -21
  241. package/src/components/Icon/icons/snowflake.tsx +0 -31
  242. package/src/components/Icon/icons/spray-can.tsx +0 -29
  243. package/src/components/Icon/icons/trash-2.tsx +0 -24
  244. package/src/components/Icon/icons/upload.tsx +0 -22
  245. package/src/components/Icon/icons/user.tsx +0 -21
  246. package/src/components/Icon/icons/wine.tsx +0 -23
  247. package/src/components/Icon/icons/x.tsx +0 -21
  248. package/src/components/IconPicker/IconPicker.module.css +0 -63
  249. package/src/components/IconPicker/IconPicker.stories.tsx +0 -84
  250. package/src/components/IconPicker/IconPicker.tsx +0 -90
  251. package/src/components/Input/Input.module.css +0 -52
  252. package/src/components/Input/Input.stories.tsx +0 -86
  253. package/src/components/Input/Input.tsx +0 -14
  254. package/src/components/Input/__tests__/Input.test.tsx +0 -22
  255. package/src/components/Item/Item.module.css +0 -70
  256. package/src/components/Item/Item.stories.tsx +0 -62
  257. package/src/components/Item/Item.tsx +0 -54
  258. package/src/components/Item/__tests__/Item.test.tsx +0 -39
  259. package/src/components/Kbd/Kbd.module.css +0 -14
  260. package/src/components/Kbd/Kbd.stories.tsx +0 -32
  261. package/src/components/Kbd/Kbd.tsx +0 -12
  262. package/src/components/Kbd/__tests__/Kbd.test.tsx +0 -14
  263. package/src/components/Label/Label.module.css +0 -11
  264. package/src/components/Label/Label.stories.tsx +0 -40
  265. package/src/components/Label/Label.tsx +0 -24
  266. package/src/components/Label/__tests__/Label.test.tsx +0 -32
  267. package/src/components/LegalLinks/LegalLinks.module.css +0 -24
  268. package/src/components/LegalLinks/LegalLinks.stories.tsx +0 -22
  269. package/src/components/LegalLinks/LegalLinks.tsx +0 -40
  270. package/src/components/Markdown/Markdown.module.css +0 -49
  271. package/src/components/Markdown/Markdown.stories.tsx +0 -30
  272. package/src/components/Markdown/Markdown.tsx +0 -168
  273. package/src/components/Marker/Marker.module.css +0 -33
  274. package/src/components/Marker/Marker.stories.tsx +0 -39
  275. package/src/components/Marker/Marker.tsx +0 -34
  276. package/src/components/Marker/__tests__/Marker.test.tsx +0 -23
  277. package/src/components/MemberMultiSelect/MemberMultiSelect.module.css +0 -27
  278. package/src/components/MemberMultiSelect/MemberMultiSelect.stories.tsx +0 -74
  279. package/src/components/MemberMultiSelect/MemberMultiSelect.tsx +0 -67
  280. package/src/components/Menu/Menu.module.css +0 -89
  281. package/src/components/Menu/Menu.tsx +0 -159
  282. package/src/components/Menu/__tests__/Menu.test.tsx +0 -197
  283. package/src/components/Menubar/Menubar.stories.tsx +0 -52
  284. package/src/components/Menubar/Menubar.tsx +0 -42
  285. package/src/components/Menubar/__tests__/Menubar.test.tsx +0 -45
  286. package/src/components/Message/Message.module.css +0 -99
  287. package/src/components/Message/Message.stories.tsx +0 -79
  288. package/src/components/Message/Message.tsx +0 -41
  289. package/src/components/Message/__tests__/Message.test.tsx +0 -46
  290. package/src/components/MessageScroller/MessageScroller.module.css +0 -54
  291. package/src/components/MessageScroller/MessageScroller.stories.tsx +0 -84
  292. package/src/components/MessageScroller/MessageScroller.tsx +0 -72
  293. package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +0 -105
  294. package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +0 -81
  295. package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +0 -78
  296. package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +0 -80
  297. package/src/components/MobileFooter/MobileFooter.module.css +0 -73
  298. package/src/components/MobileFooter/MobileFooter.stories.tsx +0 -47
  299. package/src/components/MobileFooter/MobileFooter.tsx +0 -109
  300. package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +0 -71
  301. package/src/components/MobileHeader/MobileHeader.module.css +0 -63
  302. package/src/components/MobileHeader/MobileHeader.stories.tsx +0 -57
  303. package/src/components/MobileHeader/MobileHeader.tsx +0 -77
  304. package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +0 -58
  305. package/src/components/NavTabs/NavTabs.module.css +0 -50
  306. package/src/components/NavTabs/NavTabs.tsx +0 -61
  307. package/src/components/NavigationMenu/NavigationMenu.module.css +0 -53
  308. package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -58
  309. package/src/components/NavigationMenu/NavigationMenu.tsx +0 -125
  310. package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +0 -93
  311. package/src/components/OfflineGate/OfflineGate.tsx +0 -44
  312. package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +0 -66
  313. package/src/components/OverlayHeader/OverlayHeader.module.css +0 -98
  314. package/src/components/OverlayHeader/OverlayHeader.tsx +0 -62
  315. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +0 -51
  316. package/src/components/PageContainer/PageContainer.module.css +0 -20
  317. package/src/components/PageContainer/PageContainer.tsx +0 -31
  318. package/src/components/PageContainer/__tests__/PageContainer.test.tsx +0 -37
  319. package/src/components/PageHeader/PageHeader.module.css +0 -32
  320. package/src/components/PageHeader/PageHeader.tsx +0 -35
  321. package/src/components/Pagination/Pagination.module.css +0 -98
  322. package/src/components/Pagination/Pagination.stories.tsx +0 -49
  323. package/src/components/Pagination/Pagination.tsx +0 -87
  324. package/src/components/Pagination/__tests__/Pagination.test.tsx +0 -56
  325. package/src/components/Popover/Popover.module.css +0 -74
  326. package/src/components/Popover/Popover.stories.tsx +0 -357
  327. package/src/components/Popover/Popover.tsx +0 -208
  328. package/src/components/Popover/__tests__/Popover.test.tsx +0 -98
  329. package/src/components/Progress/Progress.module.css +0 -20
  330. package/src/components/Progress/Progress.stories.tsx +0 -40
  331. package/src/components/Progress/Progress.tsx +0 -35
  332. package/src/components/Progress/__tests__/Progress.test.tsx +0 -31
  333. package/src/components/QuantityStepper/QuantityStepper.module.css +0 -67
  334. package/src/components/QuantityStepper/QuantityStepper.stories.tsx +0 -79
  335. package/src/components/QuantityStepper/QuantityStepper.tsx +0 -107
  336. package/src/components/RadioGroup/RadioGroup.module.css +0 -88
  337. package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -69
  338. package/src/components/RadioGroup/RadioGroup.tsx +0 -99
  339. package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +0 -59
  340. package/src/components/Resizable/Resizable.module.css +0 -70
  341. package/src/components/Resizable/Resizable.stories.tsx +0 -82
  342. package/src/components/Resizable/Resizable.tsx +0 -313
  343. package/src/components/Resizable/__tests__/Resizable.test.tsx +0 -128
  344. package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +0 -24
  345. package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +0 -73
  346. package/src/components/ScrollArea/ScrollArea.module.css +0 -20
  347. package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -57
  348. package/src/components/ScrollArea/ScrollArea.tsx +0 -27
  349. package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +0 -39
  350. package/src/components/SegmentedControl/SegmentedControl.module.css +0 -55
  351. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -118
  352. package/src/components/SegmentedControl/SegmentedControl.tsx +0 -56
  353. package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +0 -63
  354. package/src/components/Select/Select.module.css +0 -82
  355. package/src/components/Select/Select.stories.tsx +0 -115
  356. package/src/components/Select/Select.tsx +0 -48
  357. package/src/components/Select/__tests__/Select.test.tsx +0 -59
  358. package/src/components/Sheet/Sheet.module.css +0 -55
  359. package/src/components/Sheet/Sheet.tsx +0 -109
  360. package/src/components/Sheet/__tests__/Sheet.test.tsx +0 -99
  361. package/src/components/Slider/Slider.module.css +0 -95
  362. package/src/components/Slider/Slider.stories.tsx +0 -64
  363. package/src/components/Slider/Slider.tsx +0 -77
  364. package/src/components/Slider/__tests__/Slider.test.tsx +0 -45
  365. package/src/components/Spinner/Spinner.module.css +0 -39
  366. package/src/components/Spinner/Spinner.tsx +0 -23
  367. package/src/components/SplitMethodSelector/SplitMethodSelector.stories.tsx +0 -33
  368. package/src/components/SplitMethodSelector/SplitMethodSelector.tsx +0 -43
  369. package/src/components/SplitPane/SplitPane.module.css +0 -85
  370. package/src/components/SplitPane/SplitPane.stories.tsx +0 -160
  371. package/src/components/SplitPane/SplitPane.tsx +0 -149
  372. package/src/components/SplitPane/__tests__/SplitPane.test.tsx +0 -51
  373. package/src/components/StatusBadge/StatusBadge.module.css +0 -59
  374. package/src/components/StatusBadge/StatusBadge.stories.tsx +0 -74
  375. package/src/components/StatusBadge/StatusBadge.tsx +0 -65
  376. package/src/components/SuggestionInput/SuggestionInput.module.css +0 -103
  377. package/src/components/SuggestionInput/SuggestionInput.stories.tsx +0 -144
  378. package/src/components/SuggestionInput/SuggestionInput.tsx +0 -207
  379. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +0 -55
  380. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +0 -227
  381. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +0 -35
  382. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +0 -42
  383. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +0 -18
  384. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +0 -21
  385. package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +0 -29
  386. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +0 -133
  387. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +0 -61
  388. package/src/components/SwipableMobileCarousel/context.ts +0 -13
  389. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +0 -49
  390. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +0 -62
  391. package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +0 -63
  392. package/src/components/SystemBanner/SystemBanner.module.css +0 -69
  393. package/src/components/SystemBanner/SystemBanner.stories.tsx +0 -86
  394. package/src/components/SystemBanner/SystemBanner.tsx +0 -50
  395. package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +0 -50
  396. package/src/components/Table/Table.module.css +0 -45
  397. package/src/components/Table/Table.stories.tsx +0 -125
  398. package/src/components/Table/Table.tsx +0 -51
  399. package/src/components/Table/__tests__/Table.test.tsx +0 -50
  400. package/src/components/Tabs/Tabs.module.css +0 -56
  401. package/src/components/Tabs/Tabs.stories.tsx +0 -100
  402. package/src/components/Tabs/Tabs.tsx +0 -46
  403. package/src/components/Tabs/__tests__/Tabs.test.tsx +0 -45
  404. package/src/components/TagInput/TagInput.module.css +0 -141
  405. package/src/components/TagInput/TagInput.stories.tsx +0 -109
  406. package/src/components/TagInput/TagInput.tsx +0 -174
  407. package/src/components/TagInput/__tests__/TagInput.test.tsx +0 -69
  408. package/src/components/Textarea/Textarea.module.css +0 -39
  409. package/src/components/Textarea/Textarea.tsx +0 -15
  410. package/src/components/Textarea/__tests__/Textarea.test.tsx +0 -29
  411. package/src/components/Toast/Toast.module.css +0 -132
  412. package/src/components/Toast/Toast.stories.tsx +0 -191
  413. package/src/components/Toast/Toast.tsx +0 -139
  414. package/src/components/Toggle/Toggle.module.css +0 -57
  415. package/src/components/Toggle/Toggle.stories.tsx +0 -95
  416. package/src/components/Toggle/Toggle.tsx +0 -39
  417. package/src/components/Toggle/__tests__/Toggle.test.tsx +0 -47
  418. package/src/components/Tooltip/Tooltip.module.css +0 -56
  419. package/src/components/Tooltip/Tooltip.tsx +0 -30
  420. package/src/components/Typography/Typography.module.css +0 -47
  421. package/src/components/Typography/Typography.stories.tsx +0 -62
  422. package/src/components/Typography/Typography.tsx +0 -30
  423. package/src/components/Typography/__tests__/Typography.test.tsx +0 -34
  424. package/src/css-modules.d.ts +0 -4
  425. package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +0 -126
  426. package/src/hooks/__tests__/useDoubleTap.test.tsx +0 -117
  427. package/src/hooks/__tests__/useIsMobile.test.tsx +0 -80
  428. package/src/hooks/__tests__/useIsOffline.test.tsx +0 -53
  429. package/src/hooks/__tests__/useLongPress.test.tsx +0 -158
  430. package/src/hooks/__tests__/useOfflineTileState.test.tsx +0 -69
  431. package/src/hooks/__tests__/useResponsiveLayout.test.tsx +0 -87
  432. package/src/hooks/__tests__/useSnapCarousel.test.tsx +0 -93
  433. package/src/hooks/__tests__/useSwipeReveal.test.tsx +0 -110
  434. package/src/hooks/index.ts +0 -24
  435. package/src/hooks/useCarouselRouteSync.ts +0 -92
  436. package/src/hooks/useCommitOnEnterOrBlur.ts +0 -39
  437. package/src/hooks/useDoubleTap.ts +0 -91
  438. package/src/hooks/useIsMobile.ts +0 -33
  439. package/src/hooks/useIsOffline.ts +0 -41
  440. package/src/hooks/useLongPress.ts +0 -185
  441. package/src/hooks/useOfflineTileState.ts +0 -45
  442. package/src/hooks/useResponsiveLayout.ts +0 -36
  443. package/src/hooks/useSnapCarousel.ts +0 -79
  444. package/src/hooks/useSwipeReveal.ts +0 -107
  445. package/src/index.ts +0 -251
  446. package/src/motion.ts +0 -78
  447. package/src/overlay-shell.ts +0 -94
  448. package/src/scroll-lock.ts +0 -22
  449. package/src/stories/Avatar.stories.tsx +0 -44
  450. package/src/stories/Card.stories.tsx +0 -35
  451. package/src/stories/Checkbox.stories.tsx +0 -45
  452. package/src/stories/DesignSystemOverview.stories.tsx +0 -3187
  453. package/src/stories/DeviceOnlyGate.stories.tsx +0 -35
  454. package/src/stories/DeviceStorageKeyGate.stories.tsx +0 -57
  455. package/src/stories/DragHandleRow.stories.tsx +0 -41
  456. package/src/stories/EmptyState.stories.tsx +0 -33
  457. package/src/stories/FileDropzone.stories.tsx +0 -69
  458. package/src/stories/FormField.stories.tsx +0 -47
  459. package/src/stories/InteractionHooks.stories.tsx +0 -925
  460. package/src/stories/MobilePatterns.stories.tsx +0 -1536
  461. package/src/stories/NavTabs.stories.tsx +0 -53
  462. package/src/stories/OfflineGate.stories.tsx +0 -42
  463. package/src/stories/PageContainer.stories.tsx +0 -37
  464. package/src/stories/PageHeader.stories.tsx +0 -42
  465. package/src/stories/ResponsiveSurface.stories.tsx +0 -47
  466. package/src/stories/Spinner.stories.tsx +0 -25
  467. package/src/stories/SwipableMobileCarousel.stories.tsx +0 -304
  468. package/src/stories/Textarea.stories.tsx +0 -51
  469. package/src/stories/TokenGallery.stories.tsx +0 -602
  470. package/src/stories/Tooltip.stories.tsx +0 -59
  471. package/src/tokens/primitives.css +0 -157
  472. package/src/tokens/semantic.css +0 -130
  473. package/src/tokens.css +0 -5
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, ButtonHTMLAttributes, SelectHTMLAttributes, ReactElement, CSSProperties, InputHTMLAttributes, TextareaHTMLAttributes, HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes, LabelHTMLAttributes, ElementType, PointerEvent, KeyboardEvent, MouseEvent, RefObject } from 'react';
2
+ import { ReactNode, ButtonHTMLAttributes, SelectHTMLAttributes, ReactElement, CSSProperties, InputHTMLAttributes, Ref, TextareaHTMLAttributes, HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes, AnchorHTMLAttributes, LabelHTMLAttributes, ElementType, PointerEvent, KeyboardEvent, MouseEvent, RefObject } from 'react';
3
+ import * as _dnd_kit_core from '@dnd-kit/core';
3
4
 
4
5
  interface LegalLinksProps {
5
6
  privacyHref?: string;
@@ -14,30 +15,82 @@ interface LegalLinksProps {
14
15
  }
15
16
  declare function LegalLinks({ privacyHref, tosHref, renderLink, className, }: LegalLinksProps): react.JSX.Element;
16
17
 
18
+ interface ConsentPromptProps {
19
+ /** The plugin requesting access. */
20
+ consumerName: string;
21
+ /** The plugin whose data would be read. */
22
+ providerName: string;
23
+ /** The named contract being requested (e.g. `"expenses"`). */
24
+ contract: string;
25
+ /**
26
+ * The provider's own manifest-declared explanation of what this contract
27
+ * exposes (`data.provides[].description`) — never caller-supplied at
28
+ * request time, so a consuming plugin can't write its own persuasive copy
29
+ * for what it's asking to read.
30
+ */
31
+ description?: string | null;
32
+ onAllow: () => void;
33
+ onDeny?: () => void;
34
+ allowLabel?: string;
35
+ denyLabel?: string;
36
+ /** Disables both buttons and shows a busy state on Allow while a request is in flight. */
37
+ pending?: boolean;
38
+ className?: string;
39
+ }
40
+ /**
41
+ * The consent screen shown before one plugin is allowed to read another
42
+ * plugin's data (RFC 0002 §4) — the step that was previously missing between
43
+ * a `ConsentRequiredError` and a grant actually being created. Presentational
44
+ * only: holds no state of its own, so it renders identically wherever it's
45
+ * used (currently Account → Data's pending-requests list).
46
+ */
47
+ declare function ConsentPrompt({ consumerName, providerName, contract, description, onAllow, onDeny, allowLabel, denyLabel, pending, className, }: ConsentPromptProps): react.JSX.Element;
48
+
17
49
  interface MarkdownProps {
18
- /** Raw markdown source. Supports the subset used by Sovereign's own
19
- * long-form content pages: headings (#/##/###), paragraphs, **bold**,
20
- * *italic*, `code`, [links](url), unordered lists (-), and blockquotes
21
- * (>), including soft-wrapped continuation lines within a paragraph or
22
- * list item. Not a general-purpose CommonMark parser — no tables,
23
- * images, ordered lists, or nested blocks. */
50
+ /** Raw markdown source. Supports: headings (#/##/###), paragraphs,
51
+ * **bold**, *italic*, `code`, [links](url), unordered lists (-), ordered
52
+ * lists (1. / 1)), fenced code blocks (```), and blockquotes (>),
53
+ * including soft-wrapped continuation lines within a paragraph or list
54
+ * item. Not a general-purpose CommonMark parser — no tables, images, or
55
+ * nested blocks. */
24
56
  content: string;
57
+ /** Renders every line within a paragraph on its own visual line (a
58
+ * `<br>` between them) instead of the default CommonMark-style soft-wrap
59
+ * (consecutive lines joined with a single space, requiring a blank line
60
+ * for a new paragraph). Off by default — the soft-wrap behavior is
61
+ * correct for this component's primary use (long-form, first-party
62
+ * content authored *as* markdown, where a hard line break is a
63
+ * deliberate `\` ` or blank-line choice). Turn this on when rendering
64
+ * plain, user-typed multi-line text that happens to be passed through
65
+ * this component (e.g. a card description or comment body edited in an
66
+ * ordinary `<textarea>`) — there, every Enter press is a real,
67
+ * intentional line break the user typed, and silently collapsing it
68
+ * into one run-on paragraph reads as broken, not as "markdown
69
+ * formatting applied." */
70
+ preserveLineBreaks?: boolean;
25
71
  className?: string;
26
72
  }
27
73
  /** Renders a constrained markdown subset (see MarkdownProps) as styled React
28
- * elements built from Typography — no HTML string injection. Meant for
29
- * long-form, first-party content pages (privacy policy, terms of service)
30
- * sourced from a single markdown file, not for rendering arbitrary or
31
- * user-supplied markdown. */
32
- declare function Markdown({ content, className }: MarkdownProps): react.JSX.Element;
74
+ * elements built from Typography — no HTML string injection. Primarily
75
+ * meant for long-form, first-party content pages (privacy policy, terms of
76
+ * service) sourced from a single markdown file — not a general-purpose
77
+ * CommonMark renderer for arbitrary markdown. Also fine for plain,
78
+ * user-typed multi-line text (a card description, a comment body) edited
79
+ * in an ordinary `<textarea>` and passed through unchanged, as long as
80
+ * `preserveLineBreaks` is set — see that prop's own doc comment for why it
81
+ * isn't the default. */
82
+ declare function Markdown({ content, preserveLineBreaks, className }: MarkdownProps): react.JSX.Element;
33
83
 
34
84
  type BadgeVariant = 'role' | 'status' | 'mono';
35
- type BadgeSize = 'sm' | 'md' | 'lg';
85
+ type BadgeSize = 'xs' | 'sm' | 'md' | 'lg';
36
86
  type BadgeStatus = 'active' | 'enabled' | 'deactivated' | 'failed' | 'invited' | 'pending' | 'neutral';
37
87
  interface BadgeProps {
38
88
  variant?: BadgeVariant;
39
- /** 'sm' | 'md' (default) | 'lg'. 'md' is the badge's original — and until
40
- * now, only — size, so the default renders unchanged. */
89
+ /** 'xs' | 'sm' | 'md' (default) | 'lg'. 'md' is the badge's original — and
90
+ * until now, only — size, so the default renders unchanged. 'xs' is the
91
+ * smallest tier (`--sv-font-size-2xs`, 10px) — for a badge sitting inline
92
+ * next to larger text (e.g. a status chip beside a card's headline value)
93
+ * where even 'sm' still reads oversized next to it. */
41
94
  size?: BadgeSize;
42
95
  /** Only relevant when variant="status" — determines the dot colour. */
43
96
  status?: BadgeStatus;
@@ -55,12 +108,12 @@ interface BadgeProps {
55
108
  * - `status` tinted chip with leading colour dot — for Active / Deactivated / etc.
56
109
  * - `mono` monospace font, neutral surface — for platform / community / v0.1.0
57
110
  *
58
- * Three sizes (`sm`/`md`/`lg`, default `md`). ALL CAPS by default
111
+ * Four sizes (`xs`/`sm`/`md`/`lg`, default `md`). ALL CAPS by default
59
112
  * (`uppercase`) — set `uppercase={false}` for a title-case badge instead.
60
113
  */
61
114
  declare function Badge({ variant, size, status, uppercase, children, }: BadgeProps): react.JSX.Element;
62
115
 
63
- type IconName = 'house' | 'settings' | 'log-out' | 'smartphone' | 'chevron-right' | 'chevron-left' | 'chevron-down' | 'chevron-up' | 'menu' | 'x' | 'check' | 'plus' | 'trash-2' | 'pencil' | 'rotate-ccw' | 'search' | 'user' | 'shield' | 'lock' | 'eye' | 'eye-off' | 'mail' | 'bell' | 'activity' | 'package' | 'grid-2x2' | 'info' | 'alert-triangle' | 'circle-check' | 'circle-x' | 'calendar' | 'sliders-horizontal' | 'ellipsis-vertical' | 'file' | 'upload' | 'external-link' | 'banana' | 'apple' | 'carrot' | 'egg' | 'beef' | 'fish' | 'coffee' | 'wine' | 'beer' | 'cookie' | 'pizza' | 'candy' | 'salad' | 'milk' | 'drumstick' | 'croissant' | 'cup-soda' | 'spray-can' | 'snowflake' | 'shopping-basket';
116
+ type IconName = 'house' | 'settings' | 'log-out' | 'smartphone' | 'chevron-right' | 'chevron-left' | 'chevron-down' | 'chevron-up' | 'circle-chevron-left' | 'menu' | 'x' | 'check' | 'plus' | 'trash-2' | 'pencil' | 'rotate-ccw' | 'search' | 'copy' | 'user' | 'users' | 'user-round-plus' | 'shield' | 'lock' | 'eye' | 'eye-off' | 'hat-glasses' | 'mail' | 'message-square' | 'archive' | 'archive-restore' | 'clock' | 'bell' | 'inbox' | 'activity' | 'package' | 'database' | 'layers' | 'grid-2x2' | 'layout-dashboard' | 'layout-grid' | 'square-kanban' | 'info' | 'alert-triangle' | 'circle-check' | 'circle-x' | 'calendar' | 'sliders-horizontal' | 'ellipsis-vertical' | 'rectangle-ellipsis' | 'file' | 'folder' | 'upload' | 'download' | 'external-link' | 'arrow-left-right' | 'send' | 'list' | 'list-ordered' | 'link' | 'code' | 'rotate-cw' | 'refresh-cw' | 'history' | 'minus' | 'table' | 'sheet' | 'folders' | 'folder-closed' | 'file-text' | 'folder-open' | 'share-2' | 'luggage' | 'map-pin' | 'route' | 'bold' | 'italic' | 'paint-bucket' | 'banana' | 'apple' | 'carrot' | 'egg' | 'beef' | 'fish' | 'coffee' | 'wine' | 'beer' | 'cookie' | 'pizza' | 'candy' | 'salad' | 'milk' | 'drumstick' | 'croissant' | 'cup-soda' | 'spray-can' | 'snowflake' | 'shopping-basket' | 'panel-left' | 'pin' | 'credit-card' | 'ban' | 'shield-off' | 'user-x';
64
117
 
65
118
  /** Decorative icon — visually meaningful but described by surrounding text. */
66
119
  type DecorativeProps = {
@@ -207,6 +260,17 @@ interface PopoverProps {
207
260
  * regardless of CSS module load order, unlike passing an extra className.
208
261
  */
209
262
  panelStyle?: CSSProperties;
263
+ /**
264
+ * Class applied to the popover's own wrapper around `trigger` — the
265
+ * element that participates in the caller's layout.
266
+ *
267
+ * Mostly needed for flex sizing: the wrapper is an `inline-flex` box with
268
+ * the default `min-width: auto`, which pins it to the trigger's content
269
+ * width. Inside a flex row that has to shrink, that stops a truncatable
270
+ * trigger from ever giving up space, and pushes its siblings out of the
271
+ * container instead. Pass a class setting `min-width: 0` there.
272
+ */
273
+ className?: string;
210
274
  'aria-label': string;
211
275
  children: ReactNode;
212
276
  }
@@ -233,7 +297,7 @@ interface PopoverProps {
233
297
  *
234
298
  * Closes on outside click or Escape. Does not trap focus (it is non-modal).
235
299
  */
236
- declare function Popover({ trigger, open, onClose, align, width, panelStyle, 'aria-label': ariaLabel, children, }: PopoverProps): react.JSX.Element;
300
+ declare function Popover({ trigger, open, onClose, align, width, panelStyle, className, 'aria-label': ariaLabel, children, }: PopoverProps): react.JSX.Element;
237
301
 
238
302
  type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'destructive';
239
303
  type ButtonSize = 'sm' | 'md' | 'lg';
@@ -252,7 +316,13 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
252
316
  */
253
317
  declare function Button({ variant, size, type, loading, disabled, className, children, ...rest }: ButtonProps): react.JSX.Element;
254
318
 
255
- type InputProps = InputHTMLAttributes<HTMLInputElement>;
319
+ type InputProps = InputHTMLAttributes<HTMLInputElement> & {
320
+ /** React 19 ref-as-prop — no `forwardRef` needed. Not part of
321
+ * `InputHTMLAttributes` (refs aren't a DOM attribute), so it's added
322
+ * explicitly here; left out of the destructure below so it flows through
323
+ * `...rest` straight onto the underlying `<input>`. */
324
+ ref?: Ref<HTMLInputElement>;
325
+ };
256
326
  /**
257
327
  * Input — the primitive text field. Presentational and RSC-safe: it forwards
258
328
  * all native input props to the underlying `<input>`. Styling references
@@ -260,14 +330,23 @@ type InputProps = InputHTMLAttributes<HTMLInputElement>;
260
330
  */
261
331
  declare function Input({ type, className, ...rest }: InputProps): react.JSX.Element;
262
332
 
263
- type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement>;
333
+ interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
334
+ /** Grows the field's own height to fit its content as it's typed, instead
335
+ * of scrolling internally or leaving the user to drag-resize — e.g. an
336
+ * editable title that should read as a heading, not a fixed-size form
337
+ * field. Disables manual resize (`resize: none`) since a height driven by
338
+ * content and a user-draggable height would fight each other. `rows` is
339
+ * still honoured as the starting/minimum height. */
340
+ autoGrow?: boolean;
341
+ }
264
342
  /**
265
343
  * Textarea — the primitive multi-line text field. Presentational and
266
- * RSC-safe: it forwards all native textarea props to the underlying
344
+ * RSC-safe unless `autoGrow` is used (client-only, needs a layout
345
+ * measurement). Forwards all native textarea props to the underlying
267
346
  * `<textarea>`. Styling references `--sv-*` tokens via CSS Modules; there
268
347
  * are no hardcoded values.
269
348
  */
270
- declare function Textarea({ className, rows, ...rest }: TextareaProps): react.JSX.Element;
349
+ declare function Textarea({ className, rows, autoGrow, ...rest }: TextareaProps): react.JSX.Element;
271
350
 
272
351
  interface CodeTextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
273
352
  invalid?: boolean;
@@ -338,11 +417,13 @@ declare function TagInput({ value, onChange, id, name, placeholder, disabled, re
338
417
  /**
339
418
  * Lets content deep inside a `Dialog` (e.g. a plugin's own route layout,
340
419
  * rendered several levels below wherever the `Dialog` itself is
341
- * instantiated) supply the second-row content of the Dialog's mobile
342
- * `OverlayHeader` — typically a tab strip. Solves the "double header"
343
- * problem: without this, a plugin's own tab strip has no way to reach the
344
- * Dialog's header and ends up rendered a second time, as its own sticky bar,
345
- * inside the scrolling content area.
420
+ * instantiated) supply the second-row content of the Dialog's `OverlayHeader`
421
+ * — typically a tab strip. Solves the "double header" problem: without this,
422
+ * a plugin's own tab strip has no way to reach the Dialog's header and ends
423
+ * up rendered a second time, as its own sticky bar, inside the scrolling
424
+ * content area. Reaches the header regardless of whether it's showing via
425
+ * the `header` prop (both breakpoints) or the default `title`-driven mobile
426
+ * bar (mobile only) — see `DialogProps.header`'s own doc comment.
346
427
  *
347
428
  * A no-op when there is no enclosing `Dialog` (e.g. the same plugin layout
348
429
  * also rendered on a plain, non-overlay route) — safe to call unconditionally.
@@ -359,7 +440,7 @@ declare function TagInput({ value, onChange, id, name, placeholder, disabled, re
359
440
  * ```
360
441
  */
361
442
  declare function useOverlaySecondRow(node: ReactNode | null): boolean;
362
- type DialogSize = 'sm' | 'md' | 'lg' | 'full';
443
+ type DialogSize = 'sm' | 'md' | 'lg' | 'auto' | 'fixed';
363
444
  interface DialogProps {
364
445
  /** Whether the dialog is shown. When false, nothing renders. */
365
446
  open: boolean;
@@ -367,11 +448,42 @@ interface DialogProps {
367
448
  onClose: () => void;
368
449
  /** Panel size on desktop. Mobile always renders as a full-screen sheet. */
369
450
  size?: DialogSize;
370
- /** Accessible name for the dialog (sets `aria-label` on the panel). */
451
+ /** Accessible name for the dialog (sets `aria-label` on the panel). Falls
452
+ * back to `title` when omitted — this still applies even when `header` is
453
+ * used instead of `title` for the visible header (see `header`'s own doc
454
+ * comment), so pass `aria-label` explicitly in that case if `title` is
455
+ * also omitted. */
371
456
  'aria-label'?: string;
372
- /** On mobile: shown in the top bar alongside the close button so the title
373
- * and dismiss affordance occupy the same row instead of stacking. */
457
+ /** Shown in the top bar alongside the close button, on mobile only —
458
+ * desktop has no header row by default (see `header` for that). Ignored
459
+ * as visible content when `header` is provided; still used as the
460
+ * `aria-label` fallback either way. */
374
461
  title?: string;
462
+ /** Renders a persistent header row (via the same `OverlayHeader` component
463
+ * `title` alone only shows on mobile) on **both** desktop and mobile —
464
+ * the "Header + Body" / "Header + Body + Footer" shapes. Supersedes
465
+ * `title` for visible content when provided; `title` remains the
466
+ * `aria-label` fallback if `aria-label` is also omitted. Omitting both
467
+ * `header` and `footer` is "Body only" — today's default, unchanged. */
468
+ header?: ReactNode;
469
+ /** Renders a persistent footer row below the scrollable body — pinned on
470
+ * both desktop and mobile the same way `header`/the mobile title bar are
471
+ * (a non-scrolling flex sibling, not `position: sticky`): only the region
472
+ * between header and footer scrolls. Typically action buttons (Save/
473
+ * Cancel). Omit for no footer — today's default, where any actions live
474
+ * inside `children` and scroll with the rest of the body. Layout-agnostic
475
+ * (no built-in button alignment) — arrange the footer's own content as
476
+ * needed, same as `children`. */
477
+ footer?: ReactNode;
478
+ /** Forwarded to the active `OverlayHeader` instance's own `rowClassName`
479
+ * (whichever is showing — `header` or the mobile `title` bar) — restyle
480
+ * the title row (e.g. tighter padding to match a consumer's own compact
481
+ * header elsewhere) without changing its title/close layout. */
482
+ rowClassName?: string;
483
+ /** Forwarded to the active `OverlayHeader` instance's own `titleClassName`
484
+ * — restyle the title text (e.g. a smaller font size to match a
485
+ * consumer's own compact header elsewhere). */
486
+ titleClassName?: string;
375
487
  children: ReactNode;
376
488
  }
377
489
  /**
@@ -384,13 +496,28 @@ interface DialogProps {
384
496
  * the panel. Styling references `--sv-*` tokens only; on mobile the panel becomes
385
497
  * a full-screen sheet regardless of `size`.
386
498
  *
499
+ * Renders through a portal into the shell root (`useOverlayPortalTarget`)
500
+ * rather than where it sits in the tree, so its scrim and panel resolve
501
+ * `position: fixed` against the viewport even when it is opened from inside
502
+ * another overlay. React context and event bubbling follow the React tree,
503
+ * not the DOM, so `useOverlaySecondRow` and a consumer's own click handlers
504
+ * are unaffected; a CSS selector that reaches *into* the overlay from one of
505
+ * its DOM ancestors is not, and has to target the overlay's own content
506
+ * instead.
507
+ *
387
508
  * Animated open/close: fade + scale on desktop, slide-up on mobile (matching
388
509
  * the "feels like a page push" framing of the mobile sheet). The `open`/
389
510
  * `onClose` API is unchanged — closing still stays mounted internally for the
390
511
  * exit transition before actually unmounting; `prefers-reduced-motion: reduce`
391
512
  * collapses both to near-instant.
513
+ *
514
+ * Three composition shapes, chosen by which of `header`/`footer` are passed:
515
+ * Body only (both omitted — today's default), Header + Body (`header` only),
516
+ * Header + Body + Footer (`header` and `footer`). Whichever of header/footer
517
+ * are present render as non-scrolling flex siblings around `.content`, which
518
+ * stays the only scrollable region in every shape.
392
519
  */
393
- declare function Dialog({ open, onClose, size, 'aria-label': ariaLabel, title, children, }: DialogProps): react.JSX.Element | null;
520
+ declare function Dialog({ open, onClose, size, 'aria-label': ariaLabel, title, header, footer, rowClassName, titleClassName, children, }: DialogProps): react.ReactPortal | null;
394
521
 
395
522
  interface DrawerProps {
396
523
  /** Whether the drawer is shown. When false, nothing renders. */
@@ -433,14 +560,23 @@ interface DrawerProps {
433
560
  * — dragging from body content would fight that content's own internal
434
561
  * scroll, the same reasoning behind the tasks plugin's edge-zone-only
435
562
  * swipe-to-reveal technique.
563
+ *
564
+ * Renders through a portal into the shell root, like `Dialog` — see
565
+ * `useOverlayPortalTarget`. The panel supplies no padding of its own: content
566
+ * that needs a gutter brings it (the pattern `DatePicker`'s own `.drawerBody`
567
+ * already follows).
436
568
  */
437
- declare function Drawer({ open, onClose, 'aria-label': ariaLabel, snapHeight, title, children, }: DrawerProps): react.JSX.Element | null;
569
+ declare function Drawer({ open, onClose, 'aria-label': ariaLabel, snapHeight, title, children, }: DrawerProps): react.ReactPortal | null;
438
570
 
439
571
  interface OverlayHeaderProps {
440
572
  /** Overlay title, shown between the optional back button and the trailing
441
573
  * slots. Optional — some consumers (e.g. `Dialog`'s mobile bar) need the
442
- * row's close button present even when no title applies. */
443
- title?: string;
574
+ * row's close button present even when no title applies. Accepts any
575
+ * `ReactNode`, not just a string — e.g. `Dialog`'s `header` prop passes
576
+ * arbitrary content through here (an icon next to text, etc.); the
577
+ * row's title slot still assumes single-line content sized like text
578
+ * (see the component's own doc comment for the line this doesn't cross). */
579
+ title?: ReactNode;
444
580
  /** Always renders a close button; called on click. */
445
581
  onClose: () => void;
446
582
  /** When provided, renders a back-chevron button before the title (for
@@ -452,6 +588,15 @@ interface OverlayHeaderProps {
452
588
  /** A second row below the title row — e.g. a tab strip. */
453
589
  secondRow?: ReactNode;
454
590
  className?: string;
591
+ /** Override the title row's own className (e.g. tighter padding to match
592
+ * a consumer's own compact header elsewhere in the same surface) — the
593
+ * row keeps its standard title/back/action/close layout, just restyled.
594
+ * For a genuinely different shape, don't use `OverlayHeader` at all (see
595
+ * this component's own doc comment). */
596
+ rowClassName?: string;
597
+ /** Override the title text's own className (e.g. a smaller font size to
598
+ * match a consumer's own compact header elsewhere in the same surface). */
599
+ titleClassName?: string;
455
600
  }
456
601
  /**
457
602
  * OverlayHeader — the shared fixed secondary header for `Dialog`'s mobile
@@ -467,7 +612,7 @@ interface OverlayHeaderProps {
467
612
  * component — render its own header row instead; `Sheet`'s `title` prop is
468
613
  * optional specifically to leave that escape hatch open.
469
614
  */
470
- declare function OverlayHeader({ title, onClose, onBack, action, secondRow, className, }: OverlayHeaderProps): react.JSX.Element;
615
+ declare function OverlayHeader({ title, onClose, onBack, action, secondRow, className, rowClassName, titleClassName, }: OverlayHeaderProps): react.JSX.Element;
471
616
 
472
617
  interface SheetProps {
473
618
  /** Whether the sheet is shown. When false, nothing renders. */
@@ -513,9 +658,10 @@ interface SheetProps {
513
658
  * "outside" to tap-dismiss. Behaviour: Esc dismisses; focus moves into the
514
659
  * panel on open and is restored on close; Tab is trapped within the panel;
515
660
  * animated open/close via the same two-phase mount as `Dialog`/`Drawer`,
516
- * respecting `prefers-reduced-motion`.
661
+ * respecting `prefers-reduced-motion`. Renders through a portal into the
662
+ * shell root, like `Dialog`/`Drawer` — see `useOverlayPortalTarget`.
517
663
  */
518
- declare function Sheet({ open, onClose, 'aria-label': ariaLabel, title, onBack, headerAction, slideFrom, children, }: SheetProps): react.JSX.Element | null;
664
+ declare function Sheet({ open, onClose, 'aria-label': ariaLabel, title, onBack, headerAction, slideFrom, children, }: SheetProps): react.ReactPortal | null;
519
665
 
520
666
  interface ConfirmDialogProps {
521
667
  /** Whether the dialog is shown. */
@@ -562,7 +708,16 @@ declare function ConfirmDialog({ open, onClose, title, message, onConfirm, confi
562
708
  interface MenuItem {
563
709
  type?: 'item';
564
710
  label: string;
565
- onSelect: () => void;
711
+ /** Optional when `href` is provided (a pure navigation entry needs no
712
+ * extra callback) — required otherwise. Called before the surface closes
713
+ * is the caller's job to avoid; `Menu`'s own wiring always closes first,
714
+ * then calls this. */
715
+ onSelect?: () => void;
716
+ /** Renders the entry as a link (`<a href>`, `role="menuitem"`) instead of
717
+ * a `<button>` — same href-vs-onClick convention as `MobileAppsDrawer`'s
718
+ * items. Framework-agnostic (a plain anchor, not `next/link`), matching
719
+ * every other `packages/ui` href slot. */
720
+ href?: string;
566
721
  icon?: IconName;
567
722
  /** Styles the item as a destructive action (e.g. "Delete list"). */
568
723
  destructive?: boolean;
@@ -598,6 +753,15 @@ interface MenuProps {
598
753
  /** Forwarded to `Popover` on desktop. Has no effect on mobile, where the
599
754
  * menu is always a full-width `Drawer`. */
600
755
  align?: 'left' | 'right';
756
+ /** Forwarded to `Popover` on desktop (same default: 288). Has no effect
757
+ * on mobile. Narrow this when the trigger lives inside a column
758
+ * narrower than the default width — e.g. a per-row "⋯" menu in a
759
+ * sidebar list — so the panel fits within that column's own bounds
760
+ * instead of overflowing past its edge, where a scrollable ancestor's
761
+ * `overflow-y: auto` (which the CSS Overflow spec computes the paired
762
+ * `overflow-x` to `auto` as well, not `visible`, once either axis is
763
+ * set) would clip it. */
764
+ width?: number | 'trigger';
601
765
  }
602
766
  /**
603
767
  * Menu — an adaptive action menu: `Popover` on desktop, a bottom-sheet
@@ -628,7 +792,148 @@ declare function MenuEntries({ items, onSelect, }: {
628
792
  /** Called after an item's own onSelect — typically the surface's onClose. */
629
793
  onSelect: (entry: MenuItem) => void;
630
794
  }): react.JSX.Element;
631
- declare function Menu({ trigger, open, onClose, items, 'aria-label': ariaLabel, align }: MenuProps): react.JSX.Element;
795
+ declare function Menu({ trigger, open, onClose, items, 'aria-label': ariaLabel, align, width, }: MenuProps): react.JSX.Element;
796
+
797
+ interface UserMenuItem {
798
+ label: string;
799
+ icon: IconName;
800
+ /** Optional when `href` is provided (a pure navigation entry needs no
801
+ * extra callback). Required otherwise (e.g. "Sign out"). */
802
+ onSelect?: () => void;
803
+ /** Renders the entry as a link (`<a href>`) instead of a button — same
804
+ * href-vs-onClick convention as `MobileAppsDrawer`'s items. */
805
+ href?: string;
806
+ destructive?: boolean;
807
+ }
808
+ interface UserMenuProps {
809
+ name?: string;
810
+ email?: string;
811
+ avatarUrl?: string;
812
+ items: UserMenuItem[];
813
+ /** Trigger button's accessible label. Defaults to "Account". */
814
+ 'aria-label'?: string;
815
+ align?: 'left' | 'right';
816
+ /** Trigger avatar size: `'md'` (36px) matches the platform sidebar's own
817
+ * `.avatar` exactly — the default, for that context. `'sm'` (32px) suits
818
+ * the more compact `Header` top-bar context instead. The panel's own
819
+ * user-info avatar (38px) is unaffected either way — only the trigger
820
+ * scales. */
821
+ size?: 'sm' | 'md';
822
+ }
823
+ /**
824
+ * UserMenu — the account avatar trigger + dropdown, generalized from the
825
+ * runtime shell's own `AccountMenu.tsx` (the real, currently-shipped
826
+ * component that every `shell: minimal` plugin would otherwise have to
827
+ * hand-rebuild locally instead of importing). Same visual structure and
828
+ * copy: an accent-filled avatar trigger (36px, matching the sidebar's own
829
+ * `.avatar`), a name+email header block (38px avatar) above a divider, then
830
+ * a plain item list.
831
+ *
832
+ * Presentational only — no auth/session/routing logic of its own. The
833
+ * consumer supplies `items`, each with its own `onSelect` and/or `href`,
834
+ * matching `MobileHeader`'s "consumer wires it up" boundary; a real
835
+ * "Sign out" is just an item whose `onSelect` does whatever the consuming
836
+ * app's sign-out actually requires. Built on the shared `Popover` primitive
837
+ * (self-contained open state, matching the original's own architecture)
838
+ * rather than a hand-rolled outside-click/Escape listener — the original
839
+ * `AccountMenu` and every plugin-local rebuild of it independently re-derive
840
+ * that same logic; one primitive here means one implementation to get
841
+ * right. Always a small anchored dropdown, never a full-width sheet —
842
+ * this is deliberately not built on the adaptive `Menu` component (which
843
+ * becomes a bottom `Drawer` on mobile): the real account menu is a compact
844
+ * floating panel on every surface, mobile included.
845
+ */
846
+ declare function UserMenu({ name, email, avatarUrl, items, 'aria-label': ariaLabel, align, size, }: UserMenuProps): react.JSX.Element;
847
+
848
+ interface AppsLauncherItem {
849
+ /** Stable identity for this tile — used as the React key. */
850
+ key: string;
851
+ icon: ReactNode;
852
+ label: string;
853
+ /** Renders an `<a>` when set; otherwise a `<button>` calling `onClick` —
854
+ * same dual-mode convention as `MobileAppsDrawer`'s items. */
855
+ href?: string;
856
+ onClick?: () => void;
857
+ }
858
+ interface AppsLauncherProps {
859
+ items: AppsLauncherItem[];
860
+ /** Shows a spinner in place of the grid — e.g. while the tile list is
861
+ * still being fetched. */
862
+ loading?: boolean;
863
+ /** Shows an error message in place of the grid. */
864
+ error?: boolean;
865
+ errorMessage?: string;
866
+ /** Trigger button's icon. Defaults to a generic grid glyph — pass the
867
+ * real Launcher plugin's own icon for pixel parity with the platform. */
868
+ triggerIcon?: ReactNode;
869
+ /** Trigger button's accessible label, and the popover's own. Defaults to "Apps". */
870
+ 'aria-label'?: string;
871
+ align?: 'left' | 'right';
872
+ }
873
+ /**
874
+ * AppsLauncher — the desktop "Apps" switcher: a 28px grid-icon trigger that
875
+ * opens a `Popover` with a 3-column tile grid. The desktop counterpart to
876
+ * `MobileAppsDrawer` — same `items` shape (`key`/`icon`/`label`/`href`/
877
+ * `onClick`), different chrome (floating panel vs. bottom sheet), matching
878
+ * how `Header`/`MobileHeader` already split the same way.
879
+ *
880
+ * Presentational only — no plugin-list fetching of its own. The consumer
881
+ * supplies `items` already resolved (and `loading`/`error` while it isn't
882
+ * yet), matching `MobileAppsDrawer`'s own boundary.
883
+ */
884
+ declare function AppsLauncher({ items, loading, error, errorMessage, triggerIcon, 'aria-label': ariaLabel, align, }: AppsLauncherProps): react.JSX.Element;
885
+
886
+ interface NotificationsPanelItem {
887
+ id: string;
888
+ /** Category icon, already resolved (sized + colored) by the consumer —
889
+ * this component doesn't own category → color mapping. */
890
+ icon: ReactNode;
891
+ title: string;
892
+ /** Pre-formatted relative time (e.g. "2d ago") — formatting stays
893
+ * consumer-side so this component needs no Date/clock dependency. */
894
+ timeLabel: string;
895
+ read: boolean;
896
+ /** Renders the title as a link when set. */
897
+ href?: string;
898
+ /** Called when the title is clicked/tapped — typically "mark as read". */
899
+ onOpen?: () => void;
900
+ onDismiss: () => void;
901
+ }
902
+ interface NotificationsPanelProps {
903
+ items: NotificationsPanelItem[];
904
+ unreadCount: number;
905
+ onMarkAllRead?: () => void;
906
+ onClearAll?: () => void;
907
+ emptyMessage?: string;
908
+ /** Trigger button's accessible label base — the unread count is appended
909
+ * automatically, matching the real NotificationBell. Defaults to
910
+ * "Notifications". */
911
+ 'aria-label'?: string;
912
+ align?: 'left' | 'right';
913
+ /** Optional footer link (e.g. to the consumer's own full notification
914
+ * list/detail view — RFC 0048) — mirrors the real `NotificationBell`'s
915
+ * "View all" link. Omit to render no footer, matching this component's
916
+ * behavior before this prop existed. */
917
+ viewAllHref?: string;
918
+ }
919
+ /**
920
+ * NotificationsPanel — the bell trigger + dropdown, generalized from the
921
+ * runtime shell's own `NotificationBell.tsx` visual structure and copy
922
+ * (title / Mark all read / Clear all / close, then a list of category-icon +
923
+ * title + time + unread-dot + dismiss rows, or an empty state).
924
+ *
925
+ * Deliberately **presentational only** — no fetch/SSE/polling, no
926
+ * category → color mapping, no mark-read/dismiss networking. The real
927
+ * `NotificationBell` owns real platform data (`/api/account/notifications`,
928
+ * a shared cross-instance store); this component can't and shouldn't know
929
+ * about that. The consumer supplies `items` already resolved
930
+ * and wires `onOpen`/`onDismiss`/`onMarkAllRead`/`onClearAll` to whatever
931
+ * its own data layer needs, against a shared panel instead of a
932
+ * `shell: minimal` plugin hand-rebuilding one locally. Built on the shared
933
+ * `Popover` primitive (self-contained open state) rather than the
934
+ * original's hand-rolled outside-click/Escape listener.
935
+ */
936
+ declare function NotificationsPanel({ items, unreadCount, onMarkAllRead, onClearAll, emptyMessage, 'aria-label': ariaLabel, align, viewAllHref, }: NotificationsPanelProps): react.JSX.Element;
632
937
 
633
938
  interface ToastItem {
634
939
  id: string;
@@ -660,6 +965,64 @@ interface CardProps extends HTMLAttributes<HTMLElement> {
660
965
  }
661
966
  declare function Card({ as: Tag, interactive, padding, className, children, ...rest }: CardProps): react.JSX.Element;
662
967
 
968
+ interface CardTileGridProps {
969
+ children: ReactNode;
970
+ /** Minimum tile width (px) before the grid wraps to fewer columns. */
971
+ minTileWidth?: number;
972
+ className?: string;
973
+ /**
974
+ * Tight spacing with tiles that keep their natural width instead of
975
+ * stretching to fill the row — for small `variant="icon"` tiles (e.g. a
976
+ * folder grid), where the default grid's spacious gap and `1fr`-stretched
977
+ * columns leave a lot of empty space around a tile that's really just an
978
+ * icon + short label.
979
+ */
980
+ dense?: boolean;
981
+ }
982
+ /** Responsive grid of `CardTile`/`NewCardTile` children — `repeat(auto-fill, minmax(minTileWidth, 1fr))`, or `minmax(minTileWidth, max-content)` with a tighter gap when `dense`. */
983
+ declare function CardTileGrid({ children, minTileWidth, className, dense, }: CardTileGridProps): react.JSX.Element;
984
+ interface CardTileProps extends HTMLAttributes<HTMLDivElement> {
985
+ /** Content centered in the tile's full-bleed top banner — typically an `<Icon>`. Omit for a plain strip. */
986
+ banner?: ReactNode;
987
+ /** Banner background color (applied inline). Falls back to a neutral sunken-surface strip when omitted. Ignored by `variant="icon"`, which has no banner box. */
988
+ bannerColor?: string;
989
+ /** Tile footer content — typically the item's name. */
990
+ children: ReactNode;
991
+ /**
992
+ * `"card"` (default): a bordered card with a colored/iconed banner strip
993
+ * over a footer label. `"icon"`: a plain "Finder icon" tile — no card
994
+ * chrome at all, just a large centered icon with a label beneath it. Use
995
+ * `"icon"` for entities that read as a single glyph + name (e.g. a
996
+ * folder), where a bordered card reads as heavier than the content needs.
997
+ */
998
+ variant?: 'card' | 'icon';
999
+ }
1000
+ /**
1001
+ * A colored/iconed banner over a footer label — a card shape `Card` can't
1002
+ * express, since `Card`'s `padding` prop always pads its single box with no
1003
+ * way for a top region to bleed to the edges. Not link-aware: wrap it in the
1004
+ * host framework's own link component (e.g. Next's `<Link>`) to make it
1005
+ * navigable, same as every existing consumer of this pattern does.
1006
+ */
1007
+ declare function CardTile({ banner, bannerColor, children, className, variant, ...rest }: CardTileProps): react.JSX.Element;
1008
+ interface NewCardTileProps extends ButtonHTMLAttributes<HTMLButtonElement> {
1009
+ /** Icon shown beside the label (`"card"`) or alone (`"icon"`). Defaults to `"plus"`. */
1010
+ icon?: IconName;
1011
+ /** Visible next to the icon for `"card"`. For `"icon"`, not rendered as text — used as the button's `aria-label` instead, since the tile is icon-only. */
1012
+ label: string;
1013
+ /**
1014
+ * `"card"` (default): a dashed box matching a `variant="card"` `CardTile`'s
1015
+ * footprint — icon beside a visible label. `"icon"`: matches a
1016
+ * `variant="icon"` `CardTile`'s footprint instead, but icon-only, no
1017
+ * visible label — a plain "+" reads as self-explanatory once every
1018
+ * sibling tile in the same `dense` grid is already an unlabeled icon
1019
+ * (the dashed border alone marks it as the "add new" affordance).
1020
+ */
1021
+ variant?: 'card' | 'icon';
1022
+ }
1023
+ /** Dashed ghost tile matching a real `CardTile`'s footprint — the grid's own "add new" affordance. */
1024
+ declare function NewCardTile({ icon, label, type, variant, className, ...rest }: NewCardTileProps): react.JSX.Element;
1025
+
663
1026
  /** Props to spread onto the field's actual form control. */
664
1027
  interface FormFieldRenderProps {
665
1028
  id: string;
@@ -690,23 +1053,127 @@ declare function FormField({ label, hint, error, id, required, className, childr
690
1053
  interface PageHeaderProps {
691
1054
  title: string;
692
1055
  description?: string;
1056
+ /** Optional icon rendered inline before the title text, on both breakpoints — e.g. `folder-open` next to a folder's name. */
1057
+ icon?: IconName;
1058
+ /**
1059
+ * Page-level controls — anywhere from one button to a whole toolbar.
1060
+ * Renders on both breakpoints unchanged; if it needs to look different on
1061
+ * mobile (e.g. collapsing into its own "⋮" trigger), that's the action
1062
+ * content's own responsibility — same pattern Shopper's `ListHeaderActions`
1063
+ * already follows (it calls `useIsMobile()` internally). `PageHeader`
1064
+ * itself has no opinion on what's inside this slot.
1065
+ */
693
1066
  action?: ReactNode;
694
1067
  className?: string;
695
1068
  /** Heading level for the title. Defaults to `1` for standalone use; pass
696
1069
  * `2` or `3` when the page already sits under a shell/plugin `<h1>`, to
697
1070
  * avoid a duplicate top-level heading. Visual style is unchanged at every
698
- * level — only the rendered tag changes. */
1071
+ * level — only the rendered tag changes. Applies on both breakpoints. */
699
1072
  headingLevel?: 1 | 2 | 3;
1073
+ /** Mobile only — shows a back button (calling this) to the left of the
1074
+ * title when provided. Omit it to render without one. */
1075
+ onBack?: () => void;
1076
+ /** Mobile only — shows a vertical-ellipsis icon button (calling this) at
1077
+ * the far right when provided, typically to open a drawer/menu. PageHeader
1078
+ * has no opinion on what that menu contains — it only renders the trigger
1079
+ * and calls back. Separate from `action`, which renders identically on
1080
+ * both breakpoints; this is a mobile-only affordance with no web
1081
+ * equivalent. */
1082
+ onMenuClick?: () => void;
700
1083
  }
701
- declare function PageHeader({ title, description, action, className, headingLevel, }: PageHeaderProps): react.JSX.Element;
1084
+ /**
1085
+ * PageHeader — a page's title section. Web: title + description on the
1086
+ * left, action on the right. Mobile: an optional back button, a single-line
1087
+ * title, and the same action slot — a compact single row, matching Kanban's
1088
+ * own hand-rolled MobileBoardHeader this was generalized from. The
1089
+ * web/mobile fork is `ResponsiveSurface` — only one tree is ever mounted.
1090
+ */
1091
+ declare function PageHeader({ title, description, icon, action, className, headingLevel, onBack, onMenuClick, }: PageHeaderProps): react.JSX.Element;
702
1092
 
703
1093
  interface PageContainerProps extends HTMLAttributes<HTMLDivElement> {
704
- /** Constrains and centers content width. Adds no padding of its own — the
705
- * runtime shell already pads plugin content. Defaults to 'md'. */
1094
+ /**
1095
+ * Constrains and centers content width. The bound includes `padding`
1096
+ * (the container is `border-box`). Defaults to `'full'` — narrowing is
1097
+ * opt-in, so wrapping a page in `PageContainer` never silently clamps it.
1098
+ */
706
1099
  maxWidth?: 'sm' | 'md' | 'lg' | 'full';
1100
+ /**
1101
+ * The page gutter. Responsive — each step is tighter at ≤768px. Defaults
1102
+ * to `'md'`, which is exactly what the runtime shell used to apply
1103
+ * (`--sv-space-8` desktop / `--sv-space-4` mobile), so a bare
1104
+ * `<PageContainer>` is the correct wrapper for a plugin that previously
1105
+ * relied on the shell for its padding.
1106
+ *
1107
+ * Use `'none'` when an ancestor already supplies the gutter — inside a
1108
+ * `Dialog`, whose own content region is padded.
1109
+ */
1110
+ padding?: 'none' | 'sm' | 'md' | 'lg';
707
1111
  children: ReactNode;
708
1112
  }
709
- declare function PageContainer({ maxWidth, className, children, ...rest }: PageContainerProps): react.JSX.Element;
1113
+ /**
1114
+ * The shared page wrapper every plugin's own layout or page renders through.
1115
+ *
1116
+ * A plugin owns its page gutter; the runtime shell does not impose one
1117
+ * (task 9.25). The shell still reserves space for chrome a plugin cannot
1118
+ * know about — the offline banner's height and the mobile footer's — but it
1119
+ * contributes no inset of its own, so this component is the single place a
1120
+ * plugin's four-sided padding comes from.
1121
+ */
1122
+ declare function PageContainer({ maxWidth, padding, className, children, ...rest }: PageContainerProps): react.JSX.Element;
1123
+
1124
+ interface HeaderPlugin {
1125
+ /** Plugin display name, rendered next to its icon. */
1126
+ name: string;
1127
+ /** Plugin icon — already sized (28px) and built by the consumer (typically
1128
+ * an `<img src="/plugin-icons/<id>.svg">`), matching `MobileHeader`'s own
1129
+ * router-agnostic convention for icon-shaped slots. */
1130
+ icon?: ReactNode;
1131
+ /** Where the plugin name/icon links to — typically the plugin's own
1132
+ * `routePrefix`. Omit to render plain (non-interactive) text instead of a
1133
+ * link — e.g. a plugin's own home page linking to itself would be a no-op. */
1134
+ href?: string;
1135
+ }
1136
+ interface HeaderProps extends HTMLAttributes<HTMLElement> {
1137
+ /** Instance name — derives the brand badge's initial (its first character,
1138
+ * uppercased) and the badge's accessible label. Does not render as text
1139
+ * anywhere in this component (unlike `MobileHeader`'s optional `title`). */
1140
+ instanceName: string;
1141
+ /** Optional instance logo image; falls back to `instanceName`'s initial
1142
+ * (matching the runtime shell's own sidebar/mobile-header fallback). */
1143
+ instanceLogoUrl?: string;
1144
+ /** Where the brand badge links — typically the Launcher plugin's route. */
1145
+ homeHref: string;
1146
+ /** The active plugin, rendered next to the brand badge. Omit for a page
1147
+ * with no single active plugin (e.g. the Launcher itself). */
1148
+ plugin?: HeaderPlugin;
1149
+ /** Apps launcher trigger — already wired by the consumer (icon button plus
1150
+ * whatever popover/drawer it opens). Always rendered, matching
1151
+ * `MobileHeader`'s bell/avatarMenu boundary: this component owns layout
1152
+ * only, never launcher/notification/account interaction logic. */
1153
+ launcher: ReactNode;
1154
+ /** Notification bell trigger — always rendered; not overridable. Same
1155
+ * "just an icon slot" boundary as `MobileHeader`'s own `bell` prop. */
1156
+ notifications: ReactNode;
1157
+ /** Avatar / account menu trigger — always rendered; not overridable. */
1158
+ avatarMenu: ReactNode;
1159
+ }
1160
+ /**
1161
+ * The web top-bar header: brand badge (+ optional active plugin) on the
1162
+ * left, launcher · notifications · avatar menu on the right. This is the
1163
+ * `shell: minimal` counterpart to the runtime shell's own sidebar — a plugin
1164
+ * that owns its whole viewport (no platform sidebar) renders this instead,
1165
+ * the same way it renders `MobileHeader` in place of the platform's mobile
1166
+ * chrome. Extracted into a shared component so a `shell: minimal` plugin
1167
+ * doesn't have to hand-roll its own top bar from scratch.
1168
+ *
1169
+ * Presentational only — no data fetching, no account/notification/launcher
1170
+ * logic of its own. `launcher`/`notifications`/`avatarMenu` are supplied
1171
+ * fully built, exactly like `MobileHeader`'s `bell`/`avatarMenu`: this
1172
+ * component stays router-agnostic and interaction-agnostic, and never
1173
+ * duplicates a real `AccountMenu`/notification-panel/apps-switcher
1174
+ * implementation.
1175
+ */
1176
+ declare function Header({ instanceName, instanceLogoUrl, homeHref, plugin, launcher, notifications, avatarMenu, className, ...rest }: HeaderProps): react.JSX.Element;
710
1177
 
711
1178
  interface MobileHeaderProps extends HTMLAttributes<HTMLDivElement> {
712
1179
  /** The brand/logo element — already a complete, clickable link home (e.g. an
@@ -744,6 +1211,13 @@ interface MobileHeaderProps extends HTMLAttributes<HTMLDivElement> {
744
1211
  * consumer's responsibility, matching the reference implementation in the
745
1212
  * runtime shell). See RFC 0088 for the immutable/overridable boundary this
746
1213
  * enforces.
1214
+ *
1215
+ * Self-measures its own rendered height and publishes it as
1216
+ * `--sv-shell-header-height` on the shell root — see
1217
+ * `usePublishShellChromeHeight`'s own doc comment for why this exists (it's
1218
+ * what makes a self-rendered header, the `shellConfig.mobileHeader: false`
1219
+ * pattern, correctly position `Dialog`'s inset and any other consumer of
1220
+ * that variable instead of being overlapped by it).
747
1221
  */
748
1222
  declare function MobileHeader({ logo, title, bell, avatarMenu, className, ...rest }: MobileHeaderProps): react.JSX.Element;
749
1223
 
@@ -782,6 +1256,13 @@ interface MobileFooterProps extends HTMLAttributes<HTMLElement> {
782
1256
  * Presentational only — no data fetching; the consumer owns the
783
1257
  * drawer/overlay `onOpenApps` opens. See RFC 0088 for the
784
1258
  * immutable/overridable boundary this enforces.
1259
+ *
1260
+ * Self-measures its own rendered height and publishes it as
1261
+ * `--sv-shell-footer-height` on the shell root — see
1262
+ * `usePublishShellChromeHeight`'s own doc comment for why this exists (it's
1263
+ * what makes a self-rendered footer, the `shellConfig.mobileFooter: false`
1264
+ * pattern, correctly clear `Sheet`/`Drawer`/`Dialog` instead of being
1265
+ * overlapped by them).
785
1266
  */
786
1267
  declare function MobileFooter({ onOpenApps, launcherIcon, launcherOpen, leftIcons, rightIcons, className, ...rest }: MobileFooterProps): react.JSX.Element;
787
1268
 
@@ -859,11 +1340,10 @@ interface DeviceOnlyGateProps {
859
1340
  /** Defaults to a generic "this app" phrasing — pass the plugin's own name. */
860
1341
  surfaceName?: string;
861
1342
  /**
862
- * The caller's own `isDeviceOnlyTierAvailable()` result (from
863
- * `@sovereignfs/sdk/device-client`) — taken as a plain boolean rather than
864
- * computed inside this component, so `@sovereignfs/ui` never depends on
865
- * `@sovereignfs/sdk` (this package must stay usable standalone, outside the
866
- * plugin runtime — RFC 0073).
1343
+ * Whether the device-only tier is available here, as the caller works it
1344
+ * out — taken as a plain boolean rather than computed inside this
1345
+ * component, so `@sovereignfs/ui` depends on no app runtime (this package
1346
+ * must stay usable standalone).
867
1347
  */
868
1348
  available: boolean;
869
1349
  className?: string;
@@ -906,12 +1386,10 @@ interface DeviceStorageKeyGateProps {
906
1386
  /** Defaults to a generic "This app" phrasing — pass the plugin's own name. */
907
1387
  surfaceName?: string;
908
1388
  /**
909
- * The caller's own `getDeviceStorageKeyStatus()` result (from
910
- * `@sovereignfs/sdk/device-only-storage`) — taken as a plain status rather
911
- * than computed inside this component, so `@sovereignfs/ui` never depends
912
- * on `@sovereignfs/sdk` (same boundary `DeviceOnlyGate`'s `available` prop
913
- * draws — this package must stay usable standalone, outside the plugin
914
- * runtime — RFC 0073).
1389
+ * The caller's own device-storage-key status — taken as a plain status
1390
+ * rather than computed inside this component, so `@sovereignfs/ui` depends
1391
+ * on no app runtime (same boundary `DeviceOnlyGate`'s `available` prop
1392
+ * draws — this package must stay usable standalone).
915
1393
  */
916
1394
  status: DeviceStorageKeyGateStatus;
917
1395
  /**
@@ -1215,6 +1693,28 @@ interface AspectRatioProps {
1215
1693
  /** AspectRatio — constrains content (image, video, embed) to a fixed ratio. */
1216
1694
  declare function AspectRatio({ ratio, children, className }: AspectRatioProps): react.JSX.Element;
1217
1695
 
1696
+ type ButtonLinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> & {
1697
+ /** Where it goes. */
1698
+ href: string;
1699
+ variant?: ButtonVariant;
1700
+ size?: ButtonSize;
1701
+ children: ReactNode;
1702
+ /**
1703
+ * How the link element is made, so a router's own link can be used without this package knowing the router
1704
+ * (the same way `NavTabs` takes its links). It is given every prop the anchor would have, `className` included,
1705
+ * and must pass them on. Without it, a plain `<a>` is rendered.
1706
+ */
1707
+ renderLink?: (props: AnchorHTMLAttributes<HTMLAnchorElement> & {
1708
+ href: string;
1709
+ }) => ReactNode;
1710
+ };
1711
+ /**
1712
+ * ButtonLink — a link that looks like a `Button`, for an action that goes to another page ("Add an app")
1713
+ * where a `<button>` would be the wrong element. It shares the button's styling, so the two never drift.
1714
+ * Presentational and RSC-safe.
1715
+ */
1716
+ declare function ButtonLink({ variant, size, className, children, renderLink, ...rest }: ButtonLinkProps): string | number | bigint | boolean | react.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
1717
+
1218
1718
  interface ButtonGroupProps {
1219
1719
  /** Adjacent Button elements. */
1220
1720
  children: ReactNode;
@@ -1331,6 +1831,17 @@ interface MessageScrollerProps {
1331
1831
  * content does not yank them back down — a "New messages" button appears
1332
1832
  * instead, matching the pattern every chat product (Slack, Discord,
1333
1833
  * ChatGPT) already uses.
1834
+ *
1835
+ * A short conversation is bottom-anchored, not left floating at the top of
1836
+ * the container with empty space below it — a leading flex-grow spacer
1837
+ * absorbs the leftover space and collapses to nothing once messages
1838
+ * overflow. Deliberately not `justify-content: flex-end` on the scroll
1839
+ * container itself: Chromium miscomputes `scrollHeight` as equal to
1840
+ * `clientHeight` for an overflowing flex-end-packed column, which makes the
1841
+ * container look fully scrolled to the bottom and permanently hides the
1842
+ * start of a long conversation (confirmed live before choosing this
1843
+ * approach). The spacer keeps `justify-content` at its default, so overflow
1844
+ * scrolling is unaffected.
1334
1845
  */
1335
1846
  declare function MessageScroller({ children, className }: MessageScrollerProps): react.JSX.Element;
1336
1847
 
@@ -1601,6 +2112,48 @@ interface DragHandleRowProps extends HTMLAttributes<HTMLDivElement> {
1601
2112
  */
1602
2113
  declare function DragHandleRow({ children, handleProps, isDragging, className, ...rest }: DragHandleRowProps): react.JSX.Element;
1603
2114
 
2115
+ interface GripIconProps {
2116
+ size?: number;
2117
+ className?: string;
2118
+ }
2119
+ /** Six-dot drag-handle affordance. No baked-in pointer-events or color —
2120
+ * a consumer that needs `pointer-events: none` (e.g. a button wrapping it)
2121
+ * or a specific color passes those via `className`/`currentColor`. */
2122
+ declare function GripIcon({ size, className }: GripIconProps): react.JSX.Element;
2123
+
2124
+ interface StepStripItem {
2125
+ id: string;
2126
+ }
2127
+ interface StepStripProps<T extends StepStripItem> {
2128
+ items: T[];
2129
+ activeId: string | null;
2130
+ /** Full control over each chip's content, click handling, and (if needed) drag-reorder wiring. */
2131
+ renderItem: (item: T, state: {
2132
+ isActive: boolean;
2133
+ index: number;
2134
+ }) => ReactNode;
2135
+ /** Trailing dashed "add" chip — omit to render the strip with no add affordance. */
2136
+ onAdd?: () => void;
2137
+ addLabel?: string;
2138
+ className?: string;
2139
+ 'aria-label'?: string;
2140
+ }
2141
+ /**
2142
+ * StepStrip — a horizontal, connected sequence of steps (a route or
2143
+ * itinerary strip): ordered chips joined by a thin connector line, with an
2144
+ * optional trailing dashed "add" chip. Purely presentational — it owns
2145
+ * layout and the connector lines, nothing else.
2146
+ *
2147
+ * Each chip's content, active styling, and click handling are entirely up
2148
+ * to `renderItem`; if a consumer needs drag-reorder, `renderItem` attaches
2149
+ * its own dnd-kit `useSortable` ref/listeners to whatever it returns. This
2150
+ * mirrors `DragHandleRow`'s existing split (DS owns chrome, consumer wires
2151
+ * dnd-kit via passed-through props) generalized for a horizontal,
2152
+ * handle-less strip where the whole rendered item — not a separate handle
2153
+ * icon — is both the click target and the drag surface.
2154
+ */
2155
+ declare function StepStrip<T extends StepStripItem>({ items, activeId, renderItem, onAdd, addLabel, className, 'aria-label': ariaLabel, }: StepStripProps<T>): react.JSX.Element;
2156
+
1604
2157
  interface FileDropzoneProps {
1605
2158
  /** Native `<input type="file">` `name` attribute — set this to submit the
1606
2159
  * file as part of a native `<form action={...}>` (Server Action) request. */
@@ -1632,11 +2185,8 @@ interface FileDropzoneProps {
1632
2185
  * (e.g. swap to the picked file's name/size) — this component is otherwise
1633
2186
  * uncontrolled and stateless beyond its own drag-hover visual.
1634
2187
  *
1635
- * Do not pass `required` through to a wrapping form's validation via this
1636
- * field — `display: none` on the hidden input does not exempt it from HTML5
1637
- * constraint validation, so a `required` hidden input can block submission
1638
- * with no visible field for the user to focus. Validate `onFileSelect`'s
1639
- * result in application code instead.
2188
+ * The native input is hidden to the eye and not to the keyboard (it stays in the tab order), so the dropzone takes focus,
2189
+ * shows the focus ring, and opens the picker on Space and Enter, and a form's `required` validation can focus it.
1640
2190
  */
1641
2191
  declare function FileDropzone({ name, accept, icon, label, hint, onFileSelect, ariaLabel, disabled, className, }: FileDropzoneProps): react.JSX.Element;
1642
2192
 
@@ -1749,6 +2299,63 @@ interface IconPickerProps {
1749
2299
  */
1750
2300
  declare function IconPicker({ value, onChange, options, 'aria-label': ariaLabel, triggerLabel, disabled, className, }: IconPickerProps): react.JSX.Element;
1751
2301
 
2302
+ interface ColorPickerSwatch {
2303
+ /** Accessible name and tooltip, e.g. "Sky". */
2304
+ label: string;
2305
+ /** 6-digit hex, e.g. "#b5c9e8". */
2306
+ value: string;
2307
+ }
2308
+ interface ColorPickerProps {
2309
+ /** Curated quick-pick suggestions, in display order. */
2310
+ swatches: readonly ColorPickerSwatch[];
2311
+ /** Current hex value, or `null` for no color. Compared case-insensitively
2312
+ * against `swatches` — a value that doesn't match any swatch renders as
2313
+ * the active "custom" selection instead. */
2314
+ value: string | null;
2315
+ onChange: (value: string | null) => void;
2316
+ /**
2317
+ * Fires once after a discrete, complete pick — a swatch click or the "no
2318
+ * color" click. Optional; most callers don't need it. Deliberately NOT
2319
+ * fired from the native custom-color `<input type="color">`'s `onChange`
2320
+ * — that can fire repeatedly while the user is still dragging inside the
2321
+ * browser's own color dialog, so a caller using this to auto-close a
2322
+ * wrapping `Popover` would otherwise slam it shut mid-adjustment.
2323
+ */
2324
+ onSelectionComplete?: () => void;
2325
+ /** Shows a leading "no color" option. Off by default — most consumers
2326
+ * (anything painting a fixed-size chip) always need a real color; only
2327
+ * a board canvas-style consumer typically wants "none" as a real choice. */
2328
+ allowNone?: boolean;
2329
+ noneLabel?: string;
2330
+ /** Accessible name for the native color-input trigger that lets a user
2331
+ * pick any color, not just a curated swatch. */
2332
+ customLabel?: string;
2333
+ disabled?: boolean;
2334
+ 'aria-label': string;
2335
+ className?: string;
2336
+ }
2337
+ /**
2338
+ * ColorPicker — a row of curated swatch suggestions plus a native
2339
+ * `<input type="color">` trigger for picking any color, not just the
2340
+ * curated set. The native color input is deliberately not a custom-built
2341
+ * hue/saturation picker: it already gives every browser's own full-spectrum
2342
+ * picker (with an eyedropper tool in most), guarantees valid hex output, and
2343
+ * needs no bespoke a11y work of its own.
2344
+ *
2345
+ * `value` drives which affordance reads as selected: a hex matching a
2346
+ * swatch highlights that swatch; a hex matching none of them highlights the
2347
+ * custom trigger instead (its own dial shows that color); `null` (only
2348
+ * meaningful with `allowNone`) highlights the "no color" option.
2349
+ *
2350
+ * The custom trigger shows a plain "+" on a neutral dashed box until a
2351
+ * genuinely custom color is active — it never previews an arbitrary dial
2352
+ * color the user hasn't actually chosen. The underlying native color input
2353
+ * still needs *some* real hex to hold internally at all times (it has no
2354
+ * true empty state), so that starting value stays invisible (`opacity: 0`
2355
+ * on the input itself) rather than painted as this trigger's own swatch.
2356
+ */
2357
+ declare function ColorPicker({ swatches, value, onChange, onSelectionComplete, allowNone, noneLabel, customLabel, disabled, 'aria-label': ariaLabel, className, }: ColorPickerProps): react.JSX.Element;
2358
+
1752
2359
  interface QuantityStepperProps {
1753
2360
  value: number;
1754
2361
  onChange: (value: number) => void;
@@ -1813,14 +2420,31 @@ interface CheckableListRowProps {
1813
2420
  declare function CheckableListRow({ checked, onCheckedChange, label, icon, trailing, disabled, className, }: CheckableListRowProps): react.JSX.Element;
1814
2421
 
1815
2422
  interface CurrencyInputProps extends Omit<InputProps, 'value' | 'onChange' | 'type' | 'inputMode'> {
1816
- /** Smallest currency unit (cents) — never a float. Null when the field is empty or unparsable. */
2423
+ /** Smallest currency unit — never a float. Null when the field is empty or unparsable.
2424
+ * How many of these make one major unit is `decimals` (hundredths by default). */
1817
2425
  valueCents: number | null;
1818
2426
  onValueChange: (cents: number | null) => void;
2427
+ /**
2428
+ * Fraction digits the currency uses — the exponent relating `valueCents` to
2429
+ * the amount on screen (`valueCents / 10 ** decimals`). Defaults to `2`, so
2430
+ * every existing caller keeps its cents behaviour unchanged.
2431
+ *
2432
+ * Not every currency is hundredths: ISO 4217 gives JPY and KRW zero digits
2433
+ * and several Gulf currencies three, and a consumer tracking BTC needs
2434
+ * eight (one satoshi is 1e-8 BTC — at two digits the smallest expressible
2435
+ * amount is 0.01 BTC, which is not a usable granularity for the asset).
2436
+ * Pass the currency's own exponent and `valueCents` stays an exact integer
2437
+ * in that currency's smallest unit, which is what keeps money out of
2438
+ * floating point everywhere else.
2439
+ */
2440
+ decimals?: number;
1819
2441
  }
1820
2442
  /**
1821
- * CurrencyInput — decimal amount entry that reports its value as integer
1822
- * cents, matching Sovereign's "amounts are always smallest-unit integers"
1823
- * data-model convention (never a float in application state).
2443
+ * CurrencyInput — decimal amount entry that reports its value as an integer
2444
+ * number of the currency's smallest units, matching Sovereign's "amounts are
2445
+ * always smallest-unit integers" data-model convention (never a float in
2446
+ * application state). `decimals` says how many of those units make one major
2447
+ * unit; it defaults to 2, so a caller that doesn't pass it gets cents.
1824
2448
  *
1825
2449
  * Keeps its own text buffer rather than deriving display text from
1826
2450
  * `valueCents` on every keystroke — reformatting mid-type (e.g. "12." → the
@@ -1828,9 +2452,10 @@ interface CurrencyInputProps extends Omit<InputProps, 'value' | 'onChange' | 'ty
1828
2452
  * re-syncs from `valueCents` when the prop changes to something the current
1829
2453
  * text doesn't already represent (an external reset/prefill), so typing
1830
2454
  * remains uninterrupted while the parsed amount stays in lockstep with the
1831
- * parent's state.
2455
+ * parent's state. Changing `decimals` re-syncs for the same reason: the same
2456
+ * text means a different integer once the exponent moves.
1832
2457
  */
1833
- declare function CurrencyInput({ valueCents, onValueChange, ...rest }: CurrencyInputProps): react.JSX.Element;
2458
+ declare function CurrencyInput({ valueCents, onValueChange, decimals, ...rest }: CurrencyInputProps): react.JSX.Element;
1834
2459
 
1835
2460
  interface BalanceChipProps {
1836
2461
  /** Smallest currency unit (cents). Positive = owed to them, negative = they owe, zero = settled. */
@@ -2036,12 +2661,10 @@ type OfflineTileState = 'connectivity-dimmed' | 'capability-restricted' | null;
2036
2661
  * Which of the two states, if any, applies to an app tile given its declared
2037
2662
  * offline tier.
2038
2663
  *
2039
- * `deviceOnlyAvailable` is the caller's own
2040
- * `isDeviceOnlyTierAvailable()` result (from
2041
- * `@sovereignfs/sdk/device-client`) — deliberately taken as a plain boolean
2042
- * parameter rather than computed inside this hook, so `@sovereignfs/ui` never
2043
- * depends on `@sovereignfs/sdk` (this package must stay usable standalone,
2044
- * outside the plugin runtime — RFC 0073).
2664
+ * `deviceOnlyAvailable` is whether the device-only tier is available here, as
2665
+ * the caller works it out — deliberately taken as a plain boolean parameter
2666
+ * rather than computed inside this hook, so `@sovereignfs/ui` depends on no app
2667
+ * runtime (this package must stay usable standalone).
2045
2668
  *
2046
2669
  * An `offline-first` app is never restricted by either state: that tier's
2047
2670
  * whole point is working with no network, so it stays fully interactive both
@@ -2132,12 +2755,25 @@ interface UseSnapCarouselOptions {
2132
2755
  * extracted from. Debounced rather than keyed to the `scrollend` event,
2133
2756
  * which pre-17.4 iOS Safari/WKWebView doesn't support. */
2134
2757
  debounceMs?: number;
2758
+ /** The index to seed `liveIndex` with before any scroll event has fired —
2759
+ * must match whatever the caller initially scrolls/renders to (typically
2760
+ * the same value used for `SwipableMobileCarousel`'s `activeIndex`).
2761
+ * Without this, `liveIndex` would start at 0 regardless of where the
2762
+ * carousel actually opens (e.g. a deep link straight to slide 8),
2763
+ * briefly unmounting every slide except the first few until the first
2764
+ * scroll event arrives to correct it. */
2765
+ initialIndex?: number;
2135
2766
  }
2136
2767
  interface UseSnapCarouselResult {
2137
2768
  /** Attach to the `scroll-snap-type: x` scroll container. */
2138
2769
  scrollRef: RefObject<HTMLDivElement | null>;
2139
2770
  /** Imperatively scrolls the container to a slide index. */
2140
2771
  scrollToIndex: (index: number, behavior?: ScrollBehavior) => void;
2772
+ /** The slide index nearest the container's *current* scroll position,
2773
+ * updated synchronously on every `scroll` event — not debounced, and not
2774
+ * the same thing as the last-reported `onSettle` index. See its own
2775
+ * assignment below for why a consumer needs both. */
2776
+ liveIndex: number;
2141
2777
  }
2142
2778
  /**
2143
2779
  * useSnapCarousel — debounced "settled slide" detection over a native
@@ -2147,7 +2783,7 @@ interface UseSnapCarouselResult {
2147
2783
  * Gives swipe-between-slides physics for free via native scroll-snap, with
2148
2784
  * this hook only handling "which slide did we land on."
2149
2785
  */
2150
- declare function useSnapCarousel({ itemCount, onSettle, debounceMs, }: UseSnapCarouselOptions): UseSnapCarouselResult;
2786
+ declare function useSnapCarousel({ itemCount, onSettle, debounceMs, initialIndex, }: UseSnapCarouselOptions): UseSnapCarouselResult;
2151
2787
 
2152
2788
  interface UseResponsiveLayoutOptions<TWeb, TMobile> {
2153
2789
  web: TWeb;
@@ -2212,6 +2848,89 @@ interface UseCarouselRouteSyncResult {
2212
2848
  */
2213
2849
  declare function useCarouselRouteSync({ indexForPathname, pathForIndex, pathname, onNavigate, }: UseCarouselRouteSyncOptions): UseCarouselRouteSyncResult;
2214
2850
 
2851
+ /**
2852
+ * True when a drag should be allowed to start from `target`. Refused when
2853
+ * `target` sits inside an element marked `data-no-dnd` — a nested control
2854
+ * (e.g. a visibility toggle) opts out so tapping it fires its own handler
2855
+ * instead of lifting the row. Exported standalone so it's unit-testable
2856
+ * without spinning up dnd-kit.
2857
+ */
2858
+ declare function shouldHandleDndEvent(target: EventTarget | null): boolean;
2859
+ interface UseReorderSensorsOptions {
2860
+ mouseActivationDistancePx?: number;
2861
+ touchActivationDelayMs?: number;
2862
+ touchActivationTolerancePx?: number;
2863
+ }
2864
+ /**
2865
+ * Sensor set for a `data-no-dnd`-aware drag-reorder list — MouseSensor for
2866
+ * desktop's handle-initiated drag, TouchSensor for mobile's long-press lift
2867
+ * (fixes reordering being unusable on iOS PWA/Safari, which never implements
2868
+ * the native HTML5 Drag-and-Drop API for touch input), KeyboardSensor
2869
+ * unchanged. A bare call keeps the historical 8px/300ms/8px defaults.
2870
+ */
2871
+ declare function useReorderSensors(options?: UseReorderSensorsOptions): _dnd_kit_core.SensorDescriptor<_dnd_kit_core.SensorOptions>[];
2872
+
2873
+ type SwipeDirection = 'left' | 'right' | 'up' | 'down';
2874
+ /** Duration (ms) of both the commit fling-out and the spring-back-under-
2875
+ * threshold transition. Exported so SwipeStack can hold a just-committed
2876
+ * card in place for exactly this long before advancing to the next one. */
2877
+ declare const SWIPE_STACK_TRANSITION_MS = 220;
2878
+ interface UseSwipeStackOptions {
2879
+ /** Directions this drag can move toward and resolve to. A direction left
2880
+ * out of this list is a wall, not just an unreachable outcome — the card
2881
+ * will not visually move that way at all (see clampToLiveDirections). */
2882
+ directions: readonly SwipeDirection[];
2883
+ /** Effective distance (px) a drag must cross before release commits to
2884
+ * that direction instead of springing back. No velocity component — a
2885
+ * fast short flick under this distance still springs back. */
2886
+ threshold?: number;
2887
+ /** Fires once, synchronously, when a drag (or triggerCommit) resolves to
2888
+ * a live direction past threshold. This hook has already started the
2889
+ * fling-out animation on cardRef's element by the time this is called —
2890
+ * the caller only needs to react to the outcome, not drive the motion. */
2891
+ onCommit: (direction: SwipeDirection) => void;
2892
+ /** Disables both the drag and triggerCommit — e.g. while there is no
2893
+ * current card to act on. */
2894
+ disabled?: boolean;
2895
+ }
2896
+ interface UseSwipeStackHandlers {
2897
+ onPointerDown: (e: PointerEvent) => void;
2898
+ onPointerMove: (e: PointerEvent) => void;
2899
+ onPointerUp: (e: PointerEvent) => void;
2900
+ onPointerCancel: (e: PointerEvent) => void;
2901
+ }
2902
+ interface UseSwipeStackResult {
2903
+ /** Attach to the card element that should translate/rotate with the drag. */
2904
+ cardRef: RefObject<HTMLDivElement | null>;
2905
+ /** Attach to the ancestor of both the card and any direction-stamp
2906
+ * overlays. The hook writes live drag progress here as CSS custom
2907
+ * properties (--sv-stack-progress-left/right/up/down, each 0–1) so
2908
+ * stamps — siblings of the card, not the card itself — can react without
2909
+ * a re-render per pointermove. */
2910
+ wrapRef: RefObject<HTMLDivElement | null>;
2911
+ handlers: UseSwipeStackHandlers;
2912
+ /** CSS touch-action for the card, derived from which directions are live
2913
+ * — an axis with nothing configured on it stays native (pan-x/pan-y) so
2914
+ * a page can still scroll through a stack that only uses the other axis;
2915
+ * `none` only once both axes are actually needed. */
2916
+ touchAction: string;
2917
+ /** Programmatically commits a direction — the same fling animation and
2918
+ * onCommit call a real drag past threshold would produce. Backs the
2919
+ * non-gesture fallback buttons: they are not a separate code path, just
2920
+ * a different way to call this. No-ops if the direction isn't live or
2921
+ * the hook is disabled. */
2922
+ triggerCommit: (direction: SwipeDirection) => void;
2923
+ }
2924
+ /**
2925
+ * useSwipeStack — hand-rolled 4-directional free drag, extending
2926
+ * useSwipeReveal's pattern (raw Pointer Events, a deadzone, transform
2927
+ * written straight to a ref, resolved only on release) from one axis with
2928
+ * two outcomes to four directions with a tilt. Threshold-only on purpose,
2929
+ * matching useSwipeReveal — no velocity tracking, so a fast short flick
2930
+ * under `threshold` still springs back.
2931
+ */
2932
+ declare function useSwipeStack({ directions, threshold, onCommit, disabled, }: UseSwipeStackOptions): UseSwipeStackResult;
2933
+
2215
2934
  interface ResponsiveSurfaceProps {
2216
2935
  web: ReactNode;
2217
2936
  mobile: ReactNode;
@@ -2240,6 +2959,14 @@ interface SwipableMobileCarouselDotsProps {
2240
2959
  /** Labels the whole group — required, no generic fallback (mirrors
2241
2960
  * SwipableMobileCarousel's own required aria-label). */
2242
2961
  'aria-label': string;
2962
+ /** `'compact'` halves the gap between dots (`--sv-space-2` → `--sv-space-1`)
2963
+ * and shrinks each dot's own hit target (20px → 16px) — for a carousel
2964
+ * with enough slides that even the reduced gap alone still reads as too
2965
+ * wide on a narrow viewport (e.g. a many-list mobile app). The visible
2966
+ * 6px dot itself is unchanged; only its surrounding tap-target box
2967
+ * shrinks. Defaults to `'default'` — every existing consumer's spacing is
2968
+ * unaffected unless it opts in. */
2969
+ density?: 'default' | 'compact';
2243
2970
  className?: string;
2244
2971
  }
2245
2972
  /**
@@ -2255,7 +2982,7 @@ interface SwipableMobileCarouselDotsProps {
2255
2982
  * other `role="tablist"` component) has none either, so this stays
2256
2983
  * consistent with it rather than gold-plating past its sibling.
2257
2984
  */
2258
- declare function SwipableMobileCarouselDots({ count, activeIndex, onJump, labels, 'aria-label': ariaLabel, className, }: SwipableMobileCarouselDotsProps): react.JSX.Element;
2985
+ declare function SwipableMobileCarouselDots({ count, activeIndex, onJump, labels, 'aria-label': ariaLabel, density, className, }: SwipableMobileCarouselDotsProps): react.JSX.Element;
2259
2986
 
2260
2987
  interface SwipableMobileCarouselIndicatorProps {
2261
2988
  count: number;
@@ -2387,4 +3114,357 @@ interface SwipableMobileCarouselSlideFooterProps {
2387
3114
  */
2388
3115
  declare function SwipableMobileCarouselSlideFooter({ children, className, }: SwipableMobileCarouselSlideFooterProps): react.JSX.Element;
2389
3116
 
2390
- export { Accordion, type AccordionItem, type AccordionProps, Alert, type AlertProps, type AlertVariant, AspectRatio, type AspectRatioProps, Avatar, type AvatarProps, Badge, type BadgeProps, type BadgeStatus, type BadgeVariant, BalanceChip, type BalanceChipProps, type BannerVariant, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, type ButtonSize, type ButtonVariant, Calendar, type CalendarProps, Card, type CardProps, CheckableListRow, type CheckableListRowProps, Checkbox, type CheckboxProps, CodeTextarea, type CodeTextareaProps, Collapsible, type CollapsibleProps, Combobox, type ComboboxOption, type ComboboxProps, Command, type CommandItem, type CommandProps, type CommitOnEnterOrBlurHandlers, ConfirmDialog, type ConfirmDialogProps, ContextMenu, type ContextMenuProps, CurrencyInput, type CurrencyInputProps, DataTable, type DataTableColumn, type DataTableProps, DatePicker, type DatePickerProps, DeviceOnlyGate, type DeviceOnlyGateProps, DeviceStorageKeyGate, type DeviceStorageKeyGateProps, type DeviceStorageKeyGateStatus, Dialog, type DialogProps, type DialogSize, DragHandleRow, type DragHandleRowProps, Drawer, type DrawerProps, EmptyState, type EmptyStateProps, FileDropzone, type FileDropzoneProps, type FooterIcon, FormField, type FormFieldProps, type FormFieldRenderProps, HoverCard, type HoverCardProps, Icon, type IconName, IconPicker, type IconPickerProps, type IconProps, Input, type InputProps, Item, type ItemProps, Kbd, type KbdProps, Label, type LabelProps, LegalLinks, type LegalLinksProps, type LongPressHandlers, MOBILE_BREAKPOINT_PX, Markdown, type MarkdownProps, Marker, type MarkerProps, MemberMultiSelect, type MemberMultiSelectOption, type MemberMultiSelectProps, Menu, MenuEntries, type MenuEntry, type MenuItem, type MenuLabel, type MenuProps, type MenuSeparator, Menubar, type MenubarMenu, type MenubarProps, Message, type MessageProps, MessageScroller, type MessageScrollerProps, type MessageSender, MobileAppsDrawer, type MobileAppsDrawerItem, type MobileAppsDrawerProps, MobileFooter, type MobileFooterProps, MobileHeader, type MobileHeaderProps, type NavTabItem, type NavTabLinkProps, NavTabs, type NavTabsProps, NavigationMenu, type NavigationMenuItem, type NavigationMenuProps, OfflineGate, type OfflineGateProps, type OfflineTileState, OverlayHeader, type OverlayHeaderProps, PageContainer, type PageContainerProps, PageHeader, type PageHeaderProps, Pagination, type PaginationProps, Popover, type PopoverProps, Progress, type ProgressProps, QuantityStepper, type QuantityStepperProps, RadioGroup, type RadioGroupItem, type RadioGroupProps, type ResizableDirection, ResizableHandle, type ResizableHandleProps, ResizablePanel, ResizablePanelGroup, type ResizablePanelGroupProps, type ResizablePanelProps, ResponsiveSurface, type ResponsiveSurfaceProps, ScrollArea, type ScrollAreaProps, SegmentedControl, type SegmentedControlProps, type SegmentedOption, Select, type SelectProps, Sheet, type SheetProps, Slider, type SliderProps, type SortDirection, Spinner, type SpinnerProps, type SplitMethod, SplitMethodSelector, type SplitMethodSelectorProps, SplitPane, type SplitPaneProps, StatusBadge, type StatusBadgeProps, type StatusBadgeStatus, SuggestionInput, type SuggestionInputProps, type SuggestionOption, SwipableMobileCarousel, SwipableMobileCarouselDots, type SwipableMobileCarouselDotsProps, type SwipableMobileCarouselIndicatorProps, type SwipableMobileCarouselProps, SwipableMobileCarouselSlide, SwipableMobileCarouselSlideBody, type SwipableMobileCarouselSlideBodyProps, SwipableMobileCarouselSlideFooter, type SwipableMobileCarouselSlideFooterProps, SwipableMobileCarouselSlideHeader, type SwipableMobileCarouselSlideHeaderProps, type SwipableMobileCarouselSlideProps, SystemBanner, type SystemBannerProps, type TabItem, Table, TableBody, TableCell, TableHead, TableHeaderCell, type TableProps, TableRow, Tabs, type TabsProps, TagInput, type TagInputProps, Textarea, type TextareaProps, type ToastContextValue, type ToastItem, ToastProvider, Toggle, type ToggleProps, Tooltip, type TooltipProps, Typography, type TypographyProps, type TypographyVariant, type UseCarouselRouteSyncOptions, type UseCarouselRouteSyncResult, type UseLongPressOptions, type UseResponsiveLayoutOptions, type UseResponsiveLayoutResult, type UseSnapCarouselOptions, type UseSnapCarouselResult, type UseSwipeRevealHandlers, type UseSwipeRevealOptions, type UseSwipeRevealResult, useCarouselRouteSync, useCommitOnEnterOrBlur, useDoubleTapHandler, useIsMobile, useIsOffline, useLongPress, useOfflineTileState, useOverlaySecondRow, useResponsiveLayout, useSingleOrDoubleTap, useSnapCarousel, useSwipeReveal, useToast };
3117
+ interface SwipeDirectionMeta {
3118
+ /** Visible text on the fallback button and the in-drag stamp. */
3119
+ label: string;
3120
+ icon?: IconName;
3121
+ }
3122
+ interface SwipeStackProps {
3123
+ /** Required — labels the swipeable region for assistive tech. No generic
3124
+ * fallback, since one would be wrong for every actual consumer. */
3125
+ 'aria-label': string;
3126
+ /** Which of the 4 directions are live, and what each is called. A
3127
+ * direction absent here cannot be dragged toward at all (useSwipeStack
3128
+ * treats it as a wall) and gets no fallback button or stamp. SwipeStack
3129
+ * itself has no opinion on what a direction *means* — that vocabulary
3130
+ * belongs entirely to the caller. */
3131
+ directions: Partial<Record<SwipeDirection, SwipeDirectionMeta>>;
3132
+ /** Fires once a drag (or a fallback button) commits to a direction. Every
3133
+ * configured direction behaves the same way: the card leaves for good —
3134
+ * there is no reversible "go back" state. SwipeStack has no opinion on
3135
+ * where card data lives; the caller is responsible for eventually no
3136
+ * longer including this cardId in children (e.g. once a mutation lands). */
3137
+ onSwipe: (direction: SwipeDirection, cardId: string) => void;
3138
+ /** Effective drag distance (px) required to commit. Passed straight
3139
+ * through to useSwipeStack. */
3140
+ threshold?: number;
3141
+ className?: string;
3142
+ /** Must be SwipeStackCard elements (nullish/boolean children are safely
3143
+ * skipped for conditional cards). A dev-mode warning is logged for any
3144
+ * other child type — see resolveCards below. */
3145
+ children: ReactNode;
3146
+ }
3147
+ /**
3148
+ * SwipeStack — a compound component for triaging one card at a time by
3149
+ * dragging it left/right/up/down (wrapping useSwipeStack), plus an
3150
+ * always-visible non-gesture fallback: a button per configured direction,
3151
+ * wired to the identical commit path and exit animation as a real drag.
3152
+ *
3153
+ * Every direction is independently optional and behaves the same way once
3154
+ * triggered — the card leaves for good, there is no reversible "go back".
3155
+ * SwipeStack owns rendering, drag physics, and the stamp/fallback UI only;
3156
+ * it has no opinion on where card data lives or what a direction means —
3157
+ * see SwipeStackProps.onSwipe.
3158
+ */
3159
+ declare function SwipeStack({ 'aria-label': ariaLabel, directions, onSwipe, threshold, className, children, }: SwipeStackProps): react.JSX.Element;
3160
+
3161
+ interface SwipeStackCardProps {
3162
+ /** Stable identity for this card, independent of its position among
3163
+ * siblings — required. This is what SwipeStack reads (via child.props,
3164
+ * not React's own `key`, which a component can't see on itself) to know
3165
+ * which card is on top and what id to report through onSwipe. Reuse
3166
+ * whatever id the caller already keys their `.map()` on. */
3167
+ cardId: string;
3168
+ children: ReactNode;
3169
+ }
3170
+ /**
3171
+ * SwipeStackCard — a single card of a SwipeStack. Unlike
3172
+ * SwipableMobileCarouselSlide, this has no mount-window logic of its own:
3173
+ * SwipeStack only ever renders the top two cards' elements at all (there is
3174
+ * no scroll-position reason to keep every sibling in the DOM the way the
3175
+ * carousel's scroll-snap track does), so there is nothing here to gate.
3176
+ */
3177
+ declare function SwipeStackCard({ children }: SwipeStackCardProps): react.JSX.Element;
3178
+
3179
+ interface ThreeColumnLayoutProps {
3180
+ /**
3181
+ * Exactly 2 (sidebar, main) or 3 (sidebar, main, detail) elements. Omit
3182
+ * the third child entirely to render two columns — e.g.
3183
+ * `{selected && <Detail />}` — React.Children.toArray drops falsy
3184
+ * children, so no separate `detail` prop is needed to make that
3185
+ * conditional.
3186
+ */
3187
+ children: ReactNode;
3188
+ /** px. Fixed width of the first (leftmost) column. Default 280. */
3189
+ sidebarWidth?: number;
3190
+ /** px. Fixed width of the third column, when present. Default 360. */
3191
+ detailWidth?: number;
3192
+ /**
3193
+ * Hides the sidebar column entirely — no width, no border, no layout box
3194
+ * — while keeping it mounted and every sibling in its own stable position.
3195
+ *
3196
+ * Prefer this over conditionally omitting the sidebar child, or swapping
3197
+ * to a different wrapper when collapsed. Omitting it shifts `main` into
3198
+ * the sidebar slot, and swapping the surrounding element type unmounts
3199
+ * `main`'s entire subtree — silently discarding its React state (a live
3200
+ * stream, unsent composer text, an in-memory-only conversation) on every
3201
+ * collapse toggle. Found live in Warden, whose chat column lost all of
3202
+ * that each time the sidebar was shown or hidden.
3203
+ */
3204
+ sidebarHidden?: boolean;
3205
+ className?: string;
3206
+ }
3207
+ /**
3208
+ * ThreeColumnLayout — sidebar + main + optional detail column, for the
3209
+ * common "list app" shape (a list sidebar, a primary content list, and a
3210
+ * detail pane that only takes up space once something is selected).
3211
+ *
3212
+ * Purely positional and structural — it has no awareness of what's inside
3213
+ * each slot (no injected landmarks/labels; those belong to the children
3214
+ * themselves) and no responsive behavior of its own. Collapsing further at
3215
+ * narrow desktop/tablet widths (e.g. swapping the detail column for an
3216
+ * overlay) is the consuming plugin's decision; below the mobile breakpoint,
3217
+ * compose with ResponsiveSurface to fork to a different tree entirely.
3218
+ */
3219
+ declare function ThreeColumnLayout({ children, sidebarWidth, detailWidth, sidebarHidden, className, }: ThreeColumnLayoutProps): react.JSX.Element;
3220
+
3221
+ interface HeaderFooterLayoutProps {
3222
+ /** Main content — required, always fills whatever height header/footer don't claim. */
3223
+ children: ReactNode;
3224
+ header?: ReactNode;
3225
+ footer?: ReactNode;
3226
+ /** px. Fixed height of the header, when present. Default 60 (matches
3227
+ * MobileHeader/--sv-shell-header-height's own default). */
3228
+ headerHeight?: number;
3229
+ /** px. Fixed height of the footer, when present. Default 60 (matches
3230
+ * MobileFooter/--sv-shell-footer-height's own default). */
3231
+ footerHeight?: number;
3232
+ className?: string;
3233
+ }
3234
+ /**
3235
+ * HeaderFooterLayout — header + main + optional footer, both fixed-height
3236
+ * and independently optional, with main always claiming the remaining
3237
+ * height. The vertical counterpart to ThreeColumnLayout: same purely
3238
+ * structural approach (no color/background opinions, no awareness of what's
3239
+ * inside each slot, no responsive behavior of its own — a plain flex
3240
+ * column), just rotated. "Footer always at the bottom" falls out of flex
3241
+ * layout alone; there is no position: fixed or safe-area handling here, so
3242
+ * a caller composing this with the platform shell's own chrome still owns
3243
+ * that separately.
3244
+ */
3245
+ declare function HeaderFooterLayout({ children, header, footer, headerHeight, footerHeight, className, }: HeaderFooterLayoutProps): react.JSX.Element;
3246
+
3247
+ type RootLayoutVariant = 'plain' | 'sidebar' | 'header' | 'shell';
3248
+ interface RootLayoutProps {
3249
+ /**
3250
+ * Which chrome shape this page uses. Children are positional and their
3251
+ * count is fixed per variant:
3252
+ * - `'plain'` — 1 child: `[main]`. No chrome on either breakpoint.
3253
+ * - `'sidebar'` — 2 children: `[sidebar, main]`. Sidebar + main on web
3254
+ * only — on mobile, sidebar is dropped and only main renders.
3255
+ * - `'header'` — 2 children: `[header, main]`. Header + main on both
3256
+ * breakpoints — same structure, but the header's own height differs
3257
+ * per breakpoint (`headerHeight` on web, `mobileHeaderHeight` on
3258
+ * mobile), matching how real `shell: minimal` plugins actually do
3259
+ * this (a compact web top bar vs. the platform's mobile chrome
3260
+ * height).
3261
+ * - `'shell'` — 3 children: `[header, main, footer]`. Header + main +
3262
+ * footer on mobile only — on web, header/footer are dropped and only
3263
+ * main renders.
3264
+ * Default `'plain'`.
3265
+ */
3266
+ variant?: RootLayoutVariant;
3267
+ children: ReactNode;
3268
+ /** px. Default 64 — matches the shell's own persistent nav rail
3269
+ * (`--sv-shell-sidebar-width`).
3270
+ * Only used by `'sidebar'`. */
3271
+ sidebarWidth?: number;
3272
+ /** px. Default 48 — matches a real `shell: minimal` plugin's own compact
3273
+ * web header (e.g. Kanban's `KanbanHeader`, deliberately not the
3274
+ * platform's 60px shell header since a `shell: minimal` plugin owns its
3275
+ * whole viewport). Web only — used by `'header'` on web. */
3276
+ headerHeight?: number;
3277
+ /** px. Default 60 — matches the platform's own mobile chrome
3278
+ * (`--sv-shell-header-height`, `MobileHeader`'s default). Mobile only —
3279
+ * used by `'header'` and `'shell'` on mobile. */
3280
+ mobileHeaderHeight?: number;
3281
+ /** px. Default 60 — matches the platform's own mobile chrome
3282
+ * (`--sv-shell-footer-height`, `MobileFooter`'s default). Mobile only —
3283
+ * used by `'shell'`. */
3284
+ footerHeight?: number;
3285
+ className?: string;
3286
+ }
3287
+ /**
3288
+ * RootLayout — the root-level layout a plugin's page tree sits in. Enforces
3289
+ * only structure and dimensions (`width: 100%; height: 100%`, filling
3290
+ * whatever real height the platform shell's own ancestor chain already
3291
+ * establishes — not viewport units, since a plugin isn't alone on the
3292
+ * page). A definite height, not min-height: ThreeColumnLayout/
3293
+ * HeaderFooterLayout's own .shell need one to resolve their own height:
3294
+ * 100% against (see RootLayout.module.css's comment). All content comes
3295
+ * from children; RootLayout has no opinion on what's inside any region.
3296
+ *
3297
+ * The web/mobile fork is `ResponsiveSurface` — only one of
3298
+ * `RootLayoutWeb`/`RootLayoutMobile` is ever mounted, never a CSS squeeze of
3299
+ * the same tree. Each variant's actual shape is delegated entirely to
3300
+ * `ThreeColumnLayout`/`HeaderFooterLayout`; RootLayout itself contains no
3301
+ * media queries.
3302
+ */
3303
+ declare function RootLayout({ variant, children, sidebarWidth, headerHeight, mobileHeaderHeight, footerHeight, className, }: RootLayoutProps): react.JSX.Element;
3304
+
3305
+ type PageLayoutPadding = 'none' | 'sm' | 'md' | 'lg';
3306
+ interface PageLayoutProps {
3307
+ /**
3308
+ * A page-specific header — e.g. a board title + search/share toolbar.
3309
+ * Not the app-level header (that's `RootLayout`'s `header`/`shell`
3310
+ * variants) — this is a second, contextual row scoped to this one page.
3311
+ * Renders edge-to-edge above the padded content, outside `padding`.
3312
+ */
3313
+ header?: ReactNode;
3314
+ /** The page gutter. Default `'none'` — unlike `PageContainer`, a bare
3315
+ * `PageLayout` applies no padding at all; opt in per page. Same four-step
3316
+ * scale and `--sv-page-gutter` stand-down hook as `PageContainer` (steps
3317
+ * down at the same 768px breakpoint `Dialog`/`MobileHeader` use). */
3318
+ padding?: PageLayoutPadding;
3319
+ children: ReactNode;
3320
+ className?: string;
3321
+ }
3322
+ /**
3323
+ * PageLayout — a single page's content area within a plugin (nested inside
3324
+ * `RootLayout`'s `main` slot, not a replacement for it). Enforces only
3325
+ * structure and dimensions; content always comes from props/children.
3326
+ *
3327
+ * `min-width: 100%; min-height: 100%` — safe here (unlike `RootLayout`'s own
3328
+ * root) because `PageLayout` is always nested inside an already
3329
+ * flex-stretched region (`ThreeColumnLayout`/`HeaderFooterLayout`'s
3330
+ * `.main`), and a stretched flex item's cross-size is definite for its
3331
+ * descendants' percentage resolution, so no separate height-cascading fix
3332
+ * is needed the way `RootLayout`'s root required one.
3333
+ */
3334
+ declare function PageLayout({ header, padding, children, className }: PageLayoutProps): react.JSX.Element;
3335
+
3336
+ interface NavListItem {
3337
+ id: string;
3338
+ label: string;
3339
+ href: string;
3340
+ icon: IconName;
3341
+ /** Trailing content, e.g. a count — reserved for a future consumer, not used by `variant="drilldown"`'s own chevron slot. */
3342
+ badge?: ReactNode;
3343
+ /**
3344
+ * Only meaningful for `variant="static"` — whether this row represents the
3345
+ * current page. Computed by the consumer (e.g. via `usePathname()` plus a
3346
+ * longest-prefix match against every item's `href`), since this package
3347
+ * doesn't depend on `next/navigation` — same reasoning as `NavTabs`.
3348
+ */
3349
+ active?: boolean;
3350
+ }
3351
+ interface NavListGroup {
3352
+ id: string;
3353
+ /** Omit for an ungrouped leading item/group — e.g. a pinned "Overview" row above the grouped sections. */
3354
+ label?: string;
3355
+ items: NavListItem[];
3356
+ }
3357
+ /** Props to spread onto a consumer-supplied link element — mirrors `NavTabs`' `NavTabLinkProps`. */
3358
+ interface NavListLinkProps {
3359
+ href: string;
3360
+ className: string;
3361
+ 'aria-current': 'page' | undefined;
3362
+ children: ReactNode;
3363
+ }
3364
+ interface NavListProps {
3365
+ groups: NavListGroup[];
3366
+ /**
3367
+ * `"static"`: active row highlighted via `item.active`, no chevron — a
3368
+ * persistent sidebar nav. `"drilldown"`: every row gets a trailing chevron
3369
+ * and no active-state, tapping navigates to a full-screen section — a
3370
+ * native Settings-app style index.
3371
+ */
3372
+ variant: 'static' | 'drilldown';
3373
+ /**
3374
+ * `"default"`: each row is a full `--sv-touch-target-min` tall tap target
3375
+ * — the right choice for `variant="drilldown"`'s mobile index, and for any
3376
+ * `static` sidebar list long enough that scanability matters more than
3377
+ * density. `"compact"` shrinks row height/padding for a short, always-
3378
+ * visible `static` list (e.g. 2-4 items above a longer scrollable list of
3379
+ * something else) where the default's touch-target height reads as
3380
+ * needlessly spaced out. Only meaningful for `variant="static"` —
3381
+ * `drilldown` rows stay full-height regardless, since that variant is
3382
+ * mobile-only and touch-target size there isn't optional. Defaults to
3383
+ * `"default"` so every existing consumer (e.g. Console's own sidebar) is
3384
+ * visually unchanged.
3385
+ */
3386
+ density?: 'default' | 'compact';
3387
+ 'aria-label': string;
3388
+ className?: string;
3389
+ /**
3390
+ * Render a row as something other than a plain `<a href>` — e.g. Next's
3391
+ * `<Link>`. Receives the item plus the class name/aria attributes NavList
3392
+ * would otherwise apply, matching `NavTabs`' `renderLink` contract.
3393
+ */
3394
+ renderLink?: (item: NavListItem, linkProps: NavListLinkProps) => ReactNode;
3395
+ }
3396
+ /**
3397
+ * NavList — a vertical list of icon+label rows, optionally grouped under
3398
+ * section headers. Two presentations of the same `groups` data: `static` (a
3399
+ * persistent sidebar, active item highlighted) and `drilldown` (a
3400
+ * native-Settings-app style index, every row navigates to a full section).
3401
+ *
3402
+ * Framework-agnostic like `NavTabs` — no `next/navigation`/`next/link`
3403
+ * import, no internal pathname reading. Active state and navigation behavior
3404
+ * are supplied by the consumer via `item.active` and `renderLink`.
3405
+ */
3406
+ declare function NavList({ groups, variant, density, 'aria-label': ariaLabel, className, renderLink, }: NavListProps): react.JSX.Element;
3407
+
3408
+ /**
3409
+ * Sovereign Design System — theme presets (RFC 0094).
3410
+ *
3411
+ * The single source of truth for every built-in theme preset's token
3412
+ * overrides. Deliberately plain data (no CSS-in-JS, no build dependency) so
3413
+ * it can be consumed two ways with zero duplication:
3414
+ * - `scripts/generate-theme-presets-css.ts` (`pnpm generate:theme-presets`)
3415
+ * reads this file and emits `theme-presets.css` for Storybook/standalone
3416
+ * consumers.
3417
+ * - An app imports `THEME_PRESETS` directly and flattens the selected
3418
+ * preset into its `:root { ... }` instance-style injection — no CSS
3419
+ * parsing, no second copy of these values to drift out of sync.
3420
+ *
3421
+ * Presets are closed and built-in —
3422
+ * add a new one here and re-run the generator, never accept arbitrary
3423
+ * operator-supplied values into this shape.
3424
+ *
3425
+ * `'default'` MUST stay `{ light: {}, dark: {} }` — an empty override set is
3426
+ * what guarantees today's look stays byte-identical for every instance that
3427
+ * never opts into a different preset.
3428
+ */
3429
+ type ThemePresetName = 'default' | 'neobrutalism';
3430
+ declare const THEME_PRESET_NAMES: readonly ThemePresetName[];
3431
+ interface ThemePresetTokens {
3432
+ /** Sparse — only tokens this preset overrides. Everything else falls through to the default. */
3433
+ light: Record<string, string>;
3434
+ dark: Record<string, string>;
3435
+ }
3436
+ declare const THEME_PRESETS: Record<ThemePresetName, ThemePresetTokens>;
3437
+
3438
+ interface CopyButtonProps {
3439
+ /** What is put on the clipboard. Nothing is read or written until the button is pressed. */
3440
+ text: string;
3441
+ /** The button's label before it is pressed, for example "Copy link". */
3442
+ label: string;
3443
+ /** What the button says once the text is on the clipboard. */
3444
+ copiedLabel?: string;
3445
+ /** What it says when the browser would not let the text be copied (no permission, or an insecure page). */
3446
+ failedLabel?: string;
3447
+ variant?: 'primary' | 'secondary' | 'ghost';
3448
+ size?: 'sm' | 'md' | 'lg';
3449
+ }
3450
+ /**
3451
+ * CopyButton — copies a piece of text to the clipboard and says so. The clipboard is touched only when the button is
3452
+ * pressed, and the button says plainly when the browser refused, rather than pretending the text was copied.
3453
+ */
3454
+ declare function CopyButton({ text, label, copiedLabel, failedLabel, variant, size, }: CopyButtonProps): react.JSX.Element;
3455
+
3456
+ interface QrCodeProps {
3457
+ /** The code's squares, row by row: `true` is dark. Made by a QR encoder; this component only draws them. */
3458
+ modules: readonly (readonly boolean[])[];
3459
+ /** What the picture is, for a screen reader: a QR code is read by a camera, not a person. */
3460
+ label: string;
3461
+ }
3462
+ /**
3463
+ * QrCode — draws a QR code's squares as an inline SVG: nothing is sent anywhere to make it, and nothing is injected
3464
+ * as markup. It is black on white in every theme, since a camera reads contrast, not a theme. The encoding is the
3465
+ * caller's (so this package carries no encoder): pass the module matrix an encoder such as `uqr` gives.
3466
+ */
3467
+ declare function QrCode({ modules, label }: QrCodeProps): react.JSX.Element;
3468
+
3469
+ export { Accordion, Alert, AppsLauncher, AspectRatio, Avatar, Badge, BalanceChip, Breadcrumb, Button, ButtonGroup, ButtonLink, Calendar, Card, CardTile, CardTileGrid, CheckableListRow, Checkbox, CodeTextarea, Collapsible, ColorPicker, Combobox, Command, ConfirmDialog, ConsentPrompt, ContextMenu, CopyButton, CurrencyInput, DataTable, DatePicker, DeviceOnlyGate, DeviceStorageKeyGate, Dialog, DragHandleRow, Drawer, EmptyState, FileDropzone, FormField, GripIcon, Header, HeaderFooterLayout, HoverCard, Icon, IconPicker, Input, Item, Kbd, Label, LegalLinks, MOBILE_BREAKPOINT_PX, Markdown, Marker, MemberMultiSelect, Menu, MenuEntries, Menubar, Message, MessageScroller, MobileAppsDrawer, MobileFooter, MobileHeader, NavList, NavTabs, NavigationMenu, NewCardTile, NotificationsPanel, OfflineGate, OverlayHeader, PageContainer, PageHeader, PageLayout, Pagination, Popover, Progress, QrCode, QuantityStepper, RadioGroup, ResizableHandle, ResizablePanel, ResizablePanelGroup, ResponsiveSurface, RootLayout, SWIPE_STACK_TRANSITION_MS, ScrollArea, SegmentedControl, Select, Sheet, Slider, Spinner, SplitMethodSelector, SplitPane, StatusBadge, StepStrip, SuggestionInput, SwipableMobileCarousel, SwipableMobileCarouselDots, SwipableMobileCarouselSlide, SwipableMobileCarouselSlideBody, SwipableMobileCarouselSlideFooter, SwipableMobileCarouselSlideHeader, SwipeStack, SwipeStackCard, SystemBanner, THEME_PRESETS, THEME_PRESET_NAMES, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Tabs, TagInput, Textarea, ThreeColumnLayout, ToastProvider, Toggle, Tooltip, Typography, UserMenu, shouldHandleDndEvent, useCarouselRouteSync, useCommitOnEnterOrBlur, useDoubleTapHandler, useIsMobile, useIsOffline, useLongPress, useOfflineTileState, useOverlaySecondRow, useReorderSensors, useResponsiveLayout, useSingleOrDoubleTap, useSnapCarousel, useSwipeReveal, useSwipeStack, useToast };
3470
+ export type { AccordionItem, AccordionProps, AlertProps, AlertVariant, AppsLauncherItem, AppsLauncherProps, AspectRatioProps, AvatarProps, BadgeProps, BadgeStatus, BadgeVariant, BalanceChipProps, BannerVariant, BreadcrumbItem, BreadcrumbProps, ButtonGroupProps, ButtonLinkProps, ButtonProps, ButtonSize, ButtonVariant, CalendarProps, CardProps, CardTileGridProps, CardTileProps, CheckableListRowProps, CheckboxProps, CodeTextareaProps, CollapsibleProps, ColorPickerProps, ColorPickerSwatch, ComboboxOption, ComboboxProps, CommandItem, CommandProps, CommitOnEnterOrBlurHandlers, ConfirmDialogProps, ConsentPromptProps, ContextMenuProps, CopyButtonProps, CurrencyInputProps, DataTableColumn, DataTableProps, DatePickerProps, DeviceOnlyGateProps, DeviceStorageKeyGateProps, DeviceStorageKeyGateStatus, DialogProps, DialogSize, DragHandleRowProps, DrawerProps, EmptyStateProps, FileDropzoneProps, FooterIcon, FormFieldProps, FormFieldRenderProps, GripIconProps, HeaderFooterLayoutProps, HeaderPlugin, HeaderProps, HoverCardProps, IconName, IconPickerProps, IconProps, InputProps, ItemProps, KbdProps, LabelProps, LegalLinksProps, LongPressHandlers, MarkdownProps, MarkerProps, MemberMultiSelectOption, MemberMultiSelectProps, MenuEntry, MenuItem, MenuLabel, MenuProps, MenuSeparator, MenubarMenu, MenubarProps, MessageProps, MessageScrollerProps, MessageSender, MobileAppsDrawerItem, MobileAppsDrawerProps, MobileFooterProps, MobileHeaderProps, NavListGroup, NavListItem, NavListLinkProps, NavListProps, NavTabItem, NavTabLinkProps, NavTabsProps, NavigationMenuItem, NavigationMenuProps, NewCardTileProps, NotificationsPanelItem, NotificationsPanelProps, OfflineGateProps, OfflineTileState, OverlayHeaderProps, PageContainerProps, PageHeaderProps, PageLayoutPadding, PageLayoutProps, PaginationProps, PopoverProps, ProgressProps, QrCodeProps, QuantityStepperProps, RadioGroupItem, RadioGroupProps, ResizableDirection, ResizableHandleProps, ResizablePanelGroupProps, ResizablePanelProps, ResponsiveSurfaceProps, RootLayoutProps, RootLayoutVariant, ScrollAreaProps, SegmentedControlProps, SegmentedOption, SelectProps, SheetProps, SliderProps, SortDirection, SpinnerProps, SplitMethod, SplitMethodSelectorProps, SplitPaneProps, StatusBadgeProps, StatusBadgeStatus, StepStripItem, StepStripProps, SuggestionInputProps, SuggestionOption, SwipableMobileCarouselDotsProps, SwipableMobileCarouselIndicatorProps, SwipableMobileCarouselProps, SwipableMobileCarouselSlideBodyProps, SwipableMobileCarouselSlideFooterProps, SwipableMobileCarouselSlideHeaderProps, SwipableMobileCarouselSlideProps, SwipeDirection, SwipeDirectionMeta, SwipeStackCardProps, SwipeStackProps, SystemBannerProps, TabItem, TableProps, TabsProps, TagInputProps, TextareaProps, ThemePresetName, ThemePresetTokens, ThreeColumnLayoutProps, ToastContextValue, ToastItem, ToggleProps, TooltipProps, TypographyProps, TypographyVariant, UseCarouselRouteSyncOptions, UseCarouselRouteSyncResult, UseLongPressOptions, UseReorderSensorsOptions, UseResponsiveLayoutOptions, UseResponsiveLayoutResult, UseSnapCarouselOptions, UseSnapCarouselResult, UseSwipeRevealHandlers, UseSwipeRevealOptions, UseSwipeRevealResult, UseSwipeStackHandlers, UseSwipeStackOptions, UseSwipeStackResult, UserMenuItem, UserMenuProps };