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
@@ -18,40 +18,13 @@ const _ViewConfiguration =
18
18
  .ultravisor-operationedit-header {
19
19
  margin-bottom: 1.5em;
20
20
  padding-bottom: 1em;
21
- border-bottom: 1px solid #2a2a4a;
21
+ border-bottom: 1px solid var(--uv-border-subtle);
22
22
  }
23
23
  .ultravisor-operationedit-header h1 {
24
24
  margin: 0;
25
25
  font-size: 2em;
26
26
  font-weight: 300;
27
- color: #e0e0e0;
28
- }
29
- .ultravisor-task-list-editor {
30
- background: #16213e;
31
- border: 1px solid #2a2a4a;
32
- border-radius: 6px;
33
- padding: 1em;
34
- }
35
- .ultravisor-task-list-item {
36
- display: flex;
37
- align-items: center;
38
- justify-content: space-between;
39
- padding: 0.5em 0.75em;
40
- background: #1a1a2e;
41
- border-radius: 4px;
42
- margin-bottom: 0.5em;
43
- }
44
- .ultravisor-task-list-item code {
45
- color: #4fc3f7;
46
- font-size: 0.9em;
47
- }
48
- .ultravisor-task-list-add {
49
- display: flex;
50
- gap: 0.5em;
51
- margin-top: 0.75em;
52
- }
53
- .ultravisor-task-list-add input {
54
- flex: 1;
27
+ color: var(--uv-text);
55
28
  }
56
29
  `,
57
30
 
@@ -93,15 +66,19 @@ class UltravisorOperationEditView extends libPictView
93
66
  let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
94
67
  if (!tmpOp)
95
68
  {
96
- tmpOp = { GUIDOperation: '', Name: '', Description: '', Tasks: [] };
69
+ tmpOp =
70
+ {
71
+ Hash: '', Name: '', Description: '',
72
+ Graph: { Nodes: [], Connections: [], ViewState: {} }
73
+ };
97
74
  this.pict.AppData.Ultravisor.CurrentEditOperation = tmpOp;
98
75
  }
99
76
 
100
- let tmpIsNew = !tmpOp.GUIDOperation;
77
+ let tmpIsNew = !tmpOp.Hash;
101
78
  let tmpTitleEl = document.getElementById('Ultravisor-OperationEdit-Title');
102
79
  if (tmpTitleEl)
103
80
  {
104
- tmpTitleEl.textContent = tmpIsNew ? 'New Operation' : ('Edit Operation: ' + (tmpOp.Name || tmpOp.GUIDOperation));
81
+ tmpTitleEl.textContent = tmpIsNew ? 'New Operation' : ('Edit Operation: ' + (tmpOp.Name || tmpOp.Hash));
105
82
  }
106
83
 
107
84
  this.renderForm();
@@ -124,13 +101,19 @@ class UltravisorOperationEditView extends libPictView
124
101
  renderForm()
125
102
  {
126
103
  let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
127
- let tmpIsNew = !tmpOp.GUIDOperation;
104
+ let tmpIsNew = !tmpOp.Hash;
128
105
  let tmpGlobalRef = '_Pict';
129
106
  let tmpViewRef = tmpGlobalRef + ".views['Ultravisor-OperationEdit']";
130
107
 
108
+ let tmpNodeCount = (tmpOp.Graph && tmpOp.Graph.Nodes) ? tmpOp.Graph.Nodes.length : 0;
109
+
131
110
  let tmpHTML = '';
132
- tmpHTML += '<div class="ultravisor-form-group"><label>GUID Operation</label>';
133
- tmpHTML += '<input type="text" id="Ultravisor-OperationEdit-GUIDOperation" value="' + this.escapeAttr(tmpOp.GUIDOperation) + '" ' + (tmpIsNew ? '' : 'readonly') + '></div>';
111
+
112
+ if (!tmpIsNew)
113
+ {
114
+ tmpHTML += '<div class="ultravisor-form-group"><label>Hash</label>';
115
+ tmpHTML += '<input type="text" id="Ultravisor-OperationEdit-Hash" value="' + this.escapeAttr(tmpOp.Hash) + '" readonly></div>';
116
+ }
134
117
 
135
118
  tmpHTML += '<div class="ultravisor-form-group"><label>Name</label>';
136
119
  tmpHTML += '<input type="text" id="Ultravisor-OperationEdit-Name" value="' + this.escapeAttr(tmpOp.Name || '') + '"></div>';
@@ -138,91 +121,53 @@ class UltravisorOperationEditView extends libPictView
138
121
  tmpHTML += '<div class="ultravisor-form-group"><label>Description</label>';
139
122
  tmpHTML += '<textarea id="Ultravisor-OperationEdit-Description">' + this.escapeHTML(tmpOp.Description || '') + '</textarea></div>';
140
123
 
141
- tmpHTML += '<div class="ultravisor-form-group"><label>Tasks (executed in order)</label>';
142
- tmpHTML += '<div class="ultravisor-task-list-editor">';
143
-
144
- let tmpTasks = tmpOp.Tasks || [];
145
- if (tmpTasks.length === 0)
146
- {
147
- tmpHTML += '<div style="color: #607d8b; font-size: 0.9em; padding: 0.5em;">No tasks added yet.</div>';
148
- }
149
- else
124
+ if (!tmpIsNew)
150
125
  {
151
- for (let i = 0; i < tmpTasks.length; i++)
152
- {
153
- let tmpEscTask = tmpTasks[i].replace(/'/g, "\\'");
154
- tmpHTML += '<div class="ultravisor-task-list-item">';
155
- tmpHTML += '<code>' + this.escapeHTML(tmpTasks[i]) + '</code>';
156
- tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-delete" onclick="' + tmpViewRef + '.removeTaskFromOperation(' + i + ')">Remove</button>';
157
- tmpHTML += '</div>';
158
- }
126
+ tmpHTML += '<div class="ultravisor-form-group"><label>Graph</label>';
127
+ tmpHTML += '<p style="color:var(--uv-text-secondary);">' + tmpNodeCount + ' node' + (tmpNodeCount !== 1 ? 's' : '') + ' in graph. Use the Flow Editor to modify the operation graph.</p>';
128
+ tmpHTML += '</div>';
159
129
  }
160
130
 
161
- tmpHTML += '<div class="ultravisor-task-list-add">';
162
- tmpHTML += '<input type="text" id="Ultravisor-OperationEdit-NewTaskGUID" placeholder="Task GUID to add...">';
163
- tmpHTML += '<button class="ultravisor-btn ultravisor-btn-secondary" onclick="' + tmpViewRef + '.addTaskToOperation()">Add</button>';
164
- tmpHTML += '</div>';
165
- tmpHTML += '</div></div>';
166
-
167
131
  tmpHTML += '<div class="ultravisor-form-actions">';
168
- tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.saveOperation()">Save Operation</button>';
132
+ tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.saveOperation()">Save Metadata</button>';
133
+ tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.openFlowEditor()">Open in Flow Editor</button>';
169
134
  tmpHTML += '<button class="ultravisor-btn ultravisor-btn-secondary" onclick="' + tmpGlobalRef + '.PictApplication.navigateTo(\'/Operations\')">Cancel</button>';
170
135
  tmpHTML += '</div>';
171
136
 
172
137
  this.pict.ContentAssignment.assignContent('#Ultravisor-OperationEdit-Form', tmpHTML);
173
138
  }
174
139
 
175
- addTaskToOperation()
140
+ openFlowEditor()
176
141
  {
177
- let tmpInput = document.getElementById('Ultravisor-OperationEdit-NewTaskGUID');
178
- let tmpGUID = tmpInput ? tmpInput.value.trim() : '';
179
- if (!tmpGUID) return;
180
-
142
+ // Sync form values back to the model before navigating
181
143
  let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
182
- if (!tmpOp.Tasks)
183
- {
184
- tmpOp.Tasks = [];
185
- }
186
- tmpOp.Tasks.push(tmpGUID);
187
-
188
- // Preserve current form field values before re-render
189
- tmpOp.GUIDOperation = document.getElementById('Ultravisor-OperationEdit-GUIDOperation').value.trim();
190
144
  tmpOp.Name = document.getElementById('Ultravisor-OperationEdit-Name').value.trim();
191
145
  tmpOp.Description = document.getElementById('Ultravisor-OperationEdit-Description').value.trim();
192
146
 
193
- this.renderForm();
194
- }
195
-
196
- removeTaskFromOperation(pIndex)
197
- {
198
- let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
199
- if (tmpOp.Tasks && pIndex >= 0 && pIndex < tmpOp.Tasks.length)
147
+ // Load the graph into FlowEditor data
148
+ if (tmpOp.Graph)
200
149
  {
201
- tmpOp.Tasks.splice(pIndex, 1);
150
+ this.pict.AppData.Ultravisor.Flows.Current = JSON.parse(JSON.stringify(tmpOp.Graph));
202
151
  }
203
152
 
204
- // Preserve current form field values before re-render
205
- tmpOp.GUIDOperation = document.getElementById('Ultravisor-OperationEdit-GUIDOperation').value.trim();
206
- tmpOp.Name = document.getElementById('Ultravisor-OperationEdit-Name').value.trim();
207
- tmpOp.Description = document.getElementById('Ultravisor-OperationEdit-Description').value.trim();
208
-
209
- this.renderForm();
153
+ this.pict.PictApplication.navigateTo('/FlowEditor');
210
154
  }
211
155
 
212
156
  saveOperation()
213
157
  {
158
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
159
+
214
160
  let tmpOpData =
215
161
  {
216
- GUIDOperation: document.getElementById('Ultravisor-OperationEdit-GUIDOperation').value.trim(),
217
162
  Name: document.getElementById('Ultravisor-OperationEdit-Name').value.trim(),
218
163
  Description: document.getElementById('Ultravisor-OperationEdit-Description').value.trim(),
219
- Tasks: this.pict.AppData.Ultravisor.CurrentEditOperation.Tasks || []
164
+ Graph: tmpOp.Graph || { Nodes: [], Connections: [], ViewState: {} }
220
165
  };
221
166
 
222
- if (!tmpOpData.GUIDOperation)
167
+ let tmpHashEl = document.getElementById('Ultravisor-OperationEdit-Hash');
168
+ if (tmpHashEl)
223
169
  {
224
- alert('GUID Operation is required.');
225
- return;
170
+ tmpOpData.Hash = tmpHashEl.value.trim();
226
171
  }
227
172
 
228
173
  this.pict.PictApplication.saveOperation(tmpOpData,
@@ -21,25 +21,25 @@ const _ViewConfiguration =
21
21
  align-items: center;
22
22
  margin-bottom: 1.5em;
23
23
  padding-bottom: 1em;
24
- border-bottom: 1px solid #2a2a4a;
24
+ border-bottom: 1px solid var(--uv-border-subtle);
25
25
  }
26
26
  .ultravisor-operationlist-header h1 {
27
27
  margin: 0;
28
28
  font-size: 2em;
29
29
  font-weight: 300;
30
- color: #e0e0e0;
30
+ color: var(--uv-text);
31
31
  }
32
32
  .ultravisor-operation-table {
33
33
  width: 100%;
34
34
  border-collapse: collapse;
35
35
  }
36
36
  .ultravisor-operation-table th {
37
- background-color: #16213e;
37
+ background-color: var(--uv-bg-surface);
38
38
  }
39
39
  .ultravisor-operation-table tr:hover td {
40
40
  background-color: #1a2744;
41
41
  }
42
- .ultravisor-operation-task-count {
42
+ .ultravisor-operation-node-count {
43
43
  display: inline-block;
44
44
  padding: 0.15em 0.5em;
45
45
  border-radius: 3px;
@@ -48,6 +48,66 @@ const _ViewConfiguration =
48
48
  background-color: #2e7d32;
49
49
  color: #c8e6c9;
50
50
  }
51
+ .ultravisor-library-dropdown {
52
+ background-color: var(--uv-bg-base);
53
+ color: var(--uv-text);
54
+ border: 1px solid var(--uv-border-subtle);
55
+ border-radius: 4px;
56
+ padding: 0.4em 0.6em;
57
+ font-size: 0.9em;
58
+ margin-right: 0.5em;
59
+ cursor: pointer;
60
+ }
61
+ .ultravisor-library-dropdown:hover {
62
+ border-color: #4a4a7a;
63
+ }
64
+ .ultravisor-library-dropdown option {
65
+ background-color: var(--uv-bg-base);
66
+ color: var(--uv-text);
67
+ }
68
+ .ultravisor-operationlist-header-actions {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 0.5em;
72
+ }
73
+ .ultravisor-import-success {
74
+ color: var(--uv-success);
75
+ font-size: 0.9em;
76
+ padding: 0.5em 0;
77
+ }
78
+ .ultravisor-dropzone {
79
+ border: 2px dashed var(--uv-border-subtle);
80
+ border-radius: 6px;
81
+ padding: 1.5em 2em;
82
+ text-align: center;
83
+ color: var(--uv-text-secondary);
84
+ cursor: pointer;
85
+ transition: border-color 0.2s, background-color 0.2s;
86
+ margin-bottom: 1.5em;
87
+ }
88
+ .ultravisor-dropzone:hover {
89
+ border-color: var(--uv-brand);
90
+ }
91
+ .ultravisor-dropzone-active {
92
+ border-color: var(--uv-brand);
93
+ background-color: rgba(99, 102, 241, 0.08);
94
+ color: var(--uv-text);
95
+ }
96
+ .ultravisor-dropzone-label {
97
+ font-size: 0.95em;
98
+ }
99
+ .ultravisor-dropzone-options {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ gap: 0.5em;
104
+ margin-top: 0.75em;
105
+ font-size: 0.85em;
106
+ }
107
+ .ultravisor-dropzone-options label {
108
+ cursor: pointer;
109
+ color: var(--uv-text-secondary);
110
+ }
51
111
  `,
52
112
 
53
113
  Templates:
@@ -58,9 +118,24 @@ const _ViewConfiguration =
58
118
  <div class="ultravisor-operationlist">
59
119
  <div class="ultravisor-operationlist-header">
60
120
  <h1>Operations</h1>
61
- <button class="ultravisor-btn ultravisor-btn-primary" onclick="{~P~}.PictApplication.editOperation()">New Operation</button>
121
+ <div class="ultravisor-operationlist-header-actions">
122
+ <select id="Ultravisor-LibraryDropdown" class="ultravisor-library-dropdown" onchange="{~P~}.views['Ultravisor-OperationList'].onLibraryDropdownChange()">
123
+ <option value="">From Library...</option>
124
+ </select>
125
+ <button id="Ultravisor-LibraryAddBtn" class="ultravisor-btn ultravisor-btn-primary" onclick="{~P~}.views['Ultravisor-OperationList'].importSelectedLibraryOp()" style="display:none">Add</button>
126
+ <button class="ultravisor-btn ultravisor-btn-primary" onclick="{~P~}.PictApplication.editOperation()">New Operation</button>
127
+ </div>
128
+ </div>
129
+ <div id="Ultravisor-OperationList-Dropzone" class="ultravisor-dropzone">
130
+ <div class="ultravisor-dropzone-label">Drop workflow JSON here or click to browse</div>
131
+ <input type="file" id="Ultravisor-OperationList-FileInput" accept=".json" style="display:none" />
132
+ <div class="ultravisor-dropzone-options">
133
+ <input type="checkbox" id="Ultravisor-ImportRunImmediately" />
134
+ <label for="Ultravisor-ImportRunImmediately">Run immediately after import</label>
135
+ </div>
62
136
  </div>
63
137
  <div id="Ultravisor-OperationList-Body"></div>
138
+ <div id="Ultravisor-OperationList-Result"></div>
64
139
  </div>
65
140
  `
66
141
  }
@@ -92,9 +167,164 @@ class UltravisorOperationListView extends libPictView
92
167
  this.renderOperationTable();
93
168
  }.bind(this));
94
169
 
170
+ this.pict.PictApplication.loadOperationLibrary(
171
+ function ()
172
+ {
173
+ this.populateLibraryDropdown();
174
+ }.bind(this));
175
+
176
+ this.wireDropZone();
177
+
95
178
  return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
96
179
  }
97
180
 
181
+ wireDropZone()
182
+ {
183
+ let tmpDropZone = document.getElementById('Ultravisor-OperationList-Dropzone');
184
+ let tmpFileInput = document.getElementById('Ultravisor-OperationList-FileInput');
185
+
186
+ if (!tmpDropZone || !tmpFileInput)
187
+ {
188
+ return;
189
+ }
190
+
191
+ tmpDropZone.addEventListener('dragover', function (pEvent)
192
+ {
193
+ pEvent.preventDefault();
194
+ pEvent.stopPropagation();
195
+ tmpDropZone.classList.add('ultravisor-dropzone-active');
196
+ });
197
+
198
+ tmpDropZone.addEventListener('dragleave', function (pEvent)
199
+ {
200
+ pEvent.preventDefault();
201
+ pEvent.stopPropagation();
202
+ tmpDropZone.classList.remove('ultravisor-dropzone-active');
203
+ });
204
+
205
+ tmpDropZone.addEventListener('drop', function (pEvent)
206
+ {
207
+ pEvent.preventDefault();
208
+ pEvent.stopPropagation();
209
+ tmpDropZone.classList.remove('ultravisor-dropzone-active');
210
+
211
+ if (pEvent.dataTransfer && pEvent.dataTransfer.files && pEvent.dataTransfer.files.length > 0)
212
+ {
213
+ this.handleImportFile(pEvent.dataTransfer.files[0]);
214
+ }
215
+ }.bind(this));
216
+
217
+ tmpDropZone.addEventListener('click', function (pEvent)
218
+ {
219
+ if (pEvent.target.tagName !== 'INPUT')
220
+ {
221
+ tmpFileInput.click();
222
+ }
223
+ });
224
+
225
+ tmpFileInput.addEventListener('change', function (pEvent)
226
+ {
227
+ if (pEvent.target.files && pEvent.target.files.length > 0)
228
+ {
229
+ this.handleImportFile(pEvent.target.files[0]);
230
+ pEvent.target.value = '';
231
+ }
232
+ }.bind(this));
233
+ }
234
+
235
+ handleImportFile(pFile)
236
+ {
237
+ if (!pFile || !pFile.name.endsWith('.json'))
238
+ {
239
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
240
+ '<div class="ultravisor-task-result-panel"><p style="color:var(--uv-error);">Please drop a .json file.</p></div>');
241
+ return;
242
+ }
243
+
244
+ let tmpReader = new FileReader();
245
+ let tmpSelf = this;
246
+
247
+ tmpReader.onload = function (pEvent)
248
+ {
249
+ let tmpParsed;
250
+ try
251
+ {
252
+ tmpParsed = JSON.parse(pEvent.target.result);
253
+ }
254
+ catch (pParseError)
255
+ {
256
+ tmpSelf.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
257
+ '<div class="ultravisor-task-result-panel"><p style="color:var(--uv-error);">Invalid JSON: ' + tmpSelf.escapeHTML(pParseError.message) + '</p></div>');
258
+ return;
259
+ }
260
+
261
+ if (!tmpParsed.Graph && !tmpParsed.Nodes)
262
+ {
263
+ tmpSelf.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
264
+ '<div class="ultravisor-task-result-panel"><p style="color:var(--uv-error);">JSON does not appear to be a valid operation (no Graph or Nodes found).</p></div>');
265
+ return;
266
+ }
267
+
268
+ // If the JSON has Nodes/Connections at the top level (like example flows), wrap in Graph
269
+ if (!tmpParsed.Graph && tmpParsed.Nodes)
270
+ {
271
+ tmpParsed = {
272
+ Name: tmpParsed.Name || pFile.name.replace(/\.json$/, ''),
273
+ Description: tmpParsed.Description || '',
274
+ Graph: {
275
+ Nodes: tmpParsed.Nodes,
276
+ Connections: tmpParsed.Connections || [],
277
+ ViewState: tmpParsed.ViewState || {}
278
+ }
279
+ };
280
+ }
281
+
282
+ tmpSelf.processImportedOperation(tmpParsed, pFile.name);
283
+ };
284
+
285
+ tmpReader.readAsText(pFile);
286
+ }
287
+
288
+ processImportedOperation(pOperationJSON, pFileName)
289
+ {
290
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
291
+ '<div class="ultravisor-import-success">Importing ' + this.escapeHTML(pFileName) + '...</div>');
292
+
293
+ this.pict.PictApplication.importOperationFromJSON(pOperationJSON,
294
+ function (pError, pData)
295
+ {
296
+ if (pError)
297
+ {
298
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
299
+ '<div class="ultravisor-task-result-panel"><p style="color:var(--uv-error);">Import error: ' + this.escapeHTML(pError.message) + '</p></div>');
300
+ return;
301
+ }
302
+
303
+ let tmpHash = pData.Hash || '';
304
+ let tmpRunCheckbox = document.getElementById('Ultravisor-ImportRunImmediately');
305
+ let tmpRunImmediately = tmpRunCheckbox && tmpRunCheckbox.checked;
306
+
307
+ // Reload the table
308
+ this.pict.PictApplication.loadOperations(
309
+ function ()
310
+ {
311
+ this.renderOperationTable();
312
+
313
+ if (tmpRunImmediately)
314
+ {
315
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
316
+ '<div class="ultravisor-import-success">Imported as ' + this.escapeHTML(tmpHash) + ' — running...</div>');
317
+ this.runOperation(tmpHash);
318
+ }
319
+ else
320
+ {
321
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
322
+ '<div class="ultravisor-import-success">Imported as ' + this.escapeHTML(tmpHash) + '</div>');
323
+ }
324
+ }.bind(this));
325
+ }.bind(this));
326
+ }
327
+
98
328
  renderOperationTable()
99
329
  {
100
330
  let tmpOpList = this.pict.AppData.Ultravisor.OperationList;
@@ -108,25 +338,27 @@ class UltravisorOperationListView extends libPictView
108
338
  }
109
339
 
110
340
  let tmpHTML = '<table class="ultravisor-operation-table">';
111
- tmpHTML += '<thead><tr><th>GUID</th><th>Name</th><th>Tasks</th><th>Actions</th></tr></thead>';
341
+ tmpHTML += '<thead><tr><th>Hash</th><th>Name</th><th>Nodes</th><th>Actions</th></tr></thead>';
112
342
  tmpHTML += '<tbody>';
113
343
 
114
344
  for (let i = 0; i < tmpOpList.length; i++)
115
345
  {
116
346
  let tmpOp = tmpOpList[i];
117
- let tmpGUID = tmpOp.GUIDOperation || '';
118
- let tmpName = tmpOp.Name || tmpGUID;
119
- let tmpTaskCount = (tmpOp.Tasks && Array.isArray(tmpOp.Tasks)) ? tmpOp.Tasks.length : 0;
120
- let tmpEscGUID = tmpGUID.replace(/'/g, "\\'");
347
+ let tmpHash = tmpOp.Hash || '';
348
+ let tmpName = tmpOp.Name || tmpHash;
349
+ let tmpNodeCount = (tmpOp.Graph && tmpOp.Graph.Nodes) ? tmpOp.Graph.Nodes.length : 0;
350
+ let tmpEscHash = tmpHash.replace(/'/g, "\\'");
121
351
 
122
352
  tmpHTML += '<tr>';
123
- tmpHTML += '<td><code>' + tmpGUID + '</code></td>';
124
- tmpHTML += '<td>' + tmpName + '</td>';
125
- tmpHTML += '<td><span class="ultravisor-operation-task-count">' + tmpTaskCount + ' task' + (tmpTaskCount !== 1 ? 's' : '') + '</span></td>';
353
+ tmpHTML += '<td><code>' + this.escapeHTML(tmpHash) + '</code></td>';
354
+ tmpHTML += '<td>' + this.escapeHTML(tmpName) + '</td>';
355
+ tmpHTML += '<td><span class="ultravisor-operation-node-count">' + tmpNodeCount + ' node' + (tmpNodeCount !== 1 ? 's' : '') + '</span></td>';
126
356
  tmpHTML += '<td><div class="ultravisor-task-actions">';
127
- tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-execute" onclick="' + tmpGlobalRef + '.PictApplication.executeOperation(\'' + tmpEscGUID + '\', function(pErr, pData){ alert(pErr ? \'Error: \'+pErr.message : \'Operation executed. Status: \'+(pData&&pData.Status||\'Done\')); })">Run</button>';
128
- tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-edit" onclick="' + tmpGlobalRef + '.PictApplication.editOperation(\'' + tmpEscGUID + '\')">Edit</button>';
129
- tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-delete" onclick="if(confirm(\'Delete operation ' + tmpEscGUID + '?\')){ ' + tmpGlobalRef + '.PictApplication.deleteOperation(\'' + tmpEscGUID + '\', function(){ ' + tmpGlobalRef + '.PictApplication.showView(\'Ultravisor-OperationList\'); }); }">Delete</button>';
357
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-execute" onclick="' + tmpGlobalRef + '.views[\'Ultravisor-OperationList\'].runOperation(\'' + tmpEscHash + '\')">Run</button>';
358
+ tmpHTML += '<button class="ultravisor-btn-sm" style="background-color:#00695c;color:#e0f2f1;" onclick="' + tmpGlobalRef + '.views[\'Ultravisor-OperationList\'].runOperation(\'' + tmpEscHash + '\', \'debug\')">Debug</button>';
359
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-edit" onclick="' + tmpGlobalRef + '.PictApplication.editOperation(\'' + tmpEscHash + '\')">Edit</button>';
360
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-delete" onclick="if(confirm(\'Delete operation ' + tmpEscHash + '?\')){ ' + tmpGlobalRef + '.PictApplication.deleteOperation(\'' + tmpEscHash + '\', function(){ ' + tmpGlobalRef + '.PictApplication.showView(\'Ultravisor-OperationList\'); }); }">Delete</button>';
361
+ tmpHTML += '<button class="ultravisor-btn-sm" style="background-color:var(--uv-info);color:#bbdefb;" onclick="' + tmpGlobalRef + '.views[\'Ultravisor-OperationList\'].exportOperation(\'' + tmpEscHash + '\')">Export</button>';
130
362
  tmpHTML += '</div></td>';
131
363
  tmpHTML += '</tr>';
132
364
  }
@@ -134,6 +366,146 @@ class UltravisorOperationListView extends libPictView
134
366
  tmpHTML += '</tbody></table>';
135
367
  this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Body', tmpHTML);
136
368
  }
369
+
370
+ runOperation(pHash, pRunMode)
371
+ {
372
+ let tmpModeLabel = pRunMode === 'debug' ? ' (debug)' : '';
373
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
374
+ '<div class="ultravisor-task-result-panel"><h3>Running operation ' + this.escapeHTML(pHash) + tmpModeLabel + '...</h3></div>');
375
+
376
+ this.pict.PictApplication.executeOperation(pHash, pRunMode || null,
377
+ function (pError, pData)
378
+ {
379
+ if (pError)
380
+ {
381
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
382
+ '<div class="ultravisor-task-result-panel"><h3>Error</h3><p style="color:var(--uv-error);">' + this.escapeHTML(pError.message) + '</p></div>');
383
+ return;
384
+ }
385
+
386
+ let tmpHTML = '<div class="ultravisor-task-result-panel">';
387
+ tmpHTML += '<h3>Operation Result: ' + this.escapeHTML(pHash) + '</h3>';
388
+ tmpHTML += '<p><strong>Status:</strong> <span class="ultravisor-manifest-status ' + (pData.Status || '').toLowerCase() + '">' + this.escapeHTML(pData.Status || '') + '</span>';
389
+ if (pData.RunMode)
390
+ {
391
+ tmpHTML += ' &middot; <strong>Mode:</strong> ' + this.escapeHTML(pData.RunMode);
392
+ }
393
+ tmpHTML += '</p>';
394
+ tmpHTML += '<p><strong>Start:</strong> ' + this.escapeHTML(pData.StartTime || '') + ' &middot; <strong>Stop:</strong> ' + this.escapeHTML(pData.StopTime || '') + '</p>';
395
+ tmpHTML += '<p><strong>Elapsed:</strong> ' + (pData.ElapsedMs || 0) + 'ms</p>';
396
+
397
+ if (pData.Output && Object.keys(pData.Output).length > 0)
398
+ {
399
+ tmpHTML += '<h4 style="color:var(--uv-text-secondary); margin:0.75em 0 0.25em 0;">Output</h4>';
400
+ tmpHTML += '<div class="ultravisor-task-result-output">' + this.escapeHTML(JSON.stringify(pData.Output, null, 2)) + '</div>';
401
+ }
402
+
403
+ if (pData.TaskOutputs)
404
+ {
405
+ tmpHTML += '<h4 style="color:var(--uv-text-secondary); margin:0.75em 0 0.25em 0;">Task Outputs</h4>';
406
+ tmpHTML += '<div class="ultravisor-task-result-output">' + this.escapeHTML(JSON.stringify(pData.TaskOutputs, null, 2)) + '</div>';
407
+ }
408
+
409
+ if (pData.Log && pData.Log.length > 0)
410
+ {
411
+ tmpHTML += '<h4 style="color:var(--uv-text-secondary); margin:0.75em 0 0.25em 0;">Log</h4>';
412
+ tmpHTML += '<div class="ultravisor-task-result-output">' + this.escapeHTML(pData.Log.join('\n')) + '</div>';
413
+ }
414
+
415
+ tmpHTML += '</div>';
416
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result', tmpHTML);
417
+ }.bind(this));
418
+ }
419
+
420
+ populateLibraryDropdown()
421
+ {
422
+ let tmpDropdown = document.getElementById('Ultravisor-LibraryDropdown');
423
+ if (!tmpDropdown)
424
+ {
425
+ return;
426
+ }
427
+
428
+ let tmpLibrary = this.pict.AppData.Ultravisor.OperationLibrary;
429
+ let tmpHTML = '<option value="">From Library...</option>';
430
+
431
+ for (let i = 0; i < tmpLibrary.length; i++)
432
+ {
433
+ let tmpItem = tmpLibrary[i];
434
+ let tmpLabel = this.escapeHTML(tmpItem.Name || tmpItem.FileName);
435
+ tmpLabel += ' (' + (tmpItem.NodeCount || 0) + ' nodes)';
436
+ tmpHTML += '<option value="' + this.escapeHTML(tmpItem.FileName) + '">' + tmpLabel + '</option>';
437
+ }
438
+
439
+ tmpDropdown.innerHTML = tmpHTML;
440
+ }
441
+
442
+ onLibraryDropdownChange()
443
+ {
444
+ let tmpDropdown = document.getElementById('Ultravisor-LibraryDropdown');
445
+ let tmpAddBtn = document.getElementById('Ultravisor-LibraryAddBtn');
446
+
447
+ if (tmpDropdown && tmpAddBtn)
448
+ {
449
+ tmpAddBtn.style.display = tmpDropdown.value ? 'inline-block' : 'none';
450
+ }
451
+ }
452
+
453
+ importSelectedLibraryOp()
454
+ {
455
+ let tmpDropdown = document.getElementById('Ultravisor-LibraryDropdown');
456
+ if (!tmpDropdown || !tmpDropdown.value)
457
+ {
458
+ return;
459
+ }
460
+
461
+ let tmpFileName = tmpDropdown.value;
462
+
463
+ this.pict.PictApplication.importLibraryOperation(tmpFileName,
464
+ function (pError, pData)
465
+ {
466
+ if (pError)
467
+ {
468
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
469
+ '<div class="ultravisor-task-result-panel"><p style="color:var(--uv-error);">Error importing: ' + this.escapeHTML(pError.message) + '</p></div>');
470
+ return;
471
+ }
472
+
473
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
474
+ '<div class="ultravisor-import-success">Operation imported as ' + this.escapeHTML(pData.Hash) + '</div>');
475
+
476
+ // Reset dropdown
477
+ let tmpDd = document.getElementById('Ultravisor-LibraryDropdown');
478
+ if (tmpDd) { tmpDd.value = ''; }
479
+ let tmpBtn = document.getElementById('Ultravisor-LibraryAddBtn');
480
+ if (tmpBtn) { tmpBtn.style.display = 'none'; }
481
+
482
+ // Reload the table
483
+ this.pict.PictApplication.loadOperations(
484
+ function ()
485
+ {
486
+ this.renderOperationTable();
487
+ }.bind(this));
488
+ }.bind(this));
489
+ }
490
+
491
+ exportOperation(pHash)
492
+ {
493
+ this.pict.PictApplication.exportOperation(pHash,
494
+ function (pError)
495
+ {
496
+ if (pError)
497
+ {
498
+ this.pict.ContentAssignment.assignContent('#Ultravisor-OperationList-Result',
499
+ '<div class="ultravisor-task-result-panel"><p style="color:var(--uv-error);">Export error: ' + this.escapeHTML(pError.message) + '</p></div>');
500
+ }
501
+ }.bind(this));
502
+ }
503
+
504
+ escapeHTML(pValue)
505
+ {
506
+ if (!pValue) return '';
507
+ return String(pValue).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
508
+ }
137
509
  }
138
510
 
139
511
  module.exports = UltravisorOperationListView;