groove-dev 0.27.88 → 0.27.91
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 -11
- package/moe-training/client/parsers/claude-code.js +0 -2
- package/moe-training/client/session-attestation.js +2 -1
- package/moe-training/client/trajectory-capture.js +6 -0
- package/moe-training/test/client/parsers/claude-code.test.js +2 -2
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +20 -10
- package/node_modules/@groove-dev/daemon/src/conversations.js +32 -6
- package/node_modules/@groove-dev/daemon/src/preview.js +1 -1
- package/node_modules/@groove-dev/daemon/src/process.js +34 -5
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +1 -1
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +1 -1
- package/node_modules/@groove-dev/daemon/src/providers/gemini.js +1 -1
- package/node_modules/@groove-dev/daemon/src/providers/grok.js +2 -2
- package/node_modules/@groove-dev/gui/dist/assets/index-D4vJ_1ET.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/{index-B_igwWvq.js → index-MLIZRMj1.js} +1734 -1734
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +51 -3
- package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +24 -10
- package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +7 -5
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +2 -4
- package/node_modules/@groove-dev/gui/src/components/chat/conversation-list.jsx +14 -14
- package/node_modules/@groove-dev/gui/src/stores/groove.js +3 -0
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +7 -9
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +20 -10
- package/packages/daemon/src/conversations.js +32 -6
- package/packages/daemon/src/preview.js +1 -1
- package/packages/daemon/src/process.js +34 -5
- package/packages/daemon/src/providers/claude-code.js +1 -1
- package/packages/daemon/src/providers/codex.js +1 -1
- package/packages/daemon/src/providers/gemini.js +1 -1
- package/packages/daemon/src/providers/grok.js +2 -2
- package/packages/gui/dist/assets/index-D4vJ_1ET.css +1 -0
- package/packages/gui/dist/assets/{index-B_igwWvq.js → index-MLIZRMj1.js} +1734 -1734
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-file-tree.jsx +51 -3
- package/packages/gui/src/components/agents/workspace-mode.jsx +24 -10
- package/packages/gui/src/components/chat/chat-messages.jsx +7 -5
- package/packages/gui/src/components/chat/chat-view.jsx +2 -4
- package/packages/gui/src/components/chat/conversation-list.jsx +14 -14
- package/packages/gui/src/stores/groove.js +3 -0
- package/packages/gui/src/views/agents.jsx +7 -9
- package/node_modules/@groove-dev/gui/dist/assets/index-BSqk8cbI.css +0 -1
- package/packages/gui/dist/assets/index-BSqk8cbI.css +0 -1
- package/test/doomsday-clock/index.html +0 -55
- package/test/doomsday-clock/script.js +0 -66
- package/test/doomsday-clock/style.css +0 -315
|
@@ -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-MLIZRMj1.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-D4vJ_1ET.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -3,7 +3,7 @@ import { useState, useEffect, useRef, useCallback } from 'react';
|
|
|
3
3
|
import { useGrooveStore } from '../../stores/groove';
|
|
4
4
|
import { cn } from '../../lib/cn';
|
|
5
5
|
import { api } from '../../lib/api';
|
|
6
|
-
import { ChevronRight, ChevronDown, File, Folder, FolderOpen, Clock } from 'lucide-react';
|
|
6
|
+
import { ChevronRight, ChevronDown, File, Folder, FolderOpen, Clock, FilePlus, FolderPlus, RefreshCw, ChevronsDownUp } from 'lucide-react';
|
|
7
7
|
import { ScrollArea } from '../ui/scroll-area';
|
|
8
8
|
|
|
9
9
|
const FILE_COLORS = {
|
|
@@ -81,6 +81,8 @@ export function AgentFileTree({ agentId }) {
|
|
|
81
81
|
const activityLog = useGrooveStore((s) => s.activityLog);
|
|
82
82
|
const openFile = useGrooveStore((s) => s.openFile);
|
|
83
83
|
const editorActiveFile = useGrooveStore((s) => s.editorActiveFile);
|
|
84
|
+
const createFile = useGrooveStore((s) => s.createFile);
|
|
85
|
+
const addToast = useGrooveStore((s) => s.addToast);
|
|
84
86
|
|
|
85
87
|
const agent = agents.find((a) => a.id === agentId);
|
|
86
88
|
const scope = agent?.scope || [];
|
|
@@ -97,7 +99,7 @@ export function AgentFileTree({ agentId }) {
|
|
|
97
99
|
for (let i = log.length - 1; i >= 0; i--) {
|
|
98
100
|
const t = (log[i].text || '').toLowerCase();
|
|
99
101
|
if (!(t.includes('writ') || t.includes('edit') || t.includes('creat') || t.includes('read'))) continue;
|
|
100
|
-
const match = log[i].text.match(/(?:Write|Edit|Create|Read|wrote|editing|writing|reading)\S*\s+(\
|
|
102
|
+
const match = log[i].text.match(/(?:Write|Edit|Create|Read|wrote|editing|writing|reading)\S*\s+([\w./-]+\.[\w]+)/i);
|
|
101
103
|
if (!match) continue;
|
|
102
104
|
const path = match[1];
|
|
103
105
|
if (seen.has(path) || path.startsWith('.') || path.includes('node_modules')) continue;
|
|
@@ -180,8 +182,53 @@ export function AgentFileTree({ agentId }) {
|
|
|
180
182
|
openFile(path);
|
|
181
183
|
}
|
|
182
184
|
|
|
185
|
+
async function handleNewFile() {
|
|
186
|
+
const name = prompt('File name:');
|
|
187
|
+
if (!name?.trim()) return;
|
|
188
|
+
await createFile?.(name.trim());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function handleNewFolder() {
|
|
192
|
+
const name = prompt('Folder name:');
|
|
193
|
+
if (!name?.trim()) return;
|
|
194
|
+
try {
|
|
195
|
+
await api.post('/files/mkdir', { path: name.trim() });
|
|
196
|
+
addToast('success', `Created ${name.trim()}/`);
|
|
197
|
+
handleRefresh();
|
|
198
|
+
} catch (err) {
|
|
199
|
+
addToast('error', 'Create folder failed', err.message);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function handleRefresh() {
|
|
204
|
+
fetchedRef.current = new Set();
|
|
205
|
+
setExpandedDirs(new Set());
|
|
206
|
+
setLoading(true);
|
|
207
|
+
fetchDir('').then((entries) => { setTreeData(entries || []); setLoading(false); });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function handleCollapseAll() {
|
|
211
|
+
setExpandedDirs(new Set());
|
|
212
|
+
}
|
|
213
|
+
|
|
183
214
|
return (
|
|
184
|
-
<
|
|
215
|
+
<div className="flex flex-col h-full">
|
|
216
|
+
<div className="flex items-center gap-0.5 px-2 py-1.5 border-b border-border-subtle flex-shrink-0">
|
|
217
|
+
<span className="flex-1 text-2xs font-semibold text-text-3 uppercase tracking-wider px-1">Files</span>
|
|
218
|
+
<button onClick={handleNewFile} className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer" title="New file">
|
|
219
|
+
<FilePlus size={12} />
|
|
220
|
+
</button>
|
|
221
|
+
<button onClick={handleNewFolder} className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer" title="New folder">
|
|
222
|
+
<FolderPlus size={12} />
|
|
223
|
+
</button>
|
|
224
|
+
<button onClick={handleRefresh} className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer" title="Refresh">
|
|
225
|
+
<RefreshCw size={12} />
|
|
226
|
+
</button>
|
|
227
|
+
<button onClick={handleCollapseAll} className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer" title="Collapse all">
|
|
228
|
+
<ChevronsDownUp size={12} />
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
<ScrollArea className="flex-1 min-h-0">
|
|
185
232
|
<div className="py-2">
|
|
186
233
|
{recentFiles.length > 0 && (
|
|
187
234
|
<div className="mb-3">
|
|
@@ -238,6 +285,7 @@ export function AgentFileTree({ agentId }) {
|
|
|
238
285
|
)}
|
|
239
286
|
</div>
|
|
240
287
|
</ScrollArea>
|
|
288
|
+
</div>
|
|
241
289
|
);
|
|
242
290
|
}
|
|
243
291
|
|
|
@@ -13,9 +13,10 @@ import { Tooltip } from '../ui/tooltip';
|
|
|
13
13
|
import { ScrollArea } from '../ui/scroll-area';
|
|
14
14
|
import { roleColor } from '../../lib/status';
|
|
15
15
|
import { fmtNum } from '../../lib/format';
|
|
16
|
+
import { MediaViewer, isMediaFile } from '../editor/media-viewer';
|
|
16
17
|
import {
|
|
17
18
|
X, Code2, MessageSquare, Activity, FileCode, GitCompareArrows,
|
|
18
|
-
ClipboardCheck, AlertTriangle, RefreshCw,
|
|
19
|
+
ClipboardCheck, AlertTriangle, RefreshCw, Users,
|
|
19
20
|
} from 'lucide-react';
|
|
20
21
|
|
|
21
22
|
const STATUS_VARIANT = {
|
|
@@ -73,7 +74,7 @@ function TabBar({ tabs, activeFile, files, onSelect, onClose, diffMode, onToggle
|
|
|
73
74
|
const hasSnapshot = activeFile && workspaceSnapshots[activeFile];
|
|
74
75
|
|
|
75
76
|
return (
|
|
76
|
-
<div className="flex items-stretch h-
|
|
77
|
+
<div className="flex items-stretch h-8 bg-surface-2 border-b border-border-subtle flex-shrink-0">
|
|
77
78
|
<div className="flex items-stretch flex-1 min-w-0 overflow-x-auto scrollbar-none">
|
|
78
79
|
{tabs.map((path) => {
|
|
79
80
|
const isActive = path === activeFile;
|
|
@@ -85,11 +86,11 @@ function TabBar({ tabs, activeFile, files, onSelect, onClose, diffMode, onToggle
|
|
|
85
86
|
<div
|
|
86
87
|
key={path}
|
|
87
88
|
className={cn(
|
|
88
|
-
'flex items-center gap-1.5 px-3 text-
|
|
89
|
+
'flex items-center gap-1.5 px-3 text-2xs font-sans cursor-pointer select-none',
|
|
89
90
|
'border-r border-white/5 transition-colors duration-75 flex-shrink-0',
|
|
90
91
|
isActive
|
|
91
|
-
? 'bg-surface-0 text-text-
|
|
92
|
-
: '
|
|
92
|
+
? 'bg-surface-0 text-text-1 border-b border-b-accent'
|
|
93
|
+
: 'text-text-4 hover:text-text-2 hover:bg-surface-3 border-b border-b-transparent',
|
|
93
94
|
)}
|
|
94
95
|
onClick={() => onSelect(path)}
|
|
95
96
|
>
|
|
@@ -99,7 +100,7 @@ function TabBar({ tabs, activeFile, files, onSelect, onClose, diffMode, onToggle
|
|
|
99
100
|
onClick={(e) => { e.stopPropagation(); onClose(path); }}
|
|
100
101
|
className="p-0.5 rounded hover:bg-surface-5 text-text-4 hover:text-text-1 transition-colors cursor-pointer ml-0.5"
|
|
101
102
|
>
|
|
102
|
-
<X size={
|
|
103
|
+
<X size={10} />
|
|
103
104
|
</button>
|
|
104
105
|
</div>
|
|
105
106
|
);
|
|
@@ -139,6 +140,7 @@ export function WorkspaceMode() {
|
|
|
139
140
|
const workspaceReviewMode = useGrooveStore((s) => s.workspaceReviewMode);
|
|
140
141
|
const toggleReviewMode = useGrooveStore((s) => s.toggleReviewMode);
|
|
141
142
|
const workspaceSnapshots = useGrooveStore((s) => s.workspaceSnapshots);
|
|
143
|
+
const setWorkspaceMode = useGrooveStore((s) => s.setWorkspaceMode);
|
|
142
144
|
|
|
143
145
|
const editorFiles = useGrooveStore((s) => s.editorFiles);
|
|
144
146
|
const editorActiveFile = useGrooveStore((s) => s.editorActiveFile);
|
|
@@ -216,6 +218,7 @@ export function WorkspaceMode() {
|
|
|
216
218
|
const ctxPct = Math.round((agent.contextUsage || 0) * 100);
|
|
217
219
|
const file = editorActiveFile ? editorFiles[editorActiveFile] : null;
|
|
218
220
|
const hasExternalChange = editorActiveFile && editorChangedFiles[editorActiveFile];
|
|
221
|
+
const isMedia = editorActiveFile && isMediaFile(editorActiveFile);
|
|
219
222
|
|
|
220
223
|
return (
|
|
221
224
|
<div className="flex h-full bg-surface-0">
|
|
@@ -235,7 +238,7 @@ export function WorkspaceMode() {
|
|
|
235
238
|
</div>
|
|
236
239
|
|
|
237
240
|
{/* Editor Area */}
|
|
238
|
-
<div className="flex-1 flex flex-col min-w-0">
|
|
241
|
+
<div className="flex-1 flex flex-col min-w-0 bg-[#1a1e25]">
|
|
239
242
|
{workspaceReviewMode ? (
|
|
240
243
|
<CodeReview agentId={agent.id} />
|
|
241
244
|
) : (
|
|
@@ -272,7 +275,7 @@ export function WorkspaceMode() {
|
|
|
272
275
|
)}
|
|
273
276
|
|
|
274
277
|
{!editorActiveFile && (
|
|
275
|
-
<div className="w-full h-full flex items-center justify-center text-text-4 font-sans">
|
|
278
|
+
<div className="w-full h-full flex items-center justify-center text-text-4 font-sans bg-[#1a1e25]">
|
|
276
279
|
<div className="text-center space-y-2">
|
|
277
280
|
<Code2 size={32} className="mx-auto" />
|
|
278
281
|
<p className="text-sm">Open a file from the tree</p>
|
|
@@ -281,11 +284,15 @@ export function WorkspaceMode() {
|
|
|
281
284
|
</div>
|
|
282
285
|
)}
|
|
283
286
|
|
|
284
|
-
{editorActiveFile &&
|
|
287
|
+
{editorActiveFile && isMedia && (
|
|
288
|
+
<MediaViewer path={editorActiveFile} />
|
|
289
|
+
)}
|
|
290
|
+
|
|
291
|
+
{editorActiveFile && diffMode && !isMedia && (
|
|
285
292
|
<DiffViewer filePath={editorActiveFile} />
|
|
286
293
|
)}
|
|
287
294
|
|
|
288
|
-
{editorActiveFile && !diffMode && file && (
|
|
295
|
+
{editorActiveFile && !diffMode && !isMedia && file && (
|
|
289
296
|
<CodeEditor
|
|
290
297
|
content={file.content}
|
|
291
298
|
language={file.language}
|
|
@@ -338,6 +345,13 @@ export function WorkspaceMode() {
|
|
|
338
345
|
>
|
|
339
346
|
<ClipboardCheck size={13} />
|
|
340
347
|
</button>
|
|
348
|
+
<button
|
|
349
|
+
onClick={() => setWorkspaceMode(false)}
|
|
350
|
+
className="flex items-center gap-1 px-2 py-1 text-xs font-sans rounded cursor-pointer transition-colors text-text-3 hover:text-text-1 hover:bg-surface-3"
|
|
351
|
+
title="Back to agent tree"
|
|
352
|
+
>
|
|
353
|
+
<Users size={13} />
|
|
354
|
+
</button>
|
|
341
355
|
</div>
|
|
342
356
|
|
|
343
357
|
{/* Tab switcher */}
|
|
@@ -259,13 +259,15 @@ function UserMessage({ msg }) {
|
|
|
259
259
|
|
|
260
260
|
function AssistantMessage({ msg, model, role }) {
|
|
261
261
|
const cleanText = stripEmojis(msg.text);
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
: (model || 'Assistant');
|
|
262
|
+
const modelName = model || 'Assistant';
|
|
263
|
+
const roleLabel = role ? role.charAt(0).toUpperCase() + role.slice(1) : null;
|
|
265
264
|
return (
|
|
266
265
|
<div className="max-w-[85%]">
|
|
267
|
-
<div className="text-2xs
|
|
268
|
-
|
|
266
|
+
<div className="text-2xs font-sans mb-1 font-medium">
|
|
267
|
+
<span className="text-text-1">{modelName}</span>
|
|
268
|
+
{roleLabel && <span className="text-text-3 ml-1">{roleLabel}</span>}
|
|
269
|
+
</div>
|
|
270
|
+
<div className="border-l-2 border-accent pl-3.5 py-1">
|
|
269
271
|
<div className="text-sm text-text-1 font-sans whitespace-pre-wrap break-words leading-relaxed">
|
|
270
272
|
<RenderedMarkdown text={cleanText} />
|
|
271
273
|
</div>
|
|
@@ -64,10 +64,8 @@ export function ChatView() {
|
|
|
64
64
|
const currentModelIsImage = activeConversation ? isImageModel(activeConversation.model) : false;
|
|
65
65
|
|
|
66
66
|
const handleNewChat = useCallback(async (provider, model) => {
|
|
67
|
-
const p = provider || 'claude-code';
|
|
68
|
-
const m = model || 'claude-sonnet-4-6';
|
|
69
67
|
try {
|
|
70
|
-
await createConversation(
|
|
68
|
+
await createConversation(provider || null, model || null, 'api');
|
|
71
69
|
} catch { /* toast handles */ }
|
|
72
70
|
}, [createConversation]);
|
|
73
71
|
|
|
@@ -134,7 +132,7 @@ export function ChatView() {
|
|
|
134
132
|
<div className="flex h-full bg-surface-0">
|
|
135
133
|
{/* Conversation sidebar */}
|
|
136
134
|
<div className={cn(
|
|
137
|
-
'flex-shrink-0 border-r border-accent/12 bg-
|
|
135
|
+
'flex-shrink-0 border-r border-accent/12 bg-surface-1 transition-all duration-200 overflow-hidden',
|
|
138
136
|
sidebarCollapsed ? 'w-0' : 'w-64',
|
|
139
137
|
)}>
|
|
140
138
|
<ConversationList onNewChat={() => handleNewChat()} />
|
|
@@ -31,7 +31,7 @@ function groupByDate(conversations) {
|
|
|
31
31
|
function GroupLabel({ label }) {
|
|
32
32
|
return (
|
|
33
33
|
<div className="px-3 pt-4 pb-1.5">
|
|
34
|
-
<span className="text-2xs font-semibold text-
|
|
34
|
+
<span className="text-2xs font-semibold text-text-4 uppercase tracking-wider font-sans">{label}</span>
|
|
35
35
|
</div>
|
|
36
36
|
);
|
|
37
37
|
}
|
|
@@ -45,23 +45,23 @@ function ConversationItem({ conv, isActive, onSelect, onRename, onPin, onDelete
|
|
|
45
45
|
className={cn(
|
|
46
46
|
'w-full flex items-center gap-2 px-3 py-2 text-left rounded-md transition-colors cursor-pointer group',
|
|
47
47
|
isActive
|
|
48
|
-
? 'bg-
|
|
49
|
-
: 'text-
|
|
48
|
+
? 'bg-accent/10 text-text-0'
|
|
49
|
+
: 'text-text-2 hover:bg-surface-4 hover:text-text-1',
|
|
50
50
|
)}
|
|
51
51
|
>
|
|
52
|
-
<MessageCircle size={13} className={cn('flex-shrink-0', isActive ? 'text-
|
|
52
|
+
<MessageCircle size={13} className={cn('flex-shrink-0', isActive ? 'text-accent' : 'text-text-4 group-hover:text-text-3')} />
|
|
53
53
|
<div className="flex-1 min-w-0">
|
|
54
|
-
<div className="text-xs font-medium font-sans truncate
|
|
54
|
+
<div className="text-xs font-medium font-sans truncate">{conv.title || 'New Chat'}</div>
|
|
55
55
|
<div className="flex items-center gap-1.5 mt-0.5">
|
|
56
56
|
{conv.mode === 'agent'
|
|
57
|
-
? <Bot size={9} className="text-
|
|
58
|
-
: <Zap size={9} className="text-
|
|
57
|
+
? <Bot size={9} className="text-purple flex-shrink-0" />
|
|
58
|
+
: <Zap size={9} className="text-accent flex-shrink-0" />
|
|
59
59
|
}
|
|
60
60
|
{conv.model && <Badge variant="default" className="text-[8px] px-1 py-0">{formatModelName(conv.model)}</Badge>}
|
|
61
|
-
<span className="text-2xs text-
|
|
61
|
+
<span className="text-2xs text-text-4 font-sans">{timeAgo(conv.updatedAt || conv.createdAt)}</span>
|
|
62
62
|
</div>
|
|
63
63
|
</div>
|
|
64
|
-
{conv.pinned && <Pin size={10} className="text-
|
|
64
|
+
{conv.pinned && <Pin size={10} className="text-accent flex-shrink-0" />}
|
|
65
65
|
</button>
|
|
66
66
|
</ContextMenuTrigger>
|
|
67
67
|
<ContextMenuContent>
|
|
@@ -125,9 +125,9 @@ export function ConversationList({ onNewChat }) {
|
|
|
125
125
|
<div className="flex-1 overflow-y-auto px-1.5 pt-3 pb-3 space-y-0.5">
|
|
126
126
|
{conversations.length === 0 ? (
|
|
127
127
|
<div className="flex flex-col items-center justify-center py-16 text-center px-4">
|
|
128
|
-
<MessageCircle size={24} className="text-
|
|
129
|
-
<p className="text-xs text-
|
|
130
|
-
<p className="text-2xs text-
|
|
128
|
+
<MessageCircle size={24} className="text-text-4 mb-3" />
|
|
129
|
+
<p className="text-xs text-text-3 font-sans">No conversations yet</p>
|
|
130
|
+
<p className="text-2xs text-text-4 font-sans mt-1">Start a new chat to begin</p>
|
|
131
131
|
</div>
|
|
132
132
|
) : (
|
|
133
133
|
<>
|
|
@@ -140,10 +140,10 @@ export function ConversationList({ onNewChat }) {
|
|
|
140
140
|
)}
|
|
141
141
|
</div>
|
|
142
142
|
|
|
143
|
-
<div className="p-3 border-t border-
|
|
143
|
+
<div className="p-3 border-t border-border-subtle">
|
|
144
144
|
<button
|
|
145
145
|
onClick={onNewChat}
|
|
146
|
-
className="w-full flex items-center justify-center gap-2 h-9 rounded-lg bg-
|
|
146
|
+
className="w-full flex items-center justify-center gap-2 h-9 rounded-lg bg-accent/15 text-accent text-xs font-semibold font-sans hover:bg-accent/25 transition-colors cursor-pointer border border-accent/20"
|
|
147
147
|
>
|
|
148
148
|
<Plus size={14} />
|
|
149
149
|
New Chat
|
|
@@ -2363,6 +2363,9 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
2363
2363
|
setWorkspaceMode(on) {
|
|
2364
2364
|
set({ workspaceMode: on });
|
|
2365
2365
|
localStorage.setItem('groove:workspaceMode', String(on));
|
|
2366
|
+
if (on) {
|
|
2367
|
+
get().closeDetail();
|
|
2368
|
+
}
|
|
2366
2369
|
if (on && !get().workspaceAgentId) {
|
|
2367
2370
|
const teamAgents = get().agents.filter((a) => a.teamId === get().activeTeamId);
|
|
2368
2371
|
const selected = get().detailPanel?.type === 'agent' ? get().detailPanel.agentId : null;
|
|
@@ -854,7 +854,7 @@ export default function AgentsView() {
|
|
|
854
854
|
<EmptyState onPlanner={launchPlanner} onSpawn={() => openDetail({ type: 'spawn' })} />
|
|
855
855
|
) : workspaceMode ? (
|
|
856
856
|
<WorkspaceMode />
|
|
857
|
-
) : showPreviewInAgents && previewState.url ? (
|
|
857
|
+
) : showPreviewInAgents && previewState.url && previewState.teamId === activeTeamId ? (
|
|
858
858
|
<PreviewWorkspace embedded />
|
|
859
859
|
) : (
|
|
860
860
|
<ReactFlowProvider key={activeTeamId}>
|
|
@@ -872,21 +872,19 @@ export default function AgentsView() {
|
|
|
872
872
|
Spawn
|
|
873
873
|
</button>
|
|
874
874
|
)}
|
|
875
|
-
{!isLoading && teamAgents.length > 0 && (
|
|
875
|
+
{!isLoading && teamAgents.length > 0 && !workspaceMode && (
|
|
876
876
|
<button
|
|
877
|
-
onClick={() => setWorkspaceMode(
|
|
877
|
+
onClick={() => setWorkspaceMode(true)}
|
|
878
878
|
className={cn(
|
|
879
879
|
'absolute bottom-4 z-40 flex items-center gap-1.5 h-8 px-4 rounded-md text-xs font-semibold font-sans transition-colors cursor-pointer select-none shadow-lg shadow-black/10',
|
|
880
|
-
previewState.url &&
|
|
881
|
-
|
|
882
|
-
? 'bg-accent/15 text-accent hover:bg-accent/25'
|
|
883
|
-
: 'bg-purple/15 text-purple hover:bg-purple/25',
|
|
880
|
+
previewState.url && previewState.teamId === activeTeamId ? 'right-32' : 'right-4',
|
|
881
|
+
'bg-purple/15 text-purple hover:bg-purple/25',
|
|
884
882
|
)}
|
|
885
883
|
>
|
|
886
|
-
|
|
884
|
+
<Code2 size={14} /> Workspace
|
|
887
885
|
</button>
|
|
888
886
|
)}
|
|
889
|
-
{!isLoading && teamAgents.length > 0 && !workspaceMode && previewState.url && (
|
|
887
|
+
{!isLoading && teamAgents.length > 0 && !workspaceMode && previewState.url && previewState.teamId === activeTeamId && (
|
|
890
888
|
<button
|
|
891
889
|
onClick={togglePreviewInAgents}
|
|
892
890
|
className="absolute bottom-4 right-4 z-40 flex items-center gap-1.5 h-8 px-4 rounded-md bg-info/15 text-info text-xs font-semibold font-sans hover:bg-info/25 transition-colors cursor-pointer select-none shadow-lg shadow-black/10"
|