claw-dashboard 1.8.1 → 1.8.4

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.
package/FEATURES.md ADDED
@@ -0,0 +1,133 @@
1
+ # Claw Dashboard Feature History
2
+
3
+ ## Features Tried
4
+
5
+ ### Top Processes Widget
6
+ - **Date tried**: 2026-02-11, 2026-02-12
7
+ - **Status**: REMOVED - PERMANENTLY DECLINED
8
+ - **Reason**: Layout issues (squeezed display, overflow problems)
9
+ - **User feedback (2026-02-11)**: Declined for lack of interest
10
+ - **User feedback (2026-02-12)**:
11
+ 1. Redundant to previous rejection
12
+ 2. **Colors render as markup** - blessed color tags showing literally instead of rendering
13
+ 3. **Layout problems**: Split screen makes logs unreadable, limits visible content
14
+ - **Re-added by**: Cron job 2026-02-12 (mistake - should not have been re-added)
15
+ - **Action**: NEVER implement this feature again
16
+
17
+ ## Current Features (Retained)
18
+ - System stats (CPU, memory, GPU, disk, network)
19
+ - OpenClaw sessions list
20
+ - OpenClaw agents list
21
+ - Uptime tracking
22
+ - OpenClaw logs
23
+ - Settings panel
24
+
25
+ ## User Preferences
26
+ - Prefer clean, uncluttered layout
27
+ - Session list format should match `clawps` style
28
+ - Memory calculation should exclude cache (Activity Monitor style)
29
+ - No interest in top processes widget
30
+
31
+ ## Pending Ideas
32
+ - None currently
33
+
34
+ ## Technical Notes
35
+
36
+ ### Color Markup Fix (blessed)
37
+ When colors show as literal text like `{green-fg}text{/green-fg}` instead of rendering:
38
+ - **Cause**: Mixing blessed tags with plain strings, or improper tag formatting
39
+ - **Fix**: Use blessed's `{color-fg}text{/color-fg}` format consistently
40
+ - **Check**: Ensure `tags: true` is set on blessed text elements
41
+ - **Avoid**: Concatenating tagged strings with plain strings incorrectly
42
+
43
+ ### Layout Guidelines
44
+ - Split-screen layouts reduce readability
45
+ - Logs need full width to be useful
46
+ - Avoid squeezing content into narrow columns
47
+
48
+ ### Network Traffic Sparkline
49
+ - **Date tried**: 2026-02-13
50
+ - **Status**: SHIPPED
51
+ - **Description**: Added sparkline visualization to network widget showing upload/download traffic history
52
+ - **Implementation**: Combined RX+TX data displayed as activity sparkline, consistent with CPU/memory widgets
53
+ - **Version**: v1.7.0
54
+
55
+ ### Real-time Clock Widget
56
+ - **Date tried**: 2026-02-13
57
+ - **Status**: SHIPPED
58
+ - **Description**: Added a real-time clock showing current time and date in the top-right corner of the dashboard
59
+ - **Implementation**: Displays HH:MM:SS AM/PM format with date (e.g., "Feb 13"), updates every refresh cycle
60
+ - **Timezone**: America/Chicago (CST) to match user's timezone
61
+ - **Version**: v1.7.1
62
+
63
+ ### Pause/Resume Refresh Feature
64
+ - **Date tried**: 2026-02-14
65
+ - **Status**: SHIPPED
66
+ - **Description**: Added ability to pause and resume auto-refresh with 'p' or Space key
67
+ - **Implementation**:
68
+ - Press 'p' or Space to toggle pause state
69
+ - When paused: clock shows [PAUSED] in yellow, footer shows "▶ running"
70
+ - When running: footer shows "p pause"
71
+ - Help panel updated with new key binding
72
+ - **User feedback**: (to be filled in)
73
+ - **Version**: v1.7.2
74
+
75
+ ### Network Traffic Sparkline Visualization
76
+ - **Date tried**: 2026-02-15
77
+ - **Status**: SHIPPED
78
+ - **Description**: Added sparkline visualization to network widget showing combined RX+TX traffic history
79
+ - **Implementation**:
80
+ - Extended network widget height from 4 to 5 rows to accommodate sparkline
81
+ - Added combined network activity sparkline (RX + TX data) displayed below interface name
82
+ - Uses existing network history data (30 data points, ~60 seconds of history)
83
+ - Adjusted log box position to prevent overlap (top: 23, height: 100%-24)
84
+ - **User feedback**: (to be filled in)
85
+ - **Version**: v1.7.3
86
+
87
+ ### Disk Usage Sparkline
88
+ - **Date tried**: 2026-02-15
89
+ - **Status**: DECLINED
90
+ - **Description**: Added sparkline visualization to disk widget showing disk usage history over time
91
+ - **Reason declined**: Disk usage changes too slowly - sparkline provides no useful insight at 2s refresh intervals
92
+ - **User feedback**: "Disk usage changes slowly. Spark line kind of useless."
93
+ - **Version**: Not shipped
94
+
95
+ ### Load Average Display
96
+ - **Date tried**: 2026-02-15, 2026-02-18
97
+ - **Status**: REJECTED
98
+ - **Description**: Added 1/5/15 minute load average display to CPU widget detail line
99
+ - **Implementation**:
100
+ - Attempt 1 (2026-02-15): Added as new row in System widget (increased all widget heights)
101
+ - Attempt 2 (2026-02-18): Moved to CPU box detail line, replacing "X cores" text
102
+ - **Reason rejected**: Feature not needed
103
+ - **User feedback**: "It would be better added to the cpu box to replace the middle line instead of changing the row size" (2026-02-15), then ultimately rejected 2026-02-18
104
+ - **Version**: Not shipped
105
+
106
+ ### Session Sorting Feature
107
+ - **Date tried**: 2026-02-19
108
+ - **Status**: SHIPPED
109
+ - **Description**: Added ability to sort sessions by different criteria using the 'o' key
110
+ - **Sort modes**:
111
+ - `time`: Most recently updated first (default)
112
+ - `tokens`: Highest token usage first
113
+ - `idle`: Longest idle time first
114
+ - `name`: Alphabetical by agent name
115
+ - **Implementation**:
116
+ - Press 'o' to cycle through sort modes
117
+ - Current sort mode shown in footer and session box label
118
+ - Setting persists across restarts
119
+ - Updated help panel with new key binding
120
+ - **Version**: v1.8.3
121
+
122
+ ## Version History
123
+ - v1.5.1: Baseline
124
+ - v1.6.0: Session list improvements, memory calculation fix
125
+ - v1.7.0: **REVERT** - top processes added then removed
126
+ - v1.7.0 (new): Network traffic sparkline visualization
127
+ - v1.7.1: Real-time clock widget
128
+ - v1.7.2: Pause/resume refresh feature
129
+ - v1.7.3: Network sparkline widget enhancement
130
+ - v1.7.4: Disk usage sparkline visualization (DECLINED - not useful)
131
+ - v1.7.5: Load average display (REJECTED)
132
+ - v1.8.1: Session list improvements
133
+ - v1.8.2: Session sorting feature
package/README.md CHANGED
@@ -49,11 +49,21 @@ npm start
49
49
  |-----|--------|
50
50
  | `q` or `Q` | Quit the dashboard |
51
51
  | `r` or `R` | Force refresh data |
52
+ | `p` or `Space` | Pause/resume auto-refresh |
53
+ | `o` | Cycle session sort (time/tokens/idle/name) |
52
54
  | `?` or `h` | Toggle help panel |
53
55
  | `s` or `S` | Open/close settings panel |
54
56
  | `Esc` | Close settings panel (when open) |
55
57
  | `Ctrl+C` | Quit gracefully |
56
58
 
59
+ ### Session Sorting
60
+
61
+ Press `o` to cycle through different ways to sort the sessions list:
62
+ - **time** (default): Most recently updated sessions first
63
+ - **tokens**: Sessions with highest token usage first
64
+ - **idle**: Sessions with longest idle time first
65
+ - **name**: Alphabetical order by agent name
66
+
57
67
  ## ⚙️ Settings
58
68
 
59
69
  Press `s` to open the settings panel where you can customize:
package/index.js CHANGED
@@ -35,7 +35,8 @@ const DEFAULT_SETTINGS = {
35
35
  showNetwork: true,
36
36
  showGPU: true,
37
37
  showDisk: true,
38
- logLevelFilter: 'all'
38
+ logLevelFilter: 'all',
39
+ sessionSortMode: 'time' // 'time' | 'tokens' | 'idle' | 'name'
39
40
  };
40
41
 
41
42
  function loadSettings() {
@@ -407,9 +408,10 @@ class Dashboard {
407
408
  this.w.gpuDetail = blessed.text({ parent: this.w.gpuBox, top: 1, left: 'center', content: '', style: { fg: C.gray } });
408
409
  this.w.gpuSpark = blessed.text({ parent: this.w.gpuBox, top: 2, left: 'center', content: '', style: { fg: C.yellow } });
409
410
 
410
- this.w.sessBox = blessed.box({ parent: this.screen, top: 8, left: 0, width: '100%', height: 10, border: { type: 'line' }, label: ' SESSIONS ', style: { border: { fg: C.blue } } });
411
+ this.w.sessBox = blessed.box({ parent: this.screen, top: 8, left: 0, width: '100%', height: 10, border: { type: 'line' }, label: ' SESSIONS ', style: { border: { fg: C.blue } }, tags: true });
411
412
  this.w.sessHeader = blessed.text({ parent: this.w.sessBox, top: 0, left: 1, content: 'STATUS AGENT MODEL CONTEXT IDLE CHAN', style: { fg: C.brightWhite, bold: true } });
412
413
  this.w.sessList = blessed.text({ parent: this.w.sessBox, top: 1, left: 1, width: '98%', height: 7, content: '', style: { fg: C.white }, tags: true });
414
+ this.w.sessCount = blessed.text({ parent: this.w.sessBox, top: 0, right: 2, content: '', style: { fg: C.gray } });
413
415
 
414
416
  this.w.sysBox = blessed.box({ parent: this.screen, top: 18, left: 0, width: '25%', height: 4, border: { type: 'line' }, label: ' SYSTEM ', style: { border: { fg: C.gray } } });
415
417
  this.w.sysInfoLine1 = blessed.text({ parent: this.w.sysBox, top: 0, left: 'center', content: '...', style: { fg: C.gray } });
@@ -440,6 +442,15 @@ class Dashboard {
440
442
  this.screen.key(['?', 'h'], () => this.toggleHelp());
441
443
  this.screen.key(['s', 'S'], () => this.toggleSettings());
442
444
  this.screen.key(['p', ' '], () => this.togglePause());
445
+ this.screen.key('o', () => this.cycleSessionSort());
446
+ }
447
+
448
+ cycleSessionSort() {
449
+ const modes = ['time', 'tokens', 'idle', 'name'];
450
+ const currentIdx = modes.indexOf(this.settings.sessionSortMode);
451
+ this.settings.sessionSortMode = modes[(currentIdx + 1) % modes.length];
452
+ saveSettings(this.settings);
453
+ this.render();
443
454
  }
444
455
 
445
456
  togglePause() {
@@ -472,6 +483,7 @@ class Dashboard {
472
483
  ' {cyan-fg}q{/cyan-fg} or {cyan-fg}Ctrl+C{/cyan-fg} Quit the dashboard',
473
484
  ' {cyan-fg}r{/cyan-fg} Force refresh all data',
474
485
  ' {cyan-fg}p{/cyan-fg} or {cyan-fg}Space{/cyan-fg} Pause/resume auto-refresh',
486
+ ' {cyan-fg}o{/cyan-fg} Cycle session sort (time/tokens/idle/name)',
475
487
  ' {cyan-fg}?{/cyan-fg} or {cyan-fg}h{/cyan-fg} Toggle this help panel',
476
488
  ' {cyan-fg}s{/cyan-fg} or {cyan-fg}S{/cyan-fg} Open settings panel',
477
489
  '',
@@ -684,9 +696,7 @@ class Dashboard {
684
696
  transcriptPath: session.transcriptPath || ''
685
697
  }));
686
698
 
687
- // Sort by updatedAt descending (most recent first)
688
- sessions.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
689
-
699
+ // Sorting is applied in render() based on sessionSortMode setting
690
700
  return sessions;
691
701
  } catch (err) {
692
702
  throw new Error('Failed to read sessions: ' + err.message);
@@ -906,7 +916,26 @@ class Dashboard {
906
916
  }
907
917
 
908
918
  if (this.data.sessions.length) {
909
- const lines = this.data.sessions.map(s => {
919
+ // Sort sessions based on current sort mode
920
+ const sortMode = this.settings.sessionSortMode || 'time';
921
+ const sortedSessions = [...this.data.sessions].sort((a, b) => {
922
+ switch (sortMode) {
923
+ case 'time':
924
+ return (b.updatedAt || 0) - (a.updatedAt || 0); // Most recent first
925
+ case 'tokens':
926
+ return (b.totalTokens || 0) - (a.totalTokens || 0); // Most tokens first
927
+ case 'idle':
928
+ const idleA = a.updatedAt ? Date.now() - a.updatedAt : 0;
929
+ const idleB = b.updatedAt ? Date.now() - b.updatedAt : 0;
930
+ return idleB - idleA; // Longest idle first
931
+ case 'name':
932
+ return (a.displayName || '').localeCompare(b.displayName || ''); // A-Z
933
+ default:
934
+ return (b.updatedAt || 0) - (a.updatedAt || 0);
935
+ }
936
+ });
937
+
938
+ const lines = sortedSessions.map(s => {
910
939
  // Calculate idle time
911
940
  const idleMs = s.updatedAt ? Date.now() - s.updatedAt : 0;
912
941
 
@@ -953,8 +982,10 @@ class Dashboard {
953
982
  return `${statusStr} ${agentName} ${model} ${context} ${idle} ${channel}`;
954
983
  });
955
984
  this.w.sessList.setContent(lines.join('\n'));
985
+ this.w.sessCount.setContent(`${this.data.sessions.length} sessions`);
956
986
  } else {
957
987
  this.w.sessList.setContent('No active sessions');
988
+ this.w.sessCount.setContent('0 sessions');
958
989
  }
959
990
 
960
991
  // Update logs - colorize by level and filter
@@ -1054,10 +1085,15 @@ class Dashboard {
1054
1085
  this.w.uptimeBox.style.border.fg = C.gray;
1055
1086
  }
1056
1087
 
1057
- // Update footer with current refresh interval and pause state
1088
+ // Update footer with current refresh interval, pause state, and sort mode
1058
1089
  const refreshSec = Math.round(this.settings.refreshInterval / 1000);
1059
1090
  const pauseIndicator = this.isPaused ? '▶ running' : 'p pause';
1060
- this.w.footerText.setContent(`q quit r refresh ${pauseIndicator} ? help s settings • ${refreshSec}s refresh`);
1091
+ const sortMode = this.settings.sessionSortMode;
1092
+ this.w.footerText.setContent(`q quit r refresh ${pauseIndicator} o sort:${sortMode} ? help s settings • ${refreshSec}s refresh`);
1093
+
1094
+ // Update session box label to show sort mode
1095
+ const sortLabel = sortMode === 'time' ? 'TIME' : sortMode === 'tokens' ? 'TOKENS' : sortMode === 'idle' ? 'IDLE' : 'NAME';
1096
+ this.w.sessBox.setLabel(` SESSIONS (${sortLabel}) `);
1061
1097
 
1062
1098
  try {
1063
1099
  this.screen.render();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-dashboard",
3
- "version": "1.8.1",
3
+ "version": "1.8.4",
4
4
  "description": "A beautiful console dashboard for monitoring OpenClaw instances — inspired by htop/btop/mactop",
5
5
  "main": "index.js",
6
6
  "bin": {