@miragon/mcp-toolkit-ui 0.10.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 (320) hide show
  1. package/README.md +36 -0
  2. package/dist/app/adapt-data-widget.d.ts +33 -0
  3. package/dist/app/adapt-data-widget.d.ts.map +1 -0
  4. package/dist/app/adapt-data-widget.js +35 -0
  5. package/dist/app/adapt-data-widget.js.map +1 -0
  6. package/dist/app/builder/CatalogueSheet.d.ts +13 -0
  7. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -0
  8. package/dist/app/builder/CatalogueSheet.js +33 -0
  9. package/dist/app/builder/CatalogueSheet.js.map +1 -0
  10. package/dist/app/builder/PipelineStrip.d.ts +22 -0
  11. package/dist/app/builder/PipelineStrip.d.ts.map +1 -0
  12. package/dist/app/builder/PipelineStrip.js +31 -0
  13. package/dist/app/builder/PipelineStrip.js.map +1 -0
  14. package/dist/app/builder/PreviewPane.d.ts +13 -0
  15. package/dist/app/builder/PreviewPane.d.ts.map +1 -0
  16. package/dist/app/builder/PreviewPane.js +21 -0
  17. package/dist/app/builder/PreviewPane.js.map +1 -0
  18. package/dist/app/builder/SaveDialog.d.ts +14 -0
  19. package/dist/app/builder/SaveDialog.d.ts.map +1 -0
  20. package/dist/app/builder/SaveDialog.js +14 -0
  21. package/dist/app/builder/SaveDialog.js.map +1 -0
  22. package/dist/app/builder/Toolbar.d.ts +17 -0
  23. package/dist/app/builder/Toolbar.d.ts.map +1 -0
  24. package/dist/app/builder/Toolbar.js +15 -0
  25. package/dist/app/builder/Toolbar.js.map +1 -0
  26. package/dist/app/builder/Workspace.d.ts +28 -0
  27. package/dist/app/builder/Workspace.d.ts.map +1 -0
  28. package/dist/app/builder/Workspace.js +117 -0
  29. package/dist/app/builder/Workspace.js.map +1 -0
  30. package/dist/app/builder/builder-model.d.ts +86 -0
  31. package/dist/app/builder/builder-model.d.ts.map +1 -0
  32. package/dist/app/builder/builder-model.js +174 -0
  33. package/dist/app/builder/builder-model.js.map +1 -0
  34. package/dist/app/builder/builder-reducer.d.ts +71 -0
  35. package/dist/app/builder/builder-reducer.d.ts.map +1 -0
  36. package/dist/app/builder/builder-reducer.js +201 -0
  37. package/dist/app/builder/builder-reducer.js.map +1 -0
  38. package/dist/app/builder/labels.d.ts +78 -0
  39. package/dist/app/builder/labels.d.ts.map +1 -0
  40. package/dist/app/builder/labels.js +71 -0
  41. package/dist/app/builder/labels.js.map +1 -0
  42. package/dist/app/builder/status-chip.d.ts +10 -0
  43. package/dist/app/builder/status-chip.d.ts.map +1 -0
  44. package/dist/app/builder/status-chip.js +16 -0
  45. package/dist/app/builder/status-chip.js.map +1 -0
  46. package/dist/app/host-bridge.d.ts +211 -0
  47. package/dist/app/host-bridge.d.ts.map +1 -0
  48. package/dist/app/host-bridge.js +247 -0
  49. package/dist/app/host-bridge.js.map +1 -0
  50. package/dist/app/index.d.ts +16 -0
  51. package/dist/app/index.d.ts.map +1 -0
  52. package/dist/app/index.js +18 -0
  53. package/dist/app/index.js.map +1 -0
  54. package/dist/app/layout-builder.d.ts +48 -0
  55. package/dist/app/layout-builder.d.ts.map +1 -0
  56. package/dist/app/layout-builder.js +418 -0
  57. package/dist/app/layout-builder.js.map +1 -0
  58. package/dist/app/mcp-app-view.d.ts +43 -0
  59. package/dist/app/mcp-app-view.d.ts.map +1 -0
  60. package/dist/app/mcp-app-view.js +169 -0
  61. package/dist/app/mcp-app-view.js.map +1 -0
  62. package/dist/app/mcp-toolkit-app.d.ts +18 -0
  63. package/dist/app/mcp-toolkit-app.d.ts.map +1 -0
  64. package/dist/app/mcp-toolkit-app.js +22 -0
  65. package/dist/app/mcp-toolkit-app.js.map +1 -0
  66. package/dist/app/use-host-actions.d.ts +57 -0
  67. package/dist/app/use-host-actions.d.ts.map +1 -0
  68. package/dist/app/use-host-actions.js +42 -0
  69. package/dist/app/use-host-actions.js.map +1 -0
  70. package/dist/app/widget-fixture.d.ts +180 -0
  71. package/dist/app/widget-fixture.d.ts.map +1 -0
  72. package/dist/app/widget-fixture.js +273 -0
  73. package/dist/app/widget-fixture.js.map +1 -0
  74. package/dist/app/widget-props-sheet.d.ts +20 -0
  75. package/dist/app/widget-props-sheet.d.ts.map +1 -0
  76. package/dist/app/widget-props-sheet.js +87 -0
  77. package/dist/app/widget-props-sheet.js.map +1 -0
  78. package/dist/app/widget-renderer.d.ts +87 -0
  79. package/dist/app/widget-renderer.d.ts.map +1 -0
  80. package/dist/app/widget-renderer.js +105 -0
  81. package/dist/app/widget-renderer.js.map +1 -0
  82. package/dist/components/DrillButton.d.ts +22 -0
  83. package/dist/components/DrillButton.d.ts.map +1 -0
  84. package/dist/components/DrillButton.js +13 -0
  85. package/dist/components/DrillButton.js.map +1 -0
  86. package/dist/components/FilterBar.d.ts +28 -0
  87. package/dist/components/FilterBar.d.ts.map +1 -0
  88. package/dist/components/FilterBar.js +12 -0
  89. package/dist/components/FilterBar.js.map +1 -0
  90. package/dist/components/GridLayout.d.ts +26 -0
  91. package/dist/components/GridLayout.d.ts.map +1 -0
  92. package/dist/components/GridLayout.js +31 -0
  93. package/dist/components/GridLayout.js.map +1 -0
  94. package/dist/components/GroupCard.d.ts +21 -0
  95. package/dist/components/GroupCard.d.ts.map +1 -0
  96. package/dist/components/GroupCard.js +24 -0
  97. package/dist/components/GroupCard.js.map +1 -0
  98. package/dist/components/KpiGrid.d.ts +43 -0
  99. package/dist/components/KpiGrid.d.ts.map +1 -0
  100. package/dist/components/KpiGrid.js +35 -0
  101. package/dist/components/KpiGrid.js.map +1 -0
  102. package/dist/components/ListFooter.d.ts +22 -0
  103. package/dist/components/ListFooter.d.ts.map +1 -0
  104. package/dist/components/ListFooter.js +13 -0
  105. package/dist/components/ListFooter.js.map +1 -0
  106. package/dist/components/Pills.d.ts +25 -0
  107. package/dist/components/Pills.d.ts.map +1 -0
  108. package/dist/components/Pills.js +17 -0
  109. package/dist/components/Pills.js.map +1 -0
  110. package/dist/components/SectionHeading.d.ts +16 -0
  111. package/dist/components/SectionHeading.d.ts.map +1 -0
  112. package/dist/components/SectionHeading.js +10 -0
  113. package/dist/components/SectionHeading.js.map +1 -0
  114. package/dist/components/WidgetHeader.d.ts +21 -0
  115. package/dist/components/WidgetHeader.d.ts.map +1 -0
  116. package/dist/components/WidgetHeader.js +11 -0
  117. package/dist/components/WidgetHeader.js.map +1 -0
  118. package/dist/hooks/index.d.ts +11 -0
  119. package/dist/hooks/index.d.ts.map +1 -0
  120. package/dist/hooks/index.js +7 -0
  121. package/dist/hooks/index.js.map +1 -0
  122. package/dist/hooks/use-debounced-value.d.ts +7 -0
  123. package/dist/hooks/use-debounced-value.d.ts.map +1 -0
  124. package/dist/hooks/use-debounced-value.js +15 -0
  125. package/dist/hooks/use-debounced-value.js.map +1 -0
  126. package/dist/hooks/use-mobile.d.ts +6 -0
  127. package/dist/hooks/use-mobile.d.ts.map +1 -0
  128. package/dist/hooks/use-mobile.js +19 -0
  129. package/dist/hooks/use-mobile.js.map +1 -0
  130. package/dist/hooks/use-tool-query.d.ts +21 -0
  131. package/dist/hooks/use-tool-query.d.ts.map +1 -0
  132. package/dist/hooks/use-tool-query.js +47 -0
  133. package/dist/hooks/use-tool-query.js.map +1 -0
  134. package/dist/hooks/use-tool-result-recovery.d.ts +54 -0
  135. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -0
  136. package/dist/hooks/use-tool-result-recovery.js +93 -0
  137. package/dist/hooks/use-tool-result-recovery.js.map +1 -0
  138. package/dist/hooks/use-view-data.d.ts +19 -0
  139. package/dist/hooks/use-view-data.d.ts.map +1 -0
  140. package/dist/hooks/use-view-data.js +18 -0
  141. package/dist/hooks/use-view-data.js.map +1 -0
  142. package/dist/hooks/use-view-tool-query.d.ts +18 -0
  143. package/dist/hooks/use-view-tool-query.d.ts.map +1 -0
  144. package/dist/hooks/use-view-tool-query.js +29 -0
  145. package/dist/hooks/use-view-tool-query.js.map +1 -0
  146. package/dist/index.d.ts +47 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +56 -0
  149. package/dist/index.js.map +1 -0
  150. package/dist/lib/parse-tool-result.d.ts +39 -0
  151. package/dist/lib/parse-tool-result.d.ts.map +1 -0
  152. package/dist/lib/parse-tool-result.js +66 -0
  153. package/dist/lib/parse-tool-result.js.map +1 -0
  154. package/dist/lib/tone-utils.d.ts +18 -0
  155. package/dist/lib/tone-utils.d.ts.map +1 -0
  156. package/dist/lib/tone-utils.js +25 -0
  157. package/dist/lib/tone-utils.js.map +1 -0
  158. package/dist/lib/utils.d.ts +3 -0
  159. package/dist/lib/utils.d.ts.map +1 -0
  160. package/dist/lib/utils.js +6 -0
  161. package/dist/lib/utils.js.map +1 -0
  162. package/dist/locale/index.d.ts +3 -0
  163. package/dist/locale/index.d.ts.map +1 -0
  164. package/dist/locale/index.js +7 -0
  165. package/dist/locale/index.js.map +1 -0
  166. package/dist/locale/locale-provider.d.ts +50 -0
  167. package/dist/locale/locale-provider.d.ts.map +1 -0
  168. package/dist/locale/locale-provider.js +47 -0
  169. package/dist/locale/locale-provider.js.map +1 -0
  170. package/dist/primitives/alert.d.ts +10 -0
  171. package/dist/primitives/alert.d.ts.map +1 -0
  172. package/dist/primitives/alert.js +25 -0
  173. package/dist/primitives/alert.js.map +1 -0
  174. package/dist/primitives/badge.d.ts +8 -0
  175. package/dist/primitives/badge.d.ts.map +1 -0
  176. package/dist/primitives/badge.js +34 -0
  177. package/dist/primitives/badge.js.map +1 -0
  178. package/dist/primitives/button.d.ts +11 -0
  179. package/dist/primitives/button.d.ts.map +1 -0
  180. package/dist/primitives/button.js +36 -0
  181. package/dist/primitives/button.js.map +1 -0
  182. package/dist/primitives/card.d.ts +12 -0
  183. package/dist/primitives/card.d.ts.map +1 -0
  184. package/dist/primitives/card.js +25 -0
  185. package/dist/primitives/card.js.map +1 -0
  186. package/dist/primitives/dialog.d.ts +18 -0
  187. package/dist/primitives/dialog.d.ts.map +1 -0
  188. package/dist/primitives/dialog.js +38 -0
  189. package/dist/primitives/dialog.js.map +1 -0
  190. package/dist/primitives/input.d.ts +4 -0
  191. package/dist/primitives/input.d.ts.map +1 -0
  192. package/dist/primitives/input.js +7 -0
  193. package/dist/primitives/input.js.map +1 -0
  194. package/dist/primitives/scroll-area.d.ts +6 -0
  195. package/dist/primitives/scroll-area.d.ts.map +1 -0
  196. package/dist/primitives/scroll-area.js +11 -0
  197. package/dist/primitives/scroll-area.js.map +1 -0
  198. package/dist/primitives/select.d.ts +16 -0
  199. package/dist/primitives/select.d.ts.map +1 -0
  200. package/dist/primitives/select.js +38 -0
  201. package/dist/primitives/select.js.map +1 -0
  202. package/dist/primitives/separator.d.ts +4 -0
  203. package/dist/primitives/separator.d.ts.map +1 -0
  204. package/dist/primitives/separator.js +9 -0
  205. package/dist/primitives/separator.js.map +1 -0
  206. package/dist/primitives/sheet.d.ts +15 -0
  207. package/dist/primitives/sheet.d.ts.map +1 -0
  208. package/dist/primitives/sheet.js +41 -0
  209. package/dist/primitives/sheet.js.map +1 -0
  210. package/dist/primitives/skeleton.d.ts +3 -0
  211. package/dist/primitives/skeleton.d.ts.map +1 -0
  212. package/dist/primitives/skeleton.js +7 -0
  213. package/dist/primitives/skeleton.js.map +1 -0
  214. package/dist/primitives/switch.d.ts +5 -0
  215. package/dist/primitives/switch.d.ts.map +1 -0
  216. package/dist/primitives/switch.js +8 -0
  217. package/dist/primitives/switch.js.map +1 -0
  218. package/dist/primitives/table.d.ts +11 -0
  219. package/dist/primitives/table.d.ts.map +1 -0
  220. package/dist/primitives/table.js +28 -0
  221. package/dist/primitives/table.js.map +1 -0
  222. package/dist/primitives/tabs.d.ts +12 -0
  223. package/dist/primitives/tabs.d.ts.map +1 -0
  224. package/dist/primitives/tabs.js +29 -0
  225. package/dist/primitives/tabs.js.map +1 -0
  226. package/dist/providers/query-provider.d.ts +11 -0
  227. package/dist/providers/query-provider.d.ts.map +1 -0
  228. package/dist/providers/query-provider.js +20 -0
  229. package/dist/providers/query-provider.js.map +1 -0
  230. package/dist/theme/create-theme.d.ts +130 -0
  231. package/dist/theme/create-theme.d.ts.map +1 -0
  232. package/dist/theme/create-theme.js +65 -0
  233. package/dist/theme/create-theme.js.map +1 -0
  234. package/dist/theme/index.d.ts +7 -0
  235. package/dist/theme/index.d.ts.map +1 -0
  236. package/dist/theme/index.js +7 -0
  237. package/dist/theme/index.js.map +1 -0
  238. package/dist/theme/presets.d.ts +25 -0
  239. package/dist/theme/presets.d.ts.map +1 -0
  240. package/dist/theme/presets.js +68 -0
  241. package/dist/theme/presets.js.map +1 -0
  242. package/dist/theme/theme-provider.d.ts +69 -0
  243. package/dist/theme/theme-provider.d.ts.map +1 -0
  244. package/dist/theme/theme-provider.js +79 -0
  245. package/dist/theme/theme-provider.js.map +1 -0
  246. package/package.json +71 -0
  247. package/src/app/adapt-data-widget.tsx +51 -0
  248. package/src/app/builder/CatalogueSheet.tsx +230 -0
  249. package/src/app/builder/PipelineStrip.tsx +260 -0
  250. package/src/app/builder/PreviewPane.tsx +72 -0
  251. package/src/app/builder/SaveDialog.tsx +95 -0
  252. package/src/app/builder/Toolbar.tsx +95 -0
  253. package/src/app/builder/Workspace.tsx +483 -0
  254. package/src/app/builder/builder-model.test.ts +351 -0
  255. package/src/app/builder/builder-model.ts +213 -0
  256. package/src/app/builder/builder-reducer.test.ts +441 -0
  257. package/src/app/builder/builder-reducer.ts +289 -0
  258. package/src/app/builder/labels.ts +139 -0
  259. package/src/app/builder/status-chip.tsx +37 -0
  260. package/src/app/host-bridge.test.ts +206 -0
  261. package/src/app/host-bridge.tsx +371 -0
  262. package/src/app/index.ts +51 -0
  263. package/src/app/layout-builder.tsx +872 -0
  264. package/src/app/mcp-app-view.tsx +367 -0
  265. package/src/app/mcp-toolkit-app.tsx +25 -0
  266. package/src/app/use-host-actions.test.ts +16 -0
  267. package/src/app/use-host-actions.tsx +88 -0
  268. package/src/app/widget-fixture.test.ts +108 -0
  269. package/src/app/widget-fixture.tsx +441 -0
  270. package/src/app/widget-props-sheet.tsx +285 -0
  271. package/src/app/widget-renderer.test.ts +42 -0
  272. package/src/app/widget-renderer.tsx +200 -0
  273. package/src/components/DrillButton.tsx +37 -0
  274. package/src/components/FilterBar.tsx +68 -0
  275. package/src/components/GridLayout.tsx +49 -0
  276. package/src/components/GroupCard.tsx +52 -0
  277. package/src/components/KpiGrid.tsx +134 -0
  278. package/src/components/ListFooter.tsx +48 -0
  279. package/src/components/Pills.tsx +45 -0
  280. package/src/components/SectionHeading.tsx +26 -0
  281. package/src/components/WidgetHeader.tsx +51 -0
  282. package/src/globals.css +168 -0
  283. package/src/hooks/index.ts +17 -0
  284. package/src/hooks/use-debounced-value.ts +15 -0
  285. package/src/hooks/use-mobile.ts +24 -0
  286. package/src/hooks/use-tool-query.ts +63 -0
  287. package/src/hooks/use-tool-result-recovery.test.ts +65 -0
  288. package/src/hooks/use-tool-result-recovery.ts +144 -0
  289. package/src/hooks/use-view-data.ts +31 -0
  290. package/src/hooks/use-view-tool-query.ts +38 -0
  291. package/src/index.ts +134 -0
  292. package/src/lib/parse-tool-result.test.ts +139 -0
  293. package/src/lib/parse-tool-result.ts +89 -0
  294. package/src/lib/tone-utils.test.ts +35 -0
  295. package/src/lib/tone-utils.ts +38 -0
  296. package/src/lib/utils.ts +6 -0
  297. package/src/locale/index.ts +7 -0
  298. package/src/locale/locale-provider.tsx +81 -0
  299. package/src/primitives/alert.tsx +60 -0
  300. package/src/primitives/badge.tsx +49 -0
  301. package/src/primitives/button.tsx +62 -0
  302. package/src/primitives/card.tsx +92 -0
  303. package/src/primitives/dialog.tsx +144 -0
  304. package/src/primitives/input.tsx +21 -0
  305. package/src/primitives/scroll-area.tsx +54 -0
  306. package/src/primitives/select.tsx +173 -0
  307. package/src/primitives/separator.tsx +21 -0
  308. package/src/primitives/sheet.tsx +134 -0
  309. package/src/primitives/skeleton.tsx +13 -0
  310. package/src/primitives/switch.tsx +24 -0
  311. package/src/primitives/table.tsx +87 -0
  312. package/src/primitives/tabs.tsx +79 -0
  313. package/src/providers/query-provider.tsx +38 -0
  314. package/src/theme/create-theme.test.ts +108 -0
  315. package/src/theme/create-theme.ts +158 -0
  316. package/src/theme/index.ts +9 -0
  317. package/src/theme/presets.ts +82 -0
  318. package/src/theme/theme-provider.tsx +152 -0
  319. package/src/ui-catalog.test.ts +112 -0
  320. package/ui-catalog.json +1280 -0
@@ -0,0 +1,68 @@
1
+ import { createTheme } from "./create-theme.js";
2
+ /**
3
+ * Ready-made white-label themes — a demonstration of {@link createTheme} and a
4
+ * starting point a client can fork. Each preset sets a distinct brand `primary`,
5
+ * a matching `ring`/`accent`, and a `radius`, plus a `dark` override that
6
+ * brightens the brand colour for legibility on dark surfaces (mirroring how
7
+ * `globals.css` brightens tokens under `.dark`).
8
+ *
9
+ * Colours are authored in `oklch` to stay consistent with the toolkit's token
10
+ * palette in `globals.css`. Apply one with `<ThemeProvider theme={themePresets.violet}>`.
11
+ */
12
+ export const themePresets = {
13
+ /**
14
+ * The toolkit's own brand: a confident indigo-blue with a slightly tighter
15
+ * radius. A sensible default when a client has no brand of their own yet.
16
+ */
17
+ miragon: createTheme({
18
+ primary: "oklch(0.52 0.21 264)",
19
+ primaryForeground: "oklch(0.985 0 0)",
20
+ accent: "oklch(0.95 0.03 264)",
21
+ accentForeground: "oklch(0.32 0.12 264)",
22
+ ring: "oklch(0.52 0.21 264)",
23
+ radius: "0.5rem",
24
+ }, {
25
+ dark: {
26
+ primary: "oklch(0.7 0.16 264)",
27
+ primaryForeground: "oklch(0.18 0.02 264)",
28
+ accent: "oklch(0.32 0.06 264)",
29
+ accentForeground: "oklch(0.97 0 0)",
30
+ ring: "oklch(0.7 0.16 264)",
31
+ },
32
+ }),
33
+ /** A vivid violet brand with rounder corners — playful, consumer-facing. */
34
+ violet: createTheme({
35
+ primary: "oklch(0.55 0.24 295)",
36
+ primaryForeground: "oklch(0.985 0 0)",
37
+ accent: "oklch(0.95 0.04 295)",
38
+ accentForeground: "oklch(0.34 0.14 295)",
39
+ ring: "oklch(0.55 0.24 295)",
40
+ radius: "0.875rem",
41
+ }, {
42
+ dark: {
43
+ primary: "oklch(0.72 0.18 295)",
44
+ primaryForeground: "oklch(0.18 0.03 295)",
45
+ accent: "oklch(0.33 0.08 295)",
46
+ accentForeground: "oklch(0.97 0 0)",
47
+ ring: "oklch(0.72 0.18 295)",
48
+ },
49
+ }),
50
+ /** A grounded emerald brand with a crisp, near-square radius — enterprise. */
51
+ emerald: createTheme({
52
+ primary: "oklch(0.58 0.15 162)",
53
+ primaryForeground: "oklch(0.985 0 0)",
54
+ accent: "oklch(0.95 0.04 162)",
55
+ accentForeground: "oklch(0.34 0.1 162)",
56
+ ring: "oklch(0.58 0.15 162)",
57
+ radius: "0.375rem",
58
+ }, {
59
+ dark: {
60
+ primary: "oklch(0.72 0.15 162)",
61
+ primaryForeground: "oklch(0.17 0.03 162)",
62
+ accent: "oklch(0.32 0.07 162)",
63
+ accentForeground: "oklch(0.97 0 0)",
64
+ ring: "oklch(0.72 0.15 162)",
65
+ },
66
+ }),
67
+ };
68
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/theme/presets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,mBAAmB,CAAA;AAErE;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;;;OAGG;IACH,OAAO,EAAE,WAAW,CAClB;QACE,OAAO,EAAE,sBAAsB;QAC/B,iBAAiB,EAAE,kBAAkB;QACrC,MAAM,EAAE,sBAAsB;QAC9B,gBAAgB,EAAE,sBAAsB;QACxC,IAAI,EAAE,sBAAsB;QAC5B,MAAM,EAAE,QAAQ;KACjB,EACD;QACE,IAAI,EAAE;YACJ,OAAO,EAAE,qBAAqB;YAC9B,iBAAiB,EAAE,sBAAsB;YACzC,MAAM,EAAE,sBAAsB;YAC9B,gBAAgB,EAAE,iBAAiB;YACnC,IAAI,EAAE,qBAAqB;SAC5B;KACF,CACF;IAED,4EAA4E;IAC5E,MAAM,EAAE,WAAW,CACjB;QACE,OAAO,EAAE,sBAAsB;QAC/B,iBAAiB,EAAE,kBAAkB;QACrC,MAAM,EAAE,sBAAsB;QAC9B,gBAAgB,EAAE,sBAAsB;QACxC,IAAI,EAAE,sBAAsB;QAC5B,MAAM,EAAE,UAAU;KACnB,EACD;QACE,IAAI,EAAE;YACJ,OAAO,EAAE,sBAAsB;YAC/B,iBAAiB,EAAE,sBAAsB;YACzC,MAAM,EAAE,sBAAsB;YAC9B,gBAAgB,EAAE,iBAAiB;YACnC,IAAI,EAAE,sBAAsB;SAC7B;KACF,CACF;IAED,8EAA8E;IAC9E,OAAO,EAAE,WAAW,CAClB;QACE,OAAO,EAAE,sBAAsB;QAC/B,iBAAiB,EAAE,kBAAkB;QACrC,MAAM,EAAE,sBAAsB;QAC9B,gBAAgB,EAAE,qBAAqB;QACvC,IAAI,EAAE,sBAAsB;QAC5B,MAAM,EAAE,UAAU;KACnB,EACD;QACE,IAAI,EAAE;YACJ,OAAO,EAAE,sBAAsB;YAC/B,iBAAiB,EAAE,sBAAsB;YACzC,MAAM,EAAE,sBAAsB;YAC9B,gBAAgB,EAAE,iBAAiB;YACnC,IAAI,EAAE,sBAAsB;SAC7B;KACF,CACF;CACwC,CAAA"}
@@ -0,0 +1,69 @@
1
+ import { type ReactNode } from "react";
2
+ import type { ThemeDefinition } from "./create-theme.js";
3
+ /**
4
+ * How the `.dark` class is resolved on the theme scope:
5
+ * - `"light"` / `"dark"` — forced, ignores the OS preference.
6
+ * - `"system"` — follows `prefers-color-scheme` via `matchMedia`, updating live.
7
+ *
8
+ * A host that already knows the colour scheme (e.g. `HostBridge.theme`) can pass
9
+ * its value straight through as `mode` so the white-label theme tracks the host.
10
+ */
11
+ export type ThemeMode = "light" | "dark" | "system";
12
+ /** The value exposed by {@link useTheme}. */
13
+ export interface ThemeContextValue {
14
+ /** The active theme definition, or `null` when only a mode is being managed. */
15
+ theme: ThemeDefinition | null;
16
+ /** The mode as requested on the provider (may be `"system"`). */
17
+ mode: ThemeMode;
18
+ /** The mode actually applied right now (`"system"` resolved to light/dark). */
19
+ resolvedMode: "light" | "dark";
20
+ }
21
+ /**
22
+ * Read the active theme/mode from the nearest {@link ThemeProvider}. Throws when
23
+ * called outside a provider so a missing wrapper fails loudly rather than
24
+ * silently rendering the default theme.
25
+ */
26
+ export declare function useTheme(): ThemeContextValue;
27
+ /** Props for {@link ThemeProvider}. */
28
+ export interface ThemeProviderProps {
29
+ /**
30
+ * The white-label theme to apply (from {@link createTheme} or `themePresets`).
31
+ * Omit to manage only the light/dark `mode` against the toolkit defaults.
32
+ */
33
+ theme?: ThemeDefinition;
34
+ /**
35
+ * Colour-scheme mode. Defaults to `"light"`. Pass `HostBridge.theme` through
36
+ * here to follow the host, or `"system"` to track the OS preference.
37
+ */
38
+ mode?: ThemeMode;
39
+ /**
40
+ * The HTML element to render as the scope wrapper. Defaults to `"div"`. Use
41
+ * `"span"` for inline contexts; any intrinsic element with a `style`/className
42
+ * works.
43
+ */
44
+ as?: "div" | "span" | "section" | "main";
45
+ /** Extra classes merged onto the scope wrapper. */
46
+ className?: string;
47
+ children: ReactNode;
48
+ }
49
+ /**
50
+ * Apply a white-label {@link ThemeDefinition} to a subtree. Renders a wrapper
51
+ * element carrying the theme's CSS variables as inline `style`, so they override
52
+ * the `:root` defaults for every descendant — `Card`, `Button`, `bg-card`,
53
+ * `text-primary`, and `rounded-lg` all re-skin without touching the widgets.
54
+ *
55
+ * Dark mode is handled at the scope: the `.dark` class is toggled on the wrapper
56
+ * (not `<html>`) per the resolved {@link ThemeMode}, and the theme's `darkVars`
57
+ * are layered on under it. This keeps theming local — two differently themed
58
+ * subtrees (e.g. a brand preview gallery) coexist on one page.
59
+ *
60
+ * Uses only React + the DOM (no `mcp-use/react`), so it ships from the root
61
+ * barrel and works in a host-free standalone app, a host, or SSR alike.
62
+ *
63
+ * @example
64
+ * <ThemeProvider theme={themePresets.violet} mode={host.theme ?? "system"}>
65
+ * <YourWidget />
66
+ * </ThemeProvider>
67
+ */
68
+ export declare function ThemeProvider({ theme, mode, as: As, className, children, }: ThemeProviderProps): ReactNode;
69
+ //# sourceMappingURL=theme-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider.d.ts","sourceRoot":"","sources":["../../src/theme/theme-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AACd,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnD,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,KAAK,EAAE,eAAe,GAAG,IAAI,CAAA;IAC7B,iEAAiE;IACjE,IAAI,EAAE,SAAS,CAAA;IACf,+EAA+E;IAC/E,YAAY,EAAE,OAAO,GAAG,MAAM,CAAA;CAC/B;AAID;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,iBAAiB,CAM5C;AA6BD,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;OAIG;IACH,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;IACxC,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,IAAc,EACd,EAAE,EAAE,EAAU,EACd,SAAS,EACT,QAAQ,GACT,EAAE,kBAAkB,GAAG,SAAS,CA8BhC"}
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useEffect, useMemo, useState, } from "react";
3
+ const ThemeContext = createContext(null);
4
+ /**
5
+ * Read the active theme/mode from the nearest {@link ThemeProvider}. Throws when
6
+ * called outside a provider so a missing wrapper fails loudly rather than
7
+ * silently rendering the default theme.
8
+ */
9
+ export function useTheme() {
10
+ const value = useContext(ThemeContext);
11
+ if (!value) {
12
+ throw new Error("useTheme() must be used within a <ThemeProvider>.");
13
+ }
14
+ return value;
15
+ }
16
+ /**
17
+ * Resolve `"system"` to a concrete `"light"` | `"dark"` and keep it in sync with
18
+ * the OS preference. SSR-safe: returns `"light"` until mounted (no `window`),
19
+ * then subscribes to `matchMedia` on the client. Forced modes short-circuit the
20
+ * media query entirely.
21
+ */
22
+ function useResolvedMode(mode) {
23
+ const [systemMode, setSystemMode] = useState("light");
24
+ useEffect(() => {
25
+ if (mode !== "system")
26
+ return;
27
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function")
28
+ return;
29
+ const query = window.matchMedia("(prefers-color-scheme: dark)");
30
+ const sync = () => {
31
+ setSystemMode(query.matches ? "dark" : "light");
32
+ };
33
+ sync();
34
+ query.addEventListener("change", sync);
35
+ return () => {
36
+ query.removeEventListener("change", sync);
37
+ };
38
+ }, [mode]);
39
+ return mode === "system" ? systemMode : mode;
40
+ }
41
+ /**
42
+ * Apply a white-label {@link ThemeDefinition} to a subtree. Renders a wrapper
43
+ * element carrying the theme's CSS variables as inline `style`, so they override
44
+ * the `:root` defaults for every descendant — `Card`, `Button`, `bg-card`,
45
+ * `text-primary`, and `rounded-lg` all re-skin without touching the widgets.
46
+ *
47
+ * Dark mode is handled at the scope: the `.dark` class is toggled on the wrapper
48
+ * (not `<html>`) per the resolved {@link ThemeMode}, and the theme's `darkVars`
49
+ * are layered on under it. This keeps theming local — two differently themed
50
+ * subtrees (e.g. a brand preview gallery) coexist on one page.
51
+ *
52
+ * Uses only React + the DOM (no `mcp-use/react`), so it ships from the root
53
+ * barrel and works in a host-free standalone app, a host, or SSR alike.
54
+ *
55
+ * @example
56
+ * <ThemeProvider theme={themePresets.violet} mode={host.theme ?? "system"}>
57
+ * <YourWidget />
58
+ * </ThemeProvider>
59
+ */
60
+ export function ThemeProvider({ theme, mode = "light", as: As = "div", className, children, }) {
61
+ const resolvedMode = useResolvedMode(mode);
62
+ const isDark = resolvedMode === "dark";
63
+ // Merge the light vars with the dark overrides only when dark is active, so a
64
+ // dark scope gets the brand's dark palette on top of `.dark`'s defaults.
65
+ const style = useMemo(() => {
66
+ if (!theme)
67
+ return {};
68
+ if (isDark && theme.darkVars) {
69
+ return { ...theme.vars, ...theme.darkVars };
70
+ }
71
+ return theme.vars;
72
+ }, [theme, isDark]);
73
+ const value = useMemo(() => ({ theme: theme ?? null, mode, resolvedMode }), [theme, mode, resolvedMode]);
74
+ const classes = [isDark ? "dark" : undefined, "bg-background text-foreground", className]
75
+ .filter(Boolean)
76
+ .join(" ");
77
+ return (_jsx(ThemeContext.Provider, { value: value, children: _jsx(As, { className: classes, style: style, "data-theme-mode": resolvedMode, children: children }) }));
78
+ }
79
+ //# sourceMappingURL=theme-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-provider.js","sourceRoot":"","sources":["../../src/theme/theme-provider.tsx"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@miragon/mcp-toolkit-ui",
3
+ "version": "0.10.1",
4
+ "description": "React UI for MCP servers: shadcn primitives, composite components, TanStack Query hooks, and the MCP App shell (McpAppView + WidgetRenderer).",
5
+ "type": "module",
6
+ "sideEffects": [
7
+ "**/*.css"
8
+ ],
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./app": {
17
+ "types": "./dist/app/index.d.ts",
18
+ "import": "./dist/app/index.js"
19
+ },
20
+ "./hooks": {
21
+ "types": "./dist/hooks/index.d.ts",
22
+ "import": "./dist/hooks/index.js"
23
+ },
24
+ "./globals.css": "./src/globals.css"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "src",
29
+ "ui-catalog.json",
30
+ "README.md"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "peerDependencies": {
36
+ "mcp-use": "1.34.5",
37
+ "react": "19.2.8",
38
+ "react-dom": "19.2.8",
39
+ "tailwindcss": "4.3.3",
40
+ "@miragon/mcp-toolkit-core": "0.10.1"
41
+ },
42
+ "dependencies": {
43
+ "@base-ui/react": "1.6.0",
44
+ "@fontsource-variable/geist": "5.3.0",
45
+ "@tanstack/react-query": "5.101.4",
46
+ "class-variance-authority": "0.7.1",
47
+ "clsx": "2.1.1",
48
+ "lucide-react": "1.25.0",
49
+ "radix-ui": "1.6.7",
50
+ "tailwind-merge": "3.6.0",
51
+ "tw-animate-css": "1.4.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/react": "19.2.17",
55
+ "@types/react-dom": "19.2.3",
56
+ "mcp-use": "1.34.5",
57
+ "react": "19.2.8",
58
+ "react-dom": "19.2.8",
59
+ "tailwindcss": "4.3.3",
60
+ "typescript": "6.0.3",
61
+ "vitest": "4.1.10",
62
+ "@miragon/mcp-toolkit-core": "0.10.1"
63
+ },
64
+ "scripts": {
65
+ "build": "tsc -p tsconfig.build.json",
66
+ "typecheck": "tsc --noEmit",
67
+ "test": "vitest run",
68
+ "lint": "eslint src --max-warnings 0",
69
+ "lint:fix": "eslint src --fix"
70
+ }
71
+ }
@@ -0,0 +1,51 @@
1
+ import type { ComponentType } from "react"
2
+ import { ModelContext } from "mcp-use/react"
3
+ import type { WidgetProps } from "@miragon/mcp-toolkit-core"
4
+ import type { WidgetComponent } from "./widget-renderer.js"
5
+
6
+ /**
7
+ * Builds the model-context line for an adapted widget. Receives the resolved
8
+ * step data (never null — the adapter skips the description while loading) plus
9
+ * the per-cell layout props so scope filters that only travel as props (e.g.
10
+ * `period` / `engine` on dashboard widgets) can be reported too.
11
+ */
12
+ export type DescribeForModel<T> = (
13
+ data: NonNullable<T>,
14
+ props: Readonly<Record<string, unknown>>,
15
+ ) => string
16
+
17
+ /**
18
+ * Wraps a "single-data" widget component (signature `({ data }: { data: T | null })`)
19
+ * so it can be registered as a framework {@link WidgetComponent} (which receives
20
+ * `WidgetProps = { keys, context, widgetProps? }`). The adapter looks up the
21
+ * matching step result in `context.steps` by its `_dataType` and forwards
22
+ * `result.data` to the wrapped widget. Lets `render-view` and `*_show_*` tools
23
+ * (via `buildSingleWidgetView`) share the same widget components without
24
+ * changing their props.
25
+ *
26
+ * Per-cell `props` from the layout (`row[].props`) are spread onto the
27
+ * wrapped widget as named props. This lets the same widget appear multiple
28
+ * times in one view with different scoping (e.g. one tab per process key).
29
+ *
30
+ * When `describeForModel` is provided the adapter wraps the widget in a
31
+ * `ModelContext` so the model knows what the user is looking at (view identity,
32
+ * active filters, headline numbers) without per-widget boilerplate. The
33
+ * description is only rendered once the step data is present; widgets that
34
+ * self-fetch when the adapter has no data (cockpit views) must render their own
35
+ * `<ModelContext>` instead.
36
+ */
37
+ export function adaptDataWidget<T>(
38
+ Widget: ComponentType<{ data: T | null } & Record<string, unknown>>,
39
+ dataType: string,
40
+ describeForModel?: DescribeForModel<T>,
41
+ ): WidgetComponent {
42
+ function AdaptedWidget({ context, widgetProps }: WidgetProps) {
43
+ const stepResult = Object.values(context.steps).find((s) => s._dataType === dataType)
44
+ const data = (stepResult?.data ?? null) as T | null
45
+ const widget = <Widget {...(widgetProps ?? {})} data={data} />
46
+ if (data == null || !describeForModel) return widget
47
+ return <ModelContext content={describeForModel(data, widgetProps ?? {})}>{widget}</ModelContext>
48
+ }
49
+ AdaptedWidget.displayName = `Adapted(${Widget.displayName ?? Widget.name ?? "Widget"})`
50
+ return AdaptedWidget
51
+ }
@@ -0,0 +1,230 @@
1
+ import { useMemo } from "react"
2
+ import { ArrowDownToLine, Library, Plus } from "lucide-react"
3
+ import type { AvailableStep, KeyCatalogEntry, UnreachableWidget } from "@miragon/mcp-toolkit-core"
4
+ import { Badge } from "../../primitives/badge.js"
5
+ import { Button } from "../../primitives/button.js"
6
+ import { ScrollArea } from "../../primitives/scroll-area.js"
7
+ import {
8
+ Sheet,
9
+ SheetContent,
10
+ SheetDescription,
11
+ SheetHeader,
12
+ SheetTitle,
13
+ } from "../../primitives/sheet.js"
14
+ import { cn } from "../../lib/utils.js"
15
+ import type { LayoutBuilderLabels } from "./labels.js"
16
+
17
+ // -------------------------------------------------------------------------- //
18
+ // Catalogue Sheet — slides in from the right
19
+ // -------------------------------------------------------------------------- //
20
+
21
+ export function CatalogueSheet({
22
+ L,
23
+ open,
24
+ onOpenChange,
25
+ keyCatalog,
26
+ availableSteps,
27
+ unreachableWidgets,
28
+ onSeedKey,
29
+ onAddProducingStep,
30
+ }: {
31
+ L: Required<LayoutBuilderLabels>
32
+ open: boolean
33
+ onOpenChange: (open: boolean) => void
34
+ keyCatalog: KeyCatalogEntry[]
35
+ availableSteps: AvailableStep[]
36
+ unreachableWidgets: UnreachableWidget[]
37
+ onSeedKey: (name: string) => void
38
+ onAddProducingStep: (key: string) => void
39
+ }) {
40
+ const producerOf = useMemo(() => {
41
+ const map = new Map<string, string | undefined>()
42
+ for (const e of keyCatalog) map.set(e.key, e.producedBySteps[0])
43
+ return map
44
+ }, [keyCatalog])
45
+
46
+ return (
47
+ <Sheet open={open} onOpenChange={onOpenChange}>
48
+ <SheetContent className="w-full sm:max-w-md md:max-w-lg">
49
+ <SheetHeader>
50
+ <SheetTitle className="flex items-center gap-2 text-base">
51
+ <Library className="size-4" />
52
+ {L.catalogue}
53
+ </SheetTitle>
54
+ <SheetDescription>{L.catalogueDescription}</SheetDescription>
55
+ </SheetHeader>
56
+
57
+ <ScrollArea className="flex-1 px-4 pb-4">
58
+ <div className="flex flex-col gap-6">
59
+ <section className="flex flex-col gap-2">
60
+ <CatalogueSectionHeader title={L.catalogueKeys} count={keyCatalog.length} />
61
+ {keyCatalog.length === 0 ? (
62
+ <p className="text-muted-foreground text-xs italic">No keys registered.</p>
63
+ ) : (
64
+ <ul className="flex flex-col gap-1">
65
+ {keyCatalog.map((entry) => (
66
+ <li
67
+ key={entry.key}
68
+ className="hover:bg-accent/40 flex flex-col gap-1 rounded-md border px-2 py-1.5 text-xs"
69
+ >
70
+ <div className="flex items-start justify-between gap-2">
71
+ <span className="truncate font-mono">{entry.key}</span>
72
+ <span
73
+ className={cn(
74
+ "inline-flex shrink-0 items-center gap-1 text-[10px]",
75
+ entry.inContext
76
+ ? "text-emerald-600 dark:text-emerald-400"
77
+ : "text-muted-foreground",
78
+ )}
79
+ >
80
+ <span
81
+ className={cn(
82
+ "size-1.5 rounded-full",
83
+ entry.inContext ? "bg-emerald-500" : "bg-muted-foreground/40",
84
+ )}
85
+ />
86
+ {entry.inContext ? L.catalogueLive : L.catalogueMissing}
87
+ </span>
88
+ </div>
89
+ {(entry.producedBySteps.length > 0 ||
90
+ entry.consumedBySteps.length > 0 ||
91
+ entry.consumedByWidgets.length > 0) && (
92
+ <div className="text-muted-foreground/80 flex flex-col gap-0.5 text-[10px]">
93
+ {entry.producedBySteps.length > 0 && (
94
+ <span>
95
+ {L.catalogueProducedBy}{" "}
96
+ <span className="font-mono">{entry.producedBySteps.join(", ")}</span>
97
+ </span>
98
+ )}
99
+ {entry.consumedBySteps.length > 0 && (
100
+ <span>
101
+ step {L.catalogueConsumedBy}{" "}
102
+ <span className="font-mono">{entry.consumedBySteps.join(", ")}</span>
103
+ </span>
104
+ )}
105
+ {entry.consumedByWidgets.length > 0 && (
106
+ <span>
107
+ widget {L.catalogueConsumedBy}{" "}
108
+ <span className="font-mono">
109
+ {entry.consumedByWidgets.join(", ")}
110
+ </span>
111
+ </span>
112
+ )}
113
+ </div>
114
+ )}
115
+ {!entry.inContext && (
116
+ <div className="mt-0.5 flex items-center gap-1">
117
+ <Button variant="ghost" size="xs" onClick={() => onSeedKey(entry.key)}>
118
+ <Plus /> {L.cataloguePickKey}
119
+ </Button>
120
+ {producerOf.get(entry.key) && (
121
+ <Button
122
+ variant="ghost"
123
+ size="xs"
124
+ onClick={() => onAddProducingStep(entry.key)}
125
+ >
126
+ <ArrowDownToLine /> {L.catalogueAddProducingStep}
127
+ </Button>
128
+ )}
129
+ </div>
130
+ )}
131
+ </li>
132
+ ))}
133
+ </ul>
134
+ )}
135
+ </section>
136
+
137
+ <section className="flex flex-col gap-2">
138
+ <CatalogueSectionHeader title={L.catalogueSteps} count={availableSteps.length} />
139
+ {availableSteps.length === 0 ? (
140
+ <p className="text-muted-foreground text-xs italic">No steps registered.</p>
141
+ ) : (
142
+ <ul className="flex flex-col gap-1">
143
+ {availableSteps.map((s) => (
144
+ <li
145
+ key={s.id}
146
+ className="flex flex-col gap-0.5 rounded-md border px-2 py-1.5 text-xs"
147
+ >
148
+ <span className="truncate font-mono">{s.id}</span>
149
+ <div className="text-muted-foreground/80 flex flex-wrap gap-x-3 text-[10px]">
150
+ <span className="font-mono">
151
+ ← {s.requires.length ? s.requires.join(", ") : "(no inputs)"}
152
+ </span>
153
+ <span className="font-mono">
154
+ → {s.produces.length ? s.produces.join(", ") : "(none)"}
155
+ </span>
156
+ </div>
157
+ </li>
158
+ ))}
159
+ </ul>
160
+ )}
161
+ </section>
162
+
163
+ <section className="flex flex-col gap-2">
164
+ <CatalogueSectionHeader
165
+ title={L.catalogueUnreachable}
166
+ count={unreachableWidgets.length}
167
+ />
168
+ {unreachableWidgets.length === 0 ? (
169
+ <p className="text-muted-foreground text-xs italic">
170
+ {L.catalogueUnreachableEmpty}
171
+ </p>
172
+ ) : (
173
+ <ul className="flex flex-col gap-1">
174
+ {unreachableWidgets.map((w) => {
175
+ const candidate = w.missingKeys
176
+ .map((k) => ({ key: k, step: producerOf.get(k) }))
177
+ .find((c) => c.step)
178
+ return (
179
+ <li
180
+ key={w.id}
181
+ className="bg-muted/20 flex flex-col gap-1 rounded-md border px-2 py-1.5 text-xs"
182
+ >
183
+ <span className="truncate font-mono">{w.id}</span>
184
+ <span className="text-muted-foreground/80 text-[10px]">
185
+ {L.catalogueNeedsKeys}{" "}
186
+ <span className="font-mono">{w.missingKeys.join(", ")}</span>
187
+ </span>
188
+ <div className="mt-0.5 flex flex-wrap items-center gap-1">
189
+ {w.missingKeys.map((k) => (
190
+ <Button key={k} variant="ghost" size="xs" onClick={() => onSeedKey(k)}>
191
+ <Plus />
192
+ <span className="font-mono">{k}</span>
193
+ </Button>
194
+ ))}
195
+ {candidate && (
196
+ <Button
197
+ variant="outline"
198
+ size="xs"
199
+ onClick={() => onAddProducingStep(candidate.key)}
200
+ >
201
+ <ArrowDownToLine /> {L.catalogueAddProducingStep}
202
+ </Button>
203
+ )}
204
+ </div>
205
+ </li>
206
+ )
207
+ })}
208
+ </ul>
209
+ )}
210
+ </section>
211
+ </div>
212
+ </ScrollArea>
213
+ </SheetContent>
214
+ </Sheet>
215
+ )
216
+ }
217
+
218
+ function CatalogueSectionHeader({ title, count }: { title: string; count: number }) {
219
+ return (
220
+ <div className="flex items-center gap-2">
221
+ <span className="text-muted-foreground text-[10px] font-semibold tracking-[0.12em] uppercase">
222
+ {title}
223
+ </span>
224
+ <span className="bg-border h-px flex-1" aria-hidden="true" />
225
+ <Badge variant="outline" className="px-1.5 text-[10px]">
226
+ {count}
227
+ </Badge>
228
+ </div>
229
+ )
230
+ }