orcheo-studio 0.21.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 (329) hide show
  1. package/.bumpversion.cfg +14 -0
  2. package/.eslintrc +1 -0
  3. package/CHANGELOG.md +32 -0
  4. package/README.md +60 -0
  5. package/bin/orcheo-studio.js +39 -0
  6. package/components.json +21 -0
  7. package/dist/assets/arc-BbeTokFd.js +1 -0
  8. package/dist/assets/architectureDiagram-VXUJARFQ-CN00R9LC.js +36 -0
  9. package/dist/assets/avatar-01-CIAd4XAq.svg +1 -0
  10. package/dist/assets/avatar-02-BkgLJWQZ.svg +1 -0
  11. package/dist/assets/avatar-03-DVVcnD3b.svg +1 -0
  12. package/dist/assets/avatar-04-ydqFTDdh.svg +1 -0
  13. package/dist/assets/avatar-05-CSIhgJow.svg +1 -0
  14. package/dist/assets/avatar-06-B-h1Nz9Y.svg +1 -0
  15. package/dist/assets/avatar-07-BgkDZuBW.svg +1 -0
  16. package/dist/assets/avatar-08-C5KZjVH6.svg +1 -0
  17. package/dist/assets/avatar-09-2EGeSlPh.svg +1 -0
  18. package/dist/assets/avatar-10-DRf4ZGD2.svg +1 -0
  19. package/dist/assets/avatar-11-CEzBCpib.svg +1 -0
  20. package/dist/assets/avatar-12-COVngNEy.svg +1 -0
  21. package/dist/assets/avatar-13-D1i1tZVO.svg +1 -0
  22. package/dist/assets/avatar-14-B68CNAEs.svg +1 -0
  23. package/dist/assets/avatar-15-B9NsRNUX.svg +1 -0
  24. package/dist/assets/avatar-16-DQ_JJRGR.svg +1 -0
  25. package/dist/assets/avatar-17-DfDJkGv5.svg +1 -0
  26. package/dist/assets/avatar-18-BuxiqOaJ.svg +1 -0
  27. package/dist/assets/avatar-19-C2mJfrwW.svg +1 -0
  28. package/dist/assets/avatar-20-DPE_yE1a.svg +1 -0
  29. package/dist/assets/avatar-21-DbB0Lj6t.svg +1 -0
  30. package/dist/assets/blockDiagram-VD42YOAC-BPpieIRz.js +122 -0
  31. package/dist/assets/c4Diagram-YG6GDRKO-DAhuY1fj.js +10 -0
  32. package/dist/assets/channel-DtVQHWfl.js +1 -0
  33. package/dist/assets/chunk-4BX2VUAB-7RJQCroR.js +1 -0
  34. package/dist/assets/chunk-55IACEB6-CEN5yOA_.js +1 -0
  35. package/dist/assets/chunk-B4BG7PRW-vdlAz_3Q.js +165 -0
  36. package/dist/assets/chunk-DI55MBZ5-BTmKjGVg.js +220 -0
  37. package/dist/assets/chunk-FMBD7UC4-CwWvFfrg.js +15 -0
  38. package/dist/assets/chunk-QN33PNHL-XH90lUUN.js +1 -0
  39. package/dist/assets/chunk-QZHKN3VN-DMoMVES0.js +1 -0
  40. package/dist/assets/chunk-TZMSLE5B-Bx2XTXDY.js +1 -0
  41. package/dist/assets/classDiagram-2ON5EDUG-pw6P8q-u.js +1 -0
  42. package/dist/assets/classDiagram-v2-WZHVMYZB-pw6P8q-u.js +1 -0
  43. package/dist/assets/clone-DJq6GzHg.js +1 -0
  44. package/dist/assets/cose-bilkent-S5V4N54A-DS1MNwZB.js +1 -0
  45. package/dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
  46. package/dist/assets/dagre-6UL2VRFP-DdklXSXB.js +4 -0
  47. package/dist/assets/defaultLocale-C4B-KCzX.js +1 -0
  48. package/dist/assets/diagram-PSM6KHXK-BKBo8pqg.js +24 -0
  49. package/dist/assets/diagram-QEK2KX5R-DaavZhFO.js +43 -0
  50. package/dist/assets/diagram-S2PKOQOG-DkTwknYd.js +24 -0
  51. package/dist/assets/erDiagram-Q2GNP2WA-ClR5qEsI.js +60 -0
  52. package/dist/assets/flowDiagram-NV44I4VS-Cxt_sJHB.js +162 -0
  53. package/dist/assets/ganttDiagram-JELNMOA3-CIbs3JPJ.js +267 -0
  54. package/dist/assets/gitGraphDiagram-V2S2FVAM-DmqLh_uo.js +65 -0
  55. package/dist/assets/graph-zo28rS-a.js +1 -0
  56. package/dist/assets/index-B0_h2wCA.css +1 -0
  57. package/dist/assets/index-Be29_rJf.js +775 -0
  58. package/dist/assets/infoDiagram-HS3SLOUP-D9_rMkAf.js +2 -0
  59. package/dist/assets/init-Gi6I4Gst.js +1 -0
  60. package/dist/assets/isUndefined-C59bc74c.js +1 -0
  61. package/dist/assets/journeyDiagram-XKPGCS4Q-4ZuggLXE.js +139 -0
  62. package/dist/assets/kanban-definition-3W4ZIXB7-BQDNrhHL.js +89 -0
  63. package/dist/assets/katex-CBSAILhF.js +261 -0
  64. package/dist/assets/layout-CXbNm-Yo.js +1 -0
  65. package/dist/assets/linear-DQWATg1F.js +1 -0
  66. package/dist/assets/min-DJjRzMm8.js +1 -0
  67. package/dist/assets/mindmap-definition-VGOIOE7T-DiZQmsHT.js +68 -0
  68. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  69. package/dist/assets/pieDiagram-ADFJNKIX-Bkju7jCD.js +30 -0
  70. package/dist/assets/quadrantDiagram-AYHSOK5B-CIB47UvU.js +7 -0
  71. package/dist/assets/requirementDiagram-UZGBJVZJ-BSB3uACk.js +64 -0
  72. package/dist/assets/sankeyDiagram-TZEHDZUN-rB8wD2kd.js +10 -0
  73. package/dist/assets/sequenceDiagram-WL72ISMW-Cd9s3Q_1.js +145 -0
  74. package/dist/assets/stateDiagram-FKZM4ZOC-zAxtjcM9.js +1 -0
  75. package/dist/assets/stateDiagram-v2-4FDKWEC3-C4DctH65.js +1 -0
  76. package/dist/assets/timeline-definition-IT6M3QCI-Et89F1F3.js +61 -0
  77. package/dist/assets/treemap-GDKQZRPO-6mZVX2Gv.js +162 -0
  78. package/dist/assets/xychartDiagram-PRI3JC2R-CgyY8dNz.js +7 -0
  79. package/dist/favicon.ico +0 -0
  80. package/dist/index.html +19 -0
  81. package/dist/robots.txt +2 -0
  82. package/eslint.config.js +49 -0
  83. package/index.html +18 -0
  84. package/package.json +183 -0
  85. package/postcss.config.js +6 -0
  86. package/public/favicon.ico +0 -0
  87. package/public/robots.txt +2 -0
  88. package/src/App.tsx +115 -0
  89. package/src/assets/avatars/avatar-01.svg +1 -0
  90. package/src/assets/avatars/avatar-02.svg +1 -0
  91. package/src/assets/avatars/avatar-03.svg +1 -0
  92. package/src/assets/avatars/avatar-04.svg +1 -0
  93. package/src/assets/avatars/avatar-05.svg +1 -0
  94. package/src/assets/avatars/avatar-06.svg +1 -0
  95. package/src/assets/avatars/avatar-07.svg +1 -0
  96. package/src/assets/avatars/avatar-08.svg +1 -0
  97. package/src/assets/avatars/avatar-09.svg +1 -0
  98. package/src/assets/avatars/avatar-10.svg +1 -0
  99. package/src/assets/avatars/avatar-11.svg +1 -0
  100. package/src/assets/avatars/avatar-12.svg +1 -0
  101. package/src/assets/avatars/avatar-13.svg +1 -0
  102. package/src/assets/avatars/avatar-14.svg +1 -0
  103. package/src/assets/avatars/avatar-15.svg +1 -0
  104. package/src/assets/avatars/avatar-16.svg +1 -0
  105. package/src/assets/avatars/avatar-17.svg +1 -0
  106. package/src/assets/avatars/avatar-18.svg +1 -0
  107. package/src/assets/avatars/avatar-19.svg +1 -0
  108. package/src/assets/avatars/avatar-20.svg +1 -0
  109. package/src/assets/avatars/avatar-21.svg +1 -0
  110. package/src/assets/avatars/index.ts +99 -0
  111. package/src/design-system/ui/alert-dialog.tsx +141 -0
  112. package/src/design-system/ui/alert.tsx +59 -0
  113. package/src/design-system/ui/avatar.tsx +51 -0
  114. package/src/design-system/ui/badge.tsx +38 -0
  115. package/src/design-system/ui/button.tsx +59 -0
  116. package/src/design-system/ui/card.tsx +83 -0
  117. package/src/design-system/ui/dialog.tsx +122 -0
  118. package/src/design-system/ui/dropdown-menu.tsx +201 -0
  119. package/src/design-system/ui/input.tsx +22 -0
  120. package/src/design-system/ui/label.tsx +26 -0
  121. package/src/design-system/ui/scroll-area.tsx +48 -0
  122. package/src/design-system/ui/select.tsx +159 -0
  123. package/src/design-system/ui/separator.tsx +31 -0
  124. package/src/design-system/ui/sheet.tsx +141 -0
  125. package/src/design-system/ui/skeleton.tsx +15 -0
  126. package/src/design-system/ui/switch.tsx +29 -0
  127. package/src/design-system/ui/table.tsx +120 -0
  128. package/src/design-system/ui/tabs.tsx +55 -0
  129. package/src/design-system/ui/textarea.tsx +22 -0
  130. package/src/design-system/ui/toast.tsx +132 -0
  131. package/src/design-system/ui/toaster.tsx +35 -0
  132. package/src/design-system/ui/toggle-group.tsx +61 -0
  133. package/src/design-system/ui/toggle.tsx +46 -0
  134. package/src/design-system/ui/tooltip.tsx +32 -0
  135. package/src/features/MODULES.md +199 -0
  136. package/src/features/account/components/settings/appearance-settings-tab.tsx +24 -0
  137. package/src/features/account/components/theme-settings.tsx +70 -0
  138. package/src/features/account/components/use-theme-preferences.ts +106 -0
  139. package/src/features/account/pages/profile/components/profile-general-tab.tsx +89 -0
  140. package/src/features/account/pages/profile/types.ts +7 -0
  141. package/src/features/account/pages/profile.tsx +71 -0
  142. package/src/features/account/pages/service-tokens.tsx +522 -0
  143. package/src/features/account/pages/settings.tsx +42 -0
  144. package/src/features/account/pages/workspace-management.tsx +63 -0
  145. package/src/features/account/pages/workspace-members.tsx +335 -0
  146. package/src/features/auth/components/auto-login.tsx +51 -0
  147. package/src/features/auth/components/require-auth.tsx +56 -0
  148. package/src/features/auth/lib/auth-session.ts +448 -0
  149. package/src/features/auth/lib/oidc-client.ts +565 -0
  150. package/src/features/auth/pages/login.tsx +24 -0
  151. package/src/features/auth/pages/oauth-callback.tsx +63 -0
  152. package/src/features/chatkit/components/chatkit-surface.tsx +60 -0
  153. package/src/features/chatkit/components/public-chat-config.ts +70 -0
  154. package/src/features/chatkit/components/public-chat-error-boundary.tsx +74 -0
  155. package/src/features/chatkit/components/public-chat-widget.tsx +150 -0
  156. package/src/features/chatkit/components/studio-chat-bubble.tsx +451 -0
  157. package/src/features/chatkit/lib/chatkit-attachments.ts +31 -0
  158. package/src/features/chatkit/lib/chatkit-client.ts +195 -0
  159. package/src/features/chatkit/lib/chatkit-theme.ts +20 -0
  160. package/src/features/chatkit/lib/telemetry.ts +29 -0
  161. package/src/features/chatkit/lib/workflow-session.ts +70 -0
  162. package/src/features/chatkit/pages/public-chat.tsx +445 -0
  163. package/src/features/shared/components/chat-interface-options.ts +325 -0
  164. package/src/features/shared/components/chat-interface.types.ts +47 -0
  165. package/src/features/shared/components/top-navigation/account-menu.tsx +172 -0
  166. package/src/features/shared/components/top-navigation/active-workspace-indicator.tsx +280 -0
  167. package/src/features/shared/components/top-navigation/studio-brand.tsx +59 -0
  168. package/src/features/shared/components/top-navigation/top-navigation-types.ts +22 -0
  169. package/src/features/shared/components/top-navigation/version-status.tsx +280 -0
  170. package/src/features/shared/components/top-navigation.tsx +42 -0
  171. package/src/features/shared/components/workspace-bootstrap-gate.tsx +235 -0
  172. package/src/features/workflow/components/dialogs/add-credential-dialog.tsx +245 -0
  173. package/src/features/workflow/components/dialogs/confirm-delete-workflow-dialog.tsx +52 -0
  174. package/src/features/workflow/components/dialogs/credential-access-badge.tsx +34 -0
  175. package/src/features/workflow/components/dialogs/credential-status-badge.tsx +45 -0
  176. package/src/features/workflow/components/dialogs/credentials-table.tsx +453 -0
  177. package/src/features/workflow/components/dialogs/credentials-vault.tsx +85 -0
  178. package/src/features/workflow/components/dialogs/edit-credential-dialog.tsx +264 -0
  179. package/src/features/workflow/components/dialogs/update-workflow-dialog.tsx +214 -0
  180. package/src/features/workflow/components/dialogs/upload-workflow-dialog.tsx +288 -0
  181. package/src/features/workflow/components/forms/schema-config-form.tsx +41 -0
  182. package/src/features/workflow/components/layouts/sidebar-layout.tsx +288 -0
  183. package/src/features/workflow/components/layouts/use-sidebar-resize.ts +164 -0
  184. package/src/features/workflow/components/layouts/workflow-page-layout.tsx +69 -0
  185. package/src/features/workflow/components/panels/json-object-field.tsx +138 -0
  186. package/src/features/workflow/components/panels/rjsf-basic-widgets.tsx +21 -0
  187. package/src/features/workflow/components/panels/rjsf-input-widgets.tsx +105 -0
  188. package/src/features/workflow/components/panels/rjsf-templates.tsx +147 -0
  189. package/src/features/workflow/components/panels/rjsf-text-widgets.tsx +139 -0
  190. package/src/features/workflow/components/panels/rjsf-theme.tsx +10 -0
  191. package/src/features/workflow/components/panels/schema-dnd.ts +79 -0
  192. package/src/features/workflow/components/panels/workflow-diff-dialog.tsx +207 -0
  193. package/src/features/workflow/components/panels/workflow-history-filters.tsx +62 -0
  194. package/src/features/workflow/components/panels/workflow-history-footer.tsx +28 -0
  195. package/src/features/workflow/components/panels/workflow-history-header.tsx +47 -0
  196. package/src/features/workflow/components/panels/workflow-history-table.tsx +148 -0
  197. package/src/features/workflow/components/panels/workflow-history.tsx +150 -0
  198. package/src/features/workflow/components/panels/workflow-tabs.tsx +29 -0
  199. package/src/features/workflow/components/trace/agent-prism/Avatar.tsx +146 -0
  200. package/src/features/workflow/components/trace/agent-prism/Badge.tsx +95 -0
  201. package/src/features/workflow/components/trace/agent-prism/BrandLogo.tsx +102 -0
  202. package/src/features/workflow/components/trace/agent-prism/Button.tsx +124 -0
  203. package/src/features/workflow/components/trace/agent-prism/CollapseAndExpandControls.tsx +45 -0
  204. package/src/features/workflow/components/trace/agent-prism/CollapsibleSection.tsx +124 -0
  205. package/src/features/workflow/components/trace/agent-prism/CopyButton.tsx +63 -0
  206. package/src/features/workflow/components/trace/agent-prism/DetailsView/DetailsView.tsx +146 -0
  207. package/src/features/workflow/components/trace/agent-prism/DetailsView/DetailsViewAttributesTab.tsx +125 -0
  208. package/src/features/workflow/components/trace/agent-prism/DetailsView/DetailsViewContentViewer.tsx +51 -0
  209. package/src/features/workflow/components/trace/agent-prism/DetailsView/DetailsViewHeader.tsx +100 -0
  210. package/src/features/workflow/components/trace/agent-prism/DetailsView/DetailsViewInputOutputTab.tsx +401 -0
  211. package/src/features/workflow/components/trace/agent-prism/DetailsView/DetailsViewJsonOutput.tsx +33 -0
  212. package/src/features/workflow/components/trace/agent-prism/DetailsView/DetailsViewRawDataTab.tsx +27 -0
  213. package/src/features/workflow/components/trace/agent-prism/IconButton.tsx +75 -0
  214. package/src/features/workflow/components/trace/agent-prism/PriceBadge.tsx +14 -0
  215. package/src/features/workflow/components/trace/agent-prism/SearchInput.tsx +17 -0
  216. package/src/features/workflow/components/trace/agent-prism/SpanBadge.tsx +54 -0
  217. package/src/features/workflow/components/trace/agent-prism/SpanCard/SpanCard.tsx +480 -0
  218. package/src/features/workflow/components/trace/agent-prism/SpanCard/SpanCardBadges.tsx +23 -0
  219. package/src/features/workflow/components/trace/agent-prism/SpanCard/SpanCardConnector.tsx +36 -0
  220. package/src/features/workflow/components/trace/agent-prism/SpanCard/SpanCardTimeline.tsx +60 -0
  221. package/src/features/workflow/components/trace/agent-prism/SpanCard/SpanCardToggle.tsx +39 -0
  222. package/src/features/workflow/components/trace/agent-prism/SpanStatus.tsx +80 -0
  223. package/src/features/workflow/components/trace/agent-prism/TabSelector.tsx +35 -0
  224. package/src/features/workflow/components/trace/agent-prism/Tabs.tsx +140 -0
  225. package/src/features/workflow/components/trace/agent-prism/TextInput.tsx +143 -0
  226. package/src/features/workflow/components/trace/agent-prism/TimestampBadge.tsx +22 -0
  227. package/src/features/workflow/components/trace/agent-prism/TokensBadge.tsx +27 -0
  228. package/src/features/workflow/components/trace/agent-prism/TraceList/TraceList.tsx +123 -0
  229. package/src/features/workflow/components/trace/agent-prism/TraceList/TraceListItem.tsx +86 -0
  230. package/src/features/workflow/components/trace/agent-prism/TraceList/TraceListItemHeader.tsx +37 -0
  231. package/src/features/workflow/components/trace/agent-prism/TraceViewer/TraceViewer.tsx +208 -0
  232. package/src/features/workflow/components/trace/agent-prism/TraceViewer/TraceViewerDesktopLayout.tsx +99 -0
  233. package/src/features/workflow/components/trace/agent-prism/TraceViewer/TraceViewerMobileLayout.tsx +103 -0
  234. package/src/features/workflow/components/trace/agent-prism/TraceViewer/TraceViewerPlaceholder.tsx +5 -0
  235. package/src/features/workflow/components/trace/agent-prism/TraceViewer/TraceViewerSearchAndControls.tsx +30 -0
  236. package/src/features/workflow/components/trace/agent-prism/TraceViewer/TraceViewerTreeViewContainer.tsx +76 -0
  237. package/src/features/workflow/components/trace/agent-prism/TraceViewer/useTraceSelection.ts +186 -0
  238. package/src/features/workflow/components/trace/agent-prism/TreeView.tsx +73 -0
  239. package/src/features/workflow/components/trace/agent-prism/index.ts +7 -0
  240. package/src/features/workflow/components/trace/agent-prism/shared.ts +170 -0
  241. package/src/features/workflow/components/trace/agent-prism/theme/index.ts +101 -0
  242. package/src/features/workflow/components/trace/agent-prism/theme/theme.css +247 -0
  243. package/src/features/workflow/data/templates/assets/vibe-agent/config.json +5 -0
  244. package/src/features/workflow/data/templates/candidate-badges.ts +138 -0
  245. package/src/features/workflow/data/templates/index.ts +29 -0
  246. package/src/features/workflow/data/templates/template-definition.ts +77 -0
  247. package/src/features/workflow/data/templates/template-owner.ts +7 -0
  248. package/src/features/workflow/data/workflow-data.ts +8 -0
  249. package/src/features/workflow/data/workflow-types.ts +57 -0
  250. package/src/features/workflow/lib/mermaid-renderer.ts +334 -0
  251. package/src/features/workflow/lib/workflow-diff.ts +191 -0
  252. package/src/features/workflow/lib/workflow-execution-builders.ts +103 -0
  253. package/src/features/workflow/lib/workflow-execution-formatters.ts +145 -0
  254. package/src/features/workflow/lib/workflow-execution-storage.ts +63 -0
  255. package/src/features/workflow/lib/workflow-execution.types.ts +80 -0
  256. package/src/features/workflow/lib/workflow-storage-api.ts +581 -0
  257. package/src/features/workflow/lib/workflow-storage-helpers.ts +373 -0
  258. package/src/features/workflow/lib/workflow-storage-versioning.ts +127 -0
  259. package/src/features/workflow/lib/workflow-storage.constants.ts +21 -0
  260. package/src/features/workflow/lib/workflow-storage.ts +482 -0
  261. package/src/features/workflow/lib/workflow-storage.types.ts +261 -0
  262. package/src/features/workflow/pages/workflow/components/settings-tab-content.tsx +428 -0
  263. package/src/features/workflow/pages/workflow/components/trace-tab-content.tsx +183 -0
  264. package/src/features/workflow/pages/workflow/components/workflow-config-sheet.tsx +207 -0
  265. package/src/features/workflow/pages/workflow/components/workflow-config-sheet.utils.ts +275 -0
  266. package/src/features/workflow/pages/workflow/components/workflow-layout.tsx +143 -0
  267. package/src/features/workflow/pages/workflow/components/workflow-tab-content.tsx +766 -0
  268. package/src/features/workflow/pages/workflow/handlers/credentials.ts +338 -0
  269. package/src/features/workflow/pages/workflow/helpers/execution.ts +39 -0
  270. package/src/features/workflow/pages/workflow/helpers/trace.ts +838 -0
  271. package/src/features/workflow/pages/workflow/helpers/types.ts +28 -0
  272. package/src/features/workflow/pages/workflow/hooks/controller/build-layout-props.ts +171 -0
  273. package/src/features/workflow/pages/workflow/hooks/controller/use-workflow-controller.ts +19 -0
  274. package/src/features/workflow/pages/workflow/hooks/controller/use-workflow-core.ts +84 -0
  275. package/src/features/workflow/pages/workflow/hooks/controller/use-workflow-execution-controller.ts +167 -0
  276. package/src/features/workflow/pages/workflow/hooks/controller/use-workflow-lifecycle.ts +37 -0
  277. package/src/features/workflow/pages/workflow/hooks/controller/use-workflow-resources.ts +57 -0
  278. package/src/features/workflow/pages/workflow/hooks/execution-log-helpers.ts +75 -0
  279. package/src/features/workflow/pages/workflow/hooks/execution-node-status.ts +46 -0
  280. package/src/features/workflow/pages/workflow/hooks/execution-record-updater.ts +119 -0
  281. package/src/features/workflow/pages/workflow/hooks/execution-record.ts +31 -0
  282. package/src/features/workflow/pages/workflow/hooks/execution-runtime-updates.ts +96 -0
  283. package/src/features/workflow/pages/workflow/hooks/use-execution-trace.ts +512 -0
  284. package/src/features/workflow/pages/workflow/hooks/use-execution-updates.ts +125 -0
  285. package/src/features/workflow/pages/workflow/hooks/use-pause-workflow.ts +70 -0
  286. package/src/features/workflow/pages/workflow/hooks/use-studio-ui-state.ts +10 -0
  287. package/src/features/workflow/pages/workflow/hooks/use-workflow-chat.ts +212 -0
  288. package/src/features/workflow/pages/workflow/hooks/use-workflow-credential-readiness.ts +72 -0
  289. package/src/features/workflow/pages/workflow/hooks/use-workflow-credentials.ts +167 -0
  290. package/src/features/workflow/pages/workflow/hooks/use-workflow-execution-state.ts +20 -0
  291. package/src/features/workflow/pages/workflow/hooks/use-workflow-listeners.ts +182 -0
  292. package/src/features/workflow/pages/workflow/hooks/use-workflow-loader.ts +236 -0
  293. package/src/features/workflow/pages/workflow/hooks/use-workflow-metadata-state.ts +56 -0
  294. package/src/features/workflow/pages/workflow/hooks/use-workflow-saver.ts +191 -0
  295. package/src/features/workflow/pages/workflow/hooks/use-workflow-storage-listener.ts +81 -0
  296. package/src/features/workflow/pages/workflow/hooks/workflow-runner-websocket.ts +150 -0
  297. package/src/features/workflow/pages/workflow-gallery/types.ts +3 -0
  298. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-actions.ts +223 -0
  299. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-state.ts +206 -0
  300. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery.ts +14 -0
  301. package/src/features/workflow/pages/workflow-gallery/workflow-card-size.ts +8 -0
  302. package/src/features/workflow/pages/workflow-gallery/workflow-card.tsx +327 -0
  303. package/src/features/workflow/pages/workflow-gallery/workflow-gallery-tabs.tsx +173 -0
  304. package/src/features/workflow/pages/workflow-gallery.tsx +96 -0
  305. package/src/features/workflow/pages/workflow.tsx +119 -0
  306. package/src/features/workflow/types/credential-vault.ts +52 -0
  307. package/src/hooks/browser-context-provider.tsx +19 -0
  308. package/src/hooks/use-browser-context.ts +188 -0
  309. package/src/hooks/use-color-scheme.ts +77 -0
  310. package/src/hooks/use-credential-vault.ts +442 -0
  311. package/src/hooks/use-page-context.ts +39 -0
  312. package/src/hooks/use-toast.ts +182 -0
  313. package/src/hooks/use-uploads-allowed.ts +28 -0
  314. package/src/index.css +95 -0
  315. package/src/lib/api.ts +445 -0
  316. package/src/lib/auth-fetch.ts +35 -0
  317. package/src/lib/config.ts +102 -0
  318. package/src/lib/theme.ts +109 -0
  319. package/src/lib/utils.ts +6 -0
  320. package/src/lib/workspace-routing.ts +67 -0
  321. package/src/lib/workspace-session.ts +66 -0
  322. package/src/lib/workspace-slug.ts +10 -0
  323. package/src/main.tsx +28 -0
  324. package/src/vite-env.d.ts +17 -0
  325. package/tailwind.config.js +153 -0
  326. package/tsconfig.app.json +30 -0
  327. package/tsconfig.json +16 -0
  328. package/tsconfig.node.json +21 -0
  329. package/vite.config.ts +122 -0
@@ -0,0 +1,766 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ import { useUploadsAllowed } from "@/hooks/use-uploads-allowed";
3
+ import {
4
+ AlertTriangle,
5
+ Copy,
6
+ ExternalLink,
7
+ Play,
8
+ RefreshCw,
9
+ UserMinus,
10
+ } from "lucide-react";
11
+ import { useNavigate } from "react-router-dom";
12
+ import { Controls, ReactFlow, type Node, type NodeProps } from "@xyflow/react";
13
+ import "@xyflow/react/dist/style.css";
14
+
15
+ import { Alert, AlertDescription, AlertTitle } from "@/design-system/ui/alert";
16
+ import {
17
+ AlertDialog,
18
+ AlertDialogAction,
19
+ AlertDialogCancel,
20
+ AlertDialogContent,
21
+ AlertDialogDescription,
22
+ AlertDialogFooter,
23
+ AlertDialogHeader,
24
+ AlertDialogTitle,
25
+ } from "@/design-system/ui/alert-dialog";
26
+ import { Button } from "@/design-system/ui/button";
27
+ import { Switch } from "@/design-system/ui/switch";
28
+ import { toast } from "@/hooks/use-toast";
29
+ import { ConfirmDeleteWorkflowDialog } from "@features/workflow/components/dialogs/confirm-delete-workflow-dialog";
30
+ import { UpdateWorkflowDialog } from "@features/workflow/components/dialogs/update-workflow-dialog";
31
+ import { deleteWorkflow } from "@features/workflow/lib/workflow-storage";
32
+ import {
33
+ fetchCronTriggerConfig,
34
+ publishWorkflow,
35
+ scheduleWorkflowFromLatestVersion,
36
+ unpublishWorkflow,
37
+ unscheduleWorkflow,
38
+ } from "@features/workflow/lib/workflow-storage-api";
39
+ import type {
40
+ WorkflowRunnableConfig,
41
+ WorkflowVersionRecord,
42
+ } from "@features/workflow/lib/workflow-storage.types";
43
+ import {
44
+ buildMermaidCacheKey,
45
+ buildMermaidRenderId,
46
+ makeMermaidSvgTransparent,
47
+ renderMermaidSvg,
48
+ } from "@features/workflow/lib/mermaid-renderer";
49
+ import { resolveWorkflowVersionMermaidSource } from "@features/workflow/lib/workflow-storage-helpers";
50
+ import { WorkflowConfigSheet } from "@features/workflow/pages/workflow/components/workflow-config-sheet";
51
+
52
+ export interface WorkflowTabContentProps {
53
+ workflowId: string | null;
54
+ workflowRouteRef?: string | null;
55
+ workflowName: string;
56
+ versions: WorkflowVersionRecord[];
57
+ isLoading: boolean;
58
+ loadError: string | null;
59
+ isRunPending: boolean;
60
+ onRunWorkflow: () => Promise<void>;
61
+ onSaveConfig: (nextConfig: WorkflowRunnableConfig | null) => Promise<void>;
62
+ hasCronTriggerNode: boolean;
63
+ initialIsPublished: boolean;
64
+ initialShareUrl: string | null;
65
+ missingCredentials?: string[];
66
+ }
67
+
68
+ interface MermaidSvgNodeData {
69
+ svg: string;
70
+ width: number;
71
+ height: number;
72
+ }
73
+
74
+ const defaultMermaid = "flowchart LR\n START([Start]) --> END([End])";
75
+ const DEFAULT_SVG_SIZE = { width: 960, height: 560 };
76
+ const MIN_SVG_WIDTH = 320;
77
+ const MIN_SVG_HEIGHT = 220;
78
+ const MAX_SVG_WIDTH = 2400;
79
+ const MAX_SVG_HEIGHT = 1800;
80
+
81
+ const clamp = (value: number, min: number, max: number) =>
82
+ Math.min(max, Math.max(min, value));
83
+
84
+ const parseSvgDimension = (rawValue: string | undefined): number | null => {
85
+ if (!rawValue) {
86
+ return null;
87
+ }
88
+
89
+ const match = rawValue.match(/-?\d*\.?\d+/);
90
+ if (!match) {
91
+ return null;
92
+ }
93
+
94
+ const parsed = Number.parseFloat(match[0]);
95
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
96
+ };
97
+
98
+ const resolveSvgSize = (svg: string) => {
99
+ const viewBoxMatch = svg.match(/\bviewBox\s*=\s*"([^"]+)"/i);
100
+ if (viewBoxMatch) {
101
+ const values = viewBoxMatch[1]
102
+ .trim()
103
+ .split(/[\s,]+/)
104
+ .map((value) => Number.parseFloat(value));
105
+
106
+ if (
107
+ values.length === 4 &&
108
+ values.every((value) => Number.isFinite(value)) &&
109
+ values[2] > 0 &&
110
+ values[3] > 0
111
+ ) {
112
+ return {
113
+ width: clamp(values[2], MIN_SVG_WIDTH, MAX_SVG_WIDTH),
114
+ height: clamp(values[3], MIN_SVG_HEIGHT, MAX_SVG_HEIGHT),
115
+ };
116
+ }
117
+ }
118
+
119
+ const width = parseSvgDimension(svg.match(/\bwidth\s*=\s*"([^"]+)"/i)?.[1]);
120
+ const height = parseSvgDimension(svg.match(/\bheight\s*=\s*"([^"]+)"/i)?.[1]);
121
+
122
+ if (width && height) {
123
+ return {
124
+ width: clamp(width, MIN_SVG_WIDTH, MAX_SVG_WIDTH),
125
+ height: clamp(height, MIN_SVG_HEIGHT, MAX_SVG_HEIGHT),
126
+ };
127
+ }
128
+
129
+ return DEFAULT_SVG_SIZE;
130
+ };
131
+
132
+ const MermaidSvgNode = ({ data }: NodeProps<Node<MermaidSvgNodeData>>) => {
133
+ const nodeData = data as MermaidSvgNodeData;
134
+
135
+ return (
136
+ <div className="p-1">
137
+ <div
138
+ className="workflow-mermaid-svg pointer-events-none [&_svg]:block [&_svg]:h-full [&_svg]:w-full [&_svg]:max-w-none"
139
+ style={{ width: nodeData.width, height: nodeData.height }}
140
+ dangerouslySetInnerHTML={{ __html: nodeData.svg }}
141
+ />
142
+ </div>
143
+ );
144
+ };
145
+
146
+ const nodeTypes = {
147
+ mermaidSvg: MermaidSvgNode,
148
+ };
149
+
150
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
151
+ typeof value === "object" && value !== null;
152
+
153
+ const getErrorMessage = (error: unknown, fallback: string): string => {
154
+ if (!(error instanceof Error)) {
155
+ return fallback;
156
+ }
157
+
158
+ const rawMessage = error.message.trim();
159
+ if (!rawMessage) {
160
+ return fallback;
161
+ }
162
+
163
+ try {
164
+ const parsed = JSON.parse(rawMessage);
165
+ if (!isRecord(parsed)) {
166
+ return rawMessage;
167
+ }
168
+ const detail = parsed.detail;
169
+ if (typeof detail === "string" && detail.trim().length > 0) {
170
+ return detail;
171
+ }
172
+ if (isRecord(detail) && typeof detail.message === "string") {
173
+ return detail.message;
174
+ }
175
+ } catch {
176
+ return rawMessage;
177
+ }
178
+
179
+ return rawMessage;
180
+ };
181
+
182
+ export function WorkflowTabContent({
183
+ workflowId,
184
+ workflowName,
185
+ versions,
186
+ isLoading,
187
+ loadError,
188
+ isRunPending,
189
+ onRunWorkflow,
190
+ onSaveConfig,
191
+ hasCronTriggerNode,
192
+ initialIsPublished,
193
+ initialShareUrl,
194
+ missingCredentials = [],
195
+ }: WorkflowTabContentProps) {
196
+ const navigate = useNavigate();
197
+ const uploadsAllowed = useUploadsAllowed();
198
+ const latestVersion = versions.at(-1);
199
+ const [isConfigOpen, setIsConfigOpen] = useState(false);
200
+ const [isPublished, setIsPublished] = useState(initialIsPublished);
201
+ const [isScheduled, setIsScheduled] = useState(false);
202
+ const [shareUrl, setShareUrl] = useState<string | null>(initialShareUrl);
203
+ const [isPublishPending, setIsPublishPending] = useState(false);
204
+ const [isSchedulePending, setIsSchedulePending] = useState(false);
205
+ const [diagramSvg, setDiagramSvg] = useState<string | null>(null);
206
+ const [diagramError, setDiagramError] = useState<string | null>(null);
207
+ const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
208
+ const [isDeletePending, setIsDeletePending] = useState(false);
209
+ const [isUpdateDialogOpen, setIsUpdateDialogOpen] = useState(false);
210
+ const [isMissingCredentialsDialogOpen, setIsMissingCredentialsDialogOpen] =
211
+ useState(false);
212
+ const hasMissingCredentials = missingCredentials.length > 0;
213
+ const canDeleteWorkflow = Boolean(workflowId);
214
+
215
+ useEffect(() => {
216
+ if (!workflowId) {
217
+ setIsPublished(false);
218
+ setIsScheduled(false);
219
+ setShareUrl(null);
220
+ return;
221
+ }
222
+ setIsPublished(initialIsPublished);
223
+ setShareUrl(initialShareUrl);
224
+ }, [initialIsPublished, initialShareUrl, workflowId]);
225
+
226
+ useEffect(() => {
227
+ if (!workflowId) {
228
+ setIsScheduled(false);
229
+ return;
230
+ }
231
+
232
+ if (!hasCronTriggerNode) {
233
+ setIsScheduled(false);
234
+ return;
235
+ }
236
+
237
+ let isMounted = true;
238
+
239
+ const loadWorkflowSchedule = async () => {
240
+ try {
241
+ const cronConfig = await fetchCronTriggerConfig(workflowId);
242
+ if (!isMounted) {
243
+ return;
244
+ }
245
+ setIsScheduled(Boolean(cronConfig));
246
+ } catch (error) {
247
+ if (!isMounted) {
248
+ return;
249
+ }
250
+ toast({
251
+ title: "Failed to load workflow state",
252
+ description: getErrorMessage(
253
+ error,
254
+ "Unable to load publish/schedule status.",
255
+ ),
256
+ variant: "destructive",
257
+ });
258
+ }
259
+ };
260
+
261
+ void loadWorkflowSchedule();
262
+ return () => {
263
+ isMounted = false;
264
+ };
265
+ }, [hasCronTriggerNode, workflowId]);
266
+
267
+ const mermaidSource = useMemo(() => {
268
+ return resolveWorkflowVersionMermaidSource(latestVersion);
269
+ }, [latestVersion]);
270
+
271
+ const mermaidCacheKey = useMemo(() => {
272
+ if (!mermaidSource) {
273
+ return null;
274
+ }
275
+
276
+ return buildMermaidCacheKey({
277
+ scope: "workflow-tab",
278
+ workflowId: workflowId ?? "workflow",
279
+ versionId: latestVersion?.id ?? "latest",
280
+ source: mermaidSource,
281
+ });
282
+ }, [latestVersion?.id, mermaidSource, workflowId]);
283
+
284
+ const mermaidRenderId = useMemo(() => {
285
+ if (!mermaidCacheKey) {
286
+ return null;
287
+ }
288
+
289
+ return buildMermaidRenderId("workflow-mermaid-svg", mermaidCacheKey);
290
+ }, [mermaidCacheKey]);
291
+
292
+ useEffect(() => {
293
+ if (!mermaidSource || !mermaidCacheKey || !mermaidRenderId) {
294
+ setDiagramSvg(null);
295
+ setDiagramError(null);
296
+ return;
297
+ }
298
+
299
+ let isMounted = true;
300
+
301
+ const renderMermaid = async () => {
302
+ try {
303
+ const svg = await renderMermaidSvg({
304
+ source: mermaidSource,
305
+ cacheKey: mermaidCacheKey,
306
+ renderId: mermaidRenderId,
307
+ transformSvg: makeMermaidSvgTransparent,
308
+ });
309
+
310
+ if (!isMounted) {
311
+ return;
312
+ }
313
+
314
+ setDiagramSvg(svg);
315
+ setDiagramError(null);
316
+ } catch (error) {
317
+ if (!isMounted) {
318
+ return;
319
+ }
320
+
321
+ setDiagramSvg(null);
322
+ setDiagramError(
323
+ error instanceof Error ? error.message : "Unable to render diagram.",
324
+ );
325
+ }
326
+ };
327
+
328
+ void renderMermaid();
329
+
330
+ return () => {
331
+ isMounted = false;
332
+ };
333
+ }, [mermaidCacheKey, mermaidRenderId, mermaidSource]);
334
+
335
+ const diagramNodes = useMemo(() => {
336
+ if (!diagramSvg) {
337
+ return [] as Node[];
338
+ }
339
+
340
+ const size = resolveSvgSize(diagramSvg);
341
+
342
+ return [
343
+ {
344
+ id: "mermaid-svg-root",
345
+ type: "mermaidSvg",
346
+ position: { x: 0, y: 0 },
347
+ data: {
348
+ svg: diagramSvg,
349
+ width: size.width,
350
+ height: size.height,
351
+ },
352
+ draggable: false,
353
+ selectable: false,
354
+ } satisfies Node,
355
+ ];
356
+ }, [diagramSvg]);
357
+
358
+ if (isLoading) {
359
+ return (
360
+ <div className="flex h-full items-center justify-center p-6 text-sm text-muted-foreground">
361
+ Loading workflow visualization...
362
+ </div>
363
+ );
364
+ }
365
+
366
+ if (loadError) {
367
+ return (
368
+ <div className="flex h-full items-center justify-center p-6">
369
+ <div className="rounded-md border border-destructive/40 bg-destructive/5 p-4 text-sm text-destructive">
370
+ Failed to load workflow: {loadError}
371
+ </div>
372
+ </div>
373
+ );
374
+ }
375
+
376
+ const canConfigure = Boolean(workflowId);
377
+ const canRun = Boolean(workflowId && latestVersion);
378
+ const latestConfig = latestVersion?.runnableConfig ?? null;
379
+ const canToggleSchedule = hasCronTriggerNode || isScheduled;
380
+
381
+ const handleCopyShareUrl = async () => {
382
+ if (!shareUrl) {
383
+ return;
384
+ }
385
+
386
+ try {
387
+ await navigator.clipboard.writeText(shareUrl);
388
+ toast({
389
+ title: "Public URL copied",
390
+ description: "The workflow URL has been copied to your clipboard.",
391
+ });
392
+ } catch (error) {
393
+ toast({
394
+ title: "Failed to copy public URL",
395
+ description: getErrorMessage(error, "Clipboard access is unavailable."),
396
+ variant: "destructive",
397
+ });
398
+ }
399
+ };
400
+
401
+ const handlePublishToggle = async (nextValue: boolean) => {
402
+ if (!workflowId) {
403
+ setIsPublished(false);
404
+ toast({
405
+ title: "Save workflow first",
406
+ description: "Publishing requires a saved workflow ID.",
407
+ variant: "destructive",
408
+ });
409
+ return;
410
+ }
411
+
412
+ setIsPublishPending(true);
413
+ try {
414
+ if (nextValue) {
415
+ const result = await publishWorkflow(workflowId, { actor: "studio" });
416
+ setIsPublished(true);
417
+ setShareUrl(result.shareUrl);
418
+ toast({
419
+ title: "Workflow published",
420
+ description:
421
+ result.message ??
422
+ "Workflow is now public and available via its chat URL.",
423
+ });
424
+ } else {
425
+ await unpublishWorkflow(workflowId, "studio");
426
+ setIsPublished(false);
427
+ setShareUrl(null);
428
+ toast({
429
+ title: "Workflow unpublished",
430
+ description: "Workflow is now private.",
431
+ });
432
+ }
433
+ } catch (error) {
434
+ setIsPublished(!nextValue);
435
+ toast({
436
+ title: nextValue
437
+ ? "Failed to publish workflow"
438
+ : "Failed to unpublish workflow",
439
+ description: getErrorMessage(error, "Unable to update publish status."),
440
+ variant: "destructive",
441
+ });
442
+ } finally {
443
+ setIsPublishPending(false);
444
+ }
445
+ };
446
+
447
+ const handleScheduleToggle = async (nextValue: boolean) => {
448
+ if (!workflowId) {
449
+ setIsScheduled(false);
450
+ toast({
451
+ title: "Save workflow first",
452
+ description: "Scheduling requires a saved workflow ID.",
453
+ variant: "destructive",
454
+ });
455
+ return;
456
+ }
457
+
458
+ setIsSchedulePending(true);
459
+ try {
460
+ if (nextValue) {
461
+ const result = await scheduleWorkflowFromLatestVersion(workflowId);
462
+ if (result.status === "noop") {
463
+ setIsScheduled(false);
464
+ toast({
465
+ title: "No schedule applied",
466
+ description: result.message,
467
+ });
468
+ return;
469
+ }
470
+
471
+ setIsScheduled(true);
472
+ toast({
473
+ title: "Workflow scheduled",
474
+ description: result.message,
475
+ });
476
+ } else {
477
+ const result = await unscheduleWorkflow(workflowId);
478
+ setIsScheduled(false);
479
+ toast({
480
+ title: "Workflow unscheduled",
481
+ description: result.message,
482
+ });
483
+ }
484
+ } catch (error) {
485
+ setIsScheduled(!nextValue);
486
+ toast({
487
+ title: nextValue
488
+ ? "Failed to schedule workflow"
489
+ : "Failed to unschedule workflow",
490
+ description: getErrorMessage(
491
+ error,
492
+ "Unable to update schedule status.",
493
+ ),
494
+ variant: "destructive",
495
+ });
496
+ } finally {
497
+ setIsSchedulePending(false);
498
+ }
499
+ };
500
+
501
+ const handleDeleteCurrentWorkflow = async () => {
502
+ if (!workflowId) {
503
+ return;
504
+ }
505
+
506
+ setIsDeletePending(true);
507
+ try {
508
+ await deleteWorkflow(workflowId);
509
+ toast({
510
+ title: "Colleague offboarded",
511
+ description: `"${workflowName}" has been removed from your workspace.`,
512
+ });
513
+ setIsDeleteDialogOpen(false);
514
+ navigate("/");
515
+ } catch (error) {
516
+ toast({
517
+ title: "Failed to offboard colleague",
518
+ description: getErrorMessage(error, "Unable to offboard colleague."),
519
+ variant: "destructive",
520
+ });
521
+ } finally {
522
+ setIsDeletePending(false);
523
+ }
524
+ };
525
+
526
+ return (
527
+ <>
528
+ <div className="flex min-h-0 flex-1 flex-col gap-4 p-4">
529
+ <div className="flex items-center justify-between border-b pb-3">
530
+ <div>
531
+ <h2 className="text-lg font-semibold">
532
+ {workflowName || "Workflow"}
533
+ </h2>
534
+ <p className="text-sm text-muted-foreground">
535
+ {workflowName}
536
+ {latestVersion ? ` · ${latestVersion.version}` : ""}
537
+ </p>
538
+ </div>
539
+ <div className="flex items-center gap-4">
540
+ <div className="flex items-center gap-2">
541
+ <span className="text-sm text-muted-foreground">Publish</span>
542
+ <Switch
543
+ aria-label="Publish workflow"
544
+ checked={isPublished}
545
+ onCheckedChange={(checked) => void handlePublishToggle(checked)}
546
+ disabled={isPublishPending}
547
+ />
548
+ </div>
549
+ <div className="flex items-center gap-2">
550
+ <span className="text-sm text-muted-foreground">Schedule</span>
551
+ <Switch
552
+ aria-label="Schedule workflow"
553
+ checked={isScheduled}
554
+ onCheckedChange={(checked) =>
555
+ void handleScheduleToggle(checked)
556
+ }
557
+ disabled={isSchedulePending || !canToggleSchedule}
558
+ />
559
+ </div>
560
+ {workflowId && uploadsAllowed === true ? (
561
+ <Button
562
+ variant="outline"
563
+ onClick={() => setIsUpdateDialogOpen(true)}
564
+ >
565
+ <RefreshCw className="mr-1.5 h-4 w-4" />
566
+ Update
567
+ </Button>
568
+ ) : null}
569
+ {canDeleteWorkflow ? (
570
+ <Button
571
+ variant="destructive"
572
+ onClick={() => setIsDeleteDialogOpen(true)}
573
+ disabled={isDeletePending}
574
+ >
575
+ <UserMinus className="mr-1.5 h-4 w-4" />
576
+ Offboard
577
+ </Button>
578
+ ) : null}
579
+ <Button
580
+ onClick={() => {
581
+ if (hasMissingCredentials) {
582
+ setIsMissingCredentialsDialogOpen(true);
583
+ return;
584
+ }
585
+ void onRunWorkflow();
586
+ }}
587
+ disabled={!canRun || isRunPending}
588
+ >
589
+ <Play className="mr-1.5 h-4 w-4" />
590
+ {isRunPending ? "Running..." : "Run"}
591
+ </Button>
592
+ <Button
593
+ variant="outline"
594
+ onClick={() => setIsConfigOpen(true)}
595
+ disabled={!canConfigure}
596
+ >
597
+ Config
598
+ </Button>
599
+ </div>
600
+ </div>
601
+
602
+ {isPublished && shareUrl && (
603
+ <div className="flex items-center justify-between rounded-md border border-border/60 bg-muted/20 px-3 py-2">
604
+ <div className="min-w-0">
605
+ <p className="text-xs uppercase tracking-wide text-muted-foreground">
606
+ Public URL
607
+ </p>
608
+ <a
609
+ href={shareUrl}
610
+ target="_blank"
611
+ rel="noreferrer"
612
+ className="block truncate text-sm text-primary hover:underline"
613
+ >
614
+ {shareUrl}
615
+ </a>
616
+ </div>
617
+ <div className="ml-3 flex items-center gap-2">
618
+ <Button
619
+ variant="outline"
620
+ size="sm"
621
+ onClick={() => void handleCopyShareUrl()}
622
+ >
623
+ <Copy className="mr-1.5 h-3.5 w-3.5" />
624
+ Copy
625
+ </Button>
626
+ <Button variant="outline" size="sm" asChild>
627
+ <a href={shareUrl} target="_blank" rel="noreferrer">
628
+ <ExternalLink className="mr-1.5 h-3.5 w-3.5" />
629
+ Open
630
+ </a>
631
+ </Button>
632
+ </div>
633
+ </div>
634
+ )}
635
+
636
+ {hasMissingCredentials && (
637
+ <Alert variant="destructive">
638
+ <AlertTriangle className="h-4 w-4" />
639
+ <AlertTitle>Missing credentials</AlertTitle>
640
+ <AlertDescription>
641
+ This workflow references credentials that are not in the vault.
642
+ Add them before running:
643
+ <ul className="mt-1 list-disc pl-5">
644
+ {missingCredentials.map((name) => (
645
+ <li key={name} className="font-mono">
646
+ {name}
647
+ </li>
648
+ ))}
649
+ </ul>
650
+ </AlertDescription>
651
+ </Alert>
652
+ )}
653
+
654
+ {!latestVersion && (
655
+ <div className="flex h-full items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground">
656
+ No version is available yet to generate a Mermaid diagram.
657
+ </div>
658
+ )}
659
+
660
+ {latestVersion && !mermaidSource && (
661
+ <div className="flex h-full items-center justify-center rounded-md border border-dashed text-sm text-muted-foreground">
662
+ Mermaid data is unavailable for this workflow version.
663
+ </div>
664
+ )}
665
+
666
+ {latestVersion && mermaidSource && diagramError && (
667
+ <div className="rounded-md border border-destructive/40 bg-destructive/5 p-4 text-sm text-destructive">
668
+ Unable to render Mermaid diagram: {diagramError}
669
+ </div>
670
+ )}
671
+
672
+ {latestVersion && mermaidSource && !diagramError && (
673
+ <div className="min-h-0 flex flex-1 flex-col">
674
+ <div className="min-h-0 flex-1 overflow-hidden">
675
+ {diagramNodes.length > 0 ? (
676
+ <ReactFlow
677
+ key={`${latestVersion.id}-mermaid-svg`}
678
+ nodes={diagramNodes}
679
+ edges={[]}
680
+ nodeTypes={nodeTypes}
681
+ fitView
682
+ minZoom={0.2}
683
+ maxZoom={2}
684
+ nodesDraggable={false}
685
+ nodesConnectable={false}
686
+ elementsSelectable={false}
687
+ zoomOnDoubleClick={false}
688
+ className="h-full w-full"
689
+ proOptions={{ hideAttribution: true }}
690
+ style={{ background: "transparent" }}
691
+ >
692
+ <Controls showInteractive={false} />
693
+ </ReactFlow>
694
+ ) : (
695
+ <pre className="h-full overflow-auto p-3 text-xs text-muted-foreground">
696
+ {defaultMermaid}
697
+ </pre>
698
+ )}
699
+ </div>
700
+ </div>
701
+ )}
702
+
703
+ <WorkflowConfigSheet
704
+ open={isConfigOpen}
705
+ onOpenChange={setIsConfigOpen}
706
+ initialConfig={latestConfig}
707
+ configurableSchemas={latestVersion?.configurableSchemas}
708
+ onSave={onSaveConfig}
709
+ />
710
+ </div>
711
+
712
+ {canDeleteWorkflow ? (
713
+ <ConfirmDeleteWorkflowDialog
714
+ open={isDeleteDialogOpen}
715
+ workflowName={workflowName}
716
+ isPending={isDeletePending}
717
+ onOpenChange={setIsDeleteDialogOpen}
718
+ onConfirm={handleDeleteCurrentWorkflow}
719
+ />
720
+ ) : null}
721
+
722
+ {workflowId ? (
723
+ <UpdateWorkflowDialog
724
+ open={isUpdateDialogOpen}
725
+ onOpenChange={setIsUpdateDialogOpen}
726
+ workflowId={workflowId}
727
+ workflowName={workflowName}
728
+ />
729
+ ) : null}
730
+
731
+ <AlertDialog
732
+ open={isMissingCredentialsDialogOpen}
733
+ onOpenChange={setIsMissingCredentialsDialogOpen}
734
+ >
735
+ <AlertDialogContent>
736
+ <AlertDialogHeader>
737
+ <AlertDialogTitle>Missing credentials</AlertDialogTitle>
738
+ <AlertDialogDescription>
739
+ This workflow references credentials that are not in the vault.
740
+ Running now will fail when those nodes execute. Add the following
741
+ credentials before retrying:
742
+ </AlertDialogDescription>
743
+ </AlertDialogHeader>
744
+ <ul className="list-disc pl-6 text-sm">
745
+ {missingCredentials.map((name) => (
746
+ <li key={name} className="font-mono">
747
+ {name}
748
+ </li>
749
+ ))}
750
+ </ul>
751
+ <AlertDialogFooter>
752
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
753
+ <AlertDialogAction
754
+ onClick={() => {
755
+ setIsMissingCredentialsDialogOpen(false);
756
+ void onRunWorkflow();
757
+ }}
758
+ >
759
+ Run anyway
760
+ </AlertDialogAction>
761
+ </AlertDialogFooter>
762
+ </AlertDialogContent>
763
+ </AlertDialog>
764
+ </>
765
+ );
766
+ }