ultravisor 1.0.0 → 1.0.2

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 (96) hide show
  1. package/.babelrc +6 -0
  2. package/.browserslistrc +1 -0
  3. package/.browserslistrc-BACKUP +1 -0
  4. package/.gulpfile-quackage-config.json +7 -0
  5. package/.gulpfile-quackage.js +2 -0
  6. package/CONTRIBUTING.md +50 -0
  7. package/README.md +34 -0
  8. package/debug/Harness.js +2 -1
  9. package/docs/.nojekyll +0 -0
  10. package/docs/_sidebar.md +18 -0
  11. package/docs/_topbar.md +7 -0
  12. package/docs/architecture.md +103 -0
  13. package/docs/cover.md +15 -0
  14. package/docs/features/api.md +230 -0
  15. package/docs/features/cli.md +182 -0
  16. package/docs/features/configuration.md +245 -0
  17. package/docs/features/manifests.md +177 -0
  18. package/docs/features/operations.md +292 -0
  19. package/docs/features/scheduling.md +179 -0
  20. package/docs/features/tasks.md +1857 -0
  21. package/docs/index.html +39 -0
  22. package/docs/overview.md +75 -0
  23. package/docs/quickstart.md +167 -0
  24. package/docs/retold-catalog.json +24 -0
  25. package/docs/retold-keyword-index.json +19 -0
  26. package/package.json +5 -2
  27. package/source/Ultravisor.cjs +2 -2
  28. package/source/cli/Ultravisor-CLIProgram.cjs +38 -0
  29. package/source/cli/commands/Ultravisor-Command-ScheduleOperation.cjs +26 -2
  30. package/source/cli/commands/Ultravisor-Command-ScheduleTask.cjs +26 -2
  31. package/source/cli/commands/Ultravisor-Command-ScheduleView.cjs +22 -0
  32. package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +49 -1
  33. package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +51 -1
  34. package/source/cli/commands/Ultravisor-Command-Stop.cjs +4 -0
  35. package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +91 -0
  36. package/source/config/Ultravisor-Default-Command-Configuration.cjs +6 -1
  37. package/source/services/Ultravisor-Hypervisor-Event-Base.cjs +18 -1
  38. package/source/services/Ultravisor-Hypervisor-State.cjs +213 -0
  39. package/source/services/Ultravisor-Hypervisor.cjs +225 -1
  40. package/source/services/Ultravisor-Operation-Manifest.cjs +150 -1
  41. package/source/services/Ultravisor-Operation.cjs +190 -1
  42. package/source/services/Ultravisor-Task.cjs +339 -1
  43. package/source/services/events/Ultravisor-Hypervisor-Event-Cron.cjs +71 -1
  44. package/source/services/tasks/Ultravisor-Task-Base.cjs +264 -0
  45. package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +188 -0
  46. package/source/services/tasks/Ultravisor-Task-Command.cjs +65 -0
  47. package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +190 -0
  48. package/source/services/tasks/Ultravisor-Task-Conditional.cjs +104 -0
  49. package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +72 -0
  50. package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +336 -0
  51. package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +143 -0
  52. package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +146 -0
  53. package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +158 -0
  54. package/source/services/tasks/Ultravisor-Task-Request.cjs +56 -0
  55. package/source/services/tasks/Ultravisor-Task-Solver.cjs +89 -0
  56. package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +93 -0
  57. package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +127 -0
  58. package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +119 -0
  59. package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +109 -0
  60. package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +112 -0
  61. package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +499 -0
  62. package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +150 -0
  63. package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +110 -0
  64. package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +89 -0
  65. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +87 -0
  66. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +67 -0
  67. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +66 -0
  68. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +69 -0
  69. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +95 -0
  70. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +96 -0
  71. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +99 -0
  72. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +102 -0
  73. package/source/web_server/Ultravisor-API-Server.cjs +463 -3
  74. package/test/Ultravisor_tests.js +6097 -1
  75. package/webinterface/.babelrc +6 -0
  76. package/webinterface/.browserslistrc +1 -0
  77. package/webinterface/.browserslistrc-BACKUP +1 -0
  78. package/webinterface/.gulpfile-quackage-config.json +7 -0
  79. package/webinterface/.gulpfile-quackage.js +2 -0
  80. package/webinterface/css/ultravisor.css +121 -0
  81. package/webinterface/html/index.html +32 -0
  82. package/webinterface/package.json +39 -0
  83. package/webinterface/source/Pict-Application-Ultravisor-Configuration.json +15 -0
  84. package/webinterface/source/Pict-Application-Ultravisor.js +414 -0
  85. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +42 -0
  86. package/webinterface/source/views/PictView-Ultravisor-BottomBar.js +65 -0
  87. package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +236 -0
  88. package/webinterface/source/views/PictView-Ultravisor-Layout.js +83 -0
  89. package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +273 -0
  90. package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +243 -0
  91. package/webinterface/source/views/PictView-Ultravisor-OperationList.js +141 -0
  92. package/webinterface/source/views/PictView-Ultravisor-Schedule.js +280 -0
  93. package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +220 -0
  94. package/webinterface/source/views/PictView-Ultravisor-TaskList.js +248 -0
  95. package/webinterface/source/views/PictView-Ultravisor-TimingView.js +420 -0
  96. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +147 -0
@@ -0,0 +1,420 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Ultravisor-TimingView",
6
+
7
+ DefaultRenderable: "Ultravisor-TimingView-Content",
8
+ DefaultDestinationAddress: "#Ultravisor-Content-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: /*css*/`
13
+ .ultravisor-timing {
14
+ padding: 2em;
15
+ max-width: 1200px;
16
+ margin: 0 auto;
17
+ }
18
+ .ultravisor-timing-header {
19
+ display: flex;
20
+ justify-content: space-between;
21
+ align-items: center;
22
+ margin-bottom: 1.5em;
23
+ padding-bottom: 1em;
24
+ border-bottom: 1px solid #2a2a4a;
25
+ }
26
+ .ultravisor-timing-header h1 {
27
+ margin: 0;
28
+ font-size: 2em;
29
+ font-weight: 300;
30
+ color: #e0e0e0;
31
+ }
32
+ .ultravisor-timing-selector {
33
+ margin-bottom: 1.5em;
34
+ }
35
+ .ultravisor-timing-selector select {
36
+ width: 100%;
37
+ max-width: 600px;
38
+ padding: 0.6em 0.75em;
39
+ }
40
+ .ultravisor-timing-card {
41
+ background: #16213e;
42
+ border: 1px solid #2a2a4a;
43
+ border-radius: 8px;
44
+ padding: 1.5em;
45
+ margin-bottom: 1.5em;
46
+ }
47
+ .ultravisor-timing-summary {
48
+ display: flex;
49
+ flex-wrap: wrap;
50
+ gap: 2em;
51
+ margin-bottom: 1em;
52
+ }
53
+ .ultravisor-timing-stat {
54
+ display: flex;
55
+ flex-direction: column;
56
+ }
57
+ .ultravisor-timing-stat-label {
58
+ font-size: 0.75em;
59
+ text-transform: uppercase;
60
+ letter-spacing: 0.05em;
61
+ color: #9e9ec0;
62
+ margin-bottom: 0.25em;
63
+ }
64
+ .ultravisor-timing-stat-value {
65
+ font-size: 1.2em;
66
+ font-weight: 600;
67
+ color: #e0e0e0;
68
+ }
69
+ .ultravisor-timing-stat-value.complete {
70
+ color: #66bb6a;
71
+ }
72
+ .ultravisor-timing-stat-value.error {
73
+ color: #ef5350;
74
+ }
75
+ .ultravisor-timing-chart {
76
+ margin-top: 1.5em;
77
+ }
78
+ .ultravisor-timing-chart-title {
79
+ font-size: 0.85em;
80
+ text-transform: uppercase;
81
+ letter-spacing: 0.05em;
82
+ color: #9e9ec0;
83
+ margin-bottom: 1em;
84
+ }
85
+ .ultravisor-timing-row {
86
+ display: flex;
87
+ align-items: center;
88
+ margin-bottom: 0.6em;
89
+ }
90
+ .ultravisor-timing-row-label {
91
+ width: 180px;
92
+ flex-shrink: 0;
93
+ font-size: 0.85em;
94
+ color: #b0bec5;
95
+ white-space: nowrap;
96
+ overflow: hidden;
97
+ text-overflow: ellipsis;
98
+ padding-right: 1em;
99
+ }
100
+ .ultravisor-timing-row-bar-container {
101
+ flex: 1;
102
+ height: 28px;
103
+ background: #1a1a2e;
104
+ border-radius: 4px;
105
+ position: relative;
106
+ overflow: hidden;
107
+ }
108
+ .ultravisor-timing-row-bar {
109
+ height: 100%;
110
+ border-radius: 4px;
111
+ min-width: 2px;
112
+ transition: width 0.4s ease;
113
+ display: flex;
114
+ align-items: center;
115
+ padding-left: 0.5em;
116
+ }
117
+ .ultravisor-timing-row-bar.complete {
118
+ background: linear-gradient(90deg, #2e7d32, #43a047);
119
+ }
120
+ .ultravisor-timing-row-bar.error {
121
+ background: linear-gradient(90deg, #c62828, #e53935);
122
+ }
123
+ .ultravisor-timing-row-bar.running {
124
+ background: linear-gradient(90deg, #1565c0, #1e88e5);
125
+ }
126
+ .ultravisor-timing-row-bar.other {
127
+ background: linear-gradient(90deg, #37474f, #546e7a);
128
+ }
129
+ .ultravisor-timing-row-bar-text {
130
+ font-size: 0.75em;
131
+ color: #fff;
132
+ white-space: nowrap;
133
+ text-shadow: 0 1px 2px rgba(0,0,0,0.5);
134
+ }
135
+ .ultravisor-timing-row-duration {
136
+ width: 120px;
137
+ flex-shrink: 0;
138
+ font-size: 0.8em;
139
+ color: #78909c;
140
+ text-align: right;
141
+ padding-left: 0.75em;
142
+ font-family: monospace;
143
+ }
144
+ .ultravisor-timing-axis {
145
+ display: flex;
146
+ align-items: center;
147
+ margin-top: 0.5em;
148
+ padding-left: 180px;
149
+ }
150
+ .ultravisor-timing-axis-line {
151
+ flex: 1;
152
+ display: flex;
153
+ justify-content: space-between;
154
+ border-top: 1px solid #3a3a5c;
155
+ padding-top: 0.35em;
156
+ }
157
+ .ultravisor-timing-axis-tick {
158
+ font-size: 0.7em;
159
+ color: #616161;
160
+ font-family: monospace;
161
+ }
162
+ .ultravisor-timing-axis-spacer {
163
+ width: 120px;
164
+ flex-shrink: 0;
165
+ }
166
+ .ultravisor-timing-empty {
167
+ text-align: center;
168
+ padding: 3em;
169
+ color: #616161;
170
+ font-style: italic;
171
+ }
172
+ `,
173
+
174
+ Templates:
175
+ [
176
+ {
177
+ Hash: "Ultravisor-TimingView-Template",
178
+ Template: /*html*/`
179
+ <div class="ultravisor-timing">
180
+ <div class="ultravisor-timing-header">
181
+ <h1>Timing</h1>
182
+ <button class="ultravisor-btn ultravisor-btn-secondary" onclick="{~P~}.PictApplication.showView('Ultravisor-TimingView')">Refresh</button>
183
+ </div>
184
+ <div class="ultravisor-timing-selector" id="Ultravisor-Timing-Selector"></div>
185
+ <div id="Ultravisor-Timing-Detail"></div>
186
+ </div>
187
+ `
188
+ }
189
+ ],
190
+
191
+ Renderables:
192
+ [
193
+ {
194
+ RenderableHash: "Ultravisor-TimingView-Content",
195
+ TemplateHash: "Ultravisor-TimingView-Template",
196
+ DestinationAddress: "#Ultravisor-Content-Container",
197
+ RenderMethod: "replace"
198
+ }
199
+ ]
200
+ };
201
+
202
+ class UltravisorTimingView extends libPictView
203
+ {
204
+ constructor(pFable, pOptions, pServiceHash)
205
+ {
206
+ super(pFable, pOptions, pServiceHash);
207
+ }
208
+
209
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
210
+ {
211
+ this.pict.PictApplication.loadManifests(
212
+ function ()
213
+ {
214
+ this.renderManifestSelector();
215
+ }.bind(this));
216
+
217
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
218
+ }
219
+
220
+ renderManifestSelector()
221
+ {
222
+ let tmpManifests = this.pict.AppData.Ultravisor.Manifests;
223
+ let tmpViewRef = "_Pict.views['Ultravisor-TimingView']";
224
+
225
+ if (!tmpManifests || tmpManifests.length === 0)
226
+ {
227
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Timing-Selector',
228
+ '<div class="ultravisor-timing-empty">No execution manifests recorded yet. Execute an operation to see timing data here.</div>');
229
+ return;
230
+ }
231
+
232
+ let tmpHTML = '<select id="Ultravisor-Timing-Select" onchange="' + tmpViewRef + '.onSelectManifest(this.value)">';
233
+ tmpHTML += '<option value="">-- Select an execution to visualize --</option>';
234
+
235
+ for (let i = tmpManifests.length - 1; i >= 0; i--)
236
+ {
237
+ let tmpManifest = tmpManifests[i];
238
+ let tmpGUIDRun = tmpManifest.GUIDRun || '';
239
+ let tmpLabel = (tmpManifest.Name || tmpManifest.GUIDOperation || tmpGUIDRun);
240
+ let tmpStatus = tmpManifest.Status || 'Unknown';
241
+ let tmpTime = tmpManifest.StartTime ? tmpManifest.StartTime.replace('T', ' ').replace(/\.\d+Z$/, '') : '';
242
+ tmpHTML += '<option value="' + this.escapeHTML(tmpGUIDRun) + '">' + this.escapeHTML(tmpLabel) + ' [' + tmpStatus + '] ' + tmpTime + '</option>';
243
+ }
244
+
245
+ tmpHTML += '</select>';
246
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Timing-Selector', tmpHTML);
247
+ }
248
+
249
+ onSelectManifest(pGUIDRun)
250
+ {
251
+ if (!pGUIDRun)
252
+ {
253
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Timing-Detail', '');
254
+ return;
255
+ }
256
+
257
+ this.pict.PictApplication.loadManifest(pGUIDRun,
258
+ function (pError, pManifest)
259
+ {
260
+ if (pError || !pManifest)
261
+ {
262
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Timing-Detail',
263
+ '<div class="ultravisor-timing-card"><p style="color:#ef5350;">Error loading manifest details.</p></div>');
264
+ return;
265
+ }
266
+
267
+ this.renderTimingVisualization(pManifest);
268
+ }.bind(this));
269
+ }
270
+
271
+ renderTimingVisualization(pManifest)
272
+ {
273
+ let tmpTaskResults = pManifest.TaskResults || [];
274
+ let tmpOperationElapsedMs = pManifest.ElapsedMs || 0;
275
+ let tmpOperationElapsedFormatted = pManifest.ElapsedFormatted || this.formatMs(tmpOperationElapsedMs);
276
+ let tmpAverageTaskMs = pManifest.AverageTaskMs || 0;
277
+ let tmpStatus = (pManifest.Status || 'Unknown').toLowerCase();
278
+ let tmpStatusClass = (tmpStatus === 'complete' || tmpStatus === 'error' || tmpStatus === 'running') ? tmpStatus : '';
279
+
280
+ // If no operation-level ElapsedMs, compute from task results
281
+ if (tmpOperationElapsedMs <= 0 && tmpTaskResults.length > 0)
282
+ {
283
+ for (let i = 0; i < tmpTaskResults.length; i++)
284
+ {
285
+ tmpOperationElapsedMs += (tmpTaskResults[i].ElapsedMs || 0);
286
+ }
287
+ tmpOperationElapsedFormatted = this.formatMs(tmpOperationElapsedMs);
288
+ }
289
+
290
+ // Compute average if not provided
291
+ if (tmpAverageTaskMs <= 0 && tmpTaskResults.length > 0 && tmpOperationElapsedMs > 0)
292
+ {
293
+ tmpAverageTaskMs = tmpOperationElapsedMs / tmpTaskResults.length;
294
+ }
295
+
296
+ // Find the max task duration for bar scaling
297
+ let tmpMaxTaskMs = 0;
298
+ for (let i = 0; i < tmpTaskResults.length; i++)
299
+ {
300
+ let tmpMs = tmpTaskResults[i].ElapsedMs || 0;
301
+ if (tmpMs > tmpMaxTaskMs)
302
+ {
303
+ tmpMaxTaskMs = tmpMs;
304
+ }
305
+ }
306
+ if (tmpMaxTaskMs <= 0)
307
+ {
308
+ tmpMaxTaskMs = 1;
309
+ }
310
+
311
+ let tmpHTML = '<div class="ultravisor-timing-card">';
312
+
313
+ // Summary header
314
+ tmpHTML += '<div class="ultravisor-timing-summary">';
315
+ tmpHTML += '<div class="ultravisor-timing-stat"><span class="ultravisor-timing-stat-label">Operation</span><span class="ultravisor-timing-stat-value">' + this.escapeHTML(pManifest.Name || pManifest.GUIDOperation || '') + '</span></div>';
316
+ tmpHTML += '<div class="ultravisor-timing-stat"><span class="ultravisor-timing-stat-label">Status</span><span class="ultravisor-timing-stat-value ' + tmpStatusClass + '">' + this.escapeHTML(pManifest.Status || 'Unknown') + '</span></div>';
317
+ tmpHTML += '<div class="ultravisor-timing-stat"><span class="ultravisor-timing-stat-label">Total Time</span><span class="ultravisor-timing-stat-value">' + this.escapeHTML(tmpOperationElapsedFormatted) + '</span></div>';
318
+ tmpHTML += '<div class="ultravisor-timing-stat"><span class="ultravisor-timing-stat-label">Tasks</span><span class="ultravisor-timing-stat-value">' + tmpTaskResults.length + '</span></div>';
319
+ tmpHTML += '<div class="ultravisor-timing-stat"><span class="ultravisor-timing-stat-label">Avg per Task</span><span class="ultravisor-timing-stat-value">' + this.formatMs(tmpAverageTaskMs) + '</span></div>';
320
+ tmpHTML += '</div>';
321
+
322
+ // Timeline chart
323
+ if (tmpTaskResults.length === 0)
324
+ {
325
+ tmpHTML += '<div class="ultravisor-timing-empty">No task results in this manifest.</div>';
326
+ }
327
+ else
328
+ {
329
+ tmpHTML += '<div class="ultravisor-timing-chart">';
330
+ tmpHTML += '<div class="ultravisor-timing-chart-title">Task Timeline</div>';
331
+
332
+ for (let i = 0; i < tmpTaskResults.length; i++)
333
+ {
334
+ let tmpResult = tmpTaskResults[i];
335
+ let tmpTaskMs = tmpResult.ElapsedMs || 0;
336
+ let tmpTaskFormatted = tmpResult.ElapsedFormatted || this.formatMs(tmpTaskMs);
337
+ let tmpTaskStatus = (tmpResult.Status || 'Unknown').toLowerCase();
338
+ let tmpBarClass = 'other';
339
+ if (tmpTaskStatus === 'complete')
340
+ {
341
+ tmpBarClass = 'complete';
342
+ }
343
+ else if (tmpTaskStatus === 'error')
344
+ {
345
+ tmpBarClass = 'error';
346
+ }
347
+ else if (tmpTaskStatus === 'running')
348
+ {
349
+ tmpBarClass = 'running';
350
+ }
351
+
352
+ let tmpWidthPercent = (tmpMaxTaskMs > 0) ? Math.max((tmpTaskMs / tmpMaxTaskMs) * 100, 1) : 1;
353
+ let tmpBarLabel = this.escapeHTML(tmpResult.Name || tmpResult.GUIDTask || '');
354
+
355
+ tmpHTML += '<div class="ultravisor-timing-row">';
356
+ tmpHTML += '<div class="ultravisor-timing-row-label" title="' + this.escapeHTML(tmpResult.GUIDTask || '') + '">' + this.escapeHTML(tmpResult.Name || tmpResult.GUIDTask || 'Task ' + (i + 1)) + '</div>';
357
+ tmpHTML += '<div class="ultravisor-timing-row-bar-container">';
358
+ tmpHTML += '<div class="ultravisor-timing-row-bar ' + tmpBarClass + '" style="width: ' + tmpWidthPercent.toFixed(1) + '%;">';
359
+ if (tmpWidthPercent > 20)
360
+ {
361
+ tmpHTML += '<span class="ultravisor-timing-row-bar-text">' + tmpBarLabel + '</span>';
362
+ }
363
+ tmpHTML += '</div>';
364
+ tmpHTML += '</div>';
365
+ tmpHTML += '<div class="ultravisor-timing-row-duration">' + this.escapeHTML(tmpTaskFormatted) + '</div>';
366
+ tmpHTML += '</div>';
367
+ }
368
+
369
+ // Time axis
370
+ tmpHTML += '<div class="ultravisor-timing-axis">';
371
+ tmpHTML += '<div class="ultravisor-timing-axis-line">';
372
+ let tmpTickCount = 5;
373
+ for (let t = 0; t <= tmpTickCount; t++)
374
+ {
375
+ let tmpTickMs = (tmpMaxTaskMs / tmpTickCount) * t;
376
+ tmpHTML += '<span class="ultravisor-timing-axis-tick">' + this.formatMs(tmpTickMs) + '</span>';
377
+ }
378
+ tmpHTML += '</div>';
379
+ tmpHTML += '<div class="ultravisor-timing-axis-spacer"></div>';
380
+ tmpHTML += '</div>';
381
+
382
+ tmpHTML += '</div>';
383
+ }
384
+
385
+ tmpHTML += '</div>';
386
+
387
+ this.pict.ContentAssignment.assignContent('#Ultravisor-Timing-Detail', tmpHTML);
388
+ }
389
+
390
+ formatMs(pMs)
391
+ {
392
+ if (typeof pMs !== 'number' || pMs <= 0)
393
+ {
394
+ return '0ms';
395
+ }
396
+ if (pMs < 1000)
397
+ {
398
+ return Math.round(pMs) + 'ms';
399
+ }
400
+ if (pMs < 60000)
401
+ {
402
+ let tmpSeconds = Math.floor(pMs / 1000);
403
+ let tmpMs = Math.round(pMs % 1000);
404
+ return tmpSeconds + 's ' + tmpMs + 'ms';
405
+ }
406
+ let tmpMinutes = Math.floor(pMs / 60000);
407
+ let tmpSeconds = Math.floor((pMs % 60000) / 1000);
408
+ return tmpMinutes + 'm ' + tmpSeconds + 's';
409
+ }
410
+
411
+ escapeHTML(pValue)
412
+ {
413
+ if (!pValue) return '';
414
+ return String(pValue).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
415
+ }
416
+ }
417
+
418
+ module.exports = UltravisorTimingView;
419
+
420
+ module.exports.default_configuration = _ViewConfiguration;
@@ -0,0 +1,147 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "Ultravisor-TopBar",
6
+
7
+ DefaultRenderable: "Ultravisor-TopBar-Content",
8
+ DefaultDestinationAddress: "#Ultravisor-TopBar-Container",
9
+
10
+ AutoRender: false,
11
+
12
+ CSS: /*css*/`
13
+ .ultravisor-topbar {
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: space-between;
17
+ background-color: #0f3460;
18
+ color: #e0e0e0;
19
+ padding: 0 1.5em;
20
+ height: 56px;
21
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
22
+ position: sticky;
23
+ top: 0;
24
+ z-index: 100;
25
+ }
26
+ .ultravisor-topbar-brand {
27
+ font-size: 1.25em;
28
+ font-weight: 700;
29
+ letter-spacing: 0.04em;
30
+ color: #4fc3f7;
31
+ text-decoration: none;
32
+ cursor: pointer;
33
+ }
34
+ .ultravisor-topbar-brand:hover {
35
+ color: #81d4fa;
36
+ }
37
+ .ultravisor-topbar-nav {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 0.15em;
41
+ }
42
+ .ultravisor-topbar-nav a {
43
+ color: #b0bec5;
44
+ text-decoration: none;
45
+ padding: 0.5em 0.75em;
46
+ border-radius: 4px;
47
+ font-size: 0.9em;
48
+ transition: background-color 0.15s, color 0.15s;
49
+ cursor: pointer;
50
+ }
51
+ .ultravisor-topbar-nav a:hover {
52
+ background-color: #1a4a7a;
53
+ color: #fff;
54
+ }
55
+ .ultravisor-topbar-status {
56
+ display: flex;
57
+ align-items: center;
58
+ gap: 0.5em;
59
+ font-size: 0.8em;
60
+ }
61
+ .ultravisor-status-dot {
62
+ width: 8px;
63
+ height: 8px;
64
+ border-radius: 50%;
65
+ background-color: #616161;
66
+ display: inline-block;
67
+ }
68
+ .ultravisor-status-dot.connected {
69
+ background-color: #66bb6a;
70
+ }
71
+ .ultravisor-status-dot.error {
72
+ background-color: #ef5350;
73
+ }
74
+ `,
75
+
76
+ Templates:
77
+ [
78
+ {
79
+ Hash: "Ultravisor-TopBar-Template",
80
+ Template: /*html*/`
81
+ <div class="ultravisor-topbar">
82
+ <a class="ultravisor-topbar-brand" onclick="{~P~}.PictApplication.navigateTo('/Home')">Ultravisor</a>
83
+ <div class="ultravisor-topbar-nav">
84
+ <a onclick="{~P~}.PictApplication.navigateTo('/Home')">Dashboard</a>
85
+ <a onclick="{~P~}.PictApplication.navigateTo('/Tasks')">Tasks</a>
86
+ <a onclick="{~P~}.PictApplication.navigateTo('/Operations')">Operations</a>
87
+ <a onclick="{~P~}.PictApplication.navigateTo('/Schedule')">Schedule</a>
88
+ <a onclick="{~P~}.PictApplication.navigateTo('/Manifests')">Manifests</a>
89
+ <a onclick="{~P~}.PictApplication.navigateTo('/Timing')">Timing</a>
90
+ </div>
91
+ <div class="ultravisor-topbar-status" id="Ultravisor-TopBar-StatusArea"></div>
92
+ </div>
93
+ `
94
+ },
95
+ {
96
+ Hash: "Ultravisor-TopBar-Status-Template",
97
+ Template: /*html*/`<span class="ultravisor-status-dot {~D:AppData.Ultravisor.ServerStatus.StatusClass~}"></span><span>{~D:AppData.Ultravisor.ServerStatus.StatusText~}</span>`
98
+ }
99
+ ],
100
+
101
+ Renderables:
102
+ [
103
+ {
104
+ RenderableHash: "Ultravisor-TopBar-Content",
105
+ TemplateHash: "Ultravisor-TopBar-Template",
106
+ DestinationAddress: "#Ultravisor-TopBar-Container",
107
+ RenderMethod: "replace"
108
+ }
109
+ ]
110
+ };
111
+
112
+ class UltravisorTopBarView extends libPictView
113
+ {
114
+ constructor(pFable, pOptions, pServiceHash)
115
+ {
116
+ super(pFable, pOptions, pServiceHash);
117
+ }
118
+
119
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
120
+ {
121
+ // Check server status and update the status indicator
122
+ this.pict.PictApplication.loadStatus(
123
+ function (pError)
124
+ {
125
+ let tmpStatus = this.pict.AppData.Ultravisor.ServerStatus;
126
+ if (pError)
127
+ {
128
+ tmpStatus.StatusClass = 'error';
129
+ tmpStatus.StatusText = 'Disconnected';
130
+ }
131
+ else
132
+ {
133
+ tmpStatus.StatusClass = 'connected';
134
+ tmpStatus.StatusText = tmpStatus.Status || 'Connected';
135
+ }
136
+
137
+ let tmpContent = this.pict.parseTemplateByHash('Ultravisor-TopBar-Status-Template', {}, null, this.pict);
138
+ this.pict.ContentAssignment.assignContent('#Ultravisor-TopBar-StatusArea', tmpContent);
139
+ }.bind(this));
140
+
141
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
142
+ }
143
+ }
144
+
145
+ module.exports = UltravisorTopBarView;
146
+
147
+ module.exports.default_configuration = _ViewConfiguration;