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.
Files changed (53) hide show
  1. package/CLAUDE.md +0 -11
  2. package/moe-training/client/parsers/claude-code.js +0 -2
  3. package/moe-training/client/session-attestation.js +2 -1
  4. package/moe-training/client/trajectory-capture.js +6 -0
  5. package/moe-training/test/client/parsers/claude-code.test.js +2 -2
  6. package/node_modules/@groove-dev/cli/package.json +1 -1
  7. package/node_modules/@groove-dev/daemon/package.json +1 -1
  8. package/node_modules/@groove-dev/daemon/src/api.js +20 -10
  9. package/node_modules/@groove-dev/daemon/src/conversations.js +32 -6
  10. package/node_modules/@groove-dev/daemon/src/preview.js +1 -1
  11. package/node_modules/@groove-dev/daemon/src/process.js +34 -5
  12. package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +1 -1
  13. package/node_modules/@groove-dev/daemon/src/providers/codex.js +1 -1
  14. package/node_modules/@groove-dev/daemon/src/providers/gemini.js +1 -1
  15. package/node_modules/@groove-dev/daemon/src/providers/grok.js +2 -2
  16. package/node_modules/@groove-dev/gui/dist/assets/index-D4vJ_1ET.css +1 -0
  17. package/node_modules/@groove-dev/gui/dist/assets/{index-B_igwWvq.js → index-MLIZRMj1.js} +1734 -1734
  18. package/node_modules/@groove-dev/gui/dist/index.html +2 -2
  19. package/node_modules/@groove-dev/gui/package.json +1 -1
  20. package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +51 -3
  21. package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +24 -10
  22. package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +7 -5
  23. package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +2 -4
  24. package/node_modules/@groove-dev/gui/src/components/chat/conversation-list.jsx +14 -14
  25. package/node_modules/@groove-dev/gui/src/stores/groove.js +3 -0
  26. package/node_modules/@groove-dev/gui/src/views/agents.jsx +7 -9
  27. package/package.json +1 -1
  28. package/packages/cli/package.json +1 -1
  29. package/packages/daemon/package.json +1 -1
  30. package/packages/daemon/src/api.js +20 -10
  31. package/packages/daemon/src/conversations.js +32 -6
  32. package/packages/daemon/src/preview.js +1 -1
  33. package/packages/daemon/src/process.js +34 -5
  34. package/packages/daemon/src/providers/claude-code.js +1 -1
  35. package/packages/daemon/src/providers/codex.js +1 -1
  36. package/packages/daemon/src/providers/gemini.js +1 -1
  37. package/packages/daemon/src/providers/grok.js +2 -2
  38. package/packages/gui/dist/assets/index-D4vJ_1ET.css +1 -0
  39. package/packages/gui/dist/assets/{index-B_igwWvq.js → index-MLIZRMj1.js} +1734 -1734
  40. package/packages/gui/dist/index.html +2 -2
  41. package/packages/gui/package.json +1 -1
  42. package/packages/gui/src/components/agents/agent-file-tree.jsx +51 -3
  43. package/packages/gui/src/components/agents/workspace-mode.jsx +24 -10
  44. package/packages/gui/src/components/chat/chat-messages.jsx +7 -5
  45. package/packages/gui/src/components/chat/chat-view.jsx +2 -4
  46. package/packages/gui/src/components/chat/conversation-list.jsx +14 -14
  47. package/packages/gui/src/stores/groove.js +3 -0
  48. package/packages/gui/src/views/agents.jsx +7 -9
  49. package/node_modules/@groove-dev/gui/dist/assets/index-BSqk8cbI.css +0 -1
  50. package/packages/gui/dist/assets/index-BSqk8cbI.css +0 -1
  51. package/test/doomsday-clock/index.html +0 -55
  52. package/test/doomsday-clock/script.js +0 -66
  53. 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-B_igwWvq.js"></script>
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-BSqk8cbI.css">
14
+ <link rel="stylesheet" crossorigin href="/assets/index-D4vJ_1ET.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/gui",
3
- "version": "0.27.88",
3
+ "version": "0.27.91",
4
4
  "description": "GROOVE GUI — visual agent control plane",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -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+(\S+)/i);
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
- <ScrollArea className="h-full">
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-9 bg-surface-3 border-b border-border-subtle flex-shrink-0">
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-xs font-sans cursor-pointer select-none',
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-0 border-b border-b-accent'
92
- : 'bg-surface-3 text-text-4 hover:text-text-1 hover:bg-surface-4 border-b border-b-transparent',
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={12} />
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 && diffMode && (
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 displayName = role
263
- ? `${model || 'Assistant'} ${role.charAt(0).toUpperCase() + role.slice(1)}`
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 text-text-3 font-sans mb-1 font-medium">{displayName}</div>
268
- <div className="border-l-2 border-accent/40 pl-3.5 py-1">
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(p, m, 'api');
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-accent transition-all duration-200 overflow-hidden',
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-white/60 uppercase tracking-wider font-sans">{label}</span>
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-white/15 text-white'
49
- : 'text-white/80 hover:bg-white/10 hover:text-white',
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-white' : 'text-white/50 group-hover:text-white/70')} />
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 text-white">{conv.title || 'New Chat'}</div>
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-white/70 flex-shrink-0" />
58
- : <Zap size={9} className="text-white/70 flex-shrink-0" />
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-white/50 font-sans">{timeAgo(conv.updatedAt || conv.createdAt)}</span>
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-white flex-shrink-0" />}
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-white/50 mb-3" />
129
- <p className="text-xs text-white/80 font-sans">No conversations yet</p>
130
- <p className="text-2xs text-white/60 font-sans mt-1">Start a new chat to begin</p>
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-white/15">
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-white/15 text-white text-xs font-semibold font-sans hover:bg-white/25 transition-colors cursor-pointer border border-white/20"
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(!workspaceMode)}
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 && !workspaceMode ? 'right-32' : 'right-4',
881
- workspaceMode
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
- {workspaceMode ? <><Users size={14} /> Tree</> : <><Code2 size={14} /> Workspace</>}
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"