ai-agent-session-center 2.10.15 → 2.10.34
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/LICENSE +21 -0
- package/README.md +211 -224
- package/dist/client/assets/AgendaView-DIeog3Y0.js +1 -0
- package/dist/client/assets/{CyberdromeScene-DdUd3eqZ.js → CyberdromeScene-DIkIKV_K.js} +2 -2
- package/dist/client/assets/HistoryView-BipuQyNX.js +1 -0
- package/dist/client/assets/QueueView-6AqQu3Mp.js +1 -0
- package/dist/client/assets/ReviewView-B4enbk_e.js +1 -0
- package/dist/client/assets/ReviewView-e1xjt7tx.css +1 -0
- package/dist/client/assets/index-C7eUMmiz.js +220 -0
- package/dist/client/assets/index-Dk_4ZsJt.css +1 -0
- package/dist/client/index.html +2 -2
- package/hooks/dashboard-hook-codex.sh +88 -34
- package/hooks/dashboard-hook-gemini.sh +2 -1
- package/hooks/dashboard-hook.sh +8 -0
- package/hooks/install-hooks-api.cjs +40 -12
- package/hooks/install-hooks-api.js +42 -15
- package/hooks/install-hooks-core.cjs +131 -1
- package/hooks/install-hooks-core.js +150 -0
- package/hooks/install-hooks.js +10 -10
- package/hooks/reset.js +7 -20
- package/package.json +2 -2
- package/server/apiRouter.ts +439 -113
- package/server/commandIndex.ts +397 -0
- package/server/config.ts +75 -2
- package/server/constants.ts +39 -2
- package/server/db.ts +41 -9
- package/server/extractPreviousAnswer.ts +274 -0
- package/server/floatingPrompt.ts +179 -0
- package/server/floatingSessionSpawner.ts +239 -0
- package/server/hookInstaller.js +31 -11
- package/server/index.ts +27 -3
- package/server/sessionMatcher.ts +64 -9
- package/server/sessionStore.ts +169 -68
- package/server/sessionTitle.ts +56 -0
- package/server/sshManager.ts +84 -6
- package/dist/client/assets/AgendaView-CSfXWDng.js +0 -1
- package/dist/client/assets/HistoryView-ChDFadP4.js +0 -1
- package/dist/client/assets/ProjectBrowserView-DCwNP6Si.js +0 -1
- package/dist/client/assets/QueueView-COiAfnKU.js +0 -1
- package/dist/client/assets/index-B1i4xmXp.js +0 -215
- package/dist/client/assets/index-CSzKiYbX.css +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kason Zhan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,166 +1,104 @@
|
|
|
1
1
|
# AI Agent Session Center
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Monitor, drive, queue, and resume every Claude Code, Gemini, and Codex session from one localhost dashboard — each rendered as a live 3D robot you can click into.**
|
|
4
|
+
|
|
5
|
+
*Built for developers juggling multiple AI coding agents across terminals and machines.*
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/ai-agent-session-center)
|
|
6
|
-
[](https://www.npmjs.com/package/ai-agent-session-center)
|
|
7
8
|
[](https://nodejs.org)
|
|
8
|
-
[](./LICENSE)
|
|
9
|
-
|
|
10
|
-
## Demo
|
|
11
|
-
|
|
12
|
-
**[Live demo → aasc.work/demo](https://aasc.work/demo)**
|
|
13
|
-
|
|
14
|
-
### Desktop
|
|
15
|
-
|
|
16
|
-
<table>
|
|
17
|
-
<tr>
|
|
18
|
-
<td><img src="static/screenshot-dashboard.png" alt="3D cyberdrome with active agent sessions across project rooms" width="400"></td>
|
|
19
|
-
<td><img src="static/screenshot-terminal.png" alt="SSH terminal session — control agents from the dashboard" width="400"></td>
|
|
20
|
-
</tr>
|
|
21
|
-
<tr>
|
|
22
|
-
<td><img src="static/screenshot-project-tab-detailed.png" alt="Split view with detailed session switcher cards — terminal and project file browser side by side" width="400"></td>
|
|
23
|
-
<td><img src="static/screenshot-project-tab-compact.png" alt="Split view with compact session switcher — terminal and project file browser side by side" width="400"></td>
|
|
24
|
-
</tr>
|
|
25
|
-
</table>
|
|
26
|
-
|
|
27
|
-
### Mobile
|
|
28
|
-
|
|
29
|
-
<table>
|
|
30
|
-
<tr>
|
|
31
|
-
<td><img src="static/screenshot-mobile-home.png" alt="Mobile — 3D cyberdrome with session list" width="160"></td>
|
|
32
|
-
<td><img src="static/screenshot-mobile-terminal.png" alt="Mobile — terminal tab with live conversation" width="160"></td>
|
|
33
|
-
<td><img src="static/screenshot-mobile-project.png" alt="Mobile — project file browser" width="160"></td>
|
|
34
|
-
<td><img src="static/screenshot-mobile-history.png" alt="Mobile — session history with filters" width="160"></td>
|
|
35
|
-
</tr>
|
|
36
|
-
</table>
|
|
37
|
-
|
|
38
|
-
### Video
|
|
39
|
-
|
|
40
|
-
https://github.com/user-attachments/assets/004ee6f9-942c-44c2-a4c5-d971fa0e824b
|
|
9
|
+
[](./LICENSE)
|
|
41
10
|
|
|
42
|
-
|
|
11
|
+
### **[▶ Try it live — zero install, runs in your browser → aasc.work/demo](https://aasc.work/demo)**
|
|
43
12
|
|
|
44
|
-
|
|
13
|
+
<img src="static/screenshot-dashboard.png" alt="3D cyberdrome with active agent sessions across project rooms" width="100%">
|
|
45
14
|
|
|
46
|
-
|
|
15
|
+
**Jump to:** [Install](#install) · [Why power users keep it open](#why-power-users-keep-it-open) · [What's inside](#whats-inside) · [Under the hood](#under-the-hood) · [Commands](#commands) · [Troubleshooting](#troubleshooting) · [FAQ](#faq)
|
|
47
16
|
|
|
48
|
-
|
|
17
|
+
> You're running Claude Code in one terminal, Gemini in another, Codex in a third. Which one is **stuck waiting for approval**? Which one **finished** and needs your next prompt? Which one is **burning tokens on a runaway loop**? Agent Session Center watches all of them at once and surfaces the one that needs you — so you stop tab-juggling and only step in when it matters.
|
|
49
18
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- **Dynamic room system** — four-quadrant office layout with desks, coffee lounge, gym, corridor rooms, and spatial navigation AI
|
|
54
|
-
- **Subagent connections** — parent-child agent teams render as connected robots with animated laser-line beams
|
|
55
|
-
- **Speech bubbles & particles** — floating dialogue shows current tool/prompt, burst particles on state transitions
|
|
56
|
-
- **Camera fly-to** — smooth animated camera focuses on the selected robot; OrbitControls for manual pan/zoom/rotate
|
|
57
|
-
- **Flat list fallback** — 2D sidebar list auto-activates if 3D crashes or for low-resource environments
|
|
58
|
-
|
|
59
|
-
### Session Detail Panel
|
|
60
|
-
|
|
61
|
-
- **Resizable detail panel** — slides in from the right (320px–95vw) with 7 tabs: Terminal, Prompts, Project, Queue, Notes, Activity, Summary
|
|
62
|
-
- **Session switcher** — horizontal tab strip shows all active sessions with sequence numbers, pin/unpin, compact/detailed display modes
|
|
63
|
-
- **Editable metadata** — inline-edit title, label, accent color (customizes robot glow), and pin state; all persisted to SQLite + IndexedDB
|
|
64
|
-
- **Session controls** — Resume, Kill, Archive, Delete, Summarize, Alert, room assignment, and label chips (ONEOFF, HEAVY, IMPORTANT)
|
|
65
|
-
- **Split view** — Terminal and Project side-by-side with a draggable divider (ratio persisted per session)
|
|
66
|
-
- **Approval alerts** — yellow card, visor flash, and 3-burst alarm when tools need user approval
|
|
67
|
-
|
|
68
|
-
### Terminal & SSH
|
|
69
|
-
|
|
70
|
-
- **Full terminal emulation** — xterm.js 5 with 256 colors, Unicode 11, WebLinks, and FitAddon
|
|
71
|
-
- **Local & SSH sessions** — create terminals with working directory, command, SSH host/key/password, tmux wrap/attach
|
|
72
|
-
- **Session resume** — reconnect to disconnected Claude sessions via `claude --resume` with one click
|
|
73
|
-
- **Terminal bookmarks** — save scroll positions with notes, jump back to any bookmarked line
|
|
74
|
-
- **Terminal toolbar** — fullscreen, clear, copy, paste, theme selector (auto, light, dark, Solarized, Dracula, custom)
|
|
75
|
-
- **Bidirectional WebSocket relay** — real-time I/O, 50ms debounced resize, Escape forwards `\x1b` to SSH
|
|
76
|
-
|
|
77
|
-
### Project File Browser
|
|
19
|
+
```bash
|
|
20
|
+
npx ai-agent-session-center
|
|
21
|
+
```
|
|
78
22
|
|
|
79
|
-
|
|
80
|
-
- **Syntax highlighting** — code viewer with line numbers, word wrap toggle, and markdown outline panel
|
|
81
|
-
- **Sub-tab system** — open multiple directories/files in tabs within the Project panel
|
|
82
|
-
- **File bookmarks** — save line references with notes; bookmarked lines highlighted in the code viewer; cross-file navigation
|
|
83
|
-
- **Sort & filter** — sort by name or date, toggle date/time display, file size shown per entry
|
|
84
|
-
- **File editing** — inline editor with save support for quick edits
|
|
23
|
+
The dashboard opens at **http://localhost:3333** and registers lightweight, read-only hooks automatically — no manual config, fully reversible with `--uninstall`. Robots appear the moment you use any AI CLI in any terminal.
|
|
85
24
|
|
|
86
|
-
|
|
25
|
+
Not ready to install? **[Try the live demo first — no setup → aasc.work/demo](https://aasc.work/demo)**
|
|
87
26
|
|
|
88
|
-
|
|
89
|
-
- **3 hook density levels** — High (full monitoring), Medium (default, 12 events), Low (5 events, minimal overhead)
|
|
90
|
-
- **File-based message queue** — hooks append to JSONL queue file via atomic POSIX append (~0.1ms); HTTP POST fallback
|
|
91
|
-
- **3–17ms end-to-end latency** — from hook fired to browser updated
|
|
92
|
-
- **5-priority session matching** — pending resume, terminal ID, working directory, path scan, PID parent check
|
|
93
|
-
- **Approval detection** — tool-category timeouts with child-process check; `PermissionRequest` event for reliable signal
|
|
94
|
-
- **CLI badge detection** — auto-detects CLAUDE, GEMINI, CODEX, or AIDER from launch command
|
|
27
|
+
---
|
|
95
28
|
|
|
96
|
-
|
|
29
|
+
## Why power users keep it open
|
|
97
30
|
|
|
98
|
-
|
|
99
|
-
- **Per-session queue** — compose, reorder (drag-and-drop), send, and move prompts between sessions
|
|
100
|
-
- **Auto-send mode** — queued prompts auto-dispatch when the target session becomes idle
|
|
31
|
+
A control plane for serious multi-agent work — built to be left open all day.
|
|
101
32
|
|
|
102
|
-
|
|
33
|
+
| Without it | With Agent Session Center |
|
|
34
|
+
|------------|---------------------------|
|
|
35
|
+
| Alt-tab through a wall of terminals to find the blocked one | The robot that needs you flashes, alarms, and surfaces a colored alert card |
|
|
36
|
+
| Copy-paste prompts into whichever window is free | Queue prompts per session and auto-fire them the moment it goes idle |
|
|
37
|
+
| Babysit long runs so you don't miss the approval prompt | Walk away — scheduled loops and quiet-hours windows run hands-off |
|
|
38
|
+
| Lose your whole layout when a session or machine restarts | Workspace snapshots rebuild sessions, tabs, rooms, and scrollback |
|
|
103
39
|
|
|
104
|
-
- **
|
|
105
|
-
- **
|
|
106
|
-
- **
|
|
40
|
+
- **One dashboard replaces a wall of terminals.** Monitor and drive every Claude Code, Gemini, and Codex session across local and remote machines — live terminals, full conversation transcripts, tool logs, and a file browser, in one view.
|
|
41
|
+
- **Drive agents from live terminals — even on a second monitor.** Real xterm.js terminals with SSH/tmux support, and you can pop any terminal or project panel out into its own native desktop window and fling it to another display.
|
|
42
|
+
- **Select-to-explain, anywhere.** Highlight text in any terminal or transcript and fork a floating picture-in-picture AI session to explain, translate, or define it.
|
|
43
|
+
- **Engineered for always-on use.** 3–17ms hook-to-screen latency, 700+ Vitest tests, a native Electron desktop app, and a fully mobile-responsive browser UI.
|
|
107
44
|
|
|
108
|
-
|
|
45
|
+
---
|
|
109
46
|
|
|
110
|
-
|
|
111
|
-
- **16 synthesized sounds** — per-CLI profiles with per-event sound mapping (chime, ping, alarm, fanfare, etc.)
|
|
112
|
-
- **6 ambient presets** — rain, lo-fi, server room, deep space, coffee shop, or off
|
|
113
|
-
- **Visual effects** — glowing card borders, pulsing animations, scanline CRT overlay, status particles, fog depth by theme
|
|
47
|
+
## See it
|
|
114
48
|
|
|
115
|
-
###
|
|
49
|
+
### Walkthrough video
|
|
116
50
|
|
|
117
|
-
-
|
|
118
|
-
- **AI-powered summaries** — generate session summaries via configured LLM API with customizable prompt templates
|
|
51
|
+
https://github.com/user-attachments/assets/004ee6f9-942c-44c2-a4c5-d971fa0e824b
|
|
119
52
|
|
|
120
|
-
###
|
|
53
|
+
### Desktop
|
|
121
54
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
-
|
|
55
|
+
<table>
|
|
56
|
+
<tr>
|
|
57
|
+
<td><img src="static/screenshot-terminal.png" alt="Live terminal session — drive agents straight from the dashboard" width="400"></td>
|
|
58
|
+
<td><img src="static/screenshot-project-tab-detailed.png" alt="Split view, detailed session switcher — terminal and project file browser side by side" width="400"></td>
|
|
59
|
+
</tr>
|
|
60
|
+
</table>
|
|
125
61
|
|
|
126
|
-
###
|
|
62
|
+
### Mobile
|
|
127
63
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
-
|
|
64
|
+
<table>
|
|
65
|
+
<tr>
|
|
66
|
+
<td><img src="static/screenshot-mobile-home.png" alt="Mobile — 3D cyberdrome with session list" width="160"></td>
|
|
67
|
+
<td><img src="static/screenshot-mobile-terminal.png" alt="Mobile — terminal tab with live conversation" width="160"></td>
|
|
68
|
+
<td><img src="static/screenshot-mobile-project.png" alt="Mobile — project file browser" width="160"></td>
|
|
69
|
+
<td><img src="static/screenshot-mobile-history.png" alt="Mobile — session history with filters" width="160"></td>
|
|
70
|
+
</tr>
|
|
71
|
+
</table>
|
|
131
72
|
|
|
132
|
-
|
|
73
|
+
---
|
|
133
74
|
|
|
134
|
-
|
|
135
|
-
- **Auto-snapshots** — server saves full state every 10 seconds; SSH terminals auto-respawn on restart
|
|
136
|
-
- **Session ID migration** — seamless re-keying when sessions resume with new IDs
|
|
75
|
+
## Install
|
|
137
76
|
|
|
138
|
-
|
|
77
|
+
```bash
|
|
78
|
+
npx ai-agent-session-center
|
|
79
|
+
```
|
|
139
80
|
|
|
140
|
-
|
|
81
|
+
That's the happy path. The dashboard starts at **http://localhost:3333** (configurable) and configures hooks automatically.
|
|
141
82
|
|
|
142
|
-
-
|
|
143
|
-
- **jq** (recommended) for hook enrichment — hooks still work without it but with less metadata
|
|
144
|
-
- One or more supported AI CLIs:
|
|
145
|
-
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
|
|
146
|
-
- [Gemini CLI](https://github.com/google-gemini/gemini-cli)
|
|
147
|
-
- [Codex CLI](https://github.com/openai/codex)
|
|
83
|
+
**What it changes on your machine:** read-only hook entries added to your AI CLI settings (e.g. `~/.claude/settings.json`, via atomic write) and a queue file under `/tmp/claude-session-center/`. Nothing in the CLIs themselves is modified, and it's fully reversible with `--uninstall`.
|
|
148
84
|
|
|
149
|
-
|
|
85
|
+
<details>
|
|
86
|
+
<summary><b>Other install options</b></summary>
|
|
150
87
|
|
|
151
|
-
###
|
|
88
|
+
### Global install
|
|
152
89
|
|
|
153
90
|
```bash
|
|
154
|
-
|
|
91
|
+
npm install -g ai-agent-session-center
|
|
92
|
+
ai-agent-session-center
|
|
155
93
|
```
|
|
156
94
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
### Global Install (if npx is not available)
|
|
95
|
+
### From source
|
|
160
96
|
|
|
161
97
|
```bash
|
|
162
|
-
|
|
163
|
-
ai-agent-session-center
|
|
98
|
+
git clone https://github.com/coding-by-feng/ai-agent-session-center.git
|
|
99
|
+
cd ai-agent-session-center
|
|
100
|
+
npm install
|
|
101
|
+
npm run dev
|
|
164
102
|
```
|
|
165
103
|
|
|
166
104
|
### Uninstall
|
|
@@ -173,24 +111,25 @@ npx ai-agent-session-center --uninstall
|
|
|
173
111
|
npm uninstall -g ai-agent-session-center
|
|
174
112
|
```
|
|
175
113
|
|
|
176
|
-
|
|
114
|
+
</details>
|
|
177
115
|
|
|
178
|
-
|
|
179
|
-
git clone https://github.com/coding-by-feng/ai-agent-session-center.git
|
|
180
|
-
cd ai-agent-session-center
|
|
181
|
-
npm install
|
|
182
|
-
npm run dev
|
|
183
|
-
```
|
|
116
|
+
### Requirements
|
|
184
117
|
|
|
185
|
-
|
|
118
|
+
- **Node.js 18+** with npm
|
|
119
|
+
- **jq** (recommended) for hook enrichment — hooks still work without it, with less metadata
|
|
120
|
+
- One or more supported AI CLIs:
|
|
121
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
|
|
122
|
+
- [Gemini CLI](https://github.com/google-gemini/gemini-cli)
|
|
123
|
+
- [Codex CLI](https://github.com/openai/codex)
|
|
124
|
+
|
|
125
|
+
### First run
|
|
186
126
|
|
|
187
|
-
1. Start the dashboard — robots appear automatically as you use AI CLIs in any terminal
|
|
188
|
-
2. Click **+ New** to
|
|
189
|
-
3. Click a robot to
|
|
190
|
-
4.
|
|
191
|
-
5. Open **Settings** to customize themes, sounds, and hook density
|
|
127
|
+
1. Start the dashboard — robots appear automatically as you use AI CLIs in any terminal.
|
|
128
|
+
2. Click **+ New** to spawn local or SSH terminal sessions directly from the dashboard.
|
|
129
|
+
3. Click a robot to open its detail panel (Project, Terminal, Commands, Conversation, AI Popups, Notes, Queue).
|
|
130
|
+
4. Queue prompts, assign sessions to rooms, and open **Settings** to tune themes, sounds, and hook density.
|
|
192
131
|
|
|
193
|
-
### CLI
|
|
132
|
+
### CLI options
|
|
194
133
|
|
|
195
134
|
```bash
|
|
196
135
|
ai-agent-session-center [options]
|
|
@@ -203,98 +142,88 @@ Options:
|
|
|
203
142
|
--uninstall Remove all hooks from CLI configs and exit
|
|
204
143
|
```
|
|
205
144
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
Agent Session Center uses lightweight bash hooks that append JSON events to a file-based message queue (`/tmp/claude-session-center/queue.jsonl`). The server watches this file and broadcasts updates to connected browsers via WebSocket.
|
|
209
|
-
|
|
210
|
-
No modifications to any CLI are needed. The hooks are purely observational and add negligible overhead (~2-5ms per event). **End-to-end latency: 3-17ms** from hook fired to browser updated.
|
|
211
|
-
|
|
212
|
-
```
|
|
213
|
-
AI CLI (Claude / Gemini / Codex)
|
|
214
|
-
|
|
|
215
|
-
Hook Script (bash) ~2-5ms
|
|
216
|
-
- Enriches with PID, TTY, terminal env
|
|
217
|
-
|
|
|
218
|
-
/tmp/.../queue.jsonl ~0.1ms
|
|
219
|
-
- Atomic POSIX append
|
|
220
|
-
|
|
|
221
|
-
Server (Express + WebSocket) ~0.5ms
|
|
222
|
-
- Validate, process, broadcast
|
|
223
|
-
|
|
|
224
|
-
React Frontend
|
|
225
|
-
- 3D scene + detail panels update
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### Session Matching
|
|
229
|
-
|
|
230
|
-
When a hook event arrives, a 5-priority fallback system links it to the correct terminal session: pending resume match, terminal ID env var, working directory match, path scan, and PID parent check. If no match is found, a display-only card is created with the detected source (VS Code, iTerm, Warp, Ghostty, etc.).
|
|
145
|
+
---
|
|
231
146
|
|
|
232
|
-
|
|
147
|
+
## What's inside
|
|
233
148
|
|
|
234
|
-
|
|
235
|
-
|-------|--------|----------|
|
|
236
|
-
| high | All 14 Claude events | Full monitoring, approval detection |
|
|
237
|
-
| medium | 12 events | Default, good balance |
|
|
238
|
-
| low | 5 events | Minimal overhead |
|
|
149
|
+
A quick tour. The complete reference — 45 feature docs with architecture and APIs — lives in [`docs/feature/README.md`](docs/feature/README.md).
|
|
239
150
|
|
|
240
|
-
|
|
151
|
+
- **3D cyberdrome** — 6 procedural robot models, 8 animation states, user-created project rooms plus a coffee lounge with spatial navigation, subagent laser-link beams, camera fly-to, and a 2D flat-list fallback.
|
|
152
|
+
- **Session detail panel** — a resizable 7-tab panel (Project, Terminal, Commands, Conversation, AI Popups, Notes, Queue), session switcher, inline-edit title/label/accent color, and split view.
|
|
153
|
+
- **Terminal & SSH** — xterm.js with local/SSH/tmux sessions, dual transport (IPC in Electron / WebSocket in browser), `claude --resume`, pop-out to native desktop windows, bookmarks, select-to-explain, and hold-to-speak text-to-speech.
|
|
154
|
+
- **Project browser** — lazy file tree, fuzzy find over a cached index, syntax highlighting, image/TeX viewers, inline editing, and `/`-command + `@`-file autocomplete.
|
|
155
|
+
- **Multi-CLI monitoring** — automatically links every hook event to the right terminal session with no per-session setup, via an **8-priority matching cascade** (display-only fallback when nothing matches). Works with Claude Code, Gemini, and Codex, with `--model` / `--effort` launch flags.
|
|
156
|
+
- **Queue, scheduler & loops** — a global and per-session queue with drag-reorder, auto-send-on-idle, scheduled loops with quiet-hours windows, and exportable queue history.
|
|
157
|
+
- **Workspace snapshots** — full layout serialization, 10s server auto-snapshots, SSH auto-respawn on restart, and seamless session-ID re-keying on resume.
|
|
158
|
+
- **History & search** — full-text history search across titles, projects, and labels, with date, status, and sort filters.
|
|
159
|
+
- **Theming & sound** — **9 scene themes**, **15 synthesized event sounds** with per-CLI profiles, **5 ambient presets**, and CRT/scanline + particle effects.
|
|
160
|
+
- **Security** — optional password login (rate-limited), a localhost-only hook endpoint, and directory-traversal + SSH-injection guards.
|
|
161
|
+
- **Desktop & mobile** — a native Electron app (macOS first-class, Windows supported; system tray, multi-window) plus a fully responsive browser UI that runs anywhere Node does.
|
|
241
162
|
|
|
242
|
-
|
|
243
|
-
- **Frontend**: React 19, TypeScript, Vite
|
|
244
|
-
- **3D Visualization**: Three.js, React Three Fiber, drei
|
|
245
|
-
- **State Management**: Zustand, React Query
|
|
246
|
-
- **Terminal**: xterm.js, node-pty
|
|
247
|
-
- **Database**: SQLite (server, WAL mode) + IndexedDB via Dexie (browser)
|
|
248
|
-
- **Hooks**: Bash scripts (file-based MQ primary, HTTP fallback)
|
|
249
|
-
- **Testing**: Vitest (400+ tests) + Playwright (E2E)
|
|
250
|
-
- **Charts**: Recharts
|
|
251
|
-
- **Drag & Drop**: @dnd-kit
|
|
163
|
+
---
|
|
252
164
|
|
|
253
|
-
## Session
|
|
165
|
+
## Session state machine
|
|
254
166
|
|
|
255
167
|
| Status | What it means | Visual |
|
|
256
168
|
|--------|---------------|--------|
|
|
257
169
|
| **Idle** | No activity | Green, robot seeks coffee lounge |
|
|
258
|
-
| **Prompting** | You just sent a prompt | Cyan, robot walks to desk |
|
|
170
|
+
| **Prompting** | You just sent a prompt | Cyan, robot walks to its desk |
|
|
259
171
|
| **Working** | Agent is calling tools | Orange, charging effect |
|
|
260
|
-
| **Waiting** | Agent finished, your turn | Cyan, robot
|
|
172
|
+
| **Waiting** | Agent finished, your turn | Cyan, robot heads to the coffee lounge |
|
|
261
173
|
| **Approval** | Tool blocked, needs yes/no | Yellow, visor flash, alarm |
|
|
262
174
|
| **Input** | Waiting for your answer | Purple, arm raised |
|
|
263
175
|
| **Ended** | Session closed | Red, offline animation |
|
|
264
176
|
|
|
265
|
-
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Under the hood
|
|
180
|
+
|
|
181
|
+
Lightweight bash hooks append JSON events to a file-based message queue (`/tmp/claude-session-center/queue.jsonl`). The server watches the file and broadcasts updates to connected browsers over WebSocket. No CLI is modified — the hooks are purely observational and add ~2–5ms per event. **End-to-end latency is 3–17ms** from hook fired to browser updated (measured locally; the upper bound includes the React render and 3D scene update).
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
AI CLI (Claude / Gemini / Codex)
|
|
185
|
+
|
|
|
186
|
+
Hook script (bash) ~2-5ms
|
|
187
|
+
- enriches with PID, TTY, terminal env
|
|
188
|
+
|
|
|
189
|
+
/tmp/.../queue.jsonl ~0.1ms
|
|
190
|
+
- atomic POSIX append
|
|
191
|
+
|
|
|
192
|
+
Server (Express + WebSocket) ~0.5ms
|
|
193
|
+
- validate, process, broadcast
|
|
194
|
+
|
|
|
195
|
+
React frontend + render
|
|
196
|
+
- 3D scene + detail panels update
|
|
197
|
+
```
|
|
266
198
|
|
|
267
|
-
|
|
268
|
-
|-----|--------|
|
|
269
|
-
| `/` | Focus search |
|
|
270
|
-
| `Escape` | Close modal / deselect session |
|
|
271
|
-
| `?` | Toggle shortcuts panel |
|
|
272
|
-
| `S` | Toggle settings |
|
|
273
|
-
| `K` | Kill selected session |
|
|
274
|
-
| `A` | Archive selected session |
|
|
275
|
-
| `T` | New terminal session |
|
|
276
|
-
| `M` | Mute/unmute all |
|
|
199
|
+
### Session matching
|
|
277
200
|
|
|
278
|
-
|
|
201
|
+
Each hook event is linked to the right terminal session via an **8-priority fallback cascade** — pending-resume, terminal-ID, working-directory, path-scan, and PID-parent strategies, plus fork-routing and PID-cache sub-steps. If nothing matches, a display-only card is created with the detected source (VS Code, iTerm, Warp, Ghostty, etc.).
|
|
279
202
|
|
|
280
|
-
|
|
281
|
-
- **Approval detection timing** — auto-approved long-running commands (npm install, builds) will briefly show as "waiting for approval" for ~8 seconds until the post-tool event clears it.
|
|
282
|
-
- **macOS/Linux focused** — primary development is on macOS. Linux should work identically. Windows support via PowerShell hook variant is less tested.
|
|
283
|
-
- **3D scene performance** — with many concurrent sessions (20+), the Three.js scene may impact performance on lower-end hardware.
|
|
203
|
+
### Hook density levels
|
|
284
204
|
|
|
285
|
-
|
|
205
|
+
| Level | Behavior |
|
|
206
|
+
|-------|----------|
|
|
207
|
+
| high | All Claude hook events — fullest monitoring and approval detection |
|
|
208
|
+
| medium | Default — good balance of detail and overhead |
|
|
209
|
+
| low | Minimal event set for the lowest overhead |
|
|
286
210
|
|
|
287
|
-
|
|
211
|
+
### Tech stack
|
|
288
212
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
213
|
+
| Layer | Technology |
|
|
214
|
+
|-------|------------|
|
|
215
|
+
| Backend | Node.js 18+ (ESM), Express 5, ws 8, tsx |
|
|
216
|
+
| Frontend | React 19, TypeScript, Vite 7, Zustand 5, @tanstack/react-query 5 |
|
|
217
|
+
| 3D | Three.js 0.182, @react-three/fiber 9, @react-three/drei 10 |
|
|
218
|
+
| Terminal | @xterm/xterm 5.5 (+ fit, unicode11, web-links), node-pty 1.1 |
|
|
219
|
+
| Desktop | Electron 34, electron-builder 25 |
|
|
220
|
+
| Database | better-sqlite3 12 (WAL) + Dexie 4 (IndexedDB, 15 tables) |
|
|
221
|
+
| Forms / validation | react-hook-form 7 + Zod 4 |
|
|
222
|
+
| Markdown | react-markdown 10 + rehype-highlight |
|
|
223
|
+
| Routing / misc | react-router 7 · latex.js · xlsx · auto-launch |
|
|
224
|
+
| Testing | Vitest 4 (700+ tests) + Playwright 1.58 (E2E) |
|
|
296
225
|
|
|
297
|
-
|
|
226
|
+
---
|
|
298
227
|
|
|
299
228
|
## Commands
|
|
300
229
|
|
|
@@ -306,15 +235,19 @@ npm run setup # Interactive setup wizard
|
|
|
306
235
|
npm run install-hooks # Install hooks into CLI configs
|
|
307
236
|
npm run uninstall-hooks # Remove all dashboard hooks
|
|
308
237
|
npm run reset # Reset everything (hooks, config, backup)
|
|
309
|
-
npm test # Run tests (
|
|
238
|
+
npm test # Run tests (Vitest)
|
|
310
239
|
npm run test:watch # Watch mode
|
|
311
240
|
npm run test:e2e # E2E tests (Playwright)
|
|
241
|
+
npm run electron:dev # Build + launch the Electron app
|
|
242
|
+
npm run electron:build # Build a distributable (DMG / NSIS)
|
|
312
243
|
npm run debug # Start with verbose logging
|
|
313
244
|
```
|
|
314
245
|
|
|
246
|
+
---
|
|
247
|
+
|
|
315
248
|
## Troubleshooting
|
|
316
249
|
|
|
317
|
-
### Hooks
|
|
250
|
+
### Hooks not firing
|
|
318
251
|
|
|
319
252
|
```bash
|
|
320
253
|
# Verify hooks are registered
|
|
@@ -327,33 +260,87 @@ echo '{"session_id":"test","hook_event_name":"SessionStart"}' | ~/.claude/hooks/
|
|
|
327
260
|
npm run install-hooks
|
|
328
261
|
```
|
|
329
262
|
|
|
330
|
-
### Port 3333 in
|
|
263
|
+
### Port 3333 in use
|
|
331
264
|
|
|
332
|
-
The server auto-resolves port conflicts. To
|
|
265
|
+
The server auto-resolves port conflicts. To force another port:
|
|
333
266
|
|
|
334
267
|
```bash
|
|
335
268
|
npx ai-agent-session-center --port 4444
|
|
336
269
|
PORT=4444 npm start
|
|
337
270
|
```
|
|
338
271
|
|
|
339
|
-
### jq
|
|
272
|
+
### jq not installed
|
|
340
273
|
|
|
341
274
|
```bash
|
|
342
|
-
# macOS
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
# Ubuntu/Debian
|
|
346
|
-
sudo apt-get install jq
|
|
275
|
+
brew install jq # macOS
|
|
276
|
+
sudo apt-get install jq # Ubuntu/Debian
|
|
347
277
|
```
|
|
348
278
|
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## FAQ
|
|
282
|
+
|
|
283
|
+
<details>
|
|
284
|
+
<summary><b>Does it modify my AI CLI?</b></summary>
|
|
285
|
+
|
|
286
|
+
No. It adds read-only, observational hook entries to your CLI's settings file (e.g. `~/.claude/settings.json`, written atomically) so it can mirror events. The CLIs themselves are untouched, and you can remove every hook with `npx ai-agent-session-center --uninstall` or `npm run reset`.
|
|
287
|
+
</details>
|
|
288
|
+
|
|
289
|
+
<details>
|
|
290
|
+
<summary><b>Does any data leave my machine?</b></summary>
|
|
291
|
+
|
|
292
|
+
No. The dashboard, hook endpoint, and message queue are all localhost-only. Nothing is sent to a remote server.
|
|
293
|
+
</details>
|
|
294
|
+
|
|
295
|
+
<details>
|
|
296
|
+
<summary><b>How do I fully uninstall?</b></summary>
|
|
297
|
+
|
|
298
|
+
Run `npx ai-agent-session-center --uninstall` to strip the hooks from all CLI configs, or `npm run reset` to also clean local config and back it up. Globally installed copies: `npm uninstall -g ai-agent-session-center`.
|
|
299
|
+
</details>
|
|
300
|
+
|
|
301
|
+
<details>
|
|
302
|
+
<summary><b>Which CLIs are supported?</b></summary>
|
|
303
|
+
|
|
304
|
+
Claude Code, Gemini CLI, and Codex CLI today. More integrations (OpenCode, Cursor, Windsurf, and other agentic frameworks) are on the roadmap.
|
|
305
|
+
</details>
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Known limitations
|
|
310
|
+
|
|
311
|
+
- **Session matching is heuristic** — linking hook events to terminals uses a multi-priority fallback; two sessions in the same working directory may occasionally cross-link.
|
|
312
|
+
- **Approval detection timing** — auto-approved long-running commands (npm install, builds) can briefly show as "approval" for ~8s until the PostToolUse event clears it.
|
|
313
|
+
- **Platform support** — developed and tested primarily on macOS. The browser dashboard works anywhere Node runs (including Linux). The Electron desktop build targets macOS and Windows; the Windows PowerShell hook variant is less battle-tested.
|
|
314
|
+
- **3D scene performance** — with 20+ concurrent sessions the Three.js scene may strain lower-end hardware; the 2D flat-list fallback is available.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Roadmap
|
|
319
|
+
|
|
320
|
+
Contributions and ideas welcome:
|
|
321
|
+
|
|
322
|
+
- **More CLI integrations** — OpenCode, Cursor, Windsurf, or any agentic framework
|
|
323
|
+
- **Remote monitoring** — dashboard reachable from other machines on the network
|
|
324
|
+
- **Agent creation templates** — define system prompts, tools, and configs before launch
|
|
325
|
+
- **Collaboration** — multi-user dashboards where teams see each other's sessions
|
|
326
|
+
- **Plugin system** — extensible hooks for custom visualizations
|
|
327
|
+
- **Community themes** — user-contributed 3D scene themes and robot models
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
349
331
|
## Contributing
|
|
350
332
|
|
|
351
|
-
Contributions are welcome
|
|
333
|
+
Contributions are welcome. To get started:
|
|
352
334
|
|
|
353
|
-
|
|
335
|
+
```bash
|
|
336
|
+
git clone https://github.com/coding-by-feng/ai-agent-session-center.git
|
|
337
|
+
cd ai-agent-session-center
|
|
338
|
+
npm install
|
|
339
|
+
npm run dev # Vite HMR + backend
|
|
340
|
+
```
|
|
354
341
|
|
|
355
|
-
|
|
342
|
+
Run `npm test`, `npm run lint`, and `npm run typecheck` before opening a PR, and use conventional commit messages (`feat:`, `fix:`, `docs:`, …). For the full feature reference and architecture, read [`docs/feature/README.md`](docs/feature/README.md). Release notes live in [CHANGELOG.md](CHANGELOG.md).
|
|
356
343
|
|
|
357
344
|
## License
|
|
358
345
|
|
|
359
|
-
|
|
346
|
+
Released under the [MIT License](./LICENSE).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{y as x,h as i,k as e,o as A}from"./index-C7eUMmiz.js";const P="_container_3h9eq_6",F="_filterBar_3h9eq_15",$="_toggleLabel_3h9eq_42",U="_taskList_3h9eq_61",Y="_groupHeader_3h9eq_69",G="_groupChevron_3h9eq_78",V="_collapsed_3h9eq_86",W="_groupLabel_3h9eq_90",K="_groupCount_3h9eq_99",z="_taskCard_3h9eq_107",J="_completed_3h9eq_124",Q="_taskTitle_3h9eq_128",X="_overdue_3h9eq_133",Z="_dueToday_3h9eq_138",ee="_checkbox_3h9eq_145",te="_taskContent_3h9eq_160",se="_taskHeader_3h9eq_165",ae="_taskTitleInput_3h9eq_189",ne="_priorityBadge_3h9eq_204",oe="_priorityUrgent_3h9eq_231",le="_priorityHigh_3h9eq_237",ie="_priorityMedium_3h9eq_243",re="_priorityLow_3h9eq_249",ce="_taskMeta_3h9eq_257",de="_dueDate_3h9eq_265",ue="_dueDateOverdue_3h9eq_271",pe="_dueDateToday_3h9eq_275",he="_tag_3h9eq_279",ge="_tagInput_3h9eq_294",me="_addTagBtn_3h9eq_307",_e="_description_3h9eq_327",fe="_descriptionToggle_3h9eq_337",ve="_deleteBtn_3h9eq_354",xe="_addForm_3h9eq_375",ye="_titleInput_3h9eq_418",Ce="_tagsInput_3h9eq_423",je="_addBtn_3h9eq_428",Te="_emptyState_3h9eq_454",ke="_loading_3h9eq_475",De="_stats_3h9eq_488",be="_statsSep_3h9eq_498",we="_confirmOverlay_3h9eq_504",qe="_confirmBtn_3h9eq_513",Se="_cancelBtn_3h9eq_528",s={container:P,filterBar:F,toggleLabel:$,taskList:U,groupHeader:Y,groupChevron:G,collapsed:V,groupLabel:W,groupCount:K,taskCard:z,completed:J,taskTitle:Q,overdue:X,dueToday:Z,checkbox:ee,taskContent:te,taskHeader:se,taskTitleInput:ae,priorityBadge:ne,priorityUrgent:oe,priorityHigh:le,priorityMedium:ie,priorityLow:re,taskMeta:ce,dueDate:de,dueDateOverdue:ue,dueDateToday:pe,tag:he,tagInput:ge,addTagBtn:me,description:_e,descriptionToggle:fe,deleteBtn:ve,addForm:xe,titleInput:ye,tagsInput:Ce,addBtn:je,emptyState:Te,loading:ke,stats:De,statsSep:be,confirmOverlay:we,confirmBtn:qe,cancelBtn:Se},Ne=[{value:"all",label:"All priorities"},{value:"urgent",label:"Urgent"},{value:"high",label:"High"},{value:"medium",label:"Medium"},{value:"low",label:"Low"}],Be=[{value:"priority",label:"Priority"},{value:"dueDate",label:"Due date"},{value:"createdAt",label:"Created"}];function Ie(){const t=x(n=>n.filter),l=x(n=>n.setFilter),o=x(n=>n.tasks),c=i.useMemo(()=>{const n=new Set;for(const d of o.values())for(const v of d.tags)v&&n.add(v);return[...n].sort((d,v)=>d.localeCompare(v))},[o]),u=i.useCallback(n=>l({search:n}),[l]),p=i.useCallback(n=>l({priority:n.target.value}),[l]),_=i.useCallback(n=>l({tag:n.target.value}),[l]),g=i.useCallback(n=>l({sortBy:n.target.value}),[l]),f=i.useCallback(()=>l({showCompleted:!t.showCompleted}),[l,t.showCompleted]);return e.jsxs("div",{className:s.filterBar,children:[e.jsx(A,{value:t.search,onChange:u,placeholder:"Search tasks...",debounceMs:200}),e.jsx("select",{value:t.priority,onChange:p,children:Ne.map(n=>e.jsx("option",{value:n.value,children:n.label},n.value))}),e.jsxs("select",{value:t.tag,onChange:_,disabled:c.length===0,children:[e.jsx("option",{value:"all",children:"All tags"}),c.map(n=>e.jsxs("option",{value:n,children:["#",n]},n))]}),e.jsx("select",{value:t.sortBy,onChange:g,children:Be.map(n=>e.jsx("option",{value:n.value,children:n.label},n.value))}),e.jsxs("label",{className:s.toggleLabel,children:[e.jsx("input",{type:"checkbox",checked:t.showCompleted,onChange:f}),"Show completed"]})]})}const Le={urgent:s.priorityUrgent,high:s.priorityHigh,medium:s.priorityMedium,low:s.priorityLow},Oe=["low","medium","high","urgent"];function Re(t){if(!t)return null;const l=new Date;l.setHours(0,0,0,0);const o=new Date(t+"T00:00:00");return o<l?"overdue":o.getTime()===l.getTime()?"today":"future"}function Ee(t){return new Date(t+"T00:00:00").toLocaleDateString("en-US",{month:"short",day:"numeric"})}function N({task:t}){const l=x(a=>a.toggleTask),o=x(a=>a.updateTask),c=x(a=>a.deleteTask),[u,p]=i.useState(!1),[_,g]=i.useState(t.title),[f,n]=i.useState(!1),[d,v]=i.useState(""),[j,r]=i.useState(!1),[y,m]=i.useState(!1),T=i.useRef(null),b=i.useRef(null);i.useEffect(()=>{u&&(T.current?.focus(),T.current?.select())},[u]),i.useEffect(()=>{f&&b.current?.focus()},[f]),i.useEffect(()=>{u||g(t.title)},[t.title,u]);const k=i.useCallback(()=>{p(!1);const a=_.trim();a&&a!==t.title?o(t.id,{title:a}):g(t.title)},[_,t.id,t.title,o]),w=i.useCallback(()=>{l(t.id)},[l,t.id]),h=i.useCallback(()=>{c(t.id),m(!1)},[c,t.id]),C=i.useCallback(()=>{v(t.tags.join(", ")),n(!0)},[t.tags]),R=i.useCallback(a=>{o(t.id,{priority:a.target.value})},[t.id,o]),S=i.useCallback(()=>{n(!1);const a=d.split(",").map(M=>M.trim()).filter(Boolean),E=t.tags.join(","),H=a.join(",");E!==H&&o(t.id,{tags:a})},[d,t.id,t.tags,o]),D=Re(t.dueDate),q=[s.taskCard];return t.completed&&q.push(s.completed),!t.completed&&D==="overdue"&&q.push(s.overdue),!t.completed&&D==="today"&&q.push(s.dueToday),e.jsxs("div",{className:q.join(" "),children:[e.jsx("input",{type:"checkbox",className:s.checkbox,checked:t.completed,onChange:w,"aria-label":`Mark "${t.title}" as ${t.completed?"incomplete":"complete"}`}),e.jsxs("div",{className:s.taskContent,children:[e.jsxs("div",{className:s.taskHeader,children:[u?e.jsx("input",{ref:T,className:s.taskTitleInput,value:_,onChange:a=>g(a.target.value),onBlur:k,onKeyDown:a=>{a.key==="Enter"&&(a.preventDefault(),k()),a.key==="Escape"&&(g(t.title),p(!1))},onClick:a=>a.stopPropagation()}):e.jsx("span",{className:s.taskTitle,onClick:()=>{g(t.title),p(!0)},title:"Click to edit",children:t.title}),e.jsx("select",{className:`${s.priorityBadge} ${Le[t.priority]}`,value:t.priority,onChange:R,onClick:a=>a.stopPropagation(),title:"Change priority",children:Oe.map(a=>e.jsx("option",{value:a,children:a.toUpperCase()},a))})]}),e.jsxs("div",{className:s.taskMeta,children:[t.dueDate&&e.jsxs("span",{className:`${s.dueDate} ${D==="overdue"?s.dueDateOverdue:""} ${D==="today"?s.dueDateToday:""}`,children:[D==="overdue"&&"Overdue: ",D==="today"&&"Today: ",Ee(t.dueDate)]}),f?e.jsx("input",{ref:b,className:s.tagInput,value:d,onChange:a=>v(a.target.value),onBlur:S,onKeyDown:a=>{a.key==="Enter"&&(a.preventDefault(),S()),a.key==="Escape"&&n(!1)},onClick:a=>a.stopPropagation(),placeholder:"tag1, tag2, ..."}):e.jsxs(e.Fragment,{children:[t.tags.map(a=>e.jsx("span",{className:s.tag,onClick:C,title:"Click to edit tags",style:{cursor:"pointer"},children:a},a)),e.jsx("button",{className:s.addTagBtn,onClick:C,title:"Add / edit tags",children:"+"})]})]}),t.description&&e.jsxs(e.Fragment,{children:[e.jsx("button",{className:s.descriptionToggle,onClick:()=>r(a=>!a),children:j?"- Hide details":"+ Show details"}),j&&e.jsx("div",{className:s.description,children:t.description})]})]}),y?e.jsxs("div",{className:s.confirmOverlay,children:[e.jsx("span",{children:"Delete?"}),e.jsx("button",{className:s.confirmBtn,onClick:h,children:"Yes"}),e.jsx("button",{className:s.cancelBtn,onClick:()=>m(!1),children:"No"})]}):e.jsx("button",{className:s.deleteBtn,onClick:()=>m(!0),"aria-label":`Delete "${t.title}"`,title:"Delete task",children:e.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("polyline",{points:"3 6 5 6 21 6"}),e.jsx("path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"})]})})]})}function He(){const t=x(d=>d.createTask),[l,o]=i.useState(""),[c,u]=i.useState("medium"),[p,_]=i.useState(""),[g,f]=i.useState(""),n=i.useCallback(d=>{d.preventDefault();const v=l.trim();if(!v)return;const j=g.split(",").map(r=>r.trim()).filter(Boolean);t({title:v,priority:c,tags:j,dueDate:p||void 0}),o(""),u("medium"),_(""),f("")},[l,c,p,g,t]);return e.jsxs("form",{className:s.addForm,onSubmit:n,children:[e.jsx("input",{type:"text",className:s.titleInput,placeholder:"New task title...",value:l,onChange:d=>o(d.target.value),required:!0}),e.jsxs("select",{value:c,onChange:d=>u(d.target.value),children:[e.jsx("option",{value:"urgent",children:"Urgent"}),e.jsx("option",{value:"high",children:"High"}),e.jsx("option",{value:"medium",children:"Medium"}),e.jsx("option",{value:"low",children:"Low"})]}),e.jsx("input",{type:"date",value:p,onChange:d=>_(d.target.value),title:"Due date (optional)"}),e.jsx("input",{type:"text",className:s.tagsInput,placeholder:"Tags (comma-sep)",value:g,onChange:d=>f(d.target.value)}),e.jsx("button",{type:"submit",className:s.addBtn,disabled:!l.trim(),children:"ADD"})]})}const B=["urgent","high","medium","low"],I={urgent:0,high:1,medium:2,low:3},Me={urgent:"Urgent",high:"High",medium:"Medium",low:"Low"};function Ae(t,l,o,c){if(o!=="all"&&t.priority!==o||c!=="all"&&!t.tags.includes(c))return!1;if(l){const u=l.toLowerCase(),p=t.title.toLowerCase().includes(u),_=(t.description??"").toLowerCase().includes(u);if(!p&&!_)return!1}return!0}function L(t,l){return[...t].sort((o,c)=>{if(l==="priority"){const u=I[o.priority],p=I[c.priority];return u!==p?u-p:new Date(c.createdAt).getTime()-new Date(o.createdAt).getTime()}return l==="dueDate"?!o.dueDate&&!c.dueDate?0:o.dueDate?c.dueDate?o.dueDate.localeCompare(c.dueDate):-1:1:new Date(c.createdAt).getTime()-new Date(o.createdAt).getTime()})}function O({label:t,count:l,collapsed:o,onToggle:c}){return e.jsxs("div",{className:s.groupHeader,onClick:c,children:[e.jsx("svg",{className:`${s.groupChevron} ${o?s.collapsed:""}`,width:"10",height:"10",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:e.jsx("polyline",{points:"6 9 12 15 18 9"})}),e.jsx("span",{className:s.groupLabel,children:t}),e.jsx("span",{className:s.groupCount,children:l})]})}function Fe(){const t=x(r=>r.tasks),l=x(r=>r.loading),o=x(r=>r.filter),c=x(r=>r.fetchTasks),[u,p]=i.useState(new Set);i.useEffect(()=>{c()},[c]);const _=i.useCallback(r=>{p(y=>{const m=new Set(y);return m.has(r)?m.delete(r):m.add(r),m})},[]),{groups:g,completedTasks:f,totalIncomplete:n,totalCompleted:d}=i.useMemo(()=>{const y=[...t.values()].filter(h=>Ae(h,o.search,o.priority,o.tag)),m=y.filter(h=>!h.completed),T=y.filter(h=>h.completed),b=L(m,o.sortBy),k=new Map;for(const h of B)k.set(h,[]);for(const h of b){const C=k.get(h.priority);C&&C.push(h)}const w=[];for(const h of B){const C=k.get(h)??[];C.length>0&&w.push({id:h,label:Me[h],tasks:C})}return{groups:w,completedTasks:L(T,o.sortBy),totalIncomplete:m.length,totalCompleted:T.length}},[t,o]);if(l)return e.jsx("div",{className:s.container,children:e.jsx("div",{className:s.loading,children:"Loading tasks..."})});const v=g.length===0&&f.length===0,j=t.size===0;return e.jsxs("div",{className:s.container,"data-testid":"agenda-view",children:[e.jsx(Ie,{}),e.jsxs("div",{className:s.stats,children:[e.jsxs("span",{children:[n," task",n!==1?"s":""]}),e.jsx("span",{className:s.statsSep,children:"|"}),e.jsxs("span",{children:[d," completed"]})]}),e.jsx("div",{className:s.taskList,children:j?e.jsxs("div",{className:s.emptyState,children:[e.jsx("div",{children:"No tasks yet"}),e.jsx("span",{children:"Add your first task below"})]}):v?e.jsxs("div",{className:s.emptyState,children:[e.jsx("div",{children:"No tasks match the current filter"}),e.jsx("span",{children:"Try adjusting your search or filter criteria"})]}):e.jsxs(e.Fragment,{children:[g.map(r=>{const y=u.has(r.id);return e.jsxs("div",{children:[e.jsx(O,{label:r.label,count:r.tasks.length,collapsed:y,onToggle:()=>_(r.id)}),!y&&r.tasks.map(m=>e.jsx(N,{task:m},m.id))]},r.id)}),o.showCompleted&&f.length>0&&e.jsxs("div",{children:[e.jsx(O,{label:"Completed",count:f.length,collapsed:u.has("__completed__"),onToggle:()=>_("__completed__")}),!u.has("__completed__")&&f.map(r=>e.jsx(N,{task:r},r.id))]})]})}),e.jsx(He,{})]})}export{Fe as default};
|