@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,260 @@
1
+ import { memo, useMemo } from "react"
2
+ import { ArrowDownToLine, ArrowRightToLine, Plus, X } from "lucide-react"
3
+ import type { AvailableStep, KeyCatalogEntry, PipelineStepRef } from "@miragon/mcp-toolkit-core"
4
+ import { Badge } from "../../primitives/badge.js"
5
+ import { Button } from "../../primitives/button.js"
6
+ import { Input } from "../../primitives/input.js"
7
+ import {
8
+ Select,
9
+ SelectContent,
10
+ SelectItem,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ } from "../../primitives/select.js"
14
+ import { formatLiveKeyValue, valueTypeLabel, type KeyEntry } from "./builder-model.js"
15
+ import type { LayoutBuilderLabels, RefreshStatus } from "./labels.js"
16
+ import { StatusChip } from "./status-chip.js"
17
+
18
+ // -------------------------------------------------------------------------- //
19
+ // Pipeline strip — compact horizontal Keys + Steps
20
+ // -------------------------------------------------------------------------- //
21
+
22
+ function PipelineStripImpl({
23
+ L,
24
+ keyEntries,
25
+ stepEntries,
26
+ keyCatalog,
27
+ availableSteps,
28
+ stepsByApp,
29
+ liveKeys,
30
+ onAddKey,
31
+ onUpdateKey,
32
+ onRemoveKey,
33
+ onAddStep,
34
+ onUpdateStep,
35
+ onRemoveStep,
36
+ status,
37
+ }: {
38
+ L: Required<LayoutBuilderLabels>
39
+ keyEntries: KeyEntry[]
40
+ stepEntries: PipelineStepRef[]
41
+ keyCatalog: KeyCatalogEntry[]
42
+ availableSteps: AvailableStep[]
43
+ stepsByApp: [string, AvailableStep[]][]
44
+ liveKeys: Record<string, unknown>
45
+ onAddKey: (prefilledName?: string) => void
46
+ onUpdateKey: (idx: number, patch: Partial<KeyEntry>) => void
47
+ onRemoveKey: (idx: number) => void
48
+ onAddStep: (stepId?: string) => void
49
+ onUpdateStep: (idx: number, patch: Partial<PipelineStepRef>) => void
50
+ onRemoveStep: (idx: number) => void
51
+ status: RefreshStatus
52
+ }) {
53
+ const stepLookup = useMemo(() => {
54
+ const map = new Map<string, AvailableStep>()
55
+ for (const s of availableSteps) map.set(s.id, s)
56
+ return map
57
+ }, [availableSteps])
58
+
59
+ return (
60
+ <section className="bg-muted/30 -mx-1 rounded-lg border px-3 py-3">
61
+ <div className="mb-2 flex items-center gap-2">
62
+ <span className="text-muted-foreground text-[10px] font-semibold tracking-[0.12em] uppercase">
63
+ {L.pipelineHeader}
64
+ </span>
65
+ <span className="text-muted-foreground/60 text-[10px]">· auto-applies on edit</span>
66
+ <div className="ml-auto text-[11px]">
67
+ <StatusChip status={status} L={L} />
68
+ </div>
69
+ </div>
70
+
71
+ <div className="grid grid-cols-1 gap-x-6 gap-y-3 lg:grid-cols-2">
72
+ <div className="flex flex-col gap-1.5">
73
+ <div className="text-muted-foreground flex items-center gap-1.5 text-[11px] font-semibold">
74
+ <ArrowRightToLine className="size-3" />
75
+ {L.keysHeader}
76
+ <Badge variant="outline" className="px-1.5 text-[10px]">
77
+ {keyEntries.length}
78
+ </Badge>
79
+ </div>
80
+ {keyEntries.length === 0 ? (
81
+ <div className="text-muted-foreground rounded-md border border-dashed px-2 py-1.5 text-xs italic">
82
+ {L.emptyKeys}
83
+ </div>
84
+ ) : (
85
+ <ul className="flex flex-col gap-1">
86
+ {keyEntries.map((entry, idx) => {
87
+ const livePreview = formatLiveKeyValue(liveKeys[entry.name])
88
+ const usedNames = new Set(
89
+ keyEntries.map((e, i) => (i === idx ? "" : e.name)).filter(Boolean),
90
+ )
91
+ const pickable = keyCatalog.filter(
92
+ (c) => c.key === entry.name || !usedNames.has(c.key),
93
+ )
94
+ return (
95
+ <li key={idx} className="grid grid-cols-2 items-center gap-1">
96
+ <Select
97
+ value={entry.name || undefined}
98
+ onValueChange={(value) => onUpdateKey(idx, { name: value })}
99
+ >
100
+ <SelectTrigger className="h-7 min-w-0 font-mono text-xs">
101
+ <SelectValue placeholder={L.keyName} />
102
+ </SelectTrigger>
103
+ <SelectContent>
104
+ {pickable.length === 0 ? (
105
+ <div className="text-muted-foreground px-2 py-1.5 text-xs italic">
106
+ No more keys available.
107
+ </div>
108
+ ) : (
109
+ pickable.map((c) => (
110
+ <SelectItem key={c.key} value={c.key} className="py-1.5">
111
+ <div className="flex flex-col gap-0.5">
112
+ <span className="font-mono text-xs">{c.key}</span>
113
+ {c.consumedByWidgets.length + c.consumedBySteps.length > 0 && (
114
+ <span className="text-muted-foreground/80 font-mono text-[10px]">
115
+ → {[...c.consumedByWidgets, ...c.consumedBySteps].join(", ")}
116
+ </span>
117
+ )}
118
+ </div>
119
+ </SelectItem>
120
+ ))
121
+ )}
122
+ </SelectContent>
123
+ </Select>
124
+ <div className="flex min-w-0 items-center gap-1">
125
+ <div className="relative min-w-0 flex-1">
126
+ <Input
127
+ value={entry.rawValue}
128
+ onChange={(e) => onUpdateKey(idx, { rawValue: e.target.value })}
129
+ placeholder={livePreview ?? L.keyValue}
130
+ className="h-7 pr-10 font-mono text-xs"
131
+ />
132
+ <span
133
+ className="text-muted-foreground/70 pointer-events-none absolute top-1/2 right-2 -translate-y-1/2 font-mono text-[10px] tabular-nums"
134
+ title="parsed type"
135
+ >
136
+ {valueTypeLabel(entry.rawValue)}
137
+ </span>
138
+ </div>
139
+ <Button
140
+ variant="ghost"
141
+ size="icon-xs"
142
+ onClick={() => onRemoveKey(idx)}
143
+ aria-label="Remove key"
144
+ >
145
+ <X />
146
+ </Button>
147
+ </div>
148
+ </li>
149
+ )
150
+ })}
151
+ </ul>
152
+ )}
153
+ <Button
154
+ variant="ghost"
155
+ size="xs"
156
+ onClick={() => onAddKey()}
157
+ className="text-muted-foreground hover:text-foreground self-start"
158
+ >
159
+ <Plus /> {L.addKey}
160
+ </Button>
161
+ </div>
162
+
163
+ <div className="flex flex-col gap-1.5">
164
+ <div className="text-muted-foreground flex items-center gap-1.5 text-[11px] font-semibold">
165
+ <ArrowDownToLine className="size-3" />
166
+ {L.stepsHeader}
167
+ <Badge variant="outline" className="px-1.5 text-[10px]">
168
+ {stepEntries.length}
169
+ </Badge>
170
+ </div>
171
+ {stepEntries.length === 0 ? (
172
+ <div className="text-muted-foreground rounded-md border border-dashed px-2 py-1.5 text-xs italic">
173
+ {L.emptySteps}
174
+ </div>
175
+ ) : (
176
+ <ul className="flex flex-col gap-1">
177
+ {stepEntries.map((step, idx) => {
178
+ const meta = stepLookup.get(step.step)
179
+ return (
180
+ <li key={idx} className="flex flex-col gap-0.5">
181
+ <div className="flex items-center gap-1">
182
+ <Input
183
+ value={step.id}
184
+ onChange={(e) => onUpdateStep(idx, { id: e.target.value })}
185
+ placeholder={L.stepContextId}
186
+ className="h-7 w-24 font-mono text-xs"
187
+ />
188
+ <Select
189
+ value={step.step || undefined}
190
+ onValueChange={(value) => onUpdateStep(idx, { step: value })}
191
+ >
192
+ <SelectTrigger className="h-7 flex-1 text-xs">
193
+ <SelectValue placeholder={L.stepPickerLabel} />
194
+ </SelectTrigger>
195
+ <SelectContent>
196
+ {stepsByApp.map(([app, items]) => (
197
+ <div key={app}>
198
+ <div className="text-muted-foreground px-2 py-1 text-[10px] font-semibold tracking-wide uppercase">
199
+ {app}
200
+ </div>
201
+ {items.map((s) => (
202
+ <SelectItem key={s.id} value={s.id} className="py-2">
203
+ <div className="flex flex-col gap-0.5">
204
+ <span className="font-mono text-xs">{s.id}</span>
205
+ <div className="text-muted-foreground flex flex-wrap gap-x-2 text-[10px]">
206
+ <span className="font-mono">
207
+ ←{" "}
208
+ {s.requires.length ? s.requires.join(", ") : "(no inputs)"}
209
+ </span>
210
+ <span className="font-mono">
211
+ → {s.produces.length ? s.produces.join(", ") : "(none)"}
212
+ </span>
213
+ </div>
214
+ </div>
215
+ </SelectItem>
216
+ ))}
217
+ </div>
218
+ ))}
219
+ </SelectContent>
220
+ </Select>
221
+ <Button
222
+ variant="ghost"
223
+ size="icon-xs"
224
+ onClick={() => onRemoveStep(idx)}
225
+ aria-label="Remove step"
226
+ >
227
+ <X />
228
+ </Button>
229
+ </div>
230
+ {meta && (
231
+ <div className="text-muted-foreground/80 ml-1 flex flex-wrap gap-x-2 pl-24 text-[10px]">
232
+ <span className="font-mono">
233
+ ← {meta.requires.length ? meta.requires.join(", ") : "(no inputs)"}
234
+ </span>
235
+ <span className="font-mono">
236
+ → {meta.produces.length ? meta.produces.join(", ") : "(none)"}
237
+ </span>
238
+ </div>
239
+ )}
240
+ </li>
241
+ )
242
+ })}
243
+ </ul>
244
+ )}
245
+ <Button
246
+ variant="ghost"
247
+ size="xs"
248
+ onClick={() => onAddStep()}
249
+ disabled={availableSteps.length === 0}
250
+ className="text-muted-foreground hover:text-foreground self-start"
251
+ >
252
+ <Plus /> {L.addStep}
253
+ </Button>
254
+ </div>
255
+ </div>
256
+ </section>
257
+ )
258
+ }
259
+
260
+ export const PipelineStrip = memo(PipelineStripImpl)
@@ -0,0 +1,72 @@
1
+ import { memo } from "react"
2
+ import type { RowDef, WidgetProps } from "@miragon/mcp-toolkit-core"
3
+ import { GridItem, GridLayout } from "../../components/GridLayout.js"
4
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../primitives/tabs.js"
5
+ import { RenderedWidget, type WidgetComponent } from "../widget-renderer.js"
6
+ import type { DraftLayout } from "./builder-model.js"
7
+
8
+ // -------------------------------------------------------------------------- //
9
+ // Preview pane
10
+ // -------------------------------------------------------------------------- //
11
+
12
+ function PreviewPaneImpl({
13
+ draft,
14
+ activeTabIndex,
15
+ setActiveTabIndex,
16
+ widgets,
17
+ widgetProps,
18
+ }: {
19
+ draft: DraftLayout
20
+ activeTabIndex: number
21
+ setActiveTabIndex: (idx: number) => void
22
+ widgets: Record<string, WidgetComponent>
23
+ widgetProps: WidgetProps
24
+ }) {
25
+ const renderRows = (rs: RowDef[]) => (
26
+ <div className="flex flex-col gap-4">
27
+ {rs.map((row, rowIdx) => (
28
+ <GridLayout key={rowIdx}>
29
+ {row.row.map((cell, cellIdx) => (
30
+ <GridItem key={`${cell.widget}-${cellIdx}`} span={cell.span ?? 12}>
31
+ <RenderedWidget
32
+ widget={widgets[cell.widget]}
33
+ widgetId={cell.widget}
34
+ props={widgetProps}
35
+ cellProps={cell.props}
36
+ showEmptyHint
37
+ />
38
+ </GridItem>
39
+ ))}
40
+ </GridLayout>
41
+ ))}
42
+ </div>
43
+ )
44
+
45
+ if (draft.kind === "tabs") {
46
+ return (
47
+ <Tabs
48
+ value={draft.tabs[activeTabIndex]?.label ?? ""}
49
+ onValueChange={(label) => {
50
+ const idx = draft.tabs.findIndex((t) => t.label === label)
51
+ if (idx >= 0) setActiveTabIndex(idx)
52
+ }}
53
+ >
54
+ <TabsList>
55
+ {draft.tabs.map((tab) => (
56
+ <TabsTrigger key={tab.label} value={tab.label}>
57
+ {tab.label}
58
+ </TabsTrigger>
59
+ ))}
60
+ </TabsList>
61
+ {draft.tabs.map((tab) => (
62
+ <TabsContent key={tab.label} value={tab.label} className="mt-3">
63
+ {renderRows(tab.rows)}
64
+ </TabsContent>
65
+ ))}
66
+ </Tabs>
67
+ )
68
+ }
69
+ return renderRows(draft.rows)
70
+ }
71
+
72
+ export const PreviewPane = memo(PreviewPaneImpl)
@@ -0,0 +1,95 @@
1
+ import { AlertTriangle } from "lucide-react"
2
+ import { Button } from "../../primitives/button.js"
3
+ import {
4
+ Dialog,
5
+ DialogClose,
6
+ DialogContent,
7
+ DialogDescription,
8
+ DialogFooter,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ } from "../../primitives/dialog.js"
12
+ import { Input } from "../../primitives/input.js"
13
+ import type { LayoutBuilderLabels } from "./labels.js"
14
+
15
+ // -------------------------------------------------------------------------- //
16
+ // Save dialog
17
+ // -------------------------------------------------------------------------- //
18
+
19
+ export function SaveDialog({
20
+ L,
21
+ open,
22
+ onOpenChange,
23
+ name,
24
+ setName,
25
+ description,
26
+ setDescription,
27
+ onSubmit,
28
+ busy,
29
+ error,
30
+ }: {
31
+ L: Required<LayoutBuilderLabels>
32
+ open: boolean
33
+ onOpenChange: (open: boolean) => void
34
+ name: string
35
+ setName: (v: string) => void
36
+ description: string
37
+ setDescription: (v: string) => void
38
+ onSubmit: () => Promise<void>
39
+ busy: boolean
40
+ error: string | null
41
+ }) {
42
+ return (
43
+ <Dialog open={open} onOpenChange={onOpenChange}>
44
+ <DialogContent>
45
+ <DialogHeader>
46
+ <DialogTitle>{L.saveDialogTitle}</DialogTitle>
47
+ <DialogDescription>{L.saveDialogDescription}</DialogDescription>
48
+ </DialogHeader>
49
+ <div className="flex flex-col gap-3">
50
+ <label className="flex flex-col gap-1 text-sm">
51
+ <span className="font-medium">{L.saveNameLabel}</span>
52
+ <Input
53
+ value={name}
54
+ onChange={(e) => setName(e.target.value)}
55
+ placeholder="Invoice overview"
56
+ autoFocus
57
+ />
58
+ </label>
59
+ <label className="flex flex-col gap-1 text-sm">
60
+ <span className="font-medium">{L.saveDescriptionLabel}</span>
61
+ <Input
62
+ value={description}
63
+ onChange={(e) => setDescription(e.target.value)}
64
+ placeholder="Optional"
65
+ />
66
+ </label>
67
+ {error && (
68
+ <div
69
+ className="border-destructive/30 bg-destructive/10 text-destructive flex items-start gap-2 rounded-md border px-3 py-2 text-xs"
70
+ role="alert"
71
+ >
72
+ <AlertTriangle className="mt-0.5 size-3.5 shrink-0" aria-hidden="true" />
73
+ <span className="min-w-0 break-words">
74
+ <span className="font-medium">{L.saveError}:</span> {error}
75
+ </span>
76
+ </div>
77
+ )}
78
+ </div>
79
+ <DialogFooter>
80
+ <DialogClose asChild>
81
+ <Button variant="outline">{L.cancel}</Button>
82
+ </DialogClose>
83
+ <Button
84
+ disabled={!name.trim() || busy}
85
+ onClick={() => {
86
+ void onSubmit()
87
+ }}
88
+ >
89
+ {L.saveConfirm}
90
+ </Button>
91
+ </DialogFooter>
92
+ </DialogContent>
93
+ </Dialog>
94
+ )
95
+ }
@@ -0,0 +1,95 @@
1
+ import { memo } from "react"
2
+ import { Check, Eye, Layers, Library, Save } from "lucide-react"
3
+ import { Badge } from "../../primitives/badge.js"
4
+ import { Button } from "../../primitives/button.js"
5
+ import { cn } from "../../lib/utils.js"
6
+ import type { LayoutBuilderLabels, RefreshStatus } from "./labels.js"
7
+ import { StatusChip } from "./status-chip.js"
8
+
9
+ // -------------------------------------------------------------------------- //
10
+ // Toolbar — sticky top, dense status row + actions
11
+ // -------------------------------------------------------------------------- //
12
+
13
+ function ToolbarImpl({
14
+ L,
15
+ previewMode,
16
+ isBusy,
17
+ status,
18
+ liveKeys,
19
+ reachableCount,
20
+ unreachableCount,
21
+ onOpenCatalogue,
22
+ onOpenSave,
23
+ onDone,
24
+ hasAnyCell,
25
+ }: {
26
+ L: Required<LayoutBuilderLabels>
27
+ previewMode: boolean
28
+ isBusy: boolean
29
+ status: RefreshStatus
30
+ liveKeys: number
31
+ reachableCount: number
32
+ unreachableCount: number
33
+ onOpenCatalogue: () => void
34
+ onOpenSave: () => void
35
+ onDone?: () => void
36
+ hasAnyCell: boolean
37
+ }) {
38
+ return (
39
+ <header className="bg-background/80 sticky top-0 z-10 -mx-1 flex flex-wrap items-center gap-3 border-b px-1 py-2 backdrop-blur">
40
+ <div className="flex items-center gap-2">
41
+ <span
42
+ className={cn(
43
+ "inline-flex size-6 items-center justify-center rounded-md",
44
+ previewMode ? "bg-emerald-500/10 text-emerald-500" : "bg-primary/10 text-primary",
45
+ )}
46
+ aria-hidden="true"
47
+ >
48
+ {previewMode ? <Eye className="size-3.5" /> : <Layers className="size-3.5" />}
49
+ </span>
50
+ <h2 className="text-sm font-semibold tracking-tight">{L.title}</h2>
51
+ <Badge variant={previewMode ? "outline" : "secondary"} className="font-normal">
52
+ {previewMode ? L.previewBadge : L.builderBadge}
53
+ </Badge>
54
+ </div>
55
+
56
+ <div className="text-muted-foreground hidden items-center gap-3 text-[11px] sm:flex">
57
+ <span className="inline-flex items-center gap-1.5">
58
+ <span className="size-1.5 rounded-full bg-emerald-500" />
59
+ <span>
60
+ <span className="text-foreground font-medium">{liveKeys}</span> live
61
+ </span>
62
+ </span>
63
+ <span className="bg-border h-3 w-px" aria-hidden="true" />
64
+ <span>
65
+ <span className="text-foreground font-medium">{reachableCount}</span> reachable
66
+ </span>
67
+ {unreachableCount > 0 && (
68
+ <>
69
+ <span className="bg-border h-3 w-px" aria-hidden="true" />
70
+ <span>
71
+ <span className="text-foreground font-medium">{unreachableCount}</span> unreachable
72
+ </span>
73
+ </>
74
+ )}
75
+ <StatusChip status={status} L={L} />
76
+ </div>
77
+
78
+ <div className="ml-auto flex items-center gap-1">
79
+ <Button variant="ghost" size="sm" onClick={onOpenCatalogue}>
80
+ <Library /> {L.catalogue}
81
+ </Button>
82
+ <Button size="sm" onClick={onOpenSave} disabled={isBusy || !hasAnyCell}>
83
+ <Save /> {L.save}
84
+ </Button>
85
+ {onDone && (
86
+ <Button variant="outline" size="sm" onClick={onDone}>
87
+ <Check /> {L.done}
88
+ </Button>
89
+ )}
90
+ </div>
91
+ </header>
92
+ )
93
+ }
94
+
95
+ export const Toolbar = memo(ToolbarImpl)