@flowdot.ai/mcp-server 1.0.0

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 (315) hide show
  1. package/README.md +162 -0
  2. package/bin/flowdot-mcp.js +15 -0
  3. package/dist/api-client.d.ts +349 -0
  4. package/dist/api-client.d.ts.map +1 -0
  5. package/dist/api-client.js +789 -0
  6. package/dist/api-client.js.map +1 -0
  7. package/dist/index.d.ts +26 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +32 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/server.d.ts +15 -0
  12. package/dist/server.d.ts.map +1 -0
  13. package/dist/server.js +96 -0
  14. package/dist/server.js.map +1 -0
  15. package/dist/tools/add-connection.d.ts +16 -0
  16. package/dist/tools/add-connection.d.ts.map +1 -0
  17. package/dist/tools/add-connection.js +62 -0
  18. package/dist/tools/add-connection.js.map +1 -0
  19. package/dist/tools/add-custom-node-comment.d.ts +15 -0
  20. package/dist/tools/add-custom-node-comment.d.ts.map +1 -0
  21. package/dist/tools/add-custom-node-comment.js +69 -0
  22. package/dist/tools/add-custom-node-comment.js.map +1 -0
  23. package/dist/tools/add-node.d.ts +18 -0
  24. package/dist/tools/add-node.d.ts.map +1 -0
  25. package/dist/tools/add-node.js +66 -0
  26. package/dist/tools/add-node.js.map +1 -0
  27. package/dist/tools/add-shared-result-comment.d.ts +15 -0
  28. package/dist/tools/add-shared-result-comment.d.ts.map +1 -0
  29. package/dist/tools/add-shared-result-comment.js +70 -0
  30. package/dist/tools/add-shared-result-comment.js.map +1 -0
  31. package/dist/tools/add-workflow-comment.d.ts +14 -0
  32. package/dist/tools/add-workflow-comment.d.ts.map +1 -0
  33. package/dist/tools/add-workflow-comment.js +66 -0
  34. package/dist/tools/add-workflow-comment.js.map +1 -0
  35. package/dist/tools/agent-chat.d.ts +14 -0
  36. package/dist/tools/agent-chat.d.ts.map +1 -0
  37. package/dist/tools/agent-chat.js +80 -0
  38. package/dist/tools/agent-chat.js.map +1 -0
  39. package/dist/tools/cancel-execution.d.ts +12 -0
  40. package/dist/tools/cancel-execution.d.ts.map +1 -0
  41. package/dist/tools/cancel-execution.js +37 -0
  42. package/dist/tools/cancel-execution.js.map +1 -0
  43. package/dist/tools/clone-app.d.ts +11 -0
  44. package/dist/tools/clone-app.d.ts.map +1 -0
  45. package/dist/tools/clone-app.js +63 -0
  46. package/dist/tools/clone-app.js.map +1 -0
  47. package/dist/tools/copy-custom-node.d.ts +14 -0
  48. package/dist/tools/copy-custom-node.d.ts.map +1 -0
  49. package/dist/tools/copy-custom-node.js +50 -0
  50. package/dist/tools/copy-custom-node.js.map +1 -0
  51. package/dist/tools/create-app.d.ts +11 -0
  52. package/dist/tools/create-app.d.ts.map +1 -0
  53. package/dist/tools/create-app.js +182 -0
  54. package/dist/tools/create-app.js.map +1 -0
  55. package/dist/tools/create-custom-node.d.ts +28 -0
  56. package/dist/tools/create-custom-node.d.ts.map +1 -0
  57. package/dist/tools/create-custom-node.js +221 -0
  58. package/dist/tools/create-custom-node.js.map +1 -0
  59. package/dist/tools/create-input-preset.d.ts +15 -0
  60. package/dist/tools/create-input-preset.d.ts.map +1 -0
  61. package/dist/tools/create-input-preset.js +76 -0
  62. package/dist/tools/create-input-preset.js.map +1 -0
  63. package/dist/tools/create-shared-result.d.ts +17 -0
  64. package/dist/tools/create-shared-result.d.ts.map +1 -0
  65. package/dist/tools/create-shared-result.js +78 -0
  66. package/dist/tools/create-shared-result.js.map +1 -0
  67. package/dist/tools/create-workflow.d.ts +13 -0
  68. package/dist/tools/create-workflow.d.ts.map +1 -0
  69. package/dist/tools/create-workflow.js +49 -0
  70. package/dist/tools/create-workflow.js.map +1 -0
  71. package/dist/tools/delete-app.d.ts +11 -0
  72. package/dist/tools/delete-app.d.ts.map +1 -0
  73. package/dist/tools/delete-app.js +40 -0
  74. package/dist/tools/delete-app.js.map +1 -0
  75. package/dist/tools/delete-connection.d.ts +13 -0
  76. package/dist/tools/delete-connection.d.ts.map +1 -0
  77. package/dist/tools/delete-connection.js +41 -0
  78. package/dist/tools/delete-connection.js.map +1 -0
  79. package/dist/tools/delete-custom-node.d.ts +13 -0
  80. package/dist/tools/delete-custom-node.d.ts.map +1 -0
  81. package/dist/tools/delete-custom-node.js +41 -0
  82. package/dist/tools/delete-custom-node.js.map +1 -0
  83. package/dist/tools/delete-input-preset.d.ts +13 -0
  84. package/dist/tools/delete-input-preset.d.ts.map +1 -0
  85. package/dist/tools/delete-input-preset.js +42 -0
  86. package/dist/tools/delete-input-preset.js.map +1 -0
  87. package/dist/tools/delete-node.d.ts +13 -0
  88. package/dist/tools/delete-node.d.ts.map +1 -0
  89. package/dist/tools/delete-node.js +41 -0
  90. package/dist/tools/delete-node.js.map +1 -0
  91. package/dist/tools/delete-workflow.d.ts +12 -0
  92. package/dist/tools/delete-workflow.d.ts.map +1 -0
  93. package/dist/tools/delete-workflow.js +37 -0
  94. package/dist/tools/delete-workflow.js.map +1 -0
  95. package/dist/tools/duplicate-workflow.d.ts +13 -0
  96. package/dist/tools/duplicate-workflow.d.ts.map +1 -0
  97. package/dist/tools/duplicate-workflow.js +49 -0
  98. package/dist/tools/duplicate-workflow.js.map +1 -0
  99. package/dist/tools/execute-workflow.d.ts +15 -0
  100. package/dist/tools/execute-workflow.d.ts.map +1 -0
  101. package/dist/tools/execute-workflow.js +112 -0
  102. package/dist/tools/execute-workflow.js.map +1 -0
  103. package/dist/tools/favorite-custom-node.d.ts +14 -0
  104. package/dist/tools/favorite-custom-node.d.ts.map +1 -0
  105. package/dist/tools/favorite-custom-node.js +48 -0
  106. package/dist/tools/favorite-custom-node.js.map +1 -0
  107. package/dist/tools/favorite-workflow.d.ts +13 -0
  108. package/dist/tools/favorite-workflow.d.ts.map +1 -0
  109. package/dist/tools/favorite-workflow.js +42 -0
  110. package/dist/tools/favorite-workflow.js.map +1 -0
  111. package/dist/tools/get-app-template.d.ts +10 -0
  112. package/dist/tools/get-app-template.d.ts.map +1 -0
  113. package/dist/tools/get-app-template.js +856 -0
  114. package/dist/tools/get-app-template.js.map +1 -0
  115. package/dist/tools/get-app.d.ts +11 -0
  116. package/dist/tools/get-app.d.ts.map +1 -0
  117. package/dist/tools/get-app.js +124 -0
  118. package/dist/tools/get-app.js.map +1 -0
  119. package/dist/tools/get-custom-node-comments.d.ts +13 -0
  120. package/dist/tools/get-custom-node-comments.d.ts.map +1 -0
  121. package/dist/tools/get-custom-node-comments.js +65 -0
  122. package/dist/tools/get-custom-node-comments.js.map +1 -0
  123. package/dist/tools/get-custom-node-template.d.ts +31 -0
  124. package/dist/tools/get-custom-node-template.d.ts.map +1 -0
  125. package/dist/tools/get-custom-node-template.js +212 -0
  126. package/dist/tools/get-custom-node-template.js.map +1 -0
  127. package/dist/tools/get-custom-node.d.ts +13 -0
  128. package/dist/tools/get-custom-node.d.ts.map +1 -0
  129. package/dist/tools/get-custom-node.js +98 -0
  130. package/dist/tools/get-custom-node.js.map +1 -0
  131. package/dist/tools/get-execution-history.d.ts +14 -0
  132. package/dist/tools/get-execution-history.d.ts.map +1 -0
  133. package/dist/tools/get-execution-history.js +65 -0
  134. package/dist/tools/get-execution-history.js.map +1 -0
  135. package/dist/tools/get-execution.d.ts +12 -0
  136. package/dist/tools/get-execution.d.ts.map +1 -0
  137. package/dist/tools/get-execution.js +81 -0
  138. package/dist/tools/get-execution.js.map +1 -0
  139. package/dist/tools/get-input-preset.d.ts +13 -0
  140. package/dist/tools/get-input-preset.d.ts.map +1 -0
  141. package/dist/tools/get-input-preset.js +69 -0
  142. package/dist/tools/get-input-preset.js.map +1 -0
  143. package/dist/tools/get-node-connections.d.ts +13 -0
  144. package/dist/tools/get-node-connections.d.ts.map +1 -0
  145. package/dist/tools/get-node-connections.js +67 -0
  146. package/dist/tools/get-node-connections.js.map +1 -0
  147. package/dist/tools/get-node-schema.d.ts +12 -0
  148. package/dist/tools/get-node-schema.d.ts.map +1 -0
  149. package/dist/tools/get-node-schema.js +113 -0
  150. package/dist/tools/get-node-schema.js.map +1 -0
  151. package/dist/tools/get-public-workflows.d.ts +13 -0
  152. package/dist/tools/get-public-workflows.d.ts.map +1 -0
  153. package/dist/tools/get-public-workflows.js +63 -0
  154. package/dist/tools/get-public-workflows.js.map +1 -0
  155. package/dist/tools/get-shared-result-comments.d.ts +13 -0
  156. package/dist/tools/get-shared-result-comments.d.ts.map +1 -0
  157. package/dist/tools/get-shared-result-comments.js +62 -0
  158. package/dist/tools/get-shared-result-comments.js.map +1 -0
  159. package/dist/tools/get-shared-result.d.ts +13 -0
  160. package/dist/tools/get-shared-result.d.ts.map +1 -0
  161. package/dist/tools/get-shared-result.js +83 -0
  162. package/dist/tools/get-shared-result.js.map +1 -0
  163. package/dist/tools/get-workflow-comments.d.ts +12 -0
  164. package/dist/tools/get-workflow-comments.d.ts.map +1 -0
  165. package/dist/tools/get-workflow-comments.js +60 -0
  166. package/dist/tools/get-workflow-comments.js.map +1 -0
  167. package/dist/tools/get-workflow-details.d.ts +12 -0
  168. package/dist/tools/get-workflow-details.d.ts.map +1 -0
  169. package/dist/tools/get-workflow-details.js +87 -0
  170. package/dist/tools/get-workflow-details.js.map +1 -0
  171. package/dist/tools/get-workflow-graph.d.ts +12 -0
  172. package/dist/tools/get-workflow-graph.d.ts.map +1 -0
  173. package/dist/tools/get-workflow-graph.js +83 -0
  174. package/dist/tools/get-workflow-graph.js.map +1 -0
  175. package/dist/tools/get-workflow-inputs-schema.d.ts +12 -0
  176. package/dist/tools/get-workflow-inputs-schema.d.ts.map +1 -0
  177. package/dist/tools/get-workflow-inputs-schema.js +74 -0
  178. package/dist/tools/get-workflow-inputs-schema.js.map +1 -0
  179. package/dist/tools/get-workflow-metrics.d.ts +13 -0
  180. package/dist/tools/get-workflow-metrics.d.ts.map +1 -0
  181. package/dist/tools/get-workflow-metrics.js +65 -0
  182. package/dist/tools/get-workflow-metrics.js.map +1 -0
  183. package/dist/tools/get-workflow-public-url.d.ts +12 -0
  184. package/dist/tools/get-workflow-public-url.d.ts.map +1 -0
  185. package/dist/tools/get-workflow-public-url.js +48 -0
  186. package/dist/tools/get-workflow-public-url.js.map +1 -0
  187. package/dist/tools/get-workflow-tags.d.ts +12 -0
  188. package/dist/tools/get-workflow-tags.d.ts.map +1 -0
  189. package/dist/tools/get-workflow-tags.js +42 -0
  190. package/dist/tools/get-workflow-tags.js.map +1 -0
  191. package/dist/tools/index.d.ts +12 -0
  192. package/dist/tools/index.d.ts.map +1 -0
  193. package/dist/tools/index.js +407 -0
  194. package/dist/tools/index.js.map +1 -0
  195. package/dist/tools/link-app-workflow.d.ts +11 -0
  196. package/dist/tools/link-app-workflow.d.ts.map +1 -0
  197. package/dist/tools/link-app-workflow.js +69 -0
  198. package/dist/tools/link-app-workflow.js.map +1 -0
  199. package/dist/tools/list-apps.d.ts +11 -0
  200. package/dist/tools/list-apps.d.ts.map +1 -0
  201. package/dist/tools/list-apps.js +75 -0
  202. package/dist/tools/list-apps.js.map +1 -0
  203. package/dist/tools/list-available-nodes.d.ts +10 -0
  204. package/dist/tools/list-available-nodes.d.ts.map +1 -0
  205. package/dist/tools/list-available-nodes.js +94 -0
  206. package/dist/tools/list-available-nodes.js.map +1 -0
  207. package/dist/tools/list-custom-nodes.d.ts +16 -0
  208. package/dist/tools/list-custom-nodes.d.ts.map +1 -0
  209. package/dist/tools/list-custom-nodes.js +80 -0
  210. package/dist/tools/list-custom-nodes.js.map +1 -0
  211. package/dist/tools/list-input-presets.d.ts +15 -0
  212. package/dist/tools/list-input-presets.d.ts.map +1 -0
  213. package/dist/tools/list-input-presets.js +100 -0
  214. package/dist/tools/list-input-presets.js.map +1 -0
  215. package/dist/tools/list-shared-results.d.ts +15 -0
  216. package/dist/tools/list-shared-results.d.ts.map +1 -0
  217. package/dist/tools/list-shared-results.js +80 -0
  218. package/dist/tools/list-shared-results.js.map +1 -0
  219. package/dist/tools/list-workflows.d.ts +13 -0
  220. package/dist/tools/list-workflows.d.ts.map +1 -0
  221. package/dist/tools/list-workflows.js +70 -0
  222. package/dist/tools/list-workflows.js.map +1 -0
  223. package/dist/tools/publish-app.d.ts +11 -0
  224. package/dist/tools/publish-app.d.ts.map +1 -0
  225. package/dist/tools/publish-app.js +44 -0
  226. package/dist/tools/publish-app.js.map +1 -0
  227. package/dist/tools/retry-execution.d.ts +12 -0
  228. package/dist/tools/retry-execution.d.ts.map +1 -0
  229. package/dist/tools/retry-execution.js +45 -0
  230. package/dist/tools/retry-execution.js.map +1 -0
  231. package/dist/tools/search-apps.d.ts +11 -0
  232. package/dist/tools/search-apps.d.ts.map +1 -0
  233. package/dist/tools/search-apps.js +104 -0
  234. package/dist/tools/search-apps.js.map +1 -0
  235. package/dist/tools/search-public-custom-nodes.d.ts +19 -0
  236. package/dist/tools/search-public-custom-nodes.d.ts.map +1 -0
  237. package/dist/tools/search-public-custom-nodes.js +101 -0
  238. package/dist/tools/search-public-custom-nodes.js.map +1 -0
  239. package/dist/tools/search-workflows.d.ts +14 -0
  240. package/dist/tools/search-workflows.d.ts.map +1 -0
  241. package/dist/tools/search-workflows.js +62 -0
  242. package/dist/tools/search-workflows.js.map +1 -0
  243. package/dist/tools/set-workflow-tags.d.ts +13 -0
  244. package/dist/tools/set-workflow-tags.d.ts.map +1 -0
  245. package/dist/tools/set-workflow-tags.js +42 -0
  246. package/dist/tools/set-workflow-tags.js.map +1 -0
  247. package/dist/tools/stream-execution.d.ts +13 -0
  248. package/dist/tools/stream-execution.d.ts.map +1 -0
  249. package/dist/tools/stream-execution.js +70 -0
  250. package/dist/tools/stream-execution.js.map +1 -0
  251. package/dist/tools/toggle-community-inputs.d.ts +13 -0
  252. package/dist/tools/toggle-community-inputs.d.ts.map +1 -0
  253. package/dist/tools/toggle-community-inputs.js +49 -0
  254. package/dist/tools/toggle-community-inputs.js.map +1 -0
  255. package/dist/tools/toggle-custom-node-visibility.d.ts +14 -0
  256. package/dist/tools/toggle-custom-node-visibility.d.ts.map +1 -0
  257. package/dist/tools/toggle-custom-node-visibility.js +59 -0
  258. package/dist/tools/toggle-custom-node-visibility.js.map +1 -0
  259. package/dist/tools/toggle-workflow-public.d.ts +13 -0
  260. package/dist/tools/toggle-workflow-public.d.ts.map +1 -0
  261. package/dist/tools/toggle-workflow-public.js +42 -0
  262. package/dist/tools/toggle-workflow-public.js.map +1 -0
  263. package/dist/tools/unlink-app-workflow.d.ts +11 -0
  264. package/dist/tools/unlink-app-workflow.d.ts.map +1 -0
  265. package/dist/tools/unlink-app-workflow.js +44 -0
  266. package/dist/tools/unlink-app-workflow.js.map +1 -0
  267. package/dist/tools/unpublish-app.d.ts +11 -0
  268. package/dist/tools/unpublish-app.d.ts.map +1 -0
  269. package/dist/tools/unpublish-app.js +39 -0
  270. package/dist/tools/unpublish-app.js.map +1 -0
  271. package/dist/tools/update-app.d.ts +11 -0
  272. package/dist/tools/update-app.d.ts.map +1 -0
  273. package/dist/tools/update-app.js +101 -0
  274. package/dist/tools/update-app.js.map +1 -0
  275. package/dist/tools/update-custom-node.d.ts +28 -0
  276. package/dist/tools/update-custom-node.d.ts.map +1 -0
  277. package/dist/tools/update-custom-node.js +174 -0
  278. package/dist/tools/update-custom-node.js.map +1 -0
  279. package/dist/tools/update-input-preset.d.ts +16 -0
  280. package/dist/tools/update-input-preset.d.ts.map +1 -0
  281. package/dist/tools/update-input-preset.js +79 -0
  282. package/dist/tools/update-input-preset.js.map +1 -0
  283. package/dist/tools/update-node.d.ts +18 -0
  284. package/dist/tools/update-node.d.ts.map +1 -0
  285. package/dist/tools/update-node.js +73 -0
  286. package/dist/tools/update-node.js.map +1 -0
  287. package/dist/tools/validate-workflow.d.ts +12 -0
  288. package/dist/tools/validate-workflow.d.ts.map +1 -0
  289. package/dist/tools/validate-workflow.js +61 -0
  290. package/dist/tools/validate-workflow.js.map +1 -0
  291. package/dist/tools/vote-custom-node.d.ts +14 -0
  292. package/dist/tools/vote-custom-node.d.ts.map +1 -0
  293. package/dist/tools/vote-custom-node.js +60 -0
  294. package/dist/tools/vote-custom-node.js.map +1 -0
  295. package/dist/tools/vote-input-preset.d.ts +14 -0
  296. package/dist/tools/vote-input-preset.d.ts.map +1 -0
  297. package/dist/tools/vote-input-preset.js +49 -0
  298. package/dist/tools/vote-input-preset.js.map +1 -0
  299. package/dist/tools/vote-shared-result.d.ts +14 -0
  300. package/dist/tools/vote-shared-result.d.ts.map +1 -0
  301. package/dist/tools/vote-shared-result.js +49 -0
  302. package/dist/tools/vote-shared-result.js.map +1 -0
  303. package/dist/tools/vote-workflow.d.ts +13 -0
  304. package/dist/tools/vote-workflow.d.ts.map +1 -0
  305. package/dist/tools/vote-workflow.js +45 -0
  306. package/dist/tools/vote-workflow.js.map +1 -0
  307. package/dist/types.d.ts +670 -0
  308. package/dist/types.d.ts.map +1 -0
  309. package/dist/types.js +5 -0
  310. package/dist/types.js.map +1 -0
  311. package/dist/utils/script-validator.d.ts +35 -0
  312. package/dist/utils/script-validator.d.ts.map +1 -0
  313. package/dist/utils/script-validator.js +282 -0
  314. package/dist/utils/script-validator.js.map +1 -0
  315. package/package.json +54 -0
@@ -0,0 +1,856 @@
1
+ /**
2
+ * get_app_template Tool
3
+ *
4
+ * Get example code and templates for creating FlowDot apps.
5
+ * No API call required - returns pre-defined templates.
6
+ */
7
+ export const getAppTemplateTool = {
8
+ name: 'get_app_template',
9
+ description: `Get example code and templates for creating FlowDot apps.
10
+
11
+ ## EXECUTION ENVIRONMENT
12
+ FlowDot apps run in a sandboxed browser iframe with:
13
+ - React 18 (global - use React.useState, React.useEffect, etc.)
14
+ - Tailwind CSS (full utility classes available)
15
+ - FlowDot color tokens: primary-50 to primary-900, secondary-50 to secondary-900
16
+ - invokeWorkflow(workflowHash, inputs) - to call linked workflows
17
+
18
+ ## CRITICAL CODE RULES
19
+ 1. NO IMPORTS - React is global (use React.useState, React.useEffect, React.useRef, etc.)
20
+ 2. NO EXPORTS - Just define your function
21
+ 3. Function must be named: function MyAppName() { ... }
22
+ 4. Use Tailwind CSS for ALL styling
23
+
24
+ ## WORKFLOW RESPONSE FORMAT
25
+ invokeWorkflow returns data in this structure:
26
+ {
27
+ "data": {
28
+ "[nodeId]": {
29
+ "nodeId": "uuid",
30
+ "nodeTitle": "My Output Node",
31
+ "nodeType": "text_output",
32
+ "outputs": {
33
+ "Consolidated Text": { "value": "the actual data", "metadata": {...} }
34
+ }
35
+ }
36
+ }
37
+ }
38
+
39
+ IMPORTANT: Use this helper function to extract outputs by node title:
40
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
41
+ const node = Object.values(result?.data || {}).find(n => n.nodeTitle === nodeTitle);
42
+ return node?.outputs?.[socketName]?.value;
43
+ };
44
+
45
+ Example: const weatherData = getNodeOutput(result, 'Weather Results', 'Consolidated Text');
46
+
47
+ ## DISPLAY MODES
48
+ Set config.displayMode to:
49
+ - "windowed": Standard view with FlowDot header (default)
50
+ - "fullscreen": Full viewport, minimal floating control bar
51
+ - "embedded": No FlowDot UI, for iframe embedding
52
+
53
+ Available templates:
54
+ - "basic" - Simple form that invokes a workflow
55
+ - "chat" - Chat interface with streaming
56
+ - "dashboard" - Dashboard with multiple workflow calls
57
+ - "form-builder" - Dynamic form based on workflow schema
58
+ - "data-viewer" - Display workflow results in tables/charts
59
+
60
+ You can also request "all" to see all templates at once.`,
61
+ inputSchema: {
62
+ type: 'object',
63
+ properties: {
64
+ template: {
65
+ type: 'string',
66
+ enum: ['basic', 'chat', 'dashboard', 'form-builder', 'data-viewer', 'all'],
67
+ description: 'Template type to retrieve (default: basic)',
68
+ },
69
+ },
70
+ },
71
+ };
72
+ const TEMPLATES = {
73
+ basic: {
74
+ name: 'Basic Form App',
75
+ description: 'A simple form that submits data to a workflow and displays results.',
76
+ code: `function BasicFormApp() {
77
+ const [input, setInput] = React.useState('');
78
+ const [result, setResult] = React.useState(null);
79
+ const [loading, setLoading] = React.useState(false);
80
+ const [error, setError] = React.useState(null);
81
+
82
+ // Helper to extract output from workflow response by node title
83
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
84
+ const node = Object.values(result?.data || {}).find(n => n.nodeTitle === nodeTitle);
85
+ return node?.outputs?.[socketName]?.value;
86
+ };
87
+
88
+ const handleSubmit = async (e) => {
89
+ e.preventDefault();
90
+ setLoading(true);
91
+ setError(null);
92
+
93
+ try {
94
+ // Replace 'YOUR_WORKFLOW_HASH' with your actual workflow hash
95
+ const response = await invokeWorkflow('YOUR_WORKFLOW_HASH', {
96
+ text: input
97
+ });
98
+ // Extract the output - replace 'Output' with your node's title
99
+ const output = getNodeOutput(response, 'Output');
100
+ setResult(output || response);
101
+ } catch (err) {
102
+ setError(err.message || 'An error occurred');
103
+ } finally {
104
+ setLoading(false);
105
+ }
106
+ };
107
+
108
+ return (
109
+ <div className="min-h-screen bg-gray-50 p-6">
110
+ <div className="max-w-2xl mx-auto">
111
+ <h1 className="text-2xl font-bold mb-6">My FlowDot App</h1>
112
+
113
+ <form onSubmit={handleSubmit} className="space-y-4">
114
+ <div>
115
+ <label className="block text-sm font-medium mb-1">Input</label>
116
+ <input
117
+ type="text"
118
+ value={input}
119
+ onChange={(e) => setInput(e.target.value)}
120
+ placeholder="Enter your text..."
121
+ className="w-full p-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
122
+ disabled={loading}
123
+ />
124
+ </div>
125
+
126
+ <button
127
+ type="submit"
128
+ disabled={loading || !input.trim()}
129
+ className="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 disabled:opacity-50"
130
+ >
131
+ {loading ? 'Processing...' : 'Submit'}
132
+ </button>
133
+ </form>
134
+
135
+ {error && (
136
+ <div className="mt-4 p-4 bg-red-50 text-red-700 rounded-lg">
137
+ {error}
138
+ </div>
139
+ )}
140
+
141
+ {result && (
142
+ <div className="mt-6 p-4 bg-gray-50 rounded-lg">
143
+ <h2 className="font-semibold mb-2">Result:</h2>
144
+ <pre className="whitespace-pre-wrap text-sm">
145
+ {typeof result === 'string' ? result : JSON.stringify(result, null, 2)}
146
+ </pre>
147
+ </div>
148
+ )}
149
+ </div>
150
+ </div>
151
+ );
152
+ }`,
153
+ },
154
+ chat: {
155
+ name: 'Chat Interface',
156
+ description: 'A chat-style interface for conversational workflows.',
157
+ code: `function ChatApp() {
158
+ const [messages, setMessages] = React.useState([]);
159
+ const [input, setInput] = React.useState('');
160
+ const [loading, setLoading] = React.useState(false);
161
+ const messagesEndRef = React.useRef(null);
162
+
163
+ // Helper to extract output from workflow response by node title
164
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
165
+ const node = Object.values(result?.data || {}).find(n => n.nodeTitle === nodeTitle);
166
+ return node?.outputs?.[socketName]?.value;
167
+ };
168
+
169
+ const scrollToBottom = () => {
170
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
171
+ };
172
+
173
+ React.useEffect(() => {
174
+ scrollToBottom();
175
+ }, [messages]);
176
+
177
+ const handleSend = async () => {
178
+ if (!input.trim() || loading) return;
179
+
180
+ const userMessage = { role: 'user', content: input };
181
+ setMessages(prev => [...prev, userMessage]);
182
+ setInput('');
183
+ setLoading(true);
184
+
185
+ try {
186
+ // Replace 'YOUR_WORKFLOW_HASH' with your LLM workflow hash
187
+ const response = await invokeWorkflow('YOUR_WORKFLOW_HASH', {
188
+ message: input,
189
+ history: messages.map(m => ({ role: m.role, content: m.content }))
190
+ });
191
+
192
+ // Extract the response - replace 'Response' with your output node's title
193
+ const responseText = getNodeOutput(response, 'Response') || JSON.stringify(response);
194
+ const assistantMessage = {
195
+ role: 'assistant',
196
+ content: responseText
197
+ };
198
+ setMessages(prev => [...prev, assistantMessage]);
199
+ } catch (err) {
200
+ setMessages(prev => [...prev, {
201
+ role: 'error',
202
+ content: err.message || 'Failed to get response'
203
+ }]);
204
+ } finally {
205
+ setLoading(false);
206
+ }
207
+ };
208
+
209
+ const handleKeyPress = (e) => {
210
+ if (e.key === 'Enter' && !e.shiftKey) {
211
+ e.preventDefault();
212
+ handleSend();
213
+ }
214
+ };
215
+
216
+ return (
217
+ <div className="flex flex-col h-screen bg-gray-50">
218
+ <div className="bg-white border-b p-4">
219
+ <h1 className="text-xl font-bold">Chat Assistant</h1>
220
+ </div>
221
+
222
+ <div className="flex-1 overflow-y-auto p-4 space-y-4">
223
+ {messages.length === 0 && (
224
+ <div className="text-center text-gray-500 mt-8">
225
+ Start a conversation by typing a message below.
226
+ </div>
227
+ )}
228
+
229
+ {messages.map((msg, idx) => (
230
+ <div
231
+ key={idx}
232
+ className={\`flex \${msg.role === 'user' ? 'justify-end' : 'justify-start'}\`}
233
+ >
234
+ <div
235
+ className={\`max-w-[80%] p-3 rounded-lg \${
236
+ msg.role === 'user'
237
+ ? 'bg-blue-600 text-white'
238
+ : msg.role === 'error'
239
+ ? 'bg-red-100 text-red-700'
240
+ : 'bg-gray-100 text-gray-900'
241
+ }\`}
242
+ >
243
+ <p className="whitespace-pre-wrap">{msg.content}</p>
244
+ </div>
245
+ </div>
246
+ ))}
247
+
248
+ {loading && (
249
+ <div className="flex justify-start">
250
+ <div className="bg-gray-100 p-3 rounded-lg">
251
+ <div className="flex space-x-2">
252
+ <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" />
253
+ <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-100" />
254
+ <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce delay-200" />
255
+ </div>
256
+ </div>
257
+ </div>
258
+ )}
259
+
260
+ <div ref={messagesEndRef} />
261
+ </div>
262
+
263
+ <div className="border-t bg-white p-4">
264
+ <div className="flex space-x-2 max-w-3xl mx-auto">
265
+ <input
266
+ type="text"
267
+ value={input}
268
+ onChange={(e) => setInput(e.target.value)}
269
+ onKeyPress={handleKeyPress}
270
+ placeholder="Type your message..."
271
+ className="flex-1 p-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
272
+ disabled={loading}
273
+ />
274
+ <button
275
+ onClick={handleSend}
276
+ disabled={loading || !input.trim()}
277
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
278
+ >
279
+ Send
280
+ </button>
281
+ </div>
282
+ </div>
283
+ </div>
284
+ );
285
+ }`,
286
+ },
287
+ dashboard: {
288
+ name: 'Dashboard App',
289
+ description: 'A dashboard that displays data from multiple workflow calls.',
290
+ code: `function DashboardApp() {
291
+ const [stats, setStats] = React.useState(null);
292
+ const [items, setItems] = React.useState([]);
293
+ const [loading, setLoading] = React.useState(true);
294
+ const [error, setError] = React.useState(null);
295
+
296
+ // Helper to extract output from workflow response by node title
297
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
298
+ const node = Object.values(result?.data || {}).find(n => n.nodeTitle === nodeTitle);
299
+ return node?.outputs?.[socketName]?.value;
300
+ };
301
+
302
+ const loadDashboardData = async () => {
303
+ setLoading(true);
304
+ setError(null);
305
+
306
+ try {
307
+ // Load data from multiple workflows in parallel
308
+ const [statsResult, itemsResult] = await Promise.all([
309
+ invokeWorkflow('STATS_WORKFLOW_HASH', {}),
310
+ invokeWorkflow('ITEMS_WORKFLOW_HASH', { limit: 10 })
311
+ ]);
312
+
313
+ // Extract outputs - replace node titles with your actual node names
314
+ const statsData = getNodeOutput(statsResult, 'Stats Output');
315
+ const itemsData = getNodeOutput(itemsResult, 'Items Output');
316
+
317
+ setStats(statsData ? JSON.parse(statsData) : statsResult);
318
+ setItems(itemsData ? JSON.parse(itemsData) : itemsResult.items || []);
319
+ } catch (err) {
320
+ setError(err.message || 'Failed to load dashboard data');
321
+ } finally {
322
+ setLoading(false);
323
+ }
324
+ };
325
+
326
+ React.useEffect(() => {
327
+ loadDashboardData();
328
+ }, []);
329
+
330
+ if (loading) {
331
+ return (
332
+ <div className="min-h-screen bg-gray-50 flex items-center justify-center">
333
+ <div className="text-lg text-gray-500">Loading dashboard...</div>
334
+ </div>
335
+ );
336
+ }
337
+
338
+ if (error) {
339
+ return (
340
+ <div className="min-h-screen bg-gray-50 p-6">
341
+ <div className="bg-red-50 text-red-700 p-4 rounded-lg">
342
+ {error}
343
+ <button
344
+ onClick={loadDashboardData}
345
+ className="ml-4 underline hover:no-underline"
346
+ >
347
+ Retry
348
+ </button>
349
+ </div>
350
+ </div>
351
+ );
352
+ }
353
+
354
+ return (
355
+ <div className="min-h-screen bg-gray-50 p-6 space-y-6">
356
+ <div className="flex justify-between items-center">
357
+ <h1 className="text-2xl font-bold">Dashboard</h1>
358
+ <button
359
+ onClick={loadDashboardData}
360
+ className="px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200"
361
+ >
362
+ Refresh
363
+ </button>
364
+ </div>
365
+
366
+ {/* Stats Cards */}
367
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
368
+ {stats && Object.entries(stats).map(([key, value]) => (
369
+ <div key={key} className="bg-white p-6 rounded-lg shadow">
370
+ <div className="text-sm text-gray-500 uppercase">{key}</div>
371
+ <div className="text-3xl font-bold mt-1">
372
+ {typeof value === 'number' ? value.toLocaleString() : value}
373
+ </div>
374
+ </div>
375
+ ))}
376
+ </div>
377
+
378
+ {/* Items Table */}
379
+ <div className="bg-white rounded-lg shadow overflow-hidden">
380
+ <table className="w-full">
381
+ <thead className="bg-gray-50">
382
+ <tr>
383
+ <th className="px-4 py-3 text-left text-sm font-medium text-gray-500">Name</th>
384
+ <th className="px-4 py-3 text-left text-sm font-medium text-gray-500">Status</th>
385
+ <th className="px-4 py-3 text-left text-sm font-medium text-gray-500">Date</th>
386
+ </tr>
387
+ </thead>
388
+ <tbody className="divide-y">
389
+ {items.map((item, idx) => (
390
+ <tr key={idx} className="hover:bg-gray-50">
391
+ <td className="px-4 py-3">{item.name}</td>
392
+ <td className="px-4 py-3">
393
+ <span className={\`px-2 py-1 text-xs rounded-full \${
394
+ item.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
395
+ }\`}>
396
+ {item.status}
397
+ </span>
398
+ </td>
399
+ <td className="px-4 py-3 text-gray-500">{item.date}</td>
400
+ </tr>
401
+ ))}
402
+ </tbody>
403
+ </table>
404
+ </div>
405
+ </div>
406
+ );
407
+ }`,
408
+ },
409
+ 'form-builder': {
410
+ name: 'Dynamic Form Builder',
411
+ description: 'A form that dynamically generates fields based on workflow input schema.',
412
+ code: `function DynamicFormApp() {
413
+ const [formData, setFormData] = React.useState({});
414
+ const [result, setResult] = React.useState(null);
415
+ const [loading, setLoading] = React.useState(false);
416
+ const [schemaLoading, setSchemaLoading] = React.useState(true);
417
+
418
+ // Define your workflow hash here
419
+ const WORKFLOW_HASH = 'YOUR_WORKFLOW_HASH';
420
+
421
+ // Helper to extract output from workflow response by node title
422
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
423
+ const node = Object.values(result?.data || {}).find(n => n.nodeTitle === nodeTitle);
424
+ return node?.outputs?.[socketName]?.value;
425
+ };
426
+
427
+ // Define the expected input schema
428
+ // This would typically come from the workflow's signature
429
+ const inputSchema = [
430
+ { name: 'text', type: 'string', description: 'Text input', required: true },
431
+ { name: 'count', type: 'number', description: 'Number of items', required: false },
432
+ { name: 'enabled', type: 'boolean', description: 'Enable feature', required: false },
433
+ { name: 'options', type: 'select', options: ['option1', 'option2', 'option3'], required: false },
434
+ ];
435
+
436
+ React.useEffect(() => {
437
+ // Initialize form data with defaults
438
+ const defaults = {};
439
+ inputSchema.forEach(field => {
440
+ if (field.type === 'boolean') defaults[field.name] = false;
441
+ else if (field.type === 'number') defaults[field.name] = 0;
442
+ else defaults[field.name] = '';
443
+ });
444
+ setFormData(defaults);
445
+ setSchemaLoading(false);
446
+ }, []);
447
+
448
+ const handleChange = (name, value) => {
449
+ setFormData(prev => ({ ...prev, [name]: value }));
450
+ };
451
+
452
+ const handleSubmit = async (e) => {
453
+ e.preventDefault();
454
+ setLoading(true);
455
+
456
+ try {
457
+ const response = await invokeWorkflow(WORKFLOW_HASH, formData);
458
+ // Extract the output - replace 'Output' with your node's title
459
+ const output = getNodeOutput(response, 'Output');
460
+ setResult(output || response);
461
+ } catch (err) {
462
+ setResult({ error: err.message });
463
+ } finally {
464
+ setLoading(false);
465
+ }
466
+ };
467
+
468
+ const renderField = (field) => {
469
+ const value = formData[field.name];
470
+
471
+ switch (field.type) {
472
+ case 'boolean':
473
+ return (
474
+ <label className="flex items-center space-x-2">
475
+ <input
476
+ type="checkbox"
477
+ checked={value || false}
478
+ onChange={(e) => handleChange(field.name, e.target.checked)}
479
+ className="rounded"
480
+ />
481
+ <span>{field.description}</span>
482
+ </label>
483
+ );
484
+ case 'number':
485
+ return (
486
+ <input
487
+ type="number"
488
+ value={value || 0}
489
+ onChange={(e) => handleChange(field.name, Number(e.target.value))}
490
+ className="w-full p-2 border rounded-lg"
491
+ />
492
+ );
493
+ case 'select':
494
+ return (
495
+ <select
496
+ value={value || ''}
497
+ onChange={(e) => handleChange(field.name, e.target.value)}
498
+ className="w-full p-2 border rounded-lg"
499
+ >
500
+ <option value="">Select...</option>
501
+ {field.options?.map(opt => (
502
+ <option key={opt} value={opt}>{opt}</option>
503
+ ))}
504
+ </select>
505
+ );
506
+ default:
507
+ return (
508
+ <input
509
+ type="text"
510
+ value={value || ''}
511
+ onChange={(e) => handleChange(field.name, e.target.value)}
512
+ placeholder={field.description}
513
+ className="w-full p-2 border rounded-lg"
514
+ />
515
+ );
516
+ }
517
+ };
518
+
519
+ if (schemaLoading) {
520
+ return <div className="min-h-screen bg-gray-50 p-6 text-center">Loading form...</div>;
521
+ }
522
+
523
+ return (
524
+ <div className="min-h-screen bg-gray-50 p-6">
525
+ <div className="max-w-2xl mx-auto">
526
+ <h1 className="text-2xl font-bold mb-6">Dynamic Form</h1>
527
+
528
+ <form onSubmit={handleSubmit} className="space-y-4">
529
+ {inputSchema.map(field => (
530
+ <div key={field.name}>
531
+ <label className="block text-sm font-medium mb-1">
532
+ {field.name}
533
+ {field.required && <span className="text-red-500">*</span>}
534
+ </label>
535
+ {renderField(field)}
536
+ {field.description && field.type !== 'boolean' && (
537
+ <p className="text-xs text-gray-500 mt-1">{field.description}</p>
538
+ )}
539
+ </div>
540
+ ))}
541
+
542
+ <button
543
+ type="submit"
544
+ disabled={loading}
545
+ className="w-full bg-blue-600 text-white py-2 px-4 rounded-lg hover:bg-blue-700 disabled:opacity-50"
546
+ >
547
+ {loading ? 'Processing...' : 'Submit'}
548
+ </button>
549
+ </form>
550
+
551
+ {result && (
552
+ <div className="mt-6 p-4 bg-white rounded-lg shadow">
553
+ <h2 className="font-semibold mb-2">Result:</h2>
554
+ <pre className="whitespace-pre-wrap text-sm overflow-x-auto">
555
+ {JSON.stringify(result, null, 2)}
556
+ </pre>
557
+ </div>
558
+ )}
559
+ </div>
560
+ </div>
561
+ );
562
+ }`,
563
+ },
564
+ 'data-viewer': {
565
+ name: 'Data Viewer',
566
+ description: 'Display workflow results in tables and charts.',
567
+ code: `function DataViewerApp() {
568
+ const [data, setData] = React.useState([]);
569
+ const [loading, setLoading] = React.useState(false);
570
+ const [viewMode, setViewMode] = React.useState('table'); // 'table' or 'cards'
571
+ const [sortField, setSortField] = React.useState(null);
572
+ const [sortDirection, setSortDirection] = React.useState('asc');
573
+
574
+ // Helper to extract output from workflow response by node title
575
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
576
+ const node = Object.values(result?.data || {}).find(n => n.nodeTitle === nodeTitle);
577
+ return node?.outputs?.[socketName]?.value;
578
+ };
579
+
580
+ const loadData = async () => {
581
+ setLoading(true);
582
+ try {
583
+ const result = await invokeWorkflow('YOUR_WORKFLOW_HASH', {});
584
+ // Extract the data - replace 'Data Output' with your node's title
585
+ const outputData = getNodeOutput(result, 'Data Output');
586
+ // Parse if it's JSON string, otherwise use as-is
587
+ const parsed = outputData ? JSON.parse(outputData) : result;
588
+ setData(parsed.items || parsed.data || parsed || []);
589
+ } catch (err) {
590
+ console.error('Failed to load data:', err);
591
+ } finally {
592
+ setLoading(false);
593
+ }
594
+ };
595
+
596
+ React.useEffect(() => {
597
+ loadData();
598
+ }, []);
599
+
600
+ const handleSort = (field) => {
601
+ if (sortField === field) {
602
+ setSortDirection(prev => prev === 'asc' ? 'desc' : 'asc');
603
+ } else {
604
+ setSortField(field);
605
+ setSortDirection('asc');
606
+ }
607
+ };
608
+
609
+ const sortedData = React.useMemo(() => {
610
+ if (!sortField) return data;
611
+ return [...data].sort((a, b) => {
612
+ const aVal = a[sortField];
613
+ const bVal = b[sortField];
614
+ const direction = sortDirection === 'asc' ? 1 : -1;
615
+ if (typeof aVal === 'number') return (aVal - bVal) * direction;
616
+ return String(aVal).localeCompare(String(bVal)) * direction;
617
+ });
618
+ }, [data, sortField, sortDirection]);
619
+
620
+ // Get column headers from first item
621
+ const columns = data.length > 0 ? Object.keys(data[0]) : [];
622
+
623
+ return (
624
+ <div className="min-h-screen bg-gray-50 p-6">
625
+ <div className="flex justify-between items-center mb-6">
626
+ <h1 className="text-2xl font-bold">Data Viewer</h1>
627
+ <div className="flex space-x-2">
628
+ <button
629
+ onClick={() => setViewMode('table')}
630
+ className={\`px-3 py-1 rounded \${viewMode === 'table' ? 'bg-blue-600 text-white' : 'bg-gray-100'}\`}
631
+ >
632
+ Table
633
+ </button>
634
+ <button
635
+ onClick={() => setViewMode('cards')}
636
+ className={\`px-3 py-1 rounded \${viewMode === 'cards' ? 'bg-blue-600 text-white' : 'bg-gray-100'}\`}
637
+ >
638
+ Cards
639
+ </button>
640
+ <button
641
+ onClick={loadData}
642
+ disabled={loading}
643
+ className="px-3 py-1 bg-gray-100 rounded hover:bg-gray-200 disabled:opacity-50"
644
+ >
645
+ {loading ? 'Loading...' : 'Refresh'}
646
+ </button>
647
+ </div>
648
+ </div>
649
+
650
+ {viewMode === 'table' ? (
651
+ <div className="bg-white rounded-lg shadow overflow-x-auto">
652
+ <table className="w-full">
653
+ <thead className="bg-gray-50">
654
+ <tr>
655
+ {columns.map(col => (
656
+ <th
657
+ key={col}
658
+ onClick={() => handleSort(col)}
659
+ className="px-4 py-3 text-left text-sm font-medium text-gray-500 cursor-pointer hover:bg-gray-100"
660
+ >
661
+ {col}
662
+ {sortField === col && (
663
+ <span className="ml-1">{sortDirection === 'asc' ? '↑' : '↓'}</span>
664
+ )}
665
+ </th>
666
+ ))}
667
+ </tr>
668
+ </thead>
669
+ <tbody className="divide-y">
670
+ {sortedData.map((row, idx) => (
671
+ <tr key={idx} className="hover:bg-gray-50">
672
+ {columns.map(col => (
673
+ <td key={col} className="px-4 py-3 text-sm">
674
+ {typeof row[col] === 'object'
675
+ ? JSON.stringify(row[col])
676
+ : String(row[col])
677
+ }
678
+ </td>
679
+ ))}
680
+ </tr>
681
+ ))}
682
+ </tbody>
683
+ </table>
684
+ {data.length === 0 && !loading && (
685
+ <div className="text-center py-8 text-gray-500">No data available</div>
686
+ )}
687
+ </div>
688
+ ) : (
689
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
690
+ {sortedData.map((item, idx) => (
691
+ <div key={idx} className="bg-white p-4 rounded-lg shadow">
692
+ {columns.map(col => (
693
+ <div key={col} className="mb-2">
694
+ <span className="text-xs text-gray-500 uppercase">{col}</span>
695
+ <div className="font-medium">
696
+ {typeof item[col] === 'object'
697
+ ? JSON.stringify(item[col])
698
+ : String(item[col])
699
+ }
700
+ </div>
701
+ </div>
702
+ ))}
703
+ </div>
704
+ ))}
705
+ </div>
706
+ )}
707
+
708
+ <div className="mt-4 text-sm text-gray-500">
709
+ Showing {data.length} items
710
+ </div>
711
+ </div>
712
+ );
713
+ }`,
714
+ },
715
+ };
716
+ export async function handleGetAppTemplate(args) {
717
+ const templateName = args.template || 'basic';
718
+ if (templateName === 'all') {
719
+ const allTemplates = Object.entries(TEMPLATES)
720
+ .map(([key, template]) => {
721
+ return `## ${template.name} (${key})
722
+
723
+ ${template.description}
724
+
725
+ \`\`\`jsx
726
+ ${template.code}
727
+ \`\`\``;
728
+ })
729
+ .join('\n\n---\n\n');
730
+ const text = `# FlowDot App Templates
731
+
732
+ Below are all available app templates. Each template demonstrates a different pattern for building FlowDot apps.
733
+
734
+ ## EXECUTION ENVIRONMENT
735
+
736
+ Apps run in a sandboxed browser iframe with:
737
+ - React 18 (global - use React.useState, React.useEffect, etc.)
738
+ - Tailwind CSS (full utility classes available)
739
+ - FlowDot color tokens: primary-50 to primary-900, secondary-50 to secondary-900
740
+ - invokeWorkflow(workflowHash, inputs) - to call linked workflows
741
+
742
+ ## CRITICAL CODE RULES
743
+
744
+ 1. **NO IMPORTS** - React is global (use React.useState, React.useEffect, React.useRef, React.useMemo, React.useCallback)
745
+ 2. **NO EXPORTS** - Just define your function, the system handles the rest
746
+ 3. **Function naming** - Must be: function MyAppName() { ... }
747
+ 4. **Styling** - Use Tailwind CSS for ALL styling (no inline style objects, no CSS-in-JS)
748
+
749
+ ## WORKFLOW RESPONSE FORMAT
750
+
751
+ invokeWorkflow returns data in this structure:
752
+ \`\`\`json
753
+ {
754
+ "data": {
755
+ "[nodeId]": {
756
+ "nodeId": "uuid",
757
+ "nodeTitle": "My Output Node",
758
+ "nodeType": "text_output",
759
+ "outputs": {
760
+ "Consolidated Text": { "value": "the actual data", "metadata": {...} }
761
+ }
762
+ }
763
+ }
764
+ }
765
+ \`\`\`
766
+
767
+ **IMPORTANT**: Use this helper function to extract outputs by node title:
768
+ \`\`\`javascript
769
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
770
+ const node = Object.values(result?.data || {}).find(n => n.nodeTitle === nodeTitle);
771
+ return node?.outputs?.[socketName]?.value;
772
+ };
773
+ \`\`\`
774
+
775
+ Example: \`const weatherData = getNodeOutput(result, 'Weather Results', 'Consolidated Text');\`
776
+
777
+ ## DISPLAY MODES
778
+
779
+ Set config.displayMode when creating/updating an app:
780
+ - "windowed": Standard view with FlowDot header (default)
781
+ - "fullscreen": Full viewport, minimal floating control bar
782
+ - "embedded": No FlowDot UI, for iframe embedding
783
+
784
+ ---
785
+
786
+ ${allTemplates}
787
+
788
+ ## Tips
789
+
790
+ 1. Replace 'YOUR_WORKFLOW_HASH' with your actual workflow hash
791
+ 2. Link workflows to your app using link_app_workflow
792
+ 3. Test locally before publishing
793
+ 4. Use mobile_code for mobile-specific layouts
794
+ 5. Use min-h-screen for fullscreen apps`;
795
+ return {
796
+ content: [{ type: 'text', text }],
797
+ };
798
+ }
799
+ const template = TEMPLATES[templateName];
800
+ if (!template) {
801
+ const available = Object.keys(TEMPLATES).join(', ');
802
+ return {
803
+ content: [{ type: 'text', text: `Unknown template: "${templateName}". Available templates: ${available}, all` }],
804
+ };
805
+ }
806
+ const text = `# ${template.name}
807
+
808
+ ${template.description}
809
+
810
+ ## Code
811
+
812
+ \`\`\`jsx
813
+ ${template.code}
814
+ \`\`\`
815
+
816
+ ## Usage
817
+
818
+ 1. Create a new app using create_app with this code
819
+ 2. Replace 'YOUR_WORKFLOW_HASH' with your actual workflow hash
820
+ 3. Replace node titles in getNodeOutput() calls with your actual node names
821
+ 4. Link the workflow using link_app_workflow
822
+ 5. Test and publish when ready
823
+
824
+ ## Workflow Response Format
825
+
826
+ invokeWorkflow returns data in this structure:
827
+ \`\`\`json
828
+ {
829
+ "data": {
830
+ "[nodeId]": {
831
+ "nodeId": "uuid",
832
+ "nodeTitle": "My Output Node",
833
+ "outputs": { "Consolidated Text": { "value": "the data" } }
834
+ }
835
+ }
836
+ }
837
+ \`\`\`
838
+
839
+ Use the getNodeOutput helper (included in templates) to extract by node title.
840
+
841
+ ## Critical Rules
842
+
843
+ - **NO IMPORTS** - React is global (use React.useState, React.useEffect, etc.)
844
+ - **NO EXPORTS** - Just define your function
845
+ - **invokeWorkflow(hash, inputs)** - Call a linked workflow
846
+ - **Tailwind CSS** - Full Tailwind for styling
847
+
848
+ ## Other Templates
849
+
850
+ Available templates: ${Object.keys(TEMPLATES).join(', ')}
851
+ Use \`get_app_template(template: "all")\` to see all templates.`;
852
+ return {
853
+ content: [{ type: 'text', text }],
854
+ };
855
+ }
856
+ //# sourceMappingURL=get-app-template.js.map