ultravisor 1.0.3 → 1.0.8

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 (248) hide show
  1. package/.claude/launch.json +11 -0
  2. package/.claude/ultravisor-dev-config.json +3 -0
  3. package/card-audit.json +1545 -0
  4. package/docs/_sidebar.md +17 -0
  5. package/docs/css/docuserve.css +73 -0
  6. package/docs/features/beacon-authentication.md +209 -0
  7. package/docs/features/beacon-providers.md +1299 -0
  8. package/docs/features/beacons.md +530 -0
  9. package/docs/features/capabilities.md +193 -0
  10. package/docs/features/llm-model-setup.md +505 -0
  11. package/docs/features/llm.md +262 -0
  12. package/docs/features/tasks-content-system.md +110 -0
  13. package/docs/features/tasks-data-transform.md +233 -0
  14. package/docs/features/tasks-extension.md +36 -0
  15. package/docs/features/tasks-file-system.md +203 -0
  16. package/docs/features/tasks-flow-control.md +125 -0
  17. package/docs/features/tasks-http-client.md +119 -0
  18. package/docs/features/tasks-llm.md +118 -0
  19. package/docs/features/tasks-meadow-api.md +163 -0
  20. package/docs/features/tasks-user-interaction.md +49 -0
  21. package/docs/features/tasks.md +14 -0
  22. package/docs/index.html +1 -1
  23. package/docs/retold-catalog.json +1 -1
  24. package/docs/retold-keyword-index.json +17374 -5
  25. package/operation-library/api-data-pipeline.json +143 -0
  26. package/operation-library/beacon-remote-command.json +72 -0
  27. package/operation-library/conditional-file-backup.json +143 -0
  28. package/operation-library/config-processor.json +136 -0
  29. package/operation-library/csv-data-analysis.json +228 -0
  30. package/operation-library/csv-to-json.json +160 -0
  31. package/operation-library/directory-inventory.json +128 -0
  32. package/operation-library/expression-calculator.json +114 -0
  33. package/operation-library/file-search-replace.json +175 -0
  34. package/operation-library/git-status-report.json +368 -0
  35. package/operation-library/health-check-ping.json +329 -0
  36. package/operation-library/json-config-merge.json +313 -0
  37. package/operation-library/llm-embedding-tool-use.json +314 -0
  38. package/operation-library/llm-multi-turn-analysis.json +322 -0
  39. package/operation-library/llm-summarize.json +264 -0
  40. package/operation-library/log-line-counter.json +185 -0
  41. package/operation-library/meadow-crud-lifecycle.json +264 -0
  42. package/operation-library/npm-project-validator.json +193 -0
  43. package/operation-library/rest-api-orchestrator.json +218 -0
  44. package/operation-library/shell-system-info.json +137 -0
  45. package/operation-library/simple-echo.json +95 -0
  46. package/operation-library/template-processor.json +299 -0
  47. package/operation-library/text-sanitizer.json +176 -0
  48. package/package.json +12 -8
  49. package/source/Ultravisor.cjs +30 -3
  50. package/source/beacon/Ultravisor-Beacon-CLI.cjs +143 -0
  51. package/source/beacon/Ultravisor-Beacon-CapabilityProvider.cjs +129 -0
  52. package/source/beacon/Ultravisor-Beacon-Client.cjs +568 -0
  53. package/source/beacon/Ultravisor-Beacon-Executor.cjs +500 -0
  54. package/source/beacon/Ultravisor-Beacon-ProviderRegistry.cjs +330 -0
  55. package/source/beacon/providers/Ultravisor-Beacon-Provider-FileSystem.cjs +331 -0
  56. package/source/beacon/providers/Ultravisor-Beacon-Provider-LLM.cjs +966 -0
  57. package/source/beacon/providers/Ultravisor-Beacon-Provider-Shell.cjs +95 -0
  58. package/source/cli/Ultravisor-CLIProgram.cjs +44 -22
  59. package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +29 -18
  60. package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +62 -19
  61. package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +27 -15
  62. package/source/config/Ultravisor-Default-Command-Configuration.cjs +12 -3
  63. package/source/services/Ultravisor-Beacon-Coordinator.cjs +966 -0
  64. package/source/services/Ultravisor-ExecutionEngine.cjs +1093 -0
  65. package/source/services/Ultravisor-ExecutionManifest.cjs +629 -0
  66. package/source/services/Ultravisor-Hypervisor-State.cjs +270 -97
  67. package/source/services/Ultravisor-Hypervisor.cjs +185 -75
  68. package/source/services/Ultravisor-Schedule-Persistence-Base.cjs +45 -0
  69. package/source/services/Ultravisor-StateManager.cjs +253 -0
  70. package/source/services/Ultravisor-TaskTypeRegistry.cjs +213 -0
  71. package/source/services/persistence/Ultravisor-Schedule-Persistence-JSONFile.cjs +140 -0
  72. package/source/services/tasks/Ultravisor-BuiltIn-TaskConfigs.cjs +19 -0
  73. package/source/services/tasks/Ultravisor-TaskHelper-BeaconDispatch.cjs +107 -0
  74. package/source/services/tasks/Ultravisor-TaskType-Base.cjs +125 -0
  75. package/source/services/tasks/content-system/Ultravisor-TaskConfigs-ContentSystem.cjs +90 -0
  76. package/source/services/tasks/content-system/definitions/content-create-folder.json +24 -0
  77. package/source/services/tasks/content-system/definitions/content-list-files.json +27 -0
  78. package/source/services/tasks/content-system/definitions/content-read-file.json +26 -0
  79. package/source/services/tasks/content-system/definitions/content-save-file.json +26 -0
  80. package/source/services/tasks/data-transform/Ultravisor-TaskConfigs-DataTransform.cjs +577 -0
  81. package/source/services/tasks/data-transform/Ultravisor-TaskType-ReplaceString.cjs +80 -0
  82. package/source/services/tasks/data-transform/Ultravisor-TaskType-SetValues.cjs +74 -0
  83. package/source/services/tasks/data-transform/Ultravisor-TaskType-StringAppender.cjs +90 -0
  84. package/source/services/tasks/data-transform/definitions/comprehension-intersect.json +24 -0
  85. package/source/services/tasks/data-transform/definitions/csv-transform.json +24 -0
  86. package/source/services/tasks/data-transform/definitions/expression-solver.json +20 -0
  87. package/source/services/tasks/data-transform/definitions/histogram.json +23 -0
  88. package/source/services/tasks/data-transform/definitions/parse-csv.json +27 -0
  89. package/source/services/tasks/data-transform/definitions/replace-string.json +27 -0
  90. package/source/services/tasks/data-transform/definitions/set-values.json +17 -0
  91. package/source/services/tasks/data-transform/definitions/string-appender.json +22 -0
  92. package/source/services/tasks/data-transform/definitions/template-string.json +20 -0
  93. package/source/services/tasks/extension/Ultravisor-TaskConfigs-Extension.cjs +87 -0
  94. package/source/services/tasks/extension/definitions/beacon-dispatch.json +32 -0
  95. package/source/services/tasks/file-system/Ultravisor-TaskConfigs-FileSystem.cjs +509 -0
  96. package/source/services/tasks/file-system/Ultravisor-TaskType-ReadFile.cjs +96 -0
  97. package/source/services/tasks/file-system/Ultravisor-TaskType-ReadFileBuffered.cjs +123 -0
  98. package/source/services/tasks/file-system/Ultravisor-TaskType-WriteFile.cjs +106 -0
  99. package/source/services/tasks/file-system/definitions/copy-file.json +27 -0
  100. package/source/services/tasks/file-system/definitions/list-files.json +27 -0
  101. package/source/services/tasks/file-system/definitions/read-file-buffered.json +31 -0
  102. package/source/services/tasks/file-system/definitions/read-file.json +26 -0
  103. package/source/services/tasks/file-system/definitions/read-json.json +23 -0
  104. package/source/services/tasks/file-system/definitions/write-file.json +29 -0
  105. package/source/services/tasks/file-system/definitions/write-json.json +30 -0
  106. package/source/services/tasks/flow-control/Ultravisor-TaskConfigs-FlowControl.cjs +353 -0
  107. package/source/services/tasks/flow-control/Ultravisor-TaskType-IfConditional.cjs +125 -0
  108. package/source/services/tasks/flow-control/Ultravisor-TaskType-LaunchOperation.cjs +186 -0
  109. package/source/services/tasks/flow-control/Ultravisor-TaskType-SplitExecute.cjs +164 -0
  110. package/source/services/tasks/flow-control/definitions/if-conditional.json +25 -0
  111. package/source/services/tasks/flow-control/definitions/launch-operation.json +27 -0
  112. package/source/services/tasks/flow-control/definitions/split-execute.json +32 -0
  113. package/source/services/tasks/http-client/Ultravisor-TaskConfigs-HttpClient.cjs +281 -0
  114. package/source/services/tasks/http-client/definitions/get-json.json +26 -0
  115. package/source/services/tasks/http-client/definitions/get-text.json +26 -0
  116. package/source/services/tasks/http-client/definitions/rest-request.json +32 -0
  117. package/source/services/tasks/http-client/definitions/send-json.json +28 -0
  118. package/source/services/tasks/llm/Ultravisor-TaskConfigs-LLM.cjs +605 -0
  119. package/source/services/tasks/llm/definitions/llm-chat-completion.json +46 -0
  120. package/source/services/tasks/llm/definitions/llm-embedding.json +31 -0
  121. package/source/services/tasks/llm/definitions/llm-tool-use.json +42 -0
  122. package/source/services/tasks/meadow-api/Ultravisor-TaskConfigs-MeadowApi.cjs +341 -0
  123. package/source/services/tasks/meadow-api/definitions/meadow-count.json +26 -0
  124. package/source/services/tasks/meadow-api/definitions/meadow-create.json +26 -0
  125. package/source/services/tasks/meadow-api/definitions/meadow-delete.json +23 -0
  126. package/source/services/tasks/meadow-api/definitions/meadow-read.json +26 -0
  127. package/source/services/tasks/meadow-api/definitions/meadow-reads.json +29 -0
  128. package/source/services/tasks/meadow-api/definitions/meadow-update.json +26 -0
  129. package/source/services/tasks/shell/Ultravisor-TaskConfigs-Shell.cjs +63 -0
  130. package/source/services/tasks/shell/definitions/command.json +29 -0
  131. package/source/services/tasks/user-interaction/Ultravisor-TaskConfigs-UserInteraction.cjs +64 -0
  132. package/source/services/tasks/user-interaction/Ultravisor-TaskType-ErrorMessage.cjs +55 -0
  133. package/source/services/tasks/user-interaction/Ultravisor-TaskType-ValueInput.cjs +47 -0
  134. package/source/services/tasks/user-interaction/definitions/error-message.json +18 -0
  135. package/source/services/tasks/user-interaction/definitions/value-input.json +23 -0
  136. package/source/web_server/Ultravisor-API-Server.cjs +849 -124
  137. package/test/Ultravisor_browser_tests.js +2226 -0
  138. package/test/Ultravisor_operation_library_tests.js +894 -0
  139. package/test/Ultravisor_tests.js +4946 -5044
  140. package/test/workflows/test-content-concatenate-all.json +339 -0
  141. package/test/workflows/test-content-full-pipeline.json +159 -0
  142. package/test/workflows/test-content-list-files.json +76 -0
  143. package/test/workflows/test-content-save-and-read.json +106 -0
  144. package/webinterface/css/ultravisor-themes.css +668 -0
  145. package/webinterface/css/ultravisor.css +37 -14
  146. package/webinterface/docs/card-help/beacon-dispatch.md +30 -0
  147. package/webinterface/docs/card-help/command.md +27 -0
  148. package/webinterface/docs/card-help/comprehension-intersect.md +24 -0
  149. package/webinterface/docs/card-help/content-create-folder.md +22 -0
  150. package/webinterface/docs/card-help/content-list-files.md +25 -0
  151. package/webinterface/docs/card-help/content-read-file.md +24 -0
  152. package/webinterface/docs/card-help/content-save-file.md +24 -0
  153. package/webinterface/docs/card-help/copy-file.md +25 -0
  154. package/webinterface/docs/card-help/csv-transform.md +24 -0
  155. package/webinterface/docs/card-help/end.md +11 -0
  156. package/webinterface/docs/card-help/error-message.md +16 -0
  157. package/webinterface/docs/card-help/expression-solver.md +21 -0
  158. package/webinterface/docs/card-help/get-json.md +24 -0
  159. package/webinterface/docs/card-help/get-text.md +24 -0
  160. package/webinterface/docs/card-help/histogram.md +23 -0
  161. package/webinterface/docs/card-help/if-conditional.md +23 -0
  162. package/webinterface/docs/card-help/launch-operation.md +25 -0
  163. package/webinterface/docs/card-help/list-files.md +25 -0
  164. package/webinterface/docs/card-help/llm-chat-completion.md +43 -0
  165. package/webinterface/docs/card-help/llm-embedding.md +24 -0
  166. package/webinterface/docs/card-help/llm-tool-use.md +39 -0
  167. package/webinterface/docs/card-help/meadow-count.md +24 -0
  168. package/webinterface/docs/card-help/meadow-create.md +24 -0
  169. package/webinterface/docs/card-help/meadow-delete.md +19 -0
  170. package/webinterface/docs/card-help/meadow-read.md +24 -0
  171. package/webinterface/docs/card-help/meadow-reads.md +27 -0
  172. package/webinterface/docs/card-help/meadow-update.md +24 -0
  173. package/webinterface/docs/card-help/parse-csv.md +27 -0
  174. package/webinterface/docs/card-help/read-file-buffered.md +29 -0
  175. package/webinterface/docs/card-help/read-file.md +24 -0
  176. package/webinterface/docs/card-help/read-json.md +21 -0
  177. package/webinterface/docs/card-help/replace-string.md +25 -0
  178. package/webinterface/docs/card-help/rest-request.md +30 -0
  179. package/webinterface/docs/card-help/send-json.md +26 -0
  180. package/webinterface/docs/card-help/set-values.md +16 -0
  181. package/webinterface/docs/card-help/split-execute.md +35 -0
  182. package/webinterface/docs/card-help/start.md +11 -0
  183. package/webinterface/docs/card-help/string-appender.md +22 -0
  184. package/webinterface/docs/card-help/template-string.md +21 -0
  185. package/webinterface/docs/card-help/value-input.md +24 -0
  186. package/webinterface/docs/card-help/write-file.md +27 -0
  187. package/webinterface/docs/card-help/write-json.md +28 -0
  188. package/webinterface/html/index.html +5 -0
  189. package/webinterface/package.json +13 -4
  190. package/webinterface/source/Pict-Application-Ultravisor.js +564 -70
  191. package/webinterface/source/Ultravisor-TimingUtils.js +99 -0
  192. package/webinterface/source/card-help-content.js +46 -0
  193. package/webinterface/source/cards/Ultravisor-BuiltIn-CardConfigs.js +174 -0
  194. package/webinterface/source/cards/Ultravisor-CardConfigGenerator.js +431 -0
  195. package/webinterface/source/data/ExampleFlow-CSVPipeline.js +231 -0
  196. package/webinterface/source/data/ExampleFlow-FileProcessor.js +315 -0
  197. package/webinterface/source/data/ExampleFlow-MeadowPipeline.js +328 -0
  198. package/webinterface/source/panels/Ultravisor-CardSettingsPanel.js +902 -0
  199. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +20 -8
  200. package/webinterface/source/views/PictView-Ultravisor-BeaconList.js +817 -0
  201. package/webinterface/source/views/PictView-Ultravisor-BottomBar.js +5 -5
  202. package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +25 -25
  203. package/webinterface/source/views/PictView-Ultravisor-Documentation.js +856 -0
  204. package/webinterface/source/views/PictView-Ultravisor-FlowEditor.js +777 -0
  205. package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +321 -58
  206. package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +36 -91
  207. package/webinterface/source/views/PictView-Ultravisor-OperationList.js +388 -16
  208. package/webinterface/source/views/PictView-Ultravisor-PendingInput.js +314 -0
  209. package/webinterface/source/views/PictView-Ultravisor-Schedule.js +521 -65
  210. package/webinterface/source/views/PictView-Ultravisor-TimingView.js +333 -71
  211. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +257 -21
  212. package/webinterface/theme-sampler.html +645 -0
  213. package/debug/Harness.js +0 -6
  214. package/source/services/Ultravisor-Operation-Manifest.cjs +0 -160
  215. package/source/services/Ultravisor-Operation.cjs +0 -200
  216. package/source/services/Ultravisor-Task.cjs +0 -349
  217. package/source/services/events/Ultravisor-Hypervisor-Event-Solver.cjs +0 -11
  218. package/source/services/tasks/Ultravisor-Task-Base.cjs +0 -264
  219. package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +0 -188
  220. package/source/services/tasks/Ultravisor-Task-Command.cjs +0 -65
  221. package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +0 -190
  222. package/source/services/tasks/Ultravisor-Task-Conditional.cjs +0 -104
  223. package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +0 -72
  224. package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +0 -336
  225. package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +0 -143
  226. package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +0 -146
  227. package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +0 -158
  228. package/source/services/tasks/Ultravisor-Task-Request.cjs +0 -56
  229. package/source/services/tasks/Ultravisor-Task-Solver.cjs +0 -89
  230. package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +0 -93
  231. package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +0 -127
  232. package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +0 -119
  233. package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +0 -109
  234. package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +0 -112
  235. package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +0 -499
  236. package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +0 -150
  237. package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +0 -110
  238. package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +0 -89
  239. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +0 -87
  240. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +0 -67
  241. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +0 -66
  242. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +0 -69
  243. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +0 -95
  244. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +0 -96
  245. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +0 -99
  246. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +0 -102
  247. package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +0 -220
  248. package/webinterface/source/views/PictView-Ultravisor-TaskList.js +0 -248
@@ -0,0 +1,777 @@
1
+ const libPictView = require('pict-view');
2
+ const libPictSectionFlow = require('pict-section-flow');
3
+ const libPictFlowCard = require('pict-section-flow').PictFlowCard;
4
+
5
+ // Card config generator — converts task definitions into PictFlowCard configs
6
+ const libBuiltInCardConfigs = require('../cards/Ultravisor-BuiltIn-CardConfigs.js');
7
+
8
+ const _ViewConfiguration =
9
+ {
10
+ ViewIdentifier: "Ultravisor-FlowEditor",
11
+
12
+ DefaultRenderable: "Ultravisor-FlowEditor-Content",
13
+ DefaultDestinationAddress: "#Ultravisor-Content-Container",
14
+
15
+ AutoRender: false,
16
+
17
+ CSS: /*css*/`
18
+ .ultravisor-floweditor {
19
+ padding: 0.75em;
20
+ display: flex;
21
+ flex-direction: column;
22
+ flex: 1;
23
+ height: calc(100vh - 56px - 39px - 0.75em);
24
+ min-height: 400px;
25
+ box-sizing: border-box;
26
+ }
27
+ .ultravisor-floweditor-header {
28
+ flex-shrink: 0;
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: space-between;
32
+ margin-bottom: 0.75em;
33
+ padding-bottom: 0.75em;
34
+ border-bottom: 1px solid var(--uv-border-subtle);
35
+ }
36
+ .ultravisor-floweditor-header h1 {
37
+ margin: 0;
38
+ font-size: 1.8em;
39
+ font-weight: 300;
40
+ color: var(--uv-text);
41
+ }
42
+ .ultravisor-flow-actions {
43
+ display: flex;
44
+ gap: 0.5em;
45
+ align-items: center;
46
+ flex-wrap: wrap;
47
+ }
48
+ .ultravisor-flow-meta {
49
+ flex-shrink: 0;
50
+ display: flex;
51
+ gap: 0.75em;
52
+ align-items: center;
53
+ margin-bottom: 0.5em;
54
+ padding-bottom: 0.5em;
55
+ border-bottom: 1px solid var(--uv-bg-base);
56
+ }
57
+ .ultravisor-flow-meta label {
58
+ font-size: 0.8em;
59
+ font-weight: 600;
60
+ color: var(--uv-text-secondary);
61
+ text-transform: uppercase;
62
+ margin-right: 0.25em;
63
+ }
64
+ .ultravisor-flow-meta input {
65
+ flex: 1;
66
+ min-width: 120px;
67
+ }
68
+ .ultravisor-flow-meta textarea {
69
+ flex: 2;
70
+ min-width: 200px;
71
+ resize: none;
72
+ overflow-y: hidden;
73
+ font-family: inherit;
74
+ font-size: inherit;
75
+ line-height: 1.4;
76
+ }
77
+ .ultravisor-flow-meta-hash {
78
+ font-size: 0.8em;
79
+ color: var(--uv-text-tertiary);
80
+ font-family: monospace;
81
+ }
82
+ #Ultravisor-FlowEditor-Container {
83
+ flex: 1;
84
+ min-height: 0;
85
+ }
86
+ `,
87
+
88
+ Templates:
89
+ [
90
+ {
91
+ Hash: "Ultravisor-FlowEditor-Template",
92
+ Template: /*html*/`
93
+ <div class="ultravisor-floweditor">
94
+ <div class="ultravisor-floweditor-header">
95
+ <h1 id="Ultravisor-FlowEditor-Title">Flow Editor</h1>
96
+ <div class="ultravisor-flow-actions">
97
+ <button class="ultravisor-btn ultravisor-btn-primary" onclick="{~P~}.views['Ultravisor-FlowEditor'].saveOperation()">Save Operation</button>
98
+ </div>
99
+ </div>
100
+ <div class="ultravisor-flow-meta">
101
+ <span id="Ultravisor-FlowEditor-HashDisplay" class="ultravisor-flow-meta-hash"></span>
102
+ <label>Name</label>
103
+ <input type="text" id="Ultravisor-FlowEditor-Name" placeholder="Operation name...">
104
+ <label>Description</label>
105
+ <textarea id="Ultravisor-FlowEditor-Description" rows="1" placeholder="Description..."></textarea>
106
+ </div>
107
+ <div id="Ultravisor-FlowEditor-Container"></div>
108
+ </div>
109
+ `
110
+ }
111
+ ],
112
+
113
+ Renderables:
114
+ [
115
+ {
116
+ RenderableHash: "Ultravisor-FlowEditor-Content",
117
+ TemplateHash: "Ultravisor-FlowEditor-Template",
118
+ DestinationAddress: "#Ultravisor-Content-Container",
119
+ RenderMethod: "replace"
120
+ }
121
+ ]
122
+ };
123
+
124
+ class UltravisorFlowEditorView extends libPictView
125
+ {
126
+ constructor(pFable, pOptions, pServiceHash)
127
+ {
128
+ super(pFable, pOptions, pServiceHash);
129
+
130
+ this._FlowView = null;
131
+ }
132
+
133
+ /**
134
+ * Build a map of FlowCard node type configurations keyed by hash.
135
+ * These are passed as NodeTypes in the FlowView options so they
136
+ * are available from the moment the NodeTypeProvider is created,
137
+ * before the toolbar renders.
138
+ *
139
+ * @param {Array} [pAdditionalCardConfigs] - Optional extra card configs to register.
140
+ */
141
+ _buildFlowCardNodeTypes(pAdditionalCardConfigs)
142
+ {
143
+ let tmpNodeTypes = {};
144
+
145
+ // Generate card configs from API-fetched task type definitions
146
+ let tmpDefinitions = this.pict.AppData.Ultravisor.TaskTypes || [];
147
+ let tmpCardConfigs = libBuiltInCardConfigs.generateCardConfigs(tmpDefinitions);
148
+
149
+ for (let i = 0; i < tmpCardConfigs.length; i++)
150
+ {
151
+ let tmpCard = new libPictFlowCard(this.fable, tmpCardConfigs[i], `FlowCard-${i}`);
152
+ let tmpConfig = tmpCard.getNodeTypeConfiguration();
153
+ // Ultravisor flow cards render port labels outside the node
154
+ // boundary to avoid overlapping body content
155
+ tmpConfig.PortLabelsOutside = true;
156
+ tmpNodeTypes[tmpConfig.Hash] = tmpConfig;
157
+ }
158
+
159
+ // Register any additional cards (e.g. user-defined or plugin cards)
160
+ if (Array.isArray(pAdditionalCardConfigs))
161
+ {
162
+ for (let i = 0; i < pAdditionalCardConfigs.length; i++)
163
+ {
164
+ let tmpCard = new libPictFlowCard(this.fable, pAdditionalCardConfigs[i], `FlowCard-Extra-${i}`);
165
+ let tmpConfig = tmpCard.getNodeTypeConfiguration();
166
+ tmpConfig.PortLabelsOutside = true;
167
+ tmpNodeTypes[tmpConfig.Hash] = tmpConfig;
168
+ }
169
+ }
170
+
171
+ return tmpNodeTypes;
172
+ }
173
+
174
+ /**
175
+ * Reconcile node ports with current node type definitions.
176
+ *
177
+ * Saved operations store each node's Ports array at save time. When a
178
+ * task definition gains new ports (e.g. setting ports added after the
179
+ * definitions moved to JSON), existing saved nodes lack those ports.
180
+ *
181
+ * This method compares each node's Ports to its node type's DefaultPorts
182
+ * and appends any missing ports so they appear in the editor.
183
+ *
184
+ * @param {Array} pNodes - The Nodes array from the flow data.
185
+ */
186
+ _reconcileNodePorts(pNodes)
187
+ {
188
+ if (!Array.isArray(pNodes) || !this._FlowView)
189
+ {
190
+ return;
191
+ }
192
+
193
+ let tmpNodeTypes = this._FlowView.options.NodeTypes;
194
+
195
+ for (let i = 0; i < pNodes.length; i++)
196
+ {
197
+ let tmpNode = pNodes[i];
198
+ let tmpNodeType = tmpNodeTypes[tmpNode.Type];
199
+
200
+ if (!tmpNodeType || !Array.isArray(tmpNodeType.DefaultPorts) || !Array.isArray(tmpNode.Ports))
201
+ {
202
+ continue;
203
+ }
204
+
205
+ for (let j = 0; j < tmpNodeType.DefaultPorts.length; j++)
206
+ {
207
+ let tmpDefaultPort = tmpNodeType.DefaultPorts[j];
208
+
209
+ // Check if the node already has a port with the same label and direction
210
+ let tmpExists = false;
211
+ for (let k = 0; k < tmpNode.Ports.length; k++)
212
+ {
213
+ if (tmpNode.Ports[k].Label === tmpDefaultPort.Label && tmpNode.Ports[k].Direction === tmpDefaultPort.Direction)
214
+ {
215
+ tmpExists = true;
216
+ break;
217
+ }
218
+ }
219
+
220
+ if (!tmpExists)
221
+ {
222
+ // Generate a hash for the new port: {nodeHash}-{portLabel}
223
+ let tmpPortHash = `${tmpNode.Hash}-${tmpDefaultPort.Label}`;
224
+ tmpNode.Ports.push(
225
+ {
226
+ Hash: tmpPortHash,
227
+ Direction: tmpDefaultPort.Direction,
228
+ Side: tmpDefaultPort.Side,
229
+ Label: tmpDefaultPort.Label,
230
+ PortType: tmpDefaultPort.PortType,
231
+ MinimumInputCount: tmpDefaultPort.MinimumInputCount || 0,
232
+ MaximumInputCount: tmpDefaultPort.MaximumInputCount || 1
233
+ });
234
+ }
235
+ }
236
+
237
+ // Flag orphaned ports — saved ports whose Label+Direction
238
+ // no longer match any DefaultPort in the current server definition.
239
+ for (let k = 0; k < tmpNode.Ports.length; k++)
240
+ {
241
+ let tmpPort = tmpNode.Ports[k];
242
+ let tmpMatchFound = false;
243
+ for (let j = 0; j < tmpNodeType.DefaultPorts.length; j++)
244
+ {
245
+ if (tmpNodeType.DefaultPorts[j].Label === tmpPort.Label && tmpNodeType.DefaultPorts[j].Direction === tmpPort.Direction)
246
+ {
247
+ tmpMatchFound = true;
248
+ break;
249
+ }
250
+ }
251
+ if (!tmpMatchFound)
252
+ {
253
+ tmpPort.Orphaned = true;
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
260
+ {
261
+ // Populate the metadata fields from CurrentEditOperation
262
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
263
+ if (tmpOp)
264
+ {
265
+ let tmpTitleEl = document.getElementById('Ultravisor-FlowEditor-Title');
266
+ if (tmpTitleEl)
267
+ {
268
+ tmpTitleEl.textContent = tmpOp.Hash ? ('Flow Editor: ' + (tmpOp.Name || tmpOp.Hash)) : 'Flow Editor: New Operation';
269
+ }
270
+
271
+ let tmpHashEl = document.getElementById('Ultravisor-FlowEditor-HashDisplay');
272
+ if (tmpHashEl && tmpOp.Hash)
273
+ {
274
+ tmpHashEl.textContent = tmpOp.Hash;
275
+ }
276
+
277
+ let tmpNameEl = document.getElementById('Ultravisor-FlowEditor-Name');
278
+ if (tmpNameEl)
279
+ {
280
+ tmpNameEl.value = tmpOp.Name || '';
281
+ }
282
+
283
+ let tmpDescEl = document.getElementById('Ultravisor-FlowEditor-Description');
284
+ if (tmpDescEl)
285
+ {
286
+ tmpDescEl.value = tmpOp.Description || '';
287
+ // Auto-size the textarea to fit content
288
+ tmpDescEl.style.height = 'auto';
289
+ tmpDescEl.style.height = tmpDescEl.scrollHeight + 'px';
290
+ // Keep it auto-sized as user types
291
+ tmpDescEl.addEventListener('input', function ()
292
+ {
293
+ this.style.height = 'auto';
294
+ this.style.height = this.scrollHeight + 'px';
295
+ });
296
+ }
297
+ }
298
+
299
+ // Create and render the flow section view into its container
300
+ if (!this._FlowView)
301
+ {
302
+ this._FlowView = this.pict.addView('Ultravisor-FlowDiagram',
303
+ {
304
+ ViewIdentifier: 'Ultravisor-FlowDiagram',
305
+
306
+ DefaultRenderable: 'Flow-Container',
307
+ DefaultDestinationAddress: '#Ultravisor-FlowEditor-Container',
308
+
309
+ AutoRender: false,
310
+
311
+ FlowDataAddress: 'AppData.Ultravisor.Flows.Current',
312
+
313
+ TargetElementAddress: '#Flow-SVG-Container',
314
+
315
+ EnableToolbar: true,
316
+ EnablePanning: true,
317
+ EnableZooming: true,
318
+ EnableNodeDragging: true,
319
+ EnableConnectionCreation: true,
320
+ EnableGridSnap: false,
321
+ GridSnapSize: 20,
322
+
323
+ MinZoom: 0.1,
324
+ MaxZoom: 5.0,
325
+ ZoomStep: 0.1,
326
+
327
+ DefaultNodeType: 'default',
328
+ DefaultNodeWidth: 180,
329
+ DefaultNodeHeight: 80,
330
+
331
+ // Pre-register FlowCard node types so they are available
332
+ // when the NodeTypeProvider is created, before toolbar renders
333
+ NodeTypes: this._buildFlowCardNodeTypes(),
334
+
335
+ Renderables:
336
+ [
337
+ {
338
+ RenderableHash: 'Flow-Container',
339
+ TemplateHash: 'Flow-Container-Template',
340
+ DestinationAddress: '#Ultravisor-FlowEditor-Container',
341
+ RenderMethod: 'replace'
342
+ }
343
+ ]
344
+ },
345
+ libPictSectionFlow
346
+ );
347
+ }
348
+
349
+ // Initialize default empty flow if none exists
350
+ if (!this.pict.AppData.Ultravisor.Flows.Current)
351
+ {
352
+ this.pict.AppData.Ultravisor.Flows.Current =
353
+ {
354
+ Nodes: [],
355
+ Connections: [],
356
+ ViewState: { PanX: 0, PanY: 0, Zoom: 1, SelectedNodeHash: null, SelectedConnectionHash: null }
357
+ };
358
+ }
359
+
360
+ // Reconcile node ports with current node type definitions.
361
+ // Saved operations may lack ports that were added after the operation
362
+ // was created (e.g. setting ports added when definitions moved to JSON).
363
+ this._reconcileNodePorts(this.pict.AppData.Ultravisor.Flows.Current.Nodes);
364
+
365
+ // Reset the flow view's render state so it re-initializes SVG elements
366
+ // when re-rendered (e.g. after navigating away and back)
367
+ this._FlowView.initialRenderComplete = false;
368
+ this._FlowView.render();
369
+
370
+ // Register and apply the desert theme to match Ultravisor's UI.
371
+ // Must happen after render() since the ThemeProvider is created during init.
372
+ this._registerDesertTheme();
373
+
374
+ // Re-inject CSS after the flow view creates its dynamic styles
375
+ this.pict.CSSMap.injectCSS();
376
+
377
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
378
+ }
379
+
380
+ /**
381
+ * Register a custom "desert" flow theme that harmonizes with
382
+ * Ultravisor's Desert Dusk color scheme.
383
+ */
384
+ _registerDesertTheme()
385
+ {
386
+ if (!this._FlowView || !this._FlowView._ThemeProvider)
387
+ {
388
+ return;
389
+ }
390
+
391
+ this._FlowView._ThemeProvider.registerTheme('desert',
392
+ {
393
+ Key: 'desert',
394
+ Label: 'Desert Dusk',
395
+ CSSVariables:
396
+ {
397
+ // Canvas — muted turquoise tint, lighter than cards so they stand out
398
+ '--pf-canvas-bg': '#304040',
399
+ '--pf-grid-stroke': '#384848',
400
+
401
+ // Nodes
402
+ '--pf-node-body-fill': '#1e1a16',
403
+ '--pf-node-body-stroke': '#3a3028',
404
+ '--pf-node-body-stroke-width': '1',
405
+ '--pf-node-body-radius': '3px',
406
+ '--pf-node-shadow': 'drop-shadow(0 1px 3px rgba(0, 0, 0, 0.30))',
407
+ '--pf-node-shadow-hover': 'drop-shadow(0 2px 6px rgba(0, 0, 0, 0.40))',
408
+ '--pf-node-shadow-selected': 'drop-shadow(0 2px 8px rgba(196, 149, 106, 0.30))',
409
+ '--pf-node-shadow-dragging': 'drop-shadow(0 4px 12px rgba(0, 0, 0, 0.50))',
410
+ '--pf-node-title-fill': '#d8c8a8',
411
+ '--pf-node-title-size': '11.5px',
412
+ '--pf-node-title-weight': '600',
413
+ '--pf-node-type-label-fill': '#706050',
414
+ '--pf-node-selected-stroke': '#c4956a',
415
+
416
+ // Ports — keep the 5-type semantic colors, adjusted for dark bg
417
+ '--pf-port-input-fill': '#5a9ecb',
418
+ '--pf-port-output-fill': '#5ab88a',
419
+ '--pf-port-stroke': '#252018',
420
+ '--pf-port-event-in-fill': '#5a9ecb',
421
+ '--pf-port-event-out-fill': '#5ab88a',
422
+ '--pf-port-setting-fill': '#d4884a',
423
+ '--pf-port-value-fill': '#d4b040',
424
+ '--pf-port-error-fill': '#c44e4e',
425
+
426
+ // Port labels
427
+ '--pf-port-label-bg': 'rgba(30, 26, 22, 0.85)',
428
+ '--pf-port-label-text': '#c8b8a0',
429
+
430
+ // Connections
431
+ '--pf-connection-stroke': '#706050',
432
+ '--pf-connection-selected-stroke': '#c4956a',
433
+ '--pf-connection-event-in-stroke': '#5a9ecb',
434
+ '--pf-connection-event-out-stroke': '#5ab88a',
435
+ '--pf-connection-setting-stroke': '#d4884a',
436
+ '--pf-connection-value-stroke': '#d4b040',
437
+ '--pf-connection-error-stroke': '#c44e4e',
438
+
439
+ // Panels — cooler tone to contrast with warm node bodies
440
+ '--pf-panel-bg': '#1e2228',
441
+ '--pf-panel-border': '#343c44',
442
+ '--pf-panel-radius': '6px',
443
+ '--pf-panel-shadow': '0 4px 12px rgba(0,0,0,0.35), 0 1px 3px rgba(0,0,0,0.25)',
444
+ '--pf-panel-titlebar-bg': '#262e36',
445
+ '--pf-panel-titlebar-border': '#343c44',
446
+ '--pf-panel-title-color': '#c8d0d8'
447
+ },
448
+ AdditionalCSS: `
449
+ .pict-flow-container {
450
+ border: 1px solid #3a3028;
451
+ border-radius: 6px;
452
+ }
453
+ .pict-flow-toolbar {
454
+ background-color: #252018;
455
+ border-bottom-color: #3a3028;
456
+ }
457
+ .pict-flow-toolbar-btn {
458
+ background-color: #302818;
459
+ border-color: #3a3028;
460
+ color: #c8b8a0;
461
+ }
462
+ .pict-flow-toolbar-btn:hover {
463
+ background-color: #3a3028;
464
+ color: #d8c8a8;
465
+ }
466
+ .pict-flow-toolbar-btn.active {
467
+ background-color: #4a3828;
468
+ border-color: #c4956a;
469
+ color: #d8c8a8;
470
+ }
471
+ .pict-flow-toolbar-separator {
472
+ border-left-color: #3a3028;
473
+ }
474
+ .pict-flow-toolbar-dropdown {
475
+ background-color: #252018;
476
+ border-color: #3a3028;
477
+ color: #c8b8a0;
478
+ }
479
+ .pict-flow-toolbar-dropdown-menu {
480
+ background-color: #252018;
481
+ border-color: #3a3028;
482
+ }
483
+ .pict-flow-toolbar-dropdown-item {
484
+ color: #c8b8a0;
485
+ }
486
+ .pict-flow-toolbar-dropdown-item:hover {
487
+ background-color: #3a3028;
488
+ color: #d8c8a8;
489
+ }
490
+ .pict-flow-node-title-icon {
491
+ filter: brightness(0.8) sepia(0.3) !important;
492
+ }
493
+ /* Info panel styles — cooler blue-gray tone */
494
+ .pict-flow-info-panel {
495
+ color: #b8c4cc;
496
+ }
497
+ .pict-flow-info-panel-header {
498
+ color: #c8d0d8;
499
+ }
500
+ .pict-flow-info-panel-description {
501
+ color: #808c94;
502
+ }
503
+ .pict-flow-info-panel-section-title {
504
+ color: #7088a0;
505
+ border-bottom-color: #343c44;
506
+ }
507
+ .pict-flow-info-panel-badge.category {
508
+ background: #2a3038;
509
+ color: #8090a0;
510
+ }
511
+ .pict-flow-info-panel-badge.code {
512
+ background: #1e2830;
513
+ color: #5a9ecb;
514
+ }
515
+ .pict-flow-info-panel-port {
516
+ color: #b8c4cc;
517
+ background-color: #242c34;
518
+ border-color: #343c44;
519
+ }
520
+ .pict-flow-info-panel-port-constraint {
521
+ color: #708090;
522
+ }
523
+ /* Node properties editor in panels */
524
+ .pict-flow-panel-node-props {
525
+ border-top-color: #343c44;
526
+ }
527
+ .pict-flow-panel-node-props-header {
528
+ background: #262e36;
529
+ }
530
+ .pict-flow-panel-node-props-header:hover {
531
+ background: #2e3640;
532
+ }
533
+ .pict-flow-panel-node-props-title {
534
+ color: #7088a0;
535
+ }
536
+ .pict-flow-panel-node-props-chevron {
537
+ color: #607080;
538
+ }
539
+ .pict-flow-panel-close-btn {
540
+ color: #708090;
541
+ }
542
+ .pict-flow-panel-close-btn:hover {
543
+ color: #c44e4e;
544
+ background-color: rgba(196, 78, 78, 0.12);
545
+ }
546
+ /* Form panel styles — force all text in panels to be readable */
547
+ .pict-flow-panel-body,
548
+ .pict-flow-panel-body * {
549
+ color: #b8c4cc;
550
+ }
551
+ .pict-flow-panel-body h2,
552
+ .pict-flow-panel-body h3 {
553
+ color: #c8d0d8;
554
+ }
555
+ .pict-flow-panel-body label,
556
+ .pict-flow-panel-body .pict-form label {
557
+ color: #90a0b0 !important;
558
+ }
559
+ .pict-flow-panel-body span,
560
+ .pict-flow-panel-body .pict-form span {
561
+ color: #90a0b0;
562
+ }
563
+ .pict-flow-panel-body input,
564
+ .pict-flow-panel-body textarea,
565
+ .pict-flow-panel-body select,
566
+ .pict-flow-panel-body .pict-form input,
567
+ .pict-flow-panel-body .pict-form textarea,
568
+ .pict-flow-panel-body .pict-form select {
569
+ background-color: #181c22 !important;
570
+ border-color: #343c44 !important;
571
+ color: #b8c4cc !important;
572
+ }
573
+ .pict-flow-panel-body input:focus,
574
+ .pict-flow-panel-body textarea:focus,
575
+ .pict-flow-panel-body select:focus,
576
+ .pict-flow-panel-body .pict-form input:focus,
577
+ .pict-flow-panel-body .pict-form textarea:focus,
578
+ .pict-flow-panel-body .pict-form select:focus {
579
+ border-color: #5a9ecb !important;
580
+ }
581
+ /* Port summary in panels */
582
+ .pict-flow-port-summary {
583
+ border-top-color: #343c44;
584
+ }
585
+ /* Override per-card light-mode body fills with dark theme fill.
586
+ SVG fill="" is a presentation attribute; CSS overrides it. */
587
+ .pict-flow-node-body {
588
+ fill: #1e1a16;
589
+ stroke: #3a3028;
590
+ }
591
+ /* Override built-in start/end/halt/decision body colors (specificity 0,2,0
592
+ matches the base CSS rules so this later stylesheet wins). */
593
+ .pict-flow-node-start .pict-flow-node-body {
594
+ fill: #182018;
595
+ stroke: #2a4030;
596
+ stroke-width: 1.5;
597
+ }
598
+ .pict-flow-node-end .pict-flow-node-body {
599
+ fill: #161e20;
600
+ stroke: #2a3840;
601
+ stroke-width: 1.5;
602
+ }
603
+ .pict-flow-node-halt .pict-flow-node-body {
604
+ fill: #201414;
605
+ stroke: #4a2020;
606
+ stroke-width: 1.5;
607
+ }
608
+ .pict-flow-node-decision .pict-flow-node-body {
609
+ fill: #1e1a12;
610
+ stroke: #4a3820;
611
+ stroke-width: 1.5;
612
+ }
613
+ /* Desert title bar: warm muted base for all nodes.
614
+ CSS overrides the SVG fill="" presentation attribute. */
615
+ .pict-flow-node-title-bar,
616
+ .pict-flow-node-title-bar-bottom {
617
+ fill: #3a3028;
618
+ }
619
+ /* Distinct title bars for special node types */
620
+ .pict-flow-node-start .pict-flow-node-title-bar,
621
+ .pict-flow-node-start .pict-flow-node-title-bar-bottom {
622
+ fill: #2a4030;
623
+ }
624
+ .pict-flow-node-end .pict-flow-node-title-bar,
625
+ .pict-flow-node-end .pict-flow-node-title-bar-bottom {
626
+ fill: #2a3840;
627
+ }
628
+ .pict-flow-node-halt .pict-flow-node-title-bar,
629
+ .pict-flow-node-halt .pict-flow-node-title-bar-bottom {
630
+ fill: #4a2020;
631
+ }
632
+ .pict-flow-node-decision .pict-flow-node-title-bar,
633
+ .pict-flow-node-decision .pict-flow-node-title-bar-bottom {
634
+ fill: #4a3820;
635
+ }
636
+ `,
637
+ NodeBodyMode: 'rect',
638
+ BracketConfig: null,
639
+ ConnectionConfig:
640
+ {
641
+ StrokeDashArray: null,
642
+ StrokeWidth: 2,
643
+ ArrowheadStyle: 'triangle'
644
+ },
645
+ NoiseConfig:
646
+ {
647
+ Enabled: false,
648
+ DefaultLevel: 0,
649
+ MaxJitterPx: 0,
650
+ AffectsNodes: false,
651
+ AffectsConnections: false
652
+ },
653
+ ShapeOverrides:
654
+ {
655
+ 'arrowhead-connection': { Fill: '#706050' },
656
+ 'arrowhead-connection-selected': { Fill: '#c4956a' }
657
+ }
658
+ });
659
+
660
+ this._FlowView.setTheme('desert');
661
+ }
662
+
663
+ loadExample(pExampleName)
664
+ {
665
+ let tmpFlowData = null;
666
+
667
+ if (pExampleName === 'CSVPipeline')
668
+ {
669
+ tmpFlowData = require('../data/ExampleFlow-CSVPipeline.js');
670
+ }
671
+ else if (pExampleName === 'MeadowPipeline')
672
+ {
673
+ tmpFlowData = require('../data/ExampleFlow-MeadowPipeline.js');
674
+ }
675
+ else if (pExampleName === 'FileProcessor')
676
+ {
677
+ tmpFlowData = require('../data/ExampleFlow-FileProcessor.js');
678
+ }
679
+
680
+ if (tmpFlowData)
681
+ {
682
+ this.pict.AppData.Ultravisor.Flows.Current = JSON.parse(JSON.stringify(tmpFlowData));
683
+ if (this._FlowView)
684
+ {
685
+ this._FlowView.setFlowData(this.pict.AppData.Ultravisor.Flows.Current);
686
+ // Zoom to fit after a brief delay to allow the DOM to update
687
+ let tmpFlowView = this._FlowView;
688
+ setTimeout(function()
689
+ {
690
+ if (tmpFlowView._ViewportManager)
691
+ {
692
+ tmpFlowView._ViewportManager.zoomToFit();
693
+ }
694
+ }, 100);
695
+ }
696
+ }
697
+ }
698
+
699
+ saveOperation()
700
+ {
701
+ if (!this._FlowView)
702
+ {
703
+ return;
704
+ }
705
+
706
+ let tmpFlowData = this._FlowView.getFlowData();
707
+ if (!tmpFlowData || !tmpFlowData.Nodes || tmpFlowData.Nodes.length === 0)
708
+ {
709
+ alert('No flow data to save. Add some nodes first.');
710
+ return;
711
+ }
712
+
713
+ // Read the metadata from the form fields
714
+ let tmpName = document.getElementById('Ultravisor-FlowEditor-Name').value.trim();
715
+ let tmpDescription = document.getElementById('Ultravisor-FlowEditor-Description').value.trim();
716
+
717
+ if (!tmpName)
718
+ {
719
+ alert('Please enter an operation name.');
720
+ return;
721
+ }
722
+
723
+ // Build the operation data with the graph
724
+ let tmpOpData =
725
+ {
726
+ Name: tmpName,
727
+ Description: tmpDescription,
728
+ Graph:
729
+ {
730
+ Nodes: tmpFlowData.Nodes,
731
+ Connections: tmpFlowData.Connections,
732
+ ViewState: tmpFlowData.ViewState || {}
733
+ }
734
+ };
735
+
736
+ // Include Hash if editing an existing operation
737
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
738
+ if (tmpOp && tmpOp.Hash)
739
+ {
740
+ tmpOpData.Hash = tmpOp.Hash;
741
+ }
742
+
743
+ this.pict.PictApplication.saveOperation(tmpOpData,
744
+ function (pError, pData)
745
+ {
746
+ if (pError)
747
+ {
748
+ alert('Error saving operation: ' + pError.message);
749
+ return;
750
+ }
751
+
752
+ // Update the current edit operation with the saved data
753
+ if (pData && pData.Hash)
754
+ {
755
+ if (!this.pict.AppData.Ultravisor.CurrentEditOperation)
756
+ {
757
+ this.pict.AppData.Ultravisor.CurrentEditOperation = {};
758
+ }
759
+ this.pict.AppData.Ultravisor.CurrentEditOperation.Hash = pData.Hash;
760
+ this.pict.AppData.Ultravisor.CurrentEditOperation.Name = tmpName;
761
+ this.pict.AppData.Ultravisor.CurrentEditOperation.Description = tmpDescription;
762
+
763
+ let tmpHashEl = document.getElementById('Ultravisor-FlowEditor-HashDisplay');
764
+ if (tmpHashEl)
765
+ {
766
+ tmpHashEl.textContent = pData.Hash;
767
+ }
768
+ }
769
+
770
+ alert('Operation saved successfully.');
771
+ }.bind(this));
772
+ }
773
+ }
774
+
775
+ module.exports = UltravisorFlowEditorView;
776
+
777
+ module.exports.default_configuration = _ViewConfiguration;