groove-dev 0.27.164 → 0.27.166

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.
@@ -6,7 +6,7 @@
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-CkCFf4Fl.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-B_kpnfOu.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-26L3JoZv.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/reactflow-DoBZjiHE.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/codemirror-BYKpdS2W.js">
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groove-dev/gui",
3
- "version": "0.27.164",
3
+ "version": "0.27.166",
4
4
  "description": "GROOVE GUI — visual agent control plane",
5
5
  "license": "FSL-1.1-Apache-2.0",
6
6
  "type": "module",
@@ -85,6 +85,7 @@ function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
85
85
  }
86
86
 
87
87
  const requestId = `spawn-${++spawnSeq}`;
88
+ const initialCols = term.cols;
88
89
  ws.send(JSON.stringify({ type: 'terminal:spawn', cols: term.cols, rows: term.rows, requestId }));
89
90
 
90
91
  function onMessage(event) {
@@ -93,6 +94,25 @@ function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
93
94
 
94
95
  if (msg.type === 'terminal:spawned' && msg.requestId === requestId && !termIdRef.current) {
95
96
  termIdRef.current = msg.id;
97
+ setTimeout(() => {
98
+ try { fitRef.current?.fit(); } catch {}
99
+ const c = term.cols, r = term.rows;
100
+ if (c > 1 && r > 1 && termIdRef.current) {
101
+ const w = useGrooveStore.getState().ws;
102
+ if (w?.readyState === WebSocket.OPEN) {
103
+ w.send(JSON.stringify({ type: 'terminal:resize', id: termIdRef.current, rows: r, cols: c }));
104
+ lastSizeRef.current = { cols: c, rows: r };
105
+ if (c !== initialCols) {
106
+ setTimeout(() => {
107
+ term.clear();
108
+ if (w.readyState === WebSocket.OPEN && termIdRef.current) {
109
+ w.send(JSON.stringify({ type: 'terminal:input', id: termIdRef.current, data: '\x0c' }));
110
+ }
111
+ }, 80);
112
+ }
113
+ }
114
+ }
115
+ }, 50);
96
116
  } else if (msg.type === 'terminal:output' && msg.id === termIdRef.current) {
97
117
  term.write(msg.data);
98
118
  } else if (msg.type === 'terminal:exit' && msg.id === termIdRef.current) {
@@ -114,26 +134,50 @@ function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
114
134
  term.onResize(({ cols, rows }) => {
115
135
  if (cols === lastSizeRef.current.cols && rows === lastSizeRef.current.rows) return;
116
136
  if (cols < 2 || rows < 2) return;
117
- lastSizeRef.current = { cols, rows };
118
137
  const ws = useGrooveStore.getState().ws;
119
138
  if (ws?.readyState === WebSocket.OPEN && termIdRef.current) {
139
+ lastSizeRef.current = { cols, rows };
120
140
  ws.send(JSON.stringify({ type: 'terminal:resize', id: termIdRef.current, rows, cols }));
121
141
  }
122
142
  });
123
143
  }
124
144
 
145
+ let hasSpawned = false;
146
+ function tryInitSpawn() {
147
+ if (hasSpawned) return;
148
+ if (term.cols >= 10 && term.rows >= 2) {
149
+ hasSpawned = true;
150
+ trySpawn();
151
+ }
152
+ }
153
+
125
154
  requestAnimationFrame(() => {
126
155
  try { fitAddon.fit(); } catch {}
127
- trySpawn();
156
+ requestAnimationFrame(() => {
157
+ try { fitAddon.fit(); } catch {}
158
+ tryInitSpawn();
159
+ });
128
160
  });
129
161
 
162
+ const spawnFallback = setTimeout(() => {
163
+ if (!hasSpawned) {
164
+ try { fitAddon.fit(); } catch {}
165
+ hasSpawned = true;
166
+ trySpawn();
167
+ }
168
+ }, 3000);
169
+
130
170
  const observer = new ResizeObserver(() => {
131
171
  if (!visibleRef.current) return;
132
- requestAnimationFrame(() => { try { fitAddon.fit(); } catch {} });
172
+ requestAnimationFrame(() => {
173
+ try { fitAddon.fit(); } catch {}
174
+ tryInitSpawn();
175
+ });
133
176
  });
134
177
  observer.observe(containerRef.current);
135
178
 
136
179
  return () => {
180
+ clearTimeout(spawnFallback);
137
181
  observer.disconnect();
138
182
  if (handlerRef.current) {
139
183
  handlerRef.current.ws.removeEventListener('message', handlerRef.current.handler);
@@ -149,6 +193,16 @@ function TerminalInstance({ tabId, visible, registerKill, onSelectionChange }) {
149
193
  if (visible && fitRef.current) {
150
194
  requestAnimationFrame(() => {
151
195
  try { fitRef.current.fit(); } catch {}
196
+ if (termRef.current && termIdRef.current) {
197
+ const c = termRef.current.cols, r = termRef.current.rows;
198
+ if (c > 1 && r > 1) {
199
+ const ws = useGrooveStore.getState().ws;
200
+ if (ws?.readyState === WebSocket.OPEN) {
201
+ ws.send(JSON.stringify({ type: 'terminal:resize', id: termIdRef.current, rows: r, cols: c }));
202
+ lastSizeRef.current = { cols: c, rows: r };
203
+ }
204
+ }
205
+ }
152
206
  });
153
207
  }
154
208
  }, [visible]);
@@ -5,7 +5,10 @@ import { useGrooveStore } from '../../stores/groove';
5
5
  import { FleetPane } from './fleet-pane';
6
6
 
7
7
  export function FleetContent() {
8
- const selected = useGrooveStore((s) => s.fleetSelectedAgents);
8
+ const rawSelected = useGrooveStore((s) => s.fleetSelectedAgents);
9
+ const lastSelectedRef = useRef(rawSelected);
10
+ if (rawSelected[0] || rawSelected[1]) lastSelectedRef.current = rawSelected;
11
+ const selected = (rawSelected[0] || rawSelected[1]) ? rawSelected : lastSelectedRef.current;
9
12
  const splitMode = useGrooveStore((s) => s.fleetSplitMode);
10
13
  const fleetSelectAgent = useGrooveStore((s) => s.fleetSelectAgent);
11
14
 
@@ -28,7 +28,12 @@ const STATUS_LABEL = {
28
28
  };
29
29
 
30
30
  export function FleetPane({ agentId, paneIndex }) {
31
- const liveAgent = useGrooveStore((s) => s.agents.find((a) => a.id === agentId));
31
+ const effectiveId = agentId || null;
32
+ const lastIdRef = useRef(effectiveId);
33
+ if (effectiveId) lastIdRef.current = effectiveId;
34
+ const resolvedId = effectiveId || lastIdRef.current;
35
+
36
+ const liveAgent = useGrooveStore((s) => resolvedId ? s.agents.find((a) => a.id === resolvedId) : null);
32
37
  const fleetSelectAgent = useGrooveStore((s) => s.fleetSelectAgent);
33
38
  const fleetMarkRead = useGrooveStore((s) => s.fleetMarkRead);
34
39
 
@@ -43,15 +48,15 @@ export function FleetPane({ agentId, paneIndex }) {
43
48
  }
44
49
 
45
50
  useEffect(() => {
46
- if (!liveAgent && agentId && !gone) {
47
- goneTimer.current = setTimeout(() => setGone(true), 2000);
51
+ if (!liveAgent && resolvedId && !gone) {
52
+ goneTimer.current = setTimeout(() => setGone(true), 3000);
48
53
  return () => { if (goneTimer.current) clearTimeout(goneTimer.current); };
49
54
  }
50
- }, [liveAgent, agentId, gone]);
55
+ }, [liveAgent, resolvedId, gone]);
51
56
 
52
57
  useEffect(() => {
53
- if (agentId) fleetMarkRead(agentId);
54
- }, [agentId, fleetMarkRead]);
58
+ if (effectiveId) fleetMarkRead(effectiveId);
59
+ }, [effectiveId, fleetMarkRead]);
55
60
 
56
61
  const agent = liveAgent || lastAgentRef.current;
57
62
 
@@ -199,8 +199,10 @@ export const useGrooveStore = create((set, get) => ({
199
199
  }
200
200
  for (const id of removed) delete timeline[id];
201
201
  const updates = { agents, tokenTimeline: timeline, hydrated: true };
202
- if (removed.length > 0 && st.detailPanel?.type === 'agent' && removed.includes(st.detailPanel.agentId)) {
203
- updates.detailPanel = null;
202
+ if (removed.length > 0) {
203
+ if (st.detailPanel?.type === 'agent' && removed.includes(st.detailPanel.agentId)) {
204
+ updates.detailPanel = null;
205
+ }
204
206
  }
205
207
  set(updates);
206
208
  break;
@@ -527,8 +529,15 @@ export const useGrooveStore = create((set, get) => ({
527
529
  const tid = get().activeTeamId;
528
530
  teamDetailPanels = { ...s.teamDetailPanels, [tid]: newPanel };
529
531
  }
532
+ let fleetSelectedAgents = s.fleetSelectedAgents;
533
+ if (fleetSelectedAgents[0] === oldId || fleetSelectedAgents[1] === oldId) {
534
+ fleetSelectedAgents = [
535
+ fleetSelectedAgents[0] === oldId ? newId : fleetSelectedAgents[0],
536
+ fleetSelectedAgents[1] === oldId ? newId : fleetSelectedAgents[1],
537
+ ];
538
+ }
530
539
  try { localStorage.setItem('groove:chatHistory', JSON.stringify(chatHistory)); } catch {}
531
- return { chatHistory, tokenTimeline, activityLog, chatInputs, detailPanel, teamDetailPanels };
540
+ return { chatHistory, tokenTimeline, activityLog, chatInputs, detailPanel, teamDetailPanels, fleetSelectedAgents };
532
541
  });
533
542
  break;
534
543
  }