@xemahq/ui-kernel 0.10.0 → 0.11.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 (379) hide show
  1. package/dist/lib/biome-host/agent-validation.d.ts.map +1 -1
  2. package/dist/lib/biome-host/agent-validation.js +26 -13
  3. package/dist/lib/biome-host/agent-validation.js.map +1 -1
  4. package/dist/lib/biome-host/biome-registry.d.ts.map +1 -1
  5. package/dist/lib/biome-host/biome-registry.js +27 -10
  6. package/dist/lib/biome-host/biome-registry.js.map +1 -1
  7. package/dist/lib/biome-host/biome-scope-hooks.d.ts.map +1 -1
  8. package/dist/lib/biome-host/biome-scope-hooks.js +2 -1
  9. package/dist/lib/biome-host/biome-scope-hooks.js.map +1 -1
  10. package/dist/lib/biome-host/biome-scoped-query.d.ts.map +1 -1
  11. package/dist/lib/biome-host/biome-scoped-query.js +5 -3
  12. package/dist/lib/biome-host/biome-scoped-query.js.map +1 -1
  13. package/dist/lib/biome-host/frontend-biome.d.ts +1 -1
  14. package/dist/lib/biome-host/frontend-biome.d.ts.map +1 -1
  15. package/dist/lib/biome-host/host-bridge.d.ts +1 -1
  16. package/dist/lib/biome-host/host-bridge.d.ts.map +1 -1
  17. package/dist/lib/biome-host/host-bridge.js.map +1 -1
  18. package/dist/lib/biome-host/i18n-hooks.d.ts.map +1 -1
  19. package/dist/lib/biome-host/i18n-hooks.js.map +1 -1
  20. package/dist/lib/biome-host/realtime-hooks.d.ts.map +1 -1
  21. package/dist/lib/biome-host/realtime-hooks.js.map +1 -1
  22. package/dist/lib/biome-host/session-contributions.d.ts +1 -1
  23. package/dist/lib/biome-host/session-contributions.d.ts.map +1 -1
  24. package/dist/lib/biome-host/use-mutation-with-error-toast.d.ts.map +1 -1
  25. package/dist/lib/biome-host/use-mutation-with-error-toast.js +3 -2
  26. package/dist/lib/biome-host/use-mutation-with-error-toast.js.map +1 -1
  27. package/dist/lib/biome-host/use-page-state.d.ts.map +1 -1
  28. package/dist/lib/biome-host/use-page-state.js +19 -10
  29. package/dist/lib/biome-host/use-page-state.js.map +1 -1
  30. package/dist/lib/capabilities/capability-provider.d.ts +1 -1
  31. package/dist/lib/capabilities/capability-provider.d.ts.map +1 -1
  32. package/dist/lib/capabilities/capability-provider.js +2 -2
  33. package/dist/lib/capabilities/capability-provider.js.map +1 -1
  34. package/dist/lib/capabilities/use-capability.d.ts +1 -1
  35. package/dist/lib/capabilities/use-capability.d.ts.map +1 -1
  36. package/dist/lib/capabilities/use-capability.js +2 -1
  37. package/dist/lib/capabilities/use-capability.js.map +1 -1
  38. package/dist/lib/contribution-registry/contribution-registry.d.ts.map +1 -1
  39. package/dist/lib/contribution-registry/contribution-registry.js +2 -1
  40. package/dist/lib/contribution-registry/contribution-registry.js.map +1 -1
  41. package/dist/lib/system-bus/deeplink.d.ts.map +1 -1
  42. package/dist/lib/system-bus/deeplink.js +4 -2
  43. package/dist/lib/system-bus/deeplink.js.map +1 -1
  44. package/dist/lib/system-bus/host-ports.d.ts +2 -2
  45. package/dist/lib/system-bus/host-ports.d.ts.map +1 -1
  46. package/dist/lib/system-bus/intent-registry.d.ts.map +1 -1
  47. package/dist/lib/system-bus/intent-registry.js +6 -3
  48. package/dist/lib/system-bus/intent-registry.js.map +1 -1
  49. package/dist/lib/system-bus/intents.d.ts +1 -1
  50. package/dist/lib/system-bus/intents.d.ts.map +1 -1
  51. package/dist/lib/system-bus/palette.d.ts +1 -1
  52. package/dist/lib/system-bus/palette.d.ts.map +1 -1
  53. package/dist/lib/system-bus/system-bus-builder.d.ts.map +1 -1
  54. package/dist/lib/system-bus/system-bus-builder.js +1 -1
  55. package/dist/lib/system-bus/system-bus-builder.js.map +1 -1
  56. package/dist/lib/system-bus/windows.d.ts +1 -1
  57. package/dist/lib/system-bus/windows.d.ts.map +1 -1
  58. package/dist/offline/outbox.d.ts +1 -0
  59. package/dist/offline/outbox.d.ts.map +1 -1
  60. package/dist/offline/outbox.js +4 -1
  61. package/dist/offline/outbox.js.map +1 -1
  62. package/dist/offline/react-hooks.d.ts +1 -1
  63. package/dist/offline/react-hooks.d.ts.map +1 -1
  64. package/dist/offline/react-hooks.js.map +1 -1
  65. package/dist/org-db/components/DbResultTable.d.ts.map +1 -1
  66. package/dist/org-db/components/DbResultTable.js +15 -6
  67. package/dist/org-db/components/DbResultTable.js.map +1 -1
  68. package/dist/registry/lib/agent-validation-host.d.ts.map +1 -1
  69. package/dist/registry/lib/agent-validation-host.js +1 -1
  70. package/dist/registry/lib/agent-validation-host.js.map +1 -1
  71. package/dist/registry/lib/biome-slot.d.ts.map +1 -1
  72. package/dist/registry/lib/biome-slot.js +5 -3
  73. package/dist/registry/lib/biome-slot.js.map +1 -1
  74. package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -1
  75. package/dist/registry/lib/primitives/SessionEventCard.js +14 -2
  76. package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -1
  77. package/dist/registry/lib/primitives/SessionEventTimeline.d.ts.map +1 -1
  78. package/dist/registry/lib/primitives/SessionEventTimeline.js +6 -3
  79. package/dist/registry/lib/primitives/SessionEventTimeline.js.map +1 -1
  80. package/dist/registry/lib/primitives/SessionTabbedDrawer.d.ts.map +1 -1
  81. package/dist/registry/lib/primitives/SessionTabbedDrawer.js +10 -5
  82. package/dist/registry/lib/primitives/SessionTabbedDrawer.js.map +1 -1
  83. package/dist/registry/lib/session-context-provider.d.ts +1 -1
  84. package/dist/registry/lib/session-context-provider.d.ts.map +1 -1
  85. package/dist/registry/lib/session-context-provider.js.map +1 -1
  86. package/dist/registry/lib/session-selectors.d.ts.map +1 -1
  87. package/dist/registry/lib/session-selectors.js +35 -20
  88. package/dist/registry/lib/session-selectors.js.map +1 -1
  89. package/dist/session/comments/CommentRail.d.ts.map +1 -1
  90. package/dist/session/comments/CommentRail.js.map +1 -1
  91. package/dist/session/shell/SessionWorkspaceShell.d.ts.map +1 -1
  92. package/dist/session/shell/SessionWorkspaceShell.js.map +1 -1
  93. package/dist/session-kit/approvals/ApprovalCard.d.ts.map +1 -1
  94. package/dist/session-kit/approvals/ApprovalCard.js +6 -2
  95. package/dist/session-kit/approvals/ApprovalCard.js.map +1 -1
  96. package/dist/session-kit/approvals/ApprovalsCenter.d.ts.map +1 -1
  97. package/dist/session-kit/approvals/ApprovalsCenter.js +6 -3
  98. package/dist/session-kit/approvals/ApprovalsCenter.js.map +1 -1
  99. package/dist/session-kit/combobox/Combobox.d.ts.map +1 -1
  100. package/dist/session-kit/combobox/Combobox.js +20 -6
  101. package/dist/session-kit/combobox/Combobox.js.map +1 -1
  102. package/dist/session-kit/display/MD.d.ts.map +1 -1
  103. package/dist/session-kit/display/MD.js +7 -4
  104. package/dist/session-kit/display/MD.js.map +1 -1
  105. package/dist/ui/branding/app-branding-provider.d.ts.map +1 -1
  106. package/dist/ui/branding/app-branding-provider.js.map +1 -1
  107. package/dist/ui/branding/sanitize.d.ts.map +1 -1
  108. package/dist/ui/branding/sanitize.js +12 -6
  109. package/dist/ui/branding/sanitize.js.map +1 -1
  110. package/dist/ui/capture/document-preview.d.ts +1 -1
  111. package/dist/ui/capture/document-preview.d.ts.map +1 -1
  112. package/dist/ui/capture/document-preview.js +8 -6
  113. package/dist/ui/capture/document-preview.js.map +1 -1
  114. package/dist/ui/capture/field-spec-to-zod.d.ts.map +1 -1
  115. package/dist/ui/capture/field-spec-to-zod.js +45 -16
  116. package/dist/ui/capture/field-spec-to-zod.js.map +1 -1
  117. package/dist/ui/capture/field-spec-validate.d.ts.map +1 -1
  118. package/dist/ui/capture/field-spec-validate.js +79 -38
  119. package/dist/ui/capture/field-spec-validate.js.map +1 -1
  120. package/dist/ui/capture/field-spec.d.ts.map +1 -1
  121. package/dist/ui/capture/field-spec.js +17 -6
  122. package/dist/ui/capture/field-spec.js.map +1 -1
  123. package/dist/ui/capture/schema-field-input.d.ts.map +1 -1
  124. package/dist/ui/capture/schema-field-input.js +23 -18
  125. package/dist/ui/capture/schema-field-input.js.map +1 -1
  126. package/dist/ui/capture/schema-form.d.ts +1 -1
  127. package/dist/ui/capture/schema-form.d.ts.map +1 -1
  128. package/dist/ui/capture/schema-form.js +7 -6
  129. package/dist/ui/capture/schema-form.js.map +1 -1
  130. package/dist/ui/capture/use-prefill.d.ts.map +1 -1
  131. package/dist/ui/capture/use-prefill.js +32 -14
  132. package/dist/ui/capture/use-prefill.js.map +1 -1
  133. package/dist/ui/capture/use-schema-validation.d.ts +1 -1
  134. package/dist/ui/capture/use-schema-validation.d.ts.map +1 -1
  135. package/dist/ui/capture/use-schema-validation.js +13 -8
  136. package/dist/ui/capture/use-schema-validation.js.map +1 -1
  137. package/dist/ui/capture/use-wizard.d.ts.map +1 -1
  138. package/dist/ui/capture/use-wizard.js +23 -8
  139. package/dist/ui/capture/use-wizard.js.map +1 -1
  140. package/dist/ui/capture/value-format.d.ts.map +1 -1
  141. package/dist/ui/capture/value-format.js +6 -3
  142. package/dist/ui/capture/value-format.js.map +1 -1
  143. package/dist/ui/capture/wizard.d.ts.map +1 -1
  144. package/dist/ui/capture/wizard.js +3 -3
  145. package/dist/ui/capture/wizard.js.map +1 -1
  146. package/dist/ui/charts/bar-chart.d.ts +1 -1
  147. package/dist/ui/charts/bar-chart.d.ts.map +1 -1
  148. package/dist/ui/charts/bar-chart.js +1 -1
  149. package/dist/ui/charts/bar-chart.js.map +1 -1
  150. package/dist/ui/charts/chart-frame.d.ts +2 -2
  151. package/dist/ui/charts/chart-frame.d.ts.map +1 -1
  152. package/dist/ui/charts/chart-frame.js +4 -3
  153. package/dist/ui/charts/chart-frame.js.map +1 -1
  154. package/dist/ui/charts/chart-utils.d.ts.map +1 -1
  155. package/dist/ui/charts/chart-utils.js +32 -14
  156. package/dist/ui/charts/chart-utils.js.map +1 -1
  157. package/dist/ui/charts/donut-chart.d.ts +1 -1
  158. package/dist/ui/charts/donut-chart.d.ts.map +1 -1
  159. package/dist/ui/charts/donut-chart.js +7 -3
  160. package/dist/ui/charts/donut-chart.js.map +1 -1
  161. package/dist/ui/charts/line-chart.d.ts +1 -1
  162. package/dist/ui/charts/line-chart.d.ts.map +1 -1
  163. package/dist/ui/charts/line-chart.js +7 -4
  164. package/dist/ui/charts/line-chart.js.map +1 -1
  165. package/dist/ui/charts/sparkline.d.ts +1 -1
  166. package/dist/ui/charts/sparkline.d.ts.map +1 -1
  167. package/dist/ui/charts/sparkline.js +10 -6
  168. package/dist/ui/charts/sparkline.js.map +1 -1
  169. package/dist/ui/chrome/AsyncBoundary.d.ts +2 -2
  170. package/dist/ui/chrome/AsyncBoundary.d.ts.map +1 -1
  171. package/dist/ui/chrome/AsyncBoundary.js +3 -3
  172. package/dist/ui/chrome/AsyncBoundary.js.map +1 -1
  173. package/dist/ui/chrome/EmptyState.d.ts +19 -8
  174. package/dist/ui/chrome/EmptyState.d.ts.map +1 -1
  175. package/dist/ui/chrome/EmptyState.js +6 -4
  176. package/dist/ui/chrome/EmptyState.js.map +1 -1
  177. package/dist/ui/chrome/RecordCardList.d.ts +19 -0
  178. package/dist/ui/chrome/RecordCardList.d.ts.map +1 -0
  179. package/dist/ui/chrome/RecordCardList.js +12 -0
  180. package/dist/ui/chrome/RecordCardList.js.map +1 -0
  181. package/dist/ui/chrome/page-shell.d.ts +1 -1
  182. package/dist/ui/chrome/page-shell.d.ts.map +1 -1
  183. package/dist/ui/chrome/page-shell.js +16 -5
  184. package/dist/ui/chrome/page-shell.js.map +1 -1
  185. package/dist/ui/chrome/status-badge.d.ts.map +1 -1
  186. package/dist/ui/chrome/status-badge.js +1 -1
  187. package/dist/ui/chrome/status-badge.js.map +1 -1
  188. package/dist/ui/design-tokens.d.ts +4 -6
  189. package/dist/ui/design-tokens.d.ts.map +1 -1
  190. package/dist/ui/design-tokens.js +17 -11
  191. package/dist/ui/design-tokens.js.map +1 -1
  192. package/dist/ui/index.d.ts +1 -0
  193. package/dist/ui/index.d.ts.map +1 -1
  194. package/dist/ui/index.js +1 -0
  195. package/dist/ui/index.js.map +1 -1
  196. package/dist/ui/primitives/async-combobox.d.ts.map +1 -1
  197. package/dist/ui/primitives/async-combobox.js +4 -2
  198. package/dist/ui/primitives/async-combobox.js.map +1 -1
  199. package/dist/ui/primitives/badge.d.ts +2 -2
  200. package/dist/ui/primitives/badge.d.ts.map +1 -1
  201. package/dist/ui/primitives/badge.js +3 -3
  202. package/dist/ui/primitives/badge.js.map +1 -1
  203. package/dist/ui/primitives/button.d.ts +2 -2
  204. package/dist/ui/primitives/button.d.ts.map +1 -1
  205. package/dist/ui/primitives/button.js +5 -5
  206. package/dist/ui/primitives/button.js.map +1 -1
  207. package/dist/ui/primitives/overflow-tabs.d.ts.map +1 -1
  208. package/dist/ui/primitives/overflow-tabs.js +19 -12
  209. package/dist/ui/primitives/overflow-tabs.js.map +1 -1
  210. package/dist/ui/primitives/preview-device-frame.js +8 -4
  211. package/dist/ui/primitives/preview-device-frame.js.map +1 -1
  212. package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -1
  213. package/dist/ui/primitives/tag-multi-select.js +10 -5
  214. package/dist/ui/primitives/tag-multi-select.js.map +1 -1
  215. package/dist/ui/primitives/url-preview-chrome.d.ts.map +1 -1
  216. package/dist/ui/primitives/url-preview-chrome.js.map +1 -1
  217. package/dist/vista/GenericCatalogueSurface.d.ts.map +1 -1
  218. package/dist/vista/GenericCatalogueSurface.js +5 -3
  219. package/dist/vista/GenericCatalogueSurface.js.map +1 -1
  220. package/dist/vista/VistaPanel.d.ts.map +1 -1
  221. package/dist/vista/VistaPanel.js +8 -4
  222. package/dist/vista/VistaPanel.js.map +1 -1
  223. package/dist/vista/html/CustomUiFrame.d.ts.map +1 -1
  224. package/dist/vista/html/CustomUiFrame.js +2 -1
  225. package/dist/vista/html/CustomUiFrame.js.map +1 -1
  226. package/dist/vista/html/custom-ui-frame-bridge.d.ts.map +1 -1
  227. package/dist/vista/html/custom-ui-frame-bridge.js +6 -3
  228. package/dist/vista/html/custom-ui-frame-bridge.js.map +1 -1
  229. package/dist/vista/html/custom-ui-intents.d.ts.map +1 -1
  230. package/dist/vista/html/custom-ui-intents.js +13 -15
  231. package/dist/vista/html/custom-ui-intents.js.map +1 -1
  232. package/dist/vista/surface-catalogue.d.ts.map +1 -1
  233. package/dist/vista/surface-catalogue.js +6 -3
  234. package/dist/vista/surface-catalogue.js.map +1 -1
  235. package/dist/vista/surfaces/FormSurface.d.ts.map +1 -1
  236. package/dist/vista/surfaces/FormSurface.js +20 -7
  237. package/dist/vista/surfaces/FormSurface.js.map +1 -1
  238. package/dist/vista/surfaces/TableSurface.d.ts.map +1 -1
  239. package/dist/vista/surfaces/TableSurface.js +2 -1
  240. package/dist/vista/surfaces/TableSurface.js.map +1 -1
  241. package/dist/vista/surfaces/blob-surfaces.d.ts.map +1 -1
  242. package/dist/vista/surfaces/blob-surfaces.js +7 -2
  243. package/dist/vista/surfaces/blob-surfaces.js.map +1 -1
  244. package/dist/vista/surfaces/elicitation-transport.d.ts.map +1 -1
  245. package/dist/vista/surfaces/elicitation-transport.js +14 -7
  246. package/dist/vista/surfaces/elicitation-transport.js.map +1 -1
  247. package/dist/vista/surfaces/file-surface-utils.d.ts.map +1 -1
  248. package/dist/vista/surfaces/file-surface-utils.js +8 -4
  249. package/dist/vista/surfaces/file-surface-utils.js.map +1 -1
  250. package/dist/vista/surfaces/register-surfaces.js +2 -1
  251. package/dist/vista/surfaces/register-surfaces.js.map +1 -1
  252. package/dist/vista/surfaces/text-surfaces.d.ts +1 -1
  253. package/dist/vista/surfaces/text-surfaces.d.ts.map +1 -1
  254. package/dist/vista/surfaces/text-surfaces.js +13 -7
  255. package/dist/vista/surfaces/text-surfaces.js.map +1 -1
  256. package/dist/vista/use-vista-tabs.d.ts.map +1 -1
  257. package/dist/vista/use-vista-tabs.js +8 -4
  258. package/dist/vista/use-vista-tabs.js.map +1 -1
  259. package/dist/vista/vista-code-renderer.d.ts.map +1 -1
  260. package/dist/vista/vista-code-renderer.js.map +1 -1
  261. package/dist/vista/vista-file-source.d.ts.map +1 -1
  262. package/dist/vista/vista-file-source.js +9 -5
  263. package/dist/vista/vista-file-source.js.map +1 -1
  264. package/dist/vista/vista-form-host.d.ts.map +1 -1
  265. package/dist/vista/vista-form-host.js +1 -1
  266. package/dist/vista/vista-form-host.js.map +1 -1
  267. package/dist/vista/vista-model.d.ts.map +1 -1
  268. package/dist/vista/vista-model.js +5 -3
  269. package/dist/vista/vista-model.js.map +1 -1
  270. package/dist/vista/vista-open-context.d.ts.map +1 -1
  271. package/dist/vista/vista-open-context.js +1 -1
  272. package/dist/vista/vista-open-context.js.map +1 -1
  273. package/dist/vista/vista-payloads.d.ts.map +1 -1
  274. package/dist/vista/vista-payloads.js +127 -63
  275. package/dist/vista/vista-payloads.js.map +1 -1
  276. package/dist/vista/vista-tabs-manifest.d.ts.map +1 -1
  277. package/dist/vista/vista-tabs-manifest.js +18 -9
  278. package/dist/vista/vista-tabs-manifest.js.map +1 -1
  279. package/dist/vista/vista-tools.d.ts.map +1 -1
  280. package/dist/vista/vista-tools.js +12 -6
  281. package/dist/vista/vista-tools.js.map +1 -1
  282. package/package.json +7 -4
  283. package/src/lib/biome-host/agent-validation.ts +37 -13
  284. package/src/lib/biome-host/biome-registry.ts +48 -13
  285. package/src/lib/biome-host/biome-scope-hooks.ts +3 -1
  286. package/src/lib/biome-host/biome-scoped-query.ts +7 -6
  287. package/src/lib/biome-host/frontend-biome.ts +1 -1
  288. package/src/lib/biome-host/host-bridge.ts +5 -4
  289. package/src/lib/biome-host/i18n-hooks.ts +1 -0
  290. package/src/lib/biome-host/realtime-hooks.ts +1 -0
  291. package/src/lib/biome-host/session-contributions.ts +1 -1
  292. package/src/lib/biome-host/use-mutation-with-error-toast.ts +4 -2
  293. package/src/lib/biome-host/use-page-state.ts +27 -10
  294. package/src/lib/capabilities/capability-provider.tsx +12 -5
  295. package/src/lib/capabilities/use-capability.ts +8 -4
  296. package/src/lib/contribution-registry/contribution-registry.ts +3 -1
  297. package/src/lib/system-bus/deeplink.ts +14 -4
  298. package/src/lib/system-bus/host-ports.ts +3 -7
  299. package/src/lib/system-bus/intent-registry.ts +12 -8
  300. package/src/lib/system-bus/intents.ts +6 -2
  301. package/src/lib/system-bus/palette.ts +2 -1
  302. package/src/lib/system-bus/system-bus-builder.ts +4 -2
  303. package/src/lib/system-bus/windows.ts +1 -1
  304. package/src/offline/outbox.test.ts +12 -3
  305. package/src/offline/outbox.ts +5 -1
  306. package/src/offline/react-hooks.ts +5 -2
  307. package/src/org-db/components/DbResultTable.tsx +48 -15
  308. package/src/registry/lib/agent-validation-host.ts +1 -2
  309. package/src/registry/lib/biome-slot.tsx +7 -4
  310. package/src/registry/lib/primitives/SessionEventCard.tsx +29 -5
  311. package/src/registry/lib/primitives/SessionEventTimeline.tsx +9 -3
  312. package/src/registry/lib/primitives/SessionTabbedDrawer.tsx +15 -5
  313. package/src/registry/lib/session-context-provider.tsx +1 -1
  314. package/src/registry/lib/session-selectors.ts +66 -27
  315. package/src/session/comments/CommentRail.tsx +2 -2
  316. package/src/session/shell/SessionWorkspaceShell.tsx +2 -2
  317. package/src/session-kit/approvals/ApprovalCard.tsx +7 -2
  318. package/src/session-kit/approvals/ApprovalsCenter.tsx +13 -4
  319. package/src/session-kit/combobox/Combobox.tsx +42 -11
  320. package/src/session-kit/display/MD.tsx +14 -5
  321. package/src/ui/branding/app-branding-provider.tsx +2 -3
  322. package/src/ui/branding/sanitize.ts +18 -6
  323. package/src/ui/capture/document-preview.tsx +84 -19
  324. package/src/ui/capture/field-spec-to-zod.ts +62 -14
  325. package/src/ui/capture/field-spec-validate.ts +132 -43
  326. package/src/ui/capture/field-spec.test.ts +2 -2
  327. package/src/ui/capture/field-spec.ts +41 -10
  328. package/src/ui/capture/schema-field-input.tsx +218 -38
  329. package/src/ui/capture/schema-form.tsx +54 -13
  330. package/src/ui/capture/use-prefill.ts +50 -17
  331. package/src/ui/capture/use-schema-validation.ts +45 -13
  332. package/src/ui/capture/use-wizard.ts +57 -11
  333. package/src/ui/capture/value-format.ts +14 -4
  334. package/src/ui/capture/wizard.tsx +9 -5
  335. package/src/ui/charts/bar-chart.tsx +8 -4
  336. package/src/ui/charts/chart-frame.tsx +18 -5
  337. package/src/ui/charts/chart-utils.ts +47 -15
  338. package/src/ui/charts/donut-chart.tsx +24 -6
  339. package/src/ui/charts/line-chart.tsx +34 -12
  340. package/src/ui/charts/sparkline.tsx +17 -9
  341. package/src/ui/chrome/AsyncBoundary.tsx +14 -6
  342. package/src/ui/chrome/EmptyState.tsx +116 -43
  343. package/src/ui/chrome/RecordCardList.tsx +98 -0
  344. package/src/ui/chrome/page-shell.spec.tsx +117 -0
  345. package/src/ui/chrome/page-shell.tsx +81 -25
  346. package/src/ui/chrome/status-badge.tsx +8 -4
  347. package/src/ui/design-tokens.ts +39 -13
  348. package/src/ui/index.ts +5 -0
  349. package/src/ui/primitives/async-combobox.tsx +7 -3
  350. package/src/ui/primitives/badge.tsx +14 -5
  351. package/src/ui/primitives/button.tsx +13 -6
  352. package/src/ui/primitives/overflow-tabs.tsx +46 -16
  353. package/src/ui/primitives/preview-device-frame.tsx +12 -4
  354. package/src/ui/primitives/tag-multi-select.tsx +30 -9
  355. package/src/ui/primitives/url-preview-chrome.tsx +1 -0
  356. package/src/vista/GenericCatalogueSurface.tsx +7 -5
  357. package/src/vista/VistaPanel.tsx +12 -4
  358. package/src/vista/html/CustomUiFrame.spec.tsx +3 -1
  359. package/src/vista/html/CustomUiFrame.tsx +3 -1
  360. package/src/vista/html/custom-ui-frame-bridge.ts +9 -3
  361. package/src/vista/html/custom-ui-intents.spec.ts +3 -1
  362. package/src/vista/html/custom-ui-intents.ts +19 -15
  363. package/src/vista/surface-catalogue.ts +9 -3
  364. package/src/vista/surfaces/FormSurface.tsx +27 -10
  365. package/src/vista/surfaces/TableSurface.tsx +4 -3
  366. package/src/vista/surfaces/blob-surfaces.tsx +13 -3
  367. package/src/vista/surfaces/elicitation-transport.ts +21 -7
  368. package/src/vista/surfaces/file-surface-utils.ts +11 -4
  369. package/src/vista/surfaces/register-surfaces.tsx +3 -1
  370. package/src/vista/surfaces/text-surfaces.tsx +21 -8
  371. package/src/vista/use-vista-tabs.ts +12 -4
  372. package/src/vista/vista-code-renderer.tsx +6 -5
  373. package/src/vista/vista-file-source.tsx +15 -14
  374. package/src/vista/vista-form-host.tsx +3 -8
  375. package/src/vista/vista-model.ts +10 -3
  376. package/src/vista/vista-open-context.tsx +3 -8
  377. package/src/vista/vista-payloads.ts +186 -63
  378. package/src/vista/vista-tabs-manifest.ts +28 -13
  379. package/src/vista/vista-tools.ts +18 -6
@@ -1,8 +1,8 @@
1
1
  import { FileQuestion, type LucideIcon } from 'lucide-react';
2
2
  import { type ReactNode } from 'react';
3
3
 
4
- import { useHostBridge, type PageBackTarget } from '../../lib/biome-host';
5
4
  import EmptyState from './EmptyState';
5
+ import { useHostBridge, type PageBackTarget } from '../../lib/biome-host';
6
6
  import { Tabs, TabsList, TabsTrigger } from '../primitives/tabs';
7
7
 
8
8
  interface TabDef {
@@ -44,38 +44,52 @@ interface PageShellProps {
44
44
  * page-meta store) and renders an optional tab strip + action row above the
45
45
  * scrollable content canvas. Host-framework-agnostic: reaches the page-meta
46
46
  * store through the kernel `HostBridge` singleton, never a host import.
47
+ *
48
+ * ── WHY THIS DISPATCHES INSTEAD OF BRANCHING ──────────────────────────────
49
+ *
50
+ * `embedded` means "the host page already owns the topbar — render only my
51
+ * body". But `usePageMeta` used to be called UNCONDITIONALLY, above the
52
+ * `embedded` check, and the page-meta store is a STACK ("nested pages can push
53
+ * their own meta; on unmount the outer meta is restored"). So an embedded shell
54
+ * pushed its own title, description, eyebrow, backTo and topbarActions OVER its
55
+ * host's — the precise opposite of what embedding means. The host's back button
56
+ * and title silently became the child's for as long as the child was mounted.
57
+ *
58
+ * A hook cannot be called conditionally, so the fix is structural: this
59
+ * component calls NO hooks and only chooses. The embedded variant never reaches
60
+ * the page-meta hook because it is a different component, not a later branch.
47
61
  */
48
- export default function PageShell({
49
- icon: _Icon,
50
- title,
51
- description,
62
+ export default function PageShell(props: Readonly<PageShellProps>) {
63
+ return props.embedded ? (
64
+ <EmbeddedPageShell {...props} />
65
+ ) : (
66
+ <FullPageShell {...props} />
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Embedded body. Registers NO page meta — the host page owns the topbar.
72
+ * Renders only the child content plus an optional sub-tab strip + actions.
73
+ */
74
+ function EmbeddedPageShell({
52
75
  children,
53
76
  actions,
54
77
  tabs,
55
78
  activeTab,
56
79
  onTabChange,
57
- maxWidth,
58
- embedded,
59
- eyebrow,
60
- backTo,
61
- topbarActions,
62
80
  }: Readonly<PageShellProps>) {
63
- // Single source of truth for the topbar title. Pages that bypass
64
- // PageShell call the page-meta hook directly; PageShell wraps the same
65
- // hook so consumers don't need to register meta twice.
66
- useHostBridge().pageMeta.usePageMeta({ title, description, eyebrow, backTo, topbarActions });
67
- if (embedded) {
68
- // Embedded mode: the host page already provides the title + tabs +
69
- // context, so we render ONLY the child content + any sub-tab strip +
70
- // actions inline. This eliminates the legacy duplicate icon-and-title
71
- // row that previously stole vertical space.
81
+ {
72
82
  return (
73
83
  <div className="flex h-full min-h-0 flex-col bg-paper">
74
84
  {(tabs && tabs.length > 0) || actions ? (
75
85
  <div className="shrink-0 border-b border-rule bg-paper-elev/40">
76
86
  <div className="flex items-center gap-3 px-4 py-2 lg:px-6">
77
87
  {tabs && tabs.length > 0 && (
78
- <Tabs value={activeTab} onValueChange={onTabChange} className="flex-1 min-w-0">
88
+ <Tabs
89
+ value={activeTab}
90
+ onValueChange={onTabChange}
91
+ className="flex-1 min-w-0"
92
+ >
79
93
  <div className="overflow-x-auto">
80
94
  <TabsList className="h-7 bg-transparent p-0 gap-0">
81
95
  {tabs.map((tab) => (
@@ -86,7 +100,9 @@ export default function PageShell({
86
100
  >
87
101
  {tab.label}
88
102
  {tab.count !== undefined && (
89
- <span className="ml-1.5 text-caption tabular-nums opacity-70">{tab.count}</span>
103
+ <span className="ml-1.5 text-caption tabular-nums opacity-70">
104
+ {tab.count}
105
+ </span>
90
106
  )}
91
107
  {tab.indicator && (
92
108
  <span className="ml-1.5 inline-block h-1.5 w-1.5 rounded-full bg-warning" />
@@ -98,16 +114,50 @@ export default function PageShell({
98
114
  </Tabs>
99
115
  )}
100
116
  {!tabs && <div className="flex-1" />}
101
- {actions && <div className="flex items-center gap-2 shrink-0">{actions}</div>}
117
+ {actions && (
118
+ <div className="flex items-center gap-2 shrink-0">
119
+ {actions}
120
+ </div>
121
+ )}
102
122
  </div>
103
123
  </div>
104
124
  ) : null}
105
125
  <div className="flex-1 overflow-auto min-h-0">
106
- <div className="flex h-full min-h-0 flex-col px-4 pb-4 pt-3 lg:px-6">{children}</div>
126
+ <div className="flex h-full min-h-0 flex-col px-4 pb-4 pt-3 lg:px-6">
127
+ {children}
128
+ </div>
107
129
  </div>
108
130
  </div>
109
131
  );
110
132
  }
133
+ }
134
+
135
+ /**
136
+ * Full page. Owns the topbar meta for the route it renders.
137
+ */
138
+ function FullPageShell({
139
+ title,
140
+ description,
141
+ children,
142
+ actions,
143
+ tabs,
144
+ activeTab,
145
+ onTabChange,
146
+ maxWidth,
147
+ eyebrow,
148
+ backTo,
149
+ topbarActions,
150
+ }: Readonly<PageShellProps>) {
151
+ // Single source of truth for the topbar title. Pages that bypass
152
+ // PageShell call the page-meta hook directly; PageShell wraps the same
153
+ // hook so consumers don't need to register meta twice.
154
+ useHostBridge().pageMeta.usePageMeta({
155
+ title,
156
+ description,
157
+ eyebrow,
158
+ backTo,
159
+ topbarActions,
160
+ });
111
161
 
112
162
  return (
113
163
  <div className={`flex h-full min-h-0 flex-col ${maxWidth ?? ''}`}>
@@ -116,7 +166,11 @@ export default function PageShell({
116
166
  <div className="shrink-0 border-b border-border/50 px-4 sm:px-5 lg:px-8">
117
167
  <div className="flex min-h-[34px] flex-col gap-2 py-1.5 sm:flex-row sm:items-center sm:gap-3 sm:py-0">
118
168
  {tabs && tabs.length > 0 && (
119
- <Tabs value={activeTab} onValueChange={onTabChange} className="flex-1 min-w-0">
169
+ <Tabs
170
+ value={activeTab}
171
+ onValueChange={onTabChange}
172
+ className="flex-1 min-w-0"
173
+ >
120
174
  <div className="overflow-x-auto">
121
175
  <TabsList className="h-7 bg-transparent p-0 gap-0">
122
176
  {tabs.map((tab) => (
@@ -127,7 +181,9 @@ export default function PageShell({
127
181
  >
128
182
  {tab.label}
129
183
  {tab.count !== undefined && (
130
- <span className="ml-1.5 text-caption tabular-nums opacity-60">{tab.count}</span>
184
+ <span className="ml-1.5 text-caption tabular-nums opacity-60">
185
+ {tab.count}
186
+ </span>
131
187
  )}
132
188
  {tab.indicator && (
133
189
  <span className="ml-1.5 inline-block h-1.5 w-1.5 rounded-full bg-warning" />
@@ -1,9 +1,9 @@
1
- import type { ReactNode } from 'react';
2
-
3
1
  import { cn } from '../cn';
4
2
  import { getStatusStyle } from '../design-tokens';
5
3
  import { Badge } from '../primitives/badge';
6
4
 
5
+ import type { ReactNode } from 'react';
6
+
7
7
  export type StatusBadgeSize = 'xs' | 'sm' | 'md';
8
8
 
9
9
  export interface StatusBadgeProps {
@@ -68,8 +68,12 @@ export default function StatusBadge({
68
68
  className,
69
69
  )}
70
70
  >
71
- {dot && <span className={cn('shrink-0 rounded-full', sz.dot, style.dot)} />}
72
- {children ?? <span className="capitalize">{status.replace(/_/g, ' ')}</span>}
71
+ {dot && (
72
+ <span className={cn('shrink-0 rounded-full', sz.dot, style.dot)} />
73
+ )}
74
+ {children ?? (
75
+ <span className="capitalize">{status.replace(/_/g, ' ')}</span>
76
+ )}
73
77
  </Badge>
74
78
  );
75
79
  }
@@ -142,8 +142,16 @@ export const STATUS_STYLES: Record<string, StatusStyle> = {
142
142
  manual_review: WARNING_STYLE,
143
143
  };
144
144
 
145
+ /**
146
+ * Style for an unrecognised status. Same value `STATUS_STYLES.pending` carries —
147
+ * referenced as the fragment rather than re-read out of the open `Record`, so
148
+ * the fallback is structurally guaranteed instead of being a second lookup that
149
+ * can itself miss (e.g. if the `pending` key were ever renamed).
150
+ */
151
+ const DEFAULT_STATUS_STYLE: StatusStyle = INFO_STYLE;
152
+
145
153
  export function getStatusStyle(status: string): StatusStyle {
146
- return STATUS_STYLES[status] ?? STATUS_STYLES.pending;
154
+ return STATUS_STYLES[status] ?? DEFAULT_STATUS_STYLE;
147
155
  }
148
156
 
149
157
  // ── Priority Styles ────────────────────────────────────────────────
@@ -156,6 +164,17 @@ export interface PriorityStyle {
156
164
  label: string;
157
165
  }
158
166
 
167
+ // Lowest priority — also the style for an unrecognised priority. Named so the
168
+ // map entry and `getPriorityStyle`'s fallback are the same proven value rather
169
+ // than a second `Record` lookup that can itself miss.
170
+ const PRIORITY_LOW_STYLE: PriorityStyle = {
171
+ badge: 'bg-paper-elev text-ink-3 border-border',
172
+ text: 'text-ink-3',
173
+ bg: 'bg-muted/30',
174
+ border: 'border-border',
175
+ label: 'Low',
176
+ };
177
+
159
178
  export const PRIORITY_STYLES: Record<string, PriorityStyle> = {
160
179
  p0: {
161
180
  badge: 'bg-destructive text-white border-destructive',
@@ -178,17 +197,11 @@ export const PRIORITY_STYLES: Record<string, PriorityStyle> = {
178
197
  border: 'border-warning/30',
179
198
  label: 'Medium',
180
199
  },
181
- p3: {
182
- badge: 'bg-paper-elev text-ink-3 border-border',
183
- text: 'text-ink-3',
184
- bg: 'bg-muted/30',
185
- border: 'border-border',
186
- label: 'Low',
187
- },
200
+ p3: PRIORITY_LOW_STYLE,
188
201
  };
189
202
 
190
203
  export function getPriorityStyle(priority: string): PriorityStyle {
191
- return PRIORITY_STYLES[priority] ?? PRIORITY_STYLES.p3;
204
+ return PRIORITY_STYLES[priority] ?? PRIORITY_LOW_STYLE;
192
205
  }
193
206
 
194
207
  // ── Severity Styles ────────────────────────────────────────────────
@@ -285,15 +298,28 @@ export const IMPORTANCE_STYLES: Record<number, { badge: string; label: string; s
285
298
 
286
299
  // ── Risk Level Styles ──────────────────────────────────────────────
287
300
 
288
- export const RISK_LEVEL_STYLES: Record<string, { badge: string; text: string }> = {
289
- low: { badge: 'bg-success/15 text-success dark:text-success border-success/30', text: 'text-success dark:text-success' },
301
+ export interface RiskLevelStyle {
302
+ badge: string;
303
+ text: string;
304
+ }
305
+
306
+ // Lowest risk — also the style for an unrecognised level. Named for the same
307
+ // reason as PRIORITY_LOW_STYLE: the fallback must be a proven value, not a
308
+ // second lookup into the open `Record`.
309
+ const RISK_LOW_STYLE: RiskLevelStyle = {
310
+ badge: 'bg-success/15 text-success dark:text-success border-success/30',
311
+ text: 'text-success dark:text-success',
312
+ };
313
+
314
+ export const RISK_LEVEL_STYLES: Record<string, RiskLevelStyle> = {
315
+ low: RISK_LOW_STYLE,
290
316
  medium: { badge: 'bg-warning/15 text-warning dark:text-warning border-warning/30', text: 'text-warning dark:text-warning' },
291
317
  high: { badge: 'bg-warning/15 text-warning dark:text-warning border-warning/30', text: 'text-warning dark:text-warning' },
292
318
  critical: { badge: 'bg-destructive/15 text-destructive dark:text-destructive border-destructive/30', text: 'text-destructive dark:text-destructive' },
293
319
  };
294
320
 
295
- export function getRiskLevelStyle(level: string): { badge: string; text: string } {
296
- return RISK_LEVEL_STYLES[level] ?? RISK_LEVEL_STYLES.low;
321
+ export function getRiskLevelStyle(level: string): RiskLevelStyle {
322
+ return RISK_LEVEL_STYLES[level] ?? RISK_LOW_STYLE;
297
323
  }
298
324
 
299
325
  // ── Agent Source Styles ────────────────────────────────────────────
package/src/ui/index.ts CHANGED
@@ -92,6 +92,11 @@ export {
92
92
  type EmptyStateLinkComponent,
93
93
  } from './chrome/EmptyState';
94
94
  export { StateCard } from './chrome/StateCard';
95
+ export {
96
+ RecordCardList,
97
+ type RecordCardField,
98
+ type RecordCardListProps,
99
+ } from './chrome/RecordCardList';
95
100
  export { default as PageHeader, PageToolbar } from './chrome/PageHeader';
96
101
  export { default as LoadingState, type LoadingStateVariant } from './chrome/LoadingState';
97
102
  export { default as AsyncBoundary } from './chrome/AsyncBoundary';
@@ -3,10 +3,10 @@ import { ChevronsUpDown, Loader2, Search } from 'lucide-react';
3
3
  import { useEffect, useRef, useState, type ReactNode } from 'react';
4
4
 
5
5
  import { cn } from '../cn';
6
- import { useDebouncedValue } from '../hooks/use-debounced-value';
7
6
  import { Button } from './button';
8
7
  import { Input } from './input';
9
8
  import { Popover, PopoverContent, PopoverTrigger } from './popover';
9
+ import { useDebouncedValue } from '../hooks/use-debounced-value';
10
10
 
11
11
  export interface AsyncComboboxProps<T> {
12
12
  /** Unique key prefix for query caching to avoid collisions between combobox instances. */
@@ -99,7 +99,9 @@ export function AsyncCombobox<T>({
99
99
  };
100
100
 
101
101
  const showTypeToSearchHint =
102
- !isLoading && minSearchLength > 0 && debouncedSearch.length < minSearchLength;
102
+ !isLoading &&
103
+ minSearchLength > 0 &&
104
+ debouncedSearch.length < minSearchLength;
103
105
  const showEmptyMessage = !isLoading && searchEnabled && results.length === 0;
104
106
 
105
107
  return (
@@ -156,7 +158,9 @@ export function AsyncCombobox<T>({
156
158
  )}
157
159
 
158
160
  {showEmptyMessage && (
159
- <p className="text-body-1 text-ink-3 text-center py-4">{emptyMessage}</p>
161
+ <p className="text-body-1 text-ink-3 text-center py-4">
162
+ {emptyMessage}
163
+ </p>
160
164
  )}
161
165
 
162
166
  {!isLoading &&
@@ -1,16 +1,25 @@
1
- import type { VariantProps } from "class-variance-authority";
2
- import * as React from "react";
1
+ import * as React from 'react';
3
2
 
4
3
  import { cn } from '../cn';
5
4
  import { badgeVariants } from './badge-variants';
6
5
 
7
- export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {}
6
+ import type { VariantProps } from 'class-variance-authority';
7
+
8
+ export interface BadgeProps
9
+ extends React.HTMLAttributes<HTMLSpanElement>,
10
+ VariantProps<typeof badgeVariants> {}
8
11
 
9
12
  const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
10
13
  ({ className, variant, ...props }, ref) => {
11
- return <span ref={ref} className={cn(badgeVariants({ variant }), className)} {...props} />;
14
+ return (
15
+ <span
16
+ ref={ref}
17
+ className={cn(badgeVariants({ variant }), className)}
18
+ {...props}
19
+ />
20
+ );
12
21
  },
13
22
  );
14
- Badge.displayName = "Badge";
23
+ Badge.displayName = 'Badge';
15
24
 
16
25
  export { Badge };
@@ -1,10 +1,11 @@
1
- import { Slot } from "@radix-ui/react-slot";
2
- import type { VariantProps } from "class-variance-authority";
3
- import * as React from "react";
1
+ import { Slot } from '@radix-ui/react-slot';
2
+ import * as React from 'react';
4
3
 
5
4
  import { cn } from '../cn';
6
5
  import { buttonVariants } from './button-variants';
7
6
 
7
+ import type { VariantProps } from 'class-variance-authority';
8
+
8
9
  export interface ButtonProps
9
10
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
10
11
  VariantProps<typeof buttonVariants> {
@@ -13,10 +14,16 @@ export interface ButtonProps
13
14
 
14
15
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
15
16
  ({ className, variant, size, asChild = false, ...props }, ref) => {
16
- const Comp = asChild ? Slot : "button";
17
- return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
17
+ const Comp = asChild ? Slot : 'button';
18
+ return (
19
+ <Comp
20
+ className={cn(buttonVariants({ variant, size, className }))}
21
+ ref={ref}
22
+ {...props}
23
+ />
24
+ );
18
25
  },
19
26
  );
20
- Button.displayName = "Button";
27
+ Button.displayName = 'Button';
21
28
 
22
29
  export { Button };
@@ -83,7 +83,9 @@ export function OverflowTabs({
83
83
  // Measure container width and react to resizes.
84
84
  useEffect(() => {
85
85
  const el = containerRef.current;
86
- if (!el) return;
86
+ if (!el) {
87
+ return;
88
+ }
87
89
  const ro = new ResizeObserver((entries) => {
88
90
  const w = entries[0]?.contentRect.width ?? el.clientWidth;
89
91
  setContainerWidth(w);
@@ -97,7 +99,9 @@ export function OverflowTabs({
97
99
  // when the tab list itself changes (new tabs, label edits).
98
100
  useLayoutEffect(() => {
99
101
  const el = measureRef.current;
100
- if (!el) return;
102
+ if (!el) {
103
+ return;
104
+ }
101
105
  const children = Array.from(el.children) as HTMLElement[];
102
106
  setTabWidths(children.map((c) => c.getBoundingClientRect().width));
103
107
  }, [tabs]);
@@ -118,36 +122,53 @@ export function OverflowTabs({
118
122
  const vis: OverflowTab[] = [];
119
123
  const over: OverflowTab[] = [];
120
124
  let used = 0;
121
- for (let i = 0; i < tabs.length; i++) {
122
- const w = tabWidths[i] + (vis.length > 0 ? gap : 0);
125
+ for (const [i, tab] of tabs.entries()) {
126
+ const natural = tabWidths[i];
127
+ if (natural === undefined) {
128
+ // Unreachable: the length-parity guard above already returned. Kept as a
129
+ // real check because the alternative — treating a missing measurement as
130
+ // 0px — would lay tabs out against a budget they don't actually fit.
131
+ // Fall back to the same all-visible layout the parity guard renders.
132
+ return { visible: tabs, overflow: [] as readonly OverflowTab[] };
133
+ }
134
+ const w = natural + (vis.length > 0 ? gap : 0);
123
135
  // A pinned tab is never collapsed — it stays visible whatever the budget
124
136
  // says, so a panel's permanent first tab can't hide behind "More…".
125
- if (tabs[i].pinned === true || used + w <= budget) {
126
- vis.push(tabs[i]);
137
+ if (tab.pinned === true || used + w <= budget) {
138
+ vis.push(tab);
127
139
  used += w;
128
140
  } else {
129
- over.push(tabs[i]);
141
+ over.push(tab);
130
142
  }
131
143
  }
132
144
  // Force the active tab into the visible set if it ended up in overflow.
133
- const activeIdx = tabs.findIndex((t) => t.value === value);
134
- if (activeIdx !== -1) {
135
- const activeTab = tabs[activeIdx];
145
+ // `find` (not `findIndex` + lookup) so the hit is typed as present.
146
+ const activeTab = tabs.find((t) => t.value === value);
147
+ if (activeTab !== undefined) {
136
148
  const inVisible = vis.some((t) => t.value === activeTab.value);
137
149
  if (!inVisible) {
138
150
  // Swap the last visible for the active one to keep the budget OK.
139
151
  const dropped = vis.pop();
140
- if (dropped) over.unshift(dropped);
152
+ if (dropped) {
153
+ over.unshift(dropped);
154
+ }
141
155
  vis.push(activeTab);
142
156
  const removeIdx = over.findIndex((t) => t.value === activeTab.value);
143
- if (removeIdx !== -1) over.splice(removeIdx, 1);
157
+ if (removeIdx !== -1) {
158
+ over.splice(removeIdx, 1);
159
+ }
144
160
  }
145
161
  }
146
162
  return { visible: vis, overflow: over };
147
163
  }, [tabs, tabWidths, containerWidth, gap, moreTriggerWidth, value]);
148
164
 
149
165
  return (
150
- <div className={cn('relative -mb-px flex items-end border-b border-rule', className)}>
166
+ <div
167
+ className={cn(
168
+ 'relative -mb-px flex items-end border-b border-rule',
169
+ className,
170
+ )}
171
+ >
151
172
  {/* Off-screen measurement layer — renders ALL tabs so widths are real. */}
152
173
  <div
153
174
  ref={measureRef}
@@ -156,7 +177,12 @@ export function OverflowTabs({
156
177
  style={{ visibility: 'hidden' }}
157
178
  >
158
179
  {tabs.map((t) => (
159
- <TabPill key={`measure-${t.value}`} tab={t} active={false} onClick={() => undefined} />
180
+ <TabPill
181
+ key={`measure-${t.value}`}
182
+ tab={t}
183
+ active={false}
184
+ onClick={() => undefined}
185
+ />
160
186
  ))}
161
187
  </div>
162
188
 
@@ -199,11 +225,15 @@ export function OverflowTabs({
199
225
  <DropdownMenuItem
200
226
  key={t.value}
201
227
  onClick={() => onChange(t.value)}
202
- className={cn(t.value === value && 'bg-accent/60 text-foreground')}
228
+ className={cn(
229
+ t.value === value && 'bg-accent/60 text-foreground',
230
+ )}
203
231
  >
204
232
  <span className="flex-1 truncate">{t.label}</span>
205
233
  {t.count !== undefined && (
206
- <span className="ml-2 text-caption tabular-nums text-ink-4">{t.count}</span>
234
+ <span className="ml-2 text-caption tabular-nums text-ink-4">
235
+ {t.count}
236
+ </span>
207
237
  )}
208
238
  {t.indicator && (
209
239
  <span
@@ -96,10 +96,14 @@ function ScaledBezel({ bezel, children }: ScaledBezelProps) {
96
96
 
97
97
  useLayoutEffect(() => {
98
98
  const el = containerRef.current;
99
- if (!el) return;
99
+ if (!el) {
100
+ return;
101
+ }
100
102
  const compute = (): void => {
101
103
  const { clientWidth, clientHeight } = el;
102
- if (clientWidth === 0 || clientHeight === 0) return;
104
+ if (clientWidth === 0 || clientHeight === 0) {
105
+ return;
106
+ }
103
107
  // Leave a tiny margin so the shadow is not clipped.
104
108
  const margin = 24;
105
109
  const availW = Math.max(0, clientWidth - margin);
@@ -163,10 +167,14 @@ function ResponsiveViewport({
163
167
 
164
168
  useLayoutEffect(() => {
165
169
  const el = containerRef.current;
166
- if (!el) return;
170
+ if (!el) {
171
+ return;
172
+ }
167
173
  const compute = (): void => {
168
174
  const { clientWidth, clientHeight } = el;
169
- if (clientWidth === 0 || clientHeight === 0) return;
175
+ if (clientWidth === 0 || clientHeight === 0) {
176
+ return;
177
+ }
170
178
  const margin = 24;
171
179
  const availW = Math.max(0, clientWidth - margin);
172
180
  const availH = Math.max(0, clientHeight - margin);
@@ -30,7 +30,10 @@ export interface TagMultiSelectProps {
30
30
  disabled?: boolean;
31
31
  }
32
32
 
33
- const CHIP_CLASSES: Record<NonNullable<TagMultiSelectProps['chipVariant']>, string> = {
33
+ const CHIP_CLASSES: Record<
34
+ NonNullable<TagMultiSelectProps['chipVariant']>,
35
+ string
36
+ > = {
34
37
  default: 'bg-primary/10 text-primary border-primary/20',
35
38
  secondary: 'bg-secondary text-secondary-foreground border-secondary',
36
39
  success: 'bg-success/10 text-success border-success/20',
@@ -63,7 +66,9 @@ export function TagMultiSelect({
63
66
 
64
67
  const filteredOptions = useMemo(() => {
65
68
  const q = search.trim().toLowerCase();
66
- if (!q) return options;
69
+ if (!q) {
70
+ return options;
71
+ }
67
72
  return options.filter(
68
73
  (o) =>
69
74
  o.label.toLowerCase().includes(q) ||
@@ -93,7 +98,9 @@ export function TagMultiSelect({
93
98
  };
94
99
 
95
100
  const addCustom = () => {
96
- if (!canAddCustom) return;
101
+ if (!canAddCustom) {
102
+ return;
103
+ }
97
104
  onChange([...value, normalizedSearch]);
98
105
  setSearch('');
99
106
  inputRef.current?.focus();
@@ -104,8 +111,13 @@ export function TagMultiSelect({
104
111
  e.preventDefault();
105
112
  if (canAddCustom) {
106
113
  addCustom();
107
- } else if (filteredOptions.length === 1) {
108
- toggle(filteredOptions[0].value);
114
+ } else {
115
+ // Enter with exactly one remaining match selects it.
116
+ const onlyMatch =
117
+ filteredOptions.length === 1 ? filteredOptions[0] : undefined;
118
+ if (onlyMatch !== undefined) {
119
+ toggle(onlyMatch.value);
120
+ }
109
121
  }
110
122
  }
111
123
  };
@@ -137,7 +149,9 @@ export function TagMultiSelect({
137
149
  variant="outline"
138
150
  className={cn('text-body-1 gap-1 pr-1', chipClass)}
139
151
  >
140
- <span className="truncate max-w-[160px]">{opt?.label ?? val}</span>
152
+ <span className="truncate max-w-[160px]">
153
+ {opt?.label ?? val}
154
+ </span>
141
155
  <button
142
156
  type="button"
143
157
  onClick={(e) => remove(val, e)}
@@ -183,7 +197,9 @@ export function TagMultiSelect({
183
197
  <ScrollArea className="max-h-56">
184
198
  <div className="p-1">
185
199
  {filteredOptions.length === 0 && !canAddCustom && (
186
- <p className="text-body-1 text-ink-3 text-center py-3">{emptyMessage}</p>
200
+ <p className="text-body-1 text-ink-3 text-center py-3">
201
+ {emptyMessage}
202
+ </p>
187
203
  )}
188
204
 
189
205
  {filteredOptions.map((opt) => {
@@ -209,7 +225,9 @@ export function TagMultiSelect({
209
225
  {selected && <Check className="h-4 w-4" />}
210
226
  </span>
211
227
  <span className="flex-1 min-w-0">
212
- <span className="font-medium block truncate">{opt.label}</span>
228
+ <span className="font-medium block truncate">
229
+ {opt.label}
230
+ </span>
213
231
  {opt.hint && (
214
232
  <span className="text-body-1 text-ink-3 block truncate">
215
233
  {opt.hint}
@@ -228,7 +246,10 @@ export function TagMultiSelect({
228
246
  >
229
247
  <Plus className="h-4 w-4 text-ink-3" />
230
248
  <span>
231
- Add <span className="font-mono font-medium">{normalizedSearch}</span>
249
+ Add{' '}
250
+ <span className="font-mono font-medium">
251
+ {normalizedSearch}
252
+ </span>
232
253
  </span>
233
254
  </button>
234
255
  )}
@@ -9,6 +9,7 @@
9
9
  * implementation.
10
10
  */
11
11
  import { RotateCw } from 'lucide-react';
12
+
12
13
  import type { ReactElement, ReactNode } from 'react';
13
14
 
14
15
  const TRAFFIC_LIGHTS = ['#e5645a', '#e8b13d', '#5bb35b'] as const;