groove-dev 0.17.7 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +0 -7
- package/node_modules/@groove-dev/daemon/google-oauth.json +5 -0
- package/node_modules/@groove-dev/daemon/integrations-registry.json +10 -48
- package/node_modules/@groove-dev/daemon/src/api.js +103 -12
- package/node_modules/@groove-dev/daemon/src/integrations.js +94 -16
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +4 -0
- package/node_modules/@groove-dev/gui/.groove/codebase-index.json +64 -0
- package/node_modules/@groove-dev/gui/.groove/config.json +10 -0
- package/node_modules/@groove-dev/gui/.groove/coordination.md +5 -0
- package/node_modules/@groove-dev/gui/.groove/daemon.host +1 -0
- package/node_modules/@groove-dev/gui/.groove/daemon.pid +1 -0
- package/node_modules/@groove-dev/gui/.groove/daemon.port +1 -0
- package/node_modules/@groove-dev/gui/.groove/federation/identity.key +3 -0
- package/node_modules/@groove-dev/gui/.groove/federation/identity.pub +3 -0
- package/node_modules/@groove-dev/gui/.groove/integrations/package.json +6 -0
- package/node_modules/@groove-dev/gui/.groove/state.json +3 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-DXkccbmd.js +182 -0
- package/node_modules/@groove-dev/gui/dist/index.html +1 -1
- package/node_modules/@groove-dev/gui/src/App.jsx +27 -4
- package/node_modules/@groove-dev/gui/src/components/AgentChat.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/SpawnPanel.jsx +839 -586
- package/node_modules/@groove-dev/gui/src/views/FileEditor.jsx +85 -1
- package/node_modules/@groove-dev/gui/src/views/IntegrationsStore.jsx +157 -42
- package/package.json +1 -2
- package/packages/daemon/integrations-registry.json +10 -48
- package/packages/daemon/src/api.js +103 -12
- package/packages/daemon/src/integrations.js +94 -16
- package/packages/daemon/src/providers/claude-code.js +4 -0
- package/packages/gui/dist/assets/index-DXkccbmd.js +182 -0
- package/packages/gui/dist/index.html +1 -1
- package/packages/gui/node_modules/.vite/deps/@codemirror_autocomplete.js +68 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_autocomplete.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_commands.js +1420 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_commands.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-css.js +17 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-css.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-html.js +22 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-html.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-javascript.js +34 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-javascript.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-json.js +101 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-json.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-markdown.js +2534 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-markdown.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-python.js +789 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-python.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_language.js +115 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_language.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_search.js +1136 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_search.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_state.js +63 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_state.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_theme-one-dark.js +179 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_theme-one-dark.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_view.js +104 -0
- package/packages/gui/node_modules/.vite/deps/@codemirror_view.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-fit.js +46 -0
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-fit.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-web-links.js +121 -0
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-web-links.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@xterm_xterm.js +9237 -0
- package/packages/gui/node_modules/.vite/deps/@xterm_xterm.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/@xyflow_react.js +9934 -0
- package/packages/gui/node_modules/.vite/deps/@xyflow_react.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/_metadata.json +184 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3EE34IFC.js +5169 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3EE34IFC.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3IB5EUP7.js +2000 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3IB5EUP7.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3LBP22MX.js +1115 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3LBP22MX.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3Q7HT7ZF.js +701 -0
- package/packages/gui/node_modules/.vite/deps/chunk-3Q7HT7ZF.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-44CLUOQE.js +1776 -0
- package/packages/gui/node_modules/.vite/deps/chunk-44CLUOQE.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-5RZAEUNX.js +280 -0
- package/packages/gui/node_modules/.vite/deps/chunk-5RZAEUNX.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-5WRI5ZAA.js +30 -0
- package/packages/gui/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-7FYDPZIO.js +1004 -0
- package/packages/gui/node_modules/.vite/deps/chunk-7FYDPZIO.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-BX6POZPY.js +292 -0
- package/packages/gui/node_modules/.vite/deps/chunk-BX6POZPY.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-HVFOBSCQ.js +1062 -0
- package/packages/gui/node_modules/.vite/deps/chunk-HVFOBSCQ.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-RE2FU7ZU.js +10985 -0
- package/packages/gui/node_modules/.vite/deps/chunk-RE2FU7ZU.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/chunk-YYJMNVCJ.js +3459 -0
- package/packages/gui/node_modules/.vite/deps/chunk-YYJMNVCJ.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/package.json +3 -0
- package/packages/gui/node_modules/.vite/deps/react-dom.js +6 -0
- package/packages/gui/node_modules/.vite/deps/react-dom.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/react-dom_client.js +20217 -0
- package/packages/gui/node_modules/.vite/deps/react-dom_client.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/react.js +5 -0
- package/packages/gui/node_modules/.vite/deps/react.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/react_jsx-dev-runtime.js +278 -0
- package/packages/gui/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/react_jsx-runtime.js +6 -0
- package/packages/gui/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/zustand.js +56 -0
- package/packages/gui/node_modules/.vite/deps/zustand.js.map +7 -0
- package/packages/gui/src/App.jsx +27 -4
- package/packages/gui/src/components/AgentChat.jsx +1 -1
- package/packages/gui/src/components/SpawnPanel.jsx +839 -586
- package/packages/gui/src/views/FileEditor.jsx +85 -1
- package/packages/gui/src/views/IntegrationsStore.jsx +157 -42
- package/docs/FILE-EDITOR-PLAN.md +0 -253
- package/docs/GUI_DESIGN_SPEC.md +0 -402
- package/docs/SKILLS-API-SPEC.md +0 -277
- package/node_modules/@groove-dev/gui/dist/assets/index-CsymvgNh.js +0 -156
- package/packages/gui/dist/assets/index-CsymvgNh.js +0 -156
package/docs/FILE-EDITOR-PLAN.md
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
# File Editor — Implementation Plan
|
|
2
|
-
|
|
3
|
-
## Context
|
|
4
|
-
|
|
5
|
-
Power users want to watch and verify what agents are doing in real-time. A VS Code-style file editor embedded in the GROOVE GUI lets users browse project files, see agent changes as they happen, and make quick edits — all without leaving the dashboard.
|
|
6
|
-
|
|
7
|
-
## Architecture
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
+------------------------------------------+
|
|
11
|
-
| [EditorTabs: file1.js | file2.css | ...] |
|
|
12
|
-
+----------+-------------------------------+
|
|
13
|
-
| FileTree | CodeEditor (CodeMirror 6) |
|
|
14
|
-
| (240px) | (flex: 1) |
|
|
15
|
-
| | |
|
|
16
|
-
| | [file changed banner] |
|
|
17
|
-
| | |
|
|
18
|
-
+----------+-------------------------------+
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**Editor library: CodeMirror 6** — modular (~200KB vs Monaco's ~3MB), has a native One Dark theme matching GROOVE's palette, supports programmatic content updates for live agent changes. No new daemon dependencies — file watching uses Node.js built-in `fs.watch`.
|
|
22
|
-
|
|
23
|
-
## Files to Create
|
|
24
|
-
|
|
25
|
-
| File | Purpose |
|
|
26
|
-
|------|---------|
|
|
27
|
-
| `packages/gui/src/views/FileEditor.jsx` | Main view — composes tree + tabs + editor |
|
|
28
|
-
| `packages/gui/src/components/FileTree.jsx` | Expandable directory tree sidebar |
|
|
29
|
-
| `packages/gui/src/components/EditorTabs.jsx` | Horizontal tab bar for open files |
|
|
30
|
-
| `packages/gui/src/components/CodeEditor.jsx` | CodeMirror 6 wrapper |
|
|
31
|
-
| `packages/daemon/src/filewatcher.js` | Per-file fs.watch + debounced WebSocket broadcast |
|
|
32
|
-
|
|
33
|
-
## Files to Modify
|
|
34
|
-
|
|
35
|
-
| File | Changes |
|
|
36
|
-
|------|---------|
|
|
37
|
-
| `packages/daemon/src/api.js` | Add `/api/files/tree`, `/api/files/read`, `/api/files/write` endpoints |
|
|
38
|
-
| `packages/daemon/src/index.js` | Wire `FileWatcher`, handle `editor:watch`/`editor:unwatch` WS messages |
|
|
39
|
-
| `packages/gui/src/stores/groove.js` | Add editor state + actions + `file:changed` WS handler |
|
|
40
|
-
| `packages/gui/src/App.jsx` | Add "Editor" tab, import + render `<FileEditor />` |
|
|
41
|
-
| `packages/gui/package.json` | Add CodeMirror 6 dependencies |
|
|
42
|
-
|
|
43
|
-
## Build Order
|
|
44
|
-
|
|
45
|
-
### Step 1: Backend — File API endpoints
|
|
46
|
-
|
|
47
|
-
**File: `packages/daemon/src/api.js`**
|
|
48
|
-
|
|
49
|
-
Add after the existing `// --- Directory Browser ---` section. Reuse the same path validation pattern from `/api/browse`.
|
|
50
|
-
|
|
51
|
-
**`GET /api/files/tree?path=<relPath>`**
|
|
52
|
-
Returns files AND directories for a given path. Response:
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"current": "src/components",
|
|
56
|
-
"parent": "src",
|
|
57
|
-
"entries": [
|
|
58
|
-
{ "name": "subdir", "type": "dir", "path": "src/components/subdir", "hasChildren": true },
|
|
59
|
-
{ "name": "App.jsx", "type": "file", "path": "src/components/App.jsx", "size": 4521, "language": "javascript" }
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
- Dirs first (sorted), then files (sorted)
|
|
64
|
-
- Filter: no `.git`, `node_modules`, dotfiles
|
|
65
|
-
- Language detected from extension via a simple map
|
|
66
|
-
|
|
67
|
-
**`GET /api/files/read?path=<relPath>`**
|
|
68
|
-
Returns file contents. Response:
|
|
69
|
-
```json
|
|
70
|
-
{ "path": "src/App.jsx", "content": "...", "size": 4521, "language": "javascript" }
|
|
71
|
-
```
|
|
72
|
-
- Reject files > 5MB
|
|
73
|
-
- Detect binary (null bytes in first 8KB) → return `{ binary: true }`
|
|
74
|
-
|
|
75
|
-
**`POST /api/files/write`**
|
|
76
|
-
Saves file contents. Body: `{ path, content }`. Response: `{ ok: true, size: N }`
|
|
77
|
-
- Same path validation
|
|
78
|
-
- Audit log: `daemon.audit.log('file.write', { path })`
|
|
79
|
-
|
|
80
|
-
**Language detection helper:**
|
|
81
|
-
```javascript
|
|
82
|
-
const LANG_MAP = {
|
|
83
|
-
js: 'javascript', jsx: 'javascript', mjs: 'javascript',
|
|
84
|
-
ts: 'typescript', tsx: 'typescript',
|
|
85
|
-
css: 'css', scss: 'css', html: 'html', json: 'json',
|
|
86
|
-
md: 'markdown', py: 'python', rs: 'rust', go: 'go',
|
|
87
|
-
sh: 'shell', yaml: 'yaml', yml: 'yaml', toml: 'toml',
|
|
88
|
-
sql: 'sql', xml: 'xml', java: 'java',
|
|
89
|
-
};
|
|
90
|
-
function detectLanguage(filename) {
|
|
91
|
-
const ext = filename.split('.').pop()?.toLowerCase();
|
|
92
|
-
return LANG_MAP[ext] || 'text';
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Step 2: Backend — File Watcher
|
|
97
|
-
|
|
98
|
-
**New file: `packages/daemon/src/filewatcher.js`**
|
|
99
|
-
|
|
100
|
-
Lightweight class that watches individual files (only files the user has open in the editor):
|
|
101
|
-
- `watch(relPath)` — start watching with `fs.watch`, debounce 300ms, broadcast `{ type: 'file:changed', path, timestamp }` via `daemon.broadcast()`
|
|
102
|
-
- `unwatch(relPath)` — stop watching, clean up
|
|
103
|
-
- `unwatchAll()` — cleanup on daemon shutdown
|
|
104
|
-
|
|
105
|
-
**Wire in `packages/daemon/src/index.js`:**
|
|
106
|
-
- Instantiate `this.fileWatcher = new FileWatcher(this)` in daemon constructor
|
|
107
|
-
- In the WebSocket `connection` handler, add a `message` listener:
|
|
108
|
-
- `editor:watch` → `this.fileWatcher.watch(msg.path)`
|
|
109
|
-
- `editor:unwatch` → `this.fileWatcher.unwatch(msg.path)`
|
|
110
|
-
- Call `this.fileWatcher.unwatchAll()` on daemon shutdown
|
|
111
|
-
|
|
112
|
-
### Step 3: Dependencies — Install CodeMirror 6
|
|
113
|
-
|
|
114
|
-
Add to `packages/gui/package.json`:
|
|
115
|
-
```
|
|
116
|
-
@codemirror/view, @codemirror/state, @codemirror/language,
|
|
117
|
-
@codemirror/commands, @codemirror/search, @codemirror/autocomplete,
|
|
118
|
-
@codemirror/lang-javascript, @codemirror/lang-css, @codemirror/lang-html,
|
|
119
|
-
@codemirror/lang-json, @codemirror/lang-markdown, @codemirror/lang-python,
|
|
120
|
-
@codemirror/theme-one-dark
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Run `npm install` from project root.
|
|
124
|
-
|
|
125
|
-
### Step 4: Zustand Store — Editor State
|
|
126
|
-
|
|
127
|
-
**File: `packages/gui/src/stores/groove.js`**
|
|
128
|
-
|
|
129
|
-
Add to initial state:
|
|
130
|
-
```javascript
|
|
131
|
-
editorFiles: {}, // { [path]: { content, originalContent, language, loadedAt } }
|
|
132
|
-
editorActiveFile: null, // currently visible file path
|
|
133
|
-
editorOpenTabs: [], // ordered array of open file paths
|
|
134
|
-
editorTreeCache: {}, // { [dirPath]: entries[] } — cached tree data
|
|
135
|
-
editorChangedFiles: {}, // { [path]: timestamp } — externally modified files
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Add actions:
|
|
139
|
-
- `openFile(path)` — fetch from `/api/files/read`, add to `editorFiles` + `editorOpenTabs`, set `editorActiveFile`, send `editor:watch` via WS
|
|
140
|
-
- `closeFile(path)` — remove from tabs/files, send `editor:unwatch`, switch to next tab
|
|
141
|
-
- `setActiveFile(path)` — switch active tab
|
|
142
|
-
- `updateFileContent(path, content)` — on every editor keystroke (dirty = content !== originalContent)
|
|
143
|
-
- `saveFile(path)` — POST to `/api/files/write`, reset originalContent
|
|
144
|
-
- `reloadFile(path)` — re-fetch from server, clear changed notification
|
|
145
|
-
- `dismissFileChange(path)` — clear from `editorChangedFiles`
|
|
146
|
-
- `fetchTreeDir(path)` — fetch `/api/files/tree?path=...`, cache in `editorTreeCache`
|
|
147
|
-
|
|
148
|
-
Add WS handler case:
|
|
149
|
-
```javascript
|
|
150
|
-
case 'file:changed':
|
|
151
|
-
set((s) => ({ editorChangedFiles: { ...s.editorChangedFiles, [msg.path]: msg.timestamp } }));
|
|
152
|
-
break;
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Step 5: FileTree Component
|
|
156
|
-
|
|
157
|
-
**New file: `packages/gui/src/components/FileTree.jsx`**
|
|
158
|
-
|
|
159
|
-
- Recursive expandable tree, lazy-loads children on directory expand
|
|
160
|
-
- Click file → `openFile(path)`
|
|
161
|
-
- Click directory → toggle expand, fetch children if not cached
|
|
162
|
-
- Visual: 16px indent per level, `>` / `v` arrows for dirs
|
|
163
|
-
- Active file highlighted with accent left border
|
|
164
|
-
- File colors by extension (`.js` amber, `.css` blue, `.json` green, `.md` purple, etc.)
|
|
165
|
-
- Search input at top for client-side filtering
|
|
166
|
-
- Style: `var(--bg-chrome)` background, `var(--border)` separators, matching DirPicker patterns
|
|
167
|
-
|
|
168
|
-
### Step 6: EditorTabs Component
|
|
169
|
-
|
|
170
|
-
**New file: `packages/gui/src/components/EditorTabs.jsx`**
|
|
171
|
-
|
|
172
|
-
- Horizontal scrollable row of open file tabs
|
|
173
|
-
- Each tab: filename (tooltip = full path), close `x` button
|
|
174
|
-
- Active tab: `border-bottom: 2px solid var(--accent)`, bright text
|
|
175
|
-
- Dirty indicator: small amber dot next to filename when content !== originalContent
|
|
176
|
-
- `overflow-x: auto` for many open files
|
|
177
|
-
- Style: `var(--bg-chrome)` background, 32px height, matching header tab patterns from App.jsx
|
|
178
|
-
|
|
179
|
-
### Step 7: CodeEditor Component (CodeMirror 6)
|
|
180
|
-
|
|
181
|
-
**New file: `packages/gui/src/components/CodeEditor.jsx`**
|
|
182
|
-
|
|
183
|
-
React wrapper around CodeMirror 6. Key details:
|
|
184
|
-
|
|
185
|
-
- `useRef` for container div + EditorView instance
|
|
186
|
-
- `useEffect` on mount: create EditorView with extensions (line numbers, bracket matching, search, auto-indent, one-dark theme, language support)
|
|
187
|
-
- When `content` prop changes externally (file reload / different file selected): dispatch full content replacement via `view.dispatch()`
|
|
188
|
-
- `onChange` callback on every transaction → `updateFileContent(path, newContent)` in store
|
|
189
|
-
- Language switching: reconfigure compartment when language prop changes
|
|
190
|
-
- Custom theme override: set editor bg to `var(--bg-base)` (#24282f) since One Dark default is `--bg-chrome` (#282c34)
|
|
191
|
-
- Ctrl/Cmd+S handler → `saveFile(path)`
|
|
192
|
-
- Cleanup on unmount: `view.destroy()`
|
|
193
|
-
|
|
194
|
-
### Step 8: FileEditor View (Composition)
|
|
195
|
-
|
|
196
|
-
**New file: `packages/gui/src/views/FileEditor.jsx`**
|
|
197
|
-
|
|
198
|
-
Composes all pieces:
|
|
199
|
-
```
|
|
200
|
-
Container (flex column, height 100%)
|
|
201
|
-
├── EditorTabs (flexShrink: 0)
|
|
202
|
-
└── Content Row (flex row, flex: 1, overflow: hidden)
|
|
203
|
-
├── FileTree (width: 240px, flexShrink: 0, border-right)
|
|
204
|
-
└── Editor Area (flex: 1, position: relative)
|
|
205
|
-
├── External Change Banner (absolute top, z-index 10, amber)
|
|
206
|
-
│ "File modified externally" [Reload] [Dismiss]
|
|
207
|
-
└── CodeEditor (flex: 1)
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
Empty state when no file open: centered "Select a file from the tree" text.
|
|
211
|
-
|
|
212
|
-
### Step 9: App.jsx Integration
|
|
213
|
-
|
|
214
|
-
**File: `packages/gui/src/App.jsx`**
|
|
215
|
-
|
|
216
|
-
1. Add to TABS: `{ id: 'editor', label: 'Editor' }` — position it second (after Agents)
|
|
217
|
-
2. Import `FileEditor` from `'./views/FileEditor'`
|
|
218
|
-
3. Add render: `{activeTab === 'editor' && <FileEditor />}`
|
|
219
|
-
|
|
220
|
-
The detail panel (agent chat, journalist) continues to work alongside the editor.
|
|
221
|
-
|
|
222
|
-
## Security
|
|
223
|
-
|
|
224
|
-
All file endpoints reuse the path validation from `/api/browse`:
|
|
225
|
-
- No absolute paths (`startsWith('/')`)
|
|
226
|
-
- No traversal (`includes('..')`)
|
|
227
|
-
- No null bytes (`includes('\0')`)
|
|
228
|
-
- Must resolve within `daemon.projectDir`
|
|
229
|
-
- 5MB size limit on read/write
|
|
230
|
-
- Binary detection (null bytes in first 8KB)
|
|
231
|
-
- Audit logging on writes
|
|
232
|
-
|
|
233
|
-
## Verification
|
|
234
|
-
|
|
235
|
-
1. `npm run build:gui` — should build clean with CodeMirror included
|
|
236
|
-
2. `node --test packages/daemon/test/` — all 137 tests pass
|
|
237
|
-
3. Start daemon (`groove start`), open GUI, click Editor tab
|
|
238
|
-
4. File tree should show project structure
|
|
239
|
-
5. Click a file → opens in CodeMirror with syntax highlighting
|
|
240
|
-
6. Edit content → tab shows dirty dot
|
|
241
|
-
7. Cmd+S → saves, dirty dot clears
|
|
242
|
-
8. Spawn an agent that modifies an open file → amber banner appears "File modified externally"
|
|
243
|
-
9. Click Reload → file content updates
|
|
244
|
-
10. Open multiple files → tabs work, switching preserves state
|
|
245
|
-
|
|
246
|
-
## Future Extensions (not in this build)
|
|
247
|
-
|
|
248
|
-
- Diff view for external changes (`@codemirror/merge`)
|
|
249
|
-
- Git status colors in file tree (modified/untracked)
|
|
250
|
-
- File create/delete/rename via right-click context menu
|
|
251
|
-
- Cmd+P fuzzy file finder
|
|
252
|
-
- Search across files (grep-like panel)
|
|
253
|
-
- Breadcrumb path bar above editor
|
package/docs/GUI_DESIGN_SPEC.md
DELETED
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
# Groove AI — GUI Design Specification
|
|
2
|
-
### Terminal-Native Visual Control Plane
|
|
3
|
-
|
|
4
|
-
> The GUI should feel like a terminal that happens to have graphics in it — not a website pretending to be a terminal.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Philosophy
|
|
9
|
-
|
|
10
|
-
This is a TUI-meets-browser hybrid. It runs at `localhost:3141` and is designed to sit inside a terminal multiplexer pane (cmux) alongside real terminal sessions. It should look like it belongs there. No rounded corners on containers, no card shadows, no hover animations, no gradient backgrounds. Think: htop with a React Flow node graph in the center.
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Color Palette
|
|
15
|
-
|
|
16
|
-
Based on One Dark Pro. Every color has a purpose.
|
|
17
|
-
|
|
18
|
-
### Backgrounds
|
|
19
|
-
| Token | Hex | Usage |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| `--bg-base` | `#24282f` | Main content area / node graph background |
|
|
22
|
-
| `--bg-chrome` | `#282c34` | Sidebar, header bar, panels |
|
|
23
|
-
| `--bg-surface` | `#2c313a` | Buttons, input fields, card surfaces |
|
|
24
|
-
| `--bg-hover` | `#333842` | Button/row hover state |
|
|
25
|
-
| `--bg-active` | `#3a3f4b` | Active/selected button or tab |
|
|
26
|
-
|
|
27
|
-
### Text
|
|
28
|
-
| Token | Hex | Usage |
|
|
29
|
-
|---|---|---|
|
|
30
|
-
| `--text-primary` | `#abb2bf` | Default body text, labels |
|
|
31
|
-
| `--text-bright` | `#e6e6e6` | Headings, active items, emphasis |
|
|
32
|
-
| `--text-dim` | `#5c6370` | Secondary info, timestamps, hints |
|
|
33
|
-
| `--text-muted` | `#3e4451` | Disabled text, decorative borders |
|
|
34
|
-
|
|
35
|
-
### Accent / Status
|
|
36
|
-
| Token | Hex | Usage |
|
|
37
|
-
|---|---|---|
|
|
38
|
-
| `--accent` | `#33afbc` | Brand accent — active nav item, selected node border, primary action |
|
|
39
|
-
| `--green` | `#98c379` | Agent active, success states, healthy status |
|
|
40
|
-
| `--amber` | `#e5c07b` | Agent idle, warning, pending approval |
|
|
41
|
-
| `--red` | `#e06c75` | Agent blocked/error, kill actions, conflicts |
|
|
42
|
-
| `--purple` | `#c678dd` | Journalist activity, context rotation events |
|
|
43
|
-
| `--blue` | `#61afef` | Info badges, links, secondary accent |
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Typography
|
|
48
|
-
|
|
49
|
-
One font. Everywhere. No exceptions.
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
| Element | Size | Weight | Color |
|
|
56
|
-
|---|---|---|---|
|
|
57
|
-
| Header bar title | 13px | 600 | `--text-bright` |
|
|
58
|
-
| Nav buttons | 12px | 500 | `--text-primary` (inactive), `--text-bright` (active) |
|
|
59
|
-
| Node labels | 12px | 600 | `--text-bright` |
|
|
60
|
-
| Node metadata | 11px | 400 | `--text-dim` |
|
|
61
|
-
| Panel headings | 11px | 600 | `--text-dim`, uppercase, letter-spacing 1.5px |
|
|
62
|
-
| Panel body text | 12px | 400 | `--text-primary` |
|
|
63
|
-
| Timestamps | 10px | 400 | `--text-dim` |
|
|
64
|
-
| Status badges | 10px | 600 | Status color, uppercase |
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Layout
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
┌──────────────────────────────────────────────────────┐
|
|
72
|
-
│ HEADER BAR (--bg-chrome, 40px height) │
|
|
73
|
-
│ [logo] [Agents] [Tokens] [Teams] [Permissions] │
|
|
74
|
-
├────────────────────────────────────┬─────────────────┤
|
|
75
|
-
│ │ │
|
|
76
|
-
│ NODE GRAPH │ DETAIL PANEL │
|
|
77
|
-
│ (--bg-base) │ (--bg-chrome) │
|
|
78
|
-
│ │ │
|
|
79
|
-
│ React Flow canvas with │ Context for │
|
|
80
|
-
│ agent nodes and edges │ selected node │
|
|
81
|
-
│ │ │
|
|
82
|
-
│ │ │
|
|
83
|
-
└────────────────────────────────────┴─────────────────┘
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Header Bar
|
|
87
|
-
- Height: 40px
|
|
88
|
-
- Background: `--bg-chrome`
|
|
89
|
-
- Bottom border: 1px `--text-muted`
|
|
90
|
-
- Logo: Groove infinity icon (16px), left side
|
|
91
|
-
- Nav buttons: right-aligned, evenly spaced
|
|
92
|
-
|
|
93
|
-
### Node Graph Area
|
|
94
|
-
- Background: `--bg-base`
|
|
95
|
-
- Takes remaining viewport width/height
|
|
96
|
-
- Subtle dot grid pattern: `--text-muted` dots, 20px spacing, 1px size
|
|
97
|
-
- No border, no padding — edge to edge
|
|
98
|
-
|
|
99
|
-
### Detail Panel (right sidebar)
|
|
100
|
-
- Width: 320px, collapsible
|
|
101
|
-
- Background: `--bg-chrome`
|
|
102
|
-
- Left border: 1px `--text-muted`
|
|
103
|
-
- Shows detail for whatever is selected in the graph
|
|
104
|
-
|
|
105
|
-
---
|
|
106
|
-
|
|
107
|
-
## Components
|
|
108
|
-
|
|
109
|
-
### Nav Buttons (Header)
|
|
110
|
-
|
|
111
|
-
The four main views: **Agents**, **Tokens**, **Teams**, **Permissions**
|
|
112
|
-
|
|
113
|
-
```
|
|
114
|
-
Inactive:
|
|
115
|
-
background: transparent
|
|
116
|
-
color: --text-primary
|
|
117
|
-
padding: 6px 14px
|
|
118
|
-
border: none
|
|
119
|
-
|
|
120
|
-
Hover:
|
|
121
|
-
background: --bg-hover
|
|
122
|
-
color: --text-bright
|
|
123
|
-
|
|
124
|
-
Active:
|
|
125
|
-
background: --bg-active
|
|
126
|
-
color: --text-bright
|
|
127
|
-
border-bottom: 2px --accent (or underline indicator)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
No border-radius. These are terminal tabs, not web buttons.
|
|
131
|
-
|
|
132
|
-
### Agent Nodes (React Flow)
|
|
133
|
-
|
|
134
|
-
Each node represents one running agent.
|
|
135
|
-
|
|
136
|
-
```
|
|
137
|
-
┌─────────────────────────┐
|
|
138
|
-
│ ● backend-1 │ ← status dot + agent name
|
|
139
|
-
│ src/api/** │ ← file scope
|
|
140
|
-
│ claude-code · 45% │ ← provider + context usage
|
|
141
|
-
└─────────────────────────┘
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
| Property | Value |
|
|
145
|
-
|---|---|
|
|
146
|
-
| Background | `--bg-surface` |
|
|
147
|
-
| Border | 1px `--text-muted` |
|
|
148
|
-
| Border (selected) | 1px `--accent` |
|
|
149
|
-
| Border-radius | 2px (barely rounded — terminal feel) |
|
|
150
|
-
| Padding | 10px 12px |
|
|
151
|
-
| Width | 200px |
|
|
152
|
-
| Font | 12px for name, 11px for metadata |
|
|
153
|
-
|
|
154
|
-
**Status dot** (6px circle, left of agent name):
|
|
155
|
-
- `--green` = active
|
|
156
|
-
- `--amber` = idle / waiting
|
|
157
|
-
- `--red` = blocked / error
|
|
158
|
-
- `--purple` = rotating context
|
|
159
|
-
|
|
160
|
-
**Context usage bar** (optional, bottom of node):
|
|
161
|
-
- Height: 2px
|
|
162
|
-
- Background: `--text-muted`
|
|
163
|
-
- Fill: `--green` (0-60%), `--amber` (60-80%), `--red` (80-100%)
|
|
164
|
-
|
|
165
|
-
### Edges (connections between nodes)
|
|
166
|
-
|
|
167
|
-
- Stroke: `--text-muted`
|
|
168
|
-
- Stroke width: 1px
|
|
169
|
-
- Style: straight or step (not bezier — too soft)
|
|
170
|
-
- Animated dash when data is flowing (introduction events, journalist updates)
|
|
171
|
-
|
|
172
|
-
### QC Supervisor Node (special)
|
|
173
|
-
|
|
174
|
-
Same structure as agent node but:
|
|
175
|
-
- Border: 1px `--purple`
|
|
176
|
-
- Label prefix: `[QC]`
|
|
177
|
-
- Always positioned at root/top of the tree
|
|
178
|
-
|
|
179
|
-
### Status Badges
|
|
180
|
-
|
|
181
|
-
Small inline badges for agent states.
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
background: rgba(color, 0.12)
|
|
185
|
-
color: status color
|
|
186
|
-
padding: 2px 6px
|
|
187
|
-
font-size: 10px
|
|
188
|
-
font-weight: 600
|
|
189
|
-
text-transform: uppercase
|
|
190
|
-
letter-spacing: 0.5px
|
|
191
|
-
border-radius: 2px
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Examples:
|
|
195
|
-
- `ACTIVE` — green bg tint, green text
|
|
196
|
-
- `IDLE` — amber bg tint, amber text
|
|
197
|
-
- `BLOCKED` — red bg tint, red text
|
|
198
|
-
- `ROTATING` — purple bg tint, purple text
|
|
199
|
-
|
|
200
|
-
### Token Usage View
|
|
201
|
-
|
|
202
|
-
Replaces the node graph when "Tokens" tab is active.
|
|
203
|
-
|
|
204
|
-
```
|
|
205
|
-
┌──────────────────────────────────┐
|
|
206
|
-
│ TOKEN USAGE this session│
|
|
207
|
-
│ │
|
|
208
|
-
│ Total 1,247,893 │
|
|
209
|
-
│ Saved (routing) 412,500 (33%) │
|
|
210
|
-
│ Saved (rotation) 289,100 (23%) │
|
|
211
|
-
│ │
|
|
212
|
-
│ AGENT BREAKDOWN │
|
|
213
|
-
│ ─────────────────────────────── │
|
|
214
|
-
│ backend-1 482,100 38.6% │
|
|
215
|
-
│ ████████████████░░░░░░ │
|
|
216
|
-
│ frontend-1 391,200 31.3% │
|
|
217
|
-
│ ██████████████░░░░░░░░ │
|
|
218
|
-
│ database-1 374,593 30.0% │
|
|
219
|
-
│ █████████████░░░░░░░░░ │
|
|
220
|
-
└──────────────────────────────────┘
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Bar charts use block characters or thin divs:
|
|
224
|
-
- Fill: `--accent`
|
|
225
|
-
- Track: `--text-muted`
|
|
226
|
-
- Height: 3px
|
|
227
|
-
|
|
228
|
-
Numbers right-aligned, monospace tabular. This should look like a terminal stats output, not a web dashboard.
|
|
229
|
-
|
|
230
|
-
### Teams View
|
|
231
|
-
|
|
232
|
-
List of saved team configurations.
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
┌──────────────────────────────────┐
|
|
236
|
-
│ SAVED TEAMS │
|
|
237
|
-
│ │
|
|
238
|
-
│ > fullstack 4 agents │
|
|
239
|
-
│ backend, frontend, db, qc │
|
|
240
|
-
│ saved 2026-04-05 │
|
|
241
|
-
│ │
|
|
242
|
-
│ > api-only 2 agents │
|
|
243
|
-
│ backend, database │
|
|
244
|
-
│ saved 2026-04-04 │
|
|
245
|
-
└──────────────────────────────────┘
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
- `>` prefix for each team (terminal-style list marker)
|
|
249
|
-
- Selected team: `--text-bright` text, `--bg-hover` background
|
|
250
|
-
- Load button: inline, right side, `--accent` text
|
|
251
|
-
|
|
252
|
-
### Permissions / Approvals View
|
|
253
|
-
|
|
254
|
-
Pending agent actions requiring human approval.
|
|
255
|
-
|
|
256
|
-
```
|
|
257
|
-
┌──────────────────────────────────┐
|
|
258
|
-
│ PENDING APPROVALS 2 │
|
|
259
|
-
│ │
|
|
260
|
-
│ backend-1 wants to: │
|
|
261
|
-
│ Edit src/middleware/auth.js │
|
|
262
|
-
│ "Refactoring auth to use JWT" │
|
|
263
|
-
│ [APPROVE] [REJECT] 2m ago │
|
|
264
|
-
│ │
|
|
265
|
-
│ frontend-1 wants to: │
|
|
266
|
-
│ Create src/components/Modal.jsx │
|
|
267
|
-
│ "Adding confirmation dialog" │
|
|
268
|
-
│ [APPROVE] [REJECT] 45s ago │
|
|
269
|
-
└──────────────────────────────────┘
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
Action buttons:
|
|
273
|
-
```
|
|
274
|
-
[APPROVE]:
|
|
275
|
-
background: rgba(--green, 0.12)
|
|
276
|
-
color: --green
|
|
277
|
-
border: 1px rgba(--green, 0.2)
|
|
278
|
-
padding: 4px 10px
|
|
279
|
-
font-size: 11px
|
|
280
|
-
|
|
281
|
-
[REJECT]:
|
|
282
|
-
background: rgba(--red, 0.12)
|
|
283
|
-
color: --red
|
|
284
|
-
border: 1px rgba(--red, 0.2)
|
|
285
|
-
padding: 4px 10px
|
|
286
|
-
font-size: 11px
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
## Detail Panel (Right Sidebar)
|
|
292
|
-
|
|
293
|
-
Shows context for selected agent node.
|
|
294
|
-
|
|
295
|
-
```
|
|
296
|
-
┌─────────────────────┐
|
|
297
|
-
│ AGENT DETAIL │
|
|
298
|
-
│ │
|
|
299
|
-
│ backend-1 │
|
|
300
|
-
│ Status: ACTIVE │
|
|
301
|
-
│ Provider: claude │
|
|
302
|
-
│ Context: 45% │
|
|
303
|
-
│ ████████░░░░░░░░░░ │
|
|
304
|
-
│ Tokens: 482,100 │
|
|
305
|
-
│ Scope: │
|
|
306
|
-
│ src/api/** │
|
|
307
|
-
│ src/middleware/** │
|
|
308
|
-
│ Spawned: 14:20 │
|
|
309
|
-
│ Peers: frontend-1, │
|
|
310
|
-
│ database-1 │
|
|
311
|
-
│ │
|
|
312
|
-
│ RECENT ACTIVITY │
|
|
313
|
-
│ ───────────────── │
|
|
314
|
-
│ 14:32 editing │
|
|
315
|
-
│ auth.js │
|
|
316
|
-
│ 14:28 introduced to │
|
|
317
|
-
│ frontend-1 │
|
|
318
|
-
│ 14:20 spawned │
|
|
319
|
-
└─────────────────────┘
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
- Section headers: `--text-dim`, 11px, uppercase, 1.5px letter-spacing
|
|
323
|
-
- Dividers: single line of `─` chars or 1px `--text-muted` border
|
|
324
|
-
- Key labels: `--text-dim`
|
|
325
|
-
- Values: `--text-primary`
|
|
326
|
-
- Agent name: `--text-bright`, 14px, 600 weight
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## Interaction States
|
|
331
|
-
|
|
332
|
-
| State | Treatment |
|
|
333
|
-
|---|---|
|
|
334
|
-
| Default | `--bg-surface` background, `--text-primary` text |
|
|
335
|
-
| Hover | `--bg-hover` background, `--text-bright` text |
|
|
336
|
-
| Active/Selected | `--bg-active` background, `--text-bright` text |
|
|
337
|
-
| Focused | 1px `--accent` outline (no glow, no shadow) |
|
|
338
|
-
| Disabled | `--text-muted` text, no hover change |
|
|
339
|
-
|
|
340
|
-
No transitions longer than 100ms. No easing curves. Instant or near-instant state changes — like a terminal.
|
|
341
|
-
|
|
342
|
-
---
|
|
343
|
-
|
|
344
|
-
## What NOT To Do
|
|
345
|
-
|
|
346
|
-
- No border-radius > 2px anywhere
|
|
347
|
-
- No box-shadow anywhere
|
|
348
|
-
- No gradients anywhere
|
|
349
|
-
- No opacity transitions on hover
|
|
350
|
-
- No font-size larger than 14px (exception: token counter numbers)
|
|
351
|
-
- No sans-serif fonts
|
|
352
|
-
- No rounded buttons
|
|
353
|
-
- No icons — use text labels and ASCII where possible (`●`, `>`, `─`, `█`, `░`)
|
|
354
|
-
- No loading spinners — use text like `loading...` or `syncing...`
|
|
355
|
-
- No toast notifications — use inline status text
|
|
356
|
-
- No modals — use the detail panel or inline expansion
|
|
357
|
-
|
|
358
|
-
---
|
|
359
|
-
|
|
360
|
-
## CSS Variables (copy-paste ready)
|
|
361
|
-
|
|
362
|
-
```css
|
|
363
|
-
:root {
|
|
364
|
-
/* Backgrounds */
|
|
365
|
-
--bg-base: #24282f;
|
|
366
|
-
--bg-chrome: #282c34;
|
|
367
|
-
--bg-surface: #2c313a;
|
|
368
|
-
--bg-hover: #333842;
|
|
369
|
-
--bg-active: #3a3f4b;
|
|
370
|
-
|
|
371
|
-
/* Text */
|
|
372
|
-
--text-primary: #abb2bf;
|
|
373
|
-
--text-bright: #e6e6e6;
|
|
374
|
-
--text-dim: #5c6370;
|
|
375
|
-
--text-muted: #3e4451;
|
|
376
|
-
|
|
377
|
-
/* Status */
|
|
378
|
-
--accent: #33afbc;
|
|
379
|
-
--green: #98c379;
|
|
380
|
-
--amber: #e5c07b;
|
|
381
|
-
--red: #e06c75;
|
|
382
|
-
--purple: #c678dd;
|
|
383
|
-
--blue: #61afef;
|
|
384
|
-
|
|
385
|
-
/* Typography */
|
|
386
|
-
--font: 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
|
|
387
|
-
}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
---
|
|
391
|
-
|
|
392
|
-
## File Reference
|
|
393
|
-
|
|
394
|
-
- Logo (full): `/groove-logo.png` (800x254, cyan on transparent)
|
|
395
|
-
- Logo (icon): `/favicon.png` (500x500, infinity symbol)
|
|
396
|
-
- Brand accent: `#33afbc`
|
|
397
|
-
- Site: https://grooveai.dev
|
|
398
|
-
- Docs: https://docs.grooveai.dev
|
|
399
|
-
|
|
400
|
-
---
|
|
401
|
-
|
|
402
|
-
*This spec is the single source of truth for GUI visual design. If it's not in here, don't add it. If it contradicts a mockup, this document wins.*
|