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
@@ -21,13 +21,13 @@ 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-schedule-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-schedule-controls {
33
33
  display: flex;
@@ -39,10 +39,10 @@ const _ViewConfiguration =
39
39
  margin-bottom: 2em;
40
40
  }
41
41
  .ultravisor-schedule-table th {
42
- background-color: #16213e;
42
+ background-color: var(--uv-bg-surface);
43
43
  }
44
44
  .ultravisor-schedule-table tr:hover td {
45
- background-color: #1a2744;
45
+ background-color: var(--uv-table-row-hover);
46
46
  }
47
47
  .ultravisor-schedule-active {
48
48
  display: inline-block;
@@ -52,16 +52,16 @@ const _ViewConfiguration =
52
52
  font-weight: 600;
53
53
  }
54
54
  .ultravisor-schedule-active.yes {
55
- background-color: #2e7d32;
56
- color: #c8e6c9;
55
+ background-color: var(--uv-success);
56
+ color: var(--uv-btn-primary-text);
57
57
  }
58
58
  .ultravisor-schedule-active.no {
59
- background-color: #424242;
60
- color: #9e9e9e;
59
+ background-color: var(--uv-bg-elevated);
60
+ color: var(--uv-text-tertiary);
61
61
  }
62
62
  .ultravisor-schedule-add-section {
63
- background: #16213e;
64
- border: 1px solid #2a2a4a;
63
+ background: var(--uv-bg-surface);
64
+ border: 1px solid var(--uv-border-subtle);
65
65
  border-radius: 8px;
66
66
  padding: 1.5em;
67
67
  margin-top: 1em;
@@ -70,7 +70,7 @@ const _ViewConfiguration =
70
70
  margin: 0 0 1em 0;
71
71
  font-size: 1.1em;
72
72
  font-weight: 600;
73
- color: #b0bec5;
73
+ color: var(--uv-text-secondary);
74
74
  }
75
75
  .ultravisor-schedule-add-form {
76
76
  display: flex;
@@ -86,10 +86,131 @@ const _ViewConfiguration =
86
86
  margin-bottom: 0.35em;
87
87
  font-size: 0.8em;
88
88
  font-weight: 600;
89
- color: #78909c;
89
+ color: var(--uv-text-secondary);
90
90
  text-transform: uppercase;
91
91
  letter-spacing: 0.03em;
92
92
  }
93
+
94
+ /* Crontab builder */
95
+ .ultravisor-cron-builder {
96
+ margin-top: 0.75em;
97
+ padding: 1em;
98
+ background: var(--uv-bg-base);
99
+ border: 1px solid var(--uv-border-subtle);
100
+ border-radius: 6px;
101
+ }
102
+ .ultravisor-cron-presets {
103
+ display: flex;
104
+ gap: 0.4em;
105
+ flex-wrap: wrap;
106
+ margin-bottom: 0.75em;
107
+ }
108
+ .ultravisor-cron-preset-btn {
109
+ background: var(--uv-btn-secondary-bg);
110
+ color: var(--uv-btn-secondary-text);
111
+ border: 1px solid var(--uv-border);
112
+ border-radius: 4px;
113
+ padding: 0.3em 0.7em;
114
+ font-size: 0.78em;
115
+ cursor: pointer;
116
+ transition: background-color 0.15s, border-color 0.15s;
117
+ }
118
+ .ultravisor-cron-preset-btn:hover {
119
+ border-color: var(--uv-brand);
120
+ color: var(--uv-text-heading);
121
+ }
122
+ .ultravisor-cron-preset-btn.active {
123
+ background: var(--uv-brand);
124
+ color: var(--uv-btn-primary-text);
125
+ border-color: var(--uv-brand);
126
+ }
127
+ .ultravisor-cron-fields {
128
+ display: flex;
129
+ gap: 0.5em;
130
+ align-items: flex-end;
131
+ margin-bottom: 0.5em;
132
+ }
133
+ .ultravisor-cron-field {
134
+ display: flex;
135
+ flex-direction: column;
136
+ align-items: center;
137
+ width: 5em;
138
+ }
139
+ .ultravisor-cron-field label {
140
+ font-size: 0.7em;
141
+ font-weight: 600;
142
+ color: var(--uv-text-tertiary);
143
+ text-transform: uppercase;
144
+ letter-spacing: 0.05em;
145
+ margin-bottom: 0.3em;
146
+ }
147
+ .ultravisor-cron-field input {
148
+ width: 100%;
149
+ text-align: center;
150
+ font-family: monospace;
151
+ font-size: 0.95em;
152
+ padding: 0.35em 0.25em;
153
+ background: var(--uv-bg-input);
154
+ color: var(--uv-text);
155
+ border: 1px solid var(--uv-border);
156
+ border-radius: 4px;
157
+ }
158
+ .ultravisor-cron-field input:focus {
159
+ outline: none;
160
+ border-color: var(--uv-border-focus);
161
+ }
162
+ .ultravisor-cron-field input:disabled {
163
+ opacity: 0.5;
164
+ cursor: not-allowed;
165
+ }
166
+ .ultravisor-cron-preview {
167
+ display: flex;
168
+ align-items: center;
169
+ gap: 0.75em;
170
+ margin-top: 0.5em;
171
+ font-size: 0.85em;
172
+ flex-wrap: wrap;
173
+ }
174
+ .ultravisor-cron-preview code {
175
+ color: var(--uv-brand);
176
+ font-size: 1.05em;
177
+ background: var(--uv-bg-code);
178
+ padding: 0.15em 0.5em;
179
+ border-radius: 3px;
180
+ }
181
+ .ultravisor-cron-description {
182
+ color: var(--uv-text-secondary);
183
+ font-style: italic;
184
+ }
185
+ .ultravisor-cron-raw-toggle {
186
+ background: none;
187
+ border: none;
188
+ color: var(--uv-link);
189
+ font-size: 0.8em;
190
+ cursor: pointer;
191
+ padding: 0;
192
+ text-decoration: underline;
193
+ }
194
+ .ultravisor-cron-raw-toggle:hover {
195
+ color: var(--uv-link-hover);
196
+ }
197
+ .ultravisor-cron-raw-input {
198
+ margin-top: 0.5em;
199
+ }
200
+ .ultravisor-cron-raw-input input {
201
+ font-family: monospace;
202
+ font-size: 0.95em;
203
+ padding: 0.35em 0.5em;
204
+ background: var(--uv-bg-input);
205
+ color: var(--uv-text);
206
+ border: 1px solid var(--uv-border);
207
+ border-radius: 4px;
208
+ width: 14em;
209
+ }
210
+ .ultravisor-cron-raw-input input:focus {
211
+ outline: none;
212
+ border-color: var(--uv-border-focus);
213
+ }
93
214
  `,
94
215
 
95
216
  Templates:
@@ -129,15 +250,23 @@ class UltravisorScheduleView extends libPictView
129
250
  constructor(pFable, pOptions, pServiceHash)
130
251
  {
131
252
  super(pFable, pOptions, pServiceHash);
253
+
254
+ this._rawMode = false;
255
+ this._activePreset = '';
132
256
  }
133
257
 
134
258
  onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
135
259
  {
136
- this.pict.PictApplication.loadSchedule(
260
+ // Load operations first (for the dropdown), then the schedule
261
+ this.pict.PictApplication.loadOperations(
137
262
  function ()
138
263
  {
139
- this.renderScheduleTable();
140
- this.renderAddForms();
264
+ this.pict.PictApplication.loadSchedule(
265
+ function ()
266
+ {
267
+ this.renderScheduleTable();
268
+ this.renderAddForms();
269
+ }.bind(this));
141
270
  }.bind(this));
142
271
 
143
272
  return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
@@ -147,11 +276,12 @@ class UltravisorScheduleView extends libPictView
147
276
  {
148
277
  let tmpSchedule = this.pict.AppData.Ultravisor.Schedule;
149
278
  let tmpGlobalRef = '_Pict';
279
+ let tmpOperations = this.pict.AppData.Ultravisor.Operations || {};
150
280
 
151
281
  if (!tmpSchedule || tmpSchedule.length === 0)
152
282
  {
153
283
  this.pict.ContentAssignment.assignContent('#Ultravisor-Schedule-Body',
154
- '<div class="ultravisor-empty-message">No schedule entries. Use the forms below to add tasks or operations to the schedule.</div>');
284
+ '<div class="ultravisor-empty-message">No schedule entries. Use the form below to add operations to the schedule.</div>');
155
285
  return;
156
286
  }
157
287
 
@@ -166,13 +296,29 @@ class UltravisorScheduleView extends libPictView
166
296
  let tmpEscGUID = tmpGUID.replace(/'/g, "\\'");
167
297
  let tmpActive = tmpEntry.Active ? 'yes' : 'no';
168
298
 
299
+ // Show operation name if available
300
+ let tmpTargetLabel = this.escapeHTML(tmpEntry.TargetHash || '');
301
+ let tmpOp = tmpOperations[tmpEntry.TargetHash];
302
+ if (tmpOp && tmpOp.Name)
303
+ {
304
+ tmpTargetLabel = this.escapeHTML(tmpOp.Name) + ' <code style="font-size:0.8em;">' + this.escapeHTML(tmpEntry.TargetHash) + '</code>';
305
+ }
306
+
169
307
  tmpHTML += '<tr>';
170
- tmpHTML += '<td><code style="font-size:0.8em;">' + tmpGUID + '</code></td>';
171
- tmpHTML += '<td>' + (tmpEntry.TargetType || '') + '</td>';
172
- tmpHTML += '<td><code>' + (tmpEntry.TargetGUID || '') + '</code></td>';
173
- tmpHTML += '<td><code>' + (tmpEntry.CronExpression || tmpEntry.Parameters || '') + '</code></td>';
308
+ tmpHTML += '<td><code style="font-size:0.8em;">' + this.escapeHTML(tmpGUID) + '</code></td>';
309
+ tmpHTML += '<td>' + this.escapeHTML(tmpEntry.TargetType || '') + '</td>';
310
+ tmpHTML += '<td>' + tmpTargetLabel + '</td>';
311
+ tmpHTML += '<td><code>' + this.escapeHTML(tmpEntry.CronExpression || tmpEntry.Parameters || '') + '</code></td>';
174
312
  tmpHTML += '<td><span class="ultravisor-schedule-active ' + tmpActive + '">' + (tmpEntry.Active ? 'Active' : 'Inactive') + '</span></td>';
175
- tmpHTML += '<td>';
313
+ tmpHTML += '<td style="white-space:nowrap;">';
314
+ if (tmpEntry.Active)
315
+ {
316
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-secondary" onclick="' + tmpGlobalRef + '.PictApplication.stopScheduleEntry(\'' + tmpEscGUID + '\', function(){ ' + tmpGlobalRef + '.PictApplication.showView(\'Ultravisor-Schedule\'); })">Stop</button> ';
317
+ }
318
+ else
319
+ {
320
+ tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-primary" onclick="' + tmpGlobalRef + '.PictApplication.startScheduleEntry(\'' + tmpEscGUID + '\', function(){ ' + tmpGlobalRef + '.PictApplication.showView(\'Ultravisor-Schedule\'); })">Start</button> ';
321
+ }
176
322
  tmpHTML += '<button class="ultravisor-btn-sm ultravisor-btn-delete" onclick="if(confirm(\'Remove schedule entry?\')){ ' + tmpGlobalRef + '.PictApplication.removeScheduleEntry(\'' + tmpEscGUID + '\', function(){ ' + tmpGlobalRef + '.PictApplication.showView(\'Ultravisor-Schedule\'); }); }">Remove</button>';
177
323
  tmpHTML += '</td>';
178
324
  tmpHTML += '</tr>';
@@ -184,85 +330,389 @@ class UltravisorScheduleView extends libPictView
184
330
 
185
331
  renderAddForms()
186
332
  {
187
- let tmpGlobalRef = '_Pict';
188
- let tmpViewRef = tmpGlobalRef + ".views['Ultravisor-Schedule']";
333
+ let tmpViewRef = "_Pict.views['Ultravisor-Schedule']";
334
+ let tmpOperations = this.pict.AppData.Ultravisor.OperationList || [];
189
335
 
190
336
  let tmpHTML = '';
191
337
 
192
- // Schedule a task
338
+ // Schedule an operation
193
339
  tmpHTML += '<div class="ultravisor-schedule-add-section">';
194
- tmpHTML += '<h3>Schedule a Task</h3>';
340
+ tmpHTML += '<h3>Schedule an Operation</h3>';
195
341
  tmpHTML += '<div class="ultravisor-schedule-add-form">';
196
- tmpHTML += '<div class="ultravisor-form-group"><label>Task GUID</label>';
197
- tmpHTML += '<input type="text" id="Ultravisor-Schedule-TaskGUID" placeholder="e.g. MY-TASK-001"></div>';
198
- tmpHTML += '<div class="ultravisor-form-group"><label>Schedule Type</label>';
199
- tmpHTML += '<select id="Ultravisor-Schedule-TaskScheduleType">';
200
- tmpHTML += '<option value="cron">Cron</option>';
201
- tmpHTML += '<option value="daily">Daily</option>';
202
- tmpHTML += '<option value="hourly">Hourly</option>';
342
+
343
+ // Operation dropdown
344
+ tmpHTML += '<div class="ultravisor-form-group"><label>Operation</label>';
345
+ tmpHTML += '<select id="Ultravisor-Schedule-OperationHash">';
346
+ tmpHTML += '<option value="">Select an operation...</option>';
347
+ for (let i = 0; i < tmpOperations.length; i++)
348
+ {
349
+ let tmpOp = tmpOperations[i];
350
+ let tmpName = this.escapeHTML(tmpOp.Name || tmpOp.Hash);
351
+ let tmpHash = this.escapeHTML(tmpOp.Hash);
352
+ tmpHTML += '<option value="' + tmpHash + '">' + tmpName + ' (' + tmpHash + ')</option>';
353
+ }
203
354
  tmpHTML += '</select></div>';
204
- tmpHTML += '<div class="ultravisor-form-group"><label>Parameters (cron expression)</label>';
205
- tmpHTML += '<input type="text" id="Ultravisor-Schedule-TaskParameters" placeholder="e.g. 0 * * * *"></div>';
206
- tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.addTaskSchedule()">Add</button>';
207
- tmpHTML += '</div></div>';
208
355
 
209
- // Schedule an operation
210
- tmpHTML += '<div class="ultravisor-schedule-add-section" style="margin-top:1em;">';
211
- tmpHTML += '<h3>Schedule an Operation</h3>';
212
- tmpHTML += '<div class="ultravisor-schedule-add-form">';
213
- tmpHTML += '<div class="ultravisor-form-group"><label>Operation GUID</label>';
214
- tmpHTML += '<input type="text" id="Ultravisor-Schedule-OperationGUID" placeholder="e.g. MY-OP-001"></div>';
356
+ // Schedule type
215
357
  tmpHTML += '<div class="ultravisor-form-group"><label>Schedule Type</label>';
216
- tmpHTML += '<select id="Ultravisor-Schedule-OperationScheduleType">';
358
+ tmpHTML += '<select id="Ultravisor-Schedule-OperationScheduleType" onchange="' + tmpViewRef + '.onScheduleTypeChange()">';
217
359
  tmpHTML += '<option value="cron">Cron</option>';
218
360
  tmpHTML += '<option value="daily">Daily</option>';
219
361
  tmpHTML += '<option value="hourly">Hourly</option>';
220
362
  tmpHTML += '</select></div>';
221
- tmpHTML += '<div class="ultravisor-form-group"><label>Parameters (cron expression)</label>';
222
- tmpHTML += '<input type="text" id="Ultravisor-Schedule-OperationParameters" placeholder="e.g. 0 */6 * * *"></div>';
363
+
364
+ // Add button
223
365
  tmpHTML += '<button class="ultravisor-btn ultravisor-btn-primary" onclick="' + tmpViewRef + '.addOperationSchedule()">Add</button>';
224
- tmpHTML += '</div></div>';
366
+
367
+ tmpHTML += '</div>';
368
+
369
+ // Cron builder
370
+ tmpHTML += this._renderCronBuilder(tmpViewRef);
371
+
372
+ tmpHTML += '</div>';
225
373
 
226
374
  this.pict.ContentAssignment.assignContent('#Ultravisor-Schedule-AddForms', tmpHTML);
375
+
376
+ // Initialize the preview
377
+ this._updateCronExpression();
227
378
  }
228
379
 
229
- addTaskSchedule()
380
+ _renderCronBuilder(pViewRef)
230
381
  {
231
- let tmpGUID = document.getElementById('Ultravisor-Schedule-TaskGUID').value.trim();
232
- let tmpType = document.getElementById('Ultravisor-Schedule-TaskScheduleType').value;
233
- let tmpParams = document.getElementById('Ultravisor-Schedule-TaskParameters').value.trim();
382
+ let tmpHTML = '';
383
+
384
+ tmpHTML += '<div class="ultravisor-cron-builder" id="Ultravisor-Cron-Builder">';
385
+
386
+ // Preset buttons
387
+ tmpHTML += '<div class="ultravisor-cron-presets">';
388
+ tmpHTML += '<button class="ultravisor-cron-preset-btn" onclick="' + pViewRef + '.applyCronPreset(\'every-minute\')">Every Minute</button>';
389
+ tmpHTML += '<button class="ultravisor-cron-preset-btn" onclick="' + pViewRef + '.applyCronPreset(\'hourly\')">Hourly</button>';
390
+ tmpHTML += '<button class="ultravisor-cron-preset-btn" onclick="' + pViewRef + '.applyCronPreset(\'daily\')">Daily</button>';
391
+ tmpHTML += '<button class="ultravisor-cron-preset-btn" onclick="' + pViewRef + '.applyCronPreset(\'weekly\')">Weekly</button>';
392
+ tmpHTML += '<button class="ultravisor-cron-preset-btn" onclick="' + pViewRef + '.applyCronPreset(\'monthly\')">Monthly</button>';
393
+ tmpHTML += '<button class="ultravisor-cron-preset-btn" onclick="' + pViewRef + '.applyCronPreset(\'every-6h\')">Every 6 Hours</button>';
394
+ tmpHTML += '<button class="ultravisor-cron-preset-btn" onclick="' + pViewRef + '.applyCronPreset(\'every-15m\')">Every 15 Min</button>';
395
+ tmpHTML += '</div>';
234
396
 
235
- if (!tmpGUID)
397
+ // 5 cron fields
398
+ tmpHTML += '<div class="ultravisor-cron-fields">';
399
+
400
+ let tmpFields = [
401
+ { id: 'Minute', label: 'Minute', placeholder: '0-59' },
402
+ { id: 'Hour', label: 'Hour', placeholder: '0-23' },
403
+ { id: 'DayOfMonth', label: 'Day', placeholder: '1-31' },
404
+ { id: 'Month', label: 'Month', placeholder: '1-12' },
405
+ { id: 'Weekday', label: 'Wkday', placeholder: '0-6' }
406
+ ];
407
+
408
+ for (let i = 0; i < tmpFields.length; i++)
409
+ {
410
+ let tmpField = tmpFields[i];
411
+ tmpHTML += '<div class="ultravisor-cron-field">';
412
+ tmpHTML += '<label>' + tmpField.label + '</label>';
413
+ tmpHTML += '<input type="text" id="Ultravisor-Cron-' + tmpField.id + '" value="*" placeholder="' + tmpField.placeholder + '" oninput="' + pViewRef + '._updateCronExpression()">';
414
+ tmpHTML += '</div>';
415
+ }
416
+
417
+ tmpHTML += '</div>';
418
+
419
+ // Preview
420
+ tmpHTML += '<div class="ultravisor-cron-preview">';
421
+ tmpHTML += '<code id="Ultravisor-Cron-Expression">* * * * *</code>';
422
+ tmpHTML += '<span class="ultravisor-cron-description" id="Ultravisor-Cron-Description">Every minute</span>';
423
+ tmpHTML += '<button class="ultravisor-cron-raw-toggle" onclick="' + pViewRef + '.toggleRawMode()">edit raw</button>';
424
+ tmpHTML += '</div>';
425
+
426
+ // Hidden raw input
427
+ tmpHTML += '<div class="ultravisor-cron-raw-input" id="Ultravisor-Cron-RawWrap" style="display:none;">';
428
+ tmpHTML += '<input type="text" id="Ultravisor-Cron-RawInput" value="* * * * *" placeholder="min hour day month weekday" oninput="' + pViewRef + '._onRawInput()">';
429
+ tmpHTML += '</div>';
430
+
431
+ // Hidden field for the final expression value
432
+ tmpHTML += '<input type="hidden" id="Ultravisor-Schedule-OperationParameters" value="* * * * *">';
433
+
434
+ tmpHTML += '</div>';
435
+
436
+ return tmpHTML;
437
+ }
438
+
439
+ applyCronPreset(pPreset)
440
+ {
441
+ let tmpPresets = {
442
+ 'every-minute': { m: '*', h: '*', d: '*', mo: '*', w: '*' },
443
+ 'hourly': { m: '0', h: '*', d: '*', mo: '*', w: '*' },
444
+ 'daily': { m: '0', h: '0', d: '*', mo: '*', w: '*' },
445
+ 'weekly': { m: '0', h: '0', d: '*', mo: '*', w: '0' },
446
+ 'monthly': { m: '0', h: '0', d: '1', mo: '*', w: '*' },
447
+ 'every-6h': { m: '0', h: '*/6', d: '*', mo: '*', w: '*' },
448
+ 'every-15m': { m: '*/15', h: '*', d: '*', mo: '*', w: '*' }
449
+ };
450
+
451
+ let tmpPreset = tmpPresets[pPreset];
452
+ if (!tmpPreset)
236
453
  {
237
- alert('Task GUID is required.');
238
454
  return;
239
455
  }
240
456
 
241
- this.pict.PictApplication.scheduleTask(tmpGUID, tmpType, tmpParams,
242
- function (pError)
457
+ this._activePreset = pPreset;
458
+
459
+ document.getElementById('Ultravisor-Cron-Minute').value = tmpPreset.m;
460
+ document.getElementById('Ultravisor-Cron-Hour').value = tmpPreset.h;
461
+ document.getElementById('Ultravisor-Cron-DayOfMonth').value = tmpPreset.d;
462
+ document.getElementById('Ultravisor-Cron-Month').value = tmpPreset.mo;
463
+ document.getElementById('Ultravisor-Cron-Weekday').value = tmpPreset.w;
464
+
465
+ this._updateCronExpression();
466
+
467
+ // Highlight active preset button
468
+ let tmpButtons = document.querySelectorAll('.ultravisor-cron-preset-btn');
469
+ for (let i = 0; i < tmpButtons.length; i++)
470
+ {
471
+ tmpButtons[i].classList.remove('active');
472
+ }
473
+ // Find the button that matches this preset by text content
474
+ let tmpPresetLabels = {
475
+ 'every-minute': 'Every Minute',
476
+ 'hourly': 'Hourly',
477
+ 'daily': 'Daily',
478
+ 'weekly': 'Weekly',
479
+ 'monthly': 'Monthly',
480
+ 'every-6h': 'Every 6 Hours',
481
+ 'every-15m': 'Every 15 Min'
482
+ };
483
+ let tmpLabel = tmpPresetLabels[pPreset];
484
+ for (let i = 0; i < tmpButtons.length; i++)
485
+ {
486
+ if (tmpButtons[i].textContent === tmpLabel)
243
487
  {
244
- if (pError)
245
- {
246
- alert('Error scheduling task: ' + pError.message);
247
- return;
248
- }
249
- this.pict.PictApplication.showView('Ultravisor-Schedule');
250
- }.bind(this));
488
+ tmpButtons[i].classList.add('active');
489
+ break;
490
+ }
491
+ }
492
+ }
493
+
494
+ onScheduleTypeChange()
495
+ {
496
+ let tmpType = document.getElementById('Ultravisor-Schedule-OperationScheduleType').value;
497
+ let tmpBuilder = document.getElementById('Ultravisor-Cron-Builder');
498
+
499
+ if (!tmpBuilder)
500
+ {
501
+ return;
502
+ }
503
+
504
+ let tmpFields = tmpBuilder.querySelectorAll('.ultravisor-cron-field input');
505
+
506
+ if (tmpType === 'daily')
507
+ {
508
+ this.applyCronPreset('daily');
509
+ for (let i = 0; i < tmpFields.length; i++)
510
+ {
511
+ tmpFields[i].disabled = true;
512
+ }
513
+ }
514
+ else if (tmpType === 'hourly')
515
+ {
516
+ this.applyCronPreset('hourly');
517
+ for (let i = 0; i < tmpFields.length; i++)
518
+ {
519
+ tmpFields[i].disabled = true;
520
+ }
521
+ }
522
+ else
523
+ {
524
+ for (let i = 0; i < tmpFields.length; i++)
525
+ {
526
+ tmpFields[i].disabled = false;
527
+ }
528
+ }
529
+ }
530
+
531
+ _updateCronExpression()
532
+ {
533
+ let tmpMinute = (document.getElementById('Ultravisor-Cron-Minute') || {}).value || '*';
534
+ let tmpHour = (document.getElementById('Ultravisor-Cron-Hour') || {}).value || '*';
535
+ let tmpDay = (document.getElementById('Ultravisor-Cron-DayOfMonth') || {}).value || '*';
536
+ let tmpMonth = (document.getElementById('Ultravisor-Cron-Month') || {}).value || '*';
537
+ let tmpWeekday = (document.getElementById('Ultravisor-Cron-Weekday') || {}).value || '*';
538
+
539
+ let tmpExpression = tmpMinute + ' ' + tmpHour + ' ' + tmpDay + ' ' + tmpMonth + ' ' + tmpWeekday;
540
+
541
+ // Update preview elements
542
+ let tmpExprElem = document.getElementById('Ultravisor-Cron-Expression');
543
+ if (tmpExprElem)
544
+ {
545
+ tmpExprElem.textContent = tmpExpression;
546
+ }
547
+
548
+ let tmpDescElem = document.getElementById('Ultravisor-Cron-Description');
549
+ if (tmpDescElem)
550
+ {
551
+ tmpDescElem.textContent = this._describeCron(tmpExpression);
552
+ }
553
+
554
+ // Update hidden parameter field
555
+ let tmpParamElem = document.getElementById('Ultravisor-Schedule-OperationParameters');
556
+ if (tmpParamElem)
557
+ {
558
+ tmpParamElem.value = tmpExpression;
559
+ }
560
+
561
+ // Update raw input if visible
562
+ let tmpRawInput = document.getElementById('Ultravisor-Cron-RawInput');
563
+ if (tmpRawInput && !tmpRawInput.matches(':focus'))
564
+ {
565
+ tmpRawInput.value = tmpExpression;
566
+ }
567
+
568
+ // Clear active preset highlight when user edits manually
569
+ this._activePreset = '';
570
+ let tmpButtons = document.querySelectorAll('.ultravisor-cron-preset-btn');
571
+ for (let i = 0; i < tmpButtons.length; i++)
572
+ {
573
+ tmpButtons[i].classList.remove('active');
574
+ }
575
+ }
576
+
577
+ _onRawInput()
578
+ {
579
+ let tmpRawInput = document.getElementById('Ultravisor-Cron-RawInput');
580
+ if (!tmpRawInput)
581
+ {
582
+ return;
583
+ }
584
+
585
+ let tmpParts = tmpRawInput.value.trim().split(/\s+/);
586
+ if (tmpParts.length >= 5)
587
+ {
588
+ document.getElementById('Ultravisor-Cron-Minute').value = tmpParts[0];
589
+ document.getElementById('Ultravisor-Cron-Hour').value = tmpParts[1];
590
+ document.getElementById('Ultravisor-Cron-DayOfMonth').value = tmpParts[2];
591
+ document.getElementById('Ultravisor-Cron-Month').value = tmpParts[3];
592
+ document.getElementById('Ultravisor-Cron-Weekday').value = tmpParts[4];
593
+ }
594
+
595
+ let tmpExpression = tmpRawInput.value.trim();
596
+
597
+ let tmpExprElem = document.getElementById('Ultravisor-Cron-Expression');
598
+ if (tmpExprElem)
599
+ {
600
+ tmpExprElem.textContent = tmpExpression;
601
+ }
602
+
603
+ let tmpDescElem = document.getElementById('Ultravisor-Cron-Description');
604
+ if (tmpDescElem)
605
+ {
606
+ tmpDescElem.textContent = this._describeCron(tmpExpression);
607
+ }
608
+
609
+ let tmpParamElem = document.getElementById('Ultravisor-Schedule-OperationParameters');
610
+ if (tmpParamElem)
611
+ {
612
+ tmpParamElem.value = tmpExpression;
613
+ }
614
+ }
615
+
616
+ toggleRawMode()
617
+ {
618
+ this._rawMode = !this._rawMode;
619
+
620
+ let tmpRawWrap = document.getElementById('Ultravisor-Cron-RawWrap');
621
+ let tmpToggle = document.querySelector('.ultravisor-cron-raw-toggle');
622
+
623
+ if (tmpRawWrap)
624
+ {
625
+ tmpRawWrap.style.display = this._rawMode ? 'block' : 'none';
626
+ }
627
+ if (tmpToggle)
628
+ {
629
+ tmpToggle.textContent = this._rawMode ? 'use builder' : 'edit raw';
630
+ }
631
+ }
632
+
633
+ _describeCron(pExpression)
634
+ {
635
+ let tmpParts = pExpression.trim().split(/\s+/);
636
+ if (tmpParts.length < 5)
637
+ {
638
+ return pExpression;
639
+ }
640
+
641
+ let tmpMin = tmpParts[0];
642
+ let tmpHour = tmpParts[1];
643
+ let tmpDay = tmpParts[2];
644
+ let tmpMonth = tmpParts[3];
645
+ let tmpWeekday = tmpParts[4];
646
+
647
+ let tmpDayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
648
+
649
+ // Every minute
650
+ if (tmpMin === '*' && tmpHour === '*' && tmpDay === '*' && tmpMonth === '*' && tmpWeekday === '*')
651
+ {
652
+ return 'Every minute';
653
+ }
654
+
655
+ // Every N minutes
656
+ let tmpMinStep = tmpMin.match(/^\*\/(\d+)$/);
657
+ if (tmpMinStep && tmpHour === '*' && tmpDay === '*' && tmpMonth === '*' && tmpWeekday === '*')
658
+ {
659
+ return 'Every ' + tmpMinStep[1] + ' minutes';
660
+ }
661
+
662
+ // Every hour at :MM
663
+ if (tmpMin !== '*' && tmpHour === '*' && tmpDay === '*' && tmpMonth === '*' && tmpWeekday === '*')
664
+ {
665
+ return 'Every hour at :' + tmpMin.padStart(2, '0');
666
+ }
667
+
668
+ // Every N hours
669
+ let tmpHourStep = tmpHour.match(/^\*\/(\d+)$/);
670
+ if (tmpMin !== '*' && tmpHourStep && tmpDay === '*' && tmpMonth === '*' && tmpWeekday === '*')
671
+ {
672
+ return 'Every ' + tmpHourStep[1] + ' hours';
673
+ }
674
+
675
+ // Daily at specific time
676
+ if (tmpMin !== '*' && tmpHour !== '*' && tmpDay === '*' && tmpMonth === '*' && tmpWeekday === '*')
677
+ {
678
+ return 'Daily at ' + tmpHour.padStart(2, '0') + ':' + tmpMin.padStart(2, '0');
679
+ }
680
+
681
+ // Weekly on specific day
682
+ if (tmpMin !== '*' && tmpHour !== '*' && tmpDay === '*' && tmpMonth === '*' && tmpWeekday !== '*')
683
+ {
684
+ let tmpDayName = tmpDayNames[parseInt(tmpWeekday)] || 'day ' + tmpWeekday;
685
+ return 'Weekly on ' + tmpDayName + ' at ' + tmpHour.padStart(2, '0') + ':' + tmpMin.padStart(2, '0');
686
+ }
687
+
688
+ // Monthly on specific day
689
+ if (tmpMin !== '*' && tmpHour !== '*' && tmpDay !== '*' && tmpMonth === '*' && tmpWeekday === '*')
690
+ {
691
+ let tmpSuffix = 'th';
692
+ let tmpDayNum = parseInt(tmpDay);
693
+ if (tmpDayNum === 1 || tmpDayNum === 21 || tmpDayNum === 31) tmpSuffix = 'st';
694
+ else if (tmpDayNum === 2 || tmpDayNum === 22) tmpSuffix = 'nd';
695
+ else if (tmpDayNum === 3 || tmpDayNum === 23) tmpSuffix = 'rd';
696
+ return 'Monthly on the ' + tmpDay + tmpSuffix + ' at ' + tmpHour.padStart(2, '0') + ':' + tmpMin.padStart(2, '0');
697
+ }
698
+
699
+ // Fallback
700
+ return pExpression;
251
701
  }
252
702
 
253
703
  addOperationSchedule()
254
704
  {
255
- let tmpGUID = document.getElementById('Ultravisor-Schedule-OperationGUID').value.trim();
705
+ let tmpHash = document.getElementById('Ultravisor-Schedule-OperationHash').value.trim();
256
706
  let tmpType = document.getElementById('Ultravisor-Schedule-OperationScheduleType').value;
257
707
  let tmpParams = document.getElementById('Ultravisor-Schedule-OperationParameters').value.trim();
258
708
 
259
- if (!tmpGUID)
709
+ if (!tmpHash)
260
710
  {
261
- alert('Operation GUID is required.');
711
+ alert('Please select an operation.');
262
712
  return;
263
713
  }
264
714
 
265
- this.pict.PictApplication.scheduleOperation(tmpGUID, tmpType, tmpParams,
715
+ this.pict.PictApplication.scheduleOperation(tmpHash, tmpType, tmpParams,
266
716
  function (pError)
267
717
  {
268
718
  if (pError)
@@ -273,6 +723,12 @@ class UltravisorScheduleView extends libPictView
273
723
  this.pict.PictApplication.showView('Ultravisor-Schedule');
274
724
  }.bind(this));
275
725
  }
726
+
727
+ escapeHTML(pValue)
728
+ {
729
+ if (!pValue) return '';
730
+ return String(pValue).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
731
+ }
276
732
  }
277
733
 
278
734
  module.exports = UltravisorScheduleView;