@flowdrop/flowdrop 1.0.1 → 1.2.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 (385) hide show
  1. package/README.md +50 -50
  2. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  3. package/dist/adapters/WorkflowAdapter.js +25 -25
  4. package/dist/adapters/agentspec/AgentSpecAdapter.d.ts +2 -2
  5. package/dist/adapters/agentspec/AgentSpecAdapter.js +133 -122
  6. package/dist/adapters/agentspec/agentAdapter.d.ts +2 -2
  7. package/dist/adapters/agentspec/agentAdapter.js +10 -10
  8. package/dist/adapters/agentspec/autoLayout.d.ts +1 -1
  9. package/dist/adapters/agentspec/autoLayout.js +2 -2
  10. package/dist/adapters/agentspec/componentTypeDefaults.d.ts +1 -1
  11. package/dist/adapters/agentspec/componentTypeDefaults.js +120 -120
  12. package/dist/adapters/agentspec/defaultNodeTypes.d.ts +2 -2
  13. package/dist/adapters/agentspec/defaultNodeTypes.js +307 -307
  14. package/dist/adapters/agentspec/index.d.ts +10 -10
  15. package/dist/adapters/agentspec/index.js +6 -6
  16. package/dist/adapters/agentspec/validator.d.ts +2 -2
  17. package/dist/adapters/agentspec/validator.js +22 -20
  18. package/dist/api/enhanced-client.d.ts +3 -3
  19. package/dist/api/enhanced-client.js +73 -72
  20. package/dist/components/App.svelte +1090 -961
  21. package/dist/components/App.svelte.d.ts +9 -6
  22. package/dist/components/CanvasBanner.stories.svelte +23 -20
  23. package/dist/components/CanvasBanner.stories.svelte.d.ts +1 -1
  24. package/dist/components/CanvasBanner.svelte +52 -46
  25. package/dist/components/ConfigForm.svelte +1164 -1065
  26. package/dist/components/ConfigForm.svelte.d.ts +2 -2
  27. package/dist/components/ConfigModal.svelte +180 -180
  28. package/dist/components/ConfigModal.svelte.d.ts +1 -1
  29. package/dist/components/ConfigPanel.stories.svelte +35 -35
  30. package/dist/components/ConfigPanel.stories.svelte.d.ts +1 -1
  31. package/dist/components/ConfigPanel.svelte +178 -167
  32. package/dist/components/ConfigPanel.svelte.d.ts +1 -1
  33. package/dist/components/ConnectionLine.svelte +25 -25
  34. package/dist/components/EdgeRefresher.svelte +26 -26
  35. package/dist/components/FlowDropEdge.stories.svelte +179 -143
  36. package/dist/components/FlowDropEdge.svelte +147 -147
  37. package/dist/components/FlowDropEdge.svelte.d.ts +1 -1
  38. package/dist/components/FlowDropZone.svelte +63 -60
  39. package/dist/components/FlowDropZone.svelte.d.ts +1 -1
  40. package/dist/components/LoadingSpinner.stories.svelte +19 -19
  41. package/dist/components/LoadingSpinner.stories.svelte.d.ts +1 -1
  42. package/dist/components/LoadingSpinner.svelte +21 -21
  43. package/dist/components/LoadingSpinner.svelte.d.ts +1 -1
  44. package/dist/components/Logo.stories.svelte +13 -13
  45. package/dist/components/Logo.stories.svelte.d.ts +1 -1
  46. package/dist/components/Logo.svelte +101 -95
  47. package/dist/components/LogsSidebar.svelte +553 -546
  48. package/dist/components/LogsSidebar.svelte.d.ts +1 -1
  49. package/dist/components/MarkdownDisplay.stories.svelte +29 -23
  50. package/dist/components/MarkdownDisplay.stories.svelte.d.ts +1 -1
  51. package/dist/components/MarkdownDisplay.svelte +16 -14
  52. package/dist/components/Navbar.stories.svelte +43 -38
  53. package/dist/components/Navbar.stories.svelte.d.ts +1 -1
  54. package/dist/components/Navbar.svelte +760 -706
  55. package/dist/components/Navbar.svelte.d.ts +1 -1
  56. package/dist/components/NodeSidebar.svelte +905 -746
  57. package/dist/components/NodeSidebar.svelte.d.ts +5 -1
  58. package/dist/components/NodeStatusOverlay.stories.svelte +82 -70
  59. package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +1 -1
  60. package/dist/components/NodeStatusOverlay.svelte +295 -280
  61. package/dist/components/NodeStatusOverlay.svelte.d.ts +3 -3
  62. package/dist/components/PipelineStatus.svelte +326 -300
  63. package/dist/components/PipelineStatus.svelte.d.ts +4 -4
  64. package/dist/components/PortCoordinateTracker.svelte +49 -47
  65. package/dist/components/PortCoordinateTracker.svelte.d.ts +1 -1
  66. package/dist/components/ReadOnlyDetails.svelte +156 -156
  67. package/dist/components/SchemaForm.stories.svelte +106 -98
  68. package/dist/components/SchemaForm.stories.svelte.d.ts +1 -1
  69. package/dist/components/SchemaForm.svelte +490 -463
  70. package/dist/components/SchemaForm.svelte.d.ts +2 -2
  71. package/dist/components/SettingsModal.svelte +226 -223
  72. package/dist/components/SettingsModal.svelte.d.ts +1 -1
  73. package/dist/components/SettingsPanel.svelte +637 -601
  74. package/dist/components/SettingsPanel.svelte.d.ts +1 -1
  75. package/dist/components/StatusIcon.stories.svelte +62 -49
  76. package/dist/components/StatusIcon.stories.svelte.d.ts +1 -1
  77. package/dist/components/StatusIcon.svelte +87 -87
  78. package/dist/components/StatusIcon.svelte.d.ts +2 -2
  79. package/dist/components/StatusLabel.stories.svelte +12 -12
  80. package/dist/components/StatusLabel.stories.svelte.d.ts +1 -1
  81. package/dist/components/StatusLabel.svelte +19 -19
  82. package/dist/components/ThemeToggle.stories.svelte +16 -16
  83. package/dist/components/ThemeToggle.stories.svelte.d.ts +1 -1
  84. package/dist/components/ThemeToggle.svelte +180 -169
  85. package/dist/components/ThemeToggle.svelte.d.ts +1 -1
  86. package/dist/components/UniversalNode.svelte +150 -138
  87. package/dist/components/UniversalNode.svelte.d.ts +3 -3
  88. package/dist/components/WorkflowEditor.svelte +1069 -1014
  89. package/dist/components/WorkflowEditor.svelte.d.ts +4 -4
  90. package/dist/components/form/FormArray.svelte +1034 -973
  91. package/dist/components/form/FormArray.svelte.d.ts +1 -1
  92. package/dist/components/form/FormAutocomplete.svelte +1021 -978
  93. package/dist/components/form/FormAutocomplete.svelte.d.ts +1 -1
  94. package/dist/components/form/FormCheckboxGroup.stories.svelte +23 -20
  95. package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +1 -1
  96. package/dist/components/form/FormCheckboxGroup.svelte +136 -136
  97. package/dist/components/form/FormCodeEditor.svelte +452 -434
  98. package/dist/components/form/FormField.svelte +366 -355
  99. package/dist/components/form/FormField.svelte.d.ts +2 -2
  100. package/dist/components/form/FormFieldLight.svelte +400 -384
  101. package/dist/components/form/FormFieldLight.svelte.d.ts +1 -1
  102. package/dist/components/form/FormFieldWrapper.stories.svelte +42 -42
  103. package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +1 -1
  104. package/dist/components/form/FormFieldWrapper.svelte +100 -93
  105. package/dist/components/form/FormFieldWrapper.svelte.d.ts +1 -1
  106. package/dist/components/form/FormFieldset.svelte +108 -108
  107. package/dist/components/form/FormFieldset.svelte.d.ts +2 -2
  108. package/dist/components/form/FormMarkdownEditor.svelte +758 -725
  109. package/dist/components/form/FormNumberField.stories.svelte +25 -25
  110. package/dist/components/form/FormNumberField.stories.svelte.d.ts +1 -1
  111. package/dist/components/form/FormNumberField.svelte +88 -88
  112. package/dist/components/form/FormRangeField.stories.svelte +20 -20
  113. package/dist/components/form/FormRangeField.stories.svelte.d.ts +1 -1
  114. package/dist/components/form/FormRangeField.svelte +234 -226
  115. package/dist/components/form/FormSelect.stories.svelte +38 -38
  116. package/dist/components/form/FormSelect.stories.svelte.d.ts +1 -1
  117. package/dist/components/form/FormSelect.svelte +101 -101
  118. package/dist/components/form/FormSelect.svelte.d.ts +1 -1
  119. package/dist/components/form/FormTemplateEditor.svelte +847 -798
  120. package/dist/components/form/FormTemplateEditor.svelte.d.ts +1 -1
  121. package/dist/components/form/FormTextField.stories.svelte +29 -23
  122. package/dist/components/form/FormTextField.stories.svelte.d.ts +1 -1
  123. package/dist/components/form/FormTextField.svelte +68 -68
  124. package/dist/components/form/FormTextarea.stories.svelte +28 -25
  125. package/dist/components/form/FormTextarea.stories.svelte.d.ts +1 -1
  126. package/dist/components/form/FormTextarea.svelte +74 -74
  127. package/dist/components/form/FormToggle.stories.svelte +23 -20
  128. package/dist/components/form/FormToggle.stories.svelte.d.ts +1 -1
  129. package/dist/components/form/FormToggle.svelte +98 -98
  130. package/dist/components/form/FormUISchemaRenderer.svelte +120 -113
  131. package/dist/components/form/FormUISchemaRenderer.svelte.d.ts +3 -3
  132. package/dist/components/form/index.d.ts +19 -19
  133. package/dist/components/form/index.js +18 -18
  134. package/dist/components/form/templateAutocomplete.d.ts +2 -2
  135. package/dist/components/form/templateAutocomplete.js +64 -55
  136. package/dist/components/form/types.d.ts +6 -6
  137. package/dist/components/form/types.js +9 -4
  138. package/dist/components/icons/AlertCircleIcon.svelte +11 -0
  139. package/dist/components/icons/AlertCircleIcon.svelte.d.ts +26 -0
  140. package/dist/components/icons/CogIcon.svelte +11 -0
  141. package/dist/components/icons/CogIcon.svelte.d.ts +26 -0
  142. package/dist/components/interrupt/ChoicePrompt.stories.svelte +54 -38
  143. package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +1 -1
  144. package/dist/components/interrupt/ChoicePrompt.svelte +407 -383
  145. package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +1 -1
  146. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +48 -48
  147. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +1 -1
  148. package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -274
  149. package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +1 -1
  150. package/dist/components/interrupt/FormPrompt.svelte +223 -218
  151. package/dist/components/interrupt/FormPrompt.svelte.d.ts +1 -1
  152. package/dist/components/interrupt/InterruptBubble.svelte +617 -583
  153. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +2 -2
  154. package/dist/components/interrupt/ReviewPrompt.stories.svelte +66 -56
  155. package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +1 -1
  156. package/dist/components/interrupt/ReviewPrompt.svelte +861 -841
  157. package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +1 -1
  158. package/dist/components/interrupt/TextInputPrompt.stories.svelte +38 -33
  159. package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +1 -1
  160. package/dist/components/interrupt/TextInputPrompt.svelte +333 -328
  161. package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +1 -1
  162. package/dist/components/interrupt/index.d.ts +5 -5
  163. package/dist/components/interrupt/index.js +5 -5
  164. package/dist/components/layouts/MainLayout.svelte +724 -691
  165. package/dist/components/layouts/MainLayout.svelte.d.ts +6 -6
  166. package/dist/components/nodes/GatewayNode.stories.svelte +100 -99
  167. package/dist/components/nodes/GatewayNode.svelte +605 -571
  168. package/dist/components/nodes/GatewayNode.svelte.d.ts +3 -3
  169. package/dist/components/nodes/IdeaNode.stories.svelte +44 -43
  170. package/dist/components/nodes/IdeaNode.svelte +451 -437
  171. package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
  172. package/dist/components/nodes/NotesNode.stories.svelte +65 -64
  173. package/dist/components/nodes/NotesNode.svelte +380 -369
  174. package/dist/components/nodes/NotesNode.svelte.d.ts +1 -1
  175. package/dist/components/nodes/SimpleNode.stories.svelte +145 -144
  176. package/dist/components/nodes/SimpleNode.svelte +486 -424
  177. package/dist/components/nodes/SimpleNode.svelte.d.ts +1 -1
  178. package/dist/components/nodes/SquareNode.stories.svelte +73 -73
  179. package/dist/components/nodes/SquareNode.svelte +439 -380
  180. package/dist/components/nodes/SquareNode.svelte.d.ts +1 -1
  181. package/dist/components/nodes/TerminalNode.stories.svelte +13 -13
  182. package/dist/components/nodes/TerminalNode.svelte +709 -670
  183. package/dist/components/nodes/TerminalNode.svelte.d.ts +1 -1
  184. package/dist/components/nodes/ToolNode.stories.svelte +181 -180
  185. package/dist/components/nodes/ToolNode.svelte +505 -447
  186. package/dist/components/nodes/ToolNode.svelte.d.ts +1 -1
  187. package/dist/components/nodes/WorkflowNode.stories.svelte +70 -46
  188. package/dist/components/nodes/WorkflowNode.svelte +621 -551
  189. package/dist/components/nodes/WorkflowNode.svelte.d.ts +3 -3
  190. package/dist/components/playground/ChatPanel.svelte +945 -889
  191. package/dist/components/playground/ExecutionLogs.svelte +495 -472
  192. package/dist/components/playground/InputCollector.svelte +449 -428
  193. package/dist/components/playground/MessageBubble.stories.svelte +47 -47
  194. package/dist/components/playground/MessageBubble.stories.svelte.d.ts +1 -1
  195. package/dist/components/playground/MessageBubble.svelte +626 -610
  196. package/dist/components/playground/MessageBubble.svelte.d.ts +1 -1
  197. package/dist/components/playground/Playground.svelte +1088 -1057
  198. package/dist/components/playground/Playground.svelte.d.ts +3 -3
  199. package/dist/components/playground/PlaygroundModal.svelte +208 -204
  200. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
  201. package/dist/components/playground/SessionManager.svelte +527 -521
  202. package/dist/components/playground/SessionManager.svelte.d.ts +1 -1
  203. package/dist/config/agentSpecEndpoints.d.ts +1 -1
  204. package/dist/config/agentSpecEndpoints.js +20 -20
  205. package/dist/config/constants.js +2 -2
  206. package/dist/config/defaultCategories.d.ts +1 -1
  207. package/dist/config/defaultCategories.js +86 -86
  208. package/dist/config/defaultPortConfig.d.ts +1 -1
  209. package/dist/config/defaultPortConfig.js +144 -144
  210. package/dist/config/endpoints.d.ts +4 -4
  211. package/dist/config/endpoints.js +65 -65
  212. package/dist/config/runtimeConfig.d.ts +2 -2
  213. package/dist/config/runtimeConfig.js +8 -8
  214. package/dist/core/index.d.ts +63 -59
  215. package/dist/core/index.js +35 -33
  216. package/dist/display/index.d.ts +2 -2
  217. package/dist/display/index.js +2 -2
  218. package/dist/editor/index.d.ts +62 -62
  219. package/dist/editor/index.js +53 -53
  220. package/dist/form/code.d.ts +5 -5
  221. package/dist/form/code.js +14 -14
  222. package/dist/form/fieldRegistry.d.ts +3 -3
  223. package/dist/form/fieldRegistry.js +11 -9
  224. package/dist/form/full.d.ts +8 -8
  225. package/dist/form/full.js +9 -9
  226. package/dist/form/index.d.ts +18 -18
  227. package/dist/form/index.js +16 -16
  228. package/dist/form/markdown.d.ts +4 -4
  229. package/dist/form/markdown.js +8 -8
  230. package/dist/helpers/proximityConnect.d.ts +3 -3
  231. package/dist/helpers/proximityConnect.js +34 -32
  232. package/dist/helpers/workflowEditorHelper.d.ts +5 -5
  233. package/dist/helpers/workflowEditorHelper.js +108 -96
  234. package/dist/index.d.ts +6 -6
  235. package/dist/index.js +6 -6
  236. package/dist/mocks/app-environment.js +2 -2
  237. package/dist/mocks/app-forms.js +9 -9
  238. package/dist/mocks/app-navigation.js +11 -11
  239. package/dist/mocks/app-stores.js +8 -8
  240. package/dist/playground/index.d.ts +19 -19
  241. package/dist/playground/index.js +16 -16
  242. package/dist/playground/mount.d.ts +3 -3
  243. package/dist/playground/mount.js +24 -24
  244. package/dist/registry/builtinFormats.js +13 -13
  245. package/dist/registry/builtinNodes.d.ts +2 -2
  246. package/dist/registry/builtinNodes.js +77 -77
  247. package/dist/registry/index.d.ts +4 -4
  248. package/dist/registry/index.js +4 -4
  249. package/dist/registry/nodeComponentRegistry.d.ts +8 -8
  250. package/dist/registry/nodeComponentRegistry.js +11 -9
  251. package/dist/registry/plugin.d.ts +2 -2
  252. package/dist/registry/plugin.js +11 -11
  253. package/dist/registry/workflowFormatRegistry.d.ts +3 -3
  254. package/dist/registry/workflowFormatRegistry.js +2 -2
  255. package/dist/schema/index.d.ts +1 -1
  256. package/dist/schema/index.js +2 -2
  257. package/dist/services/agentSpecExecutionService.d.ts +3 -3
  258. package/dist/services/agentSpecExecutionService.js +59 -55
  259. package/dist/services/api.d.ts +2 -2
  260. package/dist/services/api.js +37 -37
  261. package/dist/services/apiVariableService.d.ts +1 -1
  262. package/dist/services/apiVariableService.js +41 -34
  263. package/dist/services/autoSaveService.js +8 -8
  264. package/dist/services/categoriesApi.d.ts +2 -2
  265. package/dist/services/categoriesApi.js +8 -8
  266. package/dist/services/draftStorage.d.ts +1 -1
  267. package/dist/services/draftStorage.js +11 -11
  268. package/dist/services/dynamicSchemaService.d.ts +1 -1
  269. package/dist/services/dynamicSchemaService.js +41 -39
  270. package/dist/services/globalSave.d.ts +2 -2
  271. package/dist/services/globalSave.js +41 -38
  272. package/dist/services/historyService.d.ts +1 -1
  273. package/dist/services/historyService.js +8 -8
  274. package/dist/services/interruptService.d.ts +1 -1
  275. package/dist/services/interruptService.js +35 -29
  276. package/dist/services/nodeExecutionService.d.ts +1 -1
  277. package/dist/services/nodeExecutionService.js +45 -44
  278. package/dist/services/playgroundService.d.ts +1 -1
  279. package/dist/services/playgroundService.js +29 -29
  280. package/dist/services/portConfigApi.d.ts +2 -2
  281. package/dist/services/portConfigApi.js +8 -8
  282. package/dist/services/settingsService.d.ts +2 -2
  283. package/dist/services/settingsService.js +25 -19
  284. package/dist/services/toastService.d.ts +4 -4
  285. package/dist/services/toastService.js +33 -33
  286. package/dist/services/variableService.d.ts +1 -1
  287. package/dist/services/variableService.js +36 -36
  288. package/dist/services/workflowStorage.d.ts +2 -2
  289. package/dist/services/workflowStorage.js +13 -13
  290. package/dist/settings/index.d.ts +7 -7
  291. package/dist/settings/index.js +6 -6
  292. package/dist/skins/default.d.ts +2 -0
  293. package/dist/skins/default.js +1 -0
  294. package/dist/skins/index.d.ts +13 -0
  295. package/dist/skins/index.js +30 -0
  296. package/dist/skins/slate.d.ts +2 -0
  297. package/dist/skins/slate.js +78 -0
  298. package/dist/stores/categoriesStore.svelte.d.ts +1 -1
  299. package/dist/stores/categoriesStore.svelte.js +5 -5
  300. package/dist/stores/editorStateMachine.svelte.d.ts +2 -2
  301. package/dist/stores/editorStateMachine.svelte.js +65 -33
  302. package/dist/stores/historyStore.svelte.d.ts +4 -4
  303. package/dist/stores/historyStore.svelte.js +4 -4
  304. package/dist/stores/interruptStore.svelte.d.ts +3 -3
  305. package/dist/stores/interruptStore.svelte.js +21 -21
  306. package/dist/stores/playgroundStore.svelte.d.ts +2 -2
  307. package/dist/stores/playgroundStore.svelte.js +25 -18
  308. package/dist/stores/portCoordinateStore.svelte.d.ts +2 -2
  309. package/dist/stores/portCoordinateStore.svelte.js +15 -8
  310. package/dist/stores/settingsStore.svelte.d.ts +2 -2
  311. package/dist/stores/settingsStore.svelte.js +62 -57
  312. package/dist/stores/workflowStore.svelte.d.ts +3 -3
  313. package/dist/stores/workflowStore.svelte.js +50 -47
  314. package/dist/stories/CanvasDecorator.svelte +35 -32
  315. package/dist/stories/CanvasDecorator.svelte.d.ts +2 -2
  316. package/dist/stories/EdgeDecorator.svelte +102 -99
  317. package/dist/stories/EdgeDecorator.svelte.d.ts +1 -1
  318. package/dist/stories/NodeDecorator.svelte +59 -53
  319. package/dist/stories/NodeDecorator.svelte.d.ts +1 -1
  320. package/dist/stories/utils.d.ts +2 -2
  321. package/dist/stories/utils.js +105 -67
  322. package/dist/styles/base.css +599 -595
  323. package/dist/styles/toast.css +14 -14
  324. package/dist/styles/tokens.css +409 -378
  325. package/dist/svelte-app.d.ts +12 -9
  326. package/dist/svelte-app.js +40 -39
  327. package/dist/themes/default.d.ts +2 -0
  328. package/dist/themes/default.js +9 -0
  329. package/dist/themes/index.d.ts +13 -0
  330. package/dist/themes/index.js +44 -0
  331. package/dist/themes/minimal.d.ts +2 -0
  332. package/dist/themes/minimal.js +11 -0
  333. package/dist/types/agentspec.d.ts +18 -18
  334. package/dist/types/agentspec.js +2 -2
  335. package/dist/types/auth.d.ts +1 -1
  336. package/dist/types/auth.js +6 -6
  337. package/dist/types/config.d.ts +6 -6
  338. package/dist/types/events.d.ts +2 -2
  339. package/dist/types/events.js +2 -2
  340. package/dist/types/index.d.ts +32 -32
  341. package/dist/types/index.js +6 -6
  342. package/dist/types/interrupt.d.ts +6 -6
  343. package/dist/types/interrupt.js +21 -21
  344. package/dist/types/interruptState.d.ts +12 -12
  345. package/dist/types/interruptState.js +66 -66
  346. package/dist/types/playground.d.ts +7 -7
  347. package/dist/types/playground.js +14 -14
  348. package/dist/types/settings.d.ts +5 -3
  349. package/dist/types/settings.js +25 -18
  350. package/dist/types/skin.d.ts +31 -0
  351. package/dist/types/skin.js +1 -0
  352. package/dist/types/theme.d.ts +35 -0
  353. package/dist/types/theme.js +1 -0
  354. package/dist/types/uischema.d.ts +4 -4
  355. package/dist/types/uischema.js +3 -3
  356. package/dist/utils/colors.d.ts +1 -1
  357. package/dist/utils/colors.js +97 -95
  358. package/dist/utils/config.d.ts +2 -2
  359. package/dist/utils/config.js +48 -48
  360. package/dist/utils/connections.d.ts +2 -2
  361. package/dist/utils/connections.js +15 -15
  362. package/dist/utils/errors.js +3 -3
  363. package/dist/utils/fetchWithAuth.d.ts +1 -1
  364. package/dist/utils/fetchWithAuth.js +2 -2
  365. package/dist/utils/handleIds.d.ts +2 -2
  366. package/dist/utils/handleIds.js +8 -8
  367. package/dist/utils/handlePositioning.d.ts +1 -1
  368. package/dist/utils/handlePositioning.js +2 -2
  369. package/dist/utils/icons.d.ts +1 -1
  370. package/dist/utils/icons.js +74 -74
  371. package/dist/utils/logger.d.ts +1 -1
  372. package/dist/utils/logger.js +7 -7
  373. package/dist/utils/nodeStatus.d.ts +1 -1
  374. package/dist/utils/nodeStatus.js +48 -48
  375. package/dist/utils/nodeTypes.d.ts +1 -1
  376. package/dist/utils/nodeTypes.js +21 -20
  377. package/dist/utils/nodeWrapper.d.ts +7 -7
  378. package/dist/utils/nodeWrapper.js +21 -19
  379. package/dist/utils/performanceUtils.d.ts +1 -1
  380. package/dist/utils/performanceUtils.js +2 -1
  381. package/dist/utils/sanitize.js +1 -1
  382. package/dist/utils/uischema.d.ts +2 -2
  383. package/dist/utils/uischema.js +8 -8
  384. package/dist/utils/validation.js +20 -8
  385. package/package.json +1 -1
@@ -18,808 +18,857 @@
18
18
  -->
19
19
 
20
20
  <script lang="ts">
21
- import { onMount, onDestroy } from 'svelte';
22
- import {
23
- EditorView,
24
- lineNumbers,
25
- highlightActiveLineGutter,
26
- drawSelection,
27
- highlightSpecialChars,
28
- highlightActiveLine,
29
- keymap,
30
- tooltips,
31
- Decoration,
32
- ViewPlugin,
33
- MatchDecorator
34
- } from '@codemirror/view';
35
- import { EditorState, Compartment } from '@codemirror/state';
36
- import { history, historyKeymap, defaultKeymap, indentWithTab } from '@codemirror/commands';
37
- import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language';
38
- import { oneDark } from '@codemirror/theme-one-dark';
39
- import type {
40
- VariableSchema,
41
- TemplateVariablesConfig,
42
- WorkflowNode,
43
- WorkflowEdge,
44
- AuthProvider
45
- } from '../../types/index.js';
46
- import { createTemplateAutocomplete } from './templateAutocomplete.js';
47
- import { getVariableSchema } from '../../services/variableService.js';
48
- import { logger } from '../../utils/logger.js';
49
-
50
- interface Props {
51
- /** Field identifier */
52
- id: string;
53
- /** Current template value */
54
- value: string;
55
- /** Placeholder text shown when empty */
56
- placeholder?: string;
57
- /** Whether the field is required */
58
- required?: boolean;
59
- /** Whether to use dark theme */
60
- darkTheme?: boolean;
61
- /** Editor height in pixels or CSS value */
62
- height?: string;
63
- /**
64
- * Configuration for template variable autocomplete.
65
- * Controls which variables are available and how they are displayed.
66
- */
67
- variables?: TemplateVariablesConfig;
68
- /** Placeholder variable example for the hint */
69
- placeholderExample?: string;
70
- /** Whether the field is disabled (read-only) */
71
- disabled?: boolean;
72
- /** ARIA description ID */
73
- ariaDescribedBy?: string;
74
- /** Callback when value changes */
75
- onChange: (value: string) => void;
76
- /** Current workflow node (required for API mode) */
77
- node?: WorkflowNode;
78
- /** All workflow nodes (required for port-derived variables) */
79
- nodes?: WorkflowNode[];
80
- /** All workflow edges (required for port-derived variables) */
81
- edges?: WorkflowEdge[];
82
- /** Workflow ID (required for API mode) */
83
- workflowId?: string;
84
- /** Auth provider for API requests */
85
- authProvider?: AuthProvider;
86
- }
87
-
88
- let {
89
- id,
90
- value = '',
91
- placeholder = 'Enter your template here...\nUse {{ variable }} for dynamic values.',
92
- required = false,
93
- darkTheme = false,
94
- height = '250px',
95
- variables,
96
- placeholderExample = 'Hello {{ name }}, your order #{{ order_id }} is ready!',
97
- disabled = false,
98
- ariaDescribedBy,
99
- onChange,
100
- node,
101
- nodes = [],
102
- edges = [],
103
- workflowId,
104
- authProvider
105
- }: Props = $props();
106
-
107
- /** Loading state for API variable fetching */
108
- let isLoadingVariables = $state(false);
109
-
110
- /** Error state for API variable fetching */
111
- let variableLoadError = $state<string | null>(null);
112
-
113
- /** The effective variable schema (loaded synchronously or asynchronously) */
114
- let effectiveVariableSchema = $state<VariableSchema | undefined>(undefined);
115
-
116
- /**
117
- * Load variable schema on mount or when configuration changes.
118
- * Handles both synchronous (schema-based) and asynchronous (API-based) loading.
119
- */
120
- async function loadVariableSchema() {
121
- // Reset error state
122
- variableLoadError = null;
123
-
124
- // If no variables config, clear schema
125
- if (!variables) {
126
- effectiveVariableSchema = undefined;
127
- return;
128
- }
129
-
130
- // If variables config has static schema only (no API), use it directly
131
- if (variables.schema && !variables.api && Object.keys(variables.schema.variables).length > 0) {
132
- effectiveVariableSchema = variables.schema;
133
- return;
134
- }
135
-
136
- // If variables config requires node context (ports or API mode)
137
- if ((variables.ports !== undefined || variables.api) && node && nodes && edges) {
138
- try {
139
- isLoadingVariables = true;
140
- effectiveVariableSchema = await getVariableSchema(
141
- node,
142
- nodes,
143
- edges,
144
- variables,
145
- workflowId,
146
- authProvider
147
- );
148
- } catch (error) {
149
- logger.error('Failed to load variable schema:', error);
150
- variableLoadError = error instanceof Error ? error.message : 'Failed to load variables';
151
- effectiveVariableSchema = undefined;
152
- } finally {
153
- isLoadingVariables = false;
154
- }
155
- } else {
156
- // No schema available
157
- effectiveVariableSchema = undefined;
158
- }
159
- }
160
-
161
- /**
162
- * Retry loading variables after an error
163
- */
164
- function retryLoadVariables() {
165
- loadVariableSchema();
166
- }
167
-
168
- /**
169
- * Whether to show the variable hints section.
170
- * Controlled by variables.showHints (defaults to true).
171
- */
172
- const showHints = $derived(variables?.showHints !== false);
173
-
174
- /**
175
- * Derive the list of top-level variable names for the hints display.
176
- */
177
- const displayVariables = $derived.by(() => {
178
- if (effectiveVariableSchema) {
179
- return Object.keys(effectiveVariableSchema.variables);
180
- }
181
- return [];
182
- });
183
-
184
- /** Reference to the container element */
185
- let containerRef: HTMLDivElement | undefined = $state(undefined);
186
-
187
- /** CodeMirror editor instance */
188
- let editorView: EditorView | undefined = $state(undefined);
189
-
190
- /** Flag to prevent update loops */
191
- let isInternalUpdate = false;
192
-
193
- /** Compartment for dynamic autocomplete reconfiguration */
194
- const autocompleteCompartment = new Compartment();
195
-
196
- /**
197
- * Custom Twig syntax highlighter using MatchDecorator
198
- * Highlights three Twig delimiter types with different styles:
199
- * - {{ expression }} — variables/output (purple)
200
- * - {% block %} — control structures (teal)
201
- * - {# comment #} — comments (gray/italic)
202
- */
203
- const twigMatcher = new MatchDecorator({
204
- regexp: /\{\{.*?\}\}|\{%.*?%\}|\{#.*?#\}/g,
205
- decoration: (match) => {
206
- const text = match[0];
207
- if (text.startsWith('{{')) {
208
- return Decoration.mark({ class: 'cm-twig-expression' });
209
- } else if (text.startsWith('{%')) {
210
- return Decoration.mark({ class: 'cm-twig-block' });
211
- } else {
212
- return Decoration.mark({ class: 'cm-twig-comment' });
213
- }
214
- }
215
- });
216
-
217
- const twigHighlighter = ViewPlugin.fromClass(
218
- class {
219
- decorations;
220
- constructor(view: EditorView) {
221
- this.decorations = twigMatcher.createDeco(view);
222
- }
223
- update(update: import('@codemirror/view').ViewUpdate) {
224
- this.decorations = twigMatcher.updateDeco(update, this.decorations);
225
- }
226
- },
227
- { decorations: (v) => v.decorations }
228
- );
229
-
230
- /**
231
- * Handle editor content changes
232
- */
233
- function handleUpdate(update: { docChanged: boolean; state: EditorState }): void {
234
- if (!update.docChanged || isInternalUpdate) {
235
- return;
236
- }
237
-
238
- const content = update.state.doc.toString();
239
- onChange(content);
240
- }
241
-
242
- /**
243
- * Create editor extensions array for template editing
244
- * Includes autocomplete when variables are available
245
- * When disabled is true, adds readOnly/editable so the editor cannot be modified
246
- */
247
- function createExtensions() {
248
- const extensions = [
249
- // Position tooltips using fixed strategy so they aren't clipped by container overflow
250
- tooltips({ position: 'fixed' }),
251
-
252
- // Essential visual features
253
- lineNumbers(),
254
- highlightActiveLineGutter(),
255
- highlightSpecialChars(),
256
- highlightActiveLine(),
257
- drawSelection(),
258
-
259
- // Editing features (skip when read-only)
260
- ...(disabled
261
- ? []
262
- : [history(), keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab])]),
263
-
264
- // Read-only: prevent document changes and mark content as non-editable
265
- ...(disabled ? [EditorState.readOnly.of(true), EditorView.editable.of(false)] : []),
266
-
267
- // Syntax highlighting - use default for light mode, oneDark handles dark mode
268
- ...(darkTheme ? [] : [syntaxHighlighting(defaultHighlightStyle, { fallback: true })]),
269
-
270
- // Twig syntax highlighting ({{ expressions }}, {% blocks %}, {# comments #})
271
- twigHighlighter,
272
-
273
- // Update listener (only fires on user edit when not disabled)
274
- EditorView.updateListener.of(handleUpdate),
275
-
276
- // Custom theme with autocomplete styling
277
- EditorView.theme({
278
- '&': {
279
- height: height,
280
- fontSize: '0.875rem',
281
- fontFamily: "'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace"
282
- },
283
- '.cm-scroller': {
284
- overflow: 'auto'
285
- },
286
- '.cm-content': {
287
- minHeight: '100px',
288
- padding: '0.5rem 0'
289
- },
290
- '&.cm-focused': {
291
- outline: 'none'
292
- },
293
- '.cm-line': {
294
- padding: '0 0.5rem'
295
- },
296
- // Twig expression: {{ variable }}
297
- '.cm-twig-expression': {
298
- color: '#a855f7',
299
- backgroundColor: 'rgba(168, 85, 247, 0.1)',
300
- borderRadius: '3px',
301
- padding: '1px 2px',
302
- fontWeight: '500'
303
- },
304
- // Twig block: {% for ... %}
305
- '.cm-twig-block': {
306
- color: '#14b8a6',
307
- backgroundColor: 'rgba(20, 184, 166, 0.1)',
308
- borderRadius: '3px',
309
- padding: '1px 2px',
310
- fontWeight: '500'
311
- },
312
- // Twig comment: {# ... #}
313
- '.cm-twig-comment': {
314
- color: '#6b7280',
315
- fontStyle: 'italic'
316
- },
317
- // Autocomplete dropdown styling
318
- '.cm-tooltip.cm-tooltip-autocomplete': {
319
- backgroundColor: 'var(--fd-background, #ffffff)',
320
- border: '1px solid var(--fd-border, #e5e7eb)',
321
- borderRadius: 'var(--fd-radius-lg, 0.5rem)',
322
- boxShadow: 'var(--fd-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.1))',
323
- padding: '0.25rem',
324
- maxHeight: '200px',
325
- overflow: 'auto'
326
- },
327
- '.cm-tooltip.cm-tooltip-autocomplete > ul': {
328
- fontFamily: "'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace",
329
- fontSize: '0.8125rem'
330
- },
331
- '.cm-tooltip.cm-tooltip-autocomplete > ul > li': {
332
- padding: '0.375rem 0.625rem',
333
- borderRadius: 'var(--fd-radius-md, 0.375rem)',
334
- display: 'flex',
335
- alignItems: 'center',
336
- gap: '0.5rem'
337
- },
338
- '.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]': {
339
- backgroundColor: 'var(--fd-accent-muted, rgba(168, 85, 247, 0.1))',
340
- color: 'var(--fd-accent-hover, #7c3aed)'
341
- },
342
- '.cm-completionLabel': {
343
- flex: '1'
344
- },
345
- '.cm-completionDetail': {
346
- fontSize: '0.6875rem',
347
- color: 'var(--fd-muted-foreground, #6b7280)',
348
- opacity: '0.8'
349
- }
350
- }),
351
- EditorView.lineWrapping,
352
- EditorState.tabSize.of(2)
353
- ];
354
-
355
- // Add autocomplete compartment (can be reconfigured dynamically)
356
- // When disabled or no schema, use empty array
357
- if (!disabled && effectiveVariableSchema) {
358
- extensions.push(
359
- autocompleteCompartment.of(createTemplateAutocomplete(effectiveVariableSchema))
360
- );
361
- } else {
362
- extensions.push(autocompleteCompartment.of([]));
363
- }
364
-
365
- if (darkTheme) {
366
- extensions.push(oneDark);
367
- // Add dark theme overrides for Twig highlighting and autocomplete
368
- extensions.push(
369
- EditorView.theme({
370
- '.cm-twig-expression': {
371
- color: '#c084fc',
372
- backgroundColor: 'rgba(192, 132, 252, 0.15)'
373
- },
374
- '.cm-twig-block': {
375
- color: '#5eead4',
376
- backgroundColor: 'rgba(94, 234, 212, 0.1)'
377
- },
378
- '.cm-twig-comment': {
379
- color: '#6b7280'
380
- },
381
- '.cm-tooltip.cm-tooltip-autocomplete': {
382
- backgroundColor: '#1e1e1e',
383
- border: '1px solid #3e4451',
384
- boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.3)'
385
- },
386
- '.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]': {
387
- backgroundColor: 'rgba(192, 132, 252, 0.2)',
388
- color: '#c084fc'
389
- }
390
- })
391
- );
392
- }
393
-
394
- return extensions;
395
- }
396
-
397
- /**
398
- * Insert a variable placeholder at current cursor position
399
- */
400
- function insertVariable(varName: string): void {
401
- if (!editorView) {
402
- return;
403
- }
404
-
405
- const insertText = `{{ ${varName} }}`;
406
- const { from, to } = editorView.state.selection.main;
407
-
408
- editorView.dispatch({
409
- changes: { from, to, insert: insertText },
410
- selection: { anchor: from + insertText.length }
411
- });
412
-
413
- editorView.focus();
414
- }
415
-
416
- /**
417
- * Initialize CodeMirror editor and load variables on mount
418
- */
419
- onMount(() => {
420
- if (!containerRef) {
421
- return;
422
- }
423
-
424
- // Load variables first
425
- loadVariableSchema().then(() => {
426
- // Then create editor with loaded schema
427
- editorView = new EditorView({
428
- state: EditorState.create({
429
- doc: value,
430
- extensions: createExtensions()
431
- }),
432
- parent: containerRef
433
- });
434
- });
435
- });
436
-
437
- /**
438
- * Clean up editor on destroy
439
- */
440
- onDestroy(() => {
441
- if (editorView) {
442
- editorView.destroy();
443
- }
444
- });
445
-
446
- /**
447
- * Update editor content when value prop changes externally
448
- */
449
- $effect(() => {
450
- if (!editorView) {
451
- return;
452
- }
453
-
454
- const currentContent = editorView.state.doc.toString();
455
-
456
- // Only update if content actually changed and wasn't from internal edit
457
- if (value !== currentContent && !isInternalUpdate) {
458
- isInternalUpdate = true;
459
- editorView.dispatch({
460
- changes: {
461
- from: 0,
462
- to: editorView.state.doc.length,
463
- insert: value
464
- }
465
- });
466
- isInternalUpdate = false;
467
- }
468
- });
469
-
470
- /**
471
- * Reconfigure editor when variable schema changes (e.g., after async loading)
472
- */
473
- $effect(() => {
474
- // Only track effectiveVariableSchema changes
475
- const schema = effectiveVariableSchema;
476
-
477
- if (!editorView) {
478
- return;
479
- }
480
-
481
- // When effectiveVariableSchema changes, reconfigure the autocomplete compartment
482
- // This happens after async API loading completes
483
- const newAutocomplete = !disabled && schema ? createTemplateAutocomplete(schema) : [];
484
-
485
- editorView.dispatch({
486
- effects: [autocompleteCompartment.reconfigure(newAutocomplete)]
487
- });
488
- });
21
+ import { onMount, onDestroy } from "svelte";
22
+ import {
23
+ EditorView,
24
+ lineNumbers,
25
+ highlightActiveLineGutter,
26
+ drawSelection,
27
+ highlightSpecialChars,
28
+ highlightActiveLine,
29
+ keymap,
30
+ tooltips,
31
+ Decoration,
32
+ ViewPlugin,
33
+ MatchDecorator,
34
+ } from "@codemirror/view";
35
+ import { EditorState, Compartment } from "@codemirror/state";
36
+ import {
37
+ history,
38
+ historyKeymap,
39
+ defaultKeymap,
40
+ indentWithTab,
41
+ } from "@codemirror/commands";
42
+ import {
43
+ syntaxHighlighting,
44
+ defaultHighlightStyle,
45
+ } from "@codemirror/language";
46
+ import { oneDark } from "@codemirror/theme-one-dark";
47
+ import type {
48
+ VariableSchema,
49
+ TemplateVariablesConfig,
50
+ WorkflowNode,
51
+ WorkflowEdge,
52
+ AuthProvider,
53
+ } from "../../types/index.js";
54
+ import { createTemplateAutocomplete } from "./templateAutocomplete.js";
55
+ import { getVariableSchema } from "../../services/variableService.js";
56
+ import { logger } from "../../utils/logger.js";
57
+
58
+ interface Props {
59
+ /** Field identifier */
60
+ id: string;
61
+ /** Current template value */
62
+ value: string;
63
+ /** Placeholder text shown when empty */
64
+ placeholder?: string;
65
+ /** Whether the field is required */
66
+ required?: boolean;
67
+ /** Whether to use dark theme */
68
+ darkTheme?: boolean;
69
+ /** Editor height in pixels or CSS value */
70
+ height?: string;
71
+ /**
72
+ * Configuration for template variable autocomplete.
73
+ * Controls which variables are available and how they are displayed.
74
+ */
75
+ variables?: TemplateVariablesConfig;
76
+ /** Placeholder variable example for the hint */
77
+ placeholderExample?: string;
78
+ /** Whether the field is disabled (read-only) */
79
+ disabled?: boolean;
80
+ /** ARIA description ID */
81
+ ariaDescribedBy?: string;
82
+ /** Callback when value changes */
83
+ onChange: (value: string) => void;
84
+ /** Current workflow node (required for API mode) */
85
+ node?: WorkflowNode;
86
+ /** All workflow nodes (required for port-derived variables) */
87
+ nodes?: WorkflowNode[];
88
+ /** All workflow edges (required for port-derived variables) */
89
+ edges?: WorkflowEdge[];
90
+ /** Workflow ID (required for API mode) */
91
+ workflowId?: string;
92
+ /** Auth provider for API requests */
93
+ authProvider?: AuthProvider;
94
+ }
95
+
96
+ let {
97
+ id,
98
+ value = "",
99
+ placeholder = "Enter your template here...\nUse {{ variable }} for dynamic values.",
100
+ required = false,
101
+ darkTheme = false,
102
+ height = "250px",
103
+ variables,
104
+ placeholderExample = "Hello {{ name }}, your order #{{ order_id }} is ready!",
105
+ disabled = false,
106
+ ariaDescribedBy,
107
+ onChange,
108
+ node,
109
+ nodes = [],
110
+ edges = [],
111
+ workflowId,
112
+ authProvider,
113
+ }: Props = $props();
114
+
115
+ /** Loading state for API variable fetching */
116
+ let isLoadingVariables = $state(false);
117
+
118
+ /** Error state for API variable fetching */
119
+ let variableLoadError = $state<string | null>(null);
120
+
121
+ /** The effective variable schema (loaded synchronously or asynchronously) */
122
+ let effectiveVariableSchema = $state<VariableSchema | undefined>(undefined);
123
+
124
+ /**
125
+ * Load variable schema on mount or when configuration changes.
126
+ * Handles both synchronous (schema-based) and asynchronous (API-based) loading.
127
+ */
128
+ async function loadVariableSchema() {
129
+ // Reset error state
130
+ variableLoadError = null;
131
+
132
+ // If no variables config, clear schema
133
+ if (!variables) {
134
+ effectiveVariableSchema = undefined;
135
+ return;
136
+ }
137
+
138
+ // If variables config has static schema only (no API), use it directly
139
+ if (
140
+ variables.schema &&
141
+ !variables.api &&
142
+ Object.keys(variables.schema.variables).length > 0
143
+ ) {
144
+ effectiveVariableSchema = variables.schema;
145
+ return;
146
+ }
147
+
148
+ // If variables config requires node context (ports or API mode)
149
+ if (
150
+ (variables.ports !== undefined || variables.api) &&
151
+ node &&
152
+ nodes &&
153
+ edges
154
+ ) {
155
+ try {
156
+ isLoadingVariables = true;
157
+ effectiveVariableSchema = await getVariableSchema(
158
+ node,
159
+ nodes,
160
+ edges,
161
+ variables,
162
+ workflowId,
163
+ authProvider,
164
+ );
165
+ } catch (error) {
166
+ logger.error("Failed to load variable schema:", error);
167
+ variableLoadError =
168
+ error instanceof Error ? error.message : "Failed to load variables";
169
+ effectiveVariableSchema = undefined;
170
+ } finally {
171
+ isLoadingVariables = false;
172
+ }
173
+ } else {
174
+ // No schema available
175
+ effectiveVariableSchema = undefined;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Retry loading variables after an error
181
+ */
182
+ function retryLoadVariables() {
183
+ loadVariableSchema();
184
+ }
185
+
186
+ /**
187
+ * Whether to show the variable hints section.
188
+ * Controlled by variables.showHints (defaults to true).
189
+ */
190
+ const showHints = $derived(variables?.showHints !== false);
191
+
192
+ /**
193
+ * Derive the list of top-level variable names for the hints display.
194
+ */
195
+ const displayVariables = $derived.by(() => {
196
+ if (effectiveVariableSchema) {
197
+ return Object.keys(effectiveVariableSchema.variables);
198
+ }
199
+ return [];
200
+ });
201
+
202
+ /** Reference to the container element */
203
+ let containerRef: HTMLDivElement | undefined = $state(undefined);
204
+
205
+ /** CodeMirror editor instance */
206
+ let editorView: EditorView | undefined = $state(undefined);
207
+
208
+ /** Flag to prevent update loops */
209
+ let isInternalUpdate = false;
210
+
211
+ /** Compartment for dynamic autocomplete reconfiguration */
212
+ const autocompleteCompartment = new Compartment();
213
+
214
+ /**
215
+ * Custom Twig syntax highlighter using MatchDecorator
216
+ * Highlights three Twig delimiter types with different styles:
217
+ * - {{ expression }} — variables/output (purple)
218
+ * - {% block %} — control structures (teal)
219
+ * - {# comment #} — comments (gray/italic)
220
+ */
221
+ const twigMatcher = new MatchDecorator({
222
+ regexp: /\{\{.*?\}\}|\{%.*?%\}|\{#.*?#\}/g,
223
+ decoration: (match) => {
224
+ const text = match[0];
225
+ if (text.startsWith("{{")) {
226
+ return Decoration.mark({ class: "cm-twig-expression" });
227
+ } else if (text.startsWith("{%")) {
228
+ return Decoration.mark({ class: "cm-twig-block" });
229
+ } else {
230
+ return Decoration.mark({ class: "cm-twig-comment" });
231
+ }
232
+ },
233
+ });
234
+
235
+ const twigHighlighter = ViewPlugin.fromClass(
236
+ class {
237
+ decorations;
238
+ constructor(view: EditorView) {
239
+ this.decorations = twigMatcher.createDeco(view);
240
+ }
241
+ update(update: import("@codemirror/view").ViewUpdate) {
242
+ this.decorations = twigMatcher.updateDeco(update, this.decorations);
243
+ }
244
+ },
245
+ { decorations: (v) => v.decorations },
246
+ );
247
+
248
+ /**
249
+ * Handle editor content changes
250
+ */
251
+ function handleUpdate(update: {
252
+ docChanged: boolean;
253
+ state: EditorState;
254
+ }): void {
255
+ if (!update.docChanged || isInternalUpdate) {
256
+ return;
257
+ }
258
+
259
+ const content = update.state.doc.toString();
260
+ onChange(content);
261
+ }
262
+
263
+ /**
264
+ * Create editor extensions array for template editing
265
+ * Includes autocomplete when variables are available
266
+ * When disabled is true, adds readOnly/editable so the editor cannot be modified
267
+ */
268
+ function createExtensions() {
269
+ const extensions = [
270
+ // Position tooltips using fixed strategy so they aren't clipped by container overflow
271
+ tooltips({ position: "fixed" }),
272
+
273
+ // Essential visual features
274
+ lineNumbers(),
275
+ highlightActiveLineGutter(),
276
+ highlightSpecialChars(),
277
+ highlightActiveLine(),
278
+ drawSelection(),
279
+
280
+ // Editing features (skip when read-only)
281
+ ...(disabled
282
+ ? []
283
+ : [
284
+ history(),
285
+ keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab]),
286
+ ]),
287
+
288
+ // Read-only: prevent document changes and mark content as non-editable
289
+ ...(disabled
290
+ ? [EditorState.readOnly.of(true), EditorView.editable.of(false)]
291
+ : []),
292
+
293
+ // Syntax highlighting - use default for light mode, oneDark handles dark mode
294
+ ...(darkTheme
295
+ ? []
296
+ : [syntaxHighlighting(defaultHighlightStyle, { fallback: true })]),
297
+
298
+ // Twig syntax highlighting ({{ expressions }}, {% blocks %}, {# comments #})
299
+ twigHighlighter,
300
+
301
+ // Update listener (only fires on user edit when not disabled)
302
+ EditorView.updateListener.of(handleUpdate),
303
+
304
+ // Custom theme with autocomplete styling
305
+ EditorView.theme({
306
+ "&": {
307
+ height: height,
308
+ fontSize: "0.875rem",
309
+ fontFamily:
310
+ "'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace",
311
+ },
312
+ ".cm-scroller": {
313
+ overflow: "auto",
314
+ },
315
+ ".cm-content": {
316
+ minHeight: "100px",
317
+ padding: "0.5rem 0",
318
+ },
319
+ "&.cm-focused": {
320
+ outline: "none",
321
+ },
322
+ ".cm-line": {
323
+ padding: "0 0.5rem",
324
+ },
325
+ // Twig expression: {{ variable }}
326
+ ".cm-twig-expression": {
327
+ color: "#a855f7",
328
+ backgroundColor: "rgba(168, 85, 247, 0.1)",
329
+ borderRadius: "3px",
330
+ padding: "1px 2px",
331
+ fontWeight: "500",
332
+ },
333
+ // Twig block: {% for ... %}
334
+ ".cm-twig-block": {
335
+ color: "#14b8a6",
336
+ backgroundColor: "rgba(20, 184, 166, 0.1)",
337
+ borderRadius: "3px",
338
+ padding: "1px 2px",
339
+ fontWeight: "500",
340
+ },
341
+ // Twig comment: {# ... #}
342
+ ".cm-twig-comment": {
343
+ color: "#6b7280",
344
+ fontStyle: "italic",
345
+ },
346
+ // Autocomplete dropdown styling
347
+ ".cm-tooltip.cm-tooltip-autocomplete": {
348
+ backgroundColor: "var(--fd-background, #ffffff)",
349
+ border: "1px solid var(--fd-border, #e5e7eb)",
350
+ borderRadius: "var(--fd-radius-lg, 0.5rem)",
351
+ boxShadow: "var(--fd-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.1))",
352
+ padding: "0.25rem",
353
+ maxHeight: "200px",
354
+ overflow: "auto",
355
+ },
356
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
357
+ fontFamily:
358
+ "'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace",
359
+ fontSize: "0.8125rem",
360
+ },
361
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {
362
+ padding: "0.375rem 0.625rem",
363
+ borderRadius: "var(--fd-radius-md, 0.375rem)",
364
+ display: "flex",
365
+ alignItems: "center",
366
+ gap: "0.5rem",
367
+ },
368
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
369
+ backgroundColor: "var(--fd-accent-muted, rgba(168, 85, 247, 0.1))",
370
+ color: "var(--fd-accent-hover, #7c3aed)",
371
+ },
372
+ ".cm-completionLabel": {
373
+ flex: "1",
374
+ },
375
+ ".cm-completionDetail": {
376
+ fontSize: "0.6875rem",
377
+ color: "var(--fd-muted-foreground, #6b7280)",
378
+ opacity: "0.8",
379
+ },
380
+ }),
381
+ EditorView.lineWrapping,
382
+ EditorState.tabSize.of(2),
383
+ ];
384
+
385
+ // Add autocomplete compartment (can be reconfigured dynamically)
386
+ // When disabled or no schema, use empty array
387
+ if (!disabled && effectiveVariableSchema) {
388
+ extensions.push(
389
+ autocompleteCompartment.of(
390
+ createTemplateAutocomplete(effectiveVariableSchema),
391
+ ),
392
+ );
393
+ } else {
394
+ extensions.push(autocompleteCompartment.of([]));
395
+ }
396
+
397
+ if (darkTheme) {
398
+ extensions.push(oneDark);
399
+ // Add dark theme overrides for Twig highlighting and autocomplete
400
+ extensions.push(
401
+ EditorView.theme({
402
+ ".cm-twig-expression": {
403
+ color: "#c084fc",
404
+ backgroundColor: "rgba(192, 132, 252, 0.15)",
405
+ },
406
+ ".cm-twig-block": {
407
+ color: "#5eead4",
408
+ backgroundColor: "rgba(94, 234, 212, 0.1)",
409
+ },
410
+ ".cm-twig-comment": {
411
+ color: "#6b7280",
412
+ },
413
+ ".cm-tooltip.cm-tooltip-autocomplete": {
414
+ backgroundColor: "#1e1e1e",
415
+ border: "1px solid #3e4451",
416
+ boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.3)",
417
+ },
418
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
419
+ backgroundColor: "rgba(192, 132, 252, 0.2)",
420
+ color: "#c084fc",
421
+ },
422
+ }),
423
+ );
424
+ }
425
+
426
+ return extensions;
427
+ }
428
+
429
+ /**
430
+ * Insert a variable placeholder at current cursor position
431
+ */
432
+ function insertVariable(varName: string): void {
433
+ if (!editorView) {
434
+ return;
435
+ }
436
+
437
+ const insertText = `{{ ${varName} }}`;
438
+ const { from, to } = editorView.state.selection.main;
439
+
440
+ editorView.dispatch({
441
+ changes: { from, to, insert: insertText },
442
+ selection: { anchor: from + insertText.length },
443
+ });
444
+
445
+ editorView.focus();
446
+ }
447
+
448
+ /**
449
+ * Initialize CodeMirror editor and load variables on mount
450
+ */
451
+ onMount(() => {
452
+ if (!containerRef) {
453
+ return;
454
+ }
455
+
456
+ // Load variables first
457
+ loadVariableSchema().then(() => {
458
+ // Then create editor with loaded schema
459
+ editorView = new EditorView({
460
+ state: EditorState.create({
461
+ doc: value,
462
+ extensions: createExtensions(),
463
+ }),
464
+ parent: containerRef,
465
+ });
466
+ });
467
+ });
468
+
469
+ /**
470
+ * Clean up editor on destroy
471
+ */
472
+ onDestroy(() => {
473
+ if (editorView) {
474
+ editorView.destroy();
475
+ }
476
+ });
477
+
478
+ /**
479
+ * Update editor content when value prop changes externally
480
+ */
481
+ $effect(() => {
482
+ if (!editorView) {
483
+ return;
484
+ }
485
+
486
+ const currentContent = editorView.state.doc.toString();
487
+
488
+ // Only update if content actually changed and wasn't from internal edit
489
+ if (value !== currentContent && !isInternalUpdate) {
490
+ isInternalUpdate = true;
491
+ editorView.dispatch({
492
+ changes: {
493
+ from: 0,
494
+ to: editorView.state.doc.length,
495
+ insert: value,
496
+ },
497
+ });
498
+ isInternalUpdate = false;
499
+ }
500
+ });
501
+
502
+ /**
503
+ * Reconfigure editor when variable schema changes (e.g., after async loading)
504
+ */
505
+ $effect(() => {
506
+ // Only track effectiveVariableSchema changes
507
+ const schema = effectiveVariableSchema;
508
+
509
+ if (!editorView) {
510
+ return;
511
+ }
512
+
513
+ // When effectiveVariableSchema changes, reconfigure the autocomplete compartment
514
+ // This happens after async API loading completes
515
+ const newAutocomplete =
516
+ !disabled && schema ? createTemplateAutocomplete(schema) : [];
517
+
518
+ editorView.dispatch({
519
+ effects: [autocompleteCompartment.reconfigure(newAutocomplete)],
520
+ });
521
+ });
489
522
  </script>
490
523
 
491
524
  <div class="form-template-editor">
492
- <!-- Hidden input for form submission compatibility -->
493
- <input
494
- type="hidden"
495
- {id}
496
- name={id}
497
- {value}
498
- aria-describedby={ariaDescribedBy}
499
- aria-required={required}
500
- />
501
-
502
- <!-- CodeMirror container -->
503
- <div
504
- bind:this={containerRef}
505
- class="form-template-editor__container"
506
- class:form-template-editor__container--dark={darkTheme}
507
- role="textbox"
508
- aria-multiline="true"
509
- aria-label="Template editor"
510
- ></div>
511
-
512
- <!-- Loading banner (shown while fetching variables from API) -->
513
- {#if isLoadingVariables}
514
- <div class="form-template-editor__banner form-template-editor__banner--loading">
515
- <svg
516
- class="form-template-editor__banner-icon form-template-editor__banner-icon--spin"
517
- xmlns="http://www.w3.org/2000/svg"
518
- fill="none"
519
- viewBox="0 0 24 24"
520
- >
521
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
522
- ></circle>
523
- <path
524
- class="opacity-75"
525
- fill="currentColor"
526
- d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
527
- ></path>
528
- </svg>
529
- <span>Loading variables...</span>
530
- </div>
531
- {/if}
532
-
533
- <!-- Error banner (shown when API fetch fails) -->
534
- {#if variableLoadError}
535
- <div class="form-template-editor__banner form-template-editor__banner--error">
536
- <svg
537
- class="form-template-editor__banner-icon"
538
- xmlns="http://www.w3.org/2000/svg"
539
- viewBox="0 0 20 20"
540
- fill="currentColor"
541
- >
542
- <path
543
- fill-rule="evenodd"
544
- d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z"
545
- clip-rule="evenodd"
546
- />
547
- </svg>
548
- <span>{variableLoadError}</span>
549
- <button
550
- type="button"
551
- class="form-template-editor__banner-btn"
552
- onclick={retryLoadVariables}
553
- title="Retry loading variables"
554
- >
555
- Retry
556
- </button>
557
- </div>
558
- {/if}
559
-
560
- <!-- Variable hints section (shown when variables are available and showHints is true) -->
561
- {#if showHints && displayVariables.length > 0}
562
- <div class="form-template-editor__hints">
563
- <span class="form-template-editor__hints-label">Available variables:</span>
564
- <div class="form-template-editor__hints-list">
565
- {#each displayVariables as varName (varName)}
566
- <button
567
- type="button"
568
- class="form-template-editor__hint-btn"
569
- onclick={() => insertVariable(varName)}
570
- title={`Insert {{ ${varName} }}`}
571
- >
572
- <code>{'{{ '}{varName}{' }}'}</code>
573
- </button>
574
- {/each}
575
- </div>
576
- </div>
577
- {/if}
578
-
579
- <!-- Placeholder hint when empty -->
580
- {#if !value && placeholderExample}
581
- <div class="form-template-editor__placeholder">
582
- <span class="form-template-editor__placeholder-label">Example template:</span>
583
- <code class="form-template-editor__placeholder-example">{placeholderExample}</code>
584
- </div>
585
- {/if}
586
-
587
- <!-- Syntax help -->
588
- <div class="form-template-editor__help">
589
- <svg
590
- xmlns="http://www.w3.org/2000/svg"
591
- viewBox="0 0 20 20"
592
- fill="currentColor"
593
- class="form-template-editor__help-icon"
594
- >
595
- <path
596
- fill-rule="evenodd"
597
- d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z"
598
- clip-rule="evenodd"
599
- />
600
- </svg>
601
- <span
602
- >Use <code>{'{{ variable }}'}</code> syntax to insert dynamic values from the data input</span
603
- >
604
- </div>
525
+ <!-- Hidden input for form submission compatibility -->
526
+ <input
527
+ type="hidden"
528
+ {id}
529
+ name={id}
530
+ {value}
531
+ aria-describedby={ariaDescribedBy}
532
+ aria-required={required}
533
+ />
534
+
535
+ <!-- CodeMirror container -->
536
+ <div
537
+ bind:this={containerRef}
538
+ class="form-template-editor__container"
539
+ class:form-template-editor__container--dark={darkTheme}
540
+ role="textbox"
541
+ aria-multiline="true"
542
+ aria-label="Template editor"
543
+ ></div>
544
+
545
+ <!-- Loading banner (shown while fetching variables from API) -->
546
+ {#if isLoadingVariables}
547
+ <div
548
+ class="form-template-editor__banner form-template-editor__banner--loading"
549
+ >
550
+ <svg
551
+ class="form-template-editor__banner-icon form-template-editor__banner-icon--spin"
552
+ xmlns="http://www.w3.org/2000/svg"
553
+ fill="none"
554
+ viewBox="0 0 24 24"
555
+ >
556
+ <circle
557
+ class="opacity-25"
558
+ cx="12"
559
+ cy="12"
560
+ r="10"
561
+ stroke="currentColor"
562
+ stroke-width="4"
563
+ ></circle>
564
+ <path
565
+ class="opacity-75"
566
+ fill="currentColor"
567
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
568
+ ></path>
569
+ </svg>
570
+ <span>Loading variables...</span>
571
+ </div>
572
+ {/if}
573
+
574
+ <!-- Error banner (shown when API fetch fails) -->
575
+ {#if variableLoadError}
576
+ <div
577
+ class="form-template-editor__banner form-template-editor__banner--error"
578
+ >
579
+ <svg
580
+ class="form-template-editor__banner-icon"
581
+ xmlns="http://www.w3.org/2000/svg"
582
+ viewBox="0 0 20 20"
583
+ fill="currentColor"
584
+ >
585
+ <path
586
+ fill-rule="evenodd"
587
+ d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z"
588
+ clip-rule="evenodd"
589
+ />
590
+ </svg>
591
+ <span>{variableLoadError}</span>
592
+ <button
593
+ type="button"
594
+ class="form-template-editor__banner-btn"
595
+ onclick={retryLoadVariables}
596
+ title="Retry loading variables"
597
+ >
598
+ Retry
599
+ </button>
600
+ </div>
601
+ {/if}
602
+
603
+ <!-- Variable hints section (shown when variables are available and showHints is true) -->
604
+ {#if showHints && displayVariables.length > 0}
605
+ <div class="form-template-editor__hints">
606
+ <span class="form-template-editor__hints-label">Available variables:</span
607
+ >
608
+ <div class="form-template-editor__hints-list">
609
+ {#each displayVariables as varName (varName)}
610
+ <button
611
+ type="button"
612
+ class="form-template-editor__hint-btn"
613
+ onclick={() => insertVariable(varName)}
614
+ title={`Insert {{ ${varName} }}`}
615
+ >
616
+ <code>{"{{ "}{varName}{" }}"}</code>
617
+ </button>
618
+ {/each}
619
+ </div>
620
+ </div>
621
+ {/if}
622
+
623
+ <!-- Placeholder hint when empty -->
624
+ {#if !value && placeholderExample}
625
+ <div class="form-template-editor__placeholder">
626
+ <span class="form-template-editor__placeholder-label"
627
+ >Example template:</span
628
+ >
629
+ <code class="form-template-editor__placeholder-example"
630
+ >{placeholderExample}</code
631
+ >
632
+ </div>
633
+ {/if}
634
+
635
+ <!-- Syntax help -->
636
+ <div class="form-template-editor__help">
637
+ <svg
638
+ xmlns="http://www.w3.org/2000/svg"
639
+ viewBox="0 0 20 20"
640
+ fill="currentColor"
641
+ class="form-template-editor__help-icon"
642
+ >
643
+ <path
644
+ fill-rule="evenodd"
645
+ d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z"
646
+ clip-rule="evenodd"
647
+ />
648
+ </svg>
649
+ <span
650
+ >Use <code>{"{{ variable }}"}</code> syntax to insert dynamic values from the
651
+ data input</span
652
+ >
653
+ </div>
605
654
  </div>
606
655
 
607
656
  <style>
608
- .form-template-editor {
609
- position: relative;
610
- width: 100%;
611
- }
612
-
613
- .form-template-editor__container {
614
- border: 1px solid var(--fd-border);
615
- border-radius: var(--fd-radius-lg);
616
- overflow: hidden;
617
- background-color: var(--fd-muted);
618
- transition: all var(--fd-transition-normal);
619
- box-shadow: var(--fd-shadow-sm);
620
- }
621
-
622
- .form-template-editor__container:hover {
623
- border-color: var(--fd-border-strong);
624
- background-color: var(--fd-background);
625
- }
626
-
627
- .form-template-editor__container:focus-within {
628
- border-color: var(--fd-accent);
629
- background-color: var(--fd-background);
630
- box-shadow:
631
- 0 0 0 3px rgba(168, 85, 247, 0.12),
632
- var(--fd-shadow-sm);
633
- }
634
-
635
- /* Dark theme overrides */
636
- .form-template-editor__container--dark {
637
- background-color: #282c34;
638
- }
639
-
640
- .form-template-editor__container--dark:hover,
641
- .form-template-editor__container--dark:focus-within {
642
- background-color: #282c34;
643
- }
644
-
645
- /* CodeMirror styling overrides */
646
- .form-template-editor__container :global(.cm-editor) {
647
- border-radius: var(--fd-radius-lg);
648
- }
649
-
650
- .form-template-editor__container :global(.cm-gutters) {
651
- background-color: var(--fd-subtle);
652
- border-right: 1px solid var(--fd-border);
653
- border-radius: var(--fd-radius-lg) 0 0 var(--fd-radius-lg);
654
- }
655
-
656
- .form-template-editor__container--dark :global(.cm-gutters) {
657
- background-color: #21252b;
658
- border-right-color: #3e4451;
659
- }
660
-
661
- /* Variable hints section */
662
- .form-template-editor__hints {
663
- margin-top: 0.625rem;
664
- padding: 0.625rem;
665
- background-color: var(--fd-accent-muted);
666
- border: 1px solid var(--fd-accent);
667
- border-radius: var(--fd-radius-md);
668
- }
669
-
670
- .form-template-editor__hints-label {
671
- display: block;
672
- font-size: 0.6875rem;
673
- font-weight: 500;
674
- color: var(--fd-accent-hover);
675
- text-transform: uppercase;
676
- letter-spacing: 0.05em;
677
- margin-bottom: 0.375rem;
678
- }
679
-
680
- .form-template-editor__hints-list {
681
- display: flex;
682
- flex-wrap: wrap;
683
- gap: 0.375rem;
684
- }
685
-
686
- .form-template-editor__hint-btn {
687
- padding: 0.25rem 0.5rem;
688
- background-color: var(--fd-accent-muted);
689
- border: 1px solid var(--fd-accent);
690
- border-radius: var(--fd-radius-sm);
691
- cursor: pointer;
692
- transition: all var(--fd-transition-fast);
693
- }
694
-
695
- .form-template-editor__hint-btn:hover {
696
- background-color: var(--fd-accent);
697
- border-color: var(--fd-accent-hover);
698
- }
699
-
700
- .form-template-editor__hint-btn:active {
701
- transform: scale(0.98);
702
- }
703
-
704
- .form-template-editor__hint-btn code {
705
- font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
706
- font-size: 0.6875rem;
707
- color: var(--fd-accent-hover);
708
- }
709
-
710
- /* Placeholder hint */
711
- .form-template-editor__placeholder {
712
- margin-top: 0.5rem;
713
- padding: 0.5rem 0.75rem;
714
- background-color: var(--fd-muted);
715
- border: 1px dashed var(--fd-border-strong);
716
- border-radius: var(--fd-radius-md);
717
- }
718
-
719
- .form-template-editor__placeholder-label {
720
- display: block;
721
- font-size: 0.6875rem;
722
- font-weight: 500;
723
- color: var(--fd-muted-foreground);
724
- text-transform: uppercase;
725
- letter-spacing: 0.05em;
726
- margin-bottom: 0.25rem;
727
- }
728
-
729
- .form-template-editor__placeholder-example {
730
- display: block;
731
- font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
732
- font-size: var(--fd-text-xs);
733
- color: var(--fd-foreground);
734
- word-break: break-all;
735
- }
736
-
737
- /* Help text */
738
- .form-template-editor__help {
739
- display: flex;
740
- align-items: flex-start;
741
- gap: 0.375rem;
742
- margin-top: 0.5rem;
743
- font-size: 0.6875rem;
744
- color: var(--fd-muted-foreground);
745
- }
746
-
747
- .form-template-editor__help-icon {
748
- width: 0.875rem;
749
- height: 0.875rem;
750
- flex-shrink: 0;
751
- margin-top: 0.0625rem;
752
- }
753
-
754
- .form-template-editor__help code {
755
- padding: 0.0625rem 0.25rem;
756
- background-color: var(--fd-subtle);
757
- border-radius: var(--fd-radius-sm);
758
- font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', monospace;
759
- font-size: 0.625rem;
760
- }
761
-
762
- /* Loading and error banners */
763
- .form-template-editor__banner {
764
- display: flex;
765
- align-items: center;
766
- gap: 0.5rem;
767
- margin-top: 0.625rem;
768
- padding: 0.625rem 0.75rem;
769
- border-radius: var(--fd-radius-md);
770
- font-size: 0.75rem;
771
- }
772
-
773
- .form-template-editor__banner--loading {
774
- background-color: rgba(59, 130, 246, 0.1);
775
- border: 1px solid rgba(59, 130, 246, 0.3);
776
- color: rgb(29, 78, 216);
777
- }
778
-
779
- .form-template-editor__banner--error {
780
- background-color: rgba(239, 68, 68, 0.1);
781
- border: 1px solid rgba(239, 68, 68, 0.3);
782
- color: rgb(185, 28, 28);
783
- }
784
-
785
- .form-template-editor__banner-icon {
786
- width: 1rem;
787
- height: 1rem;
788
- flex-shrink: 0;
789
- }
790
-
791
- .form-template-editor__banner-icon--spin {
792
- animation: spin 1s linear infinite;
793
- }
794
-
795
- @keyframes spin {
796
- from {
797
- transform: rotate(0deg);
798
- }
799
- to {
800
- transform: rotate(360deg);
801
- }
802
- }
803
-
804
- .form-template-editor__banner-btn {
805
- margin-left: auto;
806
- padding: 0.25rem 0.625rem;
807
- background-color: rgba(239, 68, 68, 0.15);
808
- border: 1px solid rgba(239, 68, 68, 0.3);
809
- border-radius: var(--fd-radius-sm);
810
- font-size: 0.6875rem;
811
- font-weight: 500;
812
- color: rgb(185, 28, 28);
813
- cursor: pointer;
814
- transition: all var(--fd-transition-fast);
815
- }
816
-
817
- .form-template-editor__banner-btn:hover {
818
- background-color: rgba(239, 68, 68, 0.25);
819
- border-color: rgba(239, 68, 68, 0.5);
820
- }
821
-
822
- .form-template-editor__banner-btn:active {
823
- transform: scale(0.98);
824
- }
657
+ .form-template-editor {
658
+ position: relative;
659
+ width: 100%;
660
+ }
661
+
662
+ .form-template-editor__container {
663
+ border: 1px solid var(--fd-border);
664
+ border-radius: var(--fd-radius-lg);
665
+ overflow: hidden;
666
+ background-color: var(--fd-muted);
667
+ transition: all var(--fd-transition-normal);
668
+ box-shadow: var(--fd-shadow-sm);
669
+ }
670
+
671
+ .form-template-editor__container:hover {
672
+ border-color: var(--fd-border-strong);
673
+ background-color: var(--fd-background);
674
+ }
675
+
676
+ .form-template-editor__container:focus-within {
677
+ border-color: var(--fd-accent);
678
+ background-color: var(--fd-background);
679
+ box-shadow:
680
+ 0 0 0 3px rgba(168, 85, 247, 0.12),
681
+ var(--fd-shadow-sm);
682
+ }
683
+
684
+ /* Dark theme overrides */
685
+ .form-template-editor__container--dark {
686
+ background-color: #282c34;
687
+ }
688
+
689
+ .form-template-editor__container--dark:hover,
690
+ .form-template-editor__container--dark:focus-within {
691
+ background-color: #282c34;
692
+ }
693
+
694
+ /* CodeMirror styling overrides */
695
+ .form-template-editor__container :global(.cm-editor) {
696
+ border-radius: var(--fd-radius-lg);
697
+ }
698
+
699
+ .form-template-editor__container :global(.cm-gutters) {
700
+ background-color: var(--fd-subtle);
701
+ border-right: 1px solid var(--fd-border);
702
+ border-radius: var(--fd-radius-lg) 0 0 var(--fd-radius-lg);
703
+ }
704
+
705
+ .form-template-editor__container--dark :global(.cm-gutters) {
706
+ background-color: #21252b;
707
+ border-right-color: #3e4451;
708
+ }
709
+
710
+ /* Variable hints section */
711
+ .form-template-editor__hints {
712
+ margin-top: 0.625rem;
713
+ padding: 0.625rem;
714
+ background-color: var(--fd-accent-muted);
715
+ border: 1px solid var(--fd-accent);
716
+ border-radius: var(--fd-radius-md);
717
+ }
718
+
719
+ .form-template-editor__hints-label {
720
+ display: block;
721
+ font-size: 0.6875rem;
722
+ font-weight: 500;
723
+ color: var(--fd-accent-hover);
724
+ text-transform: uppercase;
725
+ letter-spacing: 0.05em;
726
+ margin-bottom: 0.375rem;
727
+ }
728
+
729
+ .form-template-editor__hints-list {
730
+ display: flex;
731
+ flex-wrap: wrap;
732
+ gap: 0.375rem;
733
+ }
734
+
735
+ .form-template-editor__hint-btn {
736
+ padding: 0.25rem 0.5rem;
737
+ background-color: var(--fd-accent-muted);
738
+ border: 1px solid var(--fd-accent);
739
+ border-radius: var(--fd-radius-sm);
740
+ cursor: pointer;
741
+ transition: all var(--fd-transition-fast);
742
+ }
743
+
744
+ .form-template-editor__hint-btn:hover {
745
+ background-color: var(--fd-accent);
746
+ border-color: var(--fd-accent-hover);
747
+ }
748
+
749
+ .form-template-editor__hint-btn:active {
750
+ transform: scale(0.98);
751
+ }
752
+
753
+ .form-template-editor__hint-btn code {
754
+ font-family: "JetBrains Mono", "Fira Code", "Monaco", "Menlo", monospace;
755
+ font-size: 0.6875rem;
756
+ color: var(--fd-accent-hover);
757
+ }
758
+
759
+ /* Placeholder hint */
760
+ .form-template-editor__placeholder {
761
+ margin-top: 0.5rem;
762
+ padding: 0.5rem 0.75rem;
763
+ background-color: var(--fd-muted);
764
+ border: 1px dashed var(--fd-border-strong);
765
+ border-radius: var(--fd-radius-md);
766
+ }
767
+
768
+ .form-template-editor__placeholder-label {
769
+ display: block;
770
+ font-size: 0.6875rem;
771
+ font-weight: 500;
772
+ color: var(--fd-muted-foreground);
773
+ text-transform: uppercase;
774
+ letter-spacing: 0.05em;
775
+ margin-bottom: 0.25rem;
776
+ }
777
+
778
+ .form-template-editor__placeholder-example {
779
+ display: block;
780
+ font-family: "JetBrains Mono", "Fira Code", "Monaco", "Menlo", monospace;
781
+ font-size: var(--fd-text-xs);
782
+ color: var(--fd-foreground);
783
+ word-break: break-all;
784
+ }
785
+
786
+ /* Help text */
787
+ .form-template-editor__help {
788
+ display: flex;
789
+ align-items: flex-start;
790
+ gap: 0.375rem;
791
+ margin-top: 0.5rem;
792
+ font-size: 0.6875rem;
793
+ color: var(--fd-muted-foreground);
794
+ }
795
+
796
+ .form-template-editor__help-icon {
797
+ width: 0.875rem;
798
+ height: 0.875rem;
799
+ flex-shrink: 0;
800
+ margin-top: 0.0625rem;
801
+ }
802
+
803
+ .form-template-editor__help code {
804
+ padding: 0.0625rem 0.25rem;
805
+ background-color: var(--fd-subtle);
806
+ border-radius: var(--fd-radius-sm);
807
+ font-family: "JetBrains Mono", "Fira Code", "Monaco", "Menlo", monospace;
808
+ font-size: 0.625rem;
809
+ }
810
+
811
+ /* Loading and error banners */
812
+ .form-template-editor__banner {
813
+ display: flex;
814
+ align-items: center;
815
+ gap: 0.5rem;
816
+ margin-top: 0.625rem;
817
+ padding: 0.625rem 0.75rem;
818
+ border-radius: var(--fd-radius-md);
819
+ font-size: 0.75rem;
820
+ }
821
+
822
+ .form-template-editor__banner--loading {
823
+ background-color: rgba(59, 130, 246, 0.1);
824
+ border: 1px solid rgba(59, 130, 246, 0.3);
825
+ color: rgb(29, 78, 216);
826
+ }
827
+
828
+ .form-template-editor__banner--error {
829
+ background-color: rgba(239, 68, 68, 0.1);
830
+ border: 1px solid rgba(239, 68, 68, 0.3);
831
+ color: rgb(185, 28, 28);
832
+ }
833
+
834
+ .form-template-editor__banner-icon {
835
+ width: 1rem;
836
+ height: 1rem;
837
+ flex-shrink: 0;
838
+ }
839
+
840
+ .form-template-editor__banner-icon--spin {
841
+ animation: spin 1s linear infinite;
842
+ }
843
+
844
+ @keyframes spin {
845
+ from {
846
+ transform: rotate(0deg);
847
+ }
848
+ to {
849
+ transform: rotate(360deg);
850
+ }
851
+ }
852
+
853
+ .form-template-editor__banner-btn {
854
+ margin-left: auto;
855
+ padding: 0.25rem 0.625rem;
856
+ background-color: rgba(239, 68, 68, 0.15);
857
+ border: 1px solid rgba(239, 68, 68, 0.3);
858
+ border-radius: var(--fd-radius-sm);
859
+ font-size: 0.6875rem;
860
+ font-weight: 500;
861
+ color: rgb(185, 28, 28);
862
+ cursor: pointer;
863
+ transition: all var(--fd-transition-fast);
864
+ }
865
+
866
+ .form-template-editor__banner-btn:hover {
867
+ background-color: rgba(239, 68, 68, 0.25);
868
+ border-color: rgba(239, 68, 68, 0.5);
869
+ }
870
+
871
+ .form-template-editor__banner-btn:active {
872
+ transform: scale(0.98);
873
+ }
825
874
  </style>