groove-dev 0.27.140 → 0.27.142
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/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/integrations-registry.json +12 -44
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +100 -23
- package/node_modules/@groove-dev/daemon/src/integrations.js +10 -0
- package/node_modules/@groove-dev/daemon/src/introducer.js +1 -1
- package/node_modules/@groove-dev/daemon/src/journalist.js +171 -1
- package/node_modules/@groove-dev/daemon/src/keeper.js +2 -2
- package/node_modules/@groove-dev/daemon/src/memory.js +8 -5
- package/node_modules/@groove-dev/daemon/src/model-lab.js +11 -0
- package/node_modules/@groove-dev/daemon/src/process.js +65 -0
- package/node_modules/@groove-dev/daemon/src/rotator.js +25 -8
- package/node_modules/@groove-dev/daemon/src/validate.js +8 -0
- package/node_modules/@groove-dev/gui/dist/assets/{codemirror-BQqYnZfL.js → codemirror-BYKpdS2W.js} +10 -10
- package/node_modules/@groove-dev/gui/dist/assets/index-Bjd91ufV.js +984 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BqdwIFn4.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +3 -3
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/app.jsx +0 -2
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +3 -4
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +8 -2
- package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +12 -8
- package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +79 -5
- package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +5 -4
- package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +109 -12
- package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +111 -0
- package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +70 -33
- package/node_modules/@groove-dev/gui/src/components/editor/ai-panel.jsx +77 -6
- package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +2 -68
- package/node_modules/@groove-dev/gui/src/components/editor/file-tree.jsx +2 -49
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +15 -4
- package/node_modules/@groove-dev/gui/src/components/keeper/global-modals.jsx +10 -10
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +1 -2
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +151 -3
- package/node_modules/@groove-dev/gui/src/components/marketplace/integration-wizard.jsx +223 -18
- package/node_modules/@groove-dev/gui/src/stores/groove.js +107 -29
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +114 -56
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +3 -71
- package/node_modules/@groove-dev/gui/src/views/memory.jsx +9 -9
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +1 -6
- package/node_modules/@groove-dev/gui/src/views/models.jsx +658 -565
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/integrations-registry.json +12 -44
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +100 -23
- package/packages/daemon/src/integrations.js +10 -0
- package/packages/daemon/src/introducer.js +1 -1
- package/packages/daemon/src/journalist.js +171 -1
- package/packages/daemon/src/keeper.js +2 -2
- package/packages/daemon/src/memory.js +8 -5
- package/packages/daemon/src/model-lab.js +11 -0
- package/packages/daemon/src/process.js +65 -0
- package/packages/daemon/src/rotator.js +25 -8
- package/packages/daemon/src/validate.js +8 -0
- package/packages/gui/dist/assets/{codemirror-BQqYnZfL.js → codemirror-BYKpdS2W.js} +10 -10
- package/packages/gui/dist/assets/index-Bjd91ufV.js +984 -0
- package/packages/gui/dist/assets/index-BqdwIFn4.css +1 -0
- package/packages/gui/dist/index.html +3 -3
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/app.jsx +0 -2
- package/packages/gui/src/components/agents/agent-chat.jsx +3 -4
- package/packages/gui/src/components/agents/agent-feed.jsx +8 -2
- package/packages/gui/src/components/agents/agent-file-tree.jsx +12 -8
- package/packages/gui/src/components/agents/agent-panel.jsx +79 -5
- package/packages/gui/src/components/agents/code-review.jsx +5 -4
- package/packages/gui/src/components/agents/workspace-mode.jsx +109 -12
- package/packages/gui/src/components/dashboard/context-gauges.jsx +111 -0
- package/packages/gui/src/components/dashboard/routing-chart.jsx +70 -33
- package/packages/gui/src/components/editor/ai-panel.jsx +77 -6
- package/packages/gui/src/components/editor/code-editor.jsx +2 -68
- package/packages/gui/src/components/editor/file-tree.jsx +2 -49
- package/packages/gui/src/components/editor/terminal.jsx +15 -4
- package/packages/gui/src/components/keeper/global-modals.jsx +10 -10
- package/packages/gui/src/components/layout/activity-bar.jsx +1 -2
- package/packages/gui/src/components/layout/terminal-panel.jsx +151 -3
- package/packages/gui/src/components/marketplace/integration-wizard.jsx +223 -18
- package/packages/gui/src/stores/groove.js +107 -29
- package/packages/gui/src/views/agents.jsx +114 -56
- package/packages/gui/src/views/dashboard.jsx +2 -0
- package/packages/gui/src/views/marketplace.jsx +3 -71
- package/packages/gui/src/views/memory.jsx +9 -9
- package/packages/gui/src/views/model-lab.jsx +1 -6
- package/packages/gui/src/views/models.jsx +658 -565
- package/plan_files/keeper-manual.md +53 -42
- package/node_modules/@groove-dev/gui/dist/assets/index-BV9CAiw1.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-DK6UIz0n.js +0 -8698
- package/node_modules/@groove-dev/gui/src/components/toys/toy-card.jsx +0 -78
- package/node_modules/@groove-dev/gui/src/components/toys/toy-creator.jsx +0 -144
- package/node_modules/@groove-dev/gui/src/components/toys/toy-launcher.jsx +0 -187
- package/node_modules/@groove-dev/gui/src/views/toys.jsx +0 -162
- package/packages/gui/dist/assets/index-BV9CAiw1.css +0 -1
- package/packages/gui/dist/assets/index-DK6UIz0n.js +0 -8698
- package/packages/gui/src/components/toys/toy-card.jsx +0 -78
- package/packages/gui/src/components/toys/toy-creator.jsx +0 -144
- package/packages/gui/src/components/toys/toy-launcher.jsx +0 -187
- package/packages/gui/src/views/toys.jsx +0 -162
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
2
|
import { useRef, useEffect, useCallback } from 'react';
|
|
3
|
-
import { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter
|
|
4
|
-
import { EditorState, Compartment
|
|
3
|
+
import { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter } from '@codemirror/view';
|
|
4
|
+
import { EditorState, Compartment } from '@codemirror/state';
|
|
5
5
|
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
|
6
6
|
import { bracketMatching } from '@codemirror/language';
|
|
7
7
|
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
|
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
vscodeLight, xcodeLight,
|
|
27
27
|
} from '@uiw/codemirror-themes-all';
|
|
28
28
|
import { useGrooveStore } from '../../stores/groove';
|
|
29
|
-
import { api } from '../../lib/api';
|
|
30
29
|
|
|
31
30
|
const LANGS = {
|
|
32
31
|
javascript: () => javascript({ jsx: true, typescript: false }),
|
|
@@ -79,52 +78,6 @@ export const EDITOR_THEMES = {
|
|
|
79
78
|
basicLight: { label: 'Basic Light', ext: basicLight },
|
|
80
79
|
};
|
|
81
80
|
|
|
82
|
-
// ── Git gutter decorations ───────────────────────────────────
|
|
83
|
-
const setGitLines = StateEffect.define();
|
|
84
|
-
|
|
85
|
-
const gitLinesField = StateField.define({
|
|
86
|
-
create() { return { added: new Set(), modified: new Set(), deleted: new Set() }; },
|
|
87
|
-
update(value, tr) {
|
|
88
|
-
for (const e of tr.effects) {
|
|
89
|
-
if (e.is(setGitLines)) return e.value;
|
|
90
|
-
}
|
|
91
|
-
return value;
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
class GitGutterMarker extends GutterMarker {
|
|
96
|
-
constructor(type) { super(); this.type = type; }
|
|
97
|
-
toDOM() {
|
|
98
|
-
const el = document.createElement('div');
|
|
99
|
-
el.style.width = '3px';
|
|
100
|
-
el.style.height = '100%';
|
|
101
|
-
el.style.borderRadius = '1px';
|
|
102
|
-
if (this.type === 'added') el.style.background = 'var(--color-success)';
|
|
103
|
-
else if (this.type === 'modified') el.style.background = 'var(--color-warning)';
|
|
104
|
-
else el.style.background = 'var(--color-danger)';
|
|
105
|
-
return el;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const addedMarker = new GitGutterMarker('added');
|
|
110
|
-
const modifiedMarker = new GitGutterMarker('modified');
|
|
111
|
-
const deletedMarker = new GitGutterMarker('deleted');
|
|
112
|
-
|
|
113
|
-
const gitGutter = gutter({
|
|
114
|
-
class: 'cm-git-gutter',
|
|
115
|
-
markers(view) {
|
|
116
|
-
const lines = view.state.field(gitLinesField);
|
|
117
|
-
const markers = [];
|
|
118
|
-
for (let i = 1; i <= view.state.doc.lines; i++) {
|
|
119
|
-
const lineStart = view.state.doc.line(i).from;
|
|
120
|
-
if (lines.added.has(i)) markers.push(addedMarker.range(lineStart));
|
|
121
|
-
else if (lines.modified.has(i)) markers.push(modifiedMarker.range(lineStart));
|
|
122
|
-
else if (lines.deleted.has(i)) markers.push(deletedMarker.range(lineStart));
|
|
123
|
-
}
|
|
124
|
-
return RangeSet.of(markers);
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
|
|
128
81
|
const editorChrome = EditorView.theme({
|
|
129
82
|
'&': { fontFamily: 'var(--font-mono)', fontSize: '12px', height: '100%', lineHeight: '1.6' },
|
|
130
83
|
'.cm-scroller': { overflow: 'auto', padding: '4px 0' },
|
|
@@ -139,8 +92,6 @@ const editorChrome = EditorView.theme({
|
|
|
139
92
|
'.cm-search .cm-button, .cm-button': { borderRadius: '4px', padding: '2px 8px', fontSize: '10px', fontFamily: 'var(--font-sans)', cursor: 'pointer', backgroundImage: 'none' },
|
|
140
93
|
'.cm-search br': { display: 'none' },
|
|
141
94
|
'.cm-panel.cm-search [name=close]': { cursor: 'pointer', padding: '0 4px' },
|
|
142
|
-
'.cm-git-gutter': { width: '4px', marginRight: '2px' },
|
|
143
|
-
'.cm-git-gutter .cm-gutterElement': { padding: '0', minWidth: '3px' },
|
|
144
95
|
});
|
|
145
96
|
|
|
146
97
|
function getThemeExt(key) {
|
|
@@ -186,8 +137,6 @@ export function CodeEditor({ content, language, onChange, onSave, onCursorChange
|
|
|
186
137
|
const state = EditorState.create({
|
|
187
138
|
doc: content || '',
|
|
188
139
|
extensions: [
|
|
189
|
-
gitLinesField,
|
|
190
|
-
gitGutter,
|
|
191
140
|
lineNumbers(),
|
|
192
141
|
highlightActiveLine(),
|
|
193
142
|
highlightActiveLineGutter(),
|
|
@@ -220,21 +169,6 @@ export function CodeEditor({ content, language, onChange, onSave, onCursorChange
|
|
|
220
169
|
return () => { view.destroy(); viewRef.current = null; if (externalViewRef) externalViewRef.current = null; };
|
|
221
170
|
}, []);
|
|
222
171
|
|
|
223
|
-
// Fetch and apply git line status when filePath changes
|
|
224
|
-
useEffect(() => {
|
|
225
|
-
const view = viewRef.current;
|
|
226
|
-
if (!view || !filePath) return;
|
|
227
|
-
api.get(`/files/git-line-status?path=${encodeURIComponent(filePath)}`).then((data) => {
|
|
228
|
-
if (!data?.lines) return;
|
|
229
|
-
const lines = {
|
|
230
|
-
added: new Set(data.lines.added || []),
|
|
231
|
-
modified: new Set(data.lines.modified || []),
|
|
232
|
-
deleted: new Set(data.lines.deleted || []),
|
|
233
|
-
};
|
|
234
|
-
view.dispatch({ effects: setGitLines.of(lines) });
|
|
235
|
-
}).catch(() => {});
|
|
236
|
-
}, [filePath, content]);
|
|
237
|
-
|
|
238
172
|
useEffect(() => {
|
|
239
173
|
const view = viewRef.current;
|
|
240
174
|
if (!view) return;
|
|
@@ -5,8 +5,8 @@ import { cn } from '../../lib/cn';
|
|
|
5
5
|
import { api } from '../../lib/api';
|
|
6
6
|
import {
|
|
7
7
|
ChevronRight, ChevronDown, File, Folder, FolderOpen,
|
|
8
|
-
|
|
9
|
-
ChevronsDownUp, PanelLeftClose,
|
|
8
|
+
FolderPlus, Search, RefreshCw, Trash2, Pencil, FilePlus,
|
|
9
|
+
ChevronsDownUp, PanelLeftClose,
|
|
10
10
|
} from 'lucide-react';
|
|
11
11
|
import { ScrollArea } from '../ui/scroll-area';
|
|
12
12
|
|
|
@@ -240,25 +240,6 @@ function TreeDir({ dirPath, depth, activePath, onFileClick, expanded, onDirToggl
|
|
|
240
240
|
|
|
241
241
|
// ── Main FileTree ────────────────────────────────────────────
|
|
242
242
|
|
|
243
|
-
// ── Collapsible Section ──────────────────────────────────────
|
|
244
|
-
function CollapsibleSection({ title, icon: Icon, count, defaultOpen = true, children }) {
|
|
245
|
-
const [open, setOpen] = useState(defaultOpen);
|
|
246
|
-
return (
|
|
247
|
-
<div className="border-b border-border-subtle">
|
|
248
|
-
<button
|
|
249
|
-
onClick={() => setOpen(!open)}
|
|
250
|
-
className="w-full flex items-center gap-1.5 px-2 py-1.5 text-2xs font-sans font-medium text-text-2 uppercase tracking-wide hover:bg-surface-4 transition-colors cursor-pointer"
|
|
251
|
-
>
|
|
252
|
-
{open ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
|
|
253
|
-
<Icon size={11} className="text-text-3" />
|
|
254
|
-
<span className="flex-1 text-left">{title}</span>
|
|
255
|
-
{count > 0 && <span className="text-text-4">{count}</span>}
|
|
256
|
-
</button>
|
|
257
|
-
{open && children}
|
|
258
|
-
</div>
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
243
|
export function FileTree({ rootDir, onCollapse }) {
|
|
263
244
|
const treeCache = useGrooveStore((s) => s.editorTreeCache);
|
|
264
245
|
const activeFile = useGrooveStore((s) => s.editorActiveFile);
|
|
@@ -508,34 +489,6 @@ export function FileTree({ rootDir, onCollapse }) {
|
|
|
508
489
|
|
|
509
490
|
{/* Tree */}
|
|
510
491
|
<ScrollArea className="flex-1">
|
|
511
|
-
{/* Git Changes section */}
|
|
512
|
-
{gitChanges.length > 0 && (
|
|
513
|
-
<CollapsibleSection title="Git Changes" icon={GitBranch} count={gitChanges.length} defaultOpen={true}>
|
|
514
|
-
<div className="py-0.5">
|
|
515
|
-
{gitChanges.map((entry) => {
|
|
516
|
-
const name = entry.path.split('/').pop();
|
|
517
|
-
const statusColor = entry.status === 'A' || entry.status === '?' ? 'text-success' : entry.status === 'D' ? 'text-danger' : 'text-warning';
|
|
518
|
-
return (
|
|
519
|
-
<button
|
|
520
|
-
key={entry.path}
|
|
521
|
-
onClick={() => openFile(entry.path)}
|
|
522
|
-
className={cn(
|
|
523
|
-
'w-full flex items-center gap-1.5 px-3 py-[3px] text-xs font-sans cursor-pointer',
|
|
524
|
-
'hover:bg-surface-5 transition-colors text-left',
|
|
525
|
-
activeFile === entry.path ? 'bg-accent/10 text-text-0' : 'text-text-1',
|
|
526
|
-
)}
|
|
527
|
-
>
|
|
528
|
-
<File size={12} className={cn('flex-shrink-0', getFileColor(name))} />
|
|
529
|
-
<span className="truncate flex-1">{name}</span>
|
|
530
|
-
<span className={cn('text-2xs font-mono flex-shrink-0', statusColor)}>{entry.status}</span>
|
|
531
|
-
</button>
|
|
532
|
-
);
|
|
533
|
-
})}
|
|
534
|
-
</div>
|
|
535
|
-
</CollapsibleSection>
|
|
536
|
-
)}
|
|
537
|
-
|
|
538
|
-
{/* File Explorer */}
|
|
539
492
|
<div
|
|
540
493
|
className="py-1"
|
|
541
494
|
onDragOver={(e) => { if (!dragState.draggingPath) return; e.preventDefault(); setDragOverDir(null); }}
|
|
@@ -22,7 +22,7 @@ const THEME = {
|
|
|
22
22
|
let tabCounter = 0;
|
|
23
23
|
let spawnSeq = 0;
|
|
24
24
|
|
|
25
|
-
function TerminalInstance({ tabId, visible, registerKill }) {
|
|
25
|
+
function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
|
|
26
26
|
const containerRef = useRef(null);
|
|
27
27
|
const termRef = useRef(null);
|
|
28
28
|
const fitRef = useRef(null);
|
|
@@ -70,6 +70,11 @@ function TerminalInstance({ tabId, visible, registerKill }) {
|
|
|
70
70
|
termRef.current = term;
|
|
71
71
|
fitRef.current = fitAddon;
|
|
72
72
|
|
|
73
|
+
term.onSelectionChange(() => {
|
|
74
|
+
const text = term.getSelection();
|
|
75
|
+
onSelectionChange?.(text || '');
|
|
76
|
+
});
|
|
77
|
+
|
|
73
78
|
let spawnAttempts = 0;
|
|
74
79
|
function trySpawn() {
|
|
75
80
|
spawnAttempts++;
|
|
@@ -117,7 +122,6 @@ function TerminalInstance({ tabId, visible, registerKill }) {
|
|
|
117
122
|
});
|
|
118
123
|
}
|
|
119
124
|
|
|
120
|
-
// Fit first, then spawn — ensures PTY gets correct column count
|
|
121
125
|
requestAnimationFrame(() => {
|
|
122
126
|
try { fitAddon.fit(); } catch {}
|
|
123
127
|
trySpawn();
|
|
@@ -169,6 +173,7 @@ export function TerminalManager() {
|
|
|
169
173
|
|
|
170
174
|
const [tabs, setTabs] = useState([{ id: 'term-0', label: 'Terminal' }]);
|
|
171
175
|
const [activeTab, setActiveTab] = useState('term-0');
|
|
176
|
+
const [selectedText, setSelectedText] = useState('');
|
|
172
177
|
const killFns = useRef({});
|
|
173
178
|
|
|
174
179
|
const registerKill = useCallback((tabId, fn) => { killFns.current[tabId] = fn; }, []);
|
|
@@ -217,13 +222,19 @@ export function TerminalManager() {
|
|
|
217
222
|
onToggleFullHeight={() => setFullHeight(true)}
|
|
218
223
|
onMinimize={() => setFullHeight(false)}
|
|
219
224
|
onClose={() => setTerminalVisible(false)}
|
|
225
|
+
selectedText={selectedText}
|
|
220
226
|
>
|
|
221
227
|
{tabs.map((tab) => (
|
|
222
|
-
<TerminalInstance
|
|
228
|
+
<TerminalInstance
|
|
229
|
+
key={tab.id}
|
|
230
|
+
tabId={tab.id}
|
|
231
|
+
visible={tab.id === activeTab}
|
|
232
|
+
registerKill={registerKill}
|
|
233
|
+
onSelectionChange={tab.id === activeTab ? setSelectedText : undefined}
|
|
234
|
+
/>
|
|
223
235
|
))}
|
|
224
236
|
</TerminalPanel>
|
|
225
237
|
);
|
|
226
238
|
}
|
|
227
239
|
|
|
228
|
-
// Keep backward-compat export for existing imports
|
|
229
240
|
export { TerminalManager as TerminalEmulator };
|
|
@@ -4,18 +4,18 @@ import { useState, useEffect, useRef } from 'react';
|
|
|
4
4
|
import { useGrooveStore } from '../../stores/groove';
|
|
5
5
|
import { Dialog, DialogContent } from '../ui/dialog';
|
|
6
6
|
import { Button } from '../ui/button';
|
|
7
|
-
import { Save
|
|
7
|
+
import { Save } from 'lucide-react';
|
|
8
8
|
|
|
9
9
|
const COMMANDS = [
|
|
10
|
-
{ cmd: '
|
|
11
|
-
{ cmd: '
|
|
12
|
-
{ cmd: '
|
|
13
|
-
{ cmd: '
|
|
14
|
-
{ cmd: '
|
|
15
|
-
{ cmd: '
|
|
16
|
-
{ cmd: '
|
|
17
|
-
{ cmd: '
|
|
18
|
-
{ cmd: '[instruct]', args: '',
|
|
10
|
+
{ cmd: 'save', args: '#tag', desc: 'Save the message and send it to the agent' },
|
|
11
|
+
{ cmd: 'append', args: '#tag', desc: 'Add to an existing memory and send to agent' },
|
|
12
|
+
{ cmd: 'update', args: '#tag', desc: 'Open the editor to modify a memory in place' },
|
|
13
|
+
{ cmd: 'delete', args: '#tag', desc: 'Remove a memory permanently' },
|
|
14
|
+
{ cmd: 'view', args: '#tag', desc: 'Read a memory in the viewer' },
|
|
15
|
+
{ cmd: 'read', args: '#tag1 #tag2 ...', desc: 'Send memory content to the agent — chat stays clean' },
|
|
16
|
+
{ cmd: 'doc', args: '#tag', desc: 'AI synthesizes the full conversation into a document' },
|
|
17
|
+
{ cmd: 'link', args: '#tag path/to/doc', desc: 'Link a memory to a NORTHSTAR or external document' },
|
|
18
|
+
{ cmd: '[instruct]', args: '', desc: 'Show this command reference' },
|
|
19
19
|
];
|
|
20
20
|
|
|
21
21
|
function KeeperEditorModal() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import { Network, Code2, ChartSpline, Puzzle,
|
|
2
|
+
import { Network, Code2, ChartSpline, Puzzle, Users, Box, FlaskConical, Newspaper, Settings, Globe, MessageCircle, Eye, BookOpen } from 'lucide-react';
|
|
3
3
|
import { cn } from '../../lib/cn';
|
|
4
4
|
import { Tooltip } from '../ui/tooltip';
|
|
5
5
|
import { useGrooveStore } from '../../stores/groove';
|
|
@@ -13,7 +13,6 @@ const BASE_NAV_ITEMS = [
|
|
|
13
13
|
{ id: 'memory', icon: BookOpen, label: 'Memory' },
|
|
14
14
|
{ id: 'teams', icon: Users, label: 'Teams' },
|
|
15
15
|
{ id: 'marketplace', icon: Puzzle, label: 'Marketplace' },
|
|
16
|
-
{ id: 'toys', icon: Gamepad2, label: 'Toys' },
|
|
17
16
|
{ id: 'models', icon: Box, label: 'Models' },
|
|
18
17
|
{ id: 'model-lab', icon: FlaskConical, label: 'Model Lab' },
|
|
19
18
|
];
|
|
@@ -1,7 +1,87 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import { useRef, useCallback, useState } from 'react';
|
|
3
|
-
import { Maximize2, Minimize2, Plus, X, Terminal } from 'lucide-react';
|
|
2
|
+
import { useRef, useCallback, useState, useEffect } from 'react';
|
|
3
|
+
import { Maximize2, Minimize2, Plus, X, Terminal, Send, ChevronDown, Sparkles } from 'lucide-react';
|
|
4
4
|
import { cn } from '../../lib/cn';
|
|
5
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
6
|
+
import { Tooltip } from '../ui/tooltip';
|
|
7
|
+
|
|
8
|
+
function AgentPicker({ onSelect, onClose }) {
|
|
9
|
+
const ref = useRef(null);
|
|
10
|
+
const agents = useGrooveStore((s) => s.agents);
|
|
11
|
+
const teams = useGrooveStore((s) => s.teams);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
function handleClick(e) {
|
|
15
|
+
if (ref.current && !ref.current.contains(e.target)) onClose();
|
|
16
|
+
}
|
|
17
|
+
function handleKey(e) {
|
|
18
|
+
if (e.key === 'Escape') onClose();
|
|
19
|
+
}
|
|
20
|
+
document.addEventListener('mousedown', handleClick);
|
|
21
|
+
document.addEventListener('keydown', handleKey);
|
|
22
|
+
return () => {
|
|
23
|
+
document.removeEventListener('mousedown', handleClick);
|
|
24
|
+
document.removeEventListener('keydown', handleKey);
|
|
25
|
+
};
|
|
26
|
+
}, [onClose]);
|
|
27
|
+
|
|
28
|
+
const running = agents.filter((a) => a.status === 'running' || a.status === 'starting');
|
|
29
|
+
const stopped = agents.filter((a) => a.status !== 'running' && a.status !== 'starting');
|
|
30
|
+
|
|
31
|
+
function teamName(teamId) {
|
|
32
|
+
const team = teams.find((t) => t.id === teamId);
|
|
33
|
+
return team?.name || 'Default';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
ref={ref}
|
|
39
|
+
className="absolute bottom-full right-0 mb-1 z-50 min-w-[220px] max-h-[300px] overflow-y-auto py-1 bg-surface-2 border border-border rounded-lg shadow-xl"
|
|
40
|
+
>
|
|
41
|
+
{running.length > 0 && (
|
|
42
|
+
<>
|
|
43
|
+
<div className="px-3 py-1 text-2xs text-text-4 font-sans font-medium uppercase tracking-wider">Active</div>
|
|
44
|
+
{running.map((agent) => (
|
|
45
|
+
<button
|
|
46
|
+
key={agent.id}
|
|
47
|
+
onClick={() => onSelect(agent.id)}
|
|
48
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs font-sans text-text-1 hover:bg-surface-5 cursor-pointer transition-colors text-left"
|
|
49
|
+
>
|
|
50
|
+
<span className="w-1.5 h-1.5 rounded-full bg-success flex-shrink-0" />
|
|
51
|
+
<span className="truncate flex-1">{agent.name}</span>
|
|
52
|
+
<span className="text-2xs text-text-4">{teamName(agent.teamId)}</span>
|
|
53
|
+
</button>
|
|
54
|
+
))}
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
{stopped.length > 0 && (
|
|
58
|
+
<>
|
|
59
|
+
<div className="px-3 py-1 text-2xs text-text-4 font-sans font-medium uppercase tracking-wider">
|
|
60
|
+
{running.length > 0 ? 'Other' : 'Agents'}
|
|
61
|
+
</div>
|
|
62
|
+
{stopped.slice(0, 10).map((agent) => (
|
|
63
|
+
<button
|
|
64
|
+
key={agent.id}
|
|
65
|
+
onClick={() => onSelect(agent.id)}
|
|
66
|
+
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs font-sans text-text-2 hover:bg-surface-5 cursor-pointer transition-colors text-left"
|
|
67
|
+
>
|
|
68
|
+
<span className={cn('w-1.5 h-1.5 rounded-full flex-shrink-0',
|
|
69
|
+
agent.status === 'completed' ? 'bg-accent' : agent.status === 'crashed' ? 'bg-danger' : 'bg-text-4',
|
|
70
|
+
)} />
|
|
71
|
+
<span className="truncate flex-1">{agent.name}</span>
|
|
72
|
+
<span className="text-2xs text-text-4">{teamName(agent.teamId)}</span>
|
|
73
|
+
</button>
|
|
74
|
+
))}
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
77
|
+
{agents.length === 0 && (
|
|
78
|
+
<div className="px-3 py-3 text-xs text-text-4 font-sans text-center">
|
|
79
|
+
No agents available
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
5
85
|
|
|
6
86
|
export function TerminalPanel({
|
|
7
87
|
children,
|
|
@@ -18,13 +98,22 @@ export function TerminalPanel({
|
|
|
18
98
|
onMinimize,
|
|
19
99
|
onClose,
|
|
20
100
|
onRenameTab,
|
|
101
|
+
selectedText,
|
|
21
102
|
}) {
|
|
22
103
|
const dragging = useRef(false);
|
|
23
104
|
const [renamingId, setRenamingId] = useState(null);
|
|
24
105
|
const [renameValue, setRenameValue] = useState('');
|
|
106
|
+
const [showPicker, setShowPicker] = useState(false);
|
|
25
107
|
const startY = useRef(0);
|
|
26
108
|
const startH = useRef(0);
|
|
27
109
|
|
|
110
|
+
const activeAgent = useGrooveStore((s) => s.editorSelectedAgent);
|
|
111
|
+
const agents = useGrooveStore((s) => s.agents);
|
|
112
|
+
const attachSnippet = useGrooveStore((s) => s.attachSnippet);
|
|
113
|
+
|
|
114
|
+
const agent = agents.find((a) => a.id === activeAgent);
|
|
115
|
+
const hasSelection = selectedText && selectedText.trim().length > 0;
|
|
116
|
+
|
|
28
117
|
const onMouseDown = useCallback((e) => {
|
|
29
118
|
if (fullHeight) return;
|
|
30
119
|
e.preventDefault();
|
|
@@ -49,6 +138,21 @@ export function TerminalPanel({
|
|
|
49
138
|
document.addEventListener('mouseup', onMouseUp);
|
|
50
139
|
}, [height, onHeightChange, fullHeight]);
|
|
51
140
|
|
|
141
|
+
function sendToAgent(agentId) {
|
|
142
|
+
if (!agentId || !selectedText?.trim()) return;
|
|
143
|
+
setShowPicker(false);
|
|
144
|
+
useGrooveStore.setState({ editorSelectedAgent: agentId });
|
|
145
|
+
attachSnippet({ type: 'terminal', code: selectedText.trim() });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function handleSendClick() {
|
|
149
|
+
if (activeAgent) {
|
|
150
|
+
sendToAgent(activeAgent);
|
|
151
|
+
} else {
|
|
152
|
+
setShowPicker(true);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
52
156
|
const tabList = tabs || [{ id: 'default', label: 'Terminal' }];
|
|
53
157
|
|
|
54
158
|
return (
|
|
@@ -116,8 +220,52 @@ export function TerminalPanel({
|
|
|
116
220
|
</button>
|
|
117
221
|
</div>
|
|
118
222
|
|
|
119
|
-
{/* Window controls */}
|
|
223
|
+
{/* Send to Agent + Window controls */}
|
|
120
224
|
<div className="flex items-center gap-0.5 flex-shrink-0 ml-2">
|
|
225
|
+
{/* Send to Agent */}
|
|
226
|
+
{hasSelection && (
|
|
227
|
+
<div className="relative flex items-center">
|
|
228
|
+
<Tooltip content={activeAgent ? `Send to ${agent?.name || 'agent'}` : 'Send to agent'} side="top">
|
|
229
|
+
<button
|
|
230
|
+
onClick={handleSendClick}
|
|
231
|
+
className={cn(
|
|
232
|
+
'flex items-center gap-1.5 px-2 py-1 rounded text-xs font-sans cursor-pointer transition-colors mr-1',
|
|
233
|
+
'bg-accent/15 text-accent hover:bg-accent/25 disabled:opacity-50',
|
|
234
|
+
)}
|
|
235
|
+
>
|
|
236
|
+
{activeAgent ? (
|
|
237
|
+
<>
|
|
238
|
+
<Send size={11} />
|
|
239
|
+
<span className="text-2xs max-w-[80px] truncate">{agent?.name || 'Agent'}</span>
|
|
240
|
+
</>
|
|
241
|
+
) : (
|
|
242
|
+
<>
|
|
243
|
+
<Sparkles size={11} />
|
|
244
|
+
<span className="text-2xs">Agent</span>
|
|
245
|
+
<ChevronDown size={9} />
|
|
246
|
+
</>
|
|
247
|
+
)}
|
|
248
|
+
</button>
|
|
249
|
+
</Tooltip>
|
|
250
|
+
{!activeAgent && hasSelection && (
|
|
251
|
+
<Tooltip content="Pick agent" side="top">
|
|
252
|
+
<button
|
|
253
|
+
onClick={() => setShowPicker(!showPicker)}
|
|
254
|
+
className="p-1 rounded text-text-3 hover:text-accent hover:bg-accent/10 cursor-pointer transition-colors mr-1"
|
|
255
|
+
>
|
|
256
|
+
<ChevronDown size={10} />
|
|
257
|
+
</button>
|
|
258
|
+
</Tooltip>
|
|
259
|
+
)}
|
|
260
|
+
{showPicker && (
|
|
261
|
+
<AgentPicker
|
|
262
|
+
onSelect={(id) => sendToAgent(id)}
|
|
263
|
+
onClose={() => setShowPicker(false)}
|
|
264
|
+
/>
|
|
265
|
+
)}
|
|
266
|
+
</div>
|
|
267
|
+
)}
|
|
268
|
+
|
|
121
269
|
{fullHeight ? (
|
|
122
270
|
<button
|
|
123
271
|
onClick={onMinimize}
|