@zendir/ui 0.5.0 → 0.6.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 (391) hide show
  1. package/CHANGELOG.md +419 -386
  2. package/README.md +640 -631
  3. package/dist/_virtual/_rolldown/runtime.js +13 -0
  4. package/dist/index.js +17 -221
  5. package/dist/react/3d/CesiumCaptureSource.d.ts +0 -1
  6. package/dist/react/3d/CesiumCaptureSource.js +364 -283
  7. package/dist/react/3d/CesiumCaptureSource.js.map +1 -1
  8. package/dist/react/3d/ZenSpace3D.d.ts +2 -3
  9. package/dist/react/3d/ZenSpace3D.js +1681 -1237
  10. package/dist/react/3d/ZenSpace3D.js.map +1 -1
  11. package/dist/react/3d/ZenSpace3DCesium.d.ts +0 -1
  12. package/dist/react/3d/ZenSpace3DCesium.js +513 -560
  13. package/dist/react/3d/ZenSpace3DCesium.js.map +1 -1
  14. package/dist/react/3d/ZenSpace3DShaders.js +61 -66
  15. package/dist/react/3d/ZenSpace3DShaders.js.map +1 -1
  16. package/dist/react/3d/ZenSpace3DTypes.d.ts +19 -20
  17. package/dist/react/3d/ZenSpace3DUtils.d.ts +0 -1
  18. package/dist/react/3d/ZenSpace3DUtils.js +37 -24
  19. package/dist/react/3d/ZenSpace3DUtils.js.map +1 -1
  20. package/dist/react/3d/capturePngAnalysis.js +42 -36
  21. package/dist/react/3d/capturePngAnalysis.js.map +1 -1
  22. package/dist/react/3d/index.d.ts +3 -3
  23. package/dist/react/3d/index.js +5 -53
  24. package/dist/react/3d/threeLoader.d.ts +1 -1
  25. package/dist/react/3d/threeLoader.js +23 -18
  26. package/dist/react/3d/threeLoader.js.map +1 -1
  27. package/dist/react/astro/ClassificationBanner.d.ts +2 -3
  28. package/dist/react/astro/ClassificationBanner.js +95 -79
  29. package/dist/react/astro/ClassificationBanner.js.map +1 -1
  30. package/dist/react/astro/GlobalStatusBar.d.ts +0 -1
  31. package/dist/react/astro/GlobalStatusBar.js +157 -163
  32. package/dist/react/astro/GlobalStatusBar.js.map +1 -1
  33. package/dist/react/astro/MissionClock.d.ts +7 -8
  34. package/dist/react/astro/MissionClock.js +377 -400
  35. package/dist/react/astro/MissionClock.js.map +1 -1
  36. package/dist/react/astro/MonitoringIcon.d.ts +1 -2
  37. package/dist/react/astro/MonitoringIcon.js +422 -358
  38. package/dist/react/astro/MonitoringIcon.js.map +1 -1
  39. package/dist/react/astro/Notification.d.ts +1 -2
  40. package/dist/react/astro/Notification.js +229 -126
  41. package/dist/react/astro/Notification.js.map +1 -1
  42. package/dist/react/astro/Progress.d.ts +0 -1
  43. package/dist/react/astro/Progress.js +199 -141
  44. package/dist/react/astro/Progress.js.map +1 -1
  45. package/dist/react/astro/SimulationControls.d.ts +3 -4
  46. package/dist/react/astro/SimulationControls.js +596 -650
  47. package/dist/react/astro/SimulationControls.js.map +1 -1
  48. package/dist/react/astro/StatusIndicator.d.ts +0 -1
  49. package/dist/react/astro/StatusIndicator.js +236 -170
  50. package/dist/react/astro/StatusIndicator.js.map +1 -1
  51. package/dist/react/astro/UnifiedTimeline.d.ts +9 -10
  52. package/dist/react/astro/UnifiedTimeline.js +2345 -2392
  53. package/dist/react/astro/UnifiedTimeline.js.map +1 -1
  54. package/dist/react/astro/index.d.ts +5 -5
  55. package/dist/react/astro/index.js +1 -25
  56. package/dist/react/cards/AccessCard.d.ts +0 -1
  57. package/dist/react/cards/AccessCard.js +471 -405
  58. package/dist/react/cards/AccessCard.js.map +1 -1
  59. package/dist/react/cards/OrbitCard.d.ts +0 -1
  60. package/dist/react/cards/OrbitCard.js +487 -364
  61. package/dist/react/cards/OrbitCard.js.map +1 -1
  62. package/dist/react/cards/SpacecraftCard.d.ts +0 -1
  63. package/dist/react/cards/SpacecraftCard.js +813 -925
  64. package/dist/react/cards/SpacecraftCard.js.map +1 -1
  65. package/dist/react/cards/TelemetryCard.d.ts +0 -1
  66. package/dist/react/cards/TelemetryCard.js +1055 -732
  67. package/dist/react/cards/TelemetryCard.js.map +1 -1
  68. package/dist/react/cards/TelemetryStreamCard.d.ts +0 -1
  69. package/dist/react/cards/TelemetryStreamCard.js +323 -297
  70. package/dist/react/cards/TelemetryStreamCard.js.map +1 -1
  71. package/dist/react/cards/index.d.ts +1 -1
  72. package/dist/react/cards/index.js +1 -8
  73. package/dist/react/charts/GroundTrackMap.d.ts +6 -7
  74. package/dist/react/charts/GroundTrackMap.js +1377 -1256
  75. package/dist/react/charts/GroundTrackMap.js.map +1 -1
  76. package/dist/react/charts/GroundTrackMapLeaflet.d.ts +1 -2
  77. package/dist/react/charts/GroundTrackMapLeaflet.js +1065 -987
  78. package/dist/react/charts/GroundTrackMapLeaflet.js.map +1 -1
  79. package/dist/react/charts/groundTrackMapLeafletTiles.js +34 -25
  80. package/dist/react/charts/groundTrackMapLeafletTiles.js.map +1 -1
  81. package/dist/react/charts/groundTrackMapLeafletUtils.js +70 -61
  82. package/dist/react/charts/groundTrackMapLeafletUtils.js.map +1 -1
  83. package/dist/react/charts/index.d.ts +1 -1
  84. package/dist/react/charts/index.js +7 -100
  85. package/dist/react/charts/unified/AstroChart.d.ts +1 -2
  86. package/dist/react/charts/unified/AstroChart.js +1331 -1411
  87. package/dist/react/charts/unified/AstroChart.js.map +1 -1
  88. package/dist/react/charts/unified/PowerOverviewChart.d.ts +3 -3
  89. package/dist/react/charts/unified/PowerOverviewChart.js +495 -478
  90. package/dist/react/charts/unified/PowerOverviewChart.js.map +1 -1
  91. package/dist/react/charts/unified/domain.d.ts +12 -13
  92. package/dist/react/charts/unified/domain.js +3999 -3145
  93. package/dist/react/charts/unified/domain.js.map +1 -1
  94. package/dist/react/charts/unified/generators.d.ts +3 -4
  95. package/dist/react/charts/unified/generators.js +561 -498
  96. package/dist/react/charts/unified/generators.js.map +1 -1
  97. package/dist/react/charts/unified/index.d.ts +1 -1
  98. package/dist/react/charts/unified/presets.d.ts +14 -15
  99. package/dist/react/charts/unified/presets.js +857 -994
  100. package/dist/react/charts/unified/presets.js.map +1 -1
  101. package/dist/react/charts/unified/sync.d.ts +2 -3
  102. package/dist/react/charts/unified/sync.js +229 -195
  103. package/dist/react/charts/unified/sync.js.map +1 -1
  104. package/dist/react/charts/unified/theme.d.ts +11 -12
  105. package/dist/react/charts/unified/theme.js +606 -642
  106. package/dist/react/charts/unified/theme.js.map +1 -1
  107. package/dist/react/charts/unified/types.d.ts +32 -33
  108. package/dist/react/charts/unified/useChartStream.d.ts +1 -2
  109. package/dist/react/charts/unified/useChartStream.js +203 -216
  110. package/dist/react/charts/unified/useChartStream.js.map +1 -1
  111. package/dist/react/chatgpt/AppCard.d.ts +1 -2
  112. package/dist/react/chatgpt/index.d.ts +7 -7
  113. package/dist/react/chatgpt/index.js +160 -139
  114. package/dist/react/chatgpt/index.js.map +1 -1
  115. package/dist/react/context/CategoryContext.d.ts +0 -1
  116. package/dist/react/context/CategoryContext.js +78 -29
  117. package/dist/react/context/CategoryContext.js.map +1 -1
  118. package/dist/react/context/DisplaySettingsContext.d.ts +1 -2
  119. package/dist/react/context/DisplaySettingsContext.js +205 -156
  120. package/dist/react/context/DisplaySettingsContext.js.map +1 -1
  121. package/dist/react/context/SpatialSelectionContext.d.ts +0 -1
  122. package/dist/react/context/SpatialSelectionContext.js +36 -7
  123. package/dist/react/context/SpatialSelectionContext.js.map +1 -1
  124. package/dist/react/core/data/DataTable.d.ts +3 -4
  125. package/dist/react/core/data/DataTable.js +473 -440
  126. package/dist/react/core/data/DataTable.js.map +1 -1
  127. package/dist/react/core/data/DataValue.d.ts +4 -5
  128. package/dist/react/core/data/DataValue.js +605 -571
  129. package/dist/react/core/data/DataValue.js.map +1 -1
  130. package/dist/react/core/data/propertyConfig.d.ts +1 -2
  131. package/dist/react/core/data/propertyConfig.js +446 -382
  132. package/dist/react/core/data/propertyConfig.js.map +1 -1
  133. package/dist/react/core/display/AstroIcon.d.ts +4 -5
  134. package/dist/react/core/display/AstroIcon.js +339 -429
  135. package/dist/react/core/display/AstroIcon.js.map +1 -1
  136. package/dist/react/core/display/Badge.d.ts +2 -3
  137. package/dist/react/core/display/Badge.js +131 -131
  138. package/dist/react/core/display/Badge.js.map +1 -1
  139. package/dist/react/core/display/CardHeader.d.ts +0 -1
  140. package/dist/react/core/display/CardHeader.js +398 -311
  141. package/dist/react/core/display/CardHeader.js.map +1 -1
  142. package/dist/react/core/display/Container.d.ts +2 -3
  143. package/dist/react/core/display/Container.js +640 -668
  144. package/dist/react/core/display/Container.js.map +1 -1
  145. package/dist/react/core/display/CopyButton.d.ts +1 -2
  146. package/dist/react/core/display/CopyButton.js +158 -101
  147. package/dist/react/core/display/CopyButton.js.map +1 -1
  148. package/dist/react/core/display/GlassCard.d.ts +3 -4
  149. package/dist/react/core/display/GlassCard.js +416 -369
  150. package/dist/react/core/display/GlassCard.js.map +1 -1
  151. package/dist/react/core/display/HeaderIconWithStatus.d.ts +0 -1
  152. package/dist/react/core/display/HeaderIconWithStatus.js +152 -152
  153. package/dist/react/core/display/HeaderIconWithStatus.js.map +1 -1
  154. package/dist/react/core/display/Icon.d.ts +2 -3
  155. package/dist/react/core/display/Icon.js +172 -143
  156. package/dist/react/core/display/Icon.js.map +1 -1
  157. package/dist/react/core/display/Typography.d.ts +24 -25
  158. package/dist/react/core/display/Typography.js +360 -178
  159. package/dist/react/core/display/Typography.js.map +1 -1
  160. package/dist/react/core/feedback/ConfirmDialog.d.ts +1 -2
  161. package/dist/react/core/feedback/ConfirmDialog.js +343 -304
  162. package/dist/react/core/feedback/ConfirmDialog.js.map +1 -1
  163. package/dist/react/core/feedback/Dialog.d.ts +2 -3
  164. package/dist/react/core/feedback/Dialog.js +189 -197
  165. package/dist/react/core/feedback/Dialog.js.map +1 -1
  166. package/dist/react/core/feedback/Toast.d.ts +2 -3
  167. package/dist/react/core/feedback/Toast.js +346 -207
  168. package/dist/react/core/feedback/Toast.js.map +1 -1
  169. package/dist/react/core/index.d.ts +26 -26
  170. package/dist/react/core/index.js +7 -119
  171. package/dist/react/core/inputs/Button.d.ts +2 -3
  172. package/dist/react/core/inputs/Button.js +281 -304
  173. package/dist/react/core/inputs/Button.js.map +1 -1
  174. package/dist/react/core/inputs/Checkbox.d.ts +2 -3
  175. package/dist/react/core/inputs/Checkbox.js +139 -127
  176. package/dist/react/core/inputs/Checkbox.js.map +1 -1
  177. package/dist/react/core/inputs/Input.d.ts +3 -4
  178. package/dist/react/core/inputs/Input.js +277 -285
  179. package/dist/react/core/inputs/Input.js.map +1 -1
  180. package/dist/react/core/inputs/LimitsBar.d.ts +1 -2
  181. package/dist/react/core/inputs/LimitsBar.js +271 -196
  182. package/dist/react/core/inputs/LimitsBar.js.map +1 -1
  183. package/dist/react/core/inputs/NumberInput.d.ts +2 -3
  184. package/dist/react/core/inputs/NumberInput.js +531 -503
  185. package/dist/react/core/inputs/NumberInput.js.map +1 -1
  186. package/dist/react/core/inputs/PinInput.d.ts +2 -3
  187. package/dist/react/core/inputs/PinInput.js +224 -207
  188. package/dist/react/core/inputs/PinInput.js.map +1 -1
  189. package/dist/react/core/inputs/Select.d.ts +1 -2
  190. package/dist/react/core/inputs/Select.js +298 -299
  191. package/dist/react/core/inputs/Select.js.map +1 -1
  192. package/dist/react/core/inputs/Toggle.d.ts +2 -3
  193. package/dist/react/core/inputs/Toggle.js +142 -148
  194. package/dist/react/core/inputs/Toggle.js.map +1 -1
  195. package/dist/react/core/layout/Box.d.ts +3 -4
  196. package/dist/react/core/layout/Box.js +118 -122
  197. package/dist/react/core/layout/Box.js.map +1 -1
  198. package/dist/react/core/layout/Center.d.ts +0 -1
  199. package/dist/react/core/layout/Center.js +45 -32
  200. package/dist/react/core/layout/Center.js.map +1 -1
  201. package/dist/react/core/layout/Divider.d.ts +1 -2
  202. package/dist/react/core/layout/Divider.js +112 -106
  203. package/dist/react/core/layout/Divider.js.map +1 -1
  204. package/dist/react/core/layout/Flex.d.ts +4 -5
  205. package/dist/react/core/layout/Flex.js +120 -124
  206. package/dist/react/core/layout/Flex.js.map +1 -1
  207. package/dist/react/core/layout/Grid.d.ts +5 -6
  208. package/dist/react/core/layout/Grid.js +131 -138
  209. package/dist/react/core/layout/Grid.js.map +1 -1
  210. package/dist/react/core/layout/Spacer.d.ts +0 -1
  211. package/dist/react/core/layout/Spacer.js +36 -29
  212. package/dist/react/core/layout/Spacer.js.map +1 -1
  213. package/dist/react/core/layout/Stack.d.ts +4 -5
  214. package/dist/react/core/layout/Stack.js +127 -70
  215. package/dist/react/core/layout/Stack.js.map +1 -1
  216. package/dist/react/core/layout/responsive.d.ts +1 -2
  217. package/dist/react/core/layout/responsive.js +25 -19
  218. package/dist/react/core/layout/responsive.js.map +1 -1
  219. package/dist/react/core/layout/useBreakpoint.d.ts +1 -1
  220. package/dist/react/core/layout/useBreakpoint.js +119 -66
  221. package/dist/react/core/layout/useBreakpoint.js.map +1 -1
  222. package/dist/react/core/navigation/AppBar.d.ts +1 -2
  223. package/dist/react/core/navigation/AppBar.js +827 -805
  224. package/dist/react/core/navigation/AppBar.js.map +1 -1
  225. package/dist/react/core/navigation/Pagination.d.ts +2 -3
  226. package/dist/react/core/navigation/Pagination.js +207 -187
  227. package/dist/react/core/navigation/Pagination.js.map +1 -1
  228. package/dist/react/core/navigation/SideNav.d.ts +5 -6
  229. package/dist/react/core/navigation/SideNav.js +855 -629
  230. package/dist/react/core/navigation/SideNav.js.map +1 -1
  231. package/dist/react/core/navigation/Tabs.d.ts +0 -1
  232. package/dist/react/core/navigation/Tabs.js +113 -119
  233. package/dist/react/core/navigation/Tabs.js.map +1 -1
  234. package/dist/react/core/overlays/Popover.d.ts +2 -3
  235. package/dist/react/core/overlays/Popover.js +303 -282
  236. package/dist/react/core/overlays/Popover.js.map +1 -1
  237. package/dist/react/core/overlays/SidePanel.d.ts +1 -2
  238. package/dist/react/core/overlays/SidePanel.js +217 -212
  239. package/dist/react/core/overlays/SidePanel.js.map +1 -1
  240. package/dist/react/core/overlays/Tooltip.d.ts +1 -2
  241. package/dist/react/core/overlays/Tooltip.js +170 -176
  242. package/dist/react/core/overlays/Tooltip.js.map +1 -1
  243. package/dist/react/core/widgets/ActivityPlanner.d.ts +0 -1
  244. package/dist/react/core/widgets/ActivityPlanner.js +855 -517
  245. package/dist/react/core/widgets/ActivityPlanner.js.map +1 -1
  246. package/dist/react/core/widgets/Capture.d.ts +2 -3
  247. package/dist/react/core/widgets/Capture.js +965 -779
  248. package/dist/react/core/widgets/Capture.js.map +1 -1
  249. package/dist/react/core/widgets/ChatPanel.d.ts +16 -17
  250. package/dist/react/core/widgets/ChatPanel.js +1734 -1034
  251. package/dist/react/core/widgets/ChatPanel.js.map +1 -1
  252. package/dist/react/core/widgets/ColorPickerPanel.d.ts +0 -1
  253. package/dist/react/core/widgets/ColorPickerPanel.js +298 -287
  254. package/dist/react/core/widgets/ColorPickerPanel.js.map +1 -1
  255. package/dist/react/core/widgets/CommandBuilder.d.ts +2 -3
  256. package/dist/react/core/widgets/CommandBuilder.js +624 -516
  257. package/dist/react/core/widgets/CommandBuilder.js.map +1 -1
  258. package/dist/react/core/widgets/ConnectionForm.d.ts +2 -3
  259. package/dist/react/core/widgets/ConnectionForm.js +528 -492
  260. package/dist/react/core/widgets/ConnectionForm.js.map +1 -1
  261. package/dist/react/core/widgets/FileExplorer.d.ts +3 -4
  262. package/dist/react/core/widgets/FileExplorer.js +617 -505
  263. package/dist/react/core/widgets/FileExplorer.js.map +1 -1
  264. package/dist/react/core/widgets/HexViewer.d.ts +2 -3
  265. package/dist/react/core/widgets/HexViewer.js +1353 -1091
  266. package/dist/react/core/widgets/HexViewer.js.map +1 -1
  267. package/dist/react/core/widgets/ImageFrame.d.ts +1 -2
  268. package/dist/react/core/widgets/ImageFrame.js +230 -193
  269. package/dist/react/core/widgets/ImageFrame.js.map +1 -1
  270. package/dist/react/core/widgets/ImageGallery.d.ts +2 -1
  271. package/dist/react/core/widgets/ImageGallery.js +380 -314
  272. package/dist/react/core/widgets/ImageGallery.js.map +1 -1
  273. package/dist/react/core/widgets/ImagePanel.d.ts +0 -1
  274. package/dist/react/core/widgets/ImagePanel.js +110 -101
  275. package/dist/react/core/widgets/ImagePanel.js.map +1 -1
  276. package/dist/react/core/widgets/LogViewer.d.ts +2 -3
  277. package/dist/react/core/widgets/LogViewer.js +711 -607
  278. package/dist/react/core/widgets/LogViewer.js.map +1 -1
  279. package/dist/react/core/widgets/MessageStream.d.ts +3 -4
  280. package/dist/react/core/widgets/MessageStream.js +514 -444
  281. package/dist/react/core/widgets/MessageStream.js.map +1 -1
  282. package/dist/react/core/widgets/MissionCalendar.d.ts +3 -4
  283. package/dist/react/core/widgets/MissionCalendar.js +1488 -1020
  284. package/dist/react/core/widgets/MissionCalendar.js.map +1 -1
  285. package/dist/react/core/widgets/PacketViewer.d.ts +2 -3
  286. package/dist/react/core/widgets/PacketViewer.js +550 -421
  287. package/dist/react/core/widgets/PacketViewer.js.map +1 -1
  288. package/dist/react/core/widgets/capture-placeholder.png.js +6 -5
  289. package/dist/react/core/widgets/capture-placeholder.png.js.map +1 -1
  290. package/dist/react/hooks/index.d.ts +5 -5
  291. package/dist/react/hooks/useAccessWindows.d.ts +0 -1
  292. package/dist/react/hooks/useCompactMode.d.ts +1 -1
  293. package/dist/react/hooks/useCompactMode.js +104 -58
  294. package/dist/react/hooks/useCompactMode.js.map +1 -1
  295. package/dist/react/hooks/useGroundTrackHistory.d.ts +0 -1
  296. package/dist/react/hooks/useSimulationScene.d.ts +1 -2
  297. package/dist/react/hooks/useSimulationScene.js +481 -374
  298. package/dist/react/hooks/useSimulationScene.js.map +1 -1
  299. package/dist/react/index.d.ts +3 -3
  300. package/dist/react/panels/LayerControlPanel.d.ts +1 -2
  301. package/dist/react/panels/LayerControlPanel.js +223 -180
  302. package/dist/react/panels/LayerControlPanel.js.map +1 -1
  303. package/dist/react/panels/ObjectInventoryPanel.d.ts +2 -3
  304. package/dist/react/panels/ObjectInventoryPanel.js +315 -250
  305. package/dist/react/panels/ObjectInventoryPanel.js.map +1 -1
  306. package/dist/react/panels/index.d.ts +1 -1
  307. package/dist/react/panels/index.js +1 -6
  308. package/dist/react/shared/ErrorBoundary.d.ts +1 -2
  309. package/dist/react/shared/ErrorBoundary.js +170 -139
  310. package/dist/react/shared/ErrorBoundary.js.map +1 -1
  311. package/dist/react/shared/Skeleton.d.ts +1 -2
  312. package/dist/react/shared/Skeleton.js +300 -301
  313. package/dist/react/shared/Skeleton.js.map +1 -1
  314. package/dist/react/theme/ThemeProvider.d.ts +2 -321
  315. package/dist/react/theme/ThemeProvider.js +313 -939
  316. package/dist/react/theme/ThemeProvider.js.map +1 -1
  317. package/dist/react/theme/cardAccent.d.ts +1 -2
  318. package/dist/react/theme/cardAccent.js +196 -126
  319. package/dist/react/theme/cardAccent.js.map +1 -1
  320. package/dist/react/theme/config.d.ts +0 -1
  321. package/dist/react/theme/config.js +47 -25
  322. package/dist/react/theme/config.js.map +1 -1
  323. package/dist/react/theme/index.d.ts +2 -2
  324. package/dist/react/theme/index.js +1 -16
  325. package/dist/react/theme/types.d.ts +332 -0
  326. package/dist/react/theme/types.js +39 -0
  327. package/dist/react/theme/types.js.map +1 -0
  328. package/dist/react/types.d.ts +8 -8
  329. package/dist/react/types.js +147 -47
  330. package/dist/react/types.js.map +1 -1
  331. package/dist/react/utils/categoryPalette.d.ts +0 -1
  332. package/dist/react/utils/categoryPalette.js +91 -102
  333. package/dist/react/utils/categoryPalette.js.map +1 -1
  334. package/dist/react/utils/index.d.ts +5 -5
  335. package/dist/react/utils/index.js +457 -371
  336. package/dist/react/utils/index.js.map +1 -1
  337. package/dist/react/visualizations/EclipseTimerCard.d.ts +0 -1
  338. package/dist/react/visualizations/LinkBudgetCard.d.ts +0 -1
  339. package/dist/react/visualizations/NavBallCard.d.ts +0 -1
  340. package/dist/react/visualizations/PropulsionCard.d.ts +1 -2
  341. package/dist/react/visualizations/SensorFootprintCard.d.ts +1 -2
  342. package/dist/react/visualizations/ThermalHeatmapCard.d.ts +0 -1
  343. package/dist/react/visualizations/index.d.ts +3 -3
  344. package/dist/react.d.ts +2 -1
  345. package/dist/react.js +17 -221
  346. package/dist/shaders/atmosphere.frag.js +6 -5
  347. package/dist/shaders/atmosphere.frag.js.map +1 -1
  348. package/dist/shaders/atmosphere.vert.js +6 -5
  349. package/dist/shaders/atmosphere.vert.js.map +1 -1
  350. package/dist/shaders/stars.frag.js +6 -5
  351. package/dist/shaders/stars.frag.js.map +1 -1
  352. package/dist/shaders/stars.vert.js +6 -5
  353. package/dist/shaders/stars.vert.js.map +1 -1
  354. package/dist/style.css +26 -3
  355. package/dist/tokens/core.d.ts +159 -0
  356. package/dist/tokens/core.js +144 -0
  357. package/dist/tokens/core.js.map +1 -0
  358. package/dist/tokens/css-vars.d.ts +213 -2
  359. package/dist/tokens/css-vars.js +590 -227
  360. package/dist/tokens/css-vars.js.map +1 -1
  361. package/dist/tokens/index.d.ts +31 -228
  362. package/dist/tokens/index.js +281 -366
  363. package/dist/tokens/index.js.map +1 -1
  364. package/dist/tokens/palettes/astro.d.ts +129 -0
  365. package/dist/tokens/palettes/astro.js +112 -0
  366. package/dist/tokens/palettes/astro.js.map +1 -0
  367. package/dist/tokens/palettes/index.d.ts +288 -0
  368. package/dist/tokens/palettes/index.js +35 -0
  369. package/dist/tokens/palettes/index.js.map +1 -0
  370. package/dist/tokens/palettes/tailwind.d.ts +324 -0
  371. package/dist/tokens/palettes/tailwind.js +201 -0
  372. package/dist/tokens/palettes/tailwind.js.map +1 -0
  373. package/dist/tokens/palettes/zendir.d.ts +126 -0
  374. package/dist/tokens/palettes/zendir.js +92 -0
  375. package/dist/tokens/palettes/zendir.js.map +1 -0
  376. package/dist/tokens/themes.d.ts +30 -0
  377. package/dist/tokens/themes.js +704 -0
  378. package/dist/tokens/themes.js.map +1 -0
  379. package/dist/tokens/tokens.css +533 -155
  380. package/dist/types/index.d.ts +2 -2
  381. package/package.json +310 -313
  382. package/dist/index.js.map +0 -1
  383. package/dist/react/3d/index.js.map +0 -1
  384. package/dist/react/astro/index.js.map +0 -1
  385. package/dist/react/cards/index.js.map +0 -1
  386. package/dist/react/charts/index.js.map +0 -1
  387. package/dist/react/core/index.js.map +0 -1
  388. package/dist/react/panels/index.js.map +0 -1
  389. package/dist/react/theme/astro-tokens.d.ts +0 -153
  390. package/dist/react/theme/index.js.map +0 -1
  391. package/dist/react.js.map +0 -1
@@ -1,357 +1,657 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import React, { memo, useState, useRef, useEffect, useCallback } from "react";
3
1
  import { useTheme } from "../../theme/ThemeProvider.js";
4
2
  import { safeAccentText } from "../../utils/index.js";
3
+ import React, { memo, useCallback, useEffect, useRef, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/react/core/widgets/ChatPanel.tsx
6
+ /**
7
+ * @zendir/ui - ChatPanel Component
8
+ *
9
+ * Status-aware chat interface for operator dashboards with AI/command assistance.
10
+ * Supports structured message blocks (alerts, telemetry, status cards, data tables)
11
+ * alongside plain text, markdown, and code.
12
+ *
13
+ * Astro UX Compliance:
14
+ * - 6-level status system with dual-coded shapes (color + shape)
15
+ * - Glass-morphism for message bubbles
16
+ * - Monospace for code/command content
17
+ * - Focus management and keyboard shortcuts
18
+ *
19
+ * AI Integration:
20
+ * - Messages can include structured `blocks` that the AI emits
21
+ * - Four integration strategies: Tool/Function Calling, YAML, JSON, and MCP
22
+ * - Tool/Function calling schemas for OpenAI, Anthropic, Gemini, and MCP servers
23
+ * - `parseChatResponse()` — universal parser for JSON, YAML, and plain text
24
+ * - `parseMcpToolResult()` — bridge MCP tool results to ChatPanel blocks
25
+ * - `CHAT_RESPONSE_TOOL_SCHEMA` — drop-in function schema for LLM API calls
26
+ * - `CHAT_RESPONSE_MCP_TOOL` — MCP server tool definition for structured responses
27
+ * - `CHAT_RESPONSE_YAML_PROMPT` — system prompt snippet for YAML mode
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * <ChatPanel
32
+ * messages={messages}
33
+ * onSend={(text) => handleSend(text)}
34
+ * loading={isStreaming}
35
+ * placeholder="Ask about telemetry..."
36
+ * title="AI Assistant"
37
+ * />
38
+ * ```
39
+ */
40
+ /**
41
+ * Lightweight recursive-descent YAML parser for AI response structures.
42
+ *
43
+ * Handles: mappings, sequences, nested structures, scalars (string, number,
44
+ * boolean, null), quoted strings, inline JSON arrays/objects, comments.
45
+ *
46
+ * Does NOT handle: multi-line strings, anchors/aliases, tags, flow collections
47
+ * across lines, merge keys, or other advanced YAML features.
48
+ *
49
+ * For production with complex YAML, plug in `js-yaml` or `yaml` via
50
+ * `createChatResponseParser({ yamlParser })`.
51
+ */
5
52
  function parseYamlLite(text) {
6
- const lines = [];
7
- for (const raw of text.split("\n")) {
8
- const content = raw.trimEnd();
9
- if (content.trim() === "" || content.trim().startsWith("#")) continue;
10
- lines.push({ indent: content.search(/\S/), text: content.trim() });
11
- }
12
- let pos = 0;
13
- const peek = () => pos < lines.length ? lines[pos] : null;
14
- const advance = () => lines[pos++];
15
- function scalar(s) {
16
- const v = s.trim();
17
- if (v === "" || v === "null" || v === "~") return null;
18
- if (v === "true") return true;
19
- if (v === "false") return false;
20
- if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(v)) return Number(v);
21
- if (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'"))
22
- return v.slice(1, -1);
23
- if (v.startsWith("[") && v.endsWith("]") || v.startsWith("{") && v.endsWith("}")) {
24
- try {
25
- return JSON.parse(v);
26
- } catch {
27
- }
28
- }
29
- return v;
30
- }
31
- function parseValue(minIndent) {
32
- const line = peek();
33
- if (!line || line.indent < minIndent) return void 0;
34
- const actualIndent = line.indent;
35
- if (line.text.startsWith("- ")) return parseSequence(actualIndent);
36
- return parseMapping(actualIndent);
37
- }
38
- function parseSequence(level) {
39
- const arr = [];
40
- while (peek() && peek().indent === level && peek().text.startsWith("- ")) {
41
- const line = advance();
42
- const after = line.text.slice(2).trim();
43
- if (!after) {
44
- const val = parseValue(level + 1);
45
- arr.push(val ?? null);
46
- continue;
47
- }
48
- const colonIdx = after.indexOf(":");
49
- if (colonIdx > 0 && !/^['"]/.test(after)) {
50
- const obj = {};
51
- const k = after.slice(0, colonIdx).trim();
52
- const v = after.slice(colonIdx + 1).trim();
53
- obj[k] = v ? scalar(v) : parseValue(level + 3) ?? null;
54
- const siblingIndent = level + 2;
55
- while (peek() && peek().indent >= siblingIndent) {
56
- if (peek().indent === siblingIndent && !peek().text.startsWith("- ")) {
57
- const next = advance();
58
- const ci = next.text.indexOf(":");
59
- if (ci > 0) {
60
- const nk = next.text.slice(0, ci).trim();
61
- const nv = next.text.slice(ci + 1).trim();
62
- obj[nk] = nv ? scalar(nv) : parseValue(siblingIndent + 1) ?? null;
63
- }
64
- } else if (peek().indent > siblingIndent) {
65
- advance();
66
- } else {
67
- break;
68
- }
69
- }
70
- arr.push(obj);
71
- } else {
72
- arr.push(scalar(after));
73
- }
74
- }
75
- return arr;
76
- }
77
- function parseMapping(level) {
78
- const obj = {};
79
- while (peek() && peek().indent === level && !peek().text.startsWith("- ")) {
80
- const line = advance();
81
- const colonIdx = line.text.indexOf(":");
82
- if (colonIdx <= 0) continue;
83
- const k = line.text.slice(0, colonIdx).trim();
84
- const v = line.text.slice(colonIdx + 1).trim();
85
- obj[k] = v ? scalar(v) : parseValue(level + 1) ?? null;
86
- }
87
- return obj;
88
- }
89
- try {
90
- return parseValue(0) ?? {};
91
- } catch {
92
- return {};
93
- }
53
+ const lines = [];
54
+ for (const raw of text.split("\n")) {
55
+ const content = raw.trimEnd();
56
+ if (content.trim() === "" || content.trim().startsWith("#")) continue;
57
+ lines.push({
58
+ indent: content.search(/\S/),
59
+ text: content.trim()
60
+ });
61
+ }
62
+ let pos = 0;
63
+ const peek = () => pos < lines.length ? lines[pos] : null;
64
+ const advance = () => lines[pos++];
65
+ function scalar(s) {
66
+ const v = s.trim();
67
+ if (v === "" || v === "null" || v === "~") return null;
68
+ if (v === "true") return true;
69
+ if (v === "false") return false;
70
+ if (/^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(v)) return Number(v);
71
+ if (v.startsWith("\"") && v.endsWith("\"") || v.startsWith("'") && v.endsWith("'")) return v.slice(1, -1);
72
+ if (v.startsWith("[") && v.endsWith("]") || v.startsWith("{") && v.endsWith("}")) try {
73
+ return JSON.parse(v);
74
+ } catch {}
75
+ return v;
76
+ }
77
+ function parseValue(minIndent) {
78
+ const line = peek();
79
+ if (!line || line.indent < minIndent) return void 0;
80
+ const actualIndent = line.indent;
81
+ if (line.text.startsWith("- ")) return parseSequence(actualIndent);
82
+ return parseMapping(actualIndent);
83
+ }
84
+ function parseSequence(level) {
85
+ const arr = [];
86
+ while (peek() && peek().indent === level && peek().text.startsWith("- ")) {
87
+ const after = advance().text.slice(2).trim();
88
+ if (!after) {
89
+ const val = parseValue(level + 1);
90
+ arr.push(val ?? null);
91
+ continue;
92
+ }
93
+ const colonIdx = after.indexOf(":");
94
+ if (colonIdx > 0 && !/^['"]/.test(after)) {
95
+ const obj = {};
96
+ const k = after.slice(0, colonIdx).trim();
97
+ const v = after.slice(colonIdx + 1).trim();
98
+ obj[k] = v ? scalar(v) : parseValue(level + 3) ?? null;
99
+ const siblingIndent = level + 2;
100
+ while (peek() && peek().indent >= siblingIndent) if (peek().indent === siblingIndent && !peek().text.startsWith("- ")) {
101
+ const next = advance();
102
+ const ci = next.text.indexOf(":");
103
+ if (ci > 0) {
104
+ const nk = next.text.slice(0, ci).trim();
105
+ const nv = next.text.slice(ci + 1).trim();
106
+ obj[nk] = nv ? scalar(nv) : parseValue(siblingIndent + 1) ?? null;
107
+ }
108
+ } else if (peek().indent > siblingIndent) advance();
109
+ else break;
110
+ arr.push(obj);
111
+ } else arr.push(scalar(after));
112
+ }
113
+ return arr;
114
+ }
115
+ function parseMapping(level) {
116
+ const obj = {};
117
+ while (peek() && peek().indent === level && !peek().text.startsWith("- ")) {
118
+ const line = advance();
119
+ const colonIdx = line.text.indexOf(":");
120
+ if (colonIdx <= 0) continue;
121
+ const k = line.text.slice(0, colonIdx).trim();
122
+ const v = line.text.slice(colonIdx + 1).trim();
123
+ obj[k] = v ? scalar(v) : parseValue(level + 1) ?? null;
124
+ }
125
+ return obj;
126
+ }
127
+ try {
128
+ return parseValue(0) ?? {};
129
+ } catch {
130
+ return {};
131
+ }
94
132
  }
133
+ /** Ensure parsed blocks have the correct array shapes so renderers never crash. */
95
134
  function sanitizeBlocks(blocks) {
96
- if (!Array.isArray(blocks)) return void 0;
97
- return blocks.filter((b) => b != null && typeof b === "object" && typeof b.type === "string").map((b) => {
98
- const block = { ...b };
99
- switch (block.type) {
100
- case "telemetry":
101
- if (!Array.isArray(block.items)) block.items = [];
102
- break;
103
- case "table":
104
- if (!Array.isArray(block.columns)) block.columns = [];
105
- if (!Array.isArray(block.rows)) block.rows = [];
106
- break;
107
- case "actions":
108
- if (!Array.isArray(block.buttons)) block.buttons = [];
109
- break;
110
- case "choice":
111
- if (!Array.isArray(block.options)) block.options = [];
112
- if (typeof block.id !== "string") block.id = `choice-${Date.now()}`;
113
- break;
114
- case "confirm":
115
- if (typeof block.id !== "string") block.id = `confirm-${Date.now()}`;
116
- if (typeof block.title !== "string") block.title = "";
117
- break;
118
- case "command":
119
- if (typeof block.command !== "string") block.command = "";
120
- break;
121
- case "kv":
122
- if (!Array.isArray(block.pairs)) block.pairs = [];
123
- break;
124
- case "alert":
125
- if (typeof block.title !== "string") block.title = "";
126
- if (typeof block.status !== "string") block.status = "normal";
127
- break;
128
- case "progress":
129
- if (typeof block.label !== "string") block.label = "";
130
- if (typeof block.value !== "number") block.value = 0;
131
- break;
132
- }
133
- return block;
134
- });
135
+ if (!Array.isArray(blocks)) return void 0;
136
+ return blocks.filter((b) => b != null && typeof b === "object" && typeof b.type === "string").map((b) => {
137
+ const block = { ...b };
138
+ switch (block.type) {
139
+ case "telemetry":
140
+ if (!Array.isArray(block.items)) block.items = [];
141
+ break;
142
+ case "table":
143
+ if (!Array.isArray(block.columns)) block.columns = [];
144
+ if (!Array.isArray(block.rows)) block.rows = [];
145
+ break;
146
+ case "actions":
147
+ if (!Array.isArray(block.buttons)) block.buttons = [];
148
+ break;
149
+ case "choice":
150
+ if (!Array.isArray(block.options)) block.options = [];
151
+ if (typeof block.id !== "string") block.id = `choice-${Date.now()}`;
152
+ break;
153
+ case "confirm":
154
+ if (typeof block.id !== "string") block.id = `confirm-${Date.now()}`;
155
+ if (typeof block.title !== "string") block.title = "";
156
+ break;
157
+ case "command":
158
+ if (typeof block.command !== "string") block.command = "";
159
+ break;
160
+ case "kv":
161
+ if (!Array.isArray(block.pairs)) block.pairs = [];
162
+ break;
163
+ case "alert":
164
+ if (typeof block.title !== "string") block.title = "";
165
+ if (typeof block.status !== "string") block.status = "normal";
166
+ break;
167
+ case "progress":
168
+ if (typeof block.label !== "string") block.label = "";
169
+ if (typeof block.value !== "number") block.value = 0;
170
+ break;
171
+ }
172
+ return block;
173
+ });
135
174
  }
136
175
  function generateId(custom) {
137
- if (custom) return custom();
138
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
139
- return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
176
+ if (custom) return custom();
177
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
178
+ return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
140
179
  }
141
180
  function parseRaw(raw, format, yamlParser) {
142
- const tryJSON = (s) => {
143
- try {
144
- const parsed = JSON.parse(s);
145
- if (parsed && typeof parsed === "object" && typeof parsed.content === "string") return parsed;
146
- return null;
147
- } catch {
148
- return null;
149
- }
150
- };
151
- const tryYAML = (s) => {
152
- try {
153
- const parser = yamlParser ?? parseYamlLite;
154
- const parsed = parser(s);
155
- if (parsed && typeof parsed === "object" && typeof parsed.content === "string")
156
- return parsed;
157
- return null;
158
- } catch {
159
- return null;
160
- }
161
- };
162
- if (format === "json") return tryJSON(raw);
163
- if (format === "yaml") return tryYAML(raw);
164
- return tryJSON(raw) ?? tryYAML(raw);
181
+ const tryJSON = (s) => {
182
+ try {
183
+ const parsed = JSON.parse(s);
184
+ if (parsed && typeof parsed === "object" && typeof parsed.content === "string") return parsed;
185
+ return null;
186
+ } catch {
187
+ return null;
188
+ }
189
+ };
190
+ const tryYAML = (s) => {
191
+ try {
192
+ const parsed = (yamlParser ?? parseYamlLite)(s);
193
+ if (parsed && typeof parsed === "object" && typeof parsed.content === "string") return parsed;
194
+ return null;
195
+ } catch {
196
+ return null;
197
+ }
198
+ };
199
+ if (format === "json") return tryJSON(raw);
200
+ if (format === "yaml") return tryYAML(raw);
201
+ return tryJSON(raw) ?? tryYAML(raw);
165
202
  }
203
+ /**
204
+ * Parse an AI response string into a `ChatMessage`.
205
+ *
206
+ * Supports three strategies (controlled by `format`):
207
+ * - **json**: Parse as JSON. Falls back to plain text on failure.
208
+ * - **yaml**: Parse as YAML (built-in lightweight parser). ~30-40% fewer tokens.
209
+ * - **auto** (default): Try JSON → YAML → plain text.
210
+ *
211
+ * All parsed blocks are sanitized to ensure required arrays exist, preventing
212
+ * runtime crashes from malformed AI output.
213
+ *
214
+ * ## Why YAML?
215
+ * For the same structured response, YAML uses ~30-40% fewer tokens:
216
+ * ```
217
+ * JSON (~180 tokens): {"content":"Battery low.","blocks":[{"type":"alert",...}]}
218
+ * YAML (~120 tokens): content: Battery low.\nblocks:\n - type: alert\n ...
219
+ * ```
220
+ *
221
+ * ## Tool/Function Calling (recommended for production)
222
+ * Pass `CHAT_RESPONSE_TOOL_SCHEMA` in your API call. The model is constrained
223
+ * at the decoding level — zero prompt tokens for the schema.
224
+ *
225
+ * ## Custom YAML parser
226
+ * Use `createChatResponseParser({ yamlParser })` to plug in `js-yaml` or `yaml`.
227
+ *
228
+ * @example
229
+ * ```ts
230
+ * const msg = parseChatResponse(aiOutput); // auto-detect
231
+ * const msg = parseChatResponse(aiOutput, 'yaml'); // force YAML
232
+ * const msg = parseChatResponse(toolCallArgs, 'json'); // from function call
233
+ * ```
234
+ */
166
235
  function parseChatResponse(raw, format = "auto", options) {
167
- const id = generateId(options == null ? void 0 : options.idGenerator);
168
- const base = {
169
- id,
170
- role: "assistant",
171
- timestamp: Date.now()
172
- };
173
- const payload = parseRaw(raw, format, options == null ? void 0 : options.yamlParser);
174
- if (payload) {
175
- return {
176
- ...base,
177
- content: payload.content ?? "",
178
- severity: payload.severity,
179
- blocks: sanitizeBlocks(payload.blocks)
180
- };
181
- }
182
- return { ...base, content: raw };
236
+ const base = {
237
+ id: generateId(options?.idGenerator),
238
+ role: "assistant",
239
+ timestamp: Date.now()
240
+ };
241
+ const payload = parseRaw(raw, format, options?.yamlParser);
242
+ if (payload) return {
243
+ ...base,
244
+ content: payload.content ?? "",
245
+ severity: payload.severity,
246
+ blocks: sanitizeBlocks(payload.blocks)
247
+ };
248
+ return {
249
+ ...base,
250
+ content: raw
251
+ };
183
252
  }
253
+ /**
254
+ * Create a pre-configured response parser.
255
+ *
256
+ * Use this to set global defaults (format, YAML parser, ID generator) so you
257
+ * don't have to pass options on every call.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * import yaml from 'js-yaml';
262
+ * import { createChatResponseParser } from '@zendir/ui';
263
+ *
264
+ * // Create once at app init
265
+ * export const parseAI = createChatResponseParser({
266
+ * yamlParser: (s) => yaml.load(s),
267
+ * defaultFormat: 'yaml',
268
+ * });
269
+ *
270
+ * // Use everywhere
271
+ * const msg = parseAI(aiOutput);
272
+ * ```
273
+ */
184
274
  function createChatResponseParser(options = {}) {
185
- return (raw, format) => {
186
- return parseChatResponse(raw, format ?? options.defaultFormat ?? "auto", options);
187
- };
275
+ return (raw, format) => {
276
+ return parseChatResponse(raw, format ?? options.defaultFormat ?? "auto", options);
277
+ };
188
278
  }
279
+ /**
280
+ * Convert an MCP tool call result into a `ChatMessage` with structured blocks.
281
+ *
282
+ * Handles three patterns automatically:
283
+ * 1. **Structured JSON/YAML** — Tool returns text matching `ChatResponsePayload` → parsed into typed blocks
284
+ * 2. **Plain text** — Rendered as a simple assistant message with tool metadata
285
+ * 3. **Error** — Rendered as a critical alert block
286
+ *
287
+ * This bridges the MCP protocol with ChatPanel's block rendering system, so any
288
+ * MCP server tool can produce rich operator UIs without custom rendering code.
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * import { parseMcpToolResult } from '@zendir/ui/react';
293
+ *
294
+ * // With use-mcp hook
295
+ * const { callTool } = useMcp({ url: 'https://ops-server.example.com/mcp' });
296
+ * const result = await callTool('get_satellite_health', { id: 'SAT-001' });
297
+ * const msg = parseMcpToolResult('get_satellite_health', result);
298
+ * setMessages(prev => [...prev, msg]);
299
+ *
300
+ * // With @modelcontextprotocol/sdk
301
+ * const result = await mcpClient.callTool({ name: 'get_satellite_health', arguments: { id: 'SAT-001' } });
302
+ * const msg = parseMcpToolResult('get_satellite_health', result);
303
+ * ```
304
+ */
189
305
  function parseMcpToolResult(toolName, result, options) {
190
- var _a, _b;
191
- const id = generateId(options == null ? void 0 : options.idGenerator);
192
- const timestamp = Date.now();
193
- if (result.isError) {
194
- const errorText = ((_a = result.content) == null ? void 0 : _a.filter((c) => c.type === "text").map((c) => c.text).join("\n")) || `Tool "${toolName}" failed`;
195
- return {
196
- id,
197
- role: "assistant",
198
- timestamp,
199
- severity: "critical",
200
- content: errorText,
201
- blocks: [{ type: "alert", status: "critical", title: `MCP Tool Error: ${toolName}`, detail: errorText }]
202
- };
203
- }
204
- const textParts = ((_b = result.content) == null ? void 0 : _b.filter((c) => c.type === "text").map((c) => c.text ?? "")) ?? [];
205
- const rawText = textParts.join("\n");
206
- if (rawText.trim()) {
207
- const payload = parseRaw(rawText, "auto");
208
- if (payload && payload.content) {
209
- return {
210
- id,
211
- role: "assistant",
212
- timestamp,
213
- content: payload.content,
214
- severity: payload.severity,
215
- blocks: sanitizeBlocks(payload.blocks)
216
- };
217
- }
218
- }
219
- return {
220
- id,
221
- role: "assistant",
222
- timestamp,
223
- content: rawText || `Tool \`${toolName}\` completed successfully.`,
224
- blocks: [{
225
- type: "kv",
226
- title: "MCP Tool Call",
227
- pairs: [
228
- { key: "Tool", value: toolName, mono: true },
229
- ...result._meta ? [{ key: "Meta", value: JSON.stringify(result._meta), mono: true }] : []
230
- ]
231
- }]
232
- };
306
+ const id = generateId(options?.idGenerator);
307
+ const timestamp = Date.now();
308
+ if (result.isError) {
309
+ const errorText = result.content?.filter((c) => c.type === "text").map((c) => c.text).join("\n") || `Tool "${toolName}" failed`;
310
+ return {
311
+ id,
312
+ role: "assistant",
313
+ timestamp,
314
+ severity: "critical",
315
+ content: errorText,
316
+ blocks: [{
317
+ type: "alert",
318
+ status: "critical",
319
+ title: `MCP Tool Error: ${toolName}`,
320
+ detail: errorText
321
+ }]
322
+ };
323
+ }
324
+ const rawText = (result.content?.filter((c) => c.type === "text").map((c) => c.text ?? "") ?? []).join("\n");
325
+ if (rawText.trim()) {
326
+ const payload = parseRaw(rawText, "auto");
327
+ if (payload && payload.content) return {
328
+ id,
329
+ role: "assistant",
330
+ timestamp,
331
+ content: payload.content,
332
+ severity: payload.severity,
333
+ blocks: sanitizeBlocks(payload.blocks)
334
+ };
335
+ }
336
+ return {
337
+ id,
338
+ role: "assistant",
339
+ timestamp,
340
+ content: rawText || `Tool \`${toolName}\` completed successfully.`,
341
+ blocks: [{
342
+ type: "kv",
343
+ title: "MCP Tool Call",
344
+ pairs: [{
345
+ key: "Tool",
346
+ value: toolName,
347
+ mono: true
348
+ }, ...result._meta ? [{
349
+ key: "Meta",
350
+ value: JSON.stringify(result._meta),
351
+ mono: true
352
+ }] : []]
353
+ }]
354
+ };
233
355
  }
234
- const CHAT_RESPONSE_MCP_TOOL = {
235
- name: "respond_to_operator",
236
- description: "Respond to the spacecraft operator with text content and optional structured UI blocks (alerts, telemetry, progress, tables, choices, confirmations, commands, key-value metadata). Use the Astro UX 6-level status system: normal, standby, caution, serious, critical, off.",
237
- inputSchema: {
238
- type: "object",
239
- required: ["content"],
240
- properties: {
241
- content: { type: "string", description: "Plain-text or markdown response shown to the operator." },
242
- severity: { type: "string", enum: ["normal", "standby", "caution", "serious", "critical", "off"] },
243
- blocks: {
244
- type: "array",
245
- description: "Structured UI blocks rendered below the text content.",
246
- items: {
247
- type: "object",
248
- required: ["type"],
249
- properties: {
250
- type: { type: "string", enum: ["alert", "telemetry", "progress", "table", "choice", "confirm", "command", "kv"] },
251
- status: { type: "string", enum: ["normal", "standby", "caution", "serious", "critical", "off"] },
252
- title: { type: "string" },
253
- detail: { type: "string" },
254
- label: { type: "string" },
255
- value: { type: "number" },
256
- id: { type: "string" },
257
- command: { type: "string" },
258
- executable: { type: "boolean" },
259
- items: { type: "array", items: { type: "object", properties: { label: { type: "string" }, value: {}, unit: { type: "string" }, status: { type: "string" } } } },
260
- columns: { type: "array", items: { type: "string" } },
261
- rows: { type: "array", items: { type: "array" } },
262
- options: { type: "array", items: { type: "object", properties: { id: { type: "string" }, label: { type: "string" }, description: { type: "string" }, status: { type: "string" } } } },
263
- pairs: { type: "array", items: { type: "object", properties: { key: { type: "string" }, value: {}, mono: { type: "boolean" } } } }
264
- }
265
- }
266
- }
267
- }
268
- }
356
+ /**
357
+ * MCP server tool definition for the `respond_to_operator` tool.
358
+ *
359
+ * Register this on your MCP server to enable structured ChatPanel responses.
360
+ * The schema is identical to `CHAT_RESPONSE_TOOL_SCHEMA` but formatted for the
361
+ * MCP `server.tool()` registration API.
362
+ *
363
+ * Compatible with: `@modelcontextprotocol/sdk`, any MCP server implementation.
364
+ *
365
+ * @example
366
+ * ```ts
367
+ * import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
368
+ * import { CHAT_RESPONSE_MCP_TOOL } from '@zendir/ui';
369
+ *
370
+ * const server = new McpServer({ name: 'ops-assistant', version: '1.0.0' });
371
+ *
372
+ * server.tool(
373
+ * CHAT_RESPONSE_MCP_TOOL.name,
374
+ * CHAT_RESPONSE_MCP_TOOL.description,
375
+ * CHAT_RESPONSE_MCP_TOOL.inputSchema,
376
+ * async ({ content, severity, blocks }) => ({
377
+ * content: [{ type: 'text', text: JSON.stringify({ content, severity, blocks }) }],
378
+ * })
379
+ * );
380
+ * ```
381
+ */
382
+ var CHAT_RESPONSE_MCP_TOOL = {
383
+ name: "respond_to_operator",
384
+ description: "Respond to the spacecraft operator with text content and optional structured UI blocks (alerts, telemetry, progress, tables, choices, confirmations, commands, key-value metadata). Use the Astro UX 6-level status system: normal, standby, caution, serious, critical, off.",
385
+ inputSchema: {
386
+ type: "object",
387
+ required: ["content"],
388
+ properties: {
389
+ content: {
390
+ type: "string",
391
+ description: "Plain-text or markdown response shown to the operator."
392
+ },
393
+ severity: {
394
+ type: "string",
395
+ enum: [
396
+ "normal",
397
+ "standby",
398
+ "caution",
399
+ "serious",
400
+ "critical",
401
+ "off"
402
+ ]
403
+ },
404
+ blocks: {
405
+ type: "array",
406
+ description: "Structured UI blocks rendered below the text content.",
407
+ items: {
408
+ type: "object",
409
+ required: ["type"],
410
+ properties: {
411
+ type: {
412
+ type: "string",
413
+ enum: [
414
+ "alert",
415
+ "telemetry",
416
+ "progress",
417
+ "table",
418
+ "choice",
419
+ "confirm",
420
+ "command",
421
+ "kv"
422
+ ]
423
+ },
424
+ status: {
425
+ type: "string",
426
+ enum: [
427
+ "normal",
428
+ "standby",
429
+ "caution",
430
+ "serious",
431
+ "critical",
432
+ "off"
433
+ ]
434
+ },
435
+ title: { type: "string" },
436
+ detail: { type: "string" },
437
+ label: { type: "string" },
438
+ value: { type: "number" },
439
+ id: { type: "string" },
440
+ command: { type: "string" },
441
+ executable: { type: "boolean" },
442
+ items: {
443
+ type: "array",
444
+ items: {
445
+ type: "object",
446
+ properties: {
447
+ label: { type: "string" },
448
+ value: {},
449
+ unit: { type: "string" },
450
+ status: { type: "string" }
451
+ }
452
+ }
453
+ },
454
+ columns: {
455
+ type: "array",
456
+ items: { type: "string" }
457
+ },
458
+ rows: {
459
+ type: "array",
460
+ items: { type: "array" }
461
+ },
462
+ options: {
463
+ type: "array",
464
+ items: {
465
+ type: "object",
466
+ properties: {
467
+ id: { type: "string" },
468
+ label: { type: "string" },
469
+ description: { type: "string" },
470
+ status: { type: "string" }
471
+ }
472
+ }
473
+ },
474
+ pairs: {
475
+ type: "array",
476
+ items: {
477
+ type: "object",
478
+ properties: {
479
+ key: { type: "string" },
480
+ value: {},
481
+ mono: { type: "boolean" }
482
+ }
483
+ }
484
+ }
485
+ }
486
+ }
487
+ }
488
+ }
489
+ }
269
490
  };
270
- const CHAT_RESPONSE_TOOL_SCHEMA = {
271
- name: "respond_to_operator",
272
- description: "Respond to the spacecraft operator with text content and optional structured UI blocks (alerts, telemetry, progress, tables, choices, confirmations, commands, key-value metadata). Use the Astro UX 6-level status system: normal, standby, caution, serious, critical, off.",
273
- parameters: {
274
- type: "object",
275
- required: ["content"],
276
- properties: {
277
- content: {
278
- type: "string",
279
- description: "Plain-text or markdown response shown to the operator."
280
- },
281
- severity: {
282
- type: "string",
283
- enum: ["normal", "standby", "caution", "serious", "critical", "off"],
284
- description: "Overall message severity. Drives the accent color and icon."
285
- },
286
- blocks: {
287
- type: "array",
288
- description: "Structured UI blocks rendered below the text content.",
289
- items: {
290
- type: "object",
291
- required: ["type"],
292
- properties: {
293
- type: {
294
- type: "string",
295
- enum: ["alert", "telemetry", "progress", "table", "choice", "confirm", "command", "kv"]
296
- },
297
- status: { type: "string", enum: ["normal", "standby", "caution", "serious", "critical", "off"] },
298
- title: { type: "string" },
299
- detail: { type: "string" },
300
- items: {
301
- type: "array",
302
- items: {
303
- type: "object",
304
- properties: {
305
- label: { type: "string" },
306
- value: {},
307
- unit: { type: "string" },
308
- status: { type: "string", enum: ["normal", "standby", "caution", "serious", "critical", "off"] }
309
- }
310
- }
311
- },
312
- label: { type: "string" },
313
- value: { type: "number" },
314
- max: { type: "number" },
315
- columns: { type: "array", items: { type: "string" } },
316
- rows: { type: "array", items: { type: "array" } },
317
- id: { type: "string" },
318
- multiple: { type: "boolean" },
319
- options: {
320
- type: "array",
321
- items: {
322
- type: "object",
323
- properties: {
324
- id: { type: "string" },
325
- label: { type: "string" },
326
- description: { type: "string" },
327
- status: { type: "string", enum: ["normal", "standby", "caution", "serious", "critical", "off"] },
328
- disabled: { type: "boolean" }
329
- }
330
- }
331
- },
332
- confirmLabel: { type: "string" },
333
- cancelLabel: { type: "string" },
334
- language: { type: "string" },
335
- command: { type: "string" },
336
- executable: { type: "boolean" },
337
- pairs: {
338
- type: "array",
339
- items: {
340
- type: "object",
341
- properties: {
342
- key: { type: "string" },
343
- value: {},
344
- mono: { type: "boolean" }
345
- }
346
- }
347
- }
348
- }
349
- }
350
- }
351
- }
352
- }
491
+ /**
492
+ * OpenAI-compatible function/tool schema for structured chat responses.
493
+ *
494
+ * Pass this as a tool definition in your API call. The model is constrained
495
+ * to emit valid JSON matching this schema — no prompt engineering required.
496
+ *
497
+ * Works with: OpenAI (Structured Outputs / function calling), Anthropic (Tool Use),
498
+ * Google Gemini (Function Calling), Mistral, Cohere, and any OpenAI-compatible API.
499
+ *
500
+ * @example
501
+ * ```ts
502
+ * // OpenAI
503
+ * tools: [{ type: 'function', function: CHAT_RESPONSE_TOOL_SCHEMA }]
504
+ *
505
+ * // Anthropic
506
+ * tools: [{ name: CHAT_RESPONSE_TOOL_SCHEMA.name,
507
+ * description: CHAT_RESPONSE_TOOL_SCHEMA.description,
508
+ * input_schema: CHAT_RESPONSE_TOOL_SCHEMA.parameters }]
509
+ *
510
+ * // Google Gemini
511
+ * tools: [{ functionDeclarations: [CHAT_RESPONSE_TOOL_SCHEMA] }]
512
+ * ```
513
+ */
514
+ var CHAT_RESPONSE_TOOL_SCHEMA = {
515
+ name: "respond_to_operator",
516
+ description: "Respond to the spacecraft operator with text content and optional structured UI blocks (alerts, telemetry, progress, tables, choices, confirmations, commands, key-value metadata). Use the Astro UX 6-level status system: normal, standby, caution, serious, critical, off.",
517
+ parameters: {
518
+ type: "object",
519
+ required: ["content"],
520
+ properties: {
521
+ content: {
522
+ type: "string",
523
+ description: "Plain-text or markdown response shown to the operator."
524
+ },
525
+ severity: {
526
+ type: "string",
527
+ enum: [
528
+ "normal",
529
+ "standby",
530
+ "caution",
531
+ "serious",
532
+ "critical",
533
+ "off"
534
+ ],
535
+ description: "Overall message severity. Drives the accent color and icon."
536
+ },
537
+ blocks: {
538
+ type: "array",
539
+ description: "Structured UI blocks rendered below the text content.",
540
+ items: {
541
+ type: "object",
542
+ required: ["type"],
543
+ properties: {
544
+ type: {
545
+ type: "string",
546
+ enum: [
547
+ "alert",
548
+ "telemetry",
549
+ "progress",
550
+ "table",
551
+ "choice",
552
+ "confirm",
553
+ "command",
554
+ "kv"
555
+ ]
556
+ },
557
+ status: {
558
+ type: "string",
559
+ enum: [
560
+ "normal",
561
+ "standby",
562
+ "caution",
563
+ "serious",
564
+ "critical",
565
+ "off"
566
+ ]
567
+ },
568
+ title: { type: "string" },
569
+ detail: { type: "string" },
570
+ items: {
571
+ type: "array",
572
+ items: {
573
+ type: "object",
574
+ properties: {
575
+ label: { type: "string" },
576
+ value: {},
577
+ unit: { type: "string" },
578
+ status: {
579
+ type: "string",
580
+ enum: [
581
+ "normal",
582
+ "standby",
583
+ "caution",
584
+ "serious",
585
+ "critical",
586
+ "off"
587
+ ]
588
+ }
589
+ }
590
+ }
591
+ },
592
+ label: { type: "string" },
593
+ value: { type: "number" },
594
+ max: { type: "number" },
595
+ columns: {
596
+ type: "array",
597
+ items: { type: "string" }
598
+ },
599
+ rows: {
600
+ type: "array",
601
+ items: { type: "array" }
602
+ },
603
+ id: { type: "string" },
604
+ multiple: { type: "boolean" },
605
+ options: {
606
+ type: "array",
607
+ items: {
608
+ type: "object",
609
+ properties: {
610
+ id: { type: "string" },
611
+ label: { type: "string" },
612
+ description: { type: "string" },
613
+ status: {
614
+ type: "string",
615
+ enum: [
616
+ "normal",
617
+ "standby",
618
+ "caution",
619
+ "serious",
620
+ "critical",
621
+ "off"
622
+ ]
623
+ },
624
+ disabled: { type: "boolean" }
625
+ }
626
+ }
627
+ },
628
+ confirmLabel: { type: "string" },
629
+ cancelLabel: { type: "string" },
630
+ language: { type: "string" },
631
+ command: { type: "string" },
632
+ executable: { type: "boolean" },
633
+ pairs: {
634
+ type: "array",
635
+ items: {
636
+ type: "object",
637
+ properties: {
638
+ key: { type: "string" },
639
+ value: {},
640
+ mono: { type: "boolean" }
641
+ }
642
+ }
643
+ }
644
+ }
645
+ }
646
+ }
647
+ }
648
+ }
353
649
  };
354
- const CHAT_RESPONSE_JSON_PROMPT = `When reporting telemetry, status, or alerts, respond with a JSON object:
650
+ /**
651
+ * JSON system prompt snippet — instructs the LLM to respond with JSON.
652
+ * Include in your system prompt for the simplest integration path.
653
+ */
654
+ var CHAT_RESPONSE_JSON_PROMPT = `When reporting telemetry, status, or alerts, respond with a JSON object:
355
655
 
356
656
  {
357
657
  "content": "Plain text explanation (supports **markdown**).",
@@ -370,7 +670,11 @@ const CHAT_RESPONSE_JSON_PROMPT = `When reporting telemetry, status, or alerts,
370
670
 
371
671
  Status levels (Astro UX): normal (nominal), standby (idle), caution (approaching limit), serious (at limit), critical (exceeding limit), off (no data).
372
672
  Always include "content". Only include "blocks" when structured data helps the operator.`;
373
- const CHAT_RESPONSE_YAML_PROMPT = `Respond in YAML format (not JSON). Use this structure:
673
+ /**
674
+ * YAML system prompt snippet — instructs the LLM to respond in compact YAML
675
+ * instead of JSON. ~30-40% fewer output tokens for the same data.
676
+ */
677
+ var CHAT_RESPONSE_YAML_PROMPT = `Respond in YAML format (not JSON). Use this structure:
374
678
 
375
679
  content: Your plain-text or markdown response here.
376
680
  severity: normal
@@ -411,7 +715,11 @@ blocks:
411
715
 
412
716
  Status levels (Astro UX): normal, standby, caution, serious, critical, off.
413
717
  Always include "content". Only include "blocks" when structured data helps the operator.`;
414
- const CHAT_STATUS_RULES_PROMPT = `Assign status levels using these thresholds:
718
+ /**
719
+ * Astro UX status rules snippet — append to any system prompt (JSON or YAML).
720
+ * Gives the LLM concrete thresholds for assigning status levels.
721
+ */
722
+ var CHAT_STATUS_RULES_PROMPT = `Assign status levels using these thresholds:
415
723
  - Battery: normal >50%, standby 30-50%, caution 20-30%, serious 10-20%, critical <10%
416
724
  - Temperature: normal <70°C, caution 70-80°C, serious 80-85°C, critical >85°C
417
725
  - Signal: normal >-80 dBm, caution -80 to -90, serious -90 to -95, critical <-95 dBm
@@ -425,721 +733,1113 @@ General rules:
425
733
  - "critical" → Exceeding hard limit, loss of signal, or failure
426
734
  - "off" → Powered down, no data`;
427
735
  function StatusShape({ status, size = 10, color }) {
428
- const glow = `${color}50`;
429
- switch (status) {
430
- case "caution":
431
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, style: { filter: `drop-shadow(0 0 3px ${glow})` }, children: /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "10", height: "10", fill: color }) });
432
- case "serious":
433
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, style: { filter: `drop-shadow(0 0 3px ${glow})` }, children: /* @__PURE__ */ jsx("polygon", { points: "6,1 11,6 6,11 1,6", fill: color }) });
434
- case "critical":
435
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, style: { filter: `drop-shadow(0 0 3px ${glow})` }, children: /* @__PURE__ */ jsx("polygon", { points: "6,11 1,2 11,2", fill: color }) });
436
- case "standby":
437
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, style: { filter: `drop-shadow(0 0 3px ${glow})` }, children: /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "3.5", fill: "none", stroke: color, strokeWidth: "2" }) });
438
- case "off":
439
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, style: { filter: `drop-shadow(0 0 3px ${glow})` }, children: /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "3", fill: color }) });
440
- default:
441
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, style: { filter: `drop-shadow(0 0 3px ${glow})` }, children: /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "5", fill: color }) });
442
- }
736
+ const glow = `${color}50`;
737
+ switch (status) {
738
+ case "caution": return /* @__PURE__ */ jsx("svg", {
739
+ viewBox: "0 0 12 12",
740
+ width: size,
741
+ height: size,
742
+ style: { filter: `drop-shadow(0 0 3px ${glow})` },
743
+ children: /* @__PURE__ */ jsx("rect", {
744
+ x: "1",
745
+ y: "1",
746
+ width: "10",
747
+ height: "10",
748
+ fill: color
749
+ })
750
+ });
751
+ case "serious": return /* @__PURE__ */ jsx("svg", {
752
+ viewBox: "0 0 12 12",
753
+ width: size,
754
+ height: size,
755
+ style: { filter: `drop-shadow(0 0 3px ${glow})` },
756
+ children: /* @__PURE__ */ jsx("polygon", {
757
+ points: "6,1 11,6 6,11 1,6",
758
+ fill: color
759
+ })
760
+ });
761
+ case "critical": return /* @__PURE__ */ jsx("svg", {
762
+ viewBox: "0 0 12 12",
763
+ width: size,
764
+ height: size,
765
+ style: { filter: `drop-shadow(0 0 3px ${glow})` },
766
+ children: /* @__PURE__ */ jsx("polygon", {
767
+ points: "6,11 1,2 11,2",
768
+ fill: color
769
+ })
770
+ });
771
+ case "standby": return /* @__PURE__ */ jsx("svg", {
772
+ viewBox: "0 0 12 12",
773
+ width: size,
774
+ height: size,
775
+ style: { filter: `drop-shadow(0 0 3px ${glow})` },
776
+ children: /* @__PURE__ */ jsx("circle", {
777
+ cx: "6",
778
+ cy: "6",
779
+ r: "3.5",
780
+ fill: "none",
781
+ stroke: color,
782
+ strokeWidth: "2"
783
+ })
784
+ });
785
+ case "off": return /* @__PURE__ */ jsx("svg", {
786
+ viewBox: "0 0 12 12",
787
+ width: size,
788
+ height: size,
789
+ style: { filter: `drop-shadow(0 0 3px ${glow})` },
790
+ children: /* @__PURE__ */ jsx("circle", {
791
+ cx: "6",
792
+ cy: "6",
793
+ r: "3",
794
+ fill: color
795
+ })
796
+ });
797
+ default: return /* @__PURE__ */ jsx("svg", {
798
+ viewBox: "0 0 12 12",
799
+ width: size,
800
+ height: size,
801
+ style: { filter: `drop-shadow(0 0 3px ${glow})` },
802
+ children: /* @__PURE__ */ jsx("circle", {
803
+ cx: "6",
804
+ cy: "6",
805
+ r: "5",
806
+ fill: color
807
+ })
808
+ });
809
+ }
443
810
  }
444
811
  function AlertBlock({ block }) {
445
- const { tokens } = useTheme();
446
- const color = tokens.colors.status[block.status];
447
- return /* @__PURE__ */ jsxs("div", { style: {
448
- display: "flex",
449
- alignItems: "flex-start",
450
- gap: 10,
451
- padding: "10px 14px",
452
- backgroundColor: `${color}12`,
453
- border: `1px solid ${color}30`,
454
- borderLeft: `3px solid ${color}`,
455
- borderRadius: tokens.borderRadius.md,
456
- marginTop: 8
457
- }, children: [
458
- /* @__PURE__ */ jsx("div", { style: { marginTop: 2, flexShrink: 0 }, children: /* @__PURE__ */ jsx(StatusShape, { status: block.status, size: 12, color }) }),
459
- /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
460
- /* @__PURE__ */ jsx("div", { style: { fontSize: tokens.typography.fontSize.sm, fontWeight: 600, color: tokens.colors.text.primary }, children: block.title }),
461
- block.detail && /* @__PURE__ */ jsx("div", { style: { fontSize: tokens.typography.fontSize.xs, color: tokens.colors.text.secondary, marginTop: 2, lineHeight: 1.5 }, children: block.detail })
462
- ] })
463
- ] });
812
+ const { tokens } = useTheme();
813
+ const color = tokens.colors.status[block.status];
814
+ return /* @__PURE__ */ jsxs("div", {
815
+ style: {
816
+ display: "flex",
817
+ alignItems: "flex-start",
818
+ gap: 10,
819
+ padding: "10px 14px",
820
+ backgroundColor: `${color}12`,
821
+ border: `1px solid ${color}30`,
822
+ borderLeft: `3px solid ${color}`,
823
+ borderRadius: tokens.borderRadius.md,
824
+ marginTop: 8
825
+ },
826
+ children: [/* @__PURE__ */ jsx("div", {
827
+ style: {
828
+ marginTop: 2,
829
+ flexShrink: 0
830
+ },
831
+ children: /* @__PURE__ */ jsx(StatusShape, {
832
+ status: block.status,
833
+ size: 12,
834
+ color
835
+ })
836
+ }), /* @__PURE__ */ jsxs("div", {
837
+ style: {
838
+ flex: 1,
839
+ minWidth: 0
840
+ },
841
+ children: [/* @__PURE__ */ jsx("div", {
842
+ style: {
843
+ fontSize: tokens.typography.fontSize.sm,
844
+ fontWeight: 600,
845
+ color: tokens.colors.text.primary
846
+ },
847
+ children: block.title
848
+ }), block.detail && /* @__PURE__ */ jsx("div", {
849
+ style: {
850
+ fontSize: tokens.typography.fontSize.xs,
851
+ color: tokens.colors.text.secondary,
852
+ marginTop: 2,
853
+ lineHeight: 1.5
854
+ },
855
+ children: block.detail
856
+ })]
857
+ })]
858
+ });
464
859
  }
465
860
  function TelemetryBlock({ block }) {
466
- const { tokens } = useTheme();
467
- return /* @__PURE__ */ jsxs("div", { style: {
468
- marginTop: 8,
469
- border: `1px solid ${tokens.colors.border.muted}`,
470
- borderRadius: tokens.borderRadius.md,
471
- overflow: "hidden"
472
- }, children: [
473
- block.title && /* @__PURE__ */ jsx("div", { style: {
474
- padding: "6px 12px",
475
- fontSize: tokens.typography.fontSize.xxs,
476
- fontWeight: 600,
477
- color: tokens.colors.text.secondary,
478
- textTransform: "uppercase",
479
- letterSpacing: "0.05em",
480
- backgroundColor: `${tokens.colors.background.elevated}80`,
481
- borderBottom: `1px solid ${tokens.colors.border.muted}`
482
- }, children: block.title }),
483
- /* @__PURE__ */ jsx("div", { style: { padding: "8px 12px", display: "flex", flexDirection: "column", gap: 6 }, children: (block.items ?? []).map((item, i) => {
484
- const itemColor = item.status ? tokens.colors.status[item.status] : tokens.colors.text.secondary;
485
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, fontSize: tokens.typography.fontSize.sm }, children: [
486
- /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6, color: tokens.colors.text.secondary }, children: [
487
- item.status && /* @__PURE__ */ jsx(StatusShape, { status: item.status, size: 8, color: tokens.colors.status[item.status] }),
488
- item.label
489
- ] }),
490
- /* @__PURE__ */ jsxs("span", { style: {
491
- fontFamily: tokens.typography.fontFamily.mono,
492
- fontVariantNumeric: "tabular-nums",
493
- color: itemColor,
494
- fontWeight: 500
495
- }, children: [
496
- item.value,
497
- item.unit ? ` ${item.unit}` : ""
498
- ] })
499
- ] }, i);
500
- }) })
501
- ] });
861
+ const { tokens } = useTheme();
862
+ return /* @__PURE__ */ jsxs("div", {
863
+ style: {
864
+ marginTop: 8,
865
+ border: `1px solid ${tokens.colors.border.muted}`,
866
+ borderRadius: tokens.borderRadius.md,
867
+ overflow: "hidden"
868
+ },
869
+ children: [block.title && /* @__PURE__ */ jsx("div", {
870
+ style: {
871
+ padding: "6px 12px",
872
+ fontSize: tokens.typography.fontSize.xxs,
873
+ fontWeight: 600,
874
+ color: tokens.colors.text.secondary,
875
+ textTransform: "uppercase",
876
+ letterSpacing: "0.05em",
877
+ backgroundColor: `${tokens.colors.background.elevated}80`,
878
+ borderBottom: `1px solid ${tokens.colors.border.muted}`
879
+ },
880
+ children: block.title
881
+ }), /* @__PURE__ */ jsx("div", {
882
+ style: {
883
+ padding: "8px 12px",
884
+ display: "flex",
885
+ flexDirection: "column",
886
+ gap: 6
887
+ },
888
+ children: (block.items ?? []).map((item, i) => {
889
+ const itemColor = item.status ? tokens.colors.status[item.status] : tokens.colors.text.secondary;
890
+ return /* @__PURE__ */ jsxs("div", {
891
+ style: {
892
+ display: "flex",
893
+ alignItems: "center",
894
+ justifyContent: "space-between",
895
+ gap: 8,
896
+ fontSize: tokens.typography.fontSize.sm
897
+ },
898
+ children: [/* @__PURE__ */ jsxs("span", {
899
+ style: {
900
+ display: "flex",
901
+ alignItems: "center",
902
+ gap: 6,
903
+ color: tokens.colors.text.secondary
904
+ },
905
+ children: [item.status && /* @__PURE__ */ jsx(StatusShape, {
906
+ status: item.status,
907
+ size: 8,
908
+ color: tokens.colors.status[item.status]
909
+ }), item.label]
910
+ }), /* @__PURE__ */ jsxs("span", {
911
+ style: {
912
+ fontFamily: tokens.typography.fontFamily.mono,
913
+ fontVariantNumeric: "tabular-nums",
914
+ color: itemColor,
915
+ fontWeight: 500
916
+ },
917
+ children: [item.value, item.unit ? ` ${item.unit}` : ""]
918
+ })]
919
+ }, i);
920
+ })
921
+ })]
922
+ });
502
923
  }
503
924
  function ProgressBlock({ block }) {
504
- const { tokens } = useTheme();
505
- const max = block.max ?? 100;
506
- const pct = Math.min(100, Math.max(0, block.value / max * 100));
507
- const effectiveStatus = block.status ?? (pct >= 75 ? "normal" : pct >= 50 ? "standby" : pct >= 25 ? "caution" : pct > 0 ? "serious" : "off");
508
- const color = tokens.colors.status[effectiveStatus];
509
- return /* @__PURE__ */ jsxs("div", { style: { marginTop: 8 }, children: [
510
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: 4, fontSize: tokens.typography.fontSize.xs, color: tokens.colors.text.secondary }, children: [
511
- /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
512
- /* @__PURE__ */ jsx(StatusShape, { status: effectiveStatus, size: 8, color }),
513
- block.label
514
- ] }),
515
- /* @__PURE__ */ jsxs("span", { style: { fontFamily: tokens.typography.fontFamily.mono, color: tokens.colors.text.primary }, children: [
516
- pct.toFixed(0),
517
- "%"
518
- ] })
519
- ] }),
520
- /* @__PURE__ */ jsx("div", { style: { height: 6, backgroundColor: tokens.colors.border.muted, borderRadius: 3, overflow: "hidden" }, children: /* @__PURE__ */ jsx("div", { style: { height: "100%", width: `${pct}%`, backgroundColor: color, borderRadius: 3, transition: "width 300ms", boxShadow: `0 0 6px ${color}40` } }) })
521
- ] });
925
+ const { tokens } = useTheme();
926
+ const max = block.max ?? 100;
927
+ const pct = Math.min(100, Math.max(0, block.value / max * 100));
928
+ const effectiveStatus = block.status ?? (pct >= 75 ? "normal" : pct >= 50 ? "standby" : pct >= 25 ? "caution" : pct > 0 ? "serious" : "off");
929
+ const color = tokens.colors.status[effectiveStatus];
930
+ return /* @__PURE__ */ jsxs("div", {
931
+ style: { marginTop: 8 },
932
+ children: [/* @__PURE__ */ jsxs("div", {
933
+ style: {
934
+ display: "flex",
935
+ justifyContent: "space-between",
936
+ marginBottom: 4,
937
+ fontSize: tokens.typography.fontSize.xs,
938
+ color: tokens.colors.text.secondary
939
+ },
940
+ children: [/* @__PURE__ */ jsxs("span", {
941
+ style: {
942
+ display: "flex",
943
+ alignItems: "center",
944
+ gap: 6
945
+ },
946
+ children: [/* @__PURE__ */ jsx(StatusShape, {
947
+ status: effectiveStatus,
948
+ size: 8,
949
+ color
950
+ }), block.label]
951
+ }), /* @__PURE__ */ jsxs("span", {
952
+ style: {
953
+ fontFamily: tokens.typography.fontFamily.mono,
954
+ color: tokens.colors.text.primary
955
+ },
956
+ children: [pct.toFixed(0), "%"]
957
+ })]
958
+ }), /* @__PURE__ */ jsx("div", {
959
+ style: {
960
+ height: 6,
961
+ backgroundColor: tokens.colors.border.muted,
962
+ borderRadius: 3,
963
+ overflow: "hidden"
964
+ },
965
+ children: /* @__PURE__ */ jsx("div", { style: {
966
+ height: "100%",
967
+ width: `${pct}%`,
968
+ backgroundColor: color,
969
+ borderRadius: 3,
970
+ transition: "width 300ms",
971
+ boxShadow: `0 0 6px ${color}40`
972
+ } })
973
+ })]
974
+ });
522
975
  }
523
976
  function TableBlock({ block }) {
524
- const { tokens } = useTheme();
525
- return /* @__PURE__ */ jsxs("div", { style: { marginTop: 8, border: `1px solid ${tokens.colors.border.muted}`, borderRadius: tokens.borderRadius.md, overflow: "hidden" }, children: [
526
- block.title && /* @__PURE__ */ jsx("div", { style: {
527
- padding: "6px 12px",
528
- fontSize: tokens.typography.fontSize.xxs,
529
- fontWeight: 600,
530
- color: tokens.colors.text.secondary,
531
- textTransform: "uppercase",
532
- letterSpacing: "0.05em",
533
- backgroundColor: `${tokens.colors.background.elevated}80`,
534
- borderBottom: `1px solid ${tokens.colors.border.muted}`
535
- }, children: block.title }),
536
- /* @__PURE__ */ jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: tokens.typography.fontSize.xs }, children: [
537
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: (block.columns ?? []).map((col, i) => /* @__PURE__ */ jsx("th", { style: {
538
- padding: "6px 12px",
539
- textAlign: "left",
540
- fontWeight: 600,
541
- color: tokens.colors.text.secondary,
542
- borderBottom: `1px solid ${tokens.colors.border.muted}`,
543
- whiteSpace: "nowrap"
544
- }, children: col }, i)) }) }),
545
- /* @__PURE__ */ jsx("tbody", { children: (block.rows ?? []).map((row, ri) => /* @__PURE__ */ jsx("tr", { children: (row ?? []).map((cell, ci) => /* @__PURE__ */ jsx("td", { style: {
546
- padding: "5px 12px",
547
- color: tokens.colors.text.primary,
548
- fontFamily: typeof cell === "number" ? tokens.typography.fontFamily.mono : void 0,
549
- fontVariantNumeric: typeof cell === "number" ? "tabular-nums" : void 0,
550
- borderBottom: ri < (block.rows ?? []).length - 1 ? `1px solid ${tokens.colors.border.muted}30` : "none",
551
- whiteSpace: "nowrap"
552
- }, children: cell }, ci)) }, ri)) })
553
- ] }) })
554
- ] });
977
+ const { tokens } = useTheme();
978
+ return /* @__PURE__ */ jsxs("div", {
979
+ style: {
980
+ marginTop: 8,
981
+ border: `1px solid ${tokens.colors.border.muted}`,
982
+ borderRadius: tokens.borderRadius.md,
983
+ overflow: "hidden"
984
+ },
985
+ children: [block.title && /* @__PURE__ */ jsx("div", {
986
+ style: {
987
+ padding: "6px 12px",
988
+ fontSize: tokens.typography.fontSize.xxs,
989
+ fontWeight: 600,
990
+ color: tokens.colors.text.secondary,
991
+ textTransform: "uppercase",
992
+ letterSpacing: "0.05em",
993
+ backgroundColor: `${tokens.colors.background.elevated}80`,
994
+ borderBottom: `1px solid ${tokens.colors.border.muted}`
995
+ },
996
+ children: block.title
997
+ }), /* @__PURE__ */ jsx("div", {
998
+ style: { overflowX: "auto" },
999
+ children: /* @__PURE__ */ jsxs("table", {
1000
+ style: {
1001
+ width: "100%",
1002
+ borderCollapse: "collapse",
1003
+ fontSize: tokens.typography.fontSize.xs
1004
+ },
1005
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: (block.columns ?? []).map((col, i) => /* @__PURE__ */ jsx("th", {
1006
+ style: {
1007
+ padding: "6px 12px",
1008
+ textAlign: "left",
1009
+ fontWeight: 600,
1010
+ color: tokens.colors.text.secondary,
1011
+ borderBottom: `1px solid ${tokens.colors.border.muted}`,
1012
+ whiteSpace: "nowrap"
1013
+ },
1014
+ children: col
1015
+ }, i)) }) }), /* @__PURE__ */ jsx("tbody", { children: (block.rows ?? []).map((row, ri) => /* @__PURE__ */ jsx("tr", { children: (row ?? []).map((cell, ci) => /* @__PURE__ */ jsx("td", {
1016
+ style: {
1017
+ padding: "5px 12px",
1018
+ color: tokens.colors.text.primary,
1019
+ fontFamily: typeof cell === "number" ? tokens.typography.fontFamily.mono : void 0,
1020
+ fontVariantNumeric: typeof cell === "number" ? "tabular-nums" : void 0,
1021
+ borderBottom: ri < (block.rows ?? []).length - 1 ? `1px solid ${tokens.colors.border.muted}30` : "none",
1022
+ whiteSpace: "nowrap"
1023
+ },
1024
+ children: cell
1025
+ }, ci)) }, ri)) })]
1026
+ })
1027
+ })]
1028
+ });
555
1029
  }
556
1030
  function ActionsBlock({ block }) {
557
- const { tokens } = useTheme();
558
- return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8, marginTop: 8, flexWrap: "wrap" }, children: (block.buttons ?? []).map((btn, i) => {
559
- const isCritical = btn.variant === "critical";
560
- const isPrimary = btn.variant === "primary" || !btn.variant && i === 0;
561
- const baseColor = isCritical ? tokens.colors.status.critical : tokens.colors.accent.primary;
562
- const textColor = isCritical ? tokens.colors.status.critical : safeAccentText(tokens.colors.accent.primary);
563
- return /* @__PURE__ */ jsx("button", { onClick: btn.onClick, style: {
564
- fontSize: tokens.typography.fontSize.xxs,
565
- padding: "4px 14px",
566
- borderRadius: tokens.borderRadius.sm,
567
- border: `1px solid ${baseColor}${isPrimary ? "" : "40"}`,
568
- backgroundColor: isPrimary ? `${baseColor}20` : "transparent",
569
- color: textColor,
570
- cursor: "pointer",
571
- fontFamily: tokens.typography.fontFamily.primary,
572
- fontWeight: 500,
573
- transition: "all 150ms"
574
- }, children: btn.label }, i);
575
- }) });
1031
+ const { tokens } = useTheme();
1032
+ return /* @__PURE__ */ jsx("div", {
1033
+ style: {
1034
+ display: "flex",
1035
+ gap: 8,
1036
+ marginTop: 8,
1037
+ flexWrap: "wrap"
1038
+ },
1039
+ children: (block.buttons ?? []).map((btn, i) => {
1040
+ const isCritical = btn.variant === "critical";
1041
+ const isPrimary = btn.variant === "primary" || !btn.variant && i === 0;
1042
+ const baseColor = isCritical ? tokens.colors.status.critical : tokens.colors.accent.primary;
1043
+ const textColor = isCritical ? tokens.colors.status.critical : safeAccentText(tokens.colors.accent.primary);
1044
+ return /* @__PURE__ */ jsx("button", {
1045
+ onClick: btn.onClick,
1046
+ style: {
1047
+ fontSize: tokens.typography.fontSize.xxs,
1048
+ padding: "4px 14px",
1049
+ borderRadius: tokens.borderRadius.sm,
1050
+ border: `1px solid ${baseColor}${isPrimary ? "" : "40"}`,
1051
+ backgroundColor: isPrimary ? `${baseColor}20` : "transparent",
1052
+ color: textColor,
1053
+ cursor: "pointer",
1054
+ fontFamily: tokens.typography.fontFamily.primary,
1055
+ fontWeight: 500,
1056
+ transition: "all 150ms"
1057
+ },
1058
+ children: btn.label
1059
+ }, i);
1060
+ })
1061
+ });
576
1062
  }
577
1063
  function ChoiceBlock({ block, messageId, onEvent }) {
578
- const { tokens } = useTheme();
579
- const [localSelected, setLocalSelected] = useState(new Set(block.selected ?? []));
580
- const isSubmitted = block.submitted;
581
- const toggle = (optId) => {
582
- if (isSubmitted) return;
583
- setLocalSelected((prev) => {
584
- const next = new Set(prev);
585
- if (block.multiple) {
586
- next.has(optId) ? next.delete(optId) : next.add(optId);
587
- } else {
588
- next.clear();
589
- next.add(optId);
590
- }
591
- return next;
592
- });
593
- };
594
- const submit = () => {
595
- if (localSelected.size === 0 || isSubmitted) return;
596
- onEvent == null ? void 0 : onEvent({ blockId: block.id, messageId, action: "select", value: Array.from(localSelected) });
597
- };
598
- return /* @__PURE__ */ jsxs("div", { style: { marginTop: 8 }, children: [
599
- block.title && /* @__PURE__ */ jsxs("div", { style: { fontSize: tokens.typography.fontSize.xs, fontWeight: 600, color: tokens.colors.text.secondary, marginBottom: 6 }, children: [
600
- block.title,
601
- block.multiple ? " (select multiple)" : ""
602
- ] }),
603
- /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: (block.options ?? []).map((opt) => {
604
- const isSelected = localSelected.has(opt.id);
605
- const optColor = opt.status ? tokens.colors.status[opt.status] : tokens.colors.accent.primary;
606
- const isDisabled = opt.disabled || isSubmitted;
607
- return /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => toggle(opt.id), disabled: isDisabled, style: {
608
- display: "flex",
609
- alignItems: "center",
610
- gap: 10,
611
- padding: "8px 12px",
612
- borderRadius: tokens.borderRadius.md,
613
- cursor: isDisabled ? "default" : "pointer",
614
- border: `1px solid ${isSelected ? optColor : tokens.colors.border.muted}`,
615
- backgroundColor: isSelected ? `${optColor}15` : "transparent",
616
- transition: "all 150ms",
617
- textAlign: "left",
618
- fontFamily: tokens.typography.fontFamily.primary,
619
- opacity: isDisabled && !isSelected ? 0.5 : 1
620
- }, children: [
621
- /* @__PURE__ */ jsx("div", { style: {
622
- width: 16,
623
- height: 16,
624
- flexShrink: 0,
625
- borderRadius: block.multiple ? 3 : "50%",
626
- border: `2px solid ${isSelected ? optColor : tokens.colors.border.muted}`,
627
- backgroundColor: isSelected ? optColor : "transparent",
628
- display: "flex",
629
- alignItems: "center",
630
- justifyContent: "center",
631
- transition: "all 150ms"
632
- }, children: isSelected && /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: 10, height: 10, fill: "none", stroke: tokens.colors.text.inverse, strokeWidth: 2, children: /* @__PURE__ */ jsx("polyline", { points: "2,6 5,9 10,3" }) }) }),
633
- /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
634
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
635
- opt.status && /* @__PURE__ */ jsx(StatusShape, { status: opt.status, size: 8, color: tokens.colors.status[opt.status] }),
636
- /* @__PURE__ */ jsx("span", { style: { fontSize: tokens.typography.fontSize.sm, color: tokens.colors.text.primary, fontWeight: 500 }, children: opt.label })
637
- ] }),
638
- opt.description && /* @__PURE__ */ jsx("div", { style: { fontSize: tokens.typography.fontSize.xs, color: tokens.colors.text.tertiary, marginTop: 1 }, children: opt.description })
639
- ] })
640
- ] }, opt.id);
641
- }) }),
642
- !isSubmitted && onEvent && /* @__PURE__ */ jsx("button", { type: "button", onClick: submit, disabled: localSelected.size === 0, style: {
643
- marginTop: 8,
644
- padding: "5px 18px",
645
- fontSize: tokens.typography.fontSize.xs,
646
- fontWeight: 600,
647
- borderRadius: tokens.borderRadius.sm,
648
- border: `1px solid ${tokens.colors.accent.primary}`,
649
- backgroundColor: localSelected.size > 0 ? `${tokens.colors.accent.primary}20` : "transparent",
650
- color: localSelected.size > 0 ? safeAccentText(tokens.colors.accent.primary) : tokens.colors.text.tertiary,
651
- cursor: localSelected.size > 0 ? "pointer" : "not-allowed",
652
- fontFamily: tokens.typography.fontFamily.primary,
653
- transition: "all 150ms"
654
- }, children: "Confirm Selection" }),
655
- isSubmitted && /* @__PURE__ */ jsx("div", { style: { marginTop: 6, fontSize: tokens.typography.fontSize.xs, color: tokens.colors.text.tertiary, fontStyle: "italic" }, children: "Selection submitted" })
656
- ] });
1064
+ const { tokens } = useTheme();
1065
+ const [localSelected, setLocalSelected] = useState(new Set(block.selected ?? []));
1066
+ const isSubmitted = block.submitted;
1067
+ const toggle = (optId) => {
1068
+ if (isSubmitted) return;
1069
+ setLocalSelected((prev) => {
1070
+ const next = new Set(prev);
1071
+ if (block.multiple) next.has(optId) ? next.delete(optId) : next.add(optId);
1072
+ else {
1073
+ next.clear();
1074
+ next.add(optId);
1075
+ }
1076
+ return next;
1077
+ });
1078
+ };
1079
+ const submit = () => {
1080
+ if (localSelected.size === 0 || isSubmitted) return;
1081
+ onEvent?.({
1082
+ blockId: block.id,
1083
+ messageId,
1084
+ action: "select",
1085
+ value: Array.from(localSelected)
1086
+ });
1087
+ };
1088
+ return /* @__PURE__ */ jsxs("div", {
1089
+ style: { marginTop: 8 },
1090
+ children: [
1091
+ block.title && /* @__PURE__ */ jsxs("div", {
1092
+ style: {
1093
+ fontSize: tokens.typography.fontSize.xs,
1094
+ fontWeight: 600,
1095
+ color: tokens.colors.text.secondary,
1096
+ marginBottom: 6
1097
+ },
1098
+ children: [block.title, block.multiple ? " (select multiple)" : ""]
1099
+ }),
1100
+ /* @__PURE__ */ jsx("div", {
1101
+ style: {
1102
+ display: "flex",
1103
+ flexDirection: "column",
1104
+ gap: 4
1105
+ },
1106
+ children: (block.options ?? []).map((opt) => {
1107
+ const isSelected = localSelected.has(opt.id);
1108
+ const optColor = opt.status ? tokens.colors.status[opt.status] : tokens.colors.accent.primary;
1109
+ const isDisabled = opt.disabled || isSubmitted;
1110
+ return /* @__PURE__ */ jsxs("button", {
1111
+ type: "button",
1112
+ onClick: () => toggle(opt.id),
1113
+ disabled: isDisabled,
1114
+ style: {
1115
+ display: "flex",
1116
+ alignItems: "center",
1117
+ gap: 10,
1118
+ padding: "8px 12px",
1119
+ borderRadius: tokens.borderRadius.md,
1120
+ cursor: isDisabled ? "default" : "pointer",
1121
+ border: `1px solid ${isSelected ? optColor : tokens.colors.border.muted}`,
1122
+ backgroundColor: isSelected ? `${optColor}15` : "transparent",
1123
+ transition: "all 150ms",
1124
+ textAlign: "left",
1125
+ fontFamily: tokens.typography.fontFamily.primary,
1126
+ opacity: isDisabled && !isSelected ? .5 : 1
1127
+ },
1128
+ children: [/* @__PURE__ */ jsx("div", {
1129
+ style: {
1130
+ width: 16,
1131
+ height: 16,
1132
+ flexShrink: 0,
1133
+ borderRadius: block.multiple ? 3 : "50%",
1134
+ border: `2px solid ${isSelected ? optColor : tokens.colors.border.muted}`,
1135
+ backgroundColor: isSelected ? optColor : "transparent",
1136
+ display: "flex",
1137
+ alignItems: "center",
1138
+ justifyContent: "center",
1139
+ transition: "all 150ms"
1140
+ },
1141
+ children: isSelected && /* @__PURE__ */ jsx("svg", {
1142
+ viewBox: "0 0 12 12",
1143
+ width: 10,
1144
+ height: 10,
1145
+ fill: "none",
1146
+ stroke: tokens.colors.text.inverse,
1147
+ strokeWidth: 2,
1148
+ children: /* @__PURE__ */ jsx("polyline", { points: "2,6 5,9 10,3" })
1149
+ })
1150
+ }), /* @__PURE__ */ jsxs("div", {
1151
+ style: {
1152
+ flex: 1,
1153
+ minWidth: 0
1154
+ },
1155
+ children: [/* @__PURE__ */ jsxs("div", {
1156
+ style: {
1157
+ display: "flex",
1158
+ alignItems: "center",
1159
+ gap: 6
1160
+ },
1161
+ children: [opt.status && /* @__PURE__ */ jsx(StatusShape, {
1162
+ status: opt.status,
1163
+ size: 8,
1164
+ color: tokens.colors.status[opt.status]
1165
+ }), /* @__PURE__ */ jsx("span", {
1166
+ style: {
1167
+ fontSize: tokens.typography.fontSize.sm,
1168
+ color: tokens.colors.text.primary,
1169
+ fontWeight: 500
1170
+ },
1171
+ children: opt.label
1172
+ })]
1173
+ }), opt.description && /* @__PURE__ */ jsx("div", {
1174
+ style: {
1175
+ fontSize: tokens.typography.fontSize.xs,
1176
+ color: tokens.colors.text.tertiary,
1177
+ marginTop: 1
1178
+ },
1179
+ children: opt.description
1180
+ })]
1181
+ })]
1182
+ }, opt.id);
1183
+ })
1184
+ }),
1185
+ !isSubmitted && onEvent && /* @__PURE__ */ jsx("button", {
1186
+ type: "button",
1187
+ onClick: submit,
1188
+ disabled: localSelected.size === 0,
1189
+ style: {
1190
+ marginTop: 8,
1191
+ padding: "5px 18px",
1192
+ fontSize: tokens.typography.fontSize.xs,
1193
+ fontWeight: 600,
1194
+ borderRadius: tokens.borderRadius.sm,
1195
+ border: `1px solid ${tokens.colors.accent.primary}`,
1196
+ backgroundColor: localSelected.size > 0 ? `${tokens.colors.accent.primary}20` : "transparent",
1197
+ color: localSelected.size > 0 ? safeAccentText(tokens.colors.accent.primary) : tokens.colors.text.tertiary,
1198
+ cursor: localSelected.size > 0 ? "pointer" : "not-allowed",
1199
+ fontFamily: tokens.typography.fontFamily.primary,
1200
+ transition: "all 150ms"
1201
+ },
1202
+ children: "Confirm Selection"
1203
+ }),
1204
+ isSubmitted && /* @__PURE__ */ jsx("div", {
1205
+ style: {
1206
+ marginTop: 6,
1207
+ fontSize: tokens.typography.fontSize.xs,
1208
+ color: tokens.colors.text.tertiary,
1209
+ fontStyle: "italic"
1210
+ },
1211
+ children: "Selection submitted"
1212
+ })
1213
+ ]
1214
+ });
657
1215
  }
658
1216
  function ConfirmBlock({ block, messageId, onEvent }) {
659
- const { tokens } = useTheme();
660
- const status = block.status ?? "caution";
661
- const color = tokens.colors.status[status];
662
- const resolved = block.resolved;
663
- return /* @__PURE__ */ jsxs("div", { style: {
664
- marginTop: 8,
665
- padding: "12px 14px",
666
- backgroundColor: `${color}10`,
667
- border: `1px solid ${color}30`,
668
- borderLeft: `3px solid ${color}`,
669
- borderRadius: tokens.borderRadius.md
670
- }, children: [
671
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }, children: [
672
- /* @__PURE__ */ jsx(StatusShape, { status, size: 12, color }),
673
- /* @__PURE__ */ jsx("span", { style: { fontSize: tokens.typography.fontSize.sm, fontWeight: 600, color: tokens.colors.text.primary }, children: block.title })
674
- ] }),
675
- block.detail && /* @__PURE__ */ jsx("div", { style: { fontSize: tokens.typography.fontSize.xs, color: tokens.colors.text.secondary, marginBottom: 8, lineHeight: 1.5 }, children: block.detail }),
676
- !resolved && onEvent ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
677
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onEvent({ blockId: block.id, messageId, action: "confirm" }), style: {
678
- padding: "5px 16px",
679
- fontSize: tokens.typography.fontSize.xs,
680
- fontWeight: 600,
681
- borderRadius: tokens.borderRadius.sm,
682
- cursor: "pointer",
683
- border: `1px solid ${color}`,
684
- backgroundColor: `${color}20`,
685
- color,
686
- fontFamily: tokens.typography.fontFamily.primary,
687
- transition: "all 150ms"
688
- }, children: block.confirmLabel ?? "Confirm" }),
689
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onEvent({ blockId: block.id, messageId, action: "cancel" }), style: {
690
- padding: "5px 16px",
691
- fontSize: tokens.typography.fontSize.xs,
692
- fontWeight: 500,
693
- borderRadius: tokens.borderRadius.sm,
694
- cursor: "pointer",
695
- border: `1px solid ${tokens.colors.border.muted}`,
696
- backgroundColor: "transparent",
697
- color: tokens.colors.text.secondary,
698
- fontFamily: tokens.typography.fontFamily.primary,
699
- transition: "all 150ms"
700
- }, children: block.cancelLabel ?? "Cancel" })
701
- ] }) : resolved ? /* @__PURE__ */ jsx("div", { style: {
702
- fontSize: tokens.typography.fontSize.xs,
703
- fontWeight: 500,
704
- color: resolved === "confirmed" ? tokens.colors.status.normal : tokens.colors.text.tertiary,
705
- fontStyle: "italic"
706
- }, children: resolved === "confirmed" ? "✓ Confirmed" : "✗ Cancelled" }) : null
707
- ] });
1217
+ const { tokens } = useTheme();
1218
+ const status = block.status ?? "caution";
1219
+ const color = tokens.colors.status[status];
1220
+ const resolved = block.resolved;
1221
+ return /* @__PURE__ */ jsxs("div", {
1222
+ style: {
1223
+ marginTop: 8,
1224
+ padding: "12px 14px",
1225
+ backgroundColor: `${color}10`,
1226
+ border: `1px solid ${color}30`,
1227
+ borderLeft: `3px solid ${color}`,
1228
+ borderRadius: tokens.borderRadius.md
1229
+ },
1230
+ children: [
1231
+ /* @__PURE__ */ jsxs("div", {
1232
+ style: {
1233
+ display: "flex",
1234
+ alignItems: "center",
1235
+ gap: 8,
1236
+ marginBottom: 4
1237
+ },
1238
+ children: [/* @__PURE__ */ jsx(StatusShape, {
1239
+ status,
1240
+ size: 12,
1241
+ color
1242
+ }), /* @__PURE__ */ jsx("span", {
1243
+ style: {
1244
+ fontSize: tokens.typography.fontSize.sm,
1245
+ fontWeight: 600,
1246
+ color: tokens.colors.text.primary
1247
+ },
1248
+ children: block.title
1249
+ })]
1250
+ }),
1251
+ block.detail && /* @__PURE__ */ jsx("div", {
1252
+ style: {
1253
+ fontSize: tokens.typography.fontSize.xs,
1254
+ color: tokens.colors.text.secondary,
1255
+ marginBottom: 8,
1256
+ lineHeight: 1.5
1257
+ },
1258
+ children: block.detail
1259
+ }),
1260
+ !resolved && onEvent ? /* @__PURE__ */ jsxs("div", {
1261
+ style: {
1262
+ display: "flex",
1263
+ gap: 8
1264
+ },
1265
+ children: [/* @__PURE__ */ jsx("button", {
1266
+ type: "button",
1267
+ onClick: () => onEvent({
1268
+ blockId: block.id,
1269
+ messageId,
1270
+ action: "confirm"
1271
+ }),
1272
+ style: {
1273
+ padding: "5px 16px",
1274
+ fontSize: tokens.typography.fontSize.xs,
1275
+ fontWeight: 600,
1276
+ borderRadius: tokens.borderRadius.sm,
1277
+ cursor: "pointer",
1278
+ border: `1px solid ${color}`,
1279
+ backgroundColor: `${color}20`,
1280
+ color,
1281
+ fontFamily: tokens.typography.fontFamily.primary,
1282
+ transition: "all 150ms"
1283
+ },
1284
+ children: block.confirmLabel ?? "Confirm"
1285
+ }), /* @__PURE__ */ jsx("button", {
1286
+ type: "button",
1287
+ onClick: () => onEvent({
1288
+ blockId: block.id,
1289
+ messageId,
1290
+ action: "cancel"
1291
+ }),
1292
+ style: {
1293
+ padding: "5px 16px",
1294
+ fontSize: tokens.typography.fontSize.xs,
1295
+ fontWeight: 500,
1296
+ borderRadius: tokens.borderRadius.sm,
1297
+ cursor: "pointer",
1298
+ border: `1px solid ${tokens.colors.border.muted}`,
1299
+ backgroundColor: "transparent",
1300
+ color: tokens.colors.text.secondary,
1301
+ fontFamily: tokens.typography.fontFamily.primary,
1302
+ transition: "all 150ms"
1303
+ },
1304
+ children: block.cancelLabel ?? "Cancel"
1305
+ })]
1306
+ }) : resolved ? /* @__PURE__ */ jsx("div", {
1307
+ style: {
1308
+ fontSize: tokens.typography.fontSize.xs,
1309
+ fontWeight: 500,
1310
+ color: resolved === "confirmed" ? tokens.colors.status.normal : tokens.colors.text.tertiary,
1311
+ fontStyle: "italic"
1312
+ },
1313
+ children: resolved === "confirmed" ? "✓ Confirmed" : "✗ Cancelled"
1314
+ }) : null
1315
+ ]
1316
+ });
708
1317
  }
709
1318
  function CommandBlock({ block, messageId, onEvent }) {
710
- const { tokens } = useTheme();
711
- const [copied, setCopied] = useState(false);
712
- const handleCopy = () => {
713
- var _a;
714
- (_a = navigator.clipboard) == null ? void 0 : _a.writeText(block.command).then(() => {
715
- setCopied(true);
716
- setTimeout(() => setCopied(false), 2e3);
717
- });
718
- if (block.id) onEvent == null ? void 0 : onEvent({ blockId: block.id, messageId, action: "copy", value: block.command });
719
- };
720
- const handleExecute = () => {
721
- if (block.id) onEvent == null ? void 0 : onEvent({ blockId: block.id, messageId, action: "execute", value: block.command });
722
- };
723
- return /* @__PURE__ */ jsxs("div", { style: { marginTop: 8, borderRadius: tokens.borderRadius.md, overflow: "hidden", border: `1px solid ${tokens.colors.border.muted}` }, children: [
724
- block.label && /* @__PURE__ */ jsx("div", { style: {
725
- padding: "5px 12px",
726
- fontSize: tokens.typography.fontSize.xxs,
727
- fontWeight: 600,
728
- color: tokens.colors.text.tertiary,
729
- textTransform: "uppercase",
730
- letterSpacing: "0.05em",
731
- backgroundColor: `${tokens.colors.background.elevated}80`,
732
- borderBottom: `1px solid ${tokens.colors.border.muted}`,
733
- display: "flex",
734
- alignItems: "center",
735
- justifyContent: "space-between"
736
- }, children: /* @__PURE__ */ jsxs("span", { children: [
737
- block.label,
738
- block.language ? ` · ${block.language}` : ""
739
- ] }) }),
740
- /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
741
- /* @__PURE__ */ jsx("pre", { style: {
742
- margin: 0,
743
- padding: "10px 12px",
744
- paddingRight: 80,
745
- fontSize: tokens.typography.fontSize.xs,
746
- fontFamily: tokens.typography.fontFamily.mono,
747
- backgroundColor: `${tokens.colors.background.base}80`,
748
- color: tokens.colors.text.primary,
749
- overflowX: "auto",
750
- lineHeight: 1.5
751
- }, children: block.command }),
752
- /* @__PURE__ */ jsxs("div", { style: { position: "absolute", top: 6, right: 8, display: "flex", gap: 4 }, children: [
753
- /* @__PURE__ */ jsx("button", { type: "button", onClick: handleCopy, style: {
754
- padding: "2px 8px",
755
- fontSize: tokens.typography.fontSize.micro,
756
- borderRadius: tokens.borderRadius.sm,
757
- border: `1px solid ${tokens.colors.border.muted}`,
758
- backgroundColor: tokens.colors.background.elevated,
759
- color: copied ? tokens.colors.status.normal : tokens.colors.text.tertiary,
760
- cursor: "pointer",
761
- fontFamily: tokens.typography.fontFamily.primary,
762
- fontWeight: 500
763
- }, children: copied ? "✓ Copied" : "Copy" }),
764
- block.executable && onEvent && block.id && /* @__PURE__ */ jsx("button", { type: "button", onClick: handleExecute, style: {
765
- padding: "2px 8px",
766
- fontSize: tokens.typography.fontSize.micro,
767
- borderRadius: tokens.borderRadius.sm,
768
- border: `1px solid ${tokens.colors.accent.primary}60`,
769
- backgroundColor: `${tokens.colors.accent.primary}15`,
770
- color: safeAccentText(tokens.colors.accent.primary),
771
- cursor: "pointer",
772
- fontFamily: tokens.typography.fontFamily.primary,
773
- fontWeight: 600
774
- }, children: "Run" })
775
- ] })
776
- ] })
777
- ] });
1319
+ const { tokens } = useTheme();
1320
+ const [copied, setCopied] = useState(false);
1321
+ const handleCopy = () => {
1322
+ navigator.clipboard?.writeText(block.command).then(() => {
1323
+ setCopied(true);
1324
+ setTimeout(() => setCopied(false), 2e3);
1325
+ });
1326
+ if (block.id) onEvent?.({
1327
+ blockId: block.id,
1328
+ messageId,
1329
+ action: "copy",
1330
+ value: block.command
1331
+ });
1332
+ };
1333
+ const handleExecute = () => {
1334
+ if (block.id) onEvent?.({
1335
+ blockId: block.id,
1336
+ messageId,
1337
+ action: "execute",
1338
+ value: block.command
1339
+ });
1340
+ };
1341
+ return /* @__PURE__ */ jsxs("div", {
1342
+ style: {
1343
+ marginTop: 8,
1344
+ borderRadius: tokens.borderRadius.md,
1345
+ overflow: "hidden",
1346
+ border: `1px solid ${tokens.colors.border.muted}`
1347
+ },
1348
+ children: [block.label && /* @__PURE__ */ jsx("div", {
1349
+ style: {
1350
+ padding: "5px 12px",
1351
+ fontSize: tokens.typography.fontSize.xxs,
1352
+ fontWeight: 600,
1353
+ color: tokens.colors.text.tertiary,
1354
+ textTransform: "uppercase",
1355
+ letterSpacing: "0.05em",
1356
+ backgroundColor: `${tokens.colors.background.elevated}80`,
1357
+ borderBottom: `1px solid ${tokens.colors.border.muted}`,
1358
+ display: "flex",
1359
+ alignItems: "center",
1360
+ justifyContent: "space-between"
1361
+ },
1362
+ children: /* @__PURE__ */ jsxs("span", { children: [block.label, block.language ? ` · ${block.language}` : ""] })
1363
+ }), /* @__PURE__ */ jsxs("div", {
1364
+ style: { position: "relative" },
1365
+ children: [/* @__PURE__ */ jsx("pre", {
1366
+ style: {
1367
+ margin: 0,
1368
+ padding: "10px 12px",
1369
+ paddingRight: 80,
1370
+ fontSize: tokens.typography.fontSize.xs,
1371
+ fontFamily: tokens.typography.fontFamily.mono,
1372
+ backgroundColor: `${tokens.colors.background.base}80`,
1373
+ color: tokens.colors.text.primary,
1374
+ overflowX: "auto",
1375
+ lineHeight: 1.5
1376
+ },
1377
+ children: block.command
1378
+ }), /* @__PURE__ */ jsxs("div", {
1379
+ style: {
1380
+ position: "absolute",
1381
+ top: 6,
1382
+ right: 8,
1383
+ display: "flex",
1384
+ gap: 4
1385
+ },
1386
+ children: [/* @__PURE__ */ jsx("button", {
1387
+ type: "button",
1388
+ onClick: handleCopy,
1389
+ style: {
1390
+ padding: "2px 8px",
1391
+ fontSize: tokens.typography.fontSize.micro,
1392
+ borderRadius: tokens.borderRadius.sm,
1393
+ border: `1px solid ${tokens.colors.border.muted}`,
1394
+ backgroundColor: tokens.colors.background.elevated,
1395
+ color: copied ? tokens.colors.status.normal : tokens.colors.text.tertiary,
1396
+ cursor: "pointer",
1397
+ fontFamily: tokens.typography.fontFamily.primary,
1398
+ fontWeight: 500
1399
+ },
1400
+ children: copied ? "✓ Copied" : "Copy"
1401
+ }), block.executable && onEvent && block.id && /* @__PURE__ */ jsx("button", {
1402
+ type: "button",
1403
+ onClick: handleExecute,
1404
+ style: {
1405
+ padding: "2px 8px",
1406
+ fontSize: tokens.typography.fontSize.micro,
1407
+ borderRadius: tokens.borderRadius.sm,
1408
+ border: `1px solid ${tokens.colors.accent.primary}60`,
1409
+ backgroundColor: `${tokens.colors.accent.primary}15`,
1410
+ color: safeAccentText(tokens.colors.accent.primary),
1411
+ cursor: "pointer",
1412
+ fontFamily: tokens.typography.fontFamily.primary,
1413
+ fontWeight: 600
1414
+ },
1415
+ children: "Run"
1416
+ })]
1417
+ })]
1418
+ })]
1419
+ });
778
1420
  }
779
1421
  function KVBlock({ block }) {
780
- const { tokens } = useTheme();
781
- return /* @__PURE__ */ jsxs("div", { style: {
782
- marginTop: 8,
783
- padding: "8px 12px",
784
- backgroundColor: `${tokens.colors.background.elevated}40`,
785
- borderRadius: tokens.borderRadius.md,
786
- border: `1px solid ${tokens.colors.border.muted}`
787
- }, children: [
788
- block.title && /* @__PURE__ */ jsx("div", { style: {
789
- fontSize: tokens.typography.fontSize.xxs,
790
- fontWeight: 600,
791
- color: tokens.colors.text.tertiary,
792
- textTransform: "uppercase",
793
- letterSpacing: "0.05em",
794
- marginBottom: 6
795
- }, children: block.title }),
796
- /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "auto 1fr", gap: "3px 16px", fontSize: tokens.typography.fontSize.xs }, children: (block.pairs ?? []).map((p, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
797
- /* @__PURE__ */ jsx("span", { style: { color: tokens.colors.text.tertiary, whiteSpace: "nowrap" }, children: p.key }),
798
- /* @__PURE__ */ jsx("span", { style: {
799
- color: tokens.colors.text.primary,
800
- fontFamily: p.mono ? tokens.typography.fontFamily.mono : void 0,
801
- fontVariantNumeric: p.mono ? "tabular-nums" : void 0
802
- }, children: p.value })
803
- ] }, i)) })
804
- ] });
1422
+ const { tokens } = useTheme();
1423
+ return /* @__PURE__ */ jsxs("div", {
1424
+ style: {
1425
+ marginTop: 8,
1426
+ padding: "8px 12px",
1427
+ backgroundColor: `${tokens.colors.background.elevated}40`,
1428
+ borderRadius: tokens.borderRadius.md,
1429
+ border: `1px solid ${tokens.colors.border.muted}`
1430
+ },
1431
+ children: [block.title && /* @__PURE__ */ jsx("div", {
1432
+ style: {
1433
+ fontSize: tokens.typography.fontSize.xxs,
1434
+ fontWeight: 600,
1435
+ color: tokens.colors.text.tertiary,
1436
+ textTransform: "uppercase",
1437
+ letterSpacing: "0.05em",
1438
+ marginBottom: 6
1439
+ },
1440
+ children: block.title
1441
+ }), /* @__PURE__ */ jsx("div", {
1442
+ style: {
1443
+ display: "grid",
1444
+ gridTemplateColumns: "auto 1fr",
1445
+ gap: "3px 16px",
1446
+ fontSize: tokens.typography.fontSize.xs
1447
+ },
1448
+ children: (block.pairs ?? []).map((p, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsx("span", {
1449
+ style: {
1450
+ color: tokens.colors.text.tertiary,
1451
+ whiteSpace: "nowrap"
1452
+ },
1453
+ children: p.key
1454
+ }), /* @__PURE__ */ jsx("span", {
1455
+ style: {
1456
+ color: tokens.colors.text.primary,
1457
+ fontFamily: p.mono ? tokens.typography.fontFamily.mono : void 0,
1458
+ fontVariantNumeric: p.mono ? "tabular-nums" : void 0
1459
+ },
1460
+ children: p.value
1461
+ })] }, i))
1462
+ })]
1463
+ });
805
1464
  }
806
1465
  function renderBlock(block, index, messageId, onEvent) {
807
- switch (block.type) {
808
- case "alert":
809
- return /* @__PURE__ */ jsx(AlertBlock, { block }, index);
810
- case "telemetry":
811
- return /* @__PURE__ */ jsx(TelemetryBlock, { block }, index);
812
- case "progress":
813
- return /* @__PURE__ */ jsx(ProgressBlock, { block }, index);
814
- case "table":
815
- return /* @__PURE__ */ jsx(TableBlock, { block }, index);
816
- case "actions":
817
- return /* @__PURE__ */ jsx(ActionsBlock, { block }, index);
818
- case "choice":
819
- return /* @__PURE__ */ jsx(ChoiceBlock, { block, messageId, onEvent }, index);
820
- case "confirm":
821
- return /* @__PURE__ */ jsx(ConfirmBlock, { block, messageId, onEvent }, index);
822
- case "command":
823
- return /* @__PURE__ */ jsx(CommandBlock, { block, messageId, onEvent }, index);
824
- case "kv":
825
- return /* @__PURE__ */ jsx(KVBlock, { block }, index);
826
- default:
827
- return null;
828
- }
1466
+ switch (block.type) {
1467
+ case "alert": return /* @__PURE__ */ jsx(AlertBlock, { block }, index);
1468
+ case "telemetry": return /* @__PURE__ */ jsx(TelemetryBlock, { block }, index);
1469
+ case "progress": return /* @__PURE__ */ jsx(ProgressBlock, { block }, index);
1470
+ case "table": return /* @__PURE__ */ jsx(TableBlock, { block }, index);
1471
+ case "actions": return /* @__PURE__ */ jsx(ActionsBlock, { block }, index);
1472
+ case "choice": return /* @__PURE__ */ jsx(ChoiceBlock, {
1473
+ block,
1474
+ messageId,
1475
+ onEvent
1476
+ }, index);
1477
+ case "confirm": return /* @__PURE__ */ jsx(ConfirmBlock, {
1478
+ block,
1479
+ messageId,
1480
+ onEvent
1481
+ }, index);
1482
+ case "command": return /* @__PURE__ */ jsx(CommandBlock, {
1483
+ block,
1484
+ messageId,
1485
+ onEvent
1486
+ }, index);
1487
+ case "kv": return /* @__PURE__ */ jsx(KVBlock, { block }, index);
1488
+ default: return null;
1489
+ }
829
1490
  }
830
- const ChatPanel = memo(function ChatPanel2({
831
- messages = [],
832
- onSend,
833
- loading = false,
834
- placeholder = "Type a message...",
835
- title,
836
- subtitle,
837
- disabled = false,
838
- showTimestamps = true,
839
- height = "100%",
840
- onClear,
841
- onBlockEvent,
842
- style
843
- }) {
844
- const { tokens } = useTheme();
845
- const [input, setInput] = useState("");
846
- const messagesEndRef = useRef(null);
847
- const inputRef = useRef(null);
848
- useEffect(() => {
849
- var _a;
850
- (_a = messagesEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
851
- }, [messages, loading]);
852
- const handleSend = useCallback(() => {
853
- const text = input.trim();
854
- if (!text || disabled) return;
855
- onSend(text);
856
- setInput("");
857
- if (inputRef.current) inputRef.current.style.height = "auto";
858
- }, [input, onSend, disabled]);
859
- const handleKeyDown = useCallback((e) => {
860
- if (e.key === "Enter" && !e.shiftKey) {
861
- e.preventDefault();
862
- handleSend();
863
- }
864
- }, [handleSend]);
865
- const handleInput = useCallback((e) => {
866
- setInput(e.target.value);
867
- const el = e.target;
868
- el.style.height = "auto";
869
- el.style.height = `${Math.min(el.scrollHeight, 120)}px`;
870
- }, []);
871
- const formatTime = (ts) => {
872
- if (!ts) return "";
873
- return new Date(ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
874
- };
875
- const renderContent = (text) => {
876
- const parts = [];
877
- const lines = text.split("\n");
878
- let inCode = false;
879
- let codeBlock = [];
880
- lines.forEach((line, i) => {
881
- if (line.startsWith("```")) {
882
- if (inCode) {
883
- parts.push(
884
- /* @__PURE__ */ jsx("pre", { style: {
885
- backgroundColor: `${tokens.colors.background.base}80`,
886
- padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,
887
- borderRadius: tokens.borderRadius.sm,
888
- fontSize: tokens.typography.fontSize.xs,
889
- fontFamily: tokens.typography.fontFamily.mono,
890
- overflowX: "auto",
891
- margin: `${tokens.spacing.xs} 0`,
892
- border: `1px solid ${tokens.colors.border.muted}`
893
- }, children: codeBlock.join("\n") }, `code-${i}`)
894
- );
895
- codeBlock = [];
896
- inCode = false;
897
- } else {
898
- inCode = true;
899
- }
900
- return;
901
- }
902
- if (inCode) {
903
- codeBlock.push(line);
904
- return;
905
- }
906
- let processed = line;
907
- if (line.includes("**")) {
908
- const segments = line.split(/\*\*(.*?)\*\*/g);
909
- processed = segments.map((seg, j) => j % 2 === 1 ? /* @__PURE__ */ jsx("strong", { children: seg }, j) : seg);
910
- }
911
- if (typeof processed === "string" && processed.includes("`")) {
912
- const segments = processed.split(/`(.*?)`/g);
913
- processed = segments.map(
914
- (seg, j) => j % 2 === 1 ? /* @__PURE__ */ jsx("code", { style: {
915
- backgroundColor: `${tokens.colors.background.base}80`,
916
- padding: "1px 4px",
917
- borderRadius: "3px",
918
- fontSize: "0.9em",
919
- fontFamily: tokens.typography.fontFamily.mono
920
- }, children: seg }, j) : seg
921
- );
922
- }
923
- parts.push(/* @__PURE__ */ jsxs(React.Fragment, { children: [
924
- processed,
925
- i < lines.length - 1 && /* @__PURE__ */ jsx("br", {})
926
- ] }, `line-${i}`));
927
- });
928
- return parts;
929
- };
930
- const TypingDots = () => /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px", padding: tokens.spacing.xs }, children: [
931
- [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { style: {
932
- width: 6,
933
- height: 6,
934
- borderRadius: "50%",
935
- backgroundColor: tokens.colors.accent.primary,
936
- opacity: 0.6,
937
- animation: `zendir-typing 1.2s ease-in-out ${i * 0.2}s infinite`
938
- } }, i)),
939
- /* @__PURE__ */ jsx("style", { children: `@keyframes zendir-typing { 0%,60%,100%{opacity:.3;transform:translateY(0)} 30%{opacity:1;transform:translateY(-3px)} }` })
940
- ] });
941
- return /* @__PURE__ */ jsxs("div", { style: {
942
- display: "flex",
943
- flexDirection: "column",
944
- height: typeof height === "number" ? `${height}px` : height,
945
- fontFamily: tokens.typography.fontFamily.primary,
946
- backgroundColor: tokens.colors.background.base,
947
- borderRadius: tokens.borderRadius.lg,
948
- border: `1px solid ${tokens.colors.border.muted}`,
949
- overflow: "hidden",
950
- ...style
951
- }, children: [
952
- (title || onClear) && /* @__PURE__ */ jsxs("div", { style: {
953
- display: "flex",
954
- alignItems: "center",
955
- justifyContent: "space-between",
956
- padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,
957
- borderBottom: `1px solid ${tokens.colors.border.muted}`,
958
- backgroundColor: tokens.colors.background.surface
959
- }, children: [
960
- /* @__PURE__ */ jsxs("div", { children: [
961
- title && /* @__PURE__ */ jsx("div", { style: { fontSize: tokens.typography.fontSize.sm, fontWeight: 600, color: tokens.colors.text.primary }, children: title }),
962
- subtitle && /* @__PURE__ */ jsx("div", { style: { fontSize: tokens.typography.fontSize.xxs, color: tokens.colors.text.tertiary }, children: subtitle })
963
- ] }),
964
- onClear && messages.length > 0 && /* @__PURE__ */ jsx("button", { onClick: onClear, style: {
965
- fontSize: tokens.typography.fontSize.xxs,
966
- color: tokens.colors.text.tertiary,
967
- backgroundColor: "transparent",
968
- border: "none",
969
- cursor: "pointer",
970
- padding: "4px 8px",
971
- borderRadius: tokens.borderRadius.sm,
972
- fontFamily: tokens.typography.fontFamily.primary
973
- }, children: "Clear" })
974
- ] }),
975
- /* @__PURE__ */ jsxs(
976
- "div",
977
- {
978
- role: "log",
979
- "aria-live": "polite",
980
- "aria-label": "Chat messages",
981
- style: {
982
- flex: 1,
983
- overflow: "auto",
984
- padding: tokens.spacing.md,
985
- display: "flex",
986
- flexDirection: "column",
987
- gap: tokens.spacing.sm
988
- },
989
- children: [
990
- messages.length === 0 && /* @__PURE__ */ jsx("div", { style: {
991
- display: "flex",
992
- alignItems: "center",
993
- justifyContent: "center",
994
- flex: 1,
995
- color: tokens.colors.text.tertiary,
996
- fontSize: tokens.typography.fontSize.sm
997
- }, children: "No messages yet" }),
998
- messages.map((msg) => {
999
- const isUser = msg.role === "user";
1000
- const isSystem = msg.role === "system";
1001
- const severity = msg.severity;
1002
- const severityColor = severity ? tokens.colors.status[severity] : void 0;
1003
- const systemBg = severityColor ? `${severityColor}12` : `${tokens.colors.status.caution}10`;
1004
- const systemBorder = severityColor ? `${severityColor}30` : `${tokens.colors.status.caution}20`;
1005
- const systemAccent = severityColor || void 0;
1006
- return /* @__PURE__ */ jsxs("div", { style: {
1007
- display: "flex",
1008
- flexDirection: "column",
1009
- alignItems: isUser ? "flex-end" : "flex-start",
1010
- maxWidth: isSystem ? "100%" : "85%",
1011
- alignSelf: isUser ? "flex-end" : "flex-start"
1012
- }, children: [
1013
- /* @__PURE__ */ jsxs("div", { style: {
1014
- padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,
1015
- borderRadius: tokens.borderRadius.md,
1016
- backgroundColor: isUser ? `${tokens.colors.accent.primary}20` : isSystem ? systemBg : tokens.colors.background.surface,
1017
- border: `1px solid ${isUser ? `${tokens.colors.accent.primary}30` : isSystem ? systemBorder : tokens.colors.border.muted}`,
1018
- borderLeft: isSystem && systemAccent ? `3px solid ${systemAccent}` : void 0,
1019
- fontSize: tokens.typography.fontSize.sm,
1020
- color: tokens.colors.text.primary,
1021
- lineHeight: 1.6,
1022
- width: isSystem ? "100%" : "auto",
1023
- opacity: msg.status === "sending" ? 0.7 : 1
1024
- }, children: [
1025
- isSystem && severity && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }, children: [
1026
- /* @__PURE__ */ jsx(StatusShape, { status: severity, size: 10, color: severityColor }),
1027
- /* @__PURE__ */ jsx("span", { style: {
1028
- fontSize: tokens.typography.fontSize.xxs,
1029
- fontWeight: 600,
1030
- color: severityColor,
1031
- textTransform: "uppercase",
1032
- letterSpacing: "0.04em"
1033
- }, children: severity })
1034
- ] }),
1035
- renderContent(msg.content),
1036
- Array.isArray(msg.blocks) && msg.blocks.map((block, i) => block ? renderBlock(block, i, msg.id, onBlockEvent) : null),
1037
- msg.actions && msg.actions.length > 0 && /* @__PURE__ */ jsx("div", { style: {
1038
- display: "flex",
1039
- gap: tokens.spacing.xs,
1040
- marginTop: tokens.spacing.xs,
1041
- paddingTop: tokens.spacing.xs,
1042
- borderTop: `1px solid ${tokens.colors.border.muted}`
1043
- }, children: msg.actions.map((action, i) => /* @__PURE__ */ jsx("button", { onClick: action.onClick, style: {
1044
- fontSize: tokens.typography.fontSize.xxs,
1045
- padding: "2px 10px",
1046
- borderRadius: tokens.borderRadius.sm,
1047
- border: `1px solid ${tokens.colors.accent.primary}40`,
1048
- backgroundColor: `${tokens.colors.accent.primary}10`,
1049
- color: safeAccentText(tokens.colors.accent.primary),
1050
- cursor: "pointer",
1051
- fontFamily: tokens.typography.fontFamily.primary
1052
- }, children: action.label }, i)) })
1053
- ] }),
1054
- showTimestamps && msg.timestamp && /* @__PURE__ */ jsxs("div", { style: {
1055
- fontSize: tokens.typography.fontSize.micro,
1056
- color: tokens.colors.text.tertiary,
1057
- marginTop: "2px",
1058
- paddingLeft: tokens.spacing.xs,
1059
- paddingRight: tokens.spacing.xs,
1060
- display: "flex",
1061
- alignItems: "center",
1062
- gap: "4px"
1063
- }, children: [
1064
- formatTime(msg.timestamp),
1065
- msg.status === "error" && /* @__PURE__ */ jsx("span", { style: { color: tokens.colors.status.critical }, children: "• Failed" })
1066
- ] })
1067
- ] }, msg.id);
1068
- }),
1069
- loading && /* @__PURE__ */ jsx("div", { style: {
1070
- alignSelf: "flex-start",
1071
- padding: `${tokens.spacing.xs} ${tokens.spacing.md}`,
1072
- borderRadius: tokens.borderRadius.md,
1073
- backgroundColor: tokens.colors.background.surface,
1074
- border: `1px solid ${tokens.colors.border.muted}`
1075
- }, children: /* @__PURE__ */ jsx(TypingDots, {}) }),
1076
- /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1077
- ]
1078
- }
1079
- ),
1080
- /* @__PURE__ */ jsxs("div", { style: {
1081
- display: "flex",
1082
- alignItems: "flex-end",
1083
- gap: tokens.spacing.sm,
1084
- padding: tokens.spacing.md,
1085
- borderTop: `1px solid ${tokens.colors.border.muted}`,
1086
- backgroundColor: tokens.colors.background.surface
1087
- }, children: [
1088
- /* @__PURE__ */ jsx(
1089
- "textarea",
1090
- {
1091
- ref: inputRef,
1092
- value: input,
1093
- onChange: handleInput,
1094
- onKeyDown: handleKeyDown,
1095
- placeholder,
1096
- disabled,
1097
- "aria-label": "Message input",
1098
- rows: 1,
1099
- style: {
1100
- flex: 1,
1101
- resize: "none",
1102
- border: `1px solid ${tokens.colors.border.muted}`,
1103
- borderRadius: tokens.borderRadius.md,
1104
- padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,
1105
- fontSize: tokens.typography.fontSize.sm,
1106
- fontFamily: tokens.typography.fontFamily.primary,
1107
- color: tokens.colors.text.primary,
1108
- backgroundColor: tokens.colors.background.base,
1109
- outline: "none",
1110
- transition: "border-color 150ms, background-color 150ms",
1111
- maxHeight: 120,
1112
- lineHeight: 1.5,
1113
- overflowY: "auto"
1114
- }
1115
- }
1116
- ),
1117
- /* @__PURE__ */ jsx("button", { onClick: handleSend, disabled: disabled || !input.trim(), "aria-label": "Send message", style: {
1118
- display: "flex",
1119
- alignItems: "center",
1120
- justifyContent: "center",
1121
- width: 44,
1122
- height: 44,
1123
- borderRadius: tokens.borderRadius.md,
1124
- border: "none",
1125
- backgroundColor: input.trim() ? tokens.colors.accent.primary : `${tokens.colors.accent.primary}20`,
1126
- color: input.trim() ? tokens.colors.text.inverse : tokens.colors.text.tertiary,
1127
- cursor: input.trim() && !disabled ? "pointer" : "not-allowed",
1128
- transition: tokens.animation.fast,
1129
- flexShrink: 0
1130
- }, children: /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) }) })
1131
- ] })
1132
- ] });
1491
+ var ChatPanel = memo(function ChatPanel({ messages = [], onSend, loading = false, placeholder = "Type a message...", title, subtitle, disabled = false, showTimestamps = true, height = "100%", onClear, onBlockEvent, style }) {
1492
+ const { tokens } = useTheme();
1493
+ const [input, setInput] = useState("");
1494
+ const messagesEndRef = useRef(null);
1495
+ const inputRef = useRef(null);
1496
+ useEffect(() => {
1497
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1498
+ }, [messages, loading]);
1499
+ const handleSend = useCallback(() => {
1500
+ const text = input.trim();
1501
+ if (!text || disabled) return;
1502
+ onSend(text);
1503
+ setInput("");
1504
+ if (inputRef.current) inputRef.current.style.height = "auto";
1505
+ }, [
1506
+ input,
1507
+ onSend,
1508
+ disabled
1509
+ ]);
1510
+ const handleKeyDown = useCallback((e) => {
1511
+ if (e.key === "Enter" && !e.shiftKey) {
1512
+ e.preventDefault();
1513
+ handleSend();
1514
+ }
1515
+ }, [handleSend]);
1516
+ const handleInput = useCallback((e) => {
1517
+ setInput(e.target.value);
1518
+ const el = e.target;
1519
+ el.style.height = "auto";
1520
+ el.style.height = `${Math.min(el.scrollHeight, 120)}px`;
1521
+ }, []);
1522
+ const formatTime = (ts) => {
1523
+ if (!ts) return "";
1524
+ return new Date(ts).toLocaleTimeString([], {
1525
+ hour: "2-digit",
1526
+ minute: "2-digit"
1527
+ });
1528
+ };
1529
+ const renderContent = (text) => {
1530
+ const parts = [];
1531
+ const lines = text.split("\n");
1532
+ let inCode = false;
1533
+ let codeBlock = [];
1534
+ lines.forEach((line, i) => {
1535
+ if (line.startsWith("```")) {
1536
+ if (inCode) {
1537
+ parts.push(/* @__PURE__ */ jsx("pre", {
1538
+ style: {
1539
+ backgroundColor: `${tokens.colors.background.base}80`,
1540
+ padding: `${tokens.spacing.xs} ${tokens.spacing.sm}`,
1541
+ borderRadius: tokens.borderRadius.sm,
1542
+ fontSize: tokens.typography.fontSize.xs,
1543
+ fontFamily: tokens.typography.fontFamily.mono,
1544
+ overflowX: "auto",
1545
+ margin: `${tokens.spacing.xs} 0`,
1546
+ border: `1px solid ${tokens.colors.border.muted}`
1547
+ },
1548
+ children: codeBlock.join("\n")
1549
+ }, `code-${i}`));
1550
+ codeBlock = [];
1551
+ inCode = false;
1552
+ } else inCode = true;
1553
+ return;
1554
+ }
1555
+ if (inCode) {
1556
+ codeBlock.push(line);
1557
+ return;
1558
+ }
1559
+ let processed = line;
1560
+ if (line.includes("**")) processed = line.split(/\*\*(.*?)\*\*/g).map((seg, j) => j % 2 === 1 ? /* @__PURE__ */ jsx("strong", { children: seg }, j) : seg);
1561
+ if (typeof processed === "string" && processed.includes("`")) processed = processed.split(/`(.*?)`/g).map((seg, j) => j % 2 === 1 ? /* @__PURE__ */ jsx("code", {
1562
+ style: {
1563
+ backgroundColor: `${tokens.colors.background.base}80`,
1564
+ padding: "1px 4px",
1565
+ borderRadius: "3px",
1566
+ fontSize: "0.9em",
1567
+ fontFamily: tokens.typography.fontFamily.mono
1568
+ },
1569
+ children: seg
1570
+ }, j) : seg);
1571
+ parts.push(/* @__PURE__ */ jsxs(React.Fragment, { children: [processed, i < lines.length - 1 && /* @__PURE__ */ jsx("br", {})] }, `line-${i}`));
1572
+ });
1573
+ return parts;
1574
+ };
1575
+ const TypingDots = () => /* @__PURE__ */ jsxs("div", {
1576
+ style: {
1577
+ display: "flex",
1578
+ alignItems: "center",
1579
+ gap: "4px",
1580
+ padding: tokens.spacing.xs
1581
+ },
1582
+ children: [[
1583
+ 0,
1584
+ 1,
1585
+ 2
1586
+ ].map((i) => /* @__PURE__ */ jsx("div", { style: {
1587
+ width: 6,
1588
+ height: 6,
1589
+ borderRadius: "50%",
1590
+ backgroundColor: tokens.colors.accent.primary,
1591
+ opacity: .6,
1592
+ animation: `zendir-typing 1.2s ease-in-out ${i * .2}s infinite`
1593
+ } }, i)), /* @__PURE__ */ jsx("style", { children: `@keyframes zendir-typing { 0%,60%,100%{opacity:.3;transform:translateY(0)} 30%{opacity:1;transform:translateY(-3px)} }` })]
1594
+ });
1595
+ return /* @__PURE__ */ jsxs("div", {
1596
+ style: {
1597
+ display: "flex",
1598
+ flexDirection: "column",
1599
+ height: typeof height === "number" ? `${height}px` : height,
1600
+ fontFamily: tokens.typography.fontFamily.primary,
1601
+ backgroundColor: tokens.colors.background.base,
1602
+ borderRadius: tokens.borderRadius.lg,
1603
+ border: `1px solid ${tokens.colors.border.muted}`,
1604
+ overflow: "hidden",
1605
+ ...style
1606
+ },
1607
+ children: [
1608
+ (title || onClear) && /* @__PURE__ */ jsxs("div", {
1609
+ style: {
1610
+ display: "flex",
1611
+ alignItems: "center",
1612
+ justifyContent: "space-between",
1613
+ padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,
1614
+ borderBottom: `1px solid ${tokens.colors.border.muted}`,
1615
+ backgroundColor: tokens.colors.background.surface
1616
+ },
1617
+ children: [/* @__PURE__ */ jsxs("div", { children: [title && /* @__PURE__ */ jsx("div", {
1618
+ style: {
1619
+ fontSize: tokens.typography.fontSize.sm,
1620
+ fontWeight: 600,
1621
+ color: tokens.colors.text.primary
1622
+ },
1623
+ children: title
1624
+ }), subtitle && /* @__PURE__ */ jsx("div", {
1625
+ style: {
1626
+ fontSize: tokens.typography.fontSize.xxs,
1627
+ color: tokens.colors.text.tertiary
1628
+ },
1629
+ children: subtitle
1630
+ })] }), onClear && messages.length > 0 && /* @__PURE__ */ jsx("button", {
1631
+ onClick: onClear,
1632
+ style: {
1633
+ fontSize: tokens.typography.fontSize.xxs,
1634
+ color: tokens.colors.text.tertiary,
1635
+ backgroundColor: "transparent",
1636
+ border: "none",
1637
+ cursor: "pointer",
1638
+ padding: "4px 8px",
1639
+ borderRadius: tokens.borderRadius.sm,
1640
+ fontFamily: tokens.typography.fontFamily.primary
1641
+ },
1642
+ children: "Clear"
1643
+ })]
1644
+ }),
1645
+ /* @__PURE__ */ jsxs("div", {
1646
+ role: "log",
1647
+ "aria-live": "polite",
1648
+ "aria-label": "Chat messages",
1649
+ style: {
1650
+ flex: 1,
1651
+ overflow: "auto",
1652
+ padding: tokens.spacing.md,
1653
+ display: "flex",
1654
+ flexDirection: "column",
1655
+ gap: tokens.spacing.sm
1656
+ },
1657
+ children: [
1658
+ messages.length === 0 && /* @__PURE__ */ jsx("div", {
1659
+ style: {
1660
+ display: "flex",
1661
+ alignItems: "center",
1662
+ justifyContent: "center",
1663
+ flex: 1,
1664
+ color: tokens.colors.text.tertiary,
1665
+ fontSize: tokens.typography.fontSize.sm
1666
+ },
1667
+ children: "No messages yet"
1668
+ }),
1669
+ messages.map((msg) => {
1670
+ const isUser = msg.role === "user";
1671
+ const isSystem = msg.role === "system";
1672
+ const severity = msg.severity;
1673
+ const severityColor = severity ? tokens.colors.status[severity] : void 0;
1674
+ const systemBg = severityColor ? `${severityColor}12` : `${tokens.colors.status.caution}10`;
1675
+ const systemBorder = severityColor ? `${severityColor}30` : `${tokens.colors.status.caution}20`;
1676
+ const systemAccent = severityColor || void 0;
1677
+ return /* @__PURE__ */ jsxs("div", {
1678
+ style: {
1679
+ display: "flex",
1680
+ flexDirection: "column",
1681
+ alignItems: isUser ? "flex-end" : "flex-start",
1682
+ maxWidth: isSystem ? "100%" : "85%",
1683
+ alignSelf: isUser ? "flex-end" : "flex-start"
1684
+ },
1685
+ children: [/* @__PURE__ */ jsxs("div", {
1686
+ style: {
1687
+ padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,
1688
+ borderRadius: tokens.borderRadius.md,
1689
+ backgroundColor: isUser ? `${tokens.colors.accent.primary}20` : isSystem ? systemBg : tokens.colors.background.surface,
1690
+ border: `1px solid ${isUser ? `${tokens.colors.accent.primary}30` : isSystem ? systemBorder : tokens.colors.border.muted}`,
1691
+ borderLeft: isSystem && systemAccent ? `3px solid ${systemAccent}` : void 0,
1692
+ fontSize: tokens.typography.fontSize.sm,
1693
+ color: tokens.colors.text.primary,
1694
+ lineHeight: 1.6,
1695
+ width: isSystem ? "100%" : "auto",
1696
+ opacity: msg.status === "sending" ? .7 : 1
1697
+ },
1698
+ children: [
1699
+ isSystem && severity && /* @__PURE__ */ jsxs("div", {
1700
+ style: {
1701
+ display: "flex",
1702
+ alignItems: "center",
1703
+ gap: 6,
1704
+ marginBottom: 4
1705
+ },
1706
+ children: [/* @__PURE__ */ jsx(StatusShape, {
1707
+ status: severity,
1708
+ size: 10,
1709
+ color: severityColor
1710
+ }), /* @__PURE__ */ jsx("span", {
1711
+ style: {
1712
+ fontSize: tokens.typography.fontSize.xxs,
1713
+ fontWeight: 600,
1714
+ color: severityColor,
1715
+ textTransform: "uppercase",
1716
+ letterSpacing: "0.04em"
1717
+ },
1718
+ children: severity
1719
+ })]
1720
+ }),
1721
+ renderContent(msg.content),
1722
+ Array.isArray(msg.blocks) && msg.blocks.map((block, i) => block ? renderBlock(block, i, msg.id, onBlockEvent) : null),
1723
+ msg.actions && msg.actions.length > 0 && /* @__PURE__ */ jsx("div", {
1724
+ style: {
1725
+ display: "flex",
1726
+ gap: tokens.spacing.xs,
1727
+ marginTop: tokens.spacing.xs,
1728
+ paddingTop: tokens.spacing.xs,
1729
+ borderTop: `1px solid ${tokens.colors.border.muted}`
1730
+ },
1731
+ children: msg.actions.map((action, i) => /* @__PURE__ */ jsx("button", {
1732
+ onClick: action.onClick,
1733
+ style: {
1734
+ fontSize: tokens.typography.fontSize.xxs,
1735
+ padding: "2px 10px",
1736
+ borderRadius: tokens.borderRadius.sm,
1737
+ border: `1px solid ${tokens.colors.accent.primary}40`,
1738
+ backgroundColor: `${tokens.colors.accent.primary}10`,
1739
+ color: safeAccentText(tokens.colors.accent.primary),
1740
+ cursor: "pointer",
1741
+ fontFamily: tokens.typography.fontFamily.primary
1742
+ },
1743
+ children: action.label
1744
+ }, i))
1745
+ })
1746
+ ]
1747
+ }), showTimestamps && msg.timestamp && /* @__PURE__ */ jsxs("div", {
1748
+ style: {
1749
+ fontSize: tokens.typography.fontSize.micro,
1750
+ color: tokens.colors.text.tertiary,
1751
+ marginTop: "2px",
1752
+ paddingLeft: tokens.spacing.xs,
1753
+ paddingRight: tokens.spacing.xs,
1754
+ display: "flex",
1755
+ alignItems: "center",
1756
+ gap: "4px"
1757
+ },
1758
+ children: [formatTime(msg.timestamp), msg.status === "error" && /* @__PURE__ */ jsx("span", {
1759
+ style: { color: tokens.colors.status.critical },
1760
+ children: "• Failed"
1761
+ })]
1762
+ })]
1763
+ }, msg.id);
1764
+ }),
1765
+ loading && /* @__PURE__ */ jsx("div", {
1766
+ style: {
1767
+ alignSelf: "flex-start",
1768
+ padding: `${tokens.spacing.xs} ${tokens.spacing.md}`,
1769
+ borderRadius: tokens.borderRadius.md,
1770
+ backgroundColor: tokens.colors.background.surface,
1771
+ border: `1px solid ${tokens.colors.border.muted}`
1772
+ },
1773
+ children: /* @__PURE__ */ jsx(TypingDots, {})
1774
+ }),
1775
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
1776
+ ]
1777
+ }),
1778
+ /* @__PURE__ */ jsxs("div", {
1779
+ style: {
1780
+ display: "flex",
1781
+ alignItems: "flex-end",
1782
+ gap: tokens.spacing.sm,
1783
+ padding: tokens.spacing.md,
1784
+ borderTop: `1px solid ${tokens.colors.border.muted}`,
1785
+ backgroundColor: tokens.colors.background.surface
1786
+ },
1787
+ children: [/* @__PURE__ */ jsx("textarea", {
1788
+ ref: inputRef,
1789
+ value: input,
1790
+ onChange: handleInput,
1791
+ onKeyDown: handleKeyDown,
1792
+ placeholder,
1793
+ disabled,
1794
+ "aria-label": "Message input",
1795
+ rows: 1,
1796
+ style: {
1797
+ flex: 1,
1798
+ resize: "none",
1799
+ border: `1px solid ${tokens.colors.border.muted}`,
1800
+ borderRadius: tokens.borderRadius.md,
1801
+ padding: `${tokens.spacing.sm} ${tokens.spacing.md}`,
1802
+ fontSize: tokens.typography.fontSize.sm,
1803
+ fontFamily: tokens.typography.fontFamily.primary,
1804
+ color: tokens.colors.text.primary,
1805
+ backgroundColor: tokens.colors.background.base,
1806
+ outline: "none",
1807
+ transition: "border-color 150ms, background-color 150ms",
1808
+ maxHeight: 120,
1809
+ lineHeight: 1.5,
1810
+ overflowY: "auto"
1811
+ }
1812
+ }), /* @__PURE__ */ jsx("button", {
1813
+ onClick: handleSend,
1814
+ disabled: disabled || !input.trim(),
1815
+ "aria-label": "Send message",
1816
+ style: {
1817
+ display: "flex",
1818
+ alignItems: "center",
1819
+ justifyContent: "center",
1820
+ width: 44,
1821
+ height: 44,
1822
+ borderRadius: tokens.borderRadius.md,
1823
+ border: "none",
1824
+ backgroundColor: input.trim() ? tokens.colors.accent.primary : `${tokens.colors.accent.primary}20`,
1825
+ color: input.trim() ? tokens.colors.text.inverse : tokens.colors.text.tertiary,
1826
+ cursor: input.trim() && !disabled ? "pointer" : "not-allowed",
1827
+ transition: tokens.animation.fast,
1828
+ flexShrink: 0
1829
+ },
1830
+ children: /* @__PURE__ */ jsx("svg", {
1831
+ width: "18",
1832
+ height: "18",
1833
+ viewBox: "0 0 24 24",
1834
+ fill: "currentColor",
1835
+ children: /* @__PURE__ */ jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" })
1836
+ })
1837
+ })]
1838
+ })
1839
+ ]
1840
+ });
1133
1841
  });
1134
- export {
1135
- CHAT_RESPONSE_JSON_PROMPT,
1136
- CHAT_RESPONSE_MCP_TOOL,
1137
- CHAT_RESPONSE_TOOL_SCHEMA,
1138
- CHAT_RESPONSE_YAML_PROMPT,
1139
- CHAT_STATUS_RULES_PROMPT,
1140
- ChatPanel,
1141
- createChatResponseParser,
1142
- parseChatResponse,
1143
- parseMcpToolResult
1144
- };
1145
- //# sourceMappingURL=ChatPanel.js.map
1842
+ //#endregion
1843
+ export { CHAT_RESPONSE_JSON_PROMPT, CHAT_RESPONSE_MCP_TOOL, CHAT_RESPONSE_TOOL_SCHEMA, CHAT_RESPONSE_YAML_PROMPT, CHAT_STATUS_RULES_PROMPT, ChatPanel, ChatPanel as default, createChatResponseParser, parseChatResponse, parseMcpToolResult };
1844
+
1845
+ //# sourceMappingURL=ChatPanel.js.map