cursedbelt 5.40.0 → 5.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (499) hide show
  1. package/dist/react/Toast.d.ts +12 -2
  2. package/dist/react/Toast.d.ts.map +1 -1
  3. package/dist/react/Toast.js +32 -3
  4. package/dist/react/Toast.js.map +1 -1
  5. package/dist/react/analytics/MetricDrilldown.js +1 -1
  6. package/dist/react/charts/AreaChart.d.ts.map +1 -1
  7. package/dist/react/charts/AreaChart.js +5 -5
  8. package/dist/react/charts/AreaChart.js.map +1 -1
  9. package/dist/react/charts/BarChart.d.ts.map +1 -1
  10. package/dist/react/charts/BarChart.js +3 -3
  11. package/dist/react/charts/BarChart.js.map +1 -1
  12. package/dist/react/charts/ChartCard.d.ts +53 -0
  13. package/dist/react/charts/ChartCard.d.ts.map +1 -0
  14. package/dist/react/charts/ChartCard.js +46 -0
  15. package/dist/react/charts/ChartCard.js.map +1 -0
  16. package/dist/react/charts/ChartFrame.d.ts +12 -5
  17. package/dist/react/charts/ChartFrame.d.ts.map +1 -1
  18. package/dist/react/charts/ChartFrame.js +12 -5
  19. package/dist/react/charts/ChartFrame.js.map +1 -1
  20. package/dist/react/charts/ChartLegend.js +1 -1
  21. package/dist/react/charts/ChartLegend.js.map +1 -1
  22. package/dist/react/charts/HeatmapChart.js +3 -3
  23. package/dist/react/charts/HeatmapChart.js.map +1 -1
  24. package/dist/react/charts/IntervalTimeline.js +1 -1
  25. package/dist/react/charts/IntervalTimeline.js.map +1 -1
  26. package/dist/react/charts/LineChart.d.ts.map +1 -1
  27. package/dist/react/charts/LineChart.js +3 -3
  28. package/dist/react/charts/LineChart.js.map +1 -1
  29. package/dist/react/charts/TimeSeriesChart.d.ts.map +1 -1
  30. package/dist/react/charts/TimeSeriesChart.js +6 -6
  31. package/dist/react/charts/TimeSeriesChart.js.map +1 -1
  32. package/dist/react/charts/chartBox.d.ts +46 -0
  33. package/dist/react/charts/chartBox.d.ts.map +1 -0
  34. package/dist/react/charts/chartBox.js +45 -0
  35. package/dist/react/charts/chartBox.js.map +1 -0
  36. package/dist/react/charts/index.d.ts +2 -0
  37. package/dist/react/charts/index.d.ts.map +1 -1
  38. package/dist/react/charts/index.js +2 -0
  39. package/dist/react/charts/index.js.map +1 -1
  40. package/dist/react/charts/shared.d.ts +13 -2
  41. package/dist/react/charts/shared.d.ts.map +1 -1
  42. package/dist/react/charts/shared.js +14 -3
  43. package/dist/react/charts/shared.js.map +1 -1
  44. package/dist/react/chat/Bubble.js +4 -4
  45. package/dist/react/chat/Bubble.js.map +1 -1
  46. package/dist/react/chat/MessageScroller.js +1 -1
  47. package/dist/react/chat/MessageScroller.js.map +1 -1
  48. package/dist/react/components/ActionBar.d.ts.map +1 -1
  49. package/dist/react/components/ActionBar.js +63 -1
  50. package/dist/react/components/ActionBar.js.map +1 -1
  51. package/dist/react/components/AddItemsMenu.js +1 -1
  52. package/dist/react/components/AddItemsMenu.js.map +1 -1
  53. package/dist/react/components/Alert.d.ts +16 -2
  54. package/dist/react/components/Alert.d.ts.map +1 -1
  55. package/dist/react/components/Alert.js +35 -2
  56. package/dist/react/components/Alert.js.map +1 -1
  57. package/dist/react/components/AppShell.d.ts +55 -33
  58. package/dist/react/components/AppShell.d.ts.map +1 -1
  59. package/dist/react/components/AppShell.js +121 -31
  60. package/dist/react/components/AppShell.js.map +1 -1
  61. package/dist/react/components/AsyncPanel.d.ts +21 -3
  62. package/dist/react/components/AsyncPanel.d.ts.map +1 -1
  63. package/dist/react/components/AsyncPanel.js +49 -5
  64. package/dist/react/components/AsyncPanel.js.map +1 -1
  65. package/dist/react/components/BannerLane.d.ts +32 -0
  66. package/dist/react/components/BannerLane.d.ts.map +1 -0
  67. package/dist/react/components/BannerLane.js +41 -0
  68. package/dist/react/components/BannerLane.js.map +1 -0
  69. package/dist/react/components/Button.d.ts.map +1 -1
  70. package/dist/react/components/Button.js +2 -1
  71. package/dist/react/components/Button.js.map +1 -1
  72. package/dist/react/components/ChipFilterStrip.js +1 -1
  73. package/dist/react/components/ChipFilterStrip.js.map +1 -1
  74. package/dist/react/components/ComboboxField.d.ts.map +1 -1
  75. package/dist/react/components/ComboboxField.js +14 -4
  76. package/dist/react/components/ComboboxField.js.map +1 -1
  77. package/dist/react/components/CommentThread.js +2 -2
  78. package/dist/react/components/CommentThread.js.map +1 -1
  79. package/dist/react/components/ConfirmDialog.js +1 -1
  80. package/dist/react/components/ConfirmDialog.js.map +1 -1
  81. package/dist/react/components/DataTable/DataTable.d.ts +18 -2
  82. package/dist/react/components/DataTable/DataTable.d.ts.map +1 -1
  83. package/dist/react/components/DataTable/DataTable.js +51 -12
  84. package/dist/react/components/DataTable/DataTable.js.map +1 -1
  85. package/dist/react/components/DataTable/Pagination.js +1 -1
  86. package/dist/react/components/DataTable/Pagination.js.map +1 -1
  87. package/dist/react/components/DataTable/RowActions.d.ts +7 -1
  88. package/dist/react/components/DataTable/RowActions.d.ts.map +1 -1
  89. package/dist/react/components/DataTable/RowActions.js +10 -3
  90. package/dist/react/components/DataTable/RowActions.js.map +1 -1
  91. package/dist/react/components/DataTable/TableFilterBuilder.js +1 -1
  92. package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -1
  93. package/dist/react/components/DataTable/cells/CodeCell.js +1 -1
  94. package/dist/react/components/DataTable/cells/CodeCell.js.map +1 -1
  95. package/dist/react/components/DataTable/cells/LinkCell.js +1 -1
  96. package/dist/react/components/DataTable/cells/LinkCell.js.map +1 -1
  97. package/dist/react/components/DataTable/index.d.ts +1 -1
  98. package/dist/react/components/DataTable/index.d.ts.map +1 -1
  99. package/dist/react/components/DataTable/index.js.map +1 -1
  100. package/dist/react/components/DataTable/types.d.ts +20 -0
  101. package/dist/react/components/DataTable/types.d.ts.map +1 -1
  102. package/dist/react/components/DataTable/types.js +22 -0
  103. package/dist/react/components/DataTable/types.js.map +1 -1
  104. package/dist/react/components/DateTimeField.d.ts.map +1 -1
  105. package/dist/react/components/DateTimeField.js +4 -1
  106. package/dist/react/components/DateTimeField.js.map +1 -1
  107. package/dist/react/components/DeleteButton.js +2 -0
  108. package/dist/react/components/DeleteButton.js.map +1 -1
  109. package/dist/react/components/Drawer.js +3 -3
  110. package/dist/react/components/Drawer.js.map +1 -1
  111. package/dist/react/components/DropdownMenu.d.ts +16 -0
  112. package/dist/react/components/DropdownMenu.d.ts.map +1 -1
  113. package/dist/react/components/DropdownMenu.js +22 -6
  114. package/dist/react/components/DropdownMenu.js.map +1 -1
  115. package/dist/react/components/EmptyState.d.ts +10 -2
  116. package/dist/react/components/EmptyState.d.ts.map +1 -1
  117. package/dist/react/components/EmptyState.js +10 -5
  118. package/dist/react/components/EmptyState.js.map +1 -1
  119. package/dist/react/components/EventTimeline.js +1 -1
  120. package/dist/react/components/EventTimeline.js.map +1 -1
  121. package/dist/react/components/FileDropZone.js +1 -1
  122. package/dist/react/components/FileDropZone.js.map +1 -1
  123. package/dist/react/components/Grid.d.ts +9 -1
  124. package/dist/react/components/Grid.d.ts.map +1 -1
  125. package/dist/react/components/Grid.js +20 -2
  126. package/dist/react/components/Grid.js.map +1 -1
  127. package/dist/react/components/Guide.d.ts +31 -0
  128. package/dist/react/components/Guide.d.ts.map +1 -0
  129. package/dist/react/components/Guide.js +18 -0
  130. package/dist/react/components/Guide.js.map +1 -0
  131. package/dist/react/components/InfoHintImpl.d.ts +0 -13
  132. package/dist/react/components/InfoHintImpl.d.ts.map +1 -1
  133. package/dist/react/components/InfoHintImpl.js +17 -1
  134. package/dist/react/components/InfoHintImpl.js.map +1 -1
  135. package/dist/react/components/InlineEditableText.js +1 -1
  136. package/dist/react/components/InlineEditableText.js.map +1 -1
  137. package/dist/react/components/IsolatedFrame.js +1 -1
  138. package/dist/react/components/IsolatedFrame.js.map +1 -1
  139. package/dist/react/components/ListToolbar.d.ts.map +1 -1
  140. package/dist/react/components/ListToolbar.js +2 -2
  141. package/dist/react/components/ListToolbar.js.map +1 -1
  142. package/dist/react/components/LogViewer.d.ts +26 -2
  143. package/dist/react/components/LogViewer.d.ts.map +1 -1
  144. package/dist/react/components/LogViewer.js +41 -26
  145. package/dist/react/components/LogViewer.js.map +1 -1
  146. package/dist/react/components/Modal.d.ts +5 -1
  147. package/dist/react/components/Modal.d.ts.map +1 -1
  148. package/dist/react/components/Modal.js +18 -8
  149. package/dist/react/components/Modal.js.map +1 -1
  150. package/dist/react/components/Page.d.ts +51 -7
  151. package/dist/react/components/Page.d.ts.map +1 -1
  152. package/dist/react/components/Page.js +53 -8
  153. package/dist/react/components/Page.js.map +1 -1
  154. package/dist/react/components/PageGuideButton.d.ts +11 -0
  155. package/dist/react/components/PageGuideButton.d.ts.map +1 -0
  156. package/dist/react/components/PageGuideButton.js +13 -0
  157. package/dist/react/components/PageGuideButton.js.map +1 -0
  158. package/dist/react/components/PanelSeparator.js +1 -1
  159. package/dist/react/components/PanelSeparator.js.map +1 -1
  160. package/dist/react/components/Popover.d.ts.map +1 -1
  161. package/dist/react/components/Popover.js +2 -1
  162. package/dist/react/components/Popover.js.map +1 -1
  163. package/dist/react/components/PromptDialog.js +1 -1
  164. package/dist/react/components/PromptDialog.js.map +1 -1
  165. package/dist/react/components/ScrollArea.d.ts +9 -1
  166. package/dist/react/components/ScrollArea.d.ts.map +1 -1
  167. package/dist/react/components/ScrollArea.js +9 -3
  168. package/dist/react/components/ScrollArea.js.map +1 -1
  169. package/dist/react/components/Section.d.ts +5 -2
  170. package/dist/react/components/Section.d.ts.map +1 -1
  171. package/dist/react/components/Section.js +5 -2
  172. package/dist/react/components/Section.js.map +1 -1
  173. package/dist/react/components/Skeleton.d.ts +19 -1
  174. package/dist/react/components/Skeleton.d.ts.map +1 -1
  175. package/dist/react/components/Skeleton.js +24 -2
  176. package/dist/react/components/Skeleton.js.map +1 -1
  177. package/dist/react/components/StatStrip.js +1 -1
  178. package/dist/react/components/StatStrip.js.map +1 -1
  179. package/dist/react/components/StatTile.d.ts +30 -3
  180. package/dist/react/components/StatTile.d.ts.map +1 -1
  181. package/dist/react/components/StatTile.js +35 -9
  182. package/dist/react/components/StatTile.js.map +1 -1
  183. package/dist/react/components/StatTileGrid.d.ts +10 -1
  184. package/dist/react/components/StatTileGrid.d.ts.map +1 -1
  185. package/dist/react/components/StatTileGrid.js +50 -33
  186. package/dist/react/components/StatTileGrid.js.map +1 -1
  187. package/dist/react/components/StatusPill.d.ts.map +1 -1
  188. package/dist/react/components/StatusPill.js +26 -1
  189. package/dist/react/components/StatusPill.js.map +1 -1
  190. package/dist/react/components/Surface.d.ts +1 -1
  191. package/dist/react/components/Surface.d.ts.map +1 -1
  192. package/dist/react/components/Surface.js +20 -11
  193. package/dist/react/components/Surface.js.map +1 -1
  194. package/dist/react/components/SwatchPicker.d.ts.map +1 -1
  195. package/dist/react/components/SwatchPicker.js +5 -1
  196. package/dist/react/components/SwatchPicker.js.map +1 -1
  197. package/dist/react/components/Switch.js +1 -1
  198. package/dist/react/components/Switch.js.map +1 -1
  199. package/dist/react/components/WidgetHeader.js +1 -1
  200. package/dist/react/components/WidgetHeader.js.map +1 -1
  201. package/dist/react/components/calendar/CalendarLegend.js +1 -1
  202. package/dist/react/components/calendar/CalendarLegend.js.map +1 -1
  203. package/dist/react/components/calendar/DayAgenda.js +1 -1
  204. package/dist/react/components/calendar/DayAgenda.js.map +1 -1
  205. package/dist/react/components/calendar/EventChip.js +1 -1
  206. package/dist/react/components/calendar/EventChip.js.map +1 -1
  207. package/dist/react/components/calendar/MonthGrid.js +1 -1
  208. package/dist/react/components/calendar/MonthGrid.js.map +1 -1
  209. package/dist/react/components/calendar/WeekView.js +2 -2
  210. package/dist/react/components/calendar/WeekView.js.map +1 -1
  211. package/dist/react/components/cardFrame.d.ts +16 -0
  212. package/dist/react/components/cardFrame.d.ts.map +1 -0
  213. package/dist/react/components/cardFrame.js +24 -0
  214. package/dist/react/components/cardFrame.js.map +1 -0
  215. package/dist/react/components/dialog.d.ts.map +1 -1
  216. package/dist/react/components/dialog.js +2 -1
  217. package/dist/react/components/dialog.js.map +1 -1
  218. package/dist/react/dashboard-grid/WidgetFrame.js +2 -2
  219. package/dist/react/dashboard-grid/WidgetFrame.js.map +1 -1
  220. package/dist/react/emoji/EmojiPicker.js +1 -1
  221. package/dist/react/emoji/EmojiPicker.js.map +1 -1
  222. package/dist/react/file-tree/FileTree.d.ts +8 -1
  223. package/dist/react/file-tree/FileTree.d.ts.map +1 -1
  224. package/dist/react/file-tree/FileTree.js +29 -4
  225. package/dist/react/file-tree/FileTree.js.map +1 -1
  226. package/dist/react/filter-rail/FilterRail.js +1 -1
  227. package/dist/react/filter-rail/FilterRail.js.map +1 -1
  228. package/dist/react/icons.js +1 -1
  229. package/dist/react/icons.js.map +1 -1
  230. package/dist/react/index.d.ts +8 -5
  231. package/dist/react/index.d.ts.map +1 -1
  232. package/dist/react/index.js +5 -2
  233. package/dist/react/index.js.map +1 -1
  234. package/dist/react/lib/menuSections.d.ts +8 -0
  235. package/dist/react/lib/menuSections.d.ts.map +1 -0
  236. package/dist/react/lib/menuSections.js +37 -0
  237. package/dist/react/lib/menuSections.js.map +1 -0
  238. package/dist/react/markdown/Markdown.js +2 -2
  239. package/dist/react/markdown/Markdown.js.map +1 -1
  240. package/dist/react/media/HoverScrubVideoThumb.js +1 -1
  241. package/dist/react/media/HoverScrubVideoThumb.js.map +1 -1
  242. package/dist/react/media/MaskPainter.js +1 -1
  243. package/dist/react/media/MediaUploadControl.js +1 -1
  244. package/dist/react/media/MediaUploadControl.js.map +1 -1
  245. package/dist/react/media/VideoTrimmer.js +1 -1
  246. package/dist/react/media/VideoTrimmer.js.map +1 -1
  247. package/dist/react/media/denseGallery.css +5 -1
  248. package/dist/react/namespace/NamespaceTargetCapsule.js +1 -1
  249. package/dist/react/namespace/NamespaceTargetCapsule.js.map +1 -1
  250. package/dist/react/nav/BreadcrumbTrail.js +1 -1
  251. package/dist/react/nav/BreadcrumbTrail.js.map +1 -1
  252. package/dist/react/nav/CommandBar.d.ts +7 -1
  253. package/dist/react/nav/CommandBar.d.ts.map +1 -1
  254. package/dist/react/nav/CommandBar.js +5 -3
  255. package/dist/react/nav/CommandBar.js.map +1 -1
  256. package/dist/react/nav/PlaceBar.d.ts.map +1 -1
  257. package/dist/react/nav/PlaceBar.js +7 -9
  258. package/dist/react/nav/PlaceBar.js.map +1 -1
  259. package/dist/react/nav/SideNav.d.ts +12 -2
  260. package/dist/react/nav/SideNav.d.ts.map +1 -1
  261. package/dist/react/nav/SideNav.js +42 -22
  262. package/dist/react/nav/SideNav.js.map +1 -1
  263. package/dist/react/nav/TopBar.d.ts +65 -31
  264. package/dist/react/nav/TopBar.d.ts.map +1 -1
  265. package/dist/react/nav/TopBar.js +59 -24
  266. package/dist/react/nav/TopBar.js.map +1 -1
  267. package/dist/react/nav/index.d.ts +1 -1
  268. package/dist/react/nav/index.d.ts.map +1 -1
  269. package/dist/react/nav/index.js +1 -1
  270. package/dist/react/nav/index.js.map +1 -1
  271. package/dist/react/nav/navShared.d.ts +10 -1
  272. package/dist/react/nav/navShared.d.ts.map +1 -1
  273. package/dist/react/nav/navShared.js +23 -0
  274. package/dist/react/nav/navShared.js.map +1 -1
  275. package/dist/react/notifications/NotificationFeed.js +2 -2
  276. package/dist/react/notifications/NotificationFeed.js.map +1 -1
  277. package/dist/react/palette/Command.d.ts.map +1 -1
  278. package/dist/react/palette/Command.js +1 -1
  279. package/dist/react/palette/Command.js.map +1 -1
  280. package/dist/react/palette/CommandPalette.d.ts +13 -1
  281. package/dist/react/palette/CommandPalette.d.ts.map +1 -1
  282. package/dist/react/palette/CommandPalette.js +44 -8
  283. package/dist/react/palette/CommandPalette.js.map +1 -1
  284. package/dist/react/palette/commandSources.d.ts +57 -0
  285. package/dist/react/palette/commandSources.d.ts.map +1 -0
  286. package/dist/react/palette/commandSources.js +129 -0
  287. package/dist/react/palette/commandSources.js.map +1 -0
  288. package/dist/react/palette/index.d.ts +1 -0
  289. package/dist/react/palette/index.d.ts.map +1 -1
  290. package/dist/react/palette/index.js +1 -0
  291. package/dist/react/palette/index.js.map +1 -1
  292. package/dist/react/rich-text/RichTextEditor.js +1 -1
  293. package/dist/react/rich-text/RichTextEditor.js.map +1 -1
  294. package/dist/react/rich-text/RichTextToolbar.js +1 -1
  295. package/dist/react/rich-text/RichTextToolbar.js.map +1 -1
  296. package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
  297. package/dist/styles-areas/analytics.css +16 -16
  298. package/dist/styles-areas/auth.css +3 -3
  299. package/dist/styles-areas/calendar.css +6 -6
  300. package/dist/styles-areas/chart-table.css +1 -1
  301. package/dist/styles-areas/charts.css +3 -3
  302. package/dist/styles-areas/chat.css +7 -6
  303. package/dist/styles-areas/companion-link.css +2 -2
  304. package/dist/styles-areas/core.css +98 -89
  305. package/dist/styles-areas/dashboard-grid.css +4 -4
  306. package/dist/styles-areas/data-table.css +16 -16
  307. package/dist/styles-areas/disk-usage.css +5 -5
  308. package/dist/styles-areas/emoji.css +2 -2
  309. package/dist/styles-areas/fields.css +13 -14
  310. package/dist/styles-areas/filter-rail.css +4 -4
  311. package/dist/styles-areas/folder-tree.css +3 -3
  312. package/dist/styles-areas/markdown.css +2 -1
  313. package/dist/styles-areas/master-detail.css +3 -3
  314. package/dist/styles-areas/media-gallery.css +8 -8
  315. package/dist/styles-areas/media.css +12 -13
  316. package/dist/styles-areas/nav.css +28 -14
  317. package/dist/styles-areas/notifications.css +4 -4
  318. package/dist/styles-areas/overlays.css +4 -4
  319. package/dist/styles-areas/palette.css +4 -3
  320. package/dist/styles-areas/rich-text.css +3 -3
  321. package/dist/styles-areas/sharing.css +4 -3
  322. package/dist/styles-areas/stats.css +9 -8
  323. package/dist/styles-areas/test-report.css +3 -3
  324. package/dist/styles-areas/wizard.css +6 -5
  325. package/dist/styles-areas/workbench.css +10 -9
  326. package/dist/styles-areas/workbook-viewer.css +2 -2
  327. package/dist/styles-utilities.css +272 -187
  328. package/dist/testing/designAudit.d.ts.map +1 -1
  329. package/dist/testing/designAudit.js +4 -0
  330. package/dist/testing/designAudit.js.map +1 -1
  331. package/dist/testing/designBaseline.d.ts.map +1 -1
  332. package/dist/testing/designBaseline.js +4 -0
  333. package/dist/testing/designBaseline.js.map +1 -1
  334. package/dist/theme.css +103 -2
  335. package/package.json +3 -2
  336. package/scripts/guardrails-baseline.json +2 -1
  337. package/scripts/guardrailsEnforce.spec.ts +64 -0
  338. package/scripts/guardrailsEnforce.ts +65 -8
  339. package/scripts/styleAreas.ts +1 -0
  340. package/src/nodeTypesDeclared.spec.ts +47 -0
  341. package/src/react/Toast.spec.tsx +43 -0
  342. package/src/react/Toast.tsx +45 -2
  343. package/src/react/analytics/MetricDrilldown.tsx +1 -1
  344. package/src/react/charts/AreaChart.tsx +9 -8
  345. package/src/react/charts/BarChart.tsx +11 -10
  346. package/src/react/charts/ChartCard.tsx +146 -0
  347. package/src/react/charts/ChartFrame.tsx +28 -10
  348. package/src/react/charts/ChartLegend.tsx +1 -1
  349. package/src/react/charts/HeatmapChart.tsx +5 -5
  350. package/src/react/charts/IntervalTimeline.tsx +2 -2
  351. package/src/react/charts/LineChart.tsx +9 -8
  352. package/src/react/charts/TimeSeriesChart.tsx +11 -10
  353. package/src/react/charts/chartBox.spec.ts +40 -0
  354. package/src/react/charts/chartBox.ts +75 -0
  355. package/src/react/charts/index.ts +2 -0
  356. package/src/react/charts/shared.ts +16 -3
  357. package/src/react/chat/Bubble.tsx +5 -5
  358. package/src/react/chat/MessageScroller.tsx +1 -1
  359. package/src/react/components/ActionBar.spec.tsx +90 -0
  360. package/src/react/components/ActionBar.tsx +82 -2
  361. package/src/react/components/AddItemsMenu.tsx +1 -1
  362. package/src/react/components/Alert.spec.tsx +32 -2
  363. package/src/react/components/Alert.tsx +47 -3
  364. package/src/react/components/AppShell.spec.tsx +199 -13
  365. package/src/react/components/AppShell.tsx +235 -92
  366. package/src/react/components/AsyncPanel.spec.tsx +7 -1
  367. package/src/react/components/AsyncPanel.tsx +85 -5
  368. package/src/react/components/BannerLane.tsx +128 -0
  369. package/src/react/components/Button.tsx +2 -0
  370. package/src/react/components/ChipFilterStrip.tsx +1 -1
  371. package/src/react/components/ComboboxField.tsx +20 -3
  372. package/src/react/components/CommentThread.tsx +2 -2
  373. package/src/react/components/ConfirmDialog.tsx +1 -1
  374. package/src/react/components/DataTable/DataTable.tsx +80 -8
  375. package/src/react/components/DataTable/Pagination.tsx +1 -1
  376. package/src/react/components/DataTable/RowActions.tsx +55 -2
  377. package/src/react/components/DataTable/TableFilterBuilder.tsx +1 -1
  378. package/src/react/components/DataTable/cells/CodeCell.tsx +1 -1
  379. package/src/react/components/DataTable/cells/LinkCell.tsx +1 -1
  380. package/src/react/components/DataTable/index.ts +1 -0
  381. package/src/react/components/DataTable/numericColumn.spec.ts +19 -0
  382. package/src/react/components/DataTable/types.ts +36 -0
  383. package/src/react/components/DateTimeField.tsx +3 -1
  384. package/src/react/components/DeleteButton.tsx +3 -0
  385. package/src/react/components/Drawer.tsx +3 -3
  386. package/src/react/components/DropdownMenu.spec.tsx +55 -0
  387. package/src/react/components/DropdownMenu.tsx +73 -20
  388. package/src/react/components/EmptyState.tsx +26 -7
  389. package/src/react/components/EventTimeline.tsx +1 -1
  390. package/src/react/components/Field.width.spec.tsx +11 -0
  391. package/src/react/components/FileDropZone.tsx +1 -1
  392. package/src/react/components/Grid.spec.tsx +44 -0
  393. package/src/react/components/Grid.tsx +41 -11
  394. package/src/react/components/Guide.tsx +77 -0
  395. package/src/react/components/InfoHint.spec.tsx +21 -0
  396. package/src/react/components/InfoHintImpl.tsx +22 -1
  397. package/src/react/components/InlineEditableText.tsx +1 -1
  398. package/src/react/components/IsolatedFrame.tsx +1 -1
  399. package/src/react/components/ListToolbar.spec.tsx +12 -0
  400. package/src/react/components/ListToolbar.tsx +8 -5
  401. package/src/react/components/LogViewer.tsx +118 -18
  402. package/src/react/components/Modal.spec.tsx +17 -2
  403. package/src/react/components/Modal.tsx +25 -10
  404. package/src/react/components/MultiSelectField.spec.tsx +6 -0
  405. package/src/react/components/Page.spec.tsx +89 -5
  406. package/src/react/components/Page.tsx +100 -16
  407. package/src/react/components/PageGuideButton.tsx +42 -0
  408. package/src/react/components/PanelSeparator.tsx +1 -1
  409. package/src/react/components/Popover.tsx +3 -1
  410. package/src/react/components/PromptDialog.tsx +1 -1
  411. package/src/react/components/ScrollArea.spec.tsx +20 -0
  412. package/src/react/components/ScrollArea.tsx +19 -0
  413. package/src/react/components/Section.spec.tsx +82 -1
  414. package/src/react/components/Section.tsx +10 -4
  415. package/src/react/components/Skeleton.tsx +50 -2
  416. package/src/react/components/StatStrip.tsx +1 -1
  417. package/src/react/components/StatTile.tsx +84 -11
  418. package/src/react/components/StatTileGrid.spec.tsx +6 -1
  419. package/src/react/components/StatTileGrid.tsx +25 -0
  420. package/src/react/components/StatusPill.tsx +26 -1
  421. package/src/react/components/Surface.tsx +22 -14
  422. package/src/react/components/SwatchPicker.tsx +4 -0
  423. package/src/react/components/Switch.tsx +1 -1
  424. package/src/react/components/WidgetHeader.tsx +1 -1
  425. package/src/react/components/calendar/CalendarLegend.tsx +1 -1
  426. package/src/react/components/calendar/DayAgenda.tsx +1 -1
  427. package/src/react/components/calendar/EventChip.tsx +1 -1
  428. package/src/react/components/calendar/MonthGrid.tsx +1 -1
  429. package/src/react/components/calendar/WeekView.tsx +2 -2
  430. package/src/react/components/cardFrame.ts +28 -0
  431. package/src/react/components/dataDisplay.spec.tsx +176 -0
  432. package/src/react/components/dialog.tsx +4 -1
  433. package/src/react/dashboard-grid/WidgetFrame.tsx +2 -2
  434. package/src/react/emoji/EmojiPicker.tsx +1 -1
  435. package/src/react/file-tree/FileTree.spec.tsx +49 -0
  436. package/src/react/file-tree/FileTree.tsx +37 -4
  437. package/src/react/filter-rail/FilterRail.tsx +1 -1
  438. package/src/react/icons.tsx +1 -1
  439. package/src/react/index.ts +28 -3
  440. package/src/react/lib/menuSections.ts +33 -0
  441. package/src/react/markdown/Markdown.tsx +2 -2
  442. package/src/react/media/HoverScrubVideoThumb.tsx +2 -2
  443. package/src/react/media/MaskPainter.tsx +1 -1
  444. package/src/react/media/MediaUploadControl.tsx +1 -1
  445. package/src/react/media/VideoTrimmer.tsx +1 -1
  446. package/src/react/media/denseGallery.css +5 -1
  447. package/src/react/namespace/NamespaceTargetCapsule.tsx +1 -1
  448. package/src/react/nav/BreadcrumbTrail.tsx +2 -2
  449. package/src/react/nav/CommandBar.tsx +14 -3
  450. package/src/react/nav/PlaceBar.tsx +18 -8
  451. package/src/react/nav/SideNav.tsx +99 -17
  452. package/src/react/nav/TopBar.spec.tsx +78 -21
  453. package/src/react/nav/TopBar.tsx +270 -98
  454. package/src/react/nav/index.ts +8 -1
  455. package/src/react/nav/nav.spec.tsx +46 -2
  456. package/src/react/nav/navShared.tsx +23 -2
  457. package/src/react/notifications/NotificationFeed.tsx +2 -2
  458. package/src/react/palette/Command.tsx +1 -1
  459. package/src/react/palette/CommandPalette.spec.tsx +31 -0
  460. package/src/react/palette/CommandPalette.tsx +108 -11
  461. package/src/react/palette/commandSources.tsx +186 -0
  462. package/src/react/palette/index.ts +7 -0
  463. package/src/react/rich-text/RichTextEditor.tsx +1 -1
  464. package/src/react/rich-text/RichTextToolbar.tsx +1 -1
  465. package/src/styles-areas/analytics.css +16 -16
  466. package/src/styles-areas/auth.css +3 -3
  467. package/src/styles-areas/calendar.css +6 -6
  468. package/src/styles-areas/chart-table.css +1 -1
  469. package/src/styles-areas/charts.css +3 -3
  470. package/src/styles-areas/chat.css +7 -6
  471. package/src/styles-areas/companion-link.css +2 -2
  472. package/src/styles-areas/core.css +98 -89
  473. package/src/styles-areas/dashboard-grid.css +4 -4
  474. package/src/styles-areas/data-table.css +16 -16
  475. package/src/styles-areas/disk-usage.css +5 -5
  476. package/src/styles-areas/emoji.css +2 -2
  477. package/src/styles-areas/fields.css +13 -14
  478. package/src/styles-areas/filter-rail.css +4 -4
  479. package/src/styles-areas/folder-tree.css +3 -3
  480. package/src/styles-areas/markdown.css +2 -1
  481. package/src/styles-areas/master-detail.css +3 -3
  482. package/src/styles-areas/media-gallery.css +8 -8
  483. package/src/styles-areas/media.css +12 -13
  484. package/src/styles-areas/nav.css +28 -14
  485. package/src/styles-areas/notifications.css +4 -4
  486. package/src/styles-areas/overlays.css +4 -4
  487. package/src/styles-areas/palette.css +4 -3
  488. package/src/styles-areas/rich-text.css +3 -3
  489. package/src/styles-areas/sharing.css +4 -3
  490. package/src/styles-areas/stats.css +9 -8
  491. package/src/styles-areas/test-report.css +3 -3
  492. package/src/styles-areas/wizard.css +6 -5
  493. package/src/styles-areas/workbench.css +10 -9
  494. package/src/styles-areas/workbook-viewer.css +2 -2
  495. package/src/styles-utilities.css +272 -187
  496. package/src/testing/designAudit.ts +3 -0
  497. package/src/testing/designBaseline.ts +4 -0
  498. package/src/theme.css +103 -2
  499. package/src/themeDarkScopeAliases.spec.ts +65 -0
@@ -0,0 +1,128 @@
1
+ import { AlertTriangle, Info, XCircle } from 'lucide-react';
2
+ import { type ReactNode, useId, useState } from 'react';
3
+ import { cn } from '../lib/cn.js';
4
+ import { Button } from './Button.js';
5
+ import { ButtonLink } from './ButtonLink.js';
6
+
7
+ export interface ShellBanner {
8
+ /** Stable key. */
9
+ id: string;
10
+ /** ONE sentence — what is wrong, in the owner's words. Never a paragraph (CPY-1). */
11
+ message: ReactNode;
12
+ /** `warning` (default) for a degraded app, `error` for a broken one, `info` for a plain fact. */
13
+ tone?: 'info' | 'warning' | 'error';
14
+ /** Leading icon. Omit for the tone's own. */
15
+ icon?: ReactNode;
16
+ /** The ONE thing to do about it — "Reconnect", "Wake the Mac". */
17
+ action?: { label: string; onClick?: () => void; href?: string };
18
+ }
19
+
20
+ export interface BannerLaneProps {
21
+ banners: readonly ShellBanner[];
22
+ className?: string;
23
+ }
24
+
25
+ const TONE = {
26
+ info: 'bg-accent/10 [&_[data-cb-banner-icon]]:text-accent',
27
+ warning: 'bg-warning/10 [&_[data-cb-banner-icon]]:text-warning',
28
+ error: 'bg-destructive/10 [&_[data-cb-banner-icon]]:text-destructive',
29
+ } as const;
30
+
31
+ const ICON = { info: <Info />, warning: <AlertTriangle />, error: <XCircle /> } as const;
32
+
33
+ /** The worst tone in a set: an error outranks a warning outranks a fact. */
34
+ const worst = (banners: readonly ShellBanner[]): keyof typeof TONE =>
35
+ banners.some((b) => b.tone === 'error')
36
+ ? 'error'
37
+ : banners.some((b) => (b.tone ?? 'warning') === 'warning')
38
+ ? 'warning'
39
+ : 'info';
40
+
41
+ /**
42
+ * The shell's ONE banner lane (NAV-10): app-wide conditions — a sign-in broke, the Mac is
43
+ * asleep, the app is offline — as full-width, one-line ribbons between the header and the
44
+ * page. Icon, sentence, one action. Two or more collapse into "N problems · View", so a bad
45
+ * morning is one line tall until the reader asks for the list.
46
+ *
47
+ * A banner is never a card inside the page, and never repeated on the page it links to.
48
+ * `AppShell banners` renders this; a shell of an app's own can render it directly.
49
+ */
50
+ export function BannerLane({ banners, className }: BannerLaneProps) {
51
+ const [open, setOpen] = useState(false);
52
+ const listId = useId();
53
+ if (banners.length === 0) return null;
54
+
55
+ const only = banners.length === 1 ? banners[0] : undefined;
56
+ return (
57
+ <section
58
+ aria-label='App alerts'
59
+ data-cb-banner-lane=''
60
+ className={cn('shrink-0 border-b border-border', className)}
61
+ >
62
+ {only ? (
63
+ <BannerLine banner={only} />
64
+ ) : (
65
+ <>
66
+ <div
67
+ data-cb-banner=''
68
+ className={cn('flex min-h-10 items-center gap-3 px-3 py-1.5 text-sm md:px-6', TONE[worst(banners)])}
69
+ >
70
+ <span data-cb-banner-icon='' aria-hidden='true' className='grid size-5 shrink-0 place-items-center [&_svg]:size-4'>
71
+ {ICON[worst(banners)]}
72
+ </span>
73
+ <span className='min-w-0 flex-1 truncate font-medium text-foreground'>{banners.length} problems</span>
74
+ <Button
75
+ variant='ghost'
76
+ size='sm'
77
+ aria-expanded={open}
78
+ aria-controls={listId}
79
+ onClick={() => setOpen((o) => !o)}
80
+ tooltip={open ? 'Hide the list' : `Show all ${banners.length}`}
81
+ tooltipMobile='off'
82
+ >
83
+ {open ? 'Hide' : 'View'}
84
+ </Button>
85
+ </div>
86
+ {open ? (
87
+ <ul id={listId} className='divide-y divide-border border-t border-border'>
88
+ {banners.map((banner) => (
89
+ <li key={banner.id}>
90
+ <BannerLine banner={banner} />
91
+ </li>
92
+ ))}
93
+ </ul>
94
+ ) : null}
95
+ </>
96
+ )}
97
+ </section>
98
+ );
99
+ }
100
+
101
+ function BannerLine({ banner }: { banner: ShellBanner }) {
102
+ const tone = banner.tone ?? 'warning';
103
+ const { action } = banner;
104
+ return (
105
+ <div
106
+ data-cb-banner=''
107
+ role={tone === 'error' ? 'alert' : 'status'}
108
+ className={cn('flex min-h-10 items-center gap-3 px-3 py-1.5 text-sm md:px-6', TONE[tone])}
109
+ >
110
+ <span data-cb-banner-icon='' aria-hidden='true' className='grid size-5 shrink-0 place-items-center [&_svg]:size-4'>
111
+ {banner.icon ?? ICON[tone]}
112
+ </span>
113
+ {/* One line: a long sentence truncates rather than growing the lane (NAV-10). */}
114
+ <span className='min-w-0 flex-1 truncate text-foreground'>{banner.message}</span>
115
+ {action ? (
116
+ action.href ? (
117
+ <ButtonLink to={action.href} variant='ghost' size='sm' onClick={action.onClick} tooltip={action.label}>
118
+ {action.label} →
119
+ </ButtonLink>
120
+ ) : (
121
+ <Button variant='ghost' size='sm' onClick={action.onClick} tooltip={action.label} tooltipMobile='off'>
122
+ {action.label} →
123
+ </Button>
124
+ )
125
+ ) : null}
126
+ </div>
127
+ );
128
+ }
@@ -242,8 +242,10 @@ export function Button({
242
242
  resetTimer.current = setTimeout(() => setJustSucceeded(false), resetMs);
243
243
  } catch (err) {
244
244
  if (errorMessage !== false) {
245
+ // OVL-9: an error toast carries the action that resolves it.
245
246
  toast.error(
246
247
  typeof errorMessage === 'string' ? errorMessage : err instanceof Error ? err.message : String(err),
248
+ { action: { label: 'Retry', onClick: () => void handleClick?.(e) } },
247
249
  );
248
250
  }
249
251
  } finally {
@@ -121,7 +121,7 @@ function Chip({ active, onClick, children }: ChipProps) {
121
121
  className={cn(
122
122
  // 44 px min tap target height via py + min-h (STANDARDS §4a). px-4 keeps every
123
123
  // pill's internal padding identical regardless of label length.
124
- 'inline-flex min-h-[44px] shrink-0 cursor-pointer items-center rounded-full px-4 text-sm font-medium transition-colors',
124
+ 'inline-flex min-h-11 shrink-0 cursor-pointer items-center rounded-full px-4 text-sm font-medium transition-colors',
125
125
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
126
126
  // Soft active state — an accent tint + accent text (the standardized active
127
127
  // treatment shared with nav items and the `accent` Badge tone), not a stark
@@ -4,7 +4,13 @@ import { Combobox } from '@base-ui/react/combobox';
4
4
  import { Check, ChevronDown, Loader2, Plus, X } from 'lucide-react';
5
5
  import { useCallback, useEffect, useRef, useState } from 'react';
6
6
  import { cn } from '../lib/cn.js';
7
- import { CONTROL_HEIGHT_CLASS, CONTROL_MIN_HEIGHT_CLASS, type ControlSize, declareFieldWidth } from '../lib/field.js';
7
+ import {
8
+ CONTROL_HEIGHT_CLASS,
9
+ CONTROL_MIN_HEIGHT_CLASS,
10
+ type ControlSize,
11
+ choiceFieldWidth,
12
+ declareFieldWidth,
13
+ } from '../lib/field.js';
8
14
  import { FLOATING_COLLISION_PADDING,
9
15
  FLOATING_CONTENT_BASE, FLOATING_ITEM_BASE } from '../lib/floatingStyles.js';
10
16
  import { TAP_TARGET } from '../lib/tapTarget.js';
@@ -242,6 +248,10 @@ export function ComboboxField(props: ComboboxFieldProps) {
242
248
  value={(multiple ? multiValue : singleValue) as never}
243
249
  onValueChange={handleValueChange as never}
244
250
  onInputValueChange={(v) => setQuery(v)}
251
+ // The value is the option's `value` string, the items are option objects: without this
252
+ // Base UI writes `String(value)` into the closed input — "electric", not "Electric"
253
+ // (forge task 2514). Called with both shapes.
254
+ itemToStringLabel={((v: string | ComboboxOption) => (typeof v === 'string' ? labelForValue(v) : v.label)) as never}
245
255
  >
246
256
  <Combobox.InputGroup className={inputGroupClass}>
247
257
  {multiple && (
@@ -269,7 +279,9 @@ export function ComboboxField(props: ComboboxFieldProps) {
269
279
  id={id}
270
280
  aria-describedby={ariaDescribedBy}
271
281
  aria-invalid={ariaInvalid}
272
- placeholder={placeholder}
282
+ // Once a chip is chosen the prompt has done its job: "Select…" beside "Idea ×" reads
283
+ // as if nothing were selected (forge task 2514).
284
+ placeholder={multiple && (multiValue?.length ?? 0) > 0 ? undefined : placeholder}
273
285
  className={cn(
274
286
  'min-w-24 flex-1 bg-transparent px-3 text-base text-field-fg outline-none placeholder:text-field-placeholder sm:text-sm',
275
287
  multiple && 'py-1.5',
@@ -320,4 +332,9 @@ export function ComboboxField(props: ComboboxFieldProps) {
320
332
  );
321
333
  }
322
334
 
323
- declareFieldWidth(ComboboxField, (props) => (props.multiple ? 'full' : 'md'));
335
+ // FRM-3: a closed list is sized by its longest option, as SelectField is. A list that can hold
336
+ // a value nobody has seen yet — fetched (`loadOptions`) or typed (`creatable`) — keeps a
337
+ // name-length `md`; a chip row (`multiple`) keeps the whole row.
338
+ declareFieldWidth(ComboboxField, (props) =>
339
+ props.multiple ? 'full' : props.loadOptions || props.creatable ? 'md' : choiceFieldWidth(props),
340
+ );
@@ -108,7 +108,7 @@ function Composer({
108
108
  if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) submit();
109
109
  if (e.key === 'Escape') onCancel?.();
110
110
  }}
111
- className={cn(FIELD_CLASS, 'min-h-[2.25rem] flex-1')}
111
+ className={cn(FIELD_CLASS, 'min-h-9 flex-1')}
112
112
  />
113
113
  <Button
114
114
  variant='primary'
@@ -177,7 +177,7 @@ function CommentItem({
177
177
  value={draft}
178
178
  rows={1}
179
179
  onChange={(e) => setDraft(e.target.value)}
180
- className={cn(FIELD_CLASS, 'min-h-[2.25rem] flex-1')}
180
+ className={cn(FIELD_CLASS, 'min-h-9 flex-1')}
181
181
  />
182
182
  <Button
183
183
  variant='primary'
@@ -53,7 +53,7 @@ export function ConfirmDialog({
53
53
  <DialogContent
54
54
  ref={contentRef}
55
55
  className={cn(
56
- 'fixed top-1/2 left-1/2 flex w-[calc(100vw-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 flex-col gap-4',
56
+ 'fixed top-1/2 left-1/2 flex w-[calc(100vw-2rem)] max-w-[var(--modal-w-sm)] -translate-x-1/2 -translate-y-1/2 flex-col gap-4',
57
57
  'rounded-xl border border-border bg-popover p-6 text-popover-foreground shadow-overlay',
58
58
  'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
59
59
  'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
@@ -50,12 +50,14 @@ import { SortHeader } from './SortHeader.js';
50
50
  import type {
51
51
  ColDef,
52
52
  DataTableHandle,
53
+ DataTableRowAction,
53
54
  DataTableRowProps,
54
55
  DataTableSelectAll,
55
56
  DataTableServerWindow,
56
57
  TableViewMode,
57
58
  } from './types.js';
58
- import { resolveColumnAlign, SELECT_COLUMN_ID } from './types.js';
59
+ import { isNumericColumn, resolveColumnAlign, SELECT_COLUMN_ID, untypedNumericColumn } from './types.js';
60
+ import { devWarnOnce } from '../../lib/devWarn.js';
59
61
  import { AUTO_TRACK_FLOOR_PX, columnFloor, columnsDroppedForWidth } from './columnPriority.js';
60
62
  import { lazyPart } from './lazyPart.js';
61
63
  import type { TablePaginationProps } from './Pagination.js';
@@ -105,7 +107,7 @@ function ToolbarPlaceholder() {
105
107
  /** Where the pager will be, the height it will be. */
106
108
  function PaginationPlaceholder() {
107
109
  return (
108
- <div data-cb-table-pagination-pending='' aria-hidden className='border-t border-border px-4 py-2.5'>
110
+ <div data-cb-table-pagination-pending='' aria-hidden className='border-t border-border px-4 py-2'>
109
111
  <div className={cn('h-8', TAP_TARGET_HEIGHT)} />
110
112
  </div>
111
113
  );
@@ -143,6 +145,8 @@ declare module '@tanstack/react-table' {
143
145
  minWidth?: number;
144
146
  hideBelow?: 'sm' | 'md' | 'lg';
145
147
  leading?: boolean;
148
+ /** The column's values are numbers — its header and cells set `tabular-nums` (DAT-1). */
149
+ numeric?: boolean;
146
150
  }
147
151
  interface TableMeta<TData extends RowData> {
148
152
  /** {@link DataTableControls} — see its doc for why it rides on the table. */
@@ -229,6 +233,12 @@ export interface DataTableProps<TRow extends Record<string, unknown>> {
229
233
  /** The kebab's accessible name for one row — `(row) => \`More for ${row.title}\``. Default
230
234
  * `Row actions`, identical on every row. */
231
235
  rowMenuLabel?: (row: TRow) => string;
236
+ /**
237
+ * The row's ONE optional inline action, beside its `⋯` (DAT-5) — only when it is the row's
238
+ * reason to exist ("Play", "Retry"). Return `null` for a row that has none. Everything else
239
+ * goes in {@link rowMenu}. Renders the actions column.
240
+ */
241
+ rowAction?: (row: TRow) => DataTableRowAction | null;
232
242
  loading?: boolean;
233
243
  /** Shown when there are no rows (default: a plain "No results"). */
234
244
  emptyState?: ReactNode;
@@ -290,6 +300,14 @@ export interface DataTableProps<TRow extends Record<string, unknown>> {
290
300
  * cells vary wildly and that never claimed fixed lanes.
291
301
  */
292
302
  fixedLayout?: boolean;
303
+ /**
304
+ * Keep the table's MEASURE (DAT-3, LAY-2): it takes its columns' natural content width, up
305
+ * to its container, instead of stretching every column across a 1536px page. A dense table
306
+ * spread over a 2,560px monitor is a row nobody can track from its name to its numbers. The
307
+ * table hugs the left edge and the toolbar keeps the container's width. Off by default,
308
+ * because a fixed-layout table's proportional tracks need the full width to mean anything.
309
+ */
310
+ fit?: boolean;
293
311
  /**
294
312
  * EXTRA freeze floor for the wide grid (px): below this the columns stop
295
313
  * compressing and the wrapper scrolls horizontally instead — the design
@@ -910,6 +928,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
910
928
  onRowDelete,
911
929
  rowMenu,
912
930
  rowMenuLabel,
931
+ rowAction,
913
932
  loading,
914
933
  emptyState,
915
934
  toolbar,
@@ -925,6 +944,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
925
944
  rowClassName,
926
945
  hideEmptyColumns,
927
946
  fixedLayout,
947
+ fit = false,
928
948
  minTableWidth,
929
949
  scrollBody,
930
950
  virtualize,
@@ -1048,6 +1068,18 @@ export function DataTable<TRow extends Record<string, unknown>>({
1048
1068
  [columns, data, hideEmptyColumns],
1049
1069
  );
1050
1070
 
1071
+ // DAT-1: a plain-text column of numbers left-aligns them in proportional digits, which is
1072
+ // the misalignment the type system exists to prevent. Warn the author (dev only, once per
1073
+ // column) instead of guessing — a column of zip codes or years is "numeric" and is not.
1074
+ useEffect(() => {
1075
+ if (data.length === 0) return;
1076
+ const sample = data.slice(0, 50);
1077
+ for (const col of columns) {
1078
+ const warning = untypedNumericColumn(col, sample.map((row) => readPath(row, col.key)));
1079
+ if (warning) devWarnOnce(`datatable-untyped-numeric:${col.key}`, warning);
1080
+ }
1081
+ }, [columns, data]);
1082
+
1051
1083
  /**
1052
1084
  * The filter BUILDER is applied to the rows before TanStack ever sees them,
1053
1085
  * and that is deliberate: it is what makes the builder AND with the search box
@@ -1109,6 +1141,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
1109
1141
  label: col.label,
1110
1142
  menuLabel: col.menuLabel,
1111
1143
  align: resolveColumnAlign(col),
1144
+ numeric: isNumericColumn(col),
1112
1145
  width: col.width,
1113
1146
  minWidth: col.minWidth,
1114
1147
  hideBelow: col.hideBelow,
@@ -1558,7 +1591,10 @@ export function DataTable<TRow extends Record<string, unknown>>({
1558
1591
  /** The spacers belong to the tier on screen; the hidden ones hold no rows and no padding. */
1559
1592
  const tierPad = (t: TableTier) =>
1560
1593
  virtualEnabled && activeTier === t ? { paddingTop: padTop, paddingBottom: padBottom } : undefined;
1561
- const hasActions = !!(onRowEdit || onRowDelete || rowMenu);
1594
+ const hasActions = !!(onRowEdit || onRowDelete || rowMenu || rowAction);
1595
+ // DAT-5: right-click opens the SAME menu the row's `⋯` opens — a desktop accelerator, never
1596
+ // the only way in. Held by row id so the menu stays anchored to that row's kebab.
1597
+ const [contextMenuRowId, setContextMenuRowId] = useState<string | null>(null);
1562
1598
 
1563
1599
  // ── Row selection (see the `selectedIds` prop doc) ──────────────────────────
1564
1600
  const canSelect = (row: TRow) => isRowSelectable?.(row) ?? true;
@@ -1769,7 +1805,23 @@ export function DataTable<TRow extends Record<string, unknown>>({
1769
1805
  ))}
1770
1806
  </div>
1771
1807
  ) : rows.length === 0 && windowTotal === 0 ? (
1772
- <div className='p-2'>{emptyState ?? <EmptyState title='No results' />}</div>
1808
+ <div className='p-2'>
1809
+ {emptyState ??
1810
+ // STA-3: rows exist and the reader's search or filters hid them all — say so, and
1811
+ // offer the one action that undoes it. A table with no data says "No results".
1812
+ (data.length > 0 && !serverWindow ? (
1813
+ <EmptyState
1814
+ compact
1815
+ onClearFilters={() => {
1816
+ setSearchText('');
1817
+ setFilterQuery(EMPTY_FILTER_QUERY);
1818
+ setColumnFilters([]);
1819
+ }}
1820
+ />
1821
+ ) : (
1822
+ <EmptyState title='No results' />
1823
+ ))}
1824
+ </div>
1773
1825
  ) : (
1774
1826
  // scrollBody makes the table body the one scroll container for BOTH axes —
1775
1827
  // required for stickyHeader to pin (see the prop doc); ScrollArea's
@@ -1859,7 +1911,8 @@ export function DataTable<TRow extends Record<string, unknown>>({
1859
1911
  onEdit={onRowEdit}
1860
1912
  onDelete={onRowDelete}
1861
1913
  menu={rowMenu?.(row.original)}
1862
- menuLabel={rowMenuLabel?.(row.original)}
1914
+ menuLabel={rowMenuLabel?.(row.original)}
1915
+ action={rowAction?.(row.original) ?? undefined}
1863
1916
  />
1864
1917
  ) : null
1865
1918
  }
@@ -1998,6 +2051,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
1998
2051
  onDelete={onRowDelete}
1999
2052
  menu={rowMenu?.(row.original)}
2000
2053
  menuLabel={rowMenuLabel?.(row.original)}
2054
+ action={rowAction?.(row.original) ?? undefined}
2001
2055
  />
2002
2056
  ) : null}
2003
2057
  </div>
@@ -2057,7 +2111,8 @@ export function DataTable<TRow extends Record<string, unknown>>({
2057
2111
  aria-rowcount={
2058
2112
  virtualEnabled ? (serverWindow ? windowTotal + 1 : onNearEnd ? -1 : rows.length + 1) : undefined
2059
2113
  }
2060
- className={cn('w-full border-collapse text-sm', fixedLayout && 'table-fixed')}
2114
+ data-cb-table-fit={fit || undefined}
2115
+ className={cn('border-collapse text-sm', fit ? 'w-auto max-w-full' : 'w-full', fixedLayout && 'table-fixed')}
2061
2116
  style={tableMinWidth != null ? { minWidth: tableMinWidth } : undefined}
2062
2117
  >
2063
2118
  {caption ? <caption className='sr-only'>{caption}</caption> : null}
@@ -2097,6 +2152,11 @@ export function DataTable<TRow extends Record<string, unknown>>({
2097
2152
  'border-b border-border px-4 text-caption font-bold uppercase tracking-wider text-muted-foreground',
2098
2153
  cellPadY,
2099
2154
  align === 'right' ? 'text-right' : 'text-left',
2155
+ meta?.numeric && 'tabular-nums',
2156
+ // Under `fit` the column takes its CONTENT's width, and a truncating
2157
+ // label contributes none — "RUNS" read "R…" over a column of 2,472s.
2158
+ // The header's own width is the column's floor instead.
2159
+ fit && 'min-w-max [&>*]:max-w-none',
2100
2160
  // Sticky pins per-<th> (a <tr> can't stick); needs its own
2101
2161
  // near-opaque bg so rows scrolling beneath never bleed through.
2102
2162
  stickyHeader &&
@@ -2143,7 +2203,8 @@ export function DataTable<TRow extends Record<string, unknown>>({
2143
2203
  {hasActions ? (
2144
2204
  <th
2145
2205
  className={cn(
2146
- 'w-20 border-b border-border px-2',
2206
+ // A fixed trailing track (DAT-12): the ⋯ lines up down the list.
2207
+ rowAction ? 'w-32 border-b border-border px-2' : 'w-20 border-b border-border px-2',
2147
2208
  stickyHeader && 'sticky top-0 z-10 bg-card/95 backdrop-blur-sm',
2148
2209
  )}
2149
2210
  aria-label='Row actions'
@@ -2220,6 +2281,13 @@ export function DataTable<TRow extends Record<string, unknown>>({
2220
2281
  ),
2221
2282
  tabIndex: onRowClick ? 0 : undefined,
2222
2283
  onClick: onRowClick ? (event: MouseEvent<HTMLElement>) => onRowClick(row.original, event) : undefined,
2284
+ onContextMenu: rowMenu
2285
+ ? (event: MouseEvent<HTMLElement>) => {
2286
+ if (rowMenu(row.original).length === 0) return;
2287
+ event.preventDefault();
2288
+ setContextMenuRowId(row.id);
2289
+ }
2290
+ : undefined,
2223
2291
  onKeyDown: onRowClick
2224
2292
  ? (e: KeyboardEvent<HTMLElement>) => {
2225
2293
  if (e.key === 'Enter') onRowClick(row.original);
@@ -2266,6 +2334,7 @@ export function DataTable<TRow extends Record<string, unknown>>({
2266
2334
  // has to escape its bounds.
2267
2335
  fixedLayout && '[overflow-wrap:anywhere]',
2268
2336
  cell.column.columnDef.meta?.align === 'right' && 'text-right',
2337
+ cell.column.columnDef.meta?.numeric && 'tabular-nums',
2269
2338
  cell.column.columnDef.meta?.hideBelow &&
2270
2339
  HIDE_BELOW_CLASS[cell.column.columnDef.meta.hideBelow],
2271
2340
  droppedByWidth.has(cell.column.id) && COLLAPSED_CLASS,
@@ -2310,7 +2379,10 @@ export function DataTable<TRow extends Record<string, unknown>>({
2310
2379
  onEdit={onRowEdit}
2311
2380
  onDelete={onRowDelete}
2312
2381
  menu={rowMenu?.(row.original)}
2313
- menuLabel={rowMenuLabel?.(row.original)}
2382
+ menuLabel={rowMenuLabel?.(row.original)}
2383
+ action={rowAction?.(row.original) ?? undefined}
2384
+ menuOpen={contextMenuRowId === row.id}
2385
+ onMenuOpenChange={(open) => setContextMenuRowId(open ? row.id : null)}
2314
2386
  />
2315
2387
  </td>
2316
2388
  ) : null}
@@ -25,7 +25,7 @@ export function TablePagination<TRow>({ table, pageSizeOptions }: TablePaginatio
25
25
  const end = Math.min((pageIndex + 1) * pageSize, total);
26
26
 
27
27
  return (
28
- <div className='flex flex-wrap items-center justify-between gap-3 border-t border-border px-4 py-2.5 text-sm text-muted-foreground'>
28
+ <div className='flex flex-wrap items-center justify-between gap-3 border-t border-border px-4 py-2 text-sm text-muted-foreground'>
29
29
  <div className='flex items-center gap-3'>
30
30
  <span>{total === 0 ? 'No results' : `${start}–${end} of ${total}`}</span>
31
31
  {pageSizeOptions?.length ? (
@@ -1,6 +1,8 @@
1
1
  import { EllipsisVertical, Pencil, Trash2 } from 'lucide-react';
2
+ import { Button } from '../Button.js';
2
3
  import type { DropdownMenuItem } from '../DropdownMenu.js';
3
4
  import { IconButton } from '../IconButton.js';
5
+ import type { DataTableRowAction } from './types.js';
4
6
  import { lazyPart } from './lazyPart.js';
5
7
 
6
8
  // The kebab's menu is Radix's Menu, and most tables never draw a kebab — so it loads
@@ -21,6 +23,11 @@ export interface RowActionsProps<TRow> {
21
23
  /** The kebab's accessible name and tooltip — say WHOSE actions (`More for Alien`). Default
22
24
  * `Row actions`, which a screen reader repeats identically on every row. */
23
25
  menuLabel?: string;
26
+ /** The row's ONE inline action (DAT-5) — a quiet button before the kebab. */
27
+ action?: DataTableRowAction;
28
+ /** Controlled open state of the kebab's menu — how a right-click on the row opens it. */
29
+ menuOpen?: boolean;
30
+ onMenuOpenChange?: (open: boolean) => void;
24
31
  }
25
32
 
26
33
  /**
@@ -30,7 +37,16 @@ export interface RowActionsProps<TRow> {
30
37
  * caller's call (wrap `onDelete` with `confirm()` from task 10) — this primitive just
31
38
  * surfaces the intent.
32
39
  */
33
- export function RowActions<TRow>({ row, onEdit, onDelete, menu, menuLabel = 'Row actions' }: RowActionsProps<TRow>) {
40
+ export function RowActions<TRow>({
41
+ row,
42
+ onEdit,
43
+ onDelete,
44
+ menu,
45
+ menuLabel = 'Row actions',
46
+ action,
47
+ menuOpen,
48
+ onMenuOpenChange,
49
+ }: RowActionsProps<TRow>) {
34
50
  const kebab = (
35
51
  <IconButton label={menuLabel} className='size-8 pointer-coarse:min-w-11 [&_svg]:size-4'>
36
52
  <EllipsisVertical />
@@ -46,6 +62,37 @@ export function RowActions<TRow>({ row, onEdit, onDelete, menu, menuLabel = 'Row
46
62
  onClick={(e) => e.stopPropagation()}
47
63
  onPointerDown={(e) => e.stopPropagation()}
48
64
  >
65
+ {action ? (
66
+ action.icon ? (
67
+ <IconButton
68
+ label={action.label}
69
+ className='size-8 pointer-coarse:min-w-11 [&_svg]:size-4'
70
+ disabled={action.disabled && !action.disabledReason}
71
+ unavailableReason={action.disabled ? action.disabledReason : undefined}
72
+ onClick={(e) => {
73
+ e.stopPropagation();
74
+ action.onClick();
75
+ }}
76
+ >
77
+ {action.icon}
78
+ </IconButton>
79
+ ) : (
80
+ <Button
81
+ variant='ghost'
82
+ size='sm'
83
+ tooltip={action.label}
84
+ tooltipMobile='off'
85
+ disabled={action.disabled && !action.disabledReason}
86
+ unavailableReason={action.disabled ? action.disabledReason : undefined}
87
+ onClick={(e) => {
88
+ e.stopPropagation();
89
+ action.onClick();
90
+ }}
91
+ >
92
+ {action.label}
93
+ </Button>
94
+ )
95
+ ) : null}
49
96
  {onEdit ? (
50
97
  <IconButton
51
98
  label='Edit row'
@@ -73,7 +120,13 @@ export function RowActions<TRow>({ row, onEdit, onDelete, menu, menuLabel = 'Row
73
120
  {menu && menu.length > 0 ? (
74
121
  // Until the menu arrives the kebab is already there, the same button in the same
75
122
  // place — so nothing shifts and nothing flashes; a click in those milliseconds opens nothing.
76
- <MenuPart align='end' trigger={kebab} items={menu} fallback={kebab} />
123
+ <MenuPart
124
+ align='end'
125
+ trigger={kebab}
126
+ items={menu}
127
+ fallback={kebab}
128
+ {...(onMenuOpenChange ? { open: menuOpen ?? false, onOpenChange: onMenuOpenChange } : {})}
129
+ />
77
130
  ) : null}
78
131
  </div>
79
132
  );
@@ -152,7 +152,7 @@ export function TableFilterBuilder({
152
152
  ) : null}
153
153
 
154
154
  {/* guardrails-ignore no-raw-overflow: bounded builder body inside a portal panel. */}
155
- <div className='flex max-h-[22rem] flex-col gap-2 overflow-y-auto'>
155
+ <div className='flex max-h-88 flex-col gap-2 overflow-y-auto'>
156
156
  {value.groups.map((group, groupIndex) => (
157
157
  <div key={group.id}>
158
158
  {groupIndex > 0 ? (
@@ -17,7 +17,7 @@ export function CodeCell({ value, language }: CodeCellProps) {
17
17
  <TruncatedText
18
18
  as='code'
19
19
  data-language={language}
20
- className='block max-w-[240px] rounded bg-muted px-1.5 py-0.5 font-mono text-xs text-muted-foreground'
20
+ className='block max-w-60 rounded bg-muted px-1.5 py-0.5 font-mono text-xs text-muted-foreground'
21
21
  >
22
22
  {value}
23
23
  </TruncatedText>
@@ -25,7 +25,7 @@ export function LinkCell({ href, label }: LinkCellProps): JSX.Element | null {
25
25
  url={href}
26
26
  {...(label === undefined ? {} : { display: label })}
27
27
  stopPropagation
28
- className={`max-w-[180px] truncate font-sans not-italic ${TAP_TARGET_HEIGHT}`}
28
+ className={`max-w-45 truncate font-sans not-italic ${TAP_TARGET_HEIGHT}`}
29
29
  />
30
30
  );
31
31
  }
@@ -71,6 +71,7 @@ export { resolveColumnAlign } from './types.js';
71
71
  export type {
72
72
  ColDef,
73
73
  DataTableHandle,
74
+ DataTableRowAction,
74
75
  DataTableRowProps,
75
76
  DataTableSelectAll,
76
77
  DataTableServerWindow,
@@ -0,0 +1,19 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { isNumericColumn, untypedNumericColumn } from './types.js';
3
+
4
+ describe('DAT-1 · what counts as a numeric column', () => {
5
+ test('money counts as numeric, text does not', () => {
6
+ expect(isNumericColumn({ key: 'a', label: 'A', type: 'money' })).toBe(true);
7
+ expect(isNumericColumn({ key: 'a', label: 'A', type: 'text' })).toBe(false);
8
+ });
9
+
10
+ test('a text column holding only numbers is named; a rendered or aligned one is left alone', () => {
11
+ const col = { key: 'code', label: 'Code', type: 'text' } as const;
12
+ expect(untypedNumericColumn(col, ['41', '7', '1,204'])).toContain("type 'number'");
13
+ expect(untypedNumericColumn(col, ['41', 'x'])).toBeNull();
14
+ expect(untypedNumericColumn(col, ['41'])).toBeNull();
15
+ expect(untypedNumericColumn({ ...col, align: 'left' }, ['41', '7'])).toBeNull();
16
+ expect(untypedNumericColumn({ ...col, render: () => null }, ['41', '7'])).toBeNull();
17
+ });
18
+
19
+ });