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,100 @@
1
+ import type { TraceSpan } from "@evilmartians/agent-prism-types";
2
+ import type { ReactNode } from "react";
3
+
4
+ import { getDurationMs, formatDuration } from "@evilmartians/agent-prism-data";
5
+ import { Check, Copy } from "lucide-react";
6
+ import { useState } from "react";
7
+
8
+ import type { AvatarProps } from "../Avatar";
9
+
10
+ import { Avatar } from "../Avatar";
11
+ import { IconButton } from "../IconButton";
12
+ import { PriceBadge } from "../PriceBadge";
13
+ import { SpanBadge } from "../SpanBadge";
14
+ import { SpanStatus } from "../SpanStatus";
15
+ import { TimestampBadge } from "../TimestampBadge";
16
+ import { TokensBadge } from "../TokensBadge";
17
+
18
+ export interface DetailsViewHeaderProps {
19
+ data: TraceSpan;
20
+ avatar?: AvatarProps;
21
+ copyButton?: {
22
+ isEnabled?: boolean;
23
+ onCopy?: (data: TraceSpan) => void;
24
+ };
25
+ /**
26
+ * Custom actions to render in the header
27
+ */
28
+ actions?: ReactNode;
29
+ /**
30
+ * Optional className for the header container
31
+ */
32
+ className?: string;
33
+ }
34
+
35
+ export const DetailsViewHeader = ({
36
+ data,
37
+ avatar,
38
+ copyButton,
39
+ actions,
40
+ className,
41
+ }: DetailsViewHeaderProps) => {
42
+ const [hasCopied, setHasCopied] = useState(false);
43
+ const durationMs = getDurationMs(data);
44
+
45
+ const handleCopy = () => {
46
+ if (copyButton?.onCopy) {
47
+ copyButton.onCopy(data);
48
+ setHasCopied(true);
49
+ setTimeout(() => setHasCopied(false), 2000);
50
+ }
51
+ };
52
+
53
+ return (
54
+ <div className={className || "flex flex-wrap items-center gap-2"}>
55
+ {avatar && <Avatar size="4" {...avatar} />}
56
+
57
+ <span className="text-agentprism-foreground tracking-wide">
58
+ {data.title}
59
+ </span>
60
+
61
+ <div className="flex size-5 items-center justify-center">
62
+ <SpanStatus status={data.status} />
63
+ </div>
64
+
65
+ {copyButton && (
66
+ <IconButton
67
+ aria-label={
68
+ copyButton.isEnabled ? "Copy span details" : "Copy disabled"
69
+ }
70
+ variant="ghost"
71
+ onClick={handleCopy}
72
+ >
73
+ {hasCopied ? (
74
+ <Check className="text-agentprism-muted-foreground size-3" />
75
+ ) : (
76
+ <Copy className="text-agentprism-muted-foreground size-3" />
77
+ )}
78
+ </IconButton>
79
+ )}
80
+
81
+ <SpanBadge category={data.type} />
82
+
83
+ {typeof data.tokensCount === "number" && (
84
+ <TokensBadge tokensCount={data.tokensCount} />
85
+ )}
86
+
87
+ {typeof data.cost === "number" && <PriceBadge cost={data.cost} />}
88
+
89
+ <span className="text-agentprism-muted-foreground text-xs">
90
+ LATENCY: {formatDuration(durationMs)}
91
+ </span>
92
+
93
+ {typeof data.startTime === "number" && (
94
+ <TimestampBadge timestamp={data.startTime} />
95
+ )}
96
+
97
+ {actions}
98
+ </div>
99
+ );
100
+ };
@@ -0,0 +1,401 @@
1
+ import type { TraceSpan } from "@evilmartians/agent-prism-types";
2
+ import type { ReactElement } from "react";
3
+ import type { TraceSpanMetadata } from "@features/workflow/pages/workflow/helpers/trace";
4
+
5
+ import { useEffect, useMemo, useState } from "react";
6
+
7
+ import type { TabItem } from "../Tabs";
8
+
9
+ import { CollapsibleSection } from "../CollapsibleSection";
10
+ import { TabSelector } from "../TabSelector";
11
+ import {
12
+ DetailsViewContentViewer,
13
+ type DetailsViewContentViewMode,
14
+ } from "./DetailsViewContentViewer";
15
+
16
+ interface DetailsViewInputOutputTabProps {
17
+ data: TraceSpan;
18
+ }
19
+
20
+ type IOSection = "Input" | "Output";
21
+ type StateDiffType = "added" | "removed" | "updated";
22
+
23
+ interface StateDiffEntry {
24
+ path: string;
25
+ type: StateDiffType;
26
+ before?: unknown;
27
+ after?: unknown;
28
+ }
29
+
30
+ const MAX_VISIBLE_DIFF_ENTRIES = 120;
31
+
32
+ const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
33
+ Boolean(value) && typeof value === "object" && !Array.isArray(value);
34
+
35
+ const valuesEqual = (left: unknown, right: unknown): boolean => {
36
+ if (Object.is(left, right)) {
37
+ return true;
38
+ }
39
+ try {
40
+ return JSON.stringify(left) === JSON.stringify(right);
41
+ } catch {
42
+ return false;
43
+ }
44
+ };
45
+
46
+ const buildStateDiff = (
47
+ before: unknown,
48
+ after: unknown,
49
+ path = "",
50
+ ): StateDiffEntry[] => {
51
+ if (isObjectRecord(before) && isObjectRecord(after)) {
52
+ const keys = new Set([...Object.keys(before), ...Object.keys(after)]);
53
+ const entries: StateDiffEntry[] = [];
54
+ for (const key of Array.from(keys).sort((a, b) => a.localeCompare(b))) {
55
+ const nextPath = path ? `${path}.${key}` : key;
56
+ const hasBefore = Object.prototype.hasOwnProperty.call(before, key);
57
+ const hasAfter = Object.prototype.hasOwnProperty.call(after, key);
58
+ if (!hasBefore && hasAfter) {
59
+ entries.push({
60
+ path: nextPath,
61
+ type: "added",
62
+ after: (after as Record<string, unknown>)[key],
63
+ });
64
+ continue;
65
+ }
66
+ if (hasBefore && !hasAfter) {
67
+ entries.push({
68
+ path: nextPath,
69
+ type: "removed",
70
+ before: (before as Record<string, unknown>)[key],
71
+ });
72
+ continue;
73
+ }
74
+ entries.push(
75
+ ...buildStateDiff(
76
+ (before as Record<string, unknown>)[key],
77
+ (after as Record<string, unknown>)[key],
78
+ nextPath,
79
+ ),
80
+ );
81
+ }
82
+ return entries;
83
+ }
84
+
85
+ if (Array.isArray(before) && Array.isArray(after)) {
86
+ const entries: StateDiffEntry[] = [];
87
+ const length = Math.max(before.length, after.length);
88
+ for (let index = 0; index < length; index += 1) {
89
+ const nextPath = `${path}[${index}]`;
90
+ if (index >= before.length) {
91
+ entries.push({
92
+ path: nextPath,
93
+ type: "added",
94
+ after: after[index],
95
+ });
96
+ continue;
97
+ }
98
+ if (index >= after.length) {
99
+ entries.push({
100
+ path: nextPath,
101
+ type: "removed",
102
+ before: before[index],
103
+ });
104
+ continue;
105
+ }
106
+ entries.push(...buildStateDiff(before[index], after[index], nextPath));
107
+ }
108
+ return entries;
109
+ }
110
+
111
+ if (valuesEqual(before, after)) {
112
+ return [];
113
+ }
114
+ return [
115
+ {
116
+ path: path || "$",
117
+ type: "updated",
118
+ before,
119
+ after,
120
+ },
121
+ ];
122
+ };
123
+
124
+ const formatPlainContent = (value: unknown): string => {
125
+ try {
126
+ return JSON.stringify(value, null, 2);
127
+ } catch {
128
+ return String(value);
129
+ }
130
+ };
131
+
132
+ const formatInlineValue = (value: unknown): string => {
133
+ if (typeof value === "string") {
134
+ return value;
135
+ }
136
+ if (
137
+ typeof value === "number" ||
138
+ typeof value === "boolean" ||
139
+ value == null
140
+ ) {
141
+ return String(value);
142
+ }
143
+ return formatPlainContent(value);
144
+ };
145
+
146
+ export const DetailsViewInputOutputTab = ({
147
+ data,
148
+ }: DetailsViewInputOutputTabProps): ReactElement => {
149
+ const metadata = data.metadata as TraceSpanMetadata | undefined;
150
+ const workflowStateBefore = metadata?.workflowStateBefore;
151
+ const workflowStateAfter = metadata?.workflowStateAfter;
152
+ const hasWorkflowState = Boolean(workflowStateBefore || workflowStateAfter);
153
+
154
+ const stateDiff = useMemo(
155
+ () => buildStateDiff(workflowStateBefore ?? {}, workflowStateAfter ?? {}),
156
+ [workflowStateBefore, workflowStateAfter],
157
+ );
158
+ const diffSummary = useMemo(
159
+ () =>
160
+ stateDiff.reduce(
161
+ (summary, entry) => {
162
+ if (entry.type === "added") {
163
+ summary.added += 1;
164
+ } else if (entry.type === "removed") {
165
+ summary.removed += 1;
166
+ } else {
167
+ summary.updated += 1;
168
+ }
169
+ return summary;
170
+ },
171
+ { added: 0, removed: 0, updated: 0 },
172
+ ),
173
+ [stateDiff],
174
+ );
175
+ const [showSnapshots, setShowSnapshots] = useState(false);
176
+
177
+ useEffect(() => {
178
+ setShowSnapshots(false);
179
+ }, [data.id]);
180
+
181
+ if (hasWorkflowState) {
182
+ const visibleDiff = stateDiff.slice(0, MAX_VISIBLE_DIFF_ENTRIES);
183
+ const hiddenCount = Math.max(stateDiff.length - visibleDiff.length, 0);
184
+ const stateInContent = formatPlainContent(workflowStateBefore ?? {});
185
+ const stateOutContent = formatPlainContent(workflowStateAfter ?? {});
186
+
187
+ return (
188
+ <div className="space-y-4">
189
+ {(metadata?.workflowStateRedacted ||
190
+ metadata?.workflowStateTruncated) && (
191
+ <div className="border-agentprism-border rounded-md border p-3 text-xs">
192
+ {metadata.workflowStateRedacted && (
193
+ <p className="text-agentprism-muted-foreground">
194
+ Sensitive fields were redacted in this snapshot.
195
+ </p>
196
+ )}
197
+ {metadata.workflowStateTruncated && (
198
+ <p className="text-agentprism-muted-foreground">
199
+ Large values were truncated in this snapshot.
200
+ </p>
201
+ )}
202
+ </div>
203
+ )}
204
+
205
+ <CollapsibleSection
206
+ title="State diff"
207
+ defaultOpen
208
+ rightContent={
209
+ <div className="text-agentprism-muted-foreground flex items-center gap-2 text-xs">
210
+ <span className="rounded border border-green-300 px-1.5 py-0.5 text-green-700">
211
+ + {diffSummary.added}
212
+ </span>
213
+ <span className="rounded border border-amber-300 px-1.5 py-0.5 text-amber-700">
214
+ ~ {diffSummary.updated}
215
+ </span>
216
+ <span className="rounded border border-red-300 px-1.5 py-0.5 text-red-700">
217
+ - {diffSummary.removed}
218
+ </span>
219
+ </div>
220
+ }
221
+ >
222
+ {stateDiff.length === 0 ? (
223
+ <p className="text-agentprism-muted-foreground p-3 text-sm">
224
+ No workflow state changes recorded for this span.
225
+ </p>
226
+ ) : (
227
+ <div className="space-y-2">
228
+ {visibleDiff.map((entry) => (
229
+ <div
230
+ key={`${entry.type}:${entry.path}`}
231
+ className="border-agentprism-border rounded-md border p-2"
232
+ >
233
+ <div className="mb-1 flex items-center gap-2 text-xs">
234
+ <span
235
+ className={
236
+ entry.type === "added"
237
+ ? "rounded border border-green-300 px-1 text-green-700"
238
+ : entry.type === "removed"
239
+ ? "rounded border border-red-300 px-1 text-red-700"
240
+ : "rounded border border-amber-300 px-1 text-amber-700"
241
+ }
242
+ >
243
+ {entry.type}
244
+ </span>
245
+ <code className="text-agentprism-foreground break-all">
246
+ {entry.path}
247
+ </code>
248
+ </div>
249
+ {entry.type !== "added" && (
250
+ <p className="text-agentprism-muted-foreground break-all text-xs">
251
+ Before: {formatInlineValue(entry.before)}
252
+ </p>
253
+ )}
254
+ {entry.type !== "removed" && (
255
+ <p className="text-agentprism-muted-foreground break-all text-xs">
256
+ After: {formatInlineValue(entry.after)}
257
+ </p>
258
+ )}
259
+ </div>
260
+ ))}
261
+ {hiddenCount > 0 && (
262
+ <p className="text-agentprism-muted-foreground px-1 text-xs">
263
+ Showing first {MAX_VISIBLE_DIFF_ENTRIES} changes (
264
+ {hiddenCount} more hidden).
265
+ </p>
266
+ )}
267
+ </div>
268
+ )}
269
+ </CollapsibleSection>
270
+
271
+ <button
272
+ type="button"
273
+ className="text-agentprism-muted-foreground text-xs underline underline-offset-2"
274
+ onClick={() => setShowSnapshots((current) => !current)}
275
+ >
276
+ {showSnapshots ? "Hide full snapshots" : "Show full snapshots"}
277
+ </button>
278
+
279
+ {showSnapshots && (
280
+ <div className="space-y-4">
281
+ <IOSection
282
+ section="Input"
283
+ content={stateInContent}
284
+ parsedContent={stateInContent}
285
+ />
286
+ <IOSection
287
+ section="Output"
288
+ content={stateOutContent}
289
+ parsedContent={stateOutContent}
290
+ />
291
+ </div>
292
+ )}
293
+ </div>
294
+ );
295
+ }
296
+
297
+ const hasInput = Boolean(data.input);
298
+ const hasOutput = Boolean(data.output);
299
+
300
+ if (!hasInput && !hasOutput) {
301
+ return (
302
+ <div className="border-agentprism-border rounded-md border p-4">
303
+ <p className="text-agentprism-muted-foreground text-sm">
304
+ No input or output data available for this span
305
+ </p>
306
+ </div>
307
+ );
308
+ }
309
+
310
+ let parsedInput: string | null = null;
311
+ let parsedOutput: string | null = null;
312
+
313
+ if (typeof data.input === "string") {
314
+ try {
315
+ JSON.parse(data.input);
316
+ parsedInput = data.input;
317
+ } catch {
318
+ parsedInput = null;
319
+ }
320
+ }
321
+
322
+ if (typeof data.output === "string") {
323
+ try {
324
+ JSON.parse(data.output);
325
+ parsedOutput = data.output;
326
+ } catch {
327
+ parsedOutput = null;
328
+ }
329
+ }
330
+
331
+ return (
332
+ <div className="space-y-4">
333
+ {typeof data.input === "string" && (
334
+ <IOSection
335
+ section="Input"
336
+ content={data.input}
337
+ parsedContent={parsedInput}
338
+ />
339
+ )}
340
+ {typeof data.output === "string" && (
341
+ <IOSection
342
+ section="Output"
343
+ content={data.output}
344
+ parsedContent={parsedOutput}
345
+ />
346
+ )}
347
+ </div>
348
+ );
349
+ };
350
+
351
+ interface IOSectionProps {
352
+ section: IOSection;
353
+ content: string;
354
+ parsedContent: string | null;
355
+ }
356
+
357
+ const IOSection = ({
358
+ section,
359
+ content,
360
+ parsedContent,
361
+ }: IOSectionProps): ReactElement => {
362
+ const [tab, setTab] = useState<DetailsViewContentViewMode>(
363
+ parsedContent ? "json" : "plain",
364
+ );
365
+
366
+ useEffect(() => {
367
+ if (tab === "json" && !parsedContent) {
368
+ setTab("plain");
369
+ }
370
+ }, [tab, parsedContent]);
371
+
372
+ const tabItems: TabItem<DetailsViewContentViewMode>[] = [
373
+ { value: "json", label: "JSON", disabled: !parsedContent },
374
+ { value: "plain", label: "Plain" },
375
+ ];
376
+
377
+ return (
378
+ <CollapsibleSection
379
+ title={section}
380
+ defaultOpen
381
+ rightContent={
382
+ <TabSelector<DetailsViewContentViewMode>
383
+ items={tabItems}
384
+ defaultValue={parsedContent ? "json" : "plain"}
385
+ value={tab}
386
+ onValueChange={setTab}
387
+ theme="pill"
388
+ onClick={(event) => event.stopPropagation()}
389
+ />
390
+ }
391
+ >
392
+ <DetailsViewContentViewer
393
+ content={content}
394
+ parsedContent={parsedContent}
395
+ mode={tab}
396
+ label={section}
397
+ id={section}
398
+ />
399
+ </CollapsibleSection>
400
+ );
401
+ };
@@ -0,0 +1,33 @@
1
+ import { type FC } from "react";
2
+ import JSONPretty from "react-json-pretty";
3
+ import colors from "tailwindcss/colors";
4
+
5
+ import { agentPrismPrefix } from "../theme";
6
+
7
+ export interface JsonViewerProps {
8
+ content: string;
9
+ id: string;
10
+ className?: string;
11
+ }
12
+
13
+ const wrapStyle = `font-size: 12px; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word;`;
14
+
15
+ export const DetailsViewJsonOutput: FC<JsonViewerProps> = ({
16
+ content,
17
+ id,
18
+ className = "",
19
+ }) => {
20
+ return (
21
+ <JSONPretty
22
+ booleanStyle={`color: ${colors.blue[800]};`}
23
+ className={`min-w-0 overflow-x-auto rounded-xl p-4 text-left ${className}`}
24
+ data={content}
25
+ id={`json-pretty-${id}`}
26
+ keyStyle={`color: oklch(var(--${agentPrismPrefix}-code-key));`}
27
+ mainStyle={`color: oklch(var(--${agentPrismPrefix}-code-base)); ${wrapStyle}`}
28
+ errorStyle={`color: oklch(var(--${agentPrismPrefix}-code-base)); ${wrapStyle}`}
29
+ stringStyle={`color: oklch(var(--${agentPrismPrefix}-code-string));`}
30
+ valueStyle={`color: oklch(var(--${agentPrismPrefix}-code-number));`}
31
+ />
32
+ );
33
+ };
@@ -0,0 +1,27 @@
1
+ import type { TraceSpan } from "@evilmartians/agent-prism-types";
2
+
3
+ import { type ReactElement } from "react";
4
+
5
+ import { CopyButton } from "../CopyButton";
6
+ import { DetailsViewJsonOutput } from "./DetailsViewJsonOutput";
7
+
8
+ interface RawDataTabProps {
9
+ data: TraceSpan;
10
+ }
11
+
12
+ export const DetailsViewRawDataTab = ({
13
+ data,
14
+ }: RawDataTabProps): ReactElement => (
15
+ <div className="border-agentprism-border min-w-0 w-full rounded-md border bg-transparent">
16
+ <div className="flex justify-end p-1.5">
17
+ <CopyButton label="Raw" content={data.raw} />
18
+ </div>
19
+
20
+ <div className="pb-4">
21
+ <DetailsViewJsonOutput
22
+ content={data.raw}
23
+ id={data.id || "span-details"}
24
+ />
25
+ </div>
26
+ </div>
27
+ );
@@ -0,0 +1,75 @@
1
+ import type { ComponentPropsWithRef } from "react";
2
+
3
+ import cn from "classnames";
4
+
5
+ import type { ComponentSize } from "./shared";
6
+
7
+ type IconButtonSize = Extract<
8
+ ComponentSize,
9
+ "6" | "7" | "8" | "9" | "10" | "11" | "12" | "16"
10
+ >;
11
+ type IconButtonVariant = "default" | "ghost";
12
+
13
+ export type IconButtonProps = ComponentPropsWithRef<"button"> & {
14
+ /**
15
+ * The size of the icon button
16
+ */
17
+ size?: IconButtonSize;
18
+
19
+ /**
20
+ * The visual variant of the icon button
21
+ */
22
+ variant?: IconButtonVariant;
23
+
24
+ /**
25
+ * Accessible label for screen readers
26
+ * Required for accessibility compliance
27
+ */
28
+ "aria-label": string;
29
+ };
30
+
31
+ const sizeClasses: Record<IconButtonSize, string> = {
32
+ "6": "h-6 min-h-6",
33
+ "7": "h-7 min-h-7",
34
+ "8": "h-8 min-h-8",
35
+ "9": "h-9 min-h-9",
36
+ "10": "h-10 min-h-10",
37
+ "11": "h-11 min-h-11",
38
+ "12": "h-12 min-h-12",
39
+ "16": "h-16 min-h-16",
40
+ };
41
+
42
+ const variantClasses: Record<IconButtonVariant, string> = {
43
+ default: "border border-agentprism-border bg-transparent",
44
+ ghost: "bg-transparent",
45
+ };
46
+
47
+ // TODO: Remake to call Icon component directly instead of passing children
48
+ export const IconButton = ({
49
+ children,
50
+ className,
51
+ size = "6",
52
+ variant = "default",
53
+ type = "button",
54
+ "aria-label": ariaLabel,
55
+ ...rest
56
+ }: IconButtonProps) => {
57
+ return (
58
+ <button
59
+ type={type}
60
+ aria-label={ariaLabel}
61
+ className={cn(
62
+ className,
63
+ sizeClasses[size],
64
+ "inline-flex aspect-square shrink-0 items-center justify-center",
65
+ "rounded-md",
66
+ variantClasses[variant],
67
+ "text-agentprism-secondary-foreground",
68
+ "hover:bg-agentprism-secondary",
69
+ )}
70
+ {...rest}
71
+ >
72
+ {children}
73
+ </button>
74
+ );
75
+ };
@@ -0,0 +1,14 @@
1
+ import type { ComponentPropsWithRef } from "react";
2
+
3
+ import type { BadgeProps } from "./Badge";
4
+
5
+ import { Badge } from "./Badge";
6
+
7
+ export type PriceBadgeProps = ComponentPropsWithRef<"span"> & {
8
+ cost: number;
9
+ size?: BadgeProps["size"];
10
+ };
11
+
12
+ export const PriceBadge = ({ cost, size, ...rest }: PriceBadgeProps) => {
13
+ return <Badge size={size} {...rest} label={`$ ${cost}`} />;
14
+ };
@@ -0,0 +1,17 @@
1
+ import { Search } from "lucide-react";
2
+
3
+ import { TextInput, type TextInputProps } from "./TextInput";
4
+
5
+ /**
6
+ * A simple wrapper around the TextInput component.
7
+ * It adds a search icon and a placeholder.
8
+ */
9
+ export const SearchInput = ({ ...props }: TextInputProps) => {
10
+ return (
11
+ <TextInput
12
+ startIcon={<Search className="size-4" />}
13
+ placeholder="Filter..."
14
+ {...props}
15
+ />
16
+ );
17
+ };