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 +133 -0
- package/README.md +10 -0
- package/index.js +44 -8
- package/package.json +1 -1
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
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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();
|