groove-dev 0.26.9 → 0.26.11
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-DVRmIjTA.css → index-C4cVCdfw.css} +1 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-DnYXpxao.js +633 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/views/models.jsx +48 -22
- package/package.json +1 -1
- package/packages/gui/dist/assets/{index-DVRmIjTA.css → index-C4cVCdfw.css} +1 -1
- package/packages/gui/dist/assets/index-DnYXpxao.js +633 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -2
- package/packages/gui/src/views/models.jsx +48 -22
- package/node_modules/@groove-dev/gui/dist/assets/index-CfD162Lt.js +0 -633
- package/packages/gui/dist/assets/index-CfD162Lt.js +0 -633
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
7
7
|
<title>Groove GUI</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DnYXpxao.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C4cVCdfw.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
|
@@ -157,9 +157,9 @@ export function SpawnWizard() {
|
|
|
157
157
|
{/* Custom role */}
|
|
158
158
|
<div className="mt-3">
|
|
159
159
|
<Input
|
|
160
|
-
placeholder="or type a custom role..."
|
|
160
|
+
placeholder="or type a custom role (e.g. chat-agent)..."
|
|
161
161
|
value={customRole}
|
|
162
|
-
onChange={(e) => { setCustomRole(e.target.value); setRole(''); }}
|
|
162
|
+
onChange={(e) => { setCustomRole(e.target.value.replace(/\s+/g, '-').replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 50)); setRole(''); }}
|
|
163
163
|
className="text-xs"
|
|
164
164
|
/>
|
|
165
165
|
</div>
|
|
@@ -200,13 +200,16 @@ function FilePicker({ repoId, onDownload, systemRamGb }) {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
// ---- Recommended Model Card ----
|
|
203
|
-
function RecommendedModel({ model, systemRamGb, onPull, pulling }) {
|
|
203
|
+
function RecommendedModel({ model, systemRamGb, onPull, pulling, isInstalled }) {
|
|
204
204
|
const tierColors = { light: 'text-green-400', medium: 'text-blue-400', heavy: 'text-orange-400' };
|
|
205
205
|
const categoryIcons = { code: '{}', general: 'AI' };
|
|
206
206
|
const headroom = systemRamGb ? Math.round((1 - model.ramGb / systemRamGb) * 100) : null;
|
|
207
207
|
|
|
208
208
|
return (
|
|
209
|
-
<div className=
|
|
209
|
+
<div className={cn(
|
|
210
|
+
'flex items-center gap-3 px-4 py-3 border rounded-lg transition-colors',
|
|
211
|
+
isInstalled ? 'bg-success/5 border-success/20' : 'bg-surface-1 border-border-subtle hover:border-accent/20',
|
|
212
|
+
)}>
|
|
210
213
|
<div className="w-9 h-9 rounded-lg bg-surface-3 flex items-center justify-center text-xs font-mono text-text-2 flex-shrink-0">
|
|
211
214
|
{categoryIcons[model.category] || 'AI'}
|
|
212
215
|
</div>
|
|
@@ -214,6 +217,7 @@ function RecommendedModel({ model, systemRamGb, onPull, pulling }) {
|
|
|
214
217
|
<div className="flex items-center gap-2">
|
|
215
218
|
<span className="text-sm font-mono font-bold text-text-0 truncate">{model.name}</span>
|
|
216
219
|
<span className={cn('text-2xs font-semibold capitalize', tierColors[model.tier])}>{model.tier}</span>
|
|
220
|
+
{isInstalled && <Badge variant="success" className="text-2xs gap-1"><Check size={8} /> Installed</Badge>}
|
|
217
221
|
</div>
|
|
218
222
|
<div className="text-2xs text-text-3 font-sans mt-0.5">{model.description}</div>
|
|
219
223
|
<div className="flex items-center gap-3 mt-1 text-2xs font-sans">
|
|
@@ -222,14 +226,18 @@ function RecommendedModel({ model, systemRamGb, onPull, pulling }) {
|
|
|
222
226
|
{headroom !== null && <span className="text-text-4">{headroom}% headroom</span>}
|
|
223
227
|
</div>
|
|
224
228
|
</div>
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
229
|
+
{isInstalled ? (
|
|
230
|
+
<span className="text-xs text-success font-sans font-medium px-3 py-1.5">Ready</span>
|
|
231
|
+
) : (
|
|
232
|
+
<button
|
|
233
|
+
onClick={() => onPull(model.id)}
|
|
234
|
+
disabled={pulling === model.id}
|
|
235
|
+
className="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-sans font-medium bg-accent/10 text-accent hover:bg-accent/20 transition-colors cursor-pointer disabled:opacity-40"
|
|
236
|
+
>
|
|
237
|
+
{pulling === model.id ? <Loader2 size={12} className="animate-spin" /> : <Download size={12} />}
|
|
238
|
+
Pull
|
|
239
|
+
</button>
|
|
240
|
+
)}
|
|
233
241
|
</div>
|
|
234
242
|
);
|
|
235
243
|
}
|
|
@@ -246,6 +254,7 @@ export default function ModelsView() {
|
|
|
246
254
|
const [hardware, setHardware] = useState(null);
|
|
247
255
|
const [expandedResult, setExpandedResult] = useState(null);
|
|
248
256
|
const [pulling, setPulling] = useState(null);
|
|
257
|
+
const [ollamaModels, setOllamaModels] = useState([]);
|
|
249
258
|
const toast = useToast();
|
|
250
259
|
|
|
251
260
|
// Fetch installed models
|
|
@@ -255,7 +264,13 @@ export default function ModelsView() {
|
|
|
255
264
|
}).catch(() => {});
|
|
256
265
|
}, []);
|
|
257
266
|
|
|
258
|
-
|
|
267
|
+
const fetchOllamaModels = useCallback(() => {
|
|
268
|
+
api.get('/providers/ollama/models').then((data) => {
|
|
269
|
+
setOllamaModels((data.installed || []).map((m) => m.id));
|
|
270
|
+
}).catch(() => {});
|
|
271
|
+
}, []);
|
|
272
|
+
|
|
273
|
+
// Fetch hardware info + recommended models + Ollama installed
|
|
259
274
|
useEffect(() => {
|
|
260
275
|
api.get('/providers/ollama/hardware').then(setHardware).catch(() => {});
|
|
261
276
|
api.get('/models/recommended').then((data) => {
|
|
@@ -263,14 +278,19 @@ export default function ModelsView() {
|
|
|
263
278
|
if (!hardware && data.hardware) setHardware(data.hardware);
|
|
264
279
|
}).catch(() => {});
|
|
265
280
|
fetchInstalled();
|
|
266
|
-
|
|
281
|
+
fetchOllamaModels();
|
|
282
|
+
}, [fetchInstalled, fetchOllamaModels]);
|
|
267
283
|
|
|
268
284
|
async function handlePull(modelId) {
|
|
269
285
|
setPulling(modelId);
|
|
270
286
|
try {
|
|
271
287
|
await api.post('/providers/ollama/pull', { model: modelId });
|
|
272
|
-
toast.success(`${modelId}
|
|
288
|
+
toast.success(`${modelId} ready to use`);
|
|
289
|
+
// Refresh all model lists so UI reflects the new install
|
|
273
290
|
fetchInstalled();
|
|
291
|
+
fetchOllamaModels();
|
|
292
|
+
// Also optimistically mark it installed immediately
|
|
293
|
+
setOllamaModels((prev) => [...prev, modelId]);
|
|
274
294
|
} catch (err) {
|
|
275
295
|
toast.error(`Pull failed: ${err.message}`);
|
|
276
296
|
}
|
|
@@ -419,15 +439,21 @@ export default function ModelsView() {
|
|
|
419
439
|
<div className="text-xs text-text-3 font-sans mb-2">
|
|
420
440
|
Top models for your system ({hardware?.totalRamGb || '?'} GB RAM). Click Pull to download via Ollama.
|
|
421
441
|
</div>
|
|
422
|
-
{recommended.map((m) =>
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
442
|
+
{recommended.map((m) => {
|
|
443
|
+
// Check if this model (or a variant) is already installed in Ollama
|
|
444
|
+
const baseId = m.id.split(':')[0];
|
|
445
|
+
const isInstalled = ollamaModels.some((id) => id === m.id || id.startsWith(baseId + ':') || id === baseId);
|
|
446
|
+
return (
|
|
447
|
+
<RecommendedModel
|
|
448
|
+
key={m.id}
|
|
449
|
+
model={m}
|
|
450
|
+
systemRamGb={hardware?.totalRamGb}
|
|
451
|
+
onPull={handlePull}
|
|
452
|
+
pulling={pulling}
|
|
453
|
+
isInstalled={isInstalled}
|
|
454
|
+
/>
|
|
455
|
+
);
|
|
456
|
+
})}
|
|
431
457
|
</>
|
|
432
458
|
)}
|
|
433
459
|
</>
|