claw-dashboard 1.9.0 → 2.0.0

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 (135) hide show
  1. package/.c8rc.json +38 -0
  2. package/.dockerignore +68 -0
  3. package/.github/dependabot.yml +45 -0
  4. package/.github/pull_request_template.md +39 -0
  5. package/.github/workflows/ci.yml +147 -0
  6. package/.github/workflows/release.yml +40 -0
  7. package/.github/workflows/security.yml +84 -0
  8. package/.husky/pre-commit +1 -0
  9. package/.planning/codebase/ARCHITECTURE.md +206 -0
  10. package/.planning/codebase/INTEGRATIONS.md +150 -0
  11. package/.planning/codebase/STACK.md +122 -0
  12. package/.planning/codebase/STRUCTURE.md +201 -0
  13. package/CHANGELOG.md +293 -0
  14. package/CONTRIBUTING.md +378 -0
  15. package/Dockerfile +54 -0
  16. package/FEATURES.md +195 -21
  17. package/README.md +83 -6
  18. package/TODO.md +28 -0
  19. package/build-cjs.js +127 -0
  20. package/cjs-shim.js +13 -0
  21. package/dist/clawdash +1729 -0
  22. package/dist/clawdash.meta.json +2236 -0
  23. package/dist/widgets.cjs +6511 -0
  24. package/docker-compose.yml +67 -0
  25. package/docs/API.md +1050 -0
  26. package/docs/PLUGINS.md +1504 -0
  27. package/esbuild.config.js +158 -0
  28. package/eslint.config.js +56 -0
  29. package/examples/plugins/README.md +122 -0
  30. package/examples/plugins/api-status/index.js +294 -0
  31. package/examples/plugins/api-status/plugin.json +19 -0
  32. package/examples/plugins/hello-world/index.js +104 -0
  33. package/examples/plugins/hello-world/plugin.json +15 -0
  34. package/examples/plugins/system-metrics-chart/index.js +339 -0
  35. package/examples/plugins/system-metrics-chart/plugin.json +18 -0
  36. package/examples/plugins/weather-widget/index.js +136 -0
  37. package/examples/plugins/weather-widget/plugin.json +19 -0
  38. package/index.cjs +23005 -0
  39. package/index.js +5236 -566
  40. package/jest.config.js +11 -0
  41. package/man/clawdash.1 +276 -0
  42. package/package.json +54 -5
  43. package/schemas/plugin-manifest.json +128 -0
  44. package/scripts/release.js +595 -0
  45. package/src/alerts.js +693 -0
  46. package/src/auto-save.js +584 -0
  47. package/src/cache.js +390 -0
  48. package/src/checksum.js +146 -0
  49. package/src/cli/args.js +110 -0
  50. package/src/cli/export-schedule.js +423 -0
  51. package/src/cli/export-snapshot.js +138 -0
  52. package/src/cli/help.js +69 -0
  53. package/src/cli/import-snapshot.js +230 -0
  54. package/src/cli/index.js +14 -0
  55. package/src/cli/list-templates.js +69 -0
  56. package/src/cli/validate-config.js +76 -0
  57. package/src/cli/validate-plugin.js +187 -0
  58. package/src/cli/version.js +15 -0
  59. package/src/config-validator.js +586 -0
  60. package/src/config-watcher.js +401 -0
  61. package/src/config.js +684 -0
  62. package/src/container-detector.js +499 -0
  63. package/src/database.js +734 -0
  64. package/src/differential-render.js +327 -0
  65. package/src/errors.js +169 -0
  66. package/src/export-scheduler.js +581 -0
  67. package/src/gateway-manager.js +685 -0
  68. package/src/hints.js +371 -0
  69. package/src/loading-states.js +509 -0
  70. package/src/logger.js +185 -0
  71. package/src/memory-pressure.js +467 -0
  72. package/src/performance-monitor.js +374 -0
  73. package/src/plugin-errors.js +586 -0
  74. package/src/plugin-manifest-validator.js +219 -0
  75. package/src/plugin-reload.js +481 -0
  76. package/src/plugin-scaffold.js +1941 -0
  77. package/src/plugin-validator.js +284 -0
  78. package/src/retry.js +218 -0
  79. package/src/security.js +860 -0
  80. package/src/snapshot.js +372 -0
  81. package/src/splash.js +115 -0
  82. package/src/theme-selector.js +411 -0
  83. package/src/themes.js +874 -0
  84. package/src/transitions.js +534 -0
  85. package/src/types.d.ts +174 -0
  86. package/src/validation.js +926 -0
  87. package/src/web-server.js +885 -0
  88. package/src/widgets/builtin-widgets.js +1057 -0
  89. package/src/widgets/config-processor.js +401 -0
  90. package/src/widgets/dependency-resolver.js +596 -0
  91. package/src/widgets/index.js +79 -0
  92. package/src/widgets/plugin-api.js +845 -0
  93. package/src/widgets/widget-error-boundary.js +773 -0
  94. package/src/widgets/widget-error-isolation.js +551 -0
  95. package/src/widgets/widget-loader.js +1437 -0
  96. package/src/workers/system-worker.js +130 -0
  97. package/src/workers/worker-pool.js +633 -0
  98. package/tests/alerts.test.js +437 -0
  99. package/tests/auto-save.test.js +529 -0
  100. package/tests/cache.test.js +317 -0
  101. package/tests/cli.test.js +351 -0
  102. package/tests/command-palette.test.js +320 -0
  103. package/tests/config-processor.test.js +452 -0
  104. package/tests/config-validator.test.js +710 -0
  105. package/tests/config-watcher.test.js +594 -0
  106. package/tests/config.test.js +755 -0
  107. package/tests/database.test.js +438 -0
  108. package/tests/errors.test.js +189 -0
  109. package/tests/example-plugins.test.js +624 -0
  110. package/tests/integration.test.js +1321 -0
  111. package/tests/loading-states.test.js +300 -0
  112. package/tests/manifest-validation-on-load.test.js +671 -0
  113. package/tests/performance-monitor.test.js +190 -0
  114. package/tests/plugin-api-rate-limit.test.js +302 -0
  115. package/tests/plugin-errors.test.js +311 -0
  116. package/tests/plugin-lifecycle-e2e.test.js +1036 -0
  117. package/tests/plugin-reload.test.js +764 -0
  118. package/tests/rate-limiter.test.js +539 -0
  119. package/tests/retry.test.js +308 -0
  120. package/tests/security.test.js +411 -0
  121. package/tests/settings-modal.test.js +226 -0
  122. package/tests/theme-selector.test.js +57 -0
  123. package/tests/utils.js +242 -0
  124. package/tests/utils.test.js +317 -0
  125. package/tests/validate-plugin-cli.test.js +359 -0
  126. package/tests/validation.test.js +837 -0
  127. package/tests/web-server.test.js +646 -0
  128. package/tests/widget-arrange-mode.test.js +183 -0
  129. package/tests/widget-config-hot-reload.test.js +465 -0
  130. package/tests/widget-dependency.test.js +591 -0
  131. package/tests/widget-error-boundary.test.js +749 -0
  132. package/tests/widget-error-isolation.test.js +469 -0
  133. package/tests/widget-integration.test.js +1147 -0
  134. package/tests/widget-refresh-intervals.test.js +284 -0
  135. package/tests/worker-pool.test.js +522 -0
@@ -0,0 +1,534 @@
1
+ /**
2
+ * View Transitions Module
3
+ * Provides smooth animations for modal views (fade, slide, scale)
4
+ */
5
+
6
+ import blessed from 'blessed';
7
+
8
+ /**
9
+ * Easing functions for animations
10
+ */
11
+ const EASING = {
12
+ linear: t => t,
13
+ easeIn: t => t * t,
14
+ easeOut: t => 1 - (1 - t) * (1 - t),
15
+ easeInOut: t => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,
16
+ spring: t => {
17
+ const c4 = (2 * Math.PI) / 3;
18
+ return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
19
+ }
20
+ };
21
+
22
+ /**
23
+ * Default transition options
24
+ */
25
+ const DEFAULT_OPTIONS = {
26
+ duration: 200, // Animation duration in ms
27
+ easing: 'easeOut', // Easing function name
28
+ fade: true, // Fade opacity
29
+ slide: false, // Slide from direction
30
+ scale: false, // Scale effect
31
+ slideDirection: 'up', // 'up', 'down', 'left', 'right'
32
+ fadeBackground: true // Fade background opacity
33
+ };
34
+
35
+ /**
36
+ * Active animations map (to handle cleanup)
37
+ * @type {Map<string, {stop: Function}>}
38
+ */
39
+ const activeAnimations = new Map();
40
+
41
+ /**
42
+ * Create a background overlay for modal dialogs
43
+ * @param {blessed.Screen} screen
44
+ * @param {Object} options
45
+ * @returns {blessed.Box}
46
+ */
47
+ export function createBackground(screen, options = {}) {
48
+ const bg = blessed.box({
49
+ parent: screen,
50
+ top: 0,
51
+ left: 0,
52
+ width: '100%',
53
+ height: '100%',
54
+ style: {
55
+ bg: 'black'
56
+ },
57
+ transparent: true
58
+ });
59
+
60
+ // Set initial opacity based on options
61
+ const opacity = options.fadeBackground !== false ? 0 : 0.4;
62
+ bg.style.transparent = true;
63
+ bg._targetOpacity = options.backgroundOpacity || 0.4;
64
+ bg._currentOpacity = opacity;
65
+
66
+ return bg;
67
+ }
68
+
69
+ /**
70
+ * Animate a value over time
71
+ * @param {Object} config
72
+ * @param {number} config.from - Start value
73
+ * @param {number} config.to - End value
74
+ * @param {number} config.duration - Duration in ms
75
+ * @param {string} config.easing - Easing function name
76
+ * @param {Function} config.onUpdate - Called with current value
77
+ * @param {Function} config.onComplete - Called when animation completes
78
+ * @returns {{stop: Function}} Animation controller
79
+ */
80
+ export function animate({
81
+ from,
82
+ to,
83
+ duration = 200,
84
+ easing = 'easeOut',
85
+ onUpdate,
86
+ onComplete
87
+ }) {
88
+ const easeFn = EASING[easing] || EASING.easeOut;
89
+ const startTime = Date.now();
90
+ let animationId = null;
91
+ let stopped = false;
92
+
93
+ const step = () => {
94
+ if (stopped) return;
95
+
96
+ const elapsed = Date.now() - startTime;
97
+ const progress = Math.min(elapsed / duration, 1);
98
+ const easedProgress = easeFn(progress);
99
+ const currentValue = from + (to - from) * easedProgress;
100
+
101
+ onUpdate(currentValue);
102
+
103
+ if (progress < 1) {
104
+ // Use setImmediate for next frame to avoid blocking
105
+ animationId = setImmediate(step);
106
+ } else {
107
+ onComplete?.();
108
+ }
109
+ };
110
+
111
+ // Start animation
112
+ animationId = setImmediate(step);
113
+
114
+ return {
115
+ stop: () => {
116
+ stopped = true;
117
+ if (animationId) {
118
+ clearImmediate(animationId);
119
+ }
120
+ }
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Transition a widget in (show with animation)
126
+ * @param {blessed.Screen} screen
127
+ * @param {blessed.Box} widget
128
+ * @param {Object} options
129
+ * @returns {Promise<void>}
130
+ */
131
+ export function transitionIn(screen, widget, options = {}) {
132
+ const opts = { ...DEFAULT_OPTIONS, ...options };
133
+ const animationId = `in_${widget.uid || Math.random().toString(36).substr(2, 9)}`;
134
+
135
+ // Stop any existing animation on this widget
136
+ if (activeAnimations.has(animationId)) {
137
+ activeAnimations.get(animationId).stop();
138
+ }
139
+
140
+ return new Promise((resolve) => {
141
+ const animations = [];
142
+ const originalTop = widget.top;
143
+ const originalLeft = widget.left;
144
+ const originalWidth = widget.width;
145
+ const originalHeight = widget.height;
146
+
147
+ // Store original position for restore
148
+ widget._originalPosition = {
149
+ top: originalTop,
150
+ left: originalLeft,
151
+ width: originalWidth,
152
+ height: originalHeight
153
+ };
154
+
155
+ // Fade animation
156
+ if (opts.fade) {
157
+ widget.style.transparent = true;
158
+ widget._opacity = 0;
159
+
160
+ const fadeAnim = animate({
161
+ from: 0,
162
+ to: 1,
163
+ duration: opts.duration,
164
+ easing: opts.easing,
165
+ onUpdate: (value) => {
166
+ widget._opacity = value;
167
+ // blessed doesn't support true opacity, so we simulate it via style
168
+ widget.style.alpha = value;
169
+ widget.style.transparent = value < 0.1;
170
+ }
171
+ });
172
+ animations.push(fadeAnim);
173
+ }
174
+
175
+ // Slide animation
176
+ if (opts.slide) {
177
+ let fromTop = originalTop;
178
+ let fromLeft = originalLeft;
179
+ const slideDistance = 20;
180
+
181
+ switch (opts.slideDirection) {
182
+ case 'up':
183
+ fromTop = originalTop + slideDistance;
184
+ break;
185
+ case 'down':
186
+ fromTop = originalTop - slideDistance;
187
+ break;
188
+ case 'left':
189
+ fromLeft = originalLeft + slideDistance;
190
+ break;
191
+ case 'right':
192
+ fromLeft = originalLeft - slideDistance;
193
+ break;
194
+ }
195
+
196
+ widget.top = fromTop;
197
+ widget.left = fromLeft;
198
+
199
+ const slideAnim = animate({
200
+ from: 0,
201
+ to: 1,
202
+ duration: opts.duration,
203
+ easing: opts.easing,
204
+ onUpdate: (value) => {
205
+ widget.top = fromTop + (originalTop - fromTop) * value;
206
+ widget.left = fromLeft + (originalLeft - fromLeft) * value;
207
+ screen.render();
208
+ }
209
+ });
210
+ animations.push(slideAnim);
211
+ }
212
+
213
+ // Scale animation (simulated via size changes)
214
+ if (opts.scale) {
215
+ const parseDim = (dim) => {
216
+ if (typeof dim === 'string' && dim.includes('%')) {
217
+ return { value: parseInt(dim), unit: '%' };
218
+ }
219
+ return { value: parseInt(dim) || 10, unit: typeof dim === 'string' && dim.includes('%') ? '%' : '' };
220
+ };
221
+
222
+ const origW = parseDim(originalWidth);
223
+ const origH = parseDim(originalHeight);
224
+
225
+ // Start from smaller size
226
+ const startScale = 0.9;
227
+ const currentW = Math.round(origW.value * startScale);
228
+ const currentH = Math.round(origH.value * startScale);
229
+
230
+ widget.width = currentW + origW.unit;
231
+ widget.height = currentH + origH.unit;
232
+
233
+ const scaleAnim = animate({
234
+ from: startScale,
235
+ to: 1,
236
+ duration: opts.duration,
237
+ easing: opts.easing,
238
+ onUpdate: (value) => {
239
+ const newW = Math.round(origW.value * value);
240
+ const newH = Math.round(origH.value * value);
241
+ widget.width = newW + origW.unit;
242
+ widget.height = newH + origH.unit;
243
+ screen.render();
244
+ },
245
+ onComplete: () => {
246
+ widget.width = originalWidth;
247
+ widget.height = originalHeight;
248
+ }
249
+ });
250
+ animations.push(scaleAnim);
251
+ }
252
+
253
+ // Background fade
254
+ let bgAnim = null;
255
+ if (opts.fadeBackground && opts.background) {
256
+ bgAnim = animate({
257
+ from: 0,
258
+ to: opts.background._targetOpacity || 0.4,
259
+ duration: opts.duration,
260
+ easing: 'linear',
261
+ onUpdate: (value) => {
262
+ // Simulate opacity via character density or just use a flag
263
+ opts.background._currentOpacity = value;
264
+ opts.background.style.alpha = value;
265
+ }
266
+ });
267
+ }
268
+
269
+ // Cleanup and resolve when done
270
+ setTimeout(() => {
271
+ animations.forEach(a => a.stop());
272
+ if (bgAnim) bgAnim.stop();
273
+ activeAnimations.delete(animationId);
274
+
275
+ // Ensure final state
276
+ widget.top = originalTop;
277
+ widget.left = originalLeft;
278
+ widget.width = originalWidth;
279
+ widget.height = originalHeight;
280
+ widget.style.transparent = false;
281
+ widget.style.alpha = 1;
282
+
283
+ screen.render();
284
+ resolve();
285
+ }, opts.duration);
286
+
287
+ activeAnimations.set(animationId, {
288
+ stop: () => {
289
+ animations.forEach(a => a.stop());
290
+ if (bgAnim) bgAnim.stop();
291
+ activeAnimations.delete(animationId);
292
+ }
293
+ });
294
+
295
+ // Render to show initial state
296
+ screen.render();
297
+ });
298
+ }
299
+
300
+ /**
301
+ * Transition a widget out (hide with animation)
302
+ * @param {blessed.Screen} screen
303
+ * @param {blessed.Box} widget
304
+ * @param {Object} options
305
+ * @returns {Promise<void>}
306
+ */
307
+ export function transitionOut(screen, widget, options = {}) {
308
+ if (!widget || widget.destroyed) {
309
+ return Promise.resolve();
310
+ }
311
+
312
+ const opts = { ...DEFAULT_OPTIONS, ...options };
313
+ const animationId = `out_${widget.uid || Math.random().toString(36).substr(2, 9)}`;
314
+
315
+ // Stop any existing animation on this widget
316
+ if (activeAnimations.has(animationId)) {
317
+ activeAnimations.get(animationId).stop();
318
+ }
319
+
320
+ return new Promise((resolve) => {
321
+ const animations = [];
322
+ const originalPosition = widget._originalPosition || {
323
+ top: widget.top,
324
+ left: widget.left,
325
+ width: widget.width,
326
+ height: widget.height
327
+ };
328
+
329
+ // Fade animation
330
+ if (opts.fade) {
331
+ const fadeAnim = animate({
332
+ from: 1,
333
+ to: 0,
334
+ duration: opts.duration,
335
+ easing: opts.easing,
336
+ onUpdate: (value) => {
337
+ widget.style.alpha = value;
338
+ widget.style.transparent = value < 0.1;
339
+ screen.render();
340
+ }
341
+ });
342
+ animations.push(fadeAnim);
343
+ }
344
+
345
+ // Slide animation
346
+ if (opts.slide) {
347
+ let toTop = originalPosition.top;
348
+ let toLeft = originalPosition.left;
349
+ const slideDistance = 20;
350
+
351
+ switch (opts.slideDirection) {
352
+ case 'up':
353
+ toTop = originalPosition.top - slideDistance;
354
+ break;
355
+ case 'down':
356
+ toTop = originalPosition.top + slideDistance;
357
+ break;
358
+ case 'left':
359
+ toLeft = originalPosition.left - slideDistance;
360
+ break;
361
+ case 'right':
362
+ toLeft = originalPosition.left + slideDistance;
363
+ break;
364
+ }
365
+
366
+ const slideAnim = animate({
367
+ from: 0,
368
+ to: 1,
369
+ duration: opts.duration,
370
+ easing: opts.easing,
371
+ onUpdate: (value) => {
372
+ widget.top = originalPosition.top + (toTop - originalPosition.top) * value;
373
+ widget.left = originalPosition.left + (toLeft - originalPosition.left) * value;
374
+ screen.render();
375
+ }
376
+ });
377
+ animations.push(slideAnim);
378
+ }
379
+
380
+ // Scale animation
381
+ if (opts.scale) {
382
+ const parseDim = (dim) => {
383
+ if (typeof dim === 'string' && dim.includes('%')) {
384
+ return { value: parseInt(dim), unit: '%' };
385
+ }
386
+ return { value: parseInt(dim) || 10, unit: typeof dim === 'string' && dim.includes('%') ? '%' : '' };
387
+ };
388
+
389
+ const origW = parseDim(originalPosition.width);
390
+ const origH = parseDim(originalPosition.height);
391
+
392
+ const endScale = 0.9;
393
+
394
+ const scaleAnim = animate({
395
+ from: 1,
396
+ to: endScale,
397
+ duration: opts.duration,
398
+ easing: opts.easing,
399
+ onUpdate: (value) => {
400
+ const newW = Math.round(origW.value * value);
401
+ const newH = Math.round(origH.value * value);
402
+ widget.width = newW + origW.unit;
403
+ widget.height = newH + origH.unit;
404
+ screen.render();
405
+ }
406
+ });
407
+ animations.push(scaleAnim);
408
+ }
409
+
410
+ // Background fade
411
+ let bgAnim = null;
412
+ if (opts.fadeBackground && opts.background) {
413
+ const startOpacity = opts.background._currentOpacity || 0.4;
414
+ bgAnim = animate({
415
+ from: startOpacity,
416
+ to: 0,
417
+ duration: opts.duration,
418
+ easing: 'linear',
419
+ onUpdate: (value) => {
420
+ opts.background._currentOpacity = value;
421
+ opts.background.style.alpha = value;
422
+ screen.render();
423
+ }
424
+ });
425
+ }
426
+
427
+ // Cleanup when done
428
+ setTimeout(() => {
429
+ animations.forEach(a => a.stop());
430
+ if (bgAnim) bgAnim.stop();
431
+ activeAnimations.delete(animationId);
432
+ resolve();
433
+ }, opts.duration);
434
+
435
+ activeAnimations.set(animationId, {
436
+ stop: () => {
437
+ animations.forEach(a => a.stop());
438
+ if (bgAnim) bgAnim.stop();
439
+ activeAnimations.delete(animationId);
440
+ }
441
+ });
442
+ });
443
+ }
444
+
445
+ /**
446
+ * Quick fade in/out utility for simple cases
447
+ * @param {blessed.Screen} screen
448
+ * @param {blessed.Box} widget
449
+ * @param {boolean} show - true to fade in, false to fade out
450
+ * @param {number} duration
451
+ * @returns {Promise<void>}
452
+ */
453
+ export function quickFade(screen, widget, show, duration = 150) {
454
+ if (!widget || widget.destroyed) return Promise.resolve();
455
+
456
+ return new Promise((resolve) => {
457
+ const from = show ? 0 : 1;
458
+ const to = show ? 1 : 0;
459
+
460
+ animate({
461
+ from,
462
+ to,
463
+ duration,
464
+ easing: 'easeOut',
465
+ onUpdate: (value) => {
466
+ widget.style.alpha = value;
467
+ widget.style.transparent = value < 0.1;
468
+ screen.render();
469
+ },
470
+ onComplete: () => {
471
+ if (!show) {
472
+ widget.hide();
473
+ } else {
474
+ widget.show();
475
+ widget.style.alpha = 1;
476
+ widget.style.transparent = false;
477
+ }
478
+ screen.render();
479
+ resolve();
480
+ }
481
+ });
482
+ });
483
+ }
484
+
485
+ /**
486
+ * Staggered animation for lists of items
487
+ * @param {blessed.Screen} screen
488
+ * @param {Array<blessed.Box>} items
489
+ * @param {boolean} show
490
+ * @param {Object} options
491
+ * @returns {Promise<void>}
492
+ */
493
+ export function staggeredFade(screen, items, show, options = {}) {
494
+ const delay = options.staggerDelay || 30;
495
+ const duration = options.duration || 100;
496
+
497
+ const promises = items.map((item, index) => {
498
+ return new Promise((resolve) => {
499
+ setTimeout(() => {
500
+ quickFade(screen, item, show, duration).then(resolve);
501
+ }, index * delay);
502
+ });
503
+ });
504
+
505
+ return Promise.all(promises);
506
+ }
507
+
508
+ /**
509
+ * Check if any animation is currently running
510
+ * @returns {boolean}
511
+ */
512
+ export function isAnimating() {
513
+ return activeAnimations.size > 0;
514
+ }
515
+
516
+ /**
517
+ * Stop all active animations
518
+ */
519
+ export function stopAll() {
520
+ activeAnimations.forEach(anim => anim.stop());
521
+ activeAnimations.clear();
522
+ }
523
+
524
+ export default {
525
+ animate,
526
+ transitionIn,
527
+ transitionOut,
528
+ quickFade,
529
+ staggeredFade,
530
+ createBackground,
531
+ isAnimating,
532
+ stopAll,
533
+ EASING
534
+ };
package/src/types.d.ts ADDED
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Claw Dashboard Type Definitions
3
+ * ProvidesScript-like Type type hints for JavaScript code via JSDoc
4
+ * IDEs like VS Code will use these for autocomplete and type checking
5
+ */
6
+
7
+ /**
8
+ * @typedef {Object} Session
9
+ * @property {string} key - Unique session identifier
10
+ * @property {string} name - Session name
11
+ * @property {string} status - Session status (active, idle, etc.)
12
+ * @property {number} tokens - Current token count
13
+ * @property {number} totalTokens - Total tokens used
14
+ * @property {number} startTime - Session start timestamp
15
+ * @property {number} lastActive - Last activity timestamp
16
+ * @property {string} [agentName] - Associated agent name
17
+ * @property {boolean} [favorite] - Whether session is favorited
18
+ */
19
+
20
+ /**
21
+ * @typedef {Object} CPUData
22
+ * @property {number[]} current - Current CPU usage per core
23
+ * @property {number} avg - Average CPU usage
24
+ * @property {string} model - CPU model name
25
+ */
26
+
27
+ /**
28
+ * @typedef {Object} MemoryData
29
+ * @property {number} total - Total memory in bytes
30
+ * @property {number} used - Used memory in bytes
31
+ * @property {number} free - Free memory in bytes
32
+ * @property {number} percent - Usage percentage
33
+ * @property {string} usedGB - Formatted used memory
34
+ * @property {string} totalGB - Formatted total memory
35
+ */
36
+
37
+ /**
38
+ * @typedef {Object} GPUData
39
+ * @property {string} short - Short GPU info string
40
+ * @property {string} long - Long GPU info string
41
+ * @property {number} [percent] - GPU usage percentage
42
+ * @property {number} [memory] - GPU memory usage
43
+ */
44
+
45
+ /**
46
+ * @typedef {Object} NetworkData
47
+ * @property {string} iface - Network interface name
48
+ * @property {number} rx - Receive bytes
49
+ * @property {number} tx - Transmit bytes
50
+ * @property {number} rx_sec - Receive bytes per second
51
+ * @property {number} tx_sec - Transmit bytes per second
52
+ * @property {string} rx_rate - Formatted receive rate
53
+ * @property {string} tx_rate - Formatted transmit rate
54
+ */
55
+
56
+ /**
57
+ * @typedef {Object} DiskData
58
+ * @property {string} mount - Mount point
59
+ * @property {string} fs - Filesystem type
60
+ * @property {number} size - Total size in bytes
61
+ * @property {number} used - Used space in bytes
62
+ * @property {number} available - Available space in bytes
63
+ * @property {number} percent - Usage percentage
64
+ * @property {string} usedGB - Formatted used space
65
+ * @property {string} totalGB - Formatted total space
66
+ */
67
+
68
+ /**
69
+ * @typedef {Object} SystemData
70
+ * @property {string} os - OS name
71
+ * @property {string} version - OS version
72
+ * @property {string} arch - Architecture
73
+ * @property {string} hostname - Hostname
74
+ */
75
+
76
+ /**
77
+ * @typedef {Object} DashboardData
78
+ * @property {CPUData} cpu - CPU data
79
+ * @property {MemoryData} memory - Memory data
80
+ * @property {GPUData} [gpu] - GPU data
81
+ * @property {NetworkData} [network] - Network data
82
+ * @property {DiskData} [disk] - Disk data
83
+ * @property {SystemData} [system] - System data
84
+ * @property {Session[]} sessions - Active sessions
85
+ * @property {Object} sessionTPS - Tokens per second tracking
86
+ * @property {Object} sessionLastTPS - Last TPS values
87
+ * @property {string|null} version - OpenClaw version
88
+ * @property {string|null} latest - Latest available version
89
+ * @property {string|null} openclaw - OpenClaw status
90
+ * @property {number|null} gatewayUptime - Gateway uptime in seconds
91
+ */
92
+
93
+ /**
94
+ * @typedef {Object} Settings
95
+ * @property {number} refreshInterval - Refresh interval in ms
96
+ * @property {string} logLevelFilter - Log level filter
97
+ * @property {string} sessionSortMode - Session sort mode
98
+ * @property {boolean} showWidget1 - Show CPU widget
99
+ * @property {boolean} showWidget2 - Show Memory widget
100
+ * @property {boolean} showWidget3 - Show GPU widget
101
+ * @property {boolean} showWidget4 - Show Network widget
102
+ * @property {boolean} showWidget5 - Show Disk widget
103
+ * @property {boolean} showWidget6 - Show System widget
104
+ * @property {boolean} showWidget7 - Show Uptime widget
105
+ * @property {boolean} showWidget8 - Show Data Health widget
106
+ * @property {string} theme - UI theme
107
+ * @property {string} exportFormat - Export format
108
+ * @property {string} exportDirectory - Export directory path
109
+ * @property {string} sessionSearchQuery - Session search query
110
+ * @property {Object.<string, boolean>} favorites - Favorited session IDs
111
+ * @property {boolean} showFavoritesOnly - Show only favorites
112
+ * @property {boolean} firstRun - First run flag
113
+ */
114
+
115
+ /**
116
+ * @typedef {'warning'|'critical'|'cleared'} AlertLevel
117
+ */
118
+
119
+ /**
120
+ * @typedef {Object} Alert
121
+ * @property {string} id - Unique alert ID
122
+ * @property {AlertLevel} level - Alert level
123
+ * @property {string} metric - Metric type (cpu, memory, disk)
124
+ * @property {number} value - Current value
125
+ * @property {number} threshold - Threshold that was crossed
126
+ * @property {number} timestamp - Alert timestamp
127
+ * @property {boolean} dismissed - Whether alert is dismissed
128
+ */
129
+
130
+ /**
131
+ * @typedef {Object} RetryOptions
132
+ * @property {number} maxRetries - Maximum retry attempts
133
+ * @property {number} initialDelay - Initial delay in ms
134
+ * @property {number} maxDelay - Maximum delay in ms
135
+ * @property {number} backoffMultiplier - Backoff multiplier
136
+ * @property {number[]} retryableStatuses - HTTP statuses to retry
137
+ * @property {string[]} retryableErrors - Error codes to retry
138
+ */
139
+
140
+ /**
141
+ * @typedef {'default'|'dark'|'high-contrast'|'ocean'|'auto'} ThemeName
142
+ */
143
+
144
+ /**
145
+ * @typedef {'time'|'tokens'|'idle'|'name'} SortMode
146
+ */
147
+
148
+ /**
149
+ * @typedef {'all'|'error'|'warn'|'info'|'debug'} LogLevel
150
+ */
151
+
152
+ /**
153
+ * @typedef {'json'|'csv'} ExportFormat
154
+ */
155
+
156
+ // Export type constants for use in code
157
+ /** @type {ThemeName} */
158
+ export const VALID_THEMES = ['default', 'dark', 'high-contrast', 'ocean', 'auto'];
159
+
160
+ /** @type {SortMode} */
161
+ export const VALID_SORT_MODES = ['time', 'tokens', 'idle', 'name'];
162
+
163
+ /** @type {LogLevel} */
164
+ export const VALID_LOG_LEVELS = ['all', 'error', 'warn', 'info', 'debug'];
165
+
166
+ /** @type {ExportFormat} */
167
+ export const VALID_EXPORT_FORMATS = ['json', 'csv'];
168
+
169
+ export default {
170
+ VALID_THEMES,
171
+ VALID_SORT_MODES,
172
+ VALID_LOG_LEVELS,
173
+ VALID_EXPORT_FORMATS
174
+ };