@miragon/mcp-toolkit-ui 2.7.0 → 2.8.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 (387) hide show
  1. package/README.md +7 -4
  2. package/dist/app/adapt-data-widget.d.ts +1 -22
  3. package/dist/app/adapt-data-widget.d.ts.map +1 -1
  4. package/dist/app/adapt-data-widget.js +1 -35
  5. package/dist/app/adapt-data-widget.js.map +1 -1
  6. package/dist/app/builder/BuilderBanners.js +1 -1
  7. package/dist/app/builder/BuilderBanners.js.map +1 -1
  8. package/dist/app/builder/CanvasRow.d.ts +4 -2
  9. package/dist/app/builder/CanvasRow.d.ts.map +1 -1
  10. package/dist/app/builder/CanvasRow.js +21 -7
  11. package/dist/app/builder/CanvasRow.js.map +1 -1
  12. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
  13. package/dist/app/builder/CatalogueSheet.js +11 -4
  14. package/dist/app/builder/CatalogueSheet.js.map +1 -1
  15. package/dist/app/builder/CellPropsSheet.d.ts +7 -3
  16. package/dist/app/builder/CellPropsSheet.d.ts.map +1 -1
  17. package/dist/app/builder/CellPropsSheet.js +10 -3
  18. package/dist/app/builder/CellPropsSheet.js.map +1 -1
  19. package/dist/app/builder/DiscardDialog.d.ts +18 -0
  20. package/dist/app/builder/DiscardDialog.d.ts.map +1 -0
  21. package/dist/app/builder/DiscardDialog.js +18 -0
  22. package/dist/app/builder/DiscardDialog.js.map +1 -0
  23. package/dist/app/builder/LayoutSurface.d.ts +33 -9
  24. package/dist/app/builder/LayoutSurface.d.ts.map +1 -1
  25. package/dist/app/builder/LayoutSurface.js +74 -23
  26. package/dist/app/builder/LayoutSurface.js.map +1 -1
  27. package/dist/app/builder/PipelineStrip.d.ts +2 -1
  28. package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
  29. package/dist/app/builder/PipelineStrip.js +26 -13
  30. package/dist/app/builder/PipelineStrip.js.map +1 -1
  31. package/dist/app/builder/SaveDialog.d.ts +9 -1
  32. package/dist/app/builder/SaveDialog.d.ts.map +1 -1
  33. package/dist/app/builder/SaveDialog.js +8 -2
  34. package/dist/app/builder/SaveDialog.js.map +1 -1
  35. package/dist/app/builder/Toolbar.d.ts +12 -4
  36. package/dist/app/builder/Toolbar.d.ts.map +1 -1
  37. package/dist/app/builder/Toolbar.js +18 -7
  38. package/dist/app/builder/Toolbar.js.map +1 -1
  39. package/dist/app/builder/Workspace.d.ts +23 -3
  40. package/dist/app/builder/Workspace.d.ts.map +1 -1
  41. package/dist/app/builder/Workspace.js +58 -8
  42. package/dist/app/builder/Workspace.js.map +1 -1
  43. package/dist/app/builder/builder-model.d.ts +143 -1
  44. package/dist/app/builder/builder-model.d.ts.map +1 -1
  45. package/dist/app/builder/builder-model.js +191 -1
  46. package/dist/app/builder/builder-model.js.map +1 -1
  47. package/dist/app/builder/reveal.d.ts +10 -0
  48. package/dist/app/builder/reveal.d.ts.map +1 -0
  49. package/dist/app/builder/reveal.js +10 -0
  50. package/dist/app/builder/reveal.js.map +1 -0
  51. package/dist/app/builder/use-builder-catalogue.d.ts +4 -1
  52. package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
  53. package/dist/app/builder/use-builder-catalogue.js +44 -17
  54. package/dist/app/builder/use-builder-catalogue.js.map +1 -1
  55. package/dist/app/builder/use-builder-session.d.ts +30 -0
  56. package/dist/app/builder/use-builder-session.d.ts.map +1 -0
  57. package/dist/app/builder/use-builder-session.js +58 -0
  58. package/dist/app/builder/use-builder-session.js.map +1 -0
  59. package/dist/app/builder/use-display-resolver.d.ts +17 -0
  60. package/dist/app/builder/use-display-resolver.d.ts.map +1 -0
  61. package/dist/app/builder/use-display-resolver.js +16 -0
  62. package/dist/app/builder/use-display-resolver.js.map +1 -0
  63. package/dist/app/index.d.ts +8 -3
  64. package/dist/app/index.d.ts.map +1 -1
  65. package/dist/app/index.js +8 -2
  66. package/dist/app/index.js.map +1 -1
  67. package/dist/app/layout-builder.d.ts +13 -25
  68. package/dist/app/layout-builder.d.ts.map +1 -1
  69. package/dist/app/layout-builder.js +75 -81
  70. package/dist/app/layout-builder.js.map +1 -1
  71. package/dist/app/mcp-app-view-chrome.d.ts +12 -3
  72. package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
  73. package/dist/app/mcp-app-view-chrome.js +21 -5
  74. package/dist/app/mcp-app-view-chrome.js.map +1 -1
  75. package/dist/app/mcp-app-view-states.d.ts +11 -3
  76. package/dist/app/mcp-app-view-states.d.ts.map +1 -1
  77. package/dist/app/mcp-app-view-states.js +29 -8
  78. package/dist/app/mcp-app-view-states.js.map +1 -1
  79. package/dist/app/mcp-app-view.d.ts +33 -1
  80. package/dist/app/mcp-app-view.d.ts.map +1 -1
  81. package/dist/app/mcp-app-view.js +181 -127
  82. package/dist/app/mcp-app-view.js.map +1 -1
  83. package/dist/app/mcp-toolkit-app.d.ts +6 -1
  84. package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
  85. package/dist/app/mcp-toolkit-app.js +8 -1
  86. package/dist/app/mcp-toolkit-app.js.map +1 -1
  87. package/dist/app/mcp-use-host-bridge.d.ts +15 -6
  88. package/dist/app/mcp-use-host-bridge.d.ts.map +1 -1
  89. package/dist/app/mcp-use-host-bridge.js +51 -22
  90. package/dist/app/mcp-use-host-bridge.js.map +1 -1
  91. package/dist/app/model-context.d.ts +71 -0
  92. package/dist/app/model-context.d.ts.map +1 -0
  93. package/dist/app/model-context.js +101 -0
  94. package/dist/app/model-context.js.map +1 -0
  95. package/dist/app/use-build-mode-focus.d.ts +33 -0
  96. package/dist/app/use-build-mode-focus.d.ts.map +1 -0
  97. package/dist/app/use-build-mode-focus.js +59 -0
  98. package/dist/app/use-build-mode-focus.js.map +1 -0
  99. package/dist/app/use-view-actions.d.ts +31 -0
  100. package/dist/app/use-view-actions.d.ts.map +1 -0
  101. package/dist/app/use-view-actions.js +81 -0
  102. package/dist/app/use-view-actions.js.map +1 -0
  103. package/dist/app/use-view-lifecycle.d.ts +4 -0
  104. package/dist/app/use-view-lifecycle.d.ts.map +1 -1
  105. package/dist/app/use-view-lifecycle.js +2 -0
  106. package/dist/app/use-view-lifecycle.js.map +1 -1
  107. package/dist/app/view-lifecycle.d.ts +80 -4
  108. package/dist/app/view-lifecycle.d.ts.map +1 -1
  109. package/dist/app/view-lifecycle.js +90 -3
  110. package/dist/app/view-lifecycle.js.map +1 -1
  111. package/dist/app/widget-fixture.d.ts +10 -3
  112. package/dist/app/widget-fixture.d.ts.map +1 -1
  113. package/dist/app/widget-fixture.js +21 -8
  114. package/dist/app/widget-fixture.js.map +1 -1
  115. package/dist/app/widget-props-sheet.d.ts +3 -1
  116. package/dist/app/widget-props-sheet.d.ts.map +1 -1
  117. package/dist/app/widget-props-sheet.js +22 -10
  118. package/dist/app/widget-props-sheet.js.map +1 -1
  119. package/dist/app/widget-renderer.js +1 -1
  120. package/dist/app/widget-renderer.js.map +1 -1
  121. package/dist/components/DrillButton.d.ts +9 -6
  122. package/dist/components/DrillButton.d.ts.map +1 -1
  123. package/dist/components/DrillButton.js +8 -6
  124. package/dist/components/DrillButton.js.map +1 -1
  125. package/dist/components/EmptyState.d.ts.map +1 -1
  126. package/dist/components/EmptyState.js +4 -2
  127. package/dist/components/EmptyState.js.map +1 -1
  128. package/dist/components/ErrorState.d.ts +5 -2
  129. package/dist/components/ErrorState.d.ts.map +1 -1
  130. package/dist/components/ErrorState.js +25 -8
  131. package/dist/components/ErrorState.js.map +1 -1
  132. package/dist/components/FilterBar.d.ts +15 -4
  133. package/dist/components/FilterBar.d.ts.map +1 -1
  134. package/dist/components/FilterBar.js +11 -6
  135. package/dist/components/FilterBar.js.map +1 -1
  136. package/dist/components/KpiGrid.d.ts +66 -21
  137. package/dist/components/KpiGrid.d.ts.map +1 -1
  138. package/dist/components/KpiGrid.js +101 -34
  139. package/dist/components/KpiGrid.js.map +1 -1
  140. package/dist/components/ListFooter.d.ts +31 -4
  141. package/dist/components/ListFooter.d.ts.map +1 -1
  142. package/dist/components/ListFooter.js +30 -11
  143. package/dist/components/ListFooter.js.map +1 -1
  144. package/dist/components/PagedListFooter.d.ts +57 -0
  145. package/dist/components/PagedListFooter.d.ts.map +1 -0
  146. package/dist/components/PagedListFooter.js +27 -0
  147. package/dist/components/PagedListFooter.js.map +1 -0
  148. package/dist/components/Pills.d.ts +27 -7
  149. package/dist/components/Pills.d.ts.map +1 -1
  150. package/dist/components/Pills.js +21 -6
  151. package/dist/components/Pills.js.map +1 -1
  152. package/dist/components/SectionHeading.d.ts +3 -1
  153. package/dist/components/SectionHeading.d.ts.map +1 -1
  154. package/dist/components/SectionHeading.js +6 -2
  155. package/dist/components/SectionHeading.js.map +1 -1
  156. package/dist/components/Toast.d.ts +9 -3
  157. package/dist/components/Toast.d.ts.map +1 -1
  158. package/dist/components/Toast.js +31 -16
  159. package/dist/components/Toast.js.map +1 -1
  160. package/dist/components/WidgetHeader.d.ts +34 -10
  161. package/dist/components/WidgetHeader.d.ts.map +1 -1
  162. package/dist/components/WidgetHeader.js +11 -6
  163. package/dist/components/WidgetHeader.js.map +1 -1
  164. package/dist/hooks/index.d.ts +4 -4
  165. package/dist/hooks/index.d.ts.map +1 -1
  166. package/dist/hooks/index.js +1 -1
  167. package/dist/hooks/index.js.map +1 -1
  168. package/dist/hooks/tool-result-recovery-model.d.ts +155 -0
  169. package/dist/hooks/tool-result-recovery-model.d.ts.map +1 -0
  170. package/dist/hooks/tool-result-recovery-model.js +273 -0
  171. package/dist/hooks/tool-result-recovery-model.js.map +1 -0
  172. package/dist/hooks/use-tool-query.d.ts +56 -14
  173. package/dist/hooks/use-tool-query.d.ts.map +1 -1
  174. package/dist/hooks/use-tool-query.js +102 -25
  175. package/dist/hooks/use-tool-query.js.map +1 -1
  176. package/dist/hooks/use-tool-result-recovery.d.ts +47 -52
  177. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
  178. package/dist/hooks/use-tool-result-recovery.js +58 -120
  179. package/dist/hooks/use-tool-result-recovery.js.map +1 -1
  180. package/dist/hooks/use-view-data.d.ts +52 -10
  181. package/dist/hooks/use-view-data.d.ts.map +1 -1
  182. package/dist/hooks/use-view-data.js +81 -16
  183. package/dist/hooks/use-view-data.js.map +1 -1
  184. package/dist/hooks/use-view-tool-query.d.ts +12 -7
  185. package/dist/hooks/use-view-tool-query.d.ts.map +1 -1
  186. package/dist/hooks/use-view-tool-query.js +15 -14
  187. package/dist/hooks/use-view-tool-query.js.map +1 -1
  188. package/dist/index.d.ts +17 -10
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +18 -6
  191. package/dist/index.js.map +1 -1
  192. package/dist/lib/focus-return.d.ts +61 -0
  193. package/dist/lib/focus-return.d.ts.map +1 -0
  194. package/dist/lib/focus-return.js +46 -0
  195. package/dist/lib/focus-return.js.map +1 -0
  196. package/dist/lib/parse-tool-result.d.ts +23 -1
  197. package/dist/lib/parse-tool-result.d.ts.map +1 -1
  198. package/dist/lib/parse-tool-result.js +38 -2
  199. package/dist/lib/parse-tool-result.js.map +1 -1
  200. package/dist/lib/toast-model.d.ts +41 -1
  201. package/dist/lib/toast-model.d.ts.map +1 -1
  202. package/dist/lib/toast-model.js +67 -1
  203. package/dist/lib/toast-model.js.map +1 -1
  204. package/dist/lib/tone-utils.d.ts +58 -6
  205. package/dist/lib/tone-utils.d.ts.map +1 -1
  206. package/dist/lib/tone-utils.js +66 -4
  207. package/dist/lib/tone-utils.js.map +1 -1
  208. package/dist/lib/tool-query.d.ts +107 -0
  209. package/dist/lib/tool-query.d.ts.map +1 -0
  210. package/dist/lib/tool-query.js +182 -0
  211. package/dist/lib/tool-query.js.map +1 -0
  212. package/dist/lib/tool-result-observer.d.ts +108 -0
  213. package/dist/lib/tool-result-observer.d.ts.map +1 -0
  214. package/dist/lib/tool-result-observer.js +158 -0
  215. package/dist/lib/tool-result-observer.js.map +1 -0
  216. package/dist/lib/typography.d.ts +37 -0
  217. package/dist/lib/typography.d.ts.map +1 -0
  218. package/dist/lib/typography.js +41 -0
  219. package/dist/lib/typography.js.map +1 -0
  220. package/dist/lib/utils.d.ts.map +1 -1
  221. package/dist/lib/utils.js +11 -1
  222. package/dist/lib/utils.js.map +1 -1
  223. package/dist/locale/index.d.ts +1 -1
  224. package/dist/locale/index.d.ts.map +1 -1
  225. package/dist/locale/message-types.d.ts +117 -4
  226. package/dist/locale/message-types.d.ts.map +1 -1
  227. package/dist/locale/messages-de.d.ts.map +1 -1
  228. package/dist/locale/messages-de.js +44 -10
  229. package/dist/locale/messages-de.js.map +1 -1
  230. package/dist/locale/messages-en.d.ts.map +1 -1
  231. package/dist/locale/messages-en.js +44 -10
  232. package/dist/locale/messages-en.js.map +1 -1
  233. package/dist/locale/toolkit-messages.d.ts +19 -1
  234. package/dist/locale/toolkit-messages.d.ts.map +1 -1
  235. package/dist/locale/toolkit-messages.js +54 -1
  236. package/dist/locale/toolkit-messages.js.map +1 -1
  237. package/dist/primitives/alert.d.ts +1 -1
  238. package/dist/primitives/alert.d.ts.map +1 -1
  239. package/dist/primitives/alert.js +2 -0
  240. package/dist/primitives/alert.js.map +1 -1
  241. package/dist/primitives/badge.d.ts +1 -1
  242. package/dist/primitives/badge.d.ts.map +1 -1
  243. package/dist/primitives/badge.js +9 -4
  244. package/dist/primitives/badge.js.map +1 -1
  245. package/dist/primitives/button.d.ts +1 -1
  246. package/dist/primitives/button.js +1 -1
  247. package/dist/primitives/button.js.map +1 -1
  248. package/dist/primitives/dialog.d.ts +1 -1
  249. package/dist/primitives/dialog.d.ts.map +1 -1
  250. package/dist/primitives/dialog.js +10 -3
  251. package/dist/primitives/dialog.js.map +1 -1
  252. package/dist/primitives/select.js +1 -1
  253. package/dist/primitives/select.js.map +1 -1
  254. package/dist/primitives/sheet.d.ts +1 -1
  255. package/dist/primitives/sheet.d.ts.map +1 -1
  256. package/dist/primitives/sheet.js +6 -2
  257. package/dist/primitives/sheet.js.map +1 -1
  258. package/dist/providers/query-provider.d.ts +32 -1
  259. package/dist/providers/query-provider.d.ts.map +1 -1
  260. package/dist/providers/query-provider.js +53 -4
  261. package/dist/providers/query-provider.js.map +1 -1
  262. package/dist/render-cases-primitives.js +1 -1
  263. package/dist/render-cases-primitives.js.map +1 -1
  264. package/dist/render-cases.d.ts.map +1 -1
  265. package/dist/render-cases.js +30 -6
  266. package/dist/render-cases.js.map +1 -1
  267. package/dist/render-effects.d.ts +42 -0
  268. package/dist/render-effects.d.ts.map +1 -0
  269. package/dist/render-effects.js +81 -0
  270. package/dist/render-effects.js.map +1 -0
  271. package/dist/theme/create-theme.d.ts +19 -1
  272. package/dist/theme/create-theme.d.ts.map +1 -1
  273. package/dist/theme/create-theme.js +15 -0
  274. package/dist/theme/create-theme.js.map +1 -1
  275. package/package.json +5 -4
  276. package/src/app/adapt-data-widget.tsx +2 -42
  277. package/src/app/builder/BuilderBanners.tsx +1 -1
  278. package/src/app/builder/CanvasRow.tsx +63 -17
  279. package/src/app/builder/CatalogueSheet.tsx +50 -29
  280. package/src/app/builder/CellPropsSheet.tsx +17 -2
  281. package/src/app/builder/DiscardDialog.tsx +59 -0
  282. package/src/app/builder/LayoutSurface.tsx +206 -66
  283. package/src/app/builder/PipelineStrip.tsx +253 -153
  284. package/src/app/builder/SaveDialog.tsx +14 -2
  285. package/src/app/builder/Toolbar.tsx +144 -59
  286. package/src/app/builder/Workspace.tsx +226 -58
  287. package/src/app/builder/builder-model.ts +341 -2
  288. package/src/app/builder/builder-model.ux.test.ts +456 -0
  289. package/src/app/builder/builder-ux.test.tsx +844 -0
  290. package/src/app/builder/reveal.ts +10 -0
  291. package/src/app/builder/use-builder-catalogue.ts +56 -19
  292. package/src/app/builder/use-builder-session.ts +68 -0
  293. package/src/app/builder/use-display-resolver.ts +24 -0
  294. package/src/app/index.ts +12 -2
  295. package/src/app/layout-builder.tsx +178 -191
  296. package/src/app/mcp-app-view-chrome.test.tsx +141 -1
  297. package/src/app/mcp-app-view-chrome.tsx +56 -6
  298. package/src/app/mcp-app-view-states.tsx +51 -11
  299. package/src/app/mcp-app-view.effects.test.tsx +292 -0
  300. package/src/app/mcp-app-view.test.tsx +222 -2
  301. package/src/app/mcp-app-view.tsx +267 -117
  302. package/src/app/mcp-toolkit-app.test.tsx +40 -0
  303. package/src/app/mcp-toolkit-app.tsx +8 -1
  304. package/src/app/mcp-use-host-bridge.test.tsx +180 -0
  305. package/src/app/mcp-use-host-bridge.tsx +67 -24
  306. package/src/app/model-context.test.tsx +116 -0
  307. package/src/app/model-context.tsx +125 -0
  308. package/src/app/use-build-mode-focus.ts +62 -0
  309. package/src/app/use-view-actions.ts +89 -0
  310. package/src/app/use-view-lifecycle.ts +6 -0
  311. package/src/app/view-lifecycle.test.ts +216 -0
  312. package/src/app/view-lifecycle.ts +165 -5
  313. package/src/app/widget-fixture.effects.test.tsx +51 -0
  314. package/src/app/widget-fixture.render.test.tsx +81 -0
  315. package/src/app/widget-fixture.tsx +34 -11
  316. package/src/app/widget-props-sheet.tsx +57 -16
  317. package/src/app/widget-renderer.tsx +1 -1
  318. package/src/components/DrillButton.tsx +23 -10
  319. package/src/components/EmptyState.tsx +6 -4
  320. package/src/components/ErrorState.tsx +31 -9
  321. package/src/components/FilterBar.test.tsx +82 -0
  322. package/src/components/FilterBar.tsx +52 -26
  323. package/src/components/KpiGrid.test.tsx +279 -0
  324. package/src/components/KpiGrid.tsx +297 -90
  325. package/src/components/ListFooter.test.tsx +116 -0
  326. package/src/components/ListFooter.tsx +102 -15
  327. package/src/components/PagedListFooter.test.tsx +140 -0
  328. package/src/components/PagedListFooter.tsx +107 -0
  329. package/src/components/Pills.test.tsx +77 -0
  330. package/src/components/Pills.tsx +63 -10
  331. package/src/components/SectionHeading.tsx +9 -3
  332. package/src/components/Toast.tsx +37 -19
  333. package/src/components/WidgetHeader.test.tsx +78 -0
  334. package/src/components/WidgetHeader.tsx +63 -16
  335. package/src/components/feedback-states.test.tsx +98 -9
  336. package/src/components/tone-figures.test.tsx +8 -4
  337. package/src/hooks/index.ts +6 -3
  338. package/src/hooks/tool-result-recovery-model.test.ts +744 -0
  339. package/src/hooks/tool-result-recovery-model.ts +418 -0
  340. package/src/hooks/use-tool-query.effects.test.tsx +205 -0
  341. package/src/hooks/use-tool-query.ts +156 -33
  342. package/src/hooks/use-tool-result-recovery.effects.test.tsx +141 -0
  343. package/src/hooks/use-tool-result-recovery.test.ts +3 -4
  344. package/src/hooks/use-tool-result-recovery.ts +84 -142
  345. package/src/hooks/use-view-data.test.tsx +118 -9
  346. package/src/hooks/use-view-data.ts +128 -18
  347. package/src/hooks/use-view-tool-query.ts +25 -19
  348. package/src/i18n-coverage.test.tsx +104 -2
  349. package/src/index.ts +48 -11
  350. package/src/lib/focus-return.test.ts +288 -0
  351. package/src/lib/focus-return.ts +96 -0
  352. package/src/lib/parse-tool-result.test.ts +46 -1
  353. package/src/lib/parse-tool-result.ts +40 -2
  354. package/src/lib/toast-model.test.ts +166 -1
  355. package/src/lib/toast-model.ts +104 -2
  356. package/src/lib/tone-utils.test.ts +86 -10
  357. package/src/lib/tone-utils.ts +89 -8
  358. package/src/lib/tool-query.test.ts +569 -0
  359. package/src/lib/tool-query.ts +253 -0
  360. package/src/lib/tool-result-observer.test.ts +296 -0
  361. package/src/lib/tool-result-observer.ts +211 -0
  362. package/src/lib/typography.test.ts +45 -0
  363. package/src/lib/typography.ts +47 -0
  364. package/src/lib/utils.ts +12 -1
  365. package/src/locale/index.ts +1 -0
  366. package/src/locale/message-types.ts +118 -2
  367. package/src/locale/messages-de.ts +49 -10
  368. package/src/locale/messages-en.ts +49 -10
  369. package/src/locale/toolkit-messages.test.ts +133 -0
  370. package/src/locale/toolkit-messages.ts +63 -2
  371. package/src/primitives/alert.tsx +3 -0
  372. package/src/primitives/badge.tsx +9 -4
  373. package/src/primitives/button.tsx +1 -1
  374. package/src/primitives/dialog.tsx +10 -1
  375. package/src/primitives/select.tsx +1 -1
  376. package/src/primitives/sheet.tsx +7 -0
  377. package/src/providers/query-provider.test.tsx +154 -0
  378. package/src/providers/query-provider.tsx +66 -5
  379. package/src/render-cases-primitives.tsx +1 -1
  380. package/src/render-cases.tsx +38 -6
  381. package/src/render-effects.tsx +96 -0
  382. package/src/theme/cd-tokens.test.ts +3 -1
  383. package/src/theme/create-theme.test.ts +10 -0
  384. package/src/theme/create-theme.ts +28 -0
  385. package/src/theme/theme-css.test.ts +51 -0
  386. package/src/theme.css +31 -1
  387. package/ui-catalog.json +448 -41
@@ -4,12 +4,28 @@
4
4
  * FITNESS.md) and the component stays a thin view over them.
5
5
  */
6
6
 
7
+ import { resolveTone } from "./tone-utils.js"
8
+
7
9
  /** Status tone of a toast — the four status roles (`--success` … `--info`). */
8
10
  export type ToastTone = "success" | "warning" | "danger" | "info"
9
11
 
12
+ /**
13
+ * The toast tone of any tone name: the four status tones as is, a retired
14
+ * name through `resolveTone` (`error` → `danger`), and `neutral` or anything
15
+ * unknown as `info`. Keeps a toast whose tone arrives untyped (plain JS, wire
16
+ * data) on the screen instead of crashing the view on a missing icon.
17
+ */
18
+ export function resolveToastTone(name: unknown): ToastTone {
19
+ const tone = resolveTone(name, "info")
20
+ return tone === "neutral" ? "info" : tone
21
+ }
22
+
10
23
  /** What a caller passes to `useToast().show(...)`. */
11
24
  export interface ToastOptions {
12
- /** Status tone; drives the edge, the icon and the default duration. Default `"info"`. */
25
+ /**
26
+ * Status tone; drives the edge, the icon and the default duration. Default
27
+ * `"info"`. An untyped retired name still resolves ({@link resolveToastTone}).
28
+ */
13
29
  tone?: ToastTone
14
30
  /** The message. Say what happened, in one short sentence ("Job retried."). */
15
31
  title: string
@@ -43,7 +59,7 @@ export function toastDuration(tone: ToastTone, explicit?: number): number {
43
59
 
44
60
  /** Normalise caller options into a list item. */
45
61
  export function createToast(id: number, options: ToastOptions): ToastItem {
46
- const tone = options.tone ?? "info"
62
+ const tone = resolveToastTone(options.tone)
47
63
  return {
48
64
  id,
49
65
  tone,
@@ -61,3 +77,89 @@ export function toastReducer(state: readonly ToastItem[], action: ToastAction):
61
77
  const next = state.filter((t) => t.id !== action.id)
62
78
  return next.length === state.length ? (state as ToastItem[]) : next
63
79
  }
80
+
81
+ /**
82
+ * The clock a {@link createToastTimer} runs on. The default reads the global
83
+ * timers at call time, so fake timers installed in a test take effect.
84
+ */
85
+ export interface ToastClock {
86
+ now: () => number
87
+ setTimeout: (callback: () => void, ms: number) => unknown
88
+ clearTimeout: (handle: unknown) => void
89
+ }
90
+
91
+ const SYSTEM_CLOCK: ToastClock = {
92
+ now: () => Date.now(),
93
+ setTimeout: (callback, ms) => globalThis.setTimeout(callback, ms),
94
+ clearTimeout: (handle) => globalThis.clearTimeout(handle as ReturnType<typeof setTimeout>),
95
+ }
96
+
97
+ /** The countdown of one toast (see {@link createToastTimer}). */
98
+ export interface ToastTimer {
99
+ /**
100
+ * Hold the toast for `reason` (`"pointer"`, `"focus"`): the countdown
101
+ * pauses and keeps the time that was left.
102
+ */
103
+ hold: (reason: string) => void
104
+ /** Release `reason`; once nothing holds the toast, it resumes with the time that was left. */
105
+ release: (reason: string) => void
106
+ /** The time left in ms — frozen while held. */
107
+ remaining: () => number
108
+ /** Stop for good (the toast left early or unmounted); `onExpire` never fires afterwards. */
109
+ cancel: () => void
110
+ }
111
+
112
+ /**
113
+ * A pausable countdown: starts at once, pauses while anything holds it
114
+ * (hover, keyboard focus — so a reader can finish), and on release RESUMES
115
+ * WITH THE REMAINING TIME instead of restarting the full duration. Fires
116
+ * `onExpire` once.
117
+ */
118
+ export function createToastTimer(
119
+ duration: number,
120
+ onExpire: () => void,
121
+ clock: ToastClock = SYSTEM_CLOCK,
122
+ ): ToastTimer {
123
+ let left = Math.max(duration, 0)
124
+ let startedAt = 0
125
+ let handle: unknown
126
+ let running = false
127
+ let finished = false
128
+ const holds = new Set<string>()
129
+
130
+ const elapsed = () => (running ? clock.now() - startedAt : 0)
131
+ const start = () => {
132
+ if (finished || running || holds.size > 0) return
133
+ running = true
134
+ startedAt = clock.now()
135
+ handle = clock.setTimeout(() => {
136
+ running = false
137
+ finished = true
138
+ left = 0
139
+ onExpire()
140
+ }, left)
141
+ }
142
+ const pause = () => {
143
+ if (!running) return
144
+ left = Math.max(left - elapsed(), 0)
145
+ running = false
146
+ clock.clearTimeout(handle)
147
+ }
148
+
149
+ start()
150
+ return {
151
+ hold: (reason) => {
152
+ holds.add(reason)
153
+ pause()
154
+ },
155
+ release: (reason) => {
156
+ holds.delete(reason)
157
+ start()
158
+ },
159
+ remaining: () => Math.max(left - elapsed(), 0),
160
+ cancel: () => {
161
+ pause()
162
+ finished = true
163
+ },
164
+ }
165
+ }
@@ -1,18 +1,29 @@
1
1
  import { describe, expect, it } from "vitest"
2
2
  import {
3
+ isToneVariant,
4
+ resolveTone,
5
+ TONE_ALIASES,
3
6
  TONE_BORDER,
4
7
  TONE_DOT,
5
8
  TONE_ICON,
9
+ TONE_INK,
6
10
  TONE_SOFT,
7
11
  TONE_TEXT,
12
+ TONE_TINT,
13
+ TONE_VARIANTS,
8
14
  type ToneVariant,
9
15
  } from "./tone-utils.js"
10
16
 
11
17
  const ALL_TONES: ToneVariant[] = ["neutral", "info", "success", "warning", "danger"]
12
18
  const STATUS_TONES = ["info", "success", "warning", "danger"] as const
13
- const ALL_MAPS = { TONE_SOFT, TONE_DOT, TONE_TEXT, TONE_BORDER, TONE_ICON }
19
+ const ALL_MAPS = { TONE_SOFT, TONE_TINT, TONE_DOT, TONE_INK, TONE_BORDER, TONE_ICON }
14
20
 
15
21
  describe("tone-utils", () => {
22
+ it("has ONE vocabulary of five tones, in severity order", () => {
23
+ expect(TONE_VARIANTS).toEqual(["danger", "warning", "success", "info", "neutral"])
24
+ expect([...TONE_VARIANTS].sort()).toEqual([...ALL_TONES].sort())
25
+ })
26
+
16
27
  it("covers every ToneVariant in each map (no missing tones)", () => {
17
28
  for (const map of Object.values(ALL_MAPS)) {
18
29
  expect(Object.keys(map).sort()).toEqual([...ALL_TONES].sort())
@@ -30,39 +41,104 @@ describe("tone-utils", () => {
30
41
  it("routes each status tone to its three roles (colour, tint, ink)", () => {
31
42
  for (const tone of STATUS_TONES) {
32
43
  expect(TONE_SOFT[tone]).toBe(`bg-${tone}-soft text-foreground`)
33
- expect(TONE_TEXT[tone]).toBe(`text-${tone}-ink`)
44
+ expect(TONE_TINT[tone]).toBe(`bg-${tone}-soft`)
45
+ expect(TONE_INK[tone]).toBe(`text-${tone}-ink`)
34
46
  expect(TONE_DOT[tone]).toBe(`bg-${tone}`)
35
47
  expect(TONE_BORDER[tone]).toBe(`border-${tone}`)
36
48
  expect(TONE_ICON[tone]).toBe(`text-${tone}`)
37
49
  }
38
50
  })
39
51
 
52
+ it("keeps TONE_TEXT as the deprecated name of the very same TONE_INK map", () => {
53
+ expect(TONE_TEXT).toBe(TONE_INK)
54
+ })
55
+
40
56
  it("never sets text in a raw status colour outside the icon map (CI §3.3)", () => {
41
57
  // Success and warning reach 1.67:1 / 1.83:1 on white; text takes the
42
58
  // tone's -ink role. TONE_ICON is the one deliberate exception (SVG
43
59
  // currentColor, always next to a readable label).
44
60
  const rawStatusText = /\btext-(success|warning|danger|info)(?![-\w])/
45
- for (const [name, map] of Object.entries({ TONE_SOFT, TONE_DOT, TONE_TEXT, TONE_BORDER })) {
61
+ for (const [name, map] of Object.entries({
62
+ TONE_SOFT,
63
+ TONE_TINT,
64
+ TONE_DOT,
65
+ TONE_INK,
66
+ TONE_BORDER,
67
+ })) {
46
68
  for (const tone of ALL_TONES) {
47
69
  expect(map[tone], `${name}:${tone}`).not.toMatch(rawStatusText)
48
70
  }
49
71
  }
50
72
  })
51
73
 
52
- it("puts the foreground on every status tint, never the ink or the status colour (CI §3.3)", () => {
74
+ it("puts the foreground on every tint, never the ink or the status colour (CI §3.3)", () => {
53
75
  // "-soft: Text darauf --cd-schwarz, Rand in der Statusfarbe" — the rim
54
- // (TONE_BORDER) carries the tone, danger/info ink is for plain surfaces.
55
- for (const tone of STATUS_TONES) {
56
- expect(TONE_SOFT[tone]).not.toMatch(/\btext-(?:success|warning|danger|info)/)
57
- expect(TONE_SOFT[tone]).toMatch(/\btext-foreground\b/)
76
+ // (TONE_BORDER) carries the tone; a neutral count on the muted tint is a
77
+ // figure and stays in the foreground too.
78
+ for (const tone of ALL_TONES) {
79
+ expect(TONE_SOFT[tone]).toBe(`${TONE_TINT[tone]} text-foreground`)
80
+ expect(TONE_TINT[tone]).not.toMatch(/\btext-/)
58
81
  }
59
82
  })
60
83
 
61
84
  it("routes the neutral tone to the muted ramp", () => {
62
- expect(TONE_SOFT.neutral).toBe("bg-muted text-muted-foreground")
85
+ expect(TONE_SOFT.neutral).toBe("bg-muted text-foreground")
86
+ expect(TONE_TINT.neutral).toBe("bg-muted")
63
87
  expect(TONE_DOT.neutral).toBe("bg-muted-foreground")
64
- expect(TONE_TEXT.neutral).toBe("text-foreground")
88
+ expect(TONE_INK.neutral).toBe("text-foreground")
65
89
  expect(TONE_BORDER.neutral).toBe("border-border")
66
90
  expect(TONE_ICON.neutral).toBe("text-muted-foreground")
67
91
  })
68
92
  })
93
+
94
+ describe("the tone vocabulary: retired names map onto the five tones", () => {
95
+ it("pins every alias (one red, one amber: critical/error/destructive are danger)", () => {
96
+ expect(TONE_ALIASES).toEqual({
97
+ critical: "danger",
98
+ error: "danger",
99
+ destructive: "danger",
100
+ failure: "danger",
101
+ warn: "warning",
102
+ ok: "success",
103
+ positive: "success",
104
+ notice: "info",
105
+ default: "neutral",
106
+ muted: "neutral",
107
+ secondary: "neutral",
108
+ })
109
+ for (const tone of Object.values(TONE_ALIASES)) expect(isToneVariant(tone)).toBe(true)
110
+ for (const alias of Object.keys(TONE_ALIASES)) expect(isToneVariant(alias)).toBe(false)
111
+ })
112
+
113
+ it("keeps a tone as it is", () => {
114
+ for (const tone of ALL_TONES) expect(resolveTone(tone)).toBe(tone)
115
+ })
116
+
117
+ it("maps every alias onto its tone, ignoring case and padding", () => {
118
+ for (const [alias, tone] of Object.entries(TONE_ALIASES)) {
119
+ expect(resolveTone(alias), alias).toBe(tone)
120
+ }
121
+ expect(resolveTone("Critical")).toBe("danger")
122
+ expect(resolveTone(" WARN ")).toBe("warning")
123
+ expect(resolveTone(" Danger")).toBe("danger")
124
+ })
125
+
126
+ it("falls back to neutral (or the caller's fallback) for anything else", () => {
127
+ for (const unknown of ["", " ", "purple", "constructor", "toString", "__proto__"]) {
128
+ expect(resolveTone(unknown), JSON.stringify(unknown)).toBe("neutral")
129
+ }
130
+ for (const notAString of [undefined, null, 3, true, {}, ["danger"]]) {
131
+ expect(resolveTone(notAString)).toBe("neutral")
132
+ }
133
+ expect(resolveTone("purple", "info")).toBe("info")
134
+ expect(resolveTone(undefined, "warning")).toBe("warning")
135
+ expect(resolveTone("critical", "info")).toBe("danger")
136
+ })
137
+
138
+ it("isToneVariant accepts exactly the five tones", () => {
139
+ for (const tone of ALL_TONES) expect(isToneVariant(tone)).toBe(true)
140
+ for (const other of ["Danger", "critical", "", undefined, null, 1, { tone: "info" }]) {
141
+ expect(isToneVariant(other)).toBe(false)
142
+ }
143
+ })
144
+ })
@@ -1,6 +1,11 @@
1
1
  /**
2
2
  * Single source of truth for the semantic "tones" used across the tone-aware
3
- * components (`Pills`, `KpiGrid`, `WidgetHeader`, `Badge`, …).
3
+ * components (`Pills`, `StatusBadge`, `KpiGrid`, `Badge`, …).
4
+ *
5
+ * ONE vocabulary: `success | warning | danger | info | neutral`
6
+ * ({@link TONE_VARIANTS}). Retired or foreign names (`critical`, `error`,
7
+ * `destructive`, …) map onto it through {@link resolveTone}, so a tone that
8
+ * arrives as data (a server-built KPI cell) never renders without a colour.
4
9
  *
5
10
  * Every status tone has THREE roles (Miragon CI §3.3), each its own token in
6
11
  * `theme.css` with light and dark values:
@@ -8,14 +13,14 @@
8
13
  * | Role | Token | Map |
9
14
  * | ---------------------------- | --------------- | -------------- |
10
15
  * | fill, dot, border, icon | `--<tone>` | `TONE_DOT`, `TONE_BORDER`, `TONE_ICON` |
11
- * | tint (badge/banner surface) | `--<tone>-soft` | `TONE_SOFT` (text on it: the foreground) |
12
- * | text on a plain surface | `--<tone>-ink` | `TONE_TEXT` |
16
+ * | tint (badge/banner surface) | `--<tone>-soft` | `TONE_SOFT`, `TONE_TINT` (text on it: the foreground) |
17
+ * | text on a plain surface | `--<tone>-ink` | `TONE_INK` |
13
18
  *
14
19
  * Status is never text in its own colour: success and warning reach only
15
20
  * 1.67:1 / 1.83:1 on white, so their ink is the foreground and the tone shows
16
21
  * as a dot, icon or border NEXT to the text. On a tint the text is always the
17
22
  * foreground (CI §3.3: "-soft: Text darauf --cd-schwarz, Rand in der
18
- * Statusfarbe"), so pair `TONE_SOFT` with `TONE_BORDER`. `TONE_TEXT` is for
23
+ * Statusfarbe"), so pair `TONE_SOFT` with `TONE_BORDER`. `TONE_INK` is for
19
24
  * a tone's words on a plain surface (a field error); figures — counts, KPI
20
25
  * values, trends — never take it: they stay in the foreground next to a
21
26
  * `TONE_DOT`. In dark mode the colours are the `-hell` values and the tints
@@ -27,20 +32,90 @@
27
32
  */
28
33
  export type ToneVariant = "neutral" | "info" | "success" | "warning" | "danger"
29
34
 
35
+ /** Every tone, in severity order (for loops, pickers and validation). */
36
+ export const TONE_VARIANTS: readonly ToneVariant[] = [
37
+ "danger",
38
+ "warning",
39
+ "success",
40
+ "info",
41
+ "neutral",
42
+ ]
43
+
44
+ /**
45
+ * Retired or foreign tone names and the tone each one means. One red and one
46
+ * amber (CI §3.3): `critical`, `error` and `destructive` are `danger`; there
47
+ * is no separate severity above it. The names on the left are deprecated:
48
+ * write the {@link ToneVariant} on the right. The map stays so data written
49
+ * against an older vocabulary (a widget-shell `critical`, a shadcn
50
+ * `destructive`) keeps its colour.
51
+ */
52
+ export const TONE_ALIASES = {
53
+ critical: "danger",
54
+ error: "danger",
55
+ destructive: "danger",
56
+ failure: "danger",
57
+ warn: "warning",
58
+ ok: "success",
59
+ positive: "success",
60
+ notice: "info",
61
+ default: "neutral",
62
+ muted: "neutral",
63
+ secondary: "neutral",
64
+ } as const satisfies Record<string, ToneVariant>
65
+
66
+ /** A name {@link resolveTone} maps onto a tone (see {@link TONE_ALIASES}). */
67
+ export type ToneAlias = keyof typeof TONE_ALIASES
68
+
69
+ /**
70
+ * What a tone-aware component accepts: a {@link ToneVariant}, or a retired
71
+ * name that {@link resolveTone} maps onto one.
72
+ */
73
+ export type ToneName = ToneVariant | ToneAlias
74
+
75
+ const TONE_SET: ReadonlySet<string> = new Set(TONE_VARIANTS)
76
+
77
+ /** Whether `value` is one of the five tones (a type guard for data from the wire). */
78
+ export function isToneVariant(value: unknown): value is ToneVariant {
79
+ return typeof value === "string" && TONE_SET.has(value)
80
+ }
81
+
82
+ /**
83
+ * Map any tone name onto the vocabulary: a {@link ToneVariant} as is, a
84
+ * retired name ({@link TONE_ALIASES}, case-insensitive) onto its tone, and
85
+ * anything else — unknown, empty, not a string — onto `fallback` (default
86
+ * `"neutral"`, the tone without a state).
87
+ */
88
+ export function resolveTone(name: unknown, fallback: ToneVariant = "neutral"): ToneVariant {
89
+ if (typeof name !== "string") return fallback
90
+ const key = name.trim().toLowerCase()
91
+ if (TONE_SET.has(key)) return key as ToneVariant
92
+ return Object.hasOwn(TONE_ALIASES, key) ? TONE_ALIASES[key as ToneAlias] : fallback
93
+ }
94
+
30
95
  /**
31
96
  * Tinted surface + its text (the foreground: CI §3.3) — badges, pills,
32
97
  * banners. Pair it with {@link TONE_BORDER}: the rim carries the tone, and in
33
- * dark mode (no tints) it is all that does.
98
+ * dark mode (no tints) it is all that does. The neutral tint is the muted
99
+ * surface, its text the foreground too (a count on it is a figure).
34
100
  */
35
101
  export const TONE_SOFT: Record<ToneVariant, string> = {
36
- neutral: "bg-muted text-muted-foreground",
102
+ neutral: "bg-muted text-foreground",
37
103
  info: "bg-info-soft text-foreground",
38
104
  success: "bg-success-soft text-foreground",
39
105
  warning: "bg-warning-soft text-foreground",
40
106
  danger: "bg-danger-soft text-foreground",
41
107
  }
42
108
 
43
- /** Solid dot colour — a status dot next to a label. */
109
+ /** The tint alone, for a surface that sets its text itself (a soft KPI card). */
110
+ export const TONE_TINT: Record<ToneVariant, string> = {
111
+ neutral: "bg-muted",
112
+ info: "bg-info-soft",
113
+ success: "bg-success-soft",
114
+ warning: "bg-warning-soft",
115
+ danger: "bg-danger-soft",
116
+ }
117
+
118
+ /** Solid dot colour — a status dot next to a label, a bar, a meter fill. */
44
119
  export const TONE_DOT: Record<ToneVariant, string> = {
45
120
  neutral: "bg-muted-foreground",
46
121
  info: "bg-info",
@@ -56,7 +131,7 @@ export const TONE_DOT: Record<ToneVariant, string> = {
56
131
  * {@link TONE_ICON} to show the tone. Never on a tint (use {@link TONE_SOFT})
57
132
  * and never for figures (they stay in the foreground next to a dot).
58
133
  */
59
- export const TONE_TEXT: Record<ToneVariant, string> = {
134
+ export const TONE_INK: Record<ToneVariant, string> = {
60
135
  neutral: "text-foreground",
61
136
  info: "text-info-ink",
62
137
  success: "text-success-ink",
@@ -64,6 +139,12 @@ export const TONE_TEXT: Record<ToneVariant, string> = {
64
139
  danger: "text-danger-ink",
65
140
  }
66
141
 
142
+ /**
143
+ * @deprecated Renamed to {@link TONE_INK} after the role it maps onto
144
+ * (`--<tone>-ink`); the same object, kept for existing imports.
145
+ */
146
+ export const TONE_TEXT: Record<ToneVariant, string> = TONE_INK
147
+
67
148
  /** Border colour in the tone — the rim of a tinted badge, banner or toast. */
68
149
  export const TONE_BORDER: Record<ToneVariant, string> = {
69
150
  neutral: "border-border",