groove-dev 0.27.144 → 0.27.146
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 +7 -0
- 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 +12 -6
- package/node_modules/@groove-dev/daemon/src/conversations.js +59 -58
- package/node_modules/@groove-dev/daemon/src/introducer.js +20 -0
- package/node_modules/@groove-dev/daemon/src/process.js +262 -15
- package/node_modules/@groove-dev/daemon/src/providers/groove-network.js +1 -3
- package/node_modules/@groove-dev/daemon/src/rotator.js +15 -3
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +49 -83
- package/node_modules/@groove-dev/daemon/templates/lab-general.json +12 -0
- package/node_modules/@groove-dev/daemon/templates/llama-cpp-setup.json +12 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BKbsE_hn.js +1011 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CEkPsSAm.css +1 -0
- 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-feed.jsx +80 -95
- package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +2 -70
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +132 -4
- package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +3 -8
- package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +199 -75
- package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +21 -4
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +10 -13
- package/node_modules/@groove-dev/gui/src/components/chat/model-picker.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +42 -34
- package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +9 -3
- package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +13 -3
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +66 -65
- package/node_modules/@groove-dev/gui/src/components/lab/preset-manager.jsx +17 -14
- package/node_modules/@groove-dev/gui/src/components/lab/runtime-config.jsx +124 -127
- package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +10 -8
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +24 -1
- package/node_modules/@groove-dev/gui/src/components/ui/question-modal.jsx +107 -0
- package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +8 -8
- package/node_modules/@groove-dev/gui/src/lib/status.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +49 -2
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +18 -2
- package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +14 -14
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +68 -32
- package/node_modules/@groove-dev/gui/src/views/models.jsx +57 -36
- package/node_modules/axios/CHANGELOG.md +260 -0
- package/node_modules/axios/README.md +595 -223
- package/node_modules/axios/dist/axios.js +1460 -1090
- package/node_modules/axios/dist/axios.js.map +1 -1
- package/node_modules/axios/dist/axios.min.js +3 -3
- package/node_modules/axios/dist/axios.min.js.map +1 -1
- package/node_modules/axios/dist/browser/axios.cjs +1560 -1132
- package/node_modules/axios/dist/browser/axios.cjs.map +1 -1
- package/node_modules/axios/dist/esm/axios.js +1557 -1128
- package/node_modules/axios/dist/esm/axios.js.map +1 -1
- package/node_modules/axios/dist/esm/axios.min.js +2 -2
- package/node_modules/axios/dist/esm/axios.min.js.map +1 -1
- package/node_modules/axios/dist/node/axios.cjs +1594 -1057
- package/node_modules/axios/dist/node/axios.cjs.map +1 -1
- package/node_modules/axios/index.d.cts +40 -41
- package/node_modules/axios/index.d.ts +151 -227
- package/node_modules/axios/index.js +2 -0
- package/node_modules/axios/lib/adapters/adapters.js +4 -2
- package/node_modules/axios/lib/adapters/fetch.js +147 -16
- package/node_modules/axios/lib/adapters/http.js +306 -58
- package/node_modules/axios/lib/adapters/xhr.js +6 -2
- package/node_modules/axios/lib/core/Axios.js +7 -3
- package/node_modules/axios/lib/core/AxiosError.js +120 -34
- package/node_modules/axios/lib/core/AxiosHeaders.js +27 -25
- package/node_modules/axios/lib/core/buildFullPath.js +1 -1
- package/node_modules/axios/lib/core/dispatchRequest.js +19 -7
- package/node_modules/axios/lib/core/mergeConfig.js +21 -4
- package/node_modules/axios/lib/core/settle.js +7 -11
- package/node_modules/axios/lib/defaults/index.js +14 -9
- package/node_modules/axios/lib/env/data.js +1 -1
- package/node_modules/axios/lib/helpers/AxiosURLSearchParams.js +1 -2
- package/node_modules/axios/lib/helpers/buildURL.js +1 -1
- package/node_modules/axios/lib/helpers/cookies.js +14 -2
- package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +28 -1
- package/node_modules/axios/lib/helpers/formDataToJSON.js +3 -1
- package/node_modules/axios/lib/helpers/formDataToStream.js +3 -2
- package/node_modules/axios/lib/helpers/parseProtocol.js +1 -1
- package/node_modules/axios/lib/helpers/progressEventReducer.js +5 -5
- package/node_modules/axios/lib/helpers/resolveConfig.js +54 -18
- package/node_modules/axios/lib/helpers/shouldBypassProxy.js +74 -2
- package/node_modules/axios/lib/helpers/toFormData.js +10 -2
- package/node_modules/axios/lib/helpers/validator.js +3 -1
- package/node_modules/axios/lib/utils.js +33 -21
- package/node_modules/axios/package.json +17 -24
- package/node_modules/follow-redirects/README.md +7 -5
- package/node_modules/follow-redirects/index.js +24 -1
- package/node_modules/follow-redirects/package.json +1 -1
- 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 +12 -6
- package/packages/daemon/src/conversations.js +59 -58
- package/packages/daemon/src/introducer.js +20 -0
- package/packages/daemon/src/process.js +262 -15
- package/packages/daemon/src/providers/groove-network.js +1 -3
- package/packages/daemon/src/rotator.js +15 -3
- package/packages/daemon/src/routes/agents.js +49 -83
- package/packages/daemon/templates/lab-general.json +12 -0
- package/packages/daemon/templates/llama-cpp-setup.json +12 -0
- package/packages/gui/dist/assets/index-BKbsE_hn.js +1011 -0
- package/packages/gui/dist/assets/index-CEkPsSAm.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +80 -95
- package/packages/gui/src/components/agents/agent-panel.jsx +2 -70
- package/packages/gui/src/components/agents/spawn-wizard.jsx +132 -4
- package/packages/gui/src/components/chat/chat-header.jsx +3 -8
- package/packages/gui/src/components/chat/chat-input.jsx +199 -75
- package/packages/gui/src/components/chat/chat-messages.jsx +21 -4
- package/packages/gui/src/components/chat/chat-view.jsx +10 -13
- package/packages/gui/src/components/chat/model-picker.jsx +3 -3
- package/packages/gui/src/components/lab/chat-playground.jsx +42 -34
- package/packages/gui/src/components/lab/lab-assistant.jsx +9 -3
- package/packages/gui/src/components/lab/metrics-panel.jsx +13 -3
- package/packages/gui/src/components/lab/parameter-panel.jsx +66 -65
- package/packages/gui/src/components/lab/preset-manager.jsx +17 -14
- package/packages/gui/src/components/lab/runtime-config.jsx +124 -127
- package/packages/gui/src/components/lab/system-prompt-editor.jsx +10 -8
- package/packages/gui/src/components/layout/app-shell.jsx +2 -0
- package/packages/gui/src/components/layout/status-bar.jsx +24 -1
- package/packages/gui/src/components/ui/question-modal.jsx +107 -0
- package/packages/gui/src/components/ui/sheet.jsx +2 -2
- package/packages/gui/src/components/ui/slider.jsx +8 -8
- package/packages/gui/src/lib/status.js +1 -0
- package/packages/gui/src/stores/groove.js +49 -2
- package/packages/gui/src/stores/slices/agents-slice.js +18 -2
- package/packages/gui/src/stores/slices/chat-slice.js +14 -14
- package/packages/gui/src/views/model-lab.jsx +68 -32
- package/packages/gui/src/views/models.jsx +57 -36
- package/node_modules/@groove-dev/gui/dist/assets/index-BcoF6_eF.js +0 -1012
- package/node_modules/@groove-dev/gui/dist/assets/index-Dd7qhiEd.css +0 -1
- package/packages/gui/dist/assets/index-BcoF6_eF.js +0 -1012
- package/packages/gui/dist/assets/index-Dd7qhiEd.css +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
2
|
import { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { useGrooveStore } from '../../stores/groove';
|
|
4
|
+
import { SidebarSection } from '../../views/model-lab';
|
|
4
5
|
import { Button } from '../ui/button';
|
|
5
6
|
import { Badge } from '../ui/badge';
|
|
6
7
|
import { Input } from '../ui/input';
|
|
@@ -95,8 +96,8 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
95
96
|
<button
|
|
96
97
|
onClick={() => onSelect(runtime.id)}
|
|
97
98
|
className={cn(
|
|
98
|
-
'w-full flex items-center gap-2.5 px-2.5 py-2 text-left transition-colors cursor-pointer rounded
|
|
99
|
-
active ? 'bg-accent/8
|
|
99
|
+
'w-full flex items-center gap-2.5 px-2.5 py-2 text-left transition-colors cursor-pointer rounded',
|
|
100
|
+
active ? 'bg-accent/8 ring-1 ring-accent/20' : 'hover:bg-surface-2',
|
|
100
101
|
)}
|
|
101
102
|
>
|
|
102
103
|
<span className={cn(
|
|
@@ -104,10 +105,10 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
104
105
|
online ? 'bg-success' : runtime.status === 'error' ? 'bg-danger' : 'bg-text-4',
|
|
105
106
|
)} />
|
|
106
107
|
<div className="flex-1 min-w-0">
|
|
107
|
-
<div className={cn('text-
|
|
108
|
+
<div className={cn('text-[11px] font-sans font-medium truncate', active ? 'text-text-0' : 'text-text-2')}>
|
|
108
109
|
{RUNTIME_TYPES.find((t) => t.value === runtime.type)?.label || runtime.type}
|
|
109
110
|
</div>
|
|
110
|
-
<div className="text-
|
|
111
|
+
<div className="text-[10px] text-text-4 flex items-center gap-1.5">
|
|
111
112
|
<span className={cn('font-sans', online ? 'text-success' : 'text-danger')}>
|
|
112
113
|
{online ? 'Online' : 'Offline'}
|
|
113
114
|
</span>
|
|
@@ -116,7 +117,7 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
116
117
|
)}
|
|
117
118
|
</div>
|
|
118
119
|
</div>
|
|
119
|
-
<div className="flex items-center gap-
|
|
120
|
+
<div className="flex items-center gap-px flex-shrink-0">
|
|
120
121
|
{managed && online && (
|
|
121
122
|
<Tooltip content="Stop server">
|
|
122
123
|
<button
|
|
@@ -142,7 +143,7 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
142
143
|
onClick={(e) => { e.stopPropagation(); onTest(runtime.id); }}
|
|
143
144
|
className="p-1 text-text-4 hover:text-accent transition-colors cursor-pointer"
|
|
144
145
|
>
|
|
145
|
-
{testing === runtime.id ? <Loader2 size={
|
|
146
|
+
{testing === runtime.id ? <Loader2 size={10} className="animate-spin" /> : <RotateCcw size={10} />}
|
|
146
147
|
</button>
|
|
147
148
|
</Tooltip>
|
|
148
149
|
<Tooltip content="Remove runtime">
|
|
@@ -150,7 +151,7 @@ function RuntimeItem({ runtime, active, onSelect, onTest, onRemove, onStop, onSt
|
|
|
150
151
|
onClick={(e) => { e.stopPropagation(); onRemove(runtime.id); }}
|
|
151
152
|
className="p-1 text-text-4 hover:text-danger transition-colors cursor-pointer"
|
|
152
153
|
>
|
|
153
|
-
<Trash2 size={
|
|
154
|
+
<Trash2 size={10} />
|
|
154
155
|
</button>
|
|
155
156
|
</Tooltip>
|
|
156
157
|
</div>
|
|
@@ -172,23 +173,30 @@ const BACKENDS = [
|
|
|
172
173
|
{ id: 'tgi', label: 'TGI', subtitle: 'HuggingFace, guided setup', autoLaunch: false },
|
|
173
174
|
];
|
|
174
175
|
|
|
175
|
-
function
|
|
176
|
-
|
|
176
|
+
function StatusBanner({ variant, icon: Icon, children }) {
|
|
177
|
+
const styles = {
|
|
178
|
+
success: 'bg-success/8 text-success',
|
|
179
|
+
danger: 'bg-danger/8 text-danger',
|
|
180
|
+
accent: 'bg-accent/8 text-accent',
|
|
181
|
+
warning: 'bg-warning/8 text-warning',
|
|
182
|
+
};
|
|
177
183
|
return (
|
|
178
|
-
<div className={cn(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
phase === 'error' && 'bg-danger/8 text-danger',
|
|
182
|
-
(phase === 'starting' || phase === 'checking') && 'bg-accent/8 text-accent',
|
|
183
|
-
)}>
|
|
184
|
-
{phase === 'starting' && <><Loader2 size={11} className="animate-spin" /> Starting server...</>}
|
|
185
|
-
{phase === 'checking' && <><Loader2 size={11} className="animate-spin" /> Checking...</>}
|
|
186
|
-
{phase === 'ready' && <><CheckCircle size={11} /> Server ready</>}
|
|
187
|
-
{phase === 'error' && <><AlertTriangle size={11} /> {error || 'Launch failed'}</>}
|
|
184
|
+
<div className={cn('flex items-center gap-2 px-2.5 py-2 text-[11px] font-sans rounded', styles[variant])}>
|
|
185
|
+
<Icon size={11} className={variant === 'accent' ? 'animate-spin' : ''} />
|
|
186
|
+
<span>{children}</span>
|
|
188
187
|
</div>
|
|
189
188
|
);
|
|
190
189
|
}
|
|
191
190
|
|
|
191
|
+
function LaunchStatus({ phase, error }) {
|
|
192
|
+
if (!phase) return null;
|
|
193
|
+
if (phase === 'starting') return <StatusBanner variant="accent" icon={Loader2}>Starting server...</StatusBanner>;
|
|
194
|
+
if (phase === 'checking') return <StatusBanner variant="accent" icon={Loader2}>Checking...</StatusBanner>;
|
|
195
|
+
if (phase === 'ready') return <StatusBanner variant="success" icon={CheckCircle}>Server ready</StatusBanner>;
|
|
196
|
+
if (phase === 'error') return <StatusBanner variant="danger" icon={AlertTriangle}>{error || 'Launch failed'}</StatusBanner>;
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
192
200
|
function getIncompatibilityReason(modelType, backendId) {
|
|
193
201
|
if (modelType === 'gguf' && backendId === 'mlx') return 'GGUF model — MLX needs MLX-format weights';
|
|
194
202
|
if (modelType === 'gguf' && (backendId === 'vllm' || backendId === 'tgi')) return 'GGUF model — needs standard HuggingFace weights';
|
|
@@ -289,22 +297,20 @@ export function LaunchModel() {
|
|
|
289
297
|
}
|
|
290
298
|
|
|
291
299
|
return (
|
|
292
|
-
<
|
|
293
|
-
<span className="text-2xs font-semibold font-sans text-text-3 uppercase tracking-wider">Launch Model</span>
|
|
294
|
-
|
|
300
|
+
<SidebarSection label="Launch Model">
|
|
295
301
|
{localModels.length === 0 ? (
|
|
296
|
-
<div className="py-
|
|
297
|
-
<HardDrive size={
|
|
298
|
-
<p className="text-
|
|
299
|
-
<p className="text-
|
|
302
|
+
<div className="py-6 text-center rounded-md bg-surface-1/50 border border-border-subtle">
|
|
303
|
+
<HardDrive size={16} className="mx-auto text-text-4 mb-2" />
|
|
304
|
+
<p className="text-[11px] text-text-3 font-sans">No downloaded models</p>
|
|
305
|
+
<p className="text-[10px] text-text-4 font-sans mt-0.5">Download models from the Models tab</p>
|
|
300
306
|
</div>
|
|
301
307
|
) : (
|
|
302
|
-
|
|
308
|
+
<div className="space-y-4">
|
|
303
309
|
<div className="relative">
|
|
304
310
|
<select
|
|
305
311
|
value={selectedModel || ''}
|
|
306
312
|
onChange={handleModelChange}
|
|
307
|
-
className="w-full h-
|
|
313
|
+
className="w-full h-9 px-2.5 pr-7 text-[11px] rounded bg-surface-1 border border-border text-text-0 font-sans appearance-none cursor-pointer focus:outline-none focus:ring-1 focus:ring-accent/50 focus:border-accent/50 transition-colors"
|
|
308
314
|
>
|
|
309
315
|
<option value="">Select a model</option>
|
|
310
316
|
{localModels.map((m) => {
|
|
@@ -318,75 +324,73 @@ export function LaunchModel() {
|
|
|
318
324
|
);
|
|
319
325
|
})}
|
|
320
326
|
</select>
|
|
321
|
-
<ChevronRight size={
|
|
327
|
+
<ChevronRight size={12} className="absolute right-2.5 top-1/2 -translate-y-1/2 text-text-4 pointer-events-none rotate-90" />
|
|
322
328
|
</div>
|
|
323
329
|
|
|
324
330
|
{selectedModel && (
|
|
325
|
-
<div className="space-y-
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
<
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
<div className="flex
|
|
344
|
-
<
|
|
345
|
-
{b.
|
|
346
|
-
|
|
347
|
-
|
|
331
|
+
<div className="space-y-4">
|
|
332
|
+
<div className="space-y-2">
|
|
333
|
+
<span className="text-[10px] font-semibold font-sans text-text-4 uppercase tracking-widest">Backend</span>
|
|
334
|
+
<div className="space-y-1 rounded-md bg-surface-1/50 border border-border-subtle p-2">
|
|
335
|
+
{backendsWithCompat.map((b) => (
|
|
336
|
+
<Tooltip key={b.id} content={b.reason} side="right">
|
|
337
|
+
<button
|
|
338
|
+
onClick={() => setSelectedBackend(b.id)}
|
|
339
|
+
className={cn(
|
|
340
|
+
'w-full flex items-center gap-2.5 px-2.5 py-2 text-left transition-colors cursor-pointer rounded',
|
|
341
|
+
selectedBackend === b.id ? 'bg-accent/10' : 'hover:bg-surface-3',
|
|
342
|
+
!b.compatible && 'opacity-40',
|
|
343
|
+
)}
|
|
344
|
+
>
|
|
345
|
+
<span className={cn(
|
|
346
|
+
'w-2 h-2 rounded-full border-[1.5px] flex-shrink-0 transition-colors',
|
|
347
|
+
selectedBackend === b.id ? 'border-accent bg-accent' : 'border-text-4',
|
|
348
|
+
)} />
|
|
349
|
+
<div className="flex-1 min-w-0">
|
|
350
|
+
<div className="flex items-center gap-1.5">
|
|
351
|
+
<span className={cn('text-[11px] font-sans font-medium', selectedBackend === b.id ? 'text-text-0' : 'text-text-2')}>
|
|
352
|
+
{b.label}
|
|
353
|
+
</span>
|
|
354
|
+
{b.compatible && b.recommended && <Badge variant="success" className="text-[9px]">Recommended</Badge>}
|
|
355
|
+
</div>
|
|
356
|
+
<div className="text-[10px] text-text-4 font-sans">{b.subtitle}</div>
|
|
348
357
|
</div>
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
))}
|
|
358
|
+
</button>
|
|
359
|
+
</Tooltip>
|
|
360
|
+
))}
|
|
361
|
+
</div>
|
|
354
362
|
</div>
|
|
355
363
|
|
|
356
364
|
{!isCompatible && (
|
|
357
|
-
<
|
|
358
|
-
|
|
359
|
-
{currentBackend?.reason}
|
|
360
|
-
</p>
|
|
365
|
+
<StatusBanner variant="warning" icon={AlertTriangle}>
|
|
366
|
+
{currentBackend?.reason}
|
|
361
367
|
{suggestion && (
|
|
362
|
-
<
|
|
363
|
-
Try <span className="font-mono font-medium">{suggestion.repoId}</span> instead
|
|
364
|
-
</p>
|
|
368
|
+
<> — try <span className="font-mono font-medium">{suggestion.repoId}</span></>
|
|
365
369
|
)}
|
|
366
|
-
</
|
|
370
|
+
</StatusBanner>
|
|
367
371
|
)}
|
|
368
372
|
|
|
369
373
|
{isCompatible && selectedBackend === 'llama-cpp' && (
|
|
370
|
-
<div
|
|
374
|
+
<div>
|
|
371
375
|
{llamaInstalled === null && (
|
|
372
|
-
<div className="flex items-center gap-2 text-
|
|
376
|
+
<div className="flex items-center gap-2 text-[11px] text-text-3 font-sans">
|
|
373
377
|
<Loader2 size={10} className="animate-spin" /> Checking llama-server...
|
|
374
378
|
</div>
|
|
375
379
|
)}
|
|
376
380
|
{llamaInstalled === true && (
|
|
377
|
-
<div className="flex items-center gap-2 text-
|
|
381
|
+
<div className="flex items-center gap-2 text-[11px] text-success font-sans">
|
|
378
382
|
<CheckCircle size={10} /> llama-server found
|
|
379
383
|
</div>
|
|
380
384
|
)}
|
|
381
385
|
{llamaInstalled === false && (
|
|
382
|
-
<div className="space-y-
|
|
383
|
-
<div className="flex items-center gap-2 text-
|
|
386
|
+
<div className="space-y-2">
|
|
387
|
+
<div className="flex items-center gap-2 text-[11px] text-danger font-sans">
|
|
384
388
|
<AlertTriangle size={10} /> llama-server not found
|
|
385
389
|
</div>
|
|
386
|
-
<code className="block text-
|
|
390
|
+
<code className="block text-[10px] font-mono text-text-3 bg-surface-2 px-2.5 py-1.5 rounded">brew install llama.cpp</code>
|
|
387
391
|
<button
|
|
388
392
|
onClick={checkLlama}
|
|
389
|
-
className="flex items-center gap-1.5 text-
|
|
393
|
+
className="flex items-center gap-1.5 text-[11px] font-sans text-accent hover:text-accent/80 transition-colors cursor-pointer"
|
|
390
394
|
>
|
|
391
395
|
<RotateCcw size={10} /> Recheck after install
|
|
392
396
|
</button>
|
|
@@ -399,44 +403,41 @@ export function LaunchModel() {
|
|
|
399
403
|
<div className="space-y-2">
|
|
400
404
|
{hasActiveAssistant && labAssistantBackend === selectedBackend ? (
|
|
401
405
|
<div className="space-y-2">
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
<><CheckCircle size={12} /> Setup complete</>
|
|
410
|
-
)}
|
|
411
|
-
</div>
|
|
406
|
+
{assistantRunning ? (
|
|
407
|
+
<StatusBanner variant="accent" icon={Loader2}>
|
|
408
|
+
Assistant is setting up {currentBackend?.label}...
|
|
409
|
+
</StatusBanner>
|
|
410
|
+
) : (
|
|
411
|
+
<StatusBanner variant="success" icon={CheckCircle}>Setup complete</StatusBanner>
|
|
412
|
+
)}
|
|
412
413
|
{!labAssistantMode && (
|
|
413
414
|
<button
|
|
414
415
|
onClick={() => setLabAssistantMode(true)}
|
|
415
|
-
className="w-full flex items-center justify-center gap-1.5
|
|
416
|
+
className="w-full flex items-center justify-center gap-1.5 h-8 text-[11px] font-sans font-medium text-text-1 bg-surface-2 hover:bg-surface-3 rounded transition-colors cursor-pointer"
|
|
416
417
|
>
|
|
417
418
|
View Assistant
|
|
418
419
|
</button>
|
|
419
420
|
)}
|
|
420
421
|
</div>
|
|
421
422
|
) : (
|
|
422
|
-
|
|
423
|
+
<div className="space-y-2">
|
|
423
424
|
<button
|
|
424
425
|
onClick={handleLaunchAssistant}
|
|
425
426
|
disabled={assistantLaunching}
|
|
426
427
|
className={cn(
|
|
427
|
-
'w-full flex items-center justify-center gap-1.5
|
|
428
|
+
'w-full flex items-center justify-center gap-1.5 h-8 text-[11px] font-sans font-medium rounded transition-colors cursor-pointer',
|
|
428
429
|
assistantLaunching ? 'bg-accent/20 text-accent' : 'bg-accent text-surface-0 hover:bg-accent/90',
|
|
429
430
|
)}
|
|
430
431
|
>
|
|
431
432
|
{assistantLaunching
|
|
432
|
-
? <><Loader2 size={
|
|
433
|
-
: <><Wrench size={
|
|
433
|
+
? <><Loader2 size={11} className="animate-spin" /> Starting Assistant...</>
|
|
434
|
+
: <><Wrench size={11} /> Setup {currentBackend?.label} with Assistant</>
|
|
434
435
|
}
|
|
435
436
|
</button>
|
|
436
|
-
<p className="text-
|
|
437
|
+
<p className="text-[10px] text-text-4 font-sans text-center">
|
|
437
438
|
An AI assistant will check your system and handle the installation.
|
|
438
439
|
</p>
|
|
439
|
-
|
|
440
|
+
</div>
|
|
440
441
|
)}
|
|
441
442
|
</div>
|
|
442
443
|
)}
|
|
@@ -444,22 +445,20 @@ export function LaunchModel() {
|
|
|
444
445
|
{isCompatible && currentBackend?.autoLaunch && (
|
|
445
446
|
<div className="space-y-2">
|
|
446
447
|
{serverRunning ? (
|
|
447
|
-
<
|
|
448
|
-
<CheckCircle size={12} /> Server Running
|
|
449
|
-
</div>
|
|
448
|
+
<StatusBanner variant="success" icon={CheckCircle}>Server Running</StatusBanner>
|
|
450
449
|
) : (
|
|
451
450
|
<button
|
|
452
451
|
disabled={!canLaunch}
|
|
453
452
|
onClick={handleLaunch}
|
|
454
453
|
className={cn(
|
|
455
|
-
'w-full flex items-center justify-center gap-1.5
|
|
454
|
+
'w-full flex items-center justify-center gap-1.5 h-8 text-[11px] font-sans font-medium rounded transition-colors cursor-pointer',
|
|
456
455
|
canLaunch ? 'bg-accent text-surface-0 hover:bg-accent/90' : 'bg-surface-3 text-text-4 cursor-not-allowed',
|
|
457
456
|
)}
|
|
458
457
|
>
|
|
459
458
|
{launching ? (
|
|
460
|
-
<><Loader2 size={
|
|
459
|
+
<><Loader2 size={11} className="animate-spin" /> Starting...</>
|
|
461
460
|
) : (
|
|
462
|
-
<><Play size={
|
|
461
|
+
<><Play size={11} /> Launch</>
|
|
463
462
|
)}
|
|
464
463
|
</button>
|
|
465
464
|
)}
|
|
@@ -468,9 +467,9 @@ export function LaunchModel() {
|
|
|
468
467
|
)}
|
|
469
468
|
</div>
|
|
470
469
|
)}
|
|
471
|
-
|
|
470
|
+
</div>
|
|
472
471
|
)}
|
|
473
|
-
</
|
|
472
|
+
</SidebarSection>
|
|
474
473
|
);
|
|
475
474
|
}
|
|
476
475
|
|
|
@@ -493,33 +492,33 @@ export function RuntimeConfig() {
|
|
|
493
492
|
}
|
|
494
493
|
|
|
495
494
|
return (
|
|
496
|
-
<
|
|
497
|
-
|
|
498
|
-
|
|
495
|
+
<SidebarSection
|
|
496
|
+
label="Runtimes"
|
|
497
|
+
action={
|
|
499
498
|
<Tooltip content="Add runtime">
|
|
500
499
|
<button
|
|
501
500
|
onClick={() => setDialogOpen(true)}
|
|
502
501
|
className="p-1 text-text-4 hover:text-accent transition-colors cursor-pointer"
|
|
503
502
|
>
|
|
504
|
-
<Plus size={
|
|
503
|
+
<Plus size={12} />
|
|
505
504
|
</button>
|
|
506
505
|
</Tooltip>
|
|
507
|
-
|
|
508
|
-
|
|
506
|
+
}
|
|
507
|
+
>
|
|
509
508
|
{runtimes.length === 0 ? (
|
|
510
|
-
<div className="py-
|
|
511
|
-
<WifiOff size={
|
|
512
|
-
<p className="text-
|
|
509
|
+
<div className="py-6 text-center rounded-md bg-surface-1/50 border border-border-subtle">
|
|
510
|
+
<WifiOff size={16} className="mx-auto text-text-4 mb-2" />
|
|
511
|
+
<p className="text-[11px] text-text-3 font-sans">No runtimes configured</p>
|
|
513
512
|
<button
|
|
514
513
|
onClick={() => setDialogOpen(true)}
|
|
515
|
-
className="mt-2 flex items-center gap-1
|
|
514
|
+
className="mt-2 inline-flex items-center gap-1 px-2.5 py-1 text-[10px] font-sans text-accent hover:text-accent/80 transition-colors cursor-pointer"
|
|
516
515
|
>
|
|
517
|
-
<Plus size={
|
|
516
|
+
<Plus size={10} /> Add Runtime
|
|
518
517
|
</button>
|
|
519
518
|
</div>
|
|
520
519
|
) : (
|
|
521
520
|
<ScrollArea className="max-h-48">
|
|
522
|
-
<div className="space-y-
|
|
521
|
+
<div className="space-y-1 rounded-md bg-surface-1/50 border border-border-subtle p-2">
|
|
523
522
|
{runtimes.map((rt) => (
|
|
524
523
|
<RuntimeItem
|
|
525
524
|
key={rt.id}
|
|
@@ -538,7 +537,7 @@ export function RuntimeConfig() {
|
|
|
538
537
|
)}
|
|
539
538
|
|
|
540
539
|
<AddRuntimeDialog open={dialogOpen} onOpenChange={setDialogOpen} />
|
|
541
|
-
</
|
|
540
|
+
</SidebarSection>
|
|
542
541
|
);
|
|
543
542
|
}
|
|
544
543
|
|
|
@@ -560,7 +559,7 @@ export function RuntimeSection() {
|
|
|
560
559
|
|
|
561
560
|
if (!serverRunning || expanded) {
|
|
562
561
|
return (
|
|
563
|
-
<div className="space-y-
|
|
562
|
+
<div className="space-y-6">
|
|
564
563
|
<LaunchModel />
|
|
565
564
|
<RuntimeConfig />
|
|
566
565
|
</div>
|
|
@@ -568,24 +567,22 @@ export function RuntimeSection() {
|
|
|
568
567
|
}
|
|
569
568
|
|
|
570
569
|
return (
|
|
571
|
-
<div className="
|
|
572
|
-
<
|
|
573
|
-
|
|
574
|
-
<div className="
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
{activeModel || 'Ready'}{activeRt?.latency != null ? ` · ${Math.round(activeRt.latency)}ms` : ''}
|
|
578
|
-
</div>
|
|
570
|
+
<div className="flex items-center gap-2.5 px-2.5 py-2 rounded-md bg-surface-1/50 border border-border-subtle">
|
|
571
|
+
<span className="w-2 h-2 rounded-full bg-success flex-shrink-0" />
|
|
572
|
+
<div className="flex-1 min-w-0">
|
|
573
|
+
<div className="text-[11px] font-sans font-medium text-text-0 truncate">{runtimeLabel}</div>
|
|
574
|
+
<div className="text-[10px] text-text-4 font-sans truncate">
|
|
575
|
+
{activeModel || 'Ready'}{activeRt?.latency != null ? ` · ${Math.round(activeRt.latency)}ms` : ''}
|
|
579
576
|
</div>
|
|
580
|
-
<Tooltip content="Runtime settings">
|
|
581
|
-
<button
|
|
582
|
-
onClick={() => setExpanded(true)}
|
|
583
|
-
className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer"
|
|
584
|
-
>
|
|
585
|
-
<Settings2 size={13} />
|
|
586
|
-
</button>
|
|
587
|
-
</Tooltip>
|
|
588
577
|
</div>
|
|
578
|
+
<Tooltip content="Runtime settings">
|
|
579
|
+
<button
|
|
580
|
+
onClick={() => setExpanded(true)}
|
|
581
|
+
className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer"
|
|
582
|
+
>
|
|
583
|
+
<Settings2 size={12} />
|
|
584
|
+
</button>
|
|
585
|
+
</Tooltip>
|
|
589
586
|
</div>
|
|
590
587
|
);
|
|
591
588
|
}
|
|
@@ -25,7 +25,7 @@ export function SystemPromptEditor() {
|
|
|
25
25
|
const systemPrompt = useGrooveStore((s) => s.labSystemPrompt);
|
|
26
26
|
const setSystemPrompt = useGrooveStore((s) => s.setLabSystemPrompt);
|
|
27
27
|
const themeKey = useGrooveStore((s) => s.editorTheme);
|
|
28
|
-
const [collapsed, setCollapsed] = useState(
|
|
28
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
29
29
|
const containerRef = useRef(null);
|
|
30
30
|
const viewRef = useRef(null);
|
|
31
31
|
const themeCompartment = useRef(new Compartment());
|
|
@@ -87,20 +87,22 @@ export function SystemPromptEditor() {
|
|
|
87
87
|
}, [systemPrompt]);
|
|
88
88
|
|
|
89
89
|
return (
|
|
90
|
-
<div className="space-y-
|
|
90
|
+
<div className="space-y-3">
|
|
91
91
|
<button
|
|
92
92
|
onClick={() => setCollapsed(!collapsed)}
|
|
93
|
-
className="flex items-center gap-1.5 w-full cursor-pointer group"
|
|
93
|
+
className="flex items-center gap-1.5 w-full h-6 cursor-pointer group"
|
|
94
94
|
>
|
|
95
95
|
{collapsed ? (
|
|
96
|
-
<ChevronRight size={
|
|
96
|
+
<ChevronRight size={10} className="text-text-4 group-hover:text-text-2 transition-colors flex-shrink-0" />
|
|
97
97
|
) : (
|
|
98
|
-
<ChevronDown size={
|
|
98
|
+
<ChevronDown size={10} className="text-text-4 group-hover:text-text-2 transition-colors flex-shrink-0" />
|
|
99
99
|
)}
|
|
100
|
-
<span className="text-
|
|
100
|
+
<span className="text-[10px] font-semibold font-sans text-text-3 uppercase tracking-widest group-hover:text-text-2 transition-colors">
|
|
101
101
|
System Prompt
|
|
102
102
|
</span>
|
|
103
|
-
|
|
103
|
+
{charCount > 0 && (
|
|
104
|
+
<span className="text-[10px] font-mono text-text-4 ml-auto tabular-nums">{charCount}</span>
|
|
105
|
+
)}
|
|
104
106
|
</button>
|
|
105
107
|
|
|
106
108
|
<div className={cn(
|
|
@@ -109,7 +111,7 @@ export function SystemPromptEditor() {
|
|
|
109
111
|
)}>
|
|
110
112
|
<div
|
|
111
113
|
ref={containerRef}
|
|
112
|
-
className="h-full border border-border-subtle
|
|
114
|
+
className="h-full rounded-md bg-surface-1/50 border border-border-subtle overflow-hidden"
|
|
113
115
|
/>
|
|
114
116
|
</div>
|
|
115
117
|
</div>
|
|
@@ -12,6 +12,7 @@ import { StatusBar } from './status-bar';
|
|
|
12
12
|
import { DetailPanel } from './detail-panel';
|
|
13
13
|
import { CommandPalette } from './command-palette';
|
|
14
14
|
import { ApprovalModal } from '../ui/approval-modal';
|
|
15
|
+
import { QuestionModal } from '../ui/question-modal';
|
|
15
16
|
import { QuickConnect } from '../settings/quick-connect';
|
|
16
17
|
|
|
17
18
|
import { TeamTabBar } from '../../views/agents';
|
|
@@ -119,6 +120,7 @@ export function AppShell({ children, detailContent, terminalContent }) {
|
|
|
119
120
|
<CommandPalette />
|
|
120
121
|
<QuickConnect />
|
|
121
122
|
<ApprovalModal />
|
|
123
|
+
<QuestionModal />
|
|
122
124
|
<ToastContainer />
|
|
123
125
|
</div>
|
|
124
126
|
</TooltipProvider>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import { Terminal, BookOpen, Radio, Plug, Globe, ArrowUpCircle, X, Unplug } from 'lucide-react';
|
|
2
|
+
import { Terminal, BookOpen, Radio, Plug, Globe, ArrowUpCircle, X, Unplug, Cpu, Square } from 'lucide-react';
|
|
3
3
|
import { cn } from '../../lib/cn';
|
|
4
4
|
import { StatusDot } from '../ui/status-dot';
|
|
5
5
|
import { Badge } from '../ui/badge';
|
|
@@ -23,7 +23,10 @@ export function StatusBar({
|
|
|
23
23
|
const updateProgress = useGrooveStore((s) => s.updateProgress);
|
|
24
24
|
const setUpdateModalOpen = useGrooveStore((s) => s.setUpdateModalOpen);
|
|
25
25
|
const navigate = useGrooveStore((s) => s.setActiveView);
|
|
26
|
+
const labRuntimes = useGrooveStore((s) => s.labRuntimes);
|
|
27
|
+
const stopLabRuntime = useGrooveStore((s) => s.stopLabRuntime);
|
|
26
28
|
const activeTunnels = savedTunnels.filter((t) => t.active);
|
|
29
|
+
const runningRuntimes = (labRuntimes || []).filter((rt) => rt.status === 'connected');
|
|
27
30
|
const electron = isElectron();
|
|
28
31
|
|
|
29
32
|
return (
|
|
@@ -110,6 +113,26 @@ export function StatusBar({
|
|
|
110
113
|
<span>Federation</span>
|
|
111
114
|
</button>
|
|
112
115
|
)}
|
|
116
|
+
{runningRuntimes.map((rt) => (
|
|
117
|
+
<div key={rt.id} className="flex items-center gap-1">
|
|
118
|
+
<button
|
|
119
|
+
onClick={() => navigate('model-lab')}
|
|
120
|
+
className="flex items-center gap-1.5 text-text-3 hover:text-text-1 cursor-pointer transition-colors"
|
|
121
|
+
title={`${rt.name} — running`}
|
|
122
|
+
>
|
|
123
|
+
<Cpu size={10} className="text-success" />
|
|
124
|
+
<span>{rt.name}</span>
|
|
125
|
+
<span className="w-1.5 h-1.5 rounded-full bg-success" />
|
|
126
|
+
</button>
|
|
127
|
+
<button
|
|
128
|
+
onClick={() => stopLabRuntime(rt.id)}
|
|
129
|
+
className="p-0.5 text-text-4 hover:text-danger cursor-pointer transition-colors rounded"
|
|
130
|
+
title={`Stop ${rt.name}`}
|
|
131
|
+
>
|
|
132
|
+
<Square size={8} />
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
))}
|
|
113
136
|
</div>
|
|
114
137
|
|
|
115
138
|
<div className="flex-1" />
|