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
@@ -1,18 +1,32 @@
1
1
  const libPictApplication = require('pict-application');
2
2
  const libPictRouter = require('pict-router');
3
+ const libPictSectionForm = require('pict-section-form');
4
+ const libPictSectionContent = require('pict-section-content');
5
+
6
+ const THEME_LIST =
7
+ [
8
+ { Key: 'desert-dusk', Label: 'Desert Dusk', Colors: ['#252018', '#c4956a', '#4a9090', '#6a3040', '#8a9a5a'] },
9
+ { Key: 'desert-day', Label: 'Desert Day', Colors: ['#faf6f0', '#5c3d2e', '#3a8a8c', '#7a2e3a', '#6b8f4a'] },
10
+ { Key: 'desert-sunset', Label: 'Desert Sunset', Colors: ['#1e1610', '#e8943a', '#2a8a8a', '#8b2442', '#d4a46a'] },
11
+ { Key: 'professional-light', Label: 'Professional Light', Colors: ['#f5f6f8', '#3b82f6', '#10b981', '#ef4444', '#6366f1'] },
12
+ { Key: 'professional-dark', Label: 'Professional Dark', Colors: ['#111318', '#60a5fa', '#34d399', '#f87171', '#a78bfa'] },
13
+ { Key: 'desert-canyon', Label: 'Desert Canyon', Colors: ['#18120e', '#e8943a', '#18a0a0', '#e05830', '#e0c870'] }
14
+ ];
3
15
 
4
16
  // Views
5
17
  const libViewLayout = require('./views/PictView-Ultravisor-Layout.js');
6
18
  const libViewTopBar = require('./views/PictView-Ultravisor-TopBar.js');
7
19
  const libViewBottomBar = require('./views/PictView-Ultravisor-BottomBar.js');
8
20
  const libViewDashboard = require('./views/PictView-Ultravisor-Dashboard.js');
9
- const libViewTaskList = require('./views/PictView-Ultravisor-TaskList.js');
10
- const libViewTaskEdit = require('./views/PictView-Ultravisor-TaskEdit.js');
11
21
  const libViewOperationList = require('./views/PictView-Ultravisor-OperationList.js');
12
22
  const libViewOperationEdit = require('./views/PictView-Ultravisor-OperationEdit.js');
13
23
  const libViewSchedule = require('./views/PictView-Ultravisor-Schedule.js');
14
24
  const libViewManifestList = require('./views/PictView-Ultravisor-ManifestList.js');
15
25
  const libViewTimingView = require('./views/PictView-Ultravisor-TimingView.js');
26
+ const libViewFlowEditor = require('./views/PictView-Ultravisor-FlowEditor.js');
27
+ const libViewPendingInput = require('./views/PictView-Ultravisor-PendingInput.js');
28
+ const libViewDocumentation = require('./views/PictView-Ultravisor-Documentation.js');
29
+ const libViewBeaconList = require('./views/PictView-Ultravisor-BeaconList.js');
16
30
 
17
31
  class UltravisorApplication extends libPictApplication
18
32
  {
@@ -20,6 +34,10 @@ class UltravisorApplication extends libPictApplication
20
34
  {
21
35
  super(pFable, pOptions, pServiceHash);
22
36
 
37
+ // Skip premature route resolution during addRoute(); the Layout view
38
+ // calls resolve() explicitly after the DOM is ready.
39
+ this.pict.settings.RouterSkipRouteResolveOnAdd = true;
40
+
23
41
  // Add the router provider with routes
24
42
  this.pict.addProvider('PictRouter', require('./providers/PictRouter-Ultravisor-Configuration.json'), libPictRouter);
25
43
 
@@ -32,36 +50,74 @@ class UltravisorApplication extends libPictApplication
32
50
 
33
51
  // Add content views
34
52
  this.pict.addView('Ultravisor-Dashboard', libViewDashboard.default_configuration, libViewDashboard);
35
- this.pict.addView('Ultravisor-TaskList', libViewTaskList.default_configuration, libViewTaskList);
36
- this.pict.addView('Ultravisor-TaskEdit', libViewTaskEdit.default_configuration, libViewTaskEdit);
37
53
  this.pict.addView('Ultravisor-OperationList', libViewOperationList.default_configuration, libViewOperationList);
38
54
  this.pict.addView('Ultravisor-OperationEdit', libViewOperationEdit.default_configuration, libViewOperationEdit);
39
55
  this.pict.addView('Ultravisor-Schedule', libViewSchedule.default_configuration, libViewSchedule);
40
56
  this.pict.addView('Ultravisor-ManifestList', libViewManifestList.default_configuration, libViewManifestList);
41
57
  this.pict.addView('Ultravisor-TimingView', libViewTimingView.default_configuration, libViewTimingView);
58
+ this.pict.addView('Ultravisor-FlowEditor', libViewFlowEditor.default_configuration, libViewFlowEditor);
59
+ this.pict.addView('Ultravisor-PendingInput', libViewPendingInput.default_configuration, libViewPendingInput);
60
+ this.pict.addView('Ultravisor-Documentation', libViewDocumentation.default_configuration, libViewDocumentation);
61
+ this.pict.addView('Ultravisor-BeaconList', libViewBeaconList.default_configuration, libViewBeaconList);
62
+
63
+ // Register pict-section-form service types so Form panels can use them
64
+ this.pict.addServiceType('PictFormMetacontroller', libPictSectionForm.PictFormMetacontroller);
65
+
66
+ // Register pict-section-content service types so Markdown panels can render content
67
+ this.pict.addServiceType('PictContentProvider', libPictSectionContent.PictContentProvider);
68
+
69
+ // Register the Ultravisor card settings panel for per-field mode toggles (Constant/Address/Default)
70
+ const libUltravisorCardSettingsPanel = require('./panels/Ultravisor-CardSettingsPanel.js');
71
+ this.pict.addServiceType('PictFlowCardPropertiesPanel-UltravisorSettings', libUltravisorCardSettingsPanel);
42
72
  }
43
73
 
44
74
  onAfterInitializeAsync(fCallback)
45
75
  {
76
+ // Apply saved theme before first render
77
+ this.loadSavedTheme();
78
+
46
79
  // Initialize application state
47
80
  this.pict.AppData.Ultravisor =
48
81
  {
49
82
  APIBaseURL: '',
50
83
  ServerStatus: { Status: 'Unknown', ScheduleEntries: 0, ScheduleRunning: false },
51
- Tasks: {},
52
- TaskList: [],
84
+ NodeTemplates: {},
85
+ NodeTemplateList: [],
86
+ TaskTypes: [],
53
87
  Operations: {},
54
88
  OperationList: [],
55
89
  Schedule: [],
56
90
  Manifests: [],
57
- CurrentEditTask: null,
58
- CurrentEditOperation: null
91
+ PendingInputs: [],
92
+ OperationLibrary: [],
93
+ Beacons: [],
94
+ WorkItems: [],
95
+ BeaconCapabilities: {},
96
+ AffinityBindings: [],
97
+ CurrentEditOperation: null,
98
+ Flows: {},
99
+ DebugMode: false
59
100
  };
60
101
 
61
- // Render the layout shell first, then the initial content
62
- this.pict.views['Ultravisor-Layout'].render();
102
+ // Load task type definitions from the server BEFORE rendering the layout.
103
+ // The layout render triggers route resolution (via PictRouter.resolve()),
104
+ // and route handlers like editOperationFromRoute need TaskTypes to be
105
+ // populated so the FlowEditor can generate card configs with all ports.
106
+ let tmpSuper = super.onAfterInitializeAsync.bind(this);
107
+ this.loadTaskTypes(
108
+ function (pError)
109
+ {
110
+ if (pError)
111
+ {
112
+ this.pict.log.warn('Failed to load task types during init; flow editor will have no cards.');
113
+ }
114
+
115
+ // Now render the layout shell — this resolves the current route,
116
+ // which may immediately render the FlowEditor if the URL matches.
117
+ this.pict.views['Ultravisor-Layout'].render();
63
118
 
64
- return super.onAfterInitializeAsync(fCallback);
119
+ return tmpSuper(fCallback);
120
+ }.bind(this));
65
121
  }
66
122
 
67
123
  navigateTo(pRoute)
@@ -102,15 +158,25 @@ class UltravisorApplication extends libPictApplication
102
158
  .then(
103
159
  function (pData)
104
160
  {
161
+ // Server responded — clear disconnected state if it was set
162
+ if (this.pict.AppData.Ultravisor.ServerStatus.StatusClass === 'error')
163
+ {
164
+ this._setConnectionState(true);
165
+ }
166
+
105
167
  if (typeof fCallback === 'function')
106
168
  {
107
169
  fCallback(null, pData);
108
170
  }
109
- })
171
+ }.bind(this))
110
172
  .catch(
111
173
  function (pError)
112
174
  {
113
175
  this.pict.log.error(`API call failed: ${pMethod} ${pPath}`, pError);
176
+
177
+ // Network error — mark as disconnected
178
+ this._setConnectionState(false);
179
+
114
180
  if (typeof fCallback === 'function')
115
181
  {
116
182
  fCallback(pError);
@@ -118,6 +184,59 @@ class UltravisorApplication extends libPictApplication
118
184
  }.bind(this));
119
185
  }
120
186
 
187
+ /**
188
+ * Update the connection state and show/hide the disconnected banner.
189
+ *
190
+ * @param {boolean} pConnected - true if the server is reachable
191
+ */
192
+ _setConnectionState(pConnected)
193
+ {
194
+ let tmpStatus = this.pict.AppData.Ultravisor.ServerStatus;
195
+
196
+ if (!pConnected)
197
+ {
198
+ if (tmpStatus.StatusClass !== 'error')
199
+ {
200
+ tmpStatus.StatusClass = 'error';
201
+ tmpStatus.StatusText = 'Disconnected';
202
+
203
+ // Update status indicator
204
+ let tmpContent = this.pict.parseTemplateByHash('Ultravisor-TopBar-Status-Template', {}, null, this.pict);
205
+ this.pict.ContentAssignment.assignContent('#Ultravisor-TopBar-StatusArea', tmpContent);
206
+
207
+ // Show a prominent banner in the content area
208
+ let tmpContentContainer = document.getElementById('Ultravisor-Content-Container');
209
+ if (tmpContentContainer)
210
+ {
211
+ tmpContentContainer.innerHTML = '<div class="ultravisor-disconnected-banner">'
212
+ + '<div class="ultravisor-disconnected-banner-icon">&#x26A0;</div>'
213
+ + '<h2>Server Unreachable</h2>'
214
+ + '<p>The Ultravisor server is not responding. Make sure it is running and refresh the page.</p>'
215
+ + '</div>';
216
+ }
217
+ }
218
+ }
219
+ else
220
+ {
221
+ if (tmpStatus.StatusClass === 'error')
222
+ {
223
+ tmpStatus.StatusClass = 'connected';
224
+ tmpStatus.StatusText = tmpStatus.Status || 'Connected';
225
+
226
+ // Update status indicator
227
+ let tmpContent = this.pict.parseTemplateByHash('Ultravisor-TopBar-Status-Template', {}, null, this.pict);
228
+ this.pict.ContentAssignment.assignContent('#Ultravisor-TopBar-StatusArea', tmpContent);
229
+
230
+ // Remove the disconnected banner if present
231
+ let tmpBanner = document.querySelector('.ultravisor-disconnected-banner');
232
+ if (tmpBanner)
233
+ {
234
+ tmpBanner.remove();
235
+ }
236
+ }
237
+ }
238
+ }
239
+
121
240
  // --- Status ---
122
241
  loadStatus(fCallback)
123
242
  {
@@ -135,23 +254,22 @@ class UltravisorApplication extends libPictApplication
135
254
  }.bind(this));
136
255
  }
137
256
 
138
- // --- Tasks ---
139
- loadTasks(fCallback)
257
+ // --- Node Templates ---
258
+ loadNodeTemplates(fCallback)
140
259
  {
141
- this.apiCall('GET', '/Task', null,
260
+ this.apiCall('GET', '/NodeTemplate', null,
142
261
  function (pError, pData)
143
262
  {
144
263
  if (!pError && pData)
145
264
  {
146
- this.pict.AppData.Ultravisor.TaskList = Array.isArray(pData) ? pData : [];
147
- // Also index by GUIDTask
148
- let tmpTasks = {};
149
- for (let i = 0; i < this.pict.AppData.Ultravisor.TaskList.length; i++)
265
+ this.pict.AppData.Ultravisor.NodeTemplateList = Array.isArray(pData) ? pData : [];
266
+ let tmpTemplates = {};
267
+ for (let i = 0; i < this.pict.AppData.Ultravisor.NodeTemplateList.length; i++)
150
268
  {
151
- let tmpTask = this.pict.AppData.Ultravisor.TaskList[i];
152
- tmpTasks[tmpTask.GUIDTask] = tmpTask;
269
+ let tmpTemplate = this.pict.AppData.Ultravisor.NodeTemplateList[i];
270
+ tmpTemplates[tmpTemplate.Hash] = tmpTemplate;
153
271
  }
154
- this.pict.AppData.Ultravisor.Tasks = tmpTasks;
272
+ this.pict.AppData.Ultravisor.NodeTemplates = tmpTemplates;
155
273
  }
156
274
  if (typeof fCallback === 'function')
157
275
  {
@@ -160,9 +278,9 @@ class UltravisorApplication extends libPictApplication
160
278
  }.bind(this));
161
279
  }
162
280
 
163
- saveTask(pTaskData, fCallback)
281
+ saveNodeTemplate(pTemplateData, fCallback)
164
282
  {
165
- this.apiCall('POST', '/Task', pTaskData,
283
+ this.apiCall('POST', '/NodeTemplate', pTemplateData,
166
284
  function (pError, pData)
167
285
  {
168
286
  if (typeof fCallback === 'function')
@@ -172,9 +290,9 @@ class UltravisorApplication extends libPictApplication
172
290
  }.bind(this));
173
291
  }
174
292
 
175
- deleteTask(pGUIDTask, fCallback)
293
+ deleteNodeTemplate(pHash, fCallback)
176
294
  {
177
- this.apiCall('DELETE', `/Task/${encodeURIComponent(pGUIDTask)}`, null,
295
+ this.apiCall('DELETE', `/NodeTemplate/${encodeURIComponent(pHash)}`, null,
178
296
  function (pError, pData)
179
297
  {
180
298
  if (typeof fCallback === 'function')
@@ -184,11 +302,16 @@ class UltravisorApplication extends libPictApplication
184
302
  }.bind(this));
185
303
  }
186
304
 
187
- executeTask(pGUIDTask, fCallback)
305
+ // --- Task Types ---
306
+ loadTaskTypes(fCallback)
188
307
  {
189
- this.apiCall('GET', `/Task/${encodeURIComponent(pGUIDTask)}/Execute`, null,
308
+ this.apiCall('GET', '/TaskType', null,
190
309
  function (pError, pData)
191
310
  {
311
+ if (!pError && pData)
312
+ {
313
+ this.pict.AppData.Ultravisor.TaskTypes = Array.isArray(pData) ? pData : [];
314
+ }
192
315
  if (typeof fCallback === 'function')
193
316
  {
194
317
  fCallback(pError, pData);
@@ -209,7 +332,7 @@ class UltravisorApplication extends libPictApplication
209
332
  for (let i = 0; i < this.pict.AppData.Ultravisor.OperationList.length; i++)
210
333
  {
211
334
  let tmpOp = this.pict.AppData.Ultravisor.OperationList[i];
212
- tmpOperations[tmpOp.GUIDOperation] = tmpOp;
335
+ tmpOperations[tmpOp.Hash] = tmpOp;
213
336
  }
214
337
  this.pict.AppData.Ultravisor.Operations = tmpOperations;
215
338
  }
@@ -232,9 +355,9 @@ class UltravisorApplication extends libPictApplication
232
355
  }.bind(this));
233
356
  }
234
357
 
235
- deleteOperation(pGUIDOperation, fCallback)
358
+ deleteOperation(pHash, fCallback)
236
359
  {
237
- this.apiCall('DELETE', `/Operation/${encodeURIComponent(pGUIDOperation)}`, null,
360
+ this.apiCall('DELETE', `/Operation/${encodeURIComponent(pHash)}`, null,
238
361
  function (pError, pData)
239
362
  {
240
363
  if (typeof fCallback === 'function')
@@ -244,9 +367,18 @@ class UltravisorApplication extends libPictApplication
244
367
  }.bind(this));
245
368
  }
246
369
 
247
- executeOperation(pGUIDOperation, fCallback)
370
+ executeOperation(pHash, pRunMode, fCallback)
248
371
  {
249
- this.apiCall('GET', `/Operation/${encodeURIComponent(pGUIDOperation)}/Execute`, null,
372
+ if (typeof pRunMode === 'function')
373
+ {
374
+ fCallback = pRunMode;
375
+ pRunMode = null;
376
+ }
377
+
378
+ let tmpRunMode = pRunMode || (this.pict.AppData.Ultravisor.DebugMode ? 'debug' : 'standard');
379
+ let tmpURL = `/Operation/${encodeURIComponent(pHash)}/Execute?RunMode=${encodeURIComponent(tmpRunMode)}`;
380
+
381
+ this.apiCall('GET', tmpURL, null,
250
382
  function (pError, pData)
251
383
  {
252
384
  if (typeof fCallback === 'function')
@@ -273,10 +405,10 @@ class UltravisorApplication extends libPictApplication
273
405
  }.bind(this));
274
406
  }
275
407
 
276
- scheduleTask(pGUIDTask, pScheduleType, pParameters, fCallback)
408
+ scheduleOperation(pHash, pScheduleType, pParameters, fCallback)
277
409
  {
278
- this.apiCall('POST', '/Schedule/Task',
279
- { GUIDTask: pGUIDTask, ScheduleType: pScheduleType, Parameters: pParameters },
410
+ this.apiCall('POST', '/Schedule/Operation',
411
+ { Hash: pHash, ScheduleType: pScheduleType, Parameters: pParameters },
280
412
  function (pError, pData)
281
413
  {
282
414
  if (typeof fCallback === 'function')
@@ -286,10 +418,9 @@ class UltravisorApplication extends libPictApplication
286
418
  }.bind(this));
287
419
  }
288
420
 
289
- scheduleOperation(pGUIDOperation, pScheduleType, pParameters, fCallback)
421
+ removeScheduleEntry(pGUID, fCallback)
290
422
  {
291
- this.apiCall('POST', '/Schedule/Operation',
292
- { GUIDOperation: pGUIDOperation, ScheduleType: pScheduleType, Parameters: pParameters },
423
+ this.apiCall('DELETE', `/Schedule/${encodeURIComponent(pGUID)}`, null,
293
424
  function (pError, pData)
294
425
  {
295
426
  if (typeof fCallback === 'function')
@@ -299,9 +430,9 @@ class UltravisorApplication extends libPictApplication
299
430
  }.bind(this));
300
431
  }
301
432
 
302
- removeScheduleEntry(pGUID, fCallback)
433
+ startSchedule(fCallback)
303
434
  {
304
- this.apiCall('DELETE', `/Schedule/${encodeURIComponent(pGUID)}`, null,
435
+ this.apiCall('GET', '/Schedule/Start', null,
305
436
  function (pError, pData)
306
437
  {
307
438
  if (typeof fCallback === 'function')
@@ -311,9 +442,9 @@ class UltravisorApplication extends libPictApplication
311
442
  }.bind(this));
312
443
  }
313
444
 
314
- startSchedule(fCallback)
445
+ stopSchedule(fCallback)
315
446
  {
316
- this.apiCall('GET', '/Schedule/Start', null,
447
+ this.apiCall('GET', '/Schedule/Stop', null,
317
448
  function (pError, pData)
318
449
  {
319
450
  if (typeof fCallback === 'function')
@@ -323,9 +454,21 @@ class UltravisorApplication extends libPictApplication
323
454
  }.bind(this));
324
455
  }
325
456
 
326
- stopSchedule(fCallback)
457
+ startScheduleEntry(pGUID, fCallback)
327
458
  {
328
- this.apiCall('GET', '/Schedule/Stop', null,
459
+ this.apiCall('GET', `/Schedule/Start/${encodeURIComponent(pGUID)}`, null,
460
+ function (pError, pData)
461
+ {
462
+ if (typeof fCallback === 'function')
463
+ {
464
+ fCallback(pError, pData);
465
+ }
466
+ }.bind(this));
467
+ }
468
+
469
+ stopScheduleEntry(pGUID, fCallback)
470
+ {
471
+ this.apiCall('GET', `/Schedule/Stop/${encodeURIComponent(pGUID)}`, null,
329
472
  function (pError, pData)
330
473
  {
331
474
  if (typeof fCallback === 'function')
@@ -352,9 +495,9 @@ class UltravisorApplication extends libPictApplication
352
495
  }.bind(this));
353
496
  }
354
497
 
355
- loadManifest(pGUIDRun, fCallback)
498
+ loadManifest(pRunHash, fCallback)
356
499
  {
357
- this.apiCall('GET', `/Manifest/${encodeURIComponent(pGUIDRun)}`, null,
500
+ this.apiCall('GET', `/Manifest/${encodeURIComponent(pRunHash)}`, null,
358
501
  function (pError, pData)
359
502
  {
360
503
  if (typeof fCallback === 'function')
@@ -364,48 +507,399 @@ class UltravisorApplication extends libPictApplication
364
507
  }.bind(this));
365
508
  }
366
509
 
367
- // --- Edit helpers ---
368
- editTask(pGUIDTask)
510
+ // --- Pending Inputs ---
511
+ loadPendingInputs(fCallback)
512
+ {
513
+ this.apiCall('GET', '/PendingInput', null,
514
+ function (pError, pData)
515
+ {
516
+ if (!pError && pData)
517
+ {
518
+ this.pict.AppData.Ultravisor.PendingInputs = Array.isArray(pData) ? pData : [];
519
+ }
520
+ if (typeof fCallback === 'function')
521
+ {
522
+ fCallback(pError, pData);
523
+ }
524
+ }.bind(this));
525
+ }
526
+
527
+ submitPendingInput(pRunHash, pNodeHash, pValue, fCallback)
528
+ {
529
+ this.apiCall('POST', `/PendingInput/${encodeURIComponent(pRunHash)}`,
530
+ { NodeHash: pNodeHash, Value: pValue },
531
+ function (pError, pData)
532
+ {
533
+ if (typeof fCallback === 'function')
534
+ {
535
+ fCallback(pError, pData);
536
+ }
537
+ }.bind(this));
538
+ }
539
+
540
+ // --- Beacons ---
541
+ loadBeacons(fCallback)
542
+ {
543
+ this.apiCall('GET', '/Beacon', null,
544
+ function (pError, pData)
545
+ {
546
+ if (!pError && pData)
547
+ {
548
+ this.pict.AppData.Ultravisor.Beacons = Array.isArray(pData) ? pData : [];
549
+ }
550
+ if (typeof fCallback === 'function')
551
+ {
552
+ fCallback(pError, pData);
553
+ }
554
+ }.bind(this));
555
+ }
556
+
557
+ loadBeacon(pBeaconID, fCallback)
558
+ {
559
+ this.apiCall('GET', `/Beacon/${encodeURIComponent(pBeaconID)}`, null,
560
+ function (pError, pData)
561
+ {
562
+ if (typeof fCallback === 'function')
563
+ {
564
+ fCallback(pError, pData);
565
+ }
566
+ }.bind(this));
567
+ }
568
+
569
+ deregisterBeacon(pBeaconID, fCallback)
570
+ {
571
+ this.apiCall('DELETE', `/Beacon/${encodeURIComponent(pBeaconID)}`, null,
572
+ function (pError, pData)
573
+ {
574
+ if (typeof fCallback === 'function')
575
+ {
576
+ fCallback(pError, pData);
577
+ }
578
+ }.bind(this));
579
+ }
580
+
581
+ loadWorkItems(fCallback)
582
+ {
583
+ this.apiCall('GET', '/Beacon/Work', null,
584
+ function (pError, pData)
585
+ {
586
+ if (!pError && pData)
587
+ {
588
+ this.pict.AppData.Ultravisor.WorkItems = Array.isArray(pData) ? pData : [];
589
+ }
590
+ if (typeof fCallback === 'function')
591
+ {
592
+ fCallback(pError, pData);
593
+ }
594
+ }.bind(this));
595
+ }
596
+
597
+ loadBeaconCapabilities(fCallback)
598
+ {
599
+ this.apiCall('GET', '/Beacon/Capabilities', null,
600
+ function (pError, pData)
601
+ {
602
+ if (!pError && pData)
603
+ {
604
+ this.pict.AppData.Ultravisor.BeaconCapabilities = pData || {};
605
+ }
606
+ if (typeof fCallback === 'function')
607
+ {
608
+ fCallback(pError, pData);
609
+ }
610
+ }.bind(this));
611
+ }
612
+
613
+ loadAffinityBindings(fCallback)
614
+ {
615
+ this.apiCall('GET', '/Beacon/Affinity', null,
616
+ function (pError, pData)
617
+ {
618
+ if (!pError && pData)
619
+ {
620
+ this.pict.AppData.Ultravisor.AffinityBindings = Array.isArray(pData) ? pData : [];
621
+ }
622
+ if (typeof fCallback === 'function')
623
+ {
624
+ fCallback(pError, pData);
625
+ }
626
+ }.bind(this));
627
+ }
628
+
629
+ // --- Operation Library ---
630
+ loadOperationLibrary(fCallback)
631
+ {
632
+ this.apiCall('GET', '/OperationLibrary', null,
633
+ function (pError, pData)
634
+ {
635
+ if (!pError && pData)
636
+ {
637
+ this.pict.AppData.Ultravisor.OperationLibrary = Array.isArray(pData) ? pData : [];
638
+ }
639
+ if (typeof fCallback === 'function')
640
+ {
641
+ fCallback(pError, pData);
642
+ }
643
+ }.bind(this));
644
+ }
645
+
646
+ importLibraryOperation(pFileName, fCallback)
647
+ {
648
+ this.apiCall('GET', `/OperationLibrary/${encodeURIComponent(pFileName)}`, null,
649
+ function (pError, pData)
650
+ {
651
+ if (pError)
652
+ {
653
+ if (typeof fCallback === 'function')
654
+ {
655
+ fCallback(pError);
656
+ }
657
+ return;
658
+ }
659
+
660
+ // Strip library metadata, keep operation-relevant fields
661
+ let tmpOperationData =
662
+ {
663
+ Name: pData.Name || '',
664
+ Description: pData.Description || '',
665
+ Graph: pData.Graph || { Nodes: [], Connections: [], ViewState: {} }
666
+ };
667
+
668
+ if (pData.SavedLayouts)
669
+ {
670
+ tmpOperationData.SavedLayouts = pData.SavedLayouts;
671
+ }
672
+ if (pData.InitialGlobalState)
673
+ {
674
+ tmpOperationData.InitialGlobalState = pData.InitialGlobalState;
675
+ }
676
+ if (pData.InitialOperationState)
677
+ {
678
+ tmpOperationData.InitialOperationState = pData.InitialOperationState;
679
+ }
680
+
681
+ // Save as a new operation (no Hash => auto-generated)
682
+ this.saveOperation(tmpOperationData, fCallback);
683
+ }.bind(this));
684
+ }
685
+
686
+ importOperationFromJSON(pOperationJSON, fCallback)
369
687
  {
370
- if (pGUIDTask && this.pict.AppData.Ultravisor.Tasks[pGUIDTask])
688
+ if (typeof (pOperationJSON) !== 'object' || pOperationJSON === null)
689
+ {
690
+ if (typeof fCallback === 'function')
691
+ {
692
+ fCallback(new Error('Invalid operation JSON.'));
693
+ }
694
+ return;
695
+ }
696
+
697
+ // Strip identifiers so a fresh hash is auto-generated on import
698
+ let tmpOperationData =
371
699
  {
372
- this.pict.AppData.Ultravisor.CurrentEditTask = JSON.parse(JSON.stringify(this.pict.AppData.Ultravisor.Tasks[pGUIDTask]));
700
+ Name: pOperationJSON.Name || 'Imported Operation',
701
+ Description: pOperationJSON.Description || '',
702
+ Graph: pOperationJSON.Graph || { Nodes: [], Connections: [], ViewState: {} }
703
+ };
704
+
705
+ if (pOperationJSON.SavedLayouts)
706
+ {
707
+ tmpOperationData.SavedLayouts = pOperationJSON.SavedLayouts;
373
708
  }
374
- else
709
+ if (pOperationJSON.InitialGlobalState)
710
+ {
711
+ tmpOperationData.InitialGlobalState = pOperationJSON.InitialGlobalState;
712
+ }
713
+ if (pOperationJSON.InitialOperationState)
375
714
  {
376
- this.pict.AppData.Ultravisor.CurrentEditTask =
715
+ tmpOperationData.InitialOperationState = pOperationJSON.InitialOperationState;
716
+ }
717
+
718
+ // Save as a new operation (no Hash => auto-generated)
719
+ this.saveOperation(tmpOperationData, fCallback);
720
+ }
721
+
722
+ exportOperation(pHash, fCallback)
723
+ {
724
+ this.apiCall('GET', `/Operation/${encodeURIComponent(pHash)}/Export`, null,
725
+ function (pError, pData)
377
726
  {
378
- GUIDTask: '',
379
- Code: '',
380
- Name: '',
381
- Type: 'Command',
382
- Command: '',
383
- URL: '',
384
- Method: 'GET',
385
- Parameters: '',
386
- Description: ''
387
- };
727
+ if (pError)
728
+ {
729
+ if (typeof fCallback === 'function')
730
+ {
731
+ fCallback(pError);
732
+ }
733
+ return;
734
+ }
735
+
736
+ // Trigger a browser download of the JSON
737
+ let tmpFileName = (pData.Name || pData.Hash || 'operation').replace(/[^a-zA-Z0-9_-]/g, '_') + '.json';
738
+ let tmpBlob = new Blob([JSON.stringify(pData, null, '\t')], { type: 'application/json' });
739
+ let tmpLink = document.createElement('a');
740
+ tmpLink.href = URL.createObjectURL(tmpBlob);
741
+ tmpLink.download = tmpFileName;
742
+ tmpLink.click();
743
+ URL.revokeObjectURL(tmpLink.href);
744
+
745
+ if (typeof fCallback === 'function')
746
+ {
747
+ fCallback(null, pData);
748
+ }
749
+ }.bind(this));
750
+ }
751
+
752
+ // --- Theme ---
753
+ applyTheme(pThemeKey)
754
+ {
755
+ let tmpThemeKey = pThemeKey || 'desert-dusk';
756
+
757
+ if (tmpThemeKey === 'desert-dusk')
758
+ {
759
+ delete document.body.dataset.theme;
760
+ }
761
+ else
762
+ {
763
+ document.body.dataset.theme = tmpThemeKey;
764
+ }
765
+
766
+ localStorage.setItem('ultravisor-theme', tmpThemeKey);
767
+
768
+ if (this.pict.AppData.Ultravisor)
769
+ {
770
+ this.pict.AppData.Ultravisor.CurrentTheme = tmpThemeKey;
388
771
  }
389
- this.navigateTo('/TaskEdit');
390
772
  }
391
773
 
392
- editOperation(pGUIDOperation)
774
+ loadSavedTheme()
775
+ {
776
+ let tmpSavedTheme = localStorage.getItem('ultravisor-theme') || 'desert-dusk';
777
+ this.applyTheme(tmpSavedTheme);
778
+ }
779
+
780
+ getThemeList()
781
+ {
782
+ return THEME_LIST;
783
+ }
784
+
785
+ // --- Edit helpers ---
786
+
787
+ /**
788
+ * Populate CurrentEditOperation and Flows.Current from a loaded operation.
789
+ * Does not navigate — caller decides whether to navigate or render directly.
790
+ */
791
+ _setCurrentEditOperation(pHash)
393
792
  {
394
- if (pGUIDOperation && this.pict.AppData.Ultravisor.Operations[pGUIDOperation])
793
+ if (pHash && this.pict.AppData.Ultravisor.Operations[pHash])
395
794
  {
396
- this.pict.AppData.Ultravisor.CurrentEditOperation = JSON.parse(JSON.stringify(this.pict.AppData.Ultravisor.Operations[pGUIDOperation]));
795
+ let tmpOp = JSON.parse(JSON.stringify(this.pict.AppData.Ultravisor.Operations[pHash]));
796
+ this.pict.AppData.Ultravisor.CurrentEditOperation = tmpOp;
797
+
798
+ // Load the operation's graph into the FlowEditor data
799
+ if (tmpOp.Graph)
800
+ {
801
+ this.pict.AppData.Ultravisor.Flows.Current = JSON.parse(JSON.stringify(tmpOp.Graph));
802
+ }
397
803
  }
398
804
  else
399
805
  {
400
806
  this.pict.AppData.Ultravisor.CurrentEditOperation =
401
807
  {
402
- GUIDOperation: '',
808
+ Hash: '',
403
809
  Name: '',
404
810
  Description: '',
405
- Tasks: []
811
+ Graph: { Nodes: [], Connections: [], ViewState: {} }
812
+ };
813
+ this.pict.AppData.Ultravisor.Flows.Current =
814
+ {
815
+ Nodes: [],
816
+ Connections: [],
817
+ ViewState: { PanX: 0, PanY: 0, Zoom: 1, SelectedNodeHash: null, SelectedConnectionHash: null }
406
818
  };
407
819
  }
408
- this.navigateTo('/OperationEdit');
820
+ }
821
+
822
+ /**
823
+ * Called from the Operations list UI to edit an operation. Navigates to the
824
+ * parameterized FlowEditor route so the URL reflects the operation hash.
825
+ */
826
+ editOperation(pHash)
827
+ {
828
+ this._setCurrentEditOperation(pHash);
829
+
830
+ if (pHash)
831
+ {
832
+ this.navigateTo(`/FlowEditor/${encodeURIComponent(pHash)}`);
833
+ }
834
+ else
835
+ {
836
+ this.navigateTo('/FlowEditor');
837
+ }
838
+ }
839
+
840
+ /**
841
+ * Route handler for `/FlowEditor/:hash`. On a cold page load both the
842
+ * Operations map and TaskTypes may be empty, so we load them first.
843
+ * TaskTypes must be available before the FlowEditor renders so that
844
+ * _buildFlowCardNodeTypes() can generate card configs with all ports.
845
+ */
846
+ editOperationFromRoute(pHash)
847
+ {
848
+ let tmpHash = decodeURIComponent(pHash);
849
+
850
+ let tmpNeedOperations = (this.pict.AppData.Ultravisor.OperationList.length < 1);
851
+ let tmpNeedTaskTypes = (this.pict.AppData.Ultravisor.TaskTypes.length < 1);
852
+
853
+ // Hot navigation — everything already loaded
854
+ if (!tmpNeedOperations && !tmpNeedTaskTypes)
855
+ {
856
+ this._setCurrentEditOperation(tmpHash);
857
+ this.pict.views['Ultravisor-FlowEditor'].render();
858
+ return;
859
+ }
860
+
861
+ // Cold reload — load whatever is missing, then render
862
+ let tmpPending = 0;
863
+ let tmpSelf = this;
864
+
865
+ function onLoaded()
866
+ {
867
+ tmpPending--;
868
+ if (tmpPending > 0)
869
+ {
870
+ return;
871
+ }
872
+ tmpSelf._setCurrentEditOperation(tmpHash);
873
+ tmpSelf.pict.views['Ultravisor-FlowEditor'].render();
874
+ }
875
+
876
+ if (tmpNeedOperations)
877
+ {
878
+ tmpPending++;
879
+ this.loadOperations(
880
+ function (pError)
881
+ {
882
+ if (pError)
883
+ {
884
+ this.pict.log.warn(`Failed to load operations for route /FlowEditor/${tmpHash}`);
885
+ }
886
+ onLoaded();
887
+ }.bind(this));
888
+ }
889
+
890
+ if (tmpNeedTaskTypes)
891
+ {
892
+ tmpPending++;
893
+ this.loadTaskTypes(
894
+ function (pError)
895
+ {
896
+ if (pError)
897
+ {
898
+ this.pict.log.warn('Failed to load task types for FlowEditor route.');
899
+ }
900
+ onLoaded();
901
+ }.bind(this));
902
+ }
409
903
  }
410
904
  }
411
905