groove-dev 0.27.18 → 0.27.20
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/gui/dist/assets/{index-Bg6_D2xK.css → index-DEPOF5Bv.css} +1 -1
- package/node_modules/@groove-dev/gui/dist/assets/{index-D3rvwTHD.js → index-plyUnji6.js} +1728 -1728
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/stores/groove.js +25 -0
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +91 -96
- package/package.json +1 -1
- package/packages/gui/dist/assets/{index-Bg6_D2xK.css → index-DEPOF5Bv.css} +1 -1
- package/packages/gui/dist/assets/{index-D3rvwTHD.js → index-plyUnji6.js} +1728 -1728
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/src/components/layout/activity-bar.jsx +5 -5
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +4 -4
- package/packages/gui/src/stores/groove.js +25 -0
- package/packages/gui/src/views/agents.jsx +91 -96
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
8
|
<title>Groove GUI</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-plyUnji6.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DEPOF5Bv.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -27,7 +27,7 @@ export function ActivityBar({ activeView, detailPanel, onNavigate, onTogglePanel
|
|
|
27
27
|
<nav className="w-12 flex-shrink-0 flex flex-col bg-surface-3 border-r border-border">
|
|
28
28
|
{/* Sidebar header — aligns with BreadcrumbBar */}
|
|
29
29
|
{darwinTrafficLights && (
|
|
30
|
-
<div className="
|
|
30
|
+
<div className="flex-shrink-0 flex items-center justify-center border-b border-border py-px">
|
|
31
31
|
<img src="/favicon.png" alt="Groove" className="h-6 w-6 rounded-full" />
|
|
32
32
|
</div>
|
|
33
33
|
)}
|
|
@@ -41,8 +41,8 @@ export function ActivityBar({ activeView, detailPanel, onNavigate, onTogglePanel
|
|
|
41
41
|
className={cn(
|
|
42
42
|
'w-8 h-8 flex items-center justify-center rounded-md transition-colors cursor-pointer',
|
|
43
43
|
activeView === item.id
|
|
44
|
-
? 'text-
|
|
45
|
-
: 'text-text-3 hover:text-
|
|
44
|
+
? 'text-accent bg-accent/10'
|
|
45
|
+
: 'text-text-3 hover:text-accent hover:bg-accent/10',
|
|
46
46
|
)}
|
|
47
47
|
>
|
|
48
48
|
<item.icon size={16} strokeWidth={activeView === item.id ? 2 : 1.5} />
|
|
@@ -67,8 +67,8 @@ export function ActivityBar({ activeView, detailPanel, onNavigate, onTogglePanel
|
|
|
67
67
|
className={cn(
|
|
68
68
|
'w-8 h-8 flex items-center justify-center rounded-md transition-colors cursor-pointer',
|
|
69
69
|
isActive
|
|
70
|
-
? 'text-
|
|
71
|
-
: 'text-text-3 hover:text-
|
|
70
|
+
? 'text-accent bg-accent/10'
|
|
71
|
+
: 'text-text-3 hover:text-accent hover:bg-accent/10',
|
|
72
72
|
)}
|
|
73
73
|
>
|
|
74
74
|
<item.icon size={16} strokeWidth={isActive ? 2 : 1.5} />
|
|
@@ -67,14 +67,14 @@ function UserMenu() {
|
|
|
67
67
|
<button
|
|
68
68
|
onClick={() => setOpen(!open)}
|
|
69
69
|
className={cn(
|
|
70
|
-
'flex items-center gap-2 h-7 pl-
|
|
70
|
+
'flex items-center gap-2 h-7 pl-2 pr-1 rounded-md transition-colors cursor-pointer select-none',
|
|
71
71
|
open ? 'bg-surface-1 border border-border' : 'hover:bg-surface-1 border border-transparent',
|
|
72
72
|
)}
|
|
73
73
|
>
|
|
74
|
-
<ProfilePic user={user} size={20} />
|
|
75
74
|
<span className="text-xs text-text-1 font-sans font-medium max-w-[100px] truncate">
|
|
76
75
|
{user?.displayName || user?.id || 'Account'}
|
|
77
76
|
</span>
|
|
77
|
+
<ProfilePic user={user} size={20} />
|
|
78
78
|
<ChevronDown size={10} className={cn('text-text-4 transition-transform', open && 'rotate-180')} />
|
|
79
79
|
</button>
|
|
80
80
|
|
|
@@ -171,7 +171,7 @@ export function BreadcrumbBar({
|
|
|
171
171
|
onClick={() => window.groove?.openFolder?.()}
|
|
172
172
|
className="text-2xs font-mono font-semibold text-accent bg-accent/10 px-1.5 py-0.5 rounded flex-shrink-0 hover:bg-accent/20 transition-colors cursor-pointer"
|
|
173
173
|
>
|
|
174
|
-
{instanceName}
|
|
174
|
+
/{instanceName}
|
|
175
175
|
</button>
|
|
176
176
|
)}
|
|
177
177
|
|
|
@@ -202,7 +202,7 @@ export function BreadcrumbBar({
|
|
|
202
202
|
<button
|
|
203
203
|
onClick={onOpenCommandPalette}
|
|
204
204
|
className={cn(
|
|
205
|
-
'absolute left-1/2 -translate-x-1/2 flex items-center gap-2.5 h-8 px-4 rounded-md w-full max-w-md',
|
|
205
|
+
'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 flex items-center gap-2.5 h-8 px-4 rounded-md w-full max-w-md',
|
|
206
206
|
'bg-surface-1 border border-border-subtle',
|
|
207
207
|
'text-xs text-text-4 font-sans',
|
|
208
208
|
'hover:border-border hover:text-text-3 transition-colors cursor-pointer',
|
|
@@ -592,6 +592,31 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
592
592
|
try { localStorage.setItem('groove:teamOrder', JSON.stringify(teams.map((t) => t.id))); } catch {}
|
|
593
593
|
},
|
|
594
594
|
|
|
595
|
+
async cloneTeam(id) {
|
|
596
|
+
const team = get().teams.find((t) => t.id === id);
|
|
597
|
+
if (!team) return;
|
|
598
|
+
const sourceAgents = get().agents.filter((a) => a.teamId === id);
|
|
599
|
+
try {
|
|
600
|
+
const newTeam = await api.post('/teams', { name: `${team.name} (copy)` });
|
|
601
|
+
set({ activeTeamId: newTeam.id });
|
|
602
|
+
localStorage.setItem('groove:activeTeamId', newTeam.id);
|
|
603
|
+
for (const agent of sourceAgents) {
|
|
604
|
+
await api.post('/agents', {
|
|
605
|
+
role: agent.role,
|
|
606
|
+
name: agent.name,
|
|
607
|
+
provider: agent.provider,
|
|
608
|
+
model: agent.model,
|
|
609
|
+
scope: agent.scope,
|
|
610
|
+
teamId: newTeam.id,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
get().addToast('success', `Cloned "${team.name}" with ${sourceAgents.length} agent${sourceAgents.length !== 1 ? 's' : ''}`);
|
|
614
|
+
return newTeam;
|
|
615
|
+
} catch (err) {
|
|
616
|
+
get().addToast('error', 'Failed to clone team', err.message);
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
|
|
595
620
|
async renameTeam(id, name) {
|
|
596
621
|
try {
|
|
597
622
|
const team = await api.patch(`/teams/${encodeURIComponent(id)}`, { name });
|
|
@@ -10,7 +10,8 @@ import { RootNode } from '../components/agents/root-node';
|
|
|
10
10
|
import { cn } from '../lib/cn';
|
|
11
11
|
import { Button } from '../components/ui/button';
|
|
12
12
|
import { Badge } from '../components/ui/badge';
|
|
13
|
-
import { Plus, Users, Zap, X, Check, Rocket, Server, Monitor, Code2, TestTube, Shield, Pencil, ChevronLeft, ChevronRight, FolderOpen } from 'lucide-react';
|
|
13
|
+
import { Plus, Users, Zap, X, Check, Rocket, Server, Monitor, Code2, TestTube, Shield, Pencil, Copy, Trash2, ChevronLeft, ChevronRight, FolderOpen } from 'lucide-react';
|
|
14
|
+
import { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuSeparator } from '../components/ui/context-menu';
|
|
14
15
|
|
|
15
16
|
const NODE_TYPES = { agentNode: AgentNode, rootNode: RootNode };
|
|
16
17
|
const NODE_W = 220;
|
|
@@ -78,7 +79,7 @@ export function TeamTabBar() {
|
|
|
78
79
|
const createTeam = useGrooveStore((s) => s.createTeam);
|
|
79
80
|
const deleteTeam = useGrooveStore((s) => s.deleteTeam);
|
|
80
81
|
const renameTeam = useGrooveStore((s) => s.renameTeam);
|
|
81
|
-
|
|
82
|
+
const cloneTeam = useGrooveStore((s) => s.cloneTeam);
|
|
82
83
|
const reorderTeams = useGrooveStore((s) => s.reorderTeams);
|
|
83
84
|
|
|
84
85
|
const [creating, setCreating] = useState(false);
|
|
@@ -158,102 +159,96 @@ export function TeamTabBar() {
|
|
|
158
159
|
const running = agents.filter((a) => a.teamId === team.id && (a.status === 'running' || a.status === 'starting')).length;
|
|
159
160
|
|
|
160
161
|
return (
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
)}
|
|
219
|
-
|
|
220
|
-
{/* Agent count badge */}
|
|
221
|
-
{count > 0 && !isRenaming && (
|
|
222
|
-
<span className={cn(
|
|
223
|
-
'flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full text-2xs font-mono font-semibold',
|
|
224
|
-
running > 0 ? 'bg-accent/15 text-accent' : 'bg-surface-4 text-text-3',
|
|
225
|
-
)}>
|
|
226
|
-
{count}
|
|
227
|
-
</span>
|
|
228
|
-
)}
|
|
229
|
-
|
|
230
|
-
{/* Actions — rename + close */}
|
|
231
|
-
{!isRenaming && (
|
|
232
|
-
<div className="flex items-center gap-0.5 ml-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
233
|
-
<button
|
|
234
|
-
onClick={(e) => { e.stopPropagation(); startRename(team); }}
|
|
235
|
-
className="p-0.5 rounded hover:bg-surface-5 text-text-4 hover:text-text-1 cursor-pointer"
|
|
236
|
-
title="Rename team"
|
|
237
|
-
>
|
|
238
|
-
<Pencil size={10} />
|
|
239
|
-
</button>
|
|
240
|
-
{!team.isDefault && (
|
|
241
|
-
<button
|
|
242
|
-
onClick={(e) => { e.stopPropagation(); deleteTeam(team.id); }}
|
|
243
|
-
className="p-0.5 rounded hover:bg-surface-5 text-text-4 hover:text-danger cursor-pointer"
|
|
244
|
-
title="Delete team"
|
|
245
|
-
>
|
|
246
|
-
<X size={10} />
|
|
247
|
-
</button>
|
|
162
|
+
<ContextMenu key={team.id}>
|
|
163
|
+
<ContextMenuTrigger asChild>
|
|
164
|
+
<div
|
|
165
|
+
draggable={!isRenaming}
|
|
166
|
+
onDragStart={(e) => { setDragId(team.id); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', ''); }}
|
|
167
|
+
onDragEnd={() => { setDragId(null); setDragOverId(null); }}
|
|
168
|
+
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; if (dragId && dragId !== team.id) setDragOverId(team.id); }}
|
|
169
|
+
onDragLeave={() => { if (dragOverId === team.id) setDragOverId(null); }}
|
|
170
|
+
onDrop={(e) => {
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
if (!dragId || dragId === team.id) return;
|
|
173
|
+
const from = teams.findIndex((t) => t.id === dragId);
|
|
174
|
+
const to = teams.findIndex((t) => t.id === team.id);
|
|
175
|
+
if (from !== -1 && to !== -1) reorderTeams(from, to);
|
|
176
|
+
setDragId(null);
|
|
177
|
+
setDragOverId(null);
|
|
178
|
+
}}
|
|
179
|
+
onClick={() => !isRenaming && switchTeam(team.id)}
|
|
180
|
+
onDoubleClick={() => startRename(team)}
|
|
181
|
+
className={cn(
|
|
182
|
+
'relative flex items-center gap-2 px-3 h-9 text-xs font-sans cursor-pointer select-none transition-colors flex-shrink-0',
|
|
183
|
+
isActive
|
|
184
|
+
? 'text-text-0 font-semibold border-x border-x-border bg-[#242830]'
|
|
185
|
+
: 'text-text-3 hover:text-text-1 hover:bg-surface-3/50',
|
|
186
|
+
dragId === team.id && 'opacity-40',
|
|
187
|
+
dragOverId === team.id && dragId !== team.id && 'border-l-2 !border-l-accent',
|
|
188
|
+
)}
|
|
189
|
+
>
|
|
190
|
+
{isActive && <div className="absolute top-0 left-0 right-0 h-px bg-accent" style={{ height: '0.5px' }} />}
|
|
191
|
+
{(() => {
|
|
192
|
+
const status = teamStatus(agents, team.id);
|
|
193
|
+
const iconColor = status === 'working' ? 'text-green-400'
|
|
194
|
+
: status === 'completed' ? 'text-green-400'
|
|
195
|
+
: status === 'crashed' ? 'text-red-400'
|
|
196
|
+
: isActive ? 'text-accent' : 'text-text-4';
|
|
197
|
+
return (
|
|
198
|
+
<span className="relative flex-shrink-0">
|
|
199
|
+
<Users size={13} className={cn(iconColor, status === 'working' && 'animate-pulse')} />
|
|
200
|
+
{status === 'working' && (
|
|
201
|
+
<span className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full bg-green-400 animate-pulse" />
|
|
202
|
+
)}
|
|
203
|
+
</span>
|
|
204
|
+
);
|
|
205
|
+
})()}
|
|
206
|
+
|
|
207
|
+
{isRenaming ? (
|
|
208
|
+
<input
|
|
209
|
+
value={renameValue}
|
|
210
|
+
onChange={(e) => setRenameValue(e.target.value)}
|
|
211
|
+
onKeyDown={(e) => { if (e.key === 'Enter') handleRename(); if (e.key === 'Escape') setRenamingId(null); }}
|
|
212
|
+
onBlur={handleRename}
|
|
213
|
+
className="h-5 w-24 px-1.5 text-xs bg-surface-0 border border-accent rounded text-text-0 font-sans focus:outline-none"
|
|
214
|
+
autoFocus
|
|
215
|
+
onClick={(e) => e.stopPropagation()}
|
|
216
|
+
/>
|
|
217
|
+
) : (
|
|
218
|
+
<span className="truncate max-w-[120px]">{team.name}</span>
|
|
248
219
|
)}
|
|
249
|
-
</div>
|
|
250
|
-
)}
|
|
251
220
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
221
|
+
{count > 0 && !isRenaming && (
|
|
222
|
+
<span className={cn(
|
|
223
|
+
'flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full text-2xs font-mono font-semibold',
|
|
224
|
+
running > 0 ? 'bg-accent/15 text-accent' : 'bg-surface-4 text-text-3',
|
|
225
|
+
)}>
|
|
226
|
+
{count}
|
|
227
|
+
</span>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
{isActive && (
|
|
231
|
+
<div className="absolute bottom-[-1px] left-0 right-0 h-px bg-[#242830]" />
|
|
232
|
+
)}
|
|
233
|
+
</div>
|
|
234
|
+
</ContextMenuTrigger>
|
|
235
|
+
<ContextMenuContent>
|
|
236
|
+
<ContextMenuItem onSelect={() => startRename(team)}>
|
|
237
|
+
<Pencil size={12} /> Rename
|
|
238
|
+
</ContextMenuItem>
|
|
239
|
+
<ContextMenuItem onSelect={() => cloneTeam(team.id)}>
|
|
240
|
+
<Copy size={12} /> Clone
|
|
241
|
+
</ContextMenuItem>
|
|
242
|
+
{!team.isDefault && (
|
|
243
|
+
<>
|
|
244
|
+
<ContextMenuSeparator />
|
|
245
|
+
<ContextMenuItem danger onSelect={() => deleteTeam(team.id)}>
|
|
246
|
+
<Trash2 size={12} /> Delete
|
|
247
|
+
</ContextMenuItem>
|
|
248
|
+
</>
|
|
249
|
+
)}
|
|
250
|
+
</ContextMenuContent>
|
|
251
|
+
</ContextMenu>
|
|
257
252
|
);
|
|
258
253
|
})}
|
|
259
254
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.20",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|