ai-agent-session-center 2.0.2 → 2.0.3
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/README.md +484 -429
- package/docs/3D/ADAPTATION_GUIDE.md +592 -0
- package/docs/3D/index.html +754 -0
- package/docs/AGENT_TEAM_TASKS.md +716 -0
- package/docs/CYBERDROME_V2_SPEC.md +531 -0
- package/docs/ERROR_185_ANALYSIS.md +263 -0
- package/docs/PLATFORM_FEATURES_PROMPT.md +296 -0
- package/docs/SESSION_DETAIL_FEATURES.md +98 -0
- package/docs/_3d_multimedia_features.md +1080 -0
- package/docs/_frontend_features.md +1057 -0
- package/docs/_server_features.md +1077 -0
- package/docs/session-duplication-fixes.md +271 -0
- package/docs/session-terminal-linkage.md +412 -0
- package/package.json +63 -5
- package/public/apple-touch-icon.svg +21 -0
- package/public/css/dashboard.css +0 -161
- package/public/css/detail-panel.css +25 -0
- package/public/css/layout.css +18 -1
- package/public/css/modals.css +0 -26
- package/public/css/settings.css +0 -150
- package/public/css/terminal.css +34 -0
- package/public/favicon.svg +18 -0
- package/public/index.html +6 -26
- package/public/js/alarmManager.js +0 -21
- package/public/js/app.js +21 -7
- package/public/js/detailPanel.js +63 -64
- package/public/js/historyPanel.js +61 -55
- package/public/js/quickActions.js +132 -48
- package/public/js/sessionCard.js +5 -20
- package/public/js/sessionControls.js +8 -0
- package/public/js/settingsManager.js +0 -142
- package/server/apiRouter.js +60 -15
- package/server/apiRouter.ts +774 -0
- package/server/approvalDetector.ts +94 -0
- package/server/authManager.ts +144 -0
- package/server/autoIdleManager.ts +110 -0
- package/server/config.ts +121 -0
- package/server/constants.ts +150 -0
- package/server/db.ts +475 -0
- package/server/hookInstaller.d.ts +3 -0
- package/server/hookProcessor.ts +108 -0
- package/server/hookRouter.ts +18 -0
- package/server/hookStats.ts +116 -0
- package/server/index.js +15 -1
- package/server/index.ts +230 -0
- package/server/logger.ts +75 -0
- package/server/mqReader.ts +349 -0
- package/server/portManager.ts +55 -0
- package/server/processMonitor.ts +239 -0
- package/server/serverConfig.ts +29 -0
- package/server/sessionMatcher.js +17 -6
- package/server/sessionMatcher.ts +403 -0
- package/server/sessionStore.js +109 -3
- package/server/sessionStore.ts +1145 -0
- package/server/sshManager.js +167 -24
- package/server/sshManager.ts +671 -0
- package/server/teamManager.ts +289 -0
- package/server/wsManager.ts +200 -0
|
@@ -0,0 +1,1057 @@
|
|
|
1
|
+
# Frontend Features — AI Agent Session Center
|
|
2
|
+
|
|
3
|
+
> Sections 13–14, 16–21, 27–30. Generated from source files in `public/js/` and `public/css/`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 13. IndexedDB Client Persistence (`browserDb.js`)
|
|
8
|
+
|
|
9
|
+
### Database Identity
|
|
10
|
+
|
|
11
|
+
| Property | Value |
|
|
12
|
+
|----------|-------|
|
|
13
|
+
| Database name | `claude-dashboard` |
|
|
14
|
+
| Schema version | `2` |
|
|
15
|
+
| API | `window.indexedDB` (browser-native) |
|
|
16
|
+
| Initialization | `openDB()` — idempotent singleton |
|
|
17
|
+
|
|
18
|
+
### Object Stores (12 total)
|
|
19
|
+
|
|
20
|
+
| Store | Key Path | Auto-Increment | Purpose |
|
|
21
|
+
|-------|----------|----------------|---------|
|
|
22
|
+
| `sessions` | `id` | No | Session snapshots, one record per session |
|
|
23
|
+
| `prompts` | `id` | Yes | User prompt history entries |
|
|
24
|
+
| `responses` | `id` | Yes | Claude response excerpts |
|
|
25
|
+
| `toolCalls` | `id` | Yes | Tool invocation records |
|
|
26
|
+
| `events` | `id` | Yes | Raw hook event log |
|
|
27
|
+
| `notes` | `id` | Yes | Per-session user notes |
|
|
28
|
+
| `promptQueue` | `id` | Yes | Queued prompts awaiting dispatch |
|
|
29
|
+
| `alerts` | `id` | Yes | Duration alert rules |
|
|
30
|
+
| `sshProfiles` | `id` | Yes | SSH connection profiles |
|
|
31
|
+
| `settings` | `key` | No | Key-value settings store |
|
|
32
|
+
| `summaryPrompts` | `id` | Yes | AI summarization prompt templates |
|
|
33
|
+
| `teams` | `id` | No | Subagent team definitions |
|
|
34
|
+
|
|
35
|
+
### Indexes
|
|
36
|
+
|
|
37
|
+
| Store | Index Name | Key Path | Notes |
|
|
38
|
+
|-------|-----------|----------|-------|
|
|
39
|
+
| `sessions` | `status` | `status` | Filter by status |
|
|
40
|
+
| `sessions` | `projectPath` | `projectPath` | Filter by project |
|
|
41
|
+
| `sessions` | `startedAt` | `startedAt` | Sort by time |
|
|
42
|
+
| `sessions` | `lastActivityAt` | `lastActivityAt` | Sort by activity |
|
|
43
|
+
| `sessions` | `archived` | `archived` | Archived flag (0/1) |
|
|
44
|
+
| `prompts` | `sessionId` | `sessionId` | Lookup by session |
|
|
45
|
+
| `prompts` | `sessionId_timestamp` | `[sessionId, timestamp]` | Compound for sorted lookup |
|
|
46
|
+
| `responses` | `sessionId` | `sessionId` | Lookup by session |
|
|
47
|
+
| `toolCalls` | `sessionId` | `sessionId` | Lookup by session |
|
|
48
|
+
| `toolCalls` | `toolName` | `toolName` | Filter by tool |
|
|
49
|
+
| `events` | `sessionId` | `sessionId` | Lookup by session |
|
|
50
|
+
| `promptQueue` | `sessionId_position` | `[sessionId, position]` | Ordered queue items |
|
|
51
|
+
| `notes` | `sessionId` | `sessionId` | Lookup notes per session |
|
|
52
|
+
| `summaryPrompts` | `isDefault` | `isDefault` | Find default template |
|
|
53
|
+
|
|
54
|
+
### Batched Write Queue
|
|
55
|
+
|
|
56
|
+
Writes are coalesced per store to minimize IndexedDB transaction overhead:
|
|
57
|
+
|
|
58
|
+
| Parameter | Value |
|
|
59
|
+
|-----------|-------|
|
|
60
|
+
| Flush interval | `200 ms` |
|
|
61
|
+
| Max queue depth before immediate flush | `20 items` |
|
|
62
|
+
|
|
63
|
+
`putBatched(storeName, data)` schedules writes; `flushWriteQueue()` executes them in a single transaction. `flushAllWriteQueues()` is called on page unload.
|
|
64
|
+
|
|
65
|
+
### Session Persistence (`persistSessionUpdate`)
|
|
66
|
+
|
|
67
|
+
When a `session_update` WebSocket message arrives, the following records are upserted/appended:
|
|
68
|
+
|
|
69
|
+
- **sessions** store: full session record (status, projectPath, title, model, label, teamId, queueCount, etc.)
|
|
70
|
+
- **prompts**: new entries deduplicated by `timestamp`
|
|
71
|
+
- **toolCalls**: new entries deduplicated by `timestamp`; maps `tool` → `toolName`, `input` → `toolInputSummary`
|
|
72
|
+
- **responses**: new entries deduplicated by `timestamp`; maps `text` → `textExcerpt`
|
|
73
|
+
- **events**: new entries deduplicated by `timestamp`
|
|
74
|
+
|
|
75
|
+
### Query API (`searchSessions`)
|
|
76
|
+
|
|
77
|
+
Supported filter parameters with defaults:
|
|
78
|
+
|
|
79
|
+
| Parameter | Type | Default | Notes |
|
|
80
|
+
|-----------|------|---------|-------|
|
|
81
|
+
| `query` | string | — | Substring match on prompt text |
|
|
82
|
+
| `project` | string | — | Exact match on `projectPath` |
|
|
83
|
+
| `status` | string | — | Exact match on `status` |
|
|
84
|
+
| `dateFrom` | number (ms) | — | `startedAt >= dateFrom` |
|
|
85
|
+
| `dateTo` | number (ms) | — | `startedAt <= dateTo` |
|
|
86
|
+
| `archived` | boolean/`'all'` | excludes archived | `archived === 'all'` returns everything |
|
|
87
|
+
| `sortBy` | string | `'startedAt'` | Any session field |
|
|
88
|
+
| `sortDir` | `'asc'`/`'desc'` | `'desc'` | — |
|
|
89
|
+
| `page` | number | `1` | 1-based |
|
|
90
|
+
| `pageSize` | number | `50` | — |
|
|
91
|
+
|
|
92
|
+
Returns `{ sessions, total, page, pageSize }`.
|
|
93
|
+
|
|
94
|
+
### Full-Text Search (`fullTextSearch`)
|
|
95
|
+
|
|
96
|
+
Searches `prompts` and `responses` stores with case-insensitive substring matching. Returns results with `<mark>`-wrapped snippets (context window: 60 chars before/after match). Supports `type` filter: `'all'`, `'prompts'`, `'responses'`.
|
|
97
|
+
|
|
98
|
+
### Analytics Queries
|
|
99
|
+
|
|
100
|
+
| Function | Data source | Output |
|
|
101
|
+
|---------|-------------|--------|
|
|
102
|
+
| `getSummaryStats()` | sessions + toolCalls + prompts | total sessions, prompts, tools, active count, avg duration, most-used tool, busiest project |
|
|
103
|
+
| `getToolBreakdown()` | toolCalls | per-tool count + % of total |
|
|
104
|
+
| `getDurationTrends({ period })` | sessions with `endedAt` | avg duration + session count per bucket |
|
|
105
|
+
| `getActiveProjects()` | sessions + prompts + toolCalls | per-project: session count, total prompts, total tools, last activity |
|
|
106
|
+
| `getHeatmap()` | events | 7×24 grid of event counts (Monday-first, 0–6) |
|
|
107
|
+
| `getTimeline({ dateFrom, dateTo, granularity, project })` | sessions + prompts + toolCalls | per-period: session_count, prompt_count, tool_call_count |
|
|
108
|
+
|
|
109
|
+
Timeline granularity options: `'hour'`, `'day'` (default), `'week'`, `'month'`. Period key format: `YYYY-MM-DD` (day), `YYYY-MM-DD HH:00` (hour), `YYYY-Www` (week), `YYYY-MM` (month).
|
|
110
|
+
|
|
111
|
+
### Prompt Queue CRUD
|
|
112
|
+
|
|
113
|
+
| Operation | Function |
|
|
114
|
+
|-----------|----------|
|
|
115
|
+
| List (ordered) | `getQueue(sessionId)` — sorted by `position` |
|
|
116
|
+
| Add | `addToQueue(sessionId, text)` — appends at max position + 1 |
|
|
117
|
+
| Remove first (dequeue) | `popQueue(sessionId)` |
|
|
118
|
+
| Reorder | `reorderQueue(sessionId, orderedIds)` — reassigns position by array index |
|
|
119
|
+
| Move items | `moveQueueItems(itemIds, targetSessionId)` |
|
|
120
|
+
| Move all | `moveAllQueue(sourceSessionId, targetSessionId)` |
|
|
121
|
+
|
|
122
|
+
### Notes Helpers
|
|
123
|
+
|
|
124
|
+
- `getNotes(sessionId)` — sorted newest-first by `createdAt`
|
|
125
|
+
- `addNote(sessionId, text)` — sets `createdAt` and `updatedAt` to `Date.now()`
|
|
126
|
+
|
|
127
|
+
### Settings Helpers
|
|
128
|
+
|
|
129
|
+
- `getSetting(key)` / `setSetting(key, value)` — single key-value
|
|
130
|
+
- `getAllSettings()` — returns plain object
|
|
131
|
+
- `setManySettings(obj)` — batch upsert
|
|
132
|
+
|
|
133
|
+
### Default Seed Data
|
|
134
|
+
|
|
135
|
+
On first DB open (`settingsCount === 0`), the following defaults are inserted:
|
|
136
|
+
|
|
137
|
+
| Key | Default Value |
|
|
138
|
+
|-----|--------------|
|
|
139
|
+
| `theme` | `'command-center'` |
|
|
140
|
+
| `fontSize` | `'13'` |
|
|
141
|
+
| `modelUrl` | `'https://threejs.org/examples/models/gltf/Xbot.glb'` |
|
|
142
|
+
| `modelName` | `'Xbot'` |
|
|
143
|
+
| `soundEnabled` | `'true'` |
|
|
144
|
+
| `soundVolume` | `'0.5'` |
|
|
145
|
+
| `soundPack` | `'default'` |
|
|
146
|
+
|
|
147
|
+
Five summary prompt templates are seeded on first use: **Detailed Technical Summary** (default), **Quick Bullet Points**, **Changelog Entry**, **Handoff Notes**, **PR Description**.
|
|
148
|
+
|
|
149
|
+
### Session ID Migration
|
|
150
|
+
|
|
151
|
+
`migrateSessionId(oldSessionId, newSessionId)` re-keys all child store records across 7 stores: `prompts`, `responses`, `toolCalls`, `events`, `notes`, `promptQueue`, `alerts`. Used when `claude --resume` creates a new session UUID.
|
|
152
|
+
|
|
153
|
+
### Delete Cascade
|
|
154
|
+
|
|
155
|
+
`deleteSession(sessionId)` removes the session record and all related records from 7 child stores.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 14. CSS Character System (`robotManager.js` + `public/css/characters/`)
|
|
160
|
+
|
|
161
|
+
### Character Models (20 total)
|
|
162
|
+
|
|
163
|
+
| Model key | CSS class | Description |
|
|
164
|
+
|-----------|-----------|-------------|
|
|
165
|
+
| `robot` | `char-robot` | Classic robot with antenna, chest light, typing dots |
|
|
166
|
+
| `cat` | `char-cat` | Cat with ears, whiskers, tail |
|
|
167
|
+
| `alien` | `char-alien` | Dome-headed alien with 3 eyes and tentacles |
|
|
168
|
+
| `ghost` | `char-ghost` | Ghost with blush marks |
|
|
169
|
+
| `orb` | `char-orb` | Energy orb with 2 rings and particles |
|
|
170
|
+
| `dragon` | `char-dragon` | Dragon with horns, wings, fire breath |
|
|
171
|
+
| `penguin` | `char-penguin` | Penguin with beak and flippers |
|
|
172
|
+
| `octopus` | `char-octopus` | Octopus with 4 tentacles |
|
|
173
|
+
| `mushroom` | `char-mushroom` | Mushroom with spots and stem |
|
|
174
|
+
| `fox` | `char-fox` | Fox with ears, snout, tail |
|
|
175
|
+
| `unicorn` | `char-unicorn` | Unicorn with horn and mane |
|
|
176
|
+
| `jellyfish` | `char-jellyfish` | Jellyfish with 5 tentacles |
|
|
177
|
+
| `owl` | `char-owl` | Owl with tufts, pupils, wings |
|
|
178
|
+
| `bat` | `char-bat` | Bat with large wings and fangs |
|
|
179
|
+
| `cactus` | `char-cactus` | Cactus with flower, arms |
|
|
180
|
+
| `slime` | `char-slime` | Blob with shine |
|
|
181
|
+
| `pumpkin` | `char-pumpkin` | Pumpkin with grooves |
|
|
182
|
+
| `yeti` | `char-yeti` | Yeti with fur and belly |
|
|
183
|
+
| `crystal` | `char-crystal` | Faceted crystal with glowing core |
|
|
184
|
+
| `bee` | `char-bee` | Bee with stripes, wings, stinger |
|
|
185
|
+
|
|
186
|
+
### Color Palette (8 colors, round-robin assignment)
|
|
187
|
+
|
|
188
|
+
| Index | Hex Value | Associated Status |
|
|
189
|
+
|-------|-----------|-----------------|
|
|
190
|
+
| 0 | `#00e5ff` | cyan — prompting |
|
|
191
|
+
| 1 | `#ff9100` | orange — working |
|
|
192
|
+
| 2 | `#00ff88` | green — idle |
|
|
193
|
+
| 3 | `#ff3355` | red — ended |
|
|
194
|
+
| 4 | `#aa66ff` | purple — input |
|
|
195
|
+
| 5 | `#ffdd00` | yellow — approval |
|
|
196
|
+
| 6 | `#ff66aa` | pink |
|
|
197
|
+
| 7 | `#66ffdd` | teal |
|
|
198
|
+
|
|
199
|
+
Colors are assigned sequentially on character creation and persisted to the server via `PUT /api/sessions/:id/accent-color`.
|
|
200
|
+
|
|
201
|
+
### Status → CSS Animation Mapping
|
|
202
|
+
|
|
203
|
+
The `data-status` attribute on `.css-robot` drives all animations via CSS:
|
|
204
|
+
|
|
205
|
+
| Status | `data-status` | Visual Behaviour |
|
|
206
|
+
|--------|--------------|-----------------|
|
|
207
|
+
| Idle | `idle` | Float up/down (or breathe/sway/sparkle per effect setting) |
|
|
208
|
+
| Prompting | `prompting` | Eye-cycle movement, wave animation |
|
|
209
|
+
| Working | `working` | Running/bobbing animation |
|
|
210
|
+
| Waiting (approval/input) | `waiting` | Bounce animation; stops when `data-checked="true"` |
|
|
211
|
+
| Ended | `ended` | Death animation then fade |
|
|
212
|
+
|
|
213
|
+
### Lifecycle
|
|
214
|
+
|
|
215
|
+
1. `createRobot(sessionId, sessionCharModel, sessionColor)` — finds `.robot-viewport` inside the card, builds HTML from the character template, sets `--robot-color` CSS variable, stores in `Map<sessionId, robotData>`.
|
|
216
|
+
2. `updateRobot(session)` — updates `data-status`, optionally switches character model, triggers emote animations (`robot.classList.add('robot-emote')` for 600 ms).
|
|
217
|
+
3. `switchSessionCharacter(sessionId, modelName)` — replaces inner HTML with new template, sets `perSession: true` flag to skip global model switches.
|
|
218
|
+
4. `switchAllCharacters(modelName)` — updates all characters without per-session overrides (called when global setting changes).
|
|
219
|
+
5. `markChecked(sessionId)` — sets `data-checked="true"` to stop the waiting bounce.
|
|
220
|
+
6. `removeRobot(sessionId)` — removes DOM element and map entry.
|
|
221
|
+
|
|
222
|
+
### Per-Session Override
|
|
223
|
+
|
|
224
|
+
Each session card has a character model selector in the detail panel header (`#detail-char-model`). Choosing a model sets `perSession: true` on the robot data object and persists the choice to both IndexedDB (`sessions` store, `characterModel` field) and the server.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 16. Session Cards (`sessionCard.js`)
|
|
229
|
+
|
|
230
|
+
### Card DOM Structure
|
|
231
|
+
|
|
232
|
+
```html
|
|
233
|
+
<div class="session-card" data-session-id="..." data-status="..." draggable="true">
|
|
234
|
+
<button class="close-btn">×</button>
|
|
235
|
+
<button class="pin-btn">▲</button>
|
|
236
|
+
<button class="summarize-card-btn">↓AI</button>
|
|
237
|
+
<button class="mute-btn">♫</button>
|
|
238
|
+
<button class="resume-card-btn hidden">▶ RESUME</button>
|
|
239
|
+
<div class="robot-viewport"><!-- CSS character injected here --></div>
|
|
240
|
+
<div class="card-info">
|
|
241
|
+
<div class="card-title"><!-- editable title --></div>
|
|
242
|
+
<div class="card-header">
|
|
243
|
+
<span class="project-name"></span>
|
|
244
|
+
<span class="card-label-badge"></span>
|
|
245
|
+
<span class="card-group-badge">+</span>
|
|
246
|
+
<span class="source-badge"></span>
|
|
247
|
+
<span class="status-badge"></span>
|
|
248
|
+
</div>
|
|
249
|
+
<div class="waiting-banner">NEEDS YOUR INPUT</div>
|
|
250
|
+
<div class="card-prompt"></div>
|
|
251
|
+
<div class="card-stats">
|
|
252
|
+
<span class="duration"></span>
|
|
253
|
+
<span class="tool-count"></span>
|
|
254
|
+
<span class="subagent-count"></span>
|
|
255
|
+
<span class="queue-count"></span>
|
|
256
|
+
</div>
|
|
257
|
+
<div class="tool-bars"><!-- top-5 tool bars --></div>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Debounced DOM Updates
|
|
263
|
+
|
|
264
|
+
| Parameter | Value |
|
|
265
|
+
|-----------|-------|
|
|
266
|
+
| Debounce delay for subsequent updates | `100 ms` |
|
|
267
|
+
| Initial card creation | No debounce (immediate) |
|
|
268
|
+
|
|
269
|
+
`pendingCardUpdates` map tracks pending timers. Only the latest update within the window is applied.
|
|
270
|
+
|
|
271
|
+
### Status Display
|
|
272
|
+
|
|
273
|
+
| Session Status | Badge Text | CSS class |
|
|
274
|
+
|---------------|-----------|-----------|
|
|
275
|
+
| `idle` | `IDLE` | `.idle` |
|
|
276
|
+
| `prompting` | `PROMPTING` | `.prompting` |
|
|
277
|
+
| `working` | `WORKING` | `.working` |
|
|
278
|
+
| `approval` | `APPROVAL NEEDED` | `.approval` |
|
|
279
|
+
| `input` | `WAITING FOR INPUT` | `.input` |
|
|
280
|
+
| `waiting` | `WAITING` | `.waiting` |
|
|
281
|
+
| `ended` | `DISCONNECTED` | `.disconnected` |
|
|
282
|
+
|
|
283
|
+
Active-status cards (`working`, `prompting`, `approval`, `input`) automatically float to the top of their grid section (ahead of unpinned cards) when their status transitions.
|
|
284
|
+
|
|
285
|
+
### Pinned Sessions
|
|
286
|
+
|
|
287
|
+
- Persisted in `localStorage['pinned-sessions']` (JSON array of session IDs).
|
|
288
|
+
- `pinSession(sessionId)` / unpin toggle on pin button click.
|
|
289
|
+
- `reorderPinnedCards()` re-inserts pinned cards at the start of their containing grid.
|
|
290
|
+
- CSS class `pinned` applied to card; pin button gets class `active`.
|
|
291
|
+
|
|
292
|
+
### Muted Sessions
|
|
293
|
+
|
|
294
|
+
- Persisted in `localStorage['muted-sessions']` (JSON array).
|
|
295
|
+
- Global mute via `toggleMuteAll()` — sets `globalMuted` flag; affects sound playback.
|
|
296
|
+
- Per-session mute toggle on the `♫` button: adds/removes `muted` class, updates icon to `M`.
|
|
297
|
+
- `isMuted(sessionId)` returns `true` if globally muted or session is individually muted.
|
|
298
|
+
|
|
299
|
+
### Tool Bars
|
|
300
|
+
|
|
301
|
+
Top 5 tools rendered as horizontal fill bars:
|
|
302
|
+
- Bar width: `(count / maxCount) * 100%`
|
|
303
|
+
- Bars sorted by count descending
|
|
304
|
+
- Maximum bars shown: `5`
|
|
305
|
+
|
|
306
|
+
### Toast Notifications
|
|
307
|
+
|
|
308
|
+
`showToast(title, message)`:
|
|
309
|
+
- Auto-dismiss after `5000 ms`
|
|
310
|
+
- Fade-out animation: `300 ms`
|
|
311
|
+
- Error/failed toasts (matching `/error|failed/i`) always shown regardless of `toastEnabled` setting
|
|
312
|
+
- Close button dismisses immediately
|
|
313
|
+
|
|
314
|
+
### Label Badges
|
|
315
|
+
|
|
316
|
+
| Label Value | CSS modifier on card |
|
|
317
|
+
|-------------|---------------------|
|
|
318
|
+
| `HEAVY` | `.heavy-session` |
|
|
319
|
+
| `ONEOFF` | `.oneoff-session` |
|
|
320
|
+
| `IMPORTANT` | `.important-session` |
|
|
321
|
+
|
|
322
|
+
Label-specific decorative frames are applied via `data-frame` attribute if configured in label settings.
|
|
323
|
+
|
|
324
|
+
### Source Badges
|
|
325
|
+
|
|
326
|
+
Displayed when session source is not `ssh`:
|
|
327
|
+
|
|
328
|
+
| Source key | Display text |
|
|
329
|
+
|-----------|-------------|
|
|
330
|
+
| `vscode` | VS Code |
|
|
331
|
+
| `jetbrains` | JetBrains |
|
|
332
|
+
| `iterm` | iTerm |
|
|
333
|
+
| `warp` | Warp |
|
|
334
|
+
| `kitty` | Kitty |
|
|
335
|
+
| `ghostty` | Ghostty |
|
|
336
|
+
| `alacritty` | Alacritty |
|
|
337
|
+
| `wezterm` | WezTerm |
|
|
338
|
+
| `hyper` | Hyper |
|
|
339
|
+
| `terminal` | Terminal |
|
|
340
|
+
| `tmux` | tmux |
|
|
341
|
+
|
|
342
|
+
### Prompt Preview
|
|
343
|
+
|
|
344
|
+
Truncated at `120 characters` with `...` suffix. Shows `currentPrompt` or latest prompt history entry.
|
|
345
|
+
|
|
346
|
+
### Drag-and-Drop Reordering
|
|
347
|
+
|
|
348
|
+
- Cards are `draggable="true"` (except display-only source cards).
|
|
349
|
+
- During drag, card gets class `dragging`.
|
|
350
|
+
- Drop target shows `drag-over-left` or `drag-over-right` class based on mouse X position relative to card midpoint.
|
|
351
|
+
- Dropping onto a group grid auto-assigns the session to that group.
|
|
352
|
+
|
|
353
|
+
### Inline Title Rename
|
|
354
|
+
|
|
355
|
+
Clicking `.card-title` makes it `contentEditable`. Saving: `blur` or `Enter`. Cancelling: `Escape` restores original. Title saved to server via `PUT /api/sessions/:id/title` and persisted to IndexedDB.
|
|
356
|
+
|
|
357
|
+
### Card-Level Summarize & Archive
|
|
358
|
+
|
|
359
|
+
The `↓AI` button on each card:
|
|
360
|
+
1. Fetches session detail from IndexedDB.
|
|
361
|
+
2. Builds prompt context (prompts, tool calls, responses).
|
|
362
|
+
3. Loads default summary template from `summaryPrompts` store.
|
|
363
|
+
4. POSTs to `POST /api/sessions/:id/summarize`.
|
|
364
|
+
5. On success: marks session `archived: 1`, updates IndexedDB, shows toast.
|
|
365
|
+
|
|
366
|
+
### Resume Button
|
|
367
|
+
|
|
368
|
+
Visible only when `status === 'ended'`. Calls `POST /api/sessions/:id/resume`. On success, opens detail panel Terminal tab.
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 17. Session Detail Panel (`detailPanel.js`)
|
|
373
|
+
|
|
374
|
+
### Panel Structure
|
|
375
|
+
|
|
376
|
+
The panel slides in from the right, overlaid on top of the main grid. Triggered by clicking a session card.
|
|
377
|
+
|
|
378
|
+
**Header fields:**
|
|
379
|
+
- Project name (`#detail-project-name`)
|
|
380
|
+
- Status badge (`#detail-status-badge`) — same text mapping as card badges
|
|
381
|
+
- Model name (`#detail-model`)
|
|
382
|
+
- Duration (`#detail-duration`)
|
|
383
|
+
- Character mini-preview (`#detail-char-preview`) — live CSS character at current status
|
|
384
|
+
- Character model selector (`#detail-char-model`) — dropdown with all 20 models
|
|
385
|
+
- Session title input (`#detail-title`)
|
|
386
|
+
- Session label input (`#detail-label`) with datalist suggestions
|
|
387
|
+
|
|
388
|
+
### Tabs (6 total)
|
|
389
|
+
|
|
390
|
+
| Tab `data-tab` | Content | Container ID |
|
|
391
|
+
|----------------|---------|-------------|
|
|
392
|
+
| `conversation` | Prompt history (newest first), previous sessions (collapsible) | `#detail-conversation` |
|
|
393
|
+
| `activity` | Merged tool calls + events + responses (newest first) | `#detail-activity-log` |
|
|
394
|
+
| `terminal` | xterm.js terminal embed | `#tab-terminal` |
|
|
395
|
+
| `notes` | Session notes with timestamps | `#tab-notes` |
|
|
396
|
+
| `queue` | Prompt queue with compose textarea | `#tab-queue` |
|
|
397
|
+
| `summary` | AI-generated summary text | `#tab-summary` |
|
|
398
|
+
|
|
399
|
+
Tab state persisted in `localStorage['active-tab']` and restored on page refresh.
|
|
400
|
+
|
|
401
|
+
### Conversation Tab Features
|
|
402
|
+
|
|
403
|
+
- Each prompt entry: numbered `#N`, timestamp, COPY button
|
|
404
|
+
- COPY button writes prompt text to clipboard; temporarily shows `COPIED` for `1500 ms`
|
|
405
|
+
- Previous session sections (from `session.previousSessions[]`) rendered as collapsible accordions with headers showing session number, time range, and prompt count
|
|
406
|
+
- Toggle: click `.prev-session-header` to expand/collapse `.prev-session-section.collapsed`
|
|
407
|
+
|
|
408
|
+
### Activity Tab
|
|
409
|
+
|
|
410
|
+
Entries merged from `session.events[]`, `session.toolLog[]`, `session.responseLog[]`, sorted newest-first:
|
|
411
|
+
|
|
412
|
+
| Entry kind | CSS class | Badge text |
|
|
413
|
+
|-----------|-----------|------------|
|
|
414
|
+
| Tool call | `.activity-tool` | Tool name |
|
|
415
|
+
| Response | `.activity-response` | `RESPONSE` |
|
|
416
|
+
| Event | `.activity-event` | Event type |
|
|
417
|
+
|
|
418
|
+
### Terminal Tab
|
|
419
|
+
|
|
420
|
+
- Auto-attaches to `session.terminalId` when Terminal tab is selected.
|
|
421
|
+
- `refitTerminal()` called on tab switch to recalculate xterm dimensions.
|
|
422
|
+
- RECONNECT button (`#terminal-reconnect-btn`): visible when `session.terminalId || session.lastTerminalId || session.status === 'ended'`.
|
|
423
|
+
- If active terminal: sends `claude --resume <id> || claude --continue` via WebSocket.
|
|
424
|
+
- If no active terminal: calls `POST /api/sessions/:id/resume` to create new PTY.
|
|
425
|
+
|
|
426
|
+
### Panel Resize
|
|
427
|
+
|
|
428
|
+
Drag handle (`#detail-resize-handle`) on the left edge:
|
|
429
|
+
- Min width: `320 px`
|
|
430
|
+
- Max width: `95 vw`
|
|
431
|
+
- Width persisted in `localStorage['detail-panel-width']`
|
|
432
|
+
- Adds `.resizing` class to panel during drag
|
|
433
|
+
|
|
434
|
+
### Selection Persistence
|
|
435
|
+
|
|
436
|
+
- Selection saved to `localStorage['selected-session']` on select.
|
|
437
|
+
- `restoreSelection()` called after WebSocket snapshot arrives: restores both selected session and active tab.
|
|
438
|
+
|
|
439
|
+
### History View Mode (`openSessionDetailFromHistory`)
|
|
440
|
+
|
|
441
|
+
Opens the detail panel from the History panel using IndexedDB data (not live session state). Renders all conversation types (user, tool, claude) and activity log. Does not show Queue or Notes tabs controls that require a live session.
|
|
442
|
+
|
|
443
|
+
### Live Search Integration
|
|
444
|
+
|
|
445
|
+
`/` key or clicking the search bar (`#live-search`) filters visible cards:
|
|
446
|
+
- Cards not matching query get class `filtered` (hidden via CSS).
|
|
447
|
+
- Match criteria: `projectName`, `cardTitle`, `promptHistory[].text`, `responseLog[].text`.
|
|
448
|
+
- When a session is selected, matching entries in Conversation and Activity tabs get class `search-highlight` (CSS highlight), and the tab containing the first match is automatically activated; the matching entry is scrolled into view.
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## 18. Session Controls (`sessionControls.js`)
|
|
453
|
+
|
|
454
|
+
All control buttons are in the detail panel header/footer area. Button IDs prefixed `ctrl-`.
|
|
455
|
+
|
|
456
|
+
### Resume (`#ctrl-resume`)
|
|
457
|
+
|
|
458
|
+
- Visible only when `session.status === 'ended'`.
|
|
459
|
+
- Calls `POST /api/sessions/:id/resume`.
|
|
460
|
+
- On success: shows toast, switches to Terminal tab.
|
|
461
|
+
- Button text cycles: `RESUME` → `RESUMING...` → `RESUME`.
|
|
462
|
+
|
|
463
|
+
### Kill (`#ctrl-kill`)
|
|
464
|
+
|
|
465
|
+
- Opens confirmation modal (`#kill-modal`).
|
|
466
|
+
- Modal message: `Kill session for "<projectName>"? This will terminate the Claude process (SIGTERM → SIGKILL).`
|
|
467
|
+
- Confirmed: `POST /api/sessions/:id/kill` with body `{ confirm: true }`.
|
|
468
|
+
- On success: deletes associated terminal via `DELETE /api/terminals/:terminalId`, deselects session, plays `kill` sound.
|
|
469
|
+
- Reports `data.pid` in success toast.
|
|
470
|
+
|
|
471
|
+
### Archive (`#ctrl-archive`)
|
|
472
|
+
|
|
473
|
+
- Sets `status: 'ended'`, `archived: 1`, `endedAt: Date.now()` in IndexedDB.
|
|
474
|
+
- Calls `DELETE /api/sessions/:id` to remove from server.
|
|
475
|
+
- Deselects session, removes card, dispatches `card-dismissed` event.
|
|
476
|
+
- Toast: `ARCHIVED — Session moved to history`.
|
|
477
|
+
|
|
478
|
+
### Permanent Delete (`#ctrl-delete`)
|
|
479
|
+
|
|
480
|
+
- Shows `confirm()` dialog: `Permanently delete session "<label>"? This cannot be undone.`
|
|
481
|
+
- Calls `DELETE /api/sessions/:id` + `db.del('sessions', id)`.
|
|
482
|
+
- No cascade delete via IndexedDB `deleteSession()` (only the top-level session record is explicitly deleted here).
|
|
483
|
+
|
|
484
|
+
### Summarize (`#ctrl-summarize`)
|
|
485
|
+
|
|
486
|
+
Opens the summarize modal with a 5-template list. Workflow:
|
|
487
|
+
|
|
488
|
+
1. User selects a template (default auto-selected).
|
|
489
|
+
2. Clicks **RUN SUMMARIZE**.
|
|
490
|
+
3. System assembles context: project info + prompts (ISO timestamps) + tool calls + responses.
|
|
491
|
+
4. `POST /api/sessions/:id/summarize` with `{ context, promptTemplate }`.
|
|
492
|
+
5. On success: updates Summary tab, auto-switches to Summary tab, marks session `archived: 1`.
|
|
493
|
+
6. Button text: `SUMMARIZE` → `SUMMARIZING...` → `RE-SUMMARIZE`.
|
|
494
|
+
|
|
495
|
+
#### Summary Template CRUD
|
|
496
|
+
|
|
497
|
+
| Action | UI element |
|
|
498
|
+
|--------|------------|
|
|
499
|
+
| Select | Click template row (highlighted selected) |
|
|
500
|
+
| Set default | Star (★) button; clears previous default |
|
|
501
|
+
| Edit | Pencil (✎) button; fills form below list |
|
|
502
|
+
| Delete | × button; removes from IndexedDB |
|
|
503
|
+
| Create new | **+ CUSTOM PROMPT** button; shows name + textarea form |
|
|
504
|
+
| Save template | **SAVE AS TEMPLATE** button |
|
|
505
|
+
| Use once | **USE ONCE** button; runs without saving |
|
|
506
|
+
|
|
507
|
+
Template preview shows first `150 characters` of the prompt text.
|
|
508
|
+
|
|
509
|
+
### Alert (`#ctrl-alert`)
|
|
510
|
+
|
|
511
|
+
Sets a duration alert for the selected session:
|
|
512
|
+
- Input: minutes (integer ≥ 1)
|
|
513
|
+
- Stored in `alerts` store: `{ sessionId, thresholdMs: minutes * 60000, triggerAt: now + thresholdMs }`
|
|
514
|
+
- Toast: `ALERT SET — Will alert after N minutes`
|
|
515
|
+
|
|
516
|
+
### Notes (`#tab-notes`)
|
|
517
|
+
|
|
518
|
+
- **Save Note** button (`#save-note`): POSTs to `POST /api/db/sessions/:id/notes`.
|
|
519
|
+
- Notes list refreshed via `GET /api/db/sessions/:id/notes` on every panel open.
|
|
520
|
+
- Each note shows timestamp + DELETE button.
|
|
521
|
+
- DELETE: `DELETE /api/db/notes/:noteId` then refreshes list.
|
|
522
|
+
|
|
523
|
+
### Title (`#detail-title`)
|
|
524
|
+
|
|
525
|
+
- Saves on `blur` or `Enter`.
|
|
526
|
+
- Syncs to server via `PUT /api/sessions/:id/title`.
|
|
527
|
+
- Persists to IndexedDB `sessions` store.
|
|
528
|
+
- Mirrors update to card `.card-title` in DOM.
|
|
529
|
+
|
|
530
|
+
### Label (`#detail-label`)
|
|
531
|
+
|
|
532
|
+
- Text input with datalist suggestions from `localStorage['sessionLabels']` (up to 30 recent labels).
|
|
533
|
+
- Saves on `blur` or `Enter` via `PUT /api/sessions/:id/label`.
|
|
534
|
+
- Persists to IndexedDB.
|
|
535
|
+
- Mirrors update to `.card-label-badge` on card.
|
|
536
|
+
- Saved labels are stored MRU in `localStorage['sessionLabels']` (max 30).
|
|
537
|
+
|
|
538
|
+
#### Label Quick-Select Chips
|
|
539
|
+
|
|
540
|
+
Three built-in label chips always visible:
|
|
541
|
+
|
|
542
|
+
| Label | Icon | Color |
|
|
543
|
+
|-------|------|-------|
|
|
544
|
+
| `ONEOFF` | 🔥 | `#ff9100` |
|
|
545
|
+
| `HEAVY` | ★ | `#ff3355` |
|
|
546
|
+
| `IMPORTANT` | ⚠ | `#aa66ff` |
|
|
547
|
+
|
|
548
|
+
Plus up to 5 custom labels from `localStorage['sessionLabels']`. Clicking a chip toggles the label (click same chip again to clear). Active chip shows colored border + background.
|
|
549
|
+
|
|
550
|
+
### Group Select (`#detail-group-select`)
|
|
551
|
+
|
|
552
|
+
Dropdown with all defined groups + `No group` + `+ New Group` option. `+ New Group` triggers `prompt()` for name, creates group, and immediately moves session card into it.
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## 19. Quick Actions (`quickActions.js`)
|
|
557
|
+
|
|
558
|
+
### Action Bar Buttons
|
|
559
|
+
|
|
560
|
+
| Element ID | Label | Behavior |
|
|
561
|
+
|-----------|-------|----------|
|
|
562
|
+
| `#qa-new-session` | + NEW SESSION | Opens full New Session modal |
|
|
563
|
+
| `#qa-quick-session` | QUICK SESSION | Opens Quick Session modal with no preset label |
|
|
564
|
+
| `#qa-oneoff` | ONEOFF | Opens Quick Session modal with label `ONEOFF` |
|
|
565
|
+
| `#qa-heavy` | HEAVY | Opens Quick Session modal with label `HEAVY` |
|
|
566
|
+
| `#qa-important` | IMPORTANT | Opens Quick Session modal with label `IMPORTANT` |
|
|
567
|
+
| `#qa-mute-all` | MUTE ALL | Toggles global mute; button text toggles |
|
|
568
|
+
| `#qa-archive-ended` | ARCHIVE ENDED | Archives all ended sessions |
|
|
569
|
+
| `#qa-new-group` | NEW GROUP | Creates a new session group |
|
|
570
|
+
|
|
571
|
+
### New Session Modal (`#new-session-modal`)
|
|
572
|
+
|
|
573
|
+
Full SSH connection form:
|
|
574
|
+
|
|
575
|
+
| Field | Default | Notes |
|
|
576
|
+
|-------|---------|-------|
|
|
577
|
+
| Host | `window.location.hostname` | Pre-filled from browser URL |
|
|
578
|
+
| Port | `22` | — |
|
|
579
|
+
| Username | — | — |
|
|
580
|
+
| Auth method | `key` | Options: `key`, `password` |
|
|
581
|
+
| Private key | — | Loaded from `GET /api/ssh-keys` (lists `~/.ssh/` keys) |
|
|
582
|
+
| Password | — | Shown only when auth method = `password` |
|
|
583
|
+
| Working directory | — | Text input with history dropdown |
|
|
584
|
+
| Command preset | — | Options: `claude`, `codex`, `gemini`, `custom` |
|
|
585
|
+
| Custom command | — | Shown only when preset = `custom` |
|
|
586
|
+
| API key | — | Auto-filled from settings for chosen CLI |
|
|
587
|
+
| Terminal theme | `auto` (from settings) | — |
|
|
588
|
+
| Session title | — | Optional |
|
|
589
|
+
| Session label | — | Datalist with saved labels |
|
|
590
|
+
|
|
591
|
+
**Session mode buttons:**
|
|
592
|
+
|
|
593
|
+
| Mode | Behavior |
|
|
594
|
+
|------|----------|
|
|
595
|
+
| New Session | Default; starts a fresh shell |
|
|
596
|
+
| tmux-wrap | Wraps command in a new tmux window |
|
|
597
|
+
| tmux-attach | Lists existing tmux sessions; allows attach |
|
|
598
|
+
|
|
599
|
+
tmux session list shows: name, window count, attached/detached state, age (e.g., `5m ago`).
|
|
600
|
+
|
|
601
|
+
**Connect & Launch** button calls `POST /api/terminals`. On success: saves session config to `localStorage['lastSession']`, saves working directory to history, sets terminal theme, shows toast.
|
|
602
|
+
|
|
603
|
+
**Special label behaviors:**
|
|
604
|
+
- `HEAVY` or `IMPORTANT` sessions: auto-pinned via `pinSession()` after 500 ms.
|
|
605
|
+
- Toast messages differ per label: `HEAVY SESSION — High-priority session launched & pinned`, `ONEOFF SESSION — One-off session launched`.
|
|
606
|
+
|
|
607
|
+
### Quick Session Modal (`#quick-session-modal`)
|
|
608
|
+
|
|
609
|
+
Abbreviated form that reuses `localStorage['lastSession']` connection config:
|
|
610
|
+
|
|
611
|
+
| Field | Notes |
|
|
612
|
+
|-------|-------|
|
|
613
|
+
| Label | Pre-filled from button (ONEOFF/HEAVY/IMPORTANT) or empty |
|
|
614
|
+
| Session title | Optional custom title |
|
|
615
|
+
| Working directory | Defaults to last-used working dir |
|
|
616
|
+
|
|
617
|
+
Requires saved session config (from previous New Session). Shows error toast if no config saved.
|
|
618
|
+
|
|
619
|
+
### Working Directory History
|
|
620
|
+
|
|
621
|
+
- Stored in `localStorage['workdir-history']` (max `20` entries, MRU order).
|
|
622
|
+
- Dropdown toggle button shows history items, each with a delete (×) icon.
|
|
623
|
+
- Closed by clicking outside `.workdir-input-wrapper`.
|
|
624
|
+
|
|
625
|
+
### Session Labels History
|
|
626
|
+
|
|
627
|
+
- Stored in `localStorage['sessionLabels']` (max `30` entries, MRU order).
|
|
628
|
+
- Label chips in Quick Session modal show saved labels with delete icons.
|
|
629
|
+
- Clicking a chip pre-fills the label input.
|
|
630
|
+
|
|
631
|
+
### Mobile FAB (`#mobile-qa-fab`)
|
|
632
|
+
|
|
633
|
+
A floating action button visible on small screens. Tapping opens `#mobile-qa-panel` with all quick action items mirroring desktop buttons. Overlay (`#mobile-qa-overlay`) dismisses the panel on click.
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## 20. Prompt Queue (`promptQueue.js`)
|
|
638
|
+
|
|
639
|
+
### Per-Session Queue (in Detail Panel Queue Tab)
|
|
640
|
+
|
|
641
|
+
The queue tab (`#tab-queue`) shows prompts staged for the selected session.
|
|
642
|
+
|
|
643
|
+
**Queue item DOM:**
|
|
644
|
+
```html
|
|
645
|
+
<div class="queue-item" draggable="true" data-queue-id="...">
|
|
646
|
+
<span class="queue-pos">1</span>
|
|
647
|
+
<div class="queue-text">prompt text</div>
|
|
648
|
+
<div class="queue-actions">
|
|
649
|
+
<button class="queue-send">SEND</button>
|
|
650
|
+
<button class="queue-expand">⤢</button>
|
|
651
|
+
<button class="queue-edit">EDIT</button>
|
|
652
|
+
<button class="queue-move">MOVE</button>
|
|
653
|
+
<button class="queue-delete">DEL</button>
|
|
654
|
+
</div>
|
|
655
|
+
</div>
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
Count badge (`#terminal-queue-count`) shows `(N)` when queue is non-empty.
|
|
659
|
+
|
|
660
|
+
### Item Actions
|
|
661
|
+
|
|
662
|
+
| Button | Behavior |
|
|
663
|
+
|--------|----------|
|
|
664
|
+
| SEND | Sends text to active terminal via WebSocket (`terminal_input`), deletes from IndexedDB, refreshes queue |
|
|
665
|
+
| EXPAND (⤢) | Opens full-screen edit modal (`#queue-expand-modal`) for multi-line editing |
|
|
666
|
+
| EDIT | Replaces `.queue-text` with inline `<textarea>`; SAVE / Enter/Escape |
|
|
667
|
+
| MOVE | Enters **Move Mode** (see below) |
|
|
668
|
+
| DEL | Removes item from IndexedDB, refreshes queue |
|
|
669
|
+
|
|
670
|
+
### Drag-to-Reorder
|
|
671
|
+
|
|
672
|
+
Queue items are draggable within the list. On `dragend`, all visible item IDs are collected in DOM order and `reorderQueue(sessionId, orderedIds)` is called to persist positions.
|
|
673
|
+
|
|
674
|
+
**Drag-to-Terminal:** Queue items can be dragged and dropped onto `#terminal-container`. On drop, sends text to terminal and removes item from queue.
|
|
675
|
+
|
|
676
|
+
### Add to Queue
|
|
677
|
+
|
|
678
|
+
`#queue-add-btn` reads `#queue-textarea`, trims text, calls `db.addToQueue(sessionId, text)`, refreshes queue, syncs count to server via WebSocket `update_queue_count`.
|
|
679
|
+
|
|
680
|
+
### Auto-Send
|
|
681
|
+
|
|
682
|
+
When `autoSendQueue` setting is `'true'`, `tryAutoSend(sessionId, terminalId)` sends the first queue item to the terminal automatically when the terminal tab is focused with an active connection.
|
|
683
|
+
|
|
684
|
+
### Keyboard Shortcut
|
|
685
|
+
|
|
686
|
+
`Ctrl+Enter` (or `Cmd+Enter`): sends the first queued prompt to the terminal and removes it from the queue.
|
|
687
|
+
|
|
688
|
+
### Move Mode
|
|
689
|
+
|
|
690
|
+
`enterQueueMoveMode(itemIds, sourceSessionId)`:
|
|
691
|
+
- Deselects current session.
|
|
692
|
+
- Shows `#move-mode-banner` with text: `Click a session to move N prompt(s)`.
|
|
693
|
+
- Adds `.move-mode` class to `<body>`.
|
|
694
|
+
- Cards: source gets `.move-source`, all others get `.move-target`.
|
|
695
|
+
- Clicking a target card calls `completeQueueMove(targetSessionId)` → `db.moveQueueItems(itemIds, targetSessionId)`.
|
|
696
|
+
- Move mode cancelled via **CANCEL** button (`#move-mode-cancel`).
|
|
697
|
+
- **MOVE ALL** button (`#queue-move-all-btn`) moves all items at once.
|
|
698
|
+
|
|
699
|
+
### Global Queue View (Queue Panel / separate route)
|
|
700
|
+
|
|
701
|
+
`renderQueueView()`:
|
|
702
|
+
- Loads all `promptQueue` store records.
|
|
703
|
+
- Groups by `sessionId`, displays in a table per session.
|
|
704
|
+
- Columns: `#`, `ID`, `Text`, `Position`, `Created`, Delete button.
|
|
705
|
+
- Stats bar: `N items across M sessions`.
|
|
706
|
+
- **EXPORT** button downloads all items as `prompt-queue-<timestamp>.json`.
|
|
707
|
+
- **REFRESH** button reloads from IndexedDB.
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## 21. Session Groups (`sessionGroups.js`)
|
|
712
|
+
|
|
713
|
+
### Default Groups (seeded on first launch)
|
|
714
|
+
|
|
715
|
+
| Name | Order |
|
|
716
|
+
|------|-------|
|
|
717
|
+
| Priority | 0 |
|
|
718
|
+
| Active | 1 |
|
|
719
|
+
| Background | 2 |
|
|
720
|
+
| Review | 3 |
|
|
721
|
+
|
|
722
|
+
Seeding is guarded by `localStorage['groups-seeded']` flag. Groups are persisted in `localStorage['session-groups']` as JSON array.
|
|
723
|
+
|
|
724
|
+
### Group Record Schema
|
|
725
|
+
|
|
726
|
+
```json
|
|
727
|
+
{
|
|
728
|
+
"id": "grp-<timestamp>",
|
|
729
|
+
"name": "Active",
|
|
730
|
+
"sessionIds": ["session-id-1", "session-id-2"],
|
|
731
|
+
"order": 1,
|
|
732
|
+
"colSpan": 6
|
|
733
|
+
}
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### CSS Grid Layout (12-Column System)
|
|
737
|
+
|
|
738
|
+
Groups container (`#groups-container`) uses CSS Grid with 12 equal columns. Each group occupies `colSpan` columns (1–12, min `3`). The `gridColumn: 'span N'` style is applied to each group element.
|
|
739
|
+
|
|
740
|
+
### Layout Presets (5 options)
|
|
741
|
+
|
|
742
|
+
| Preset key | Label | Column spans |
|
|
743
|
+
|------------|-------|-------------|
|
|
744
|
+
| `1-col` | 1 Column | `[12]` |
|
|
745
|
+
| `2-col` | 2 Columns | `[6, 6]` |
|
|
746
|
+
| `3-col` | 3 Columns | `[4, 4, 4]` |
|
|
747
|
+
| `1-3-2-3` | 1/3 + 2/3 | `[4, 8]` |
|
|
748
|
+
| `2-3-1-3` | 2/3 + 1/3 | `[8, 4]` |
|
|
749
|
+
|
|
750
|
+
Each preset button in the layout bar shows an SVG icon visualizing the column proportions. Active preset is highlighted. Saved to `localStorage['dashboard-layout']` as `{ preset, columns: 12 }`.
|
|
751
|
+
|
|
752
|
+
### Group Resize Handles
|
|
753
|
+
|
|
754
|
+
Each group element has a `.group-resize-handle` div at the right edge. Mouse drag:
|
|
755
|
+
- `startColSpan` captured on mousedown.
|
|
756
|
+
- `deltaColSpan = Math.round(dx / (containerWidth / 12))`.
|
|
757
|
+
- New span clamped to `[3, 12]`.
|
|
758
|
+
- On mouseup: saves to `localStorage`, sets preset to `'custom'`.
|
|
759
|
+
|
|
760
|
+
### Group CRUD
|
|
761
|
+
|
|
762
|
+
| Operation | Trigger |
|
|
763
|
+
|-----------|---------|
|
|
764
|
+
| Create | `#qa-new-group` button; `createGroup(name)` |
|
|
765
|
+
| Rename | Double-click `.group-name` → contentEditable |
|
|
766
|
+
| Delete | × button → `deleteGroup(groupId)` → cards moved to `#sessions-grid` |
|
|
767
|
+
| Reorder | Drag `.group-header` to another group |
|
|
768
|
+
|
|
769
|
+
Group drag uses MIME type `application/group-id` to distinguish from card drags. Drop shows `group-drop-left` / `group-drop-right` indicator.
|
|
770
|
+
|
|
771
|
+
### Session Assignment
|
|
772
|
+
|
|
773
|
+
- **Drag card into group grid**: calls `addSessionToGroup(groupId, sessionId)`.
|
|
774
|
+
- **Drag card to `#sessions-grid`**: calls `removeSessionFromGroup(sessionId)`.
|
|
775
|
+
- **Group badge on card**: click → context dropdown with all groups + `Remove from group` + `+ New Group`.
|
|
776
|
+
- **Detail panel group select**: `#detail-group-select` dropdown.
|
|
777
|
+
- **Auto-assign**: new cards are placed in `localStorage['last-used-group']` automatically.
|
|
778
|
+
|
|
779
|
+
### Group Badge on Cards
|
|
780
|
+
|
|
781
|
+
`updateCardGroupBadge(sessionId)`:
|
|
782
|
+
- Shows group name (truncated to 10 chars + `..` if longer) with class `.has-group`.
|
|
783
|
+
- Shows `+` with no group class if session is ungrouped.
|
|
784
|
+
- Click opens context dropdown.
|
|
785
|
+
|
|
786
|
+
### Group Assign Toast
|
|
787
|
+
|
|
788
|
+
Shown for new sessions (max 3 simultaneous, no duplicates):
|
|
789
|
+
- Session title + group select dropdown + `+ New Group` option + `SKIP` button.
|
|
790
|
+
- Auto-dismisses after `15000 ms`.
|
|
791
|
+
|
|
792
|
+
### Auto-Scroll During Drag
|
|
793
|
+
|
|
794
|
+
When dragging near the top/bottom of `#view-live`, the panel scrolls at up to `12 px/frame` (proportional to distance from edge zone of `60 px`).
|
|
795
|
+
|
|
796
|
+
### Collapse/Expand
|
|
797
|
+
|
|
798
|
+
Clicking `.group-collapse` toggles `.collapsed` class on the group element and updates the icon `▼` / `▶`.
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
## 27. History Panel (`historyPanel.js`)
|
|
803
|
+
|
|
804
|
+
### Data Source
|
|
805
|
+
|
|
806
|
+
History panel fetches from the **server-side SQLite database** (not IndexedDB) via REST API:
|
|
807
|
+
- `GET /api/db/projects` — distinct projects for filter dropdown
|
|
808
|
+
- `GET /api/db/sessions?...` — paginated session list
|
|
809
|
+
- `GET /api/db/sessions/:id` — full session detail
|
|
810
|
+
- `DELETE /api/db/sessions/:id` — permanent delete
|
|
811
|
+
|
|
812
|
+
### Filter Controls
|
|
813
|
+
|
|
814
|
+
| Control | Type | Notes |
|
|
815
|
+
|---------|------|-------|
|
|
816
|
+
| Search input (`#search-input`) | Text | Debounced `300 ms`; filters on prompt text |
|
|
817
|
+
| Project filter (`#history-project-filter`) | Select | Populated from `GET /api/db/projects` |
|
|
818
|
+
| Status filter (`#history-status-filter`) | Select | Options include `archived` as special value |
|
|
819
|
+
| Date from (`#history-date-from`) | Date | Converted to ms timestamp; start of day |
|
|
820
|
+
| Date to (`#history-date-to`) | Date | Converted to ms timestamp; end of day (`23:59:59`) |
|
|
821
|
+
| Sort by (`#history-sort-by`) | Select | Maps: `date → started_at`, `duration → last_activity_at` |
|
|
822
|
+
| Sort direction (`#history-sort-dir`) | Toggle button | `DESC` / `ASC`; click to toggle |
|
|
823
|
+
|
|
824
|
+
Filter changes reset `currentPage` to `1`.
|
|
825
|
+
|
|
826
|
+
### Session Row Columns
|
|
827
|
+
|
|
828
|
+
| Column | Field |
|
|
829
|
+
|--------|-------|
|
|
830
|
+
| Title | `title` (empty if none) |
|
|
831
|
+
| Project | `project_name` |
|
|
832
|
+
| Date | `started_at` formatted as `MMM D, YYYY HH:MM` (24h) |
|
|
833
|
+
| Duration | `ended_at - started_at` or `now - started_at` |
|
|
834
|
+
| Status | Color-coded badge |
|
|
835
|
+
| Prompts | `total_prompts` count |
|
|
836
|
+
| Tools | `total_tool_calls` count |
|
|
837
|
+
| Branch | `git_branch` (currently empty) |
|
|
838
|
+
| Delete | × button |
|
|
839
|
+
|
|
840
|
+
### Pagination
|
|
841
|
+
|
|
842
|
+
- Page size: `50` records per page.
|
|
843
|
+
- Shows `Prev` / `Next` buttons and page numbers.
|
|
844
|
+
- Ellipsis (`...`) for gaps in page range (shows pages within ±2 of current).
|
|
845
|
+
- Page buttons disabled when at first/last page.
|
|
846
|
+
|
|
847
|
+
### Row Click → Detail View
|
|
848
|
+
|
|
849
|
+
Clicking a row (not the delete button) calls `openHistoryDetail(sessionId)`:
|
|
850
|
+
1. Fetches `GET /api/db/sessions/:id` for full data.
|
|
851
|
+
2. Populates the same detail panel overlay used for live sessions.
|
|
852
|
+
3. Conversation tab shows interleaved prompts + responses sorted by timestamp.
|
|
853
|
+
4. Activity tab shows merged tool calls + events.
|
|
854
|
+
|
|
855
|
+
### Delete
|
|
856
|
+
|
|
857
|
+
Calls `DELETE /api/db/sessions/:id`, fades row out over `300 ms`, removes from DOM. Re-queries if page becomes empty.
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
## 28. Analytics Panel (`analyticsPanel.js`)
|
|
862
|
+
|
|
863
|
+
### Data Sources
|
|
864
|
+
|
|
865
|
+
All analytics loaded in parallel via `Promise.all`:
|
|
866
|
+
|
|
867
|
+
| API endpoint | Section |
|
|
868
|
+
|-------------|---------|
|
|
869
|
+
| `GET /api/db/analytics/summary` | Summary stats |
|
|
870
|
+
| `GET /api/db/analytics/tools` | Tool usage |
|
|
871
|
+
| `GET /api/db/analytics/projects` | Active projects |
|
|
872
|
+
| `GET /api/db/analytics/heatmap` | Activity heatmap |
|
|
873
|
+
|
|
874
|
+
Duration trends section currently receives an empty array (server endpoint not yet implemented); shows "No duration data" placeholder.
|
|
875
|
+
|
|
876
|
+
### Section 1: Summary Stats Cards
|
|
877
|
+
|
|
878
|
+
Six summary stat cards rendered into `#analytics-summary`:
|
|
879
|
+
|
|
880
|
+
| Label | Field | Detail |
|
|
881
|
+
|-------|-------|--------|
|
|
882
|
+
| Total Sessions | `total_sessions` | "all time" |
|
|
883
|
+
| Total Prompts | `total_prompts` | "all time" |
|
|
884
|
+
| Total Tool Calls | `total_tool_calls` | "all time" |
|
|
885
|
+
| Avg Duration | `avg_duration` (ms) | "per session" |
|
|
886
|
+
| Most Used Tool | `most_used_tool.tool_name` | count calls |
|
|
887
|
+
| Busiest Project | `busiest_project.name` | N sessions |
|
|
888
|
+
|
|
889
|
+
### Section 2: Tool Usage Chart (horizontal bar chart, SVG)
|
|
890
|
+
|
|
891
|
+
Container: `#tool-usage-chart`. Custom SVG chart:
|
|
892
|
+
|
|
893
|
+
| Property | Value |
|
|
894
|
+
|----------|-------|
|
|
895
|
+
| Max tools shown | `15` |
|
|
896
|
+
| Bar height | `20 px` |
|
|
897
|
+
| Gap between bars | `4 px` |
|
|
898
|
+
| Label column width | `120 px` |
|
|
899
|
+
| Value column width | `90 px` |
|
|
900
|
+
| Bar color | `#00e5ff` (cyan), opacity `0.85` → `1.0` on hover |
|
|
901
|
+
| Value display | `count (percentage%)` |
|
|
902
|
+
|
|
903
|
+
Hover tooltip shows: `ToolName: N (X%)`.
|
|
904
|
+
|
|
905
|
+
### Section 3: Duration Trends (line chart, SVG)
|
|
906
|
+
|
|
907
|
+
Container: `#duration-trends-chart`. Line chart with area fill:
|
|
908
|
+
|
|
909
|
+
| Property | Value |
|
|
910
|
+
|----------|-------|
|
|
911
|
+
| Chart height | `250 px` |
|
|
912
|
+
| Padding left | `55 px` |
|
|
913
|
+
| Padding bottom | `30 px` |
|
|
914
|
+
| Y-axis ticks | 5 (every 25% of max) |
|
|
915
|
+
| Y-axis labels | Formatted as `Xh Ym` / `Xm Ys` / `Xs` |
|
|
916
|
+
| X-axis label density | Every `max(1, floor(N/10))` buckets |
|
|
917
|
+
| Line color | `#00e5ff` |
|
|
918
|
+
| Area opacity | `0.1` |
|
|
919
|
+
| Dot radius | `3 px` |
|
|
920
|
+
| Date label format | `MMM D` (from `YYYY-MM-DD` buckets) |
|
|
921
|
+
|
|
922
|
+
Hover on dots shows: `Period: Duration`.
|
|
923
|
+
|
|
924
|
+
### Section 4: Active Projects (horizontal bar chart, SVG)
|
|
925
|
+
|
|
926
|
+
Container: `#active-projects-chart`:
|
|
927
|
+
|
|
928
|
+
| Property | Value |
|
|
929
|
+
|----------|-------|
|
|
930
|
+
| Bar height | `22 px` |
|
|
931
|
+
| Label column width | `130 px` |
|
|
932
|
+
| Value column width | `160 px` |
|
|
933
|
+
| Sort order | `session_count` descending |
|
|
934
|
+
| Bar color | `#00e5ff`, opacity `0.85` → `1.0` on hover |
|
|
935
|
+
| Value format | `N sessions | MMM D` (last active date) |
|
|
936
|
+
|
|
937
|
+
Hover tooltip shows: `ProjectName: N sessions, M prompts, P tools`.
|
|
938
|
+
|
|
939
|
+
### Section 5: Daily Activity Heatmap (CSS Grid)
|
|
940
|
+
|
|
941
|
+
Container: `#daily-heatmap-chart`. 7 rows (Mon–Sun) × 24 columns (hours):
|
|
942
|
+
|
|
943
|
+
| Property | Value |
|
|
944
|
+
|----------|-------|
|
|
945
|
+
| Cell size | `14 × 14 px` |
|
|
946
|
+
| Cell gap | `2 px` |
|
|
947
|
+
| Day label column | `40 px` |
|
|
948
|
+
| Color min (no activity) | `#12122a` |
|
|
949
|
+
| Color max (peak activity) | `#00ff88` |
|
|
950
|
+
| Day order | Monday-first (`0=Mon`, `6=Sun`) |
|
|
951
|
+
| Hour labels | `0`–`23` across top row |
|
|
952
|
+
|
|
953
|
+
Color interpolation: linear RGB blend between min and max by `value / maxValue`. Hover shows: `DayName HH:00 - N events`.
|
|
954
|
+
|
|
955
|
+
---
|
|
956
|
+
|
|
957
|
+
## 29. Timeline Panel (`timelinePanel.js`)
|
|
958
|
+
|
|
959
|
+
### Data Source
|
|
960
|
+
|
|
961
|
+
Timeline uses **IndexedDB** via `getTimeline()` from `browserDb.js` (not server API). Project filter dropdown is populated from server (`GET /api/db/projects`).
|
|
962
|
+
|
|
963
|
+
### Controls
|
|
964
|
+
|
|
965
|
+
| Control ID | Type | Default | Notes |
|
|
966
|
+
|-----------|------|---------|-------|
|
|
967
|
+
| `#timeline-granularity` | Select | `'day'` | Options: `hour`, `day`, `week`, `month` |
|
|
968
|
+
| `#timeline-project-filter` | Select | All projects | — |
|
|
969
|
+
| `#timeline-date-from` | Date | 30 days ago | Set on `init()` |
|
|
970
|
+
| `#timeline-date-to` | Date | Today | Set on `init()` |
|
|
971
|
+
|
|
972
|
+
All control changes trigger `loadTimeline()`.
|
|
973
|
+
|
|
974
|
+
### Chart: Grouped Bar Chart (SVG)
|
|
975
|
+
|
|
976
|
+
Container: `#timeline-chart`. Three bars per time bucket (grouped, not stacked):
|
|
977
|
+
|
|
978
|
+
| Series | Color |
|
|
979
|
+
|--------|-------|
|
|
980
|
+
| Sessions | `#00e5ff` (cyan) |
|
|
981
|
+
| Prompts | `#00ff88` (green) |
|
|
982
|
+
| Tool Calls | `#ff9800` (orange) |
|
|
983
|
+
|
|
984
|
+
| Property | Value |
|
|
985
|
+
|----------|-------|
|
|
986
|
+
| SVG height | `300 px` (320 px for hourly) |
|
|
987
|
+
| Padding left | `50 px` |
|
|
988
|
+
| Padding bottom | `50 px` (70 px for hourly) |
|
|
989
|
+
| Y-axis ticks | 5 (0%, 25%, 50%, 75%, 100% of max) |
|
|
990
|
+
| Y-axis labels | `formatNumber()` values |
|
|
991
|
+
| Bar min width | `2 px` |
|
|
992
|
+
| Bar gap within group | `1 px` |
|
|
993
|
+
| Bar opacity | `0.85` → `1.0` on hover |
|
|
994
|
+
| Corner radius | `2 px` |
|
|
995
|
+
|
|
996
|
+
X-axis label density:
|
|
997
|
+
- Hourly: max `12` labels
|
|
998
|
+
- Weekly: max `12` labels
|
|
999
|
+
- Daily: max `15` labels
|
|
1000
|
+
- Rotation: applied when `groupCount > 10` or granularity = `hour`; labels rotated `-40°`
|
|
1001
|
+
|
|
1002
|
+
X-axis labels are formatted by `formatTimeLabel()`:
|
|
1003
|
+
| Granularity | Format example |
|
|
1004
|
+
|------------|---------------|
|
|
1005
|
+
| `hour` | `Feb 10 14:00` |
|
|
1006
|
+
| `day` | `Feb 10` |
|
|
1007
|
+
| `week` | `Feb 10` (week start date) |
|
|
1008
|
+
| `month` | `Feb` |
|
|
1009
|
+
|
|
1010
|
+
Legend: three colored squares at bottom (`Sessions`, `Prompts`, `Tool Calls`), spaced `100 px` apart.
|
|
1011
|
+
|
|
1012
|
+
Hover on any bar shows tooltip: `Series: value\nSessions: X | Prompts: Y | Tools: Z`.
|
|
1013
|
+
|
|
1014
|
+
---
|
|
1015
|
+
|
|
1016
|
+
## 30. Keyboard Shortcuts (`keyboardShortcuts.js`)
|
|
1017
|
+
|
|
1018
|
+
### All Shortcuts
|
|
1019
|
+
|
|
1020
|
+
| Key | Context | Action |
|
|
1021
|
+
|-----|---------|--------|
|
|
1022
|
+
| `/` | Any (not in input) | Focus `#live-search` |
|
|
1023
|
+
| `?` | Any (not in input) | Toggle shortcuts help modal (`#shortcuts-modal`) |
|
|
1024
|
+
| `S` / `s` | Any (not in input) | Toggle settings modal (`#settings-modal`) |
|
|
1025
|
+
| `K` / `k` | Session selected | Click `#ctrl-kill` (open kill confirmation modal) |
|
|
1026
|
+
| `A` / `a` | Session selected | Click `#ctrl-archive` |
|
|
1027
|
+
| `T` / `t` | Any (not in input) | Show `#new-session-modal` |
|
|
1028
|
+
| `M` / `m` | Any (not in input) | Click `#qa-mute-all` (toggle global mute) |
|
|
1029
|
+
| `Escape` | Terminal tab active | Send escape character `\x1b` to SSH terminal (capture phase) |
|
|
1030
|
+
| `Ctrl+Enter` / `Cmd+Enter` | Any | Send first queued prompt to terminal; remove from queue |
|
|
1031
|
+
|
|
1032
|
+
### Implementation Notes
|
|
1033
|
+
|
|
1034
|
+
- All shortcuts skip when `e.target` is `INPUT`, `TEXTAREA`, `SELECT`, or `contentEditable`.
|
|
1035
|
+
- xterm textarea (`e.target.classList.contains('xterm-helper-textarea')`) is always bypassed.
|
|
1036
|
+
- Modifier keys (`ctrlKey`, `metaKey`, `altKey`) suppress most shortcuts (except `Ctrl+Enter`/`Cmd+Enter`).
|
|
1037
|
+
- Escape runs in **capture phase** (third `addEventListener` argument `true`) to intercept before xterm can handle it, ensuring single-Escape reliably sends `\x1b` to the remote shell.
|
|
1038
|
+
|
|
1039
|
+
### localStorage Keys Reference
|
|
1040
|
+
|
|
1041
|
+
All keys used across frontend modules:
|
|
1042
|
+
|
|
1043
|
+
| Key | Module | Contents |
|
|
1044
|
+
|-----|--------|----------|
|
|
1045
|
+
| `muted-sessions` | sessionCard | JSON array of muted session IDs |
|
|
1046
|
+
| `pinned-sessions` | sessionCard | JSON array of pinned session IDs |
|
|
1047
|
+
| `session-groups` | sessionGroups | JSON array of group objects |
|
|
1048
|
+
| `sessionLabels` | sessionControls/quickActions | JSON array of recent labels (max 30) |
|
|
1049
|
+
| `lastSession` | quickActions | JSON object with SSH connection config |
|
|
1050
|
+
| `selected-session` | detailPanel | Session ID string |
|
|
1051
|
+
| `active-tab` | detailPanel | Tab name string |
|
|
1052
|
+
| `detail-panel-width` | detailPanel | CSS width string (e.g., `420px`) |
|
|
1053
|
+
| `dashboard-layout` | sessionGroups | `{ preset, columns: 12 }` |
|
|
1054
|
+
| `groups-seeded` | sessionGroups | `'1'` flag |
|
|
1055
|
+
| `workdir-history` | quickActions | JSON array of working dirs (max 20) |
|
|
1056
|
+
| `last-used-group` | sessionGroups | Group ID string |
|
|
1057
|
+
| `debug` | utils | `'true'` to enable debug logging |
|