groove-dev 0.27.143 → 0.27.145
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 -7
- 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 +1086 -6532
- package/node_modules/@groove-dev/daemon/src/conversations.js +18 -48
- package/node_modules/@groove-dev/daemon/src/gateways/manager.js +35 -1
- package/node_modules/@groove-dev/daemon/src/index.js +3 -0
- package/node_modules/@groove-dev/daemon/src/journalist.js +23 -13
- package/node_modules/@groove-dev/daemon/src/mlx-server.js +365 -0
- package/node_modules/@groove-dev/daemon/src/model-lab.js +308 -12
- package/node_modules/@groove-dev/daemon/src/pm.js +1 -1
- package/node_modules/@groove-dev/daemon/src/process.js +2 -2
- package/node_modules/@groove-dev/daemon/src/providers/local.js +36 -8
- package/node_modules/@groove-dev/daemon/src/registry.js +21 -5
- package/node_modules/@groove-dev/daemon/src/routes/agents.js +812 -0
- package/node_modules/@groove-dev/daemon/src/routes/coordination.js +318 -0
- package/node_modules/@groove-dev/daemon/src/routes/files.js +751 -0
- package/node_modules/@groove-dev/daemon/src/routes/integrations.js +485 -0
- package/node_modules/@groove-dev/daemon/src/routes/network.js +1784 -0
- package/node_modules/@groove-dev/daemon/src/routes/providers.js +755 -0
- package/node_modules/@groove-dev/daemon/src/routes/schedules.js +110 -0
- package/node_modules/@groove-dev/daemon/src/routes/teams.js +650 -0
- package/node_modules/@groove-dev/daemon/src/scheduler.js +456 -24
- package/node_modules/@groove-dev/daemon/src/teams.js +1 -1
- package/node_modules/@groove-dev/daemon/src/validate.js +38 -1
- package/node_modules/@groove-dev/daemon/templates/mlx-setup.json +12 -0
- package/node_modules/@groove-dev/daemon/templates/tgi-setup.json +1 -1
- package/node_modules/@groove-dev/daemon/templates/vllm-setup.json +1 -1
- package/node_modules/@groove-dev/daemon/test/introducer.test.js +3 -3
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +7 -10
- package/node_modules/@groove-dev/daemon/test/registry.test.js +38 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-Bxc0gU06.js +1006 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-C0pztKBn.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/{app.jsx → App.jsx} +0 -2
- package/node_modules/@groove-dev/gui/src/app.css +35 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +1 -128
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +210 -112
- package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +8 -13
- package/node_modules/@groove-dev/gui/src/components/agents/agent-panel.jsx +2 -70
- package/node_modules/@groove-dev/gui/src/components/agents/code-review.jsx +159 -122
- package/node_modules/@groove-dev/gui/src/components/agents/diff-viewer.jsx +23 -23
- package/node_modules/@groove-dev/gui/src/components/agents/journalist-panel.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +2 -135
- package/node_modules/@groove-dev/gui/src/components/automations/automation-card.jsx +274 -0
- package/node_modules/@groove-dev/gui/src/components/automations/automation-wizard.jsx +1136 -0
- package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +68 -66
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +4 -8
- package/node_modules/@groove-dev/gui/src/components/dashboard/activity-feed.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/cache-ring.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/components/dashboard/context-gauges.jsx +6 -8
- package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +8 -14
- package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +238 -656
- package/node_modules/@groove-dev/gui/src/components/dashboard/kpi-card.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/routing-chart.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/components/dashboard/token-chart.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +39 -31
- package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +316 -82
- package/node_modules/@groove-dev/gui/src/components/lab/metrics-panel.jsx +187 -32
- package/node_modules/@groove-dev/gui/src/components/lab/parameter-panel.jsx +200 -18
- 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 +335 -152
- package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +10 -8
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +2 -4
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +4 -2
- package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +137 -108
- package/node_modules/@groove-dev/gui/src/components/network/network-health.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/network/performance-dashboard.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +81 -99
- package/node_modules/@groove-dev/gui/src/components/ui/sheet.jsx +5 -2
- package/node_modules/@groove-dev/gui/src/components/ui/slider.jsx +8 -8
- package/node_modules/@groove-dev/gui/src/lib/cron.js +64 -0
- package/node_modules/@groove-dev/gui/src/lib/status.js +25 -24
- package/node_modules/@groove-dev/gui/src/lib/theme-hex.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +51 -3144
- package/node_modules/@groove-dev/gui/src/stores/helpers.js +10 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/agents-slice.js +459 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/automations-slice.js +96 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/chat-slice.js +226 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/editor-slice.js +285 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/marketplace-slice.js +461 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/network-slice.js +361 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/preview-slice.js +109 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/providers-slice.js +897 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/teams-slice.js +413 -0
- package/node_modules/@groove-dev/gui/src/stores/slices/ui-slice.js +98 -0
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +12 -13
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +191 -3
- package/node_modules/@groove-dev/gui/src/views/model-lab.jsx +54 -12
- package/node_modules/@groove-dev/gui/src/views/models.jsx +419 -496
- package/node_modules/@groove-dev/gui/src/views/network.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +81 -94
- package/node_modules/@groove-dev/gui/src/views/teams.jsx +40 -483
- 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 +1086 -6532
- package/packages/daemon/src/conversations.js +18 -48
- package/packages/daemon/src/gateways/manager.js +35 -1
- package/packages/daemon/src/index.js +3 -0
- package/packages/daemon/src/journalist.js +23 -13
- package/packages/daemon/src/mlx-server.js +365 -0
- package/packages/daemon/src/model-lab.js +308 -12
- package/packages/daemon/src/pm.js +1 -1
- package/packages/daemon/src/process.js +2 -2
- package/packages/daemon/src/providers/local.js +36 -8
- package/packages/daemon/src/registry.js +21 -5
- package/packages/daemon/src/routes/agents.js +812 -0
- package/packages/daemon/src/routes/coordination.js +318 -0
- package/packages/daemon/src/routes/files.js +751 -0
- package/packages/daemon/src/routes/integrations.js +485 -0
- package/packages/daemon/src/routes/network.js +1784 -0
- package/packages/daemon/src/routes/providers.js +755 -0
- package/packages/daemon/src/routes/schedules.js +110 -0
- package/packages/daemon/src/routes/teams.js +650 -0
- package/packages/daemon/src/scheduler.js +456 -24
- package/packages/daemon/src/teams.js +1 -1
- package/packages/daemon/src/validate.js +38 -1
- package/packages/daemon/templates/mlx-setup.json +12 -0
- package/packages/daemon/templates/tgi-setup.json +1 -1
- package/packages/daemon/templates/vllm-setup.json +1 -1
- package/packages/gui/dist/assets/index-Bxc0gU06.js +1006 -0
- package/packages/gui/dist/assets/index-C0pztKBn.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/{app.jsx → App.jsx} +0 -2
- package/packages/gui/src/app.css +35 -0
- package/packages/gui/src/components/agents/agent-config.jsx +1 -128
- package/packages/gui/src/components/agents/agent-feed.jsx +210 -112
- package/packages/gui/src/components/agents/agent-node.jsx +8 -13
- package/packages/gui/src/components/agents/agent-panel.jsx +2 -70
- package/packages/gui/src/components/agents/code-review.jsx +159 -122
- package/packages/gui/src/components/agents/diff-viewer.jsx +23 -23
- package/packages/gui/src/components/agents/journalist-panel.jsx +1 -1
- package/packages/gui/src/components/agents/spawn-wizard.jsx +2 -135
- package/packages/gui/src/components/automations/automation-card.jsx +274 -0
- package/packages/gui/src/components/automations/automation-wizard.jsx +1136 -0
- package/packages/gui/src/components/chat/chat-header.jsx +2 -0
- package/packages/gui/src/components/chat/chat-input.jsx +68 -66
- package/packages/gui/src/components/chat/chat-view.jsx +4 -8
- package/packages/gui/src/components/dashboard/activity-feed.jsx +3 -3
- package/packages/gui/src/components/dashboard/cache-ring.jsx +5 -5
- package/packages/gui/src/components/dashboard/context-gauges.jsx +6 -8
- package/packages/gui/src/components/dashboard/fleet-panel.jsx +8 -14
- package/packages/gui/src/components/dashboard/intel-panel.jsx +238 -656
- package/packages/gui/src/components/dashboard/kpi-card.jsx +3 -3
- package/packages/gui/src/components/dashboard/routing-chart.jsx +3 -3
- package/packages/gui/src/components/dashboard/team-burn-panel.jsx +1 -1
- package/packages/gui/src/components/dashboard/token-chart.jsx +4 -4
- package/packages/gui/src/components/lab/chat-playground.jsx +39 -31
- package/packages/gui/src/components/lab/lab-assistant.jsx +316 -82
- package/packages/gui/src/components/lab/metrics-panel.jsx +187 -32
- package/packages/gui/src/components/lab/parameter-panel.jsx +200 -18
- package/packages/gui/src/components/lab/preset-manager.jsx +17 -14
- package/packages/gui/src/components/lab/runtime-config.jsx +335 -152
- package/packages/gui/src/components/lab/system-prompt-editor.jsx +10 -8
- package/packages/gui/src/components/layout/activity-bar.jsx +2 -4
- package/packages/gui/src/components/layout/terminal-panel.jsx +4 -2
- package/packages/gui/src/components/layout/welcome-splash.jsx +137 -108
- package/packages/gui/src/components/network/network-health.jsx +2 -2
- package/packages/gui/src/components/network/performance-dashboard.jsx +4 -4
- package/packages/gui/src/components/settings/ssh-wizard.jsx +81 -99
- package/packages/gui/src/components/ui/sheet.jsx +5 -2
- package/packages/gui/src/components/ui/slider.jsx +8 -8
- package/packages/gui/src/lib/cron.js +64 -0
- package/packages/gui/src/lib/status.js +25 -24
- package/packages/gui/src/lib/theme-hex.js +1 -0
- package/packages/gui/src/stores/groove.js +51 -3144
- package/packages/gui/src/stores/helpers.js +10 -0
- package/packages/gui/src/stores/slices/agents-slice.js +459 -0
- package/packages/gui/src/stores/slices/automations-slice.js +96 -0
- package/packages/gui/src/stores/slices/chat-slice.js +226 -0
- package/packages/gui/src/stores/slices/editor-slice.js +285 -0
- package/packages/gui/src/stores/slices/marketplace-slice.js +461 -0
- package/packages/gui/src/stores/slices/network-slice.js +361 -0
- package/packages/gui/src/stores/slices/preview-slice.js +109 -0
- package/packages/gui/src/stores/slices/providers-slice.js +897 -0
- package/packages/gui/src/stores/slices/teams-slice.js +413 -0
- package/packages/gui/src/stores/slices/ui-slice.js +98 -0
- package/packages/gui/src/views/agents.jsx +5 -5
- package/packages/gui/src/views/dashboard.jsx +12 -13
- package/packages/gui/src/views/marketplace.jsx +191 -3
- package/packages/gui/src/views/model-lab.jsx +54 -12
- package/packages/gui/src/views/models.jsx +419 -496
- package/packages/gui/src/views/network.jsx +3 -3
- package/packages/gui/src/views/settings.jsx +81 -94
- package/packages/gui/src/views/teams.jsx +40 -483
- package/SECURITY_SWEEP.md +0 -228
- package/TRAINING_DATA_v4.md +0 -6
- package/node_modules/@groove-dev/gui/dist/assets/index-CCVvAoQn.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-DGIv_TRm.js +0 -984
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +0 -379
- package/node_modules/@groove-dev/gui/src/views/preview.jsx +0 -6
- package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +0 -327
- package/packages/gui/dist/assets/index-CCVvAoQn.css +0 -1
- package/packages/gui/dist/assets/index-DGIv_TRm.js +0 -984
- package/packages/gui/src/components/agents/agent-chat.jsx +0 -379
- package/packages/gui/src/views/preview.jsx +0 -6
- package/packages/gui/src/views/subscription-panel.jsx +0 -327
- package/test.py +0 -571
|
@@ -4,41 +4,237 @@ import { useGrooveStore } from '../../stores/groove';
|
|
|
4
4
|
import { ScrollArea } from '../ui/scroll-area';
|
|
5
5
|
import { Badge } from '../ui/badge';
|
|
6
6
|
import { cn } from '../../lib/cn';
|
|
7
|
-
import { Send, X,
|
|
7
|
+
import { Send, X, ArrowRight, ChevronDown, AlertCircle, FlaskConical } from 'lucide-react';
|
|
8
8
|
|
|
9
|
+
// ── Inline formatting (bold, code) ──────────────────────────
|
|
10
|
+
function InlineFormat({ text }) {
|
|
11
|
+
if (!text) return null;
|
|
12
|
+
return text.split(/(\*\*[^*]+\*\*|`[^`]+`)/g).map((part, i) => {
|
|
13
|
+
if (part.startsWith('**') && part.endsWith('**'))
|
|
14
|
+
return <strong key={i} className="font-semibold text-text-0">{part.slice(2, -2)}</strong>;
|
|
15
|
+
if (part.startsWith('`') && part.endsWith('`'))
|
|
16
|
+
return <code key={i} className="px-1 py-px rounded bg-accent/8 text-[11px] font-mono text-accent">{part.slice(1, -1)}</code>;
|
|
17
|
+
return <span key={i}>{part}</span>;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ── Structured message renderer ─────────────────────────────
|
|
22
|
+
function StructuredMessage({ text }) {
|
|
23
|
+
if (!text) return null;
|
|
24
|
+
|
|
25
|
+
const blocks = [];
|
|
26
|
+
const lines = text.split('\n');
|
|
27
|
+
let i = 0;
|
|
28
|
+
|
|
29
|
+
while (i < lines.length) {
|
|
30
|
+
const line = lines[i];
|
|
31
|
+
|
|
32
|
+
if (line.trimStart().startsWith('```')) {
|
|
33
|
+
const codeLines = [];
|
|
34
|
+
i++;
|
|
35
|
+
while (i < lines.length && !lines[i].trimStart().startsWith('```')) {
|
|
36
|
+
codeLines.push(lines[i]);
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
i++;
|
|
40
|
+
blocks.push({ type: 'code', content: codeLines.join('\n') });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (/^#{1,3}\s/.test(line) || /^\*\*[^*]+:\*\*\s*$/.test(line.trim())) {
|
|
45
|
+
const heading = line.replace(/^#+\s*/, '').replace(/^\*\*/, '').replace(/:\*\*\s*$/, ':').trim();
|
|
46
|
+
blocks.push({ type: 'heading', content: heading });
|
|
47
|
+
i++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (/^\s*[-*]\s/.test(line)) {
|
|
52
|
+
const items = [];
|
|
53
|
+
while (i < lines.length && /^\s*[-*]\s/.test(lines[i])) {
|
|
54
|
+
items.push(lines[i].replace(/^\s*[-*]\s+/, '').trim());
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
57
|
+
blocks.push({ type: 'list', items });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (/^\s*\d+[\.)]\s/.test(line)) {
|
|
62
|
+
const items = [];
|
|
63
|
+
while (i < lines.length && /^\s*\d+[\.)]\s/.test(lines[i])) {
|
|
64
|
+
items.push(lines[i].replace(/^\s*\d+[\.)]\s+/, '').trim());
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
blocks.push({ type: 'numbered', items });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!line.trim()) { i++; continue; }
|
|
72
|
+
|
|
73
|
+
if (/^(Note|Warning|Important|IMPORTANT|TODO):/i.test(line.trim())) {
|
|
74
|
+
blocks.push({ type: 'note', content: line.trim() });
|
|
75
|
+
i++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const paraLines = [];
|
|
80
|
+
while (i < lines.length && lines[i].trim() && !/^#{1,3}\s/.test(lines[i]) && !/^\s*[-*]\s/.test(lines[i]) && !/^\s*\d+[\.)]\s/.test(lines[i]) && !lines[i].trimStart().startsWith('```')) {
|
|
81
|
+
paraLines.push(lines[i].trim());
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
if (paraLines.length > 0) {
|
|
85
|
+
blocks.push({ type: 'para', content: paraLines.join(' ') });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div className="space-y-2">
|
|
91
|
+
{blocks.map((block, idx) => {
|
|
92
|
+
switch (block.type) {
|
|
93
|
+
case 'heading':
|
|
94
|
+
return (
|
|
95
|
+
<div key={idx} className="flex items-center gap-1.5 pt-1.5 first:pt-0">
|
|
96
|
+
<div className="w-1 h-3.5 rounded-full bg-accent/40 flex-shrink-0" />
|
|
97
|
+
<span className="text-[13px] font-semibold text-text-0 font-sans"><InlineFormat text={block.content} /></span>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
case 'list':
|
|
101
|
+
return (
|
|
102
|
+
<div key={idx} className="space-y-1 pl-2">
|
|
103
|
+
{block.items.map((item, j) => (
|
|
104
|
+
<div key={j} className="flex gap-2 text-[13px] text-text-1 font-sans leading-relaxed">
|
|
105
|
+
<span className="text-accent/50 mt-0.5 flex-shrink-0">-</span>
|
|
106
|
+
<span className="min-w-0"><InlineFormat text={item} /></span>
|
|
107
|
+
</div>
|
|
108
|
+
))}
|
|
109
|
+
</div>
|
|
110
|
+
);
|
|
111
|
+
case 'numbered':
|
|
112
|
+
return (
|
|
113
|
+
<div key={idx} className="space-y-1 pl-2">
|
|
114
|
+
{block.items.map((item, j) => (
|
|
115
|
+
<div key={j} className="flex gap-2 text-[13px] text-text-1 font-sans leading-relaxed">
|
|
116
|
+
<span className="text-text-4 font-mono w-4 text-right flex-shrink-0">{j + 1}.</span>
|
|
117
|
+
<span className="min-w-0"><InlineFormat text={item} /></span>
|
|
118
|
+
</div>
|
|
119
|
+
))}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
case 'code':
|
|
123
|
+
return (
|
|
124
|
+
<pre key={idx} className="p-2.5 rounded-md bg-[#0d1117] text-[11px] font-mono text-[#c9d1d9] overflow-x-auto whitespace-pre-wrap border border-white/[0.06] leading-relaxed">
|
|
125
|
+
{block.content}
|
|
126
|
+
</pre>
|
|
127
|
+
);
|
|
128
|
+
case 'note':
|
|
129
|
+
return (
|
|
130
|
+
<div key={idx} className="flex items-start gap-1.5 px-2.5 py-1.5 rounded-md bg-warning/6 border border-warning/12">
|
|
131
|
+
<AlertCircle size={10} className="text-warning mt-0.5 flex-shrink-0" />
|
|
132
|
+
<span className="text-[12px] text-warning/80 font-sans"><InlineFormat text={block.content} /></span>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
case 'para':
|
|
136
|
+
default:
|
|
137
|
+
return <p key={idx} className="text-[13px] text-text-1 font-sans leading-relaxed"><InlineFormat text={block.content} /></p>;
|
|
138
|
+
}
|
|
139
|
+
})}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Thinking indicator ──────────────────────────────────────
|
|
145
|
+
const THINKING_MESSAGES = [
|
|
146
|
+
'Checking your system...',
|
|
147
|
+
'Running setup commands...',
|
|
148
|
+
'Working through installation...',
|
|
149
|
+
'Configuring the server...',
|
|
150
|
+
'Making progress...',
|
|
151
|
+
'Almost there...',
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
function LabThinkingIndicator() {
|
|
155
|
+
const [idx, setIdx] = useState(0);
|
|
156
|
+
const [fade, setFade] = useState(true);
|
|
157
|
+
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
const t = setInterval(() => {
|
|
160
|
+
setFade(false);
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
setIdx((i) => (i + 1) % THINKING_MESSAGES.length);
|
|
163
|
+
setFade(true);
|
|
164
|
+
}, 250);
|
|
165
|
+
}, 2800);
|
|
166
|
+
return () => clearInterval(t);
|
|
167
|
+
}, []);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div>
|
|
171
|
+
<div className="flex items-center gap-2 mb-1">
|
|
172
|
+
<span className="text-2xs font-semibold text-text-1 font-sans">Lab Assistant</span>
|
|
173
|
+
<span className="text-2xs text-accent font-mono">working</span>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="border-l border-accent/40 pl-3.5 py-1 flex items-center gap-2.5">
|
|
176
|
+
<div className="relative w-3.5 h-3.5 flex-shrink-0">
|
|
177
|
+
<span className="absolute inset-0 rounded-full border border-transparent border-t-accent animate-spin" style={{ animationDuration: '0.9s' }} />
|
|
178
|
+
</div>
|
|
179
|
+
<span
|
|
180
|
+
className="text-[13px] font-sans text-text-3 transition-opacity duration-[250ms]"
|
|
181
|
+
style={{ opacity: fade ? 1 : 0 }}
|
|
182
|
+
>
|
|
183
|
+
{THINKING_MESSAGES[idx]}
|
|
184
|
+
</span>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── Message components ──────────────────────────────────────
|
|
9
191
|
function AssistantMessage({ msg }) {
|
|
192
|
+
const [collapsed, setCollapsed] = useState(msg.text?.length > 800);
|
|
193
|
+
const isLong = msg.text?.length > 800;
|
|
194
|
+
|
|
10
195
|
return (
|
|
11
196
|
<div className="animate-chat-fade-in">
|
|
12
|
-
<div className="flex items-center gap-
|
|
13
|
-
<
|
|
14
|
-
<Bot size={10} className="text-text-3" />
|
|
15
|
-
</div>
|
|
16
|
-
<span className="text-2xs text-text-3 font-sans font-medium">Lab Assistant</span>
|
|
197
|
+
<div className="flex items-center gap-2 mb-1">
|
|
198
|
+
<span className="text-2xs font-semibold text-text-1 font-sans">Lab Assistant</span>
|
|
17
199
|
</div>
|
|
18
|
-
<div className="
|
|
19
|
-
<
|
|
20
|
-
'text-xs font-sans whitespace-pre-wrap break-words leading-relaxed',
|
|
21
|
-
msg.error ? 'text-danger' : 'text-text-1',
|
|
22
|
-
)}>
|
|
23
|
-
{msg.text}
|
|
24
|
-
</div>
|
|
200
|
+
<div className="border-l border-accent pl-3.5 py-1">
|
|
201
|
+
<StructuredMessage text={collapsed ? msg.text.slice(0, 800) + '...' : msg.text} />
|
|
25
202
|
</div>
|
|
203
|
+
{collapsed && (
|
|
204
|
+
<button
|
|
205
|
+
onClick={() => setCollapsed(false)}
|
|
206
|
+
className="ml-3.5 mt-1.5 flex items-center gap-1.5 text-[11px] text-accent/70 hover:text-accent font-sans font-medium cursor-pointer transition-colors"
|
|
207
|
+
>
|
|
208
|
+
<ChevronDown size={11} />
|
|
209
|
+
Show full response
|
|
210
|
+
</button>
|
|
211
|
+
)}
|
|
212
|
+
{isLong && !collapsed && (
|
|
213
|
+
<button
|
|
214
|
+
onClick={() => setCollapsed(true)}
|
|
215
|
+
className="ml-3.5 mt-1.5 flex items-center gap-1.5 text-[11px] text-accent/70 hover:text-accent font-sans font-medium cursor-pointer transition-colors"
|
|
216
|
+
>
|
|
217
|
+
<ChevronDown size={11} className="rotate-180" />
|
|
218
|
+
Collapse
|
|
219
|
+
</button>
|
|
220
|
+
)}
|
|
26
221
|
</div>
|
|
27
222
|
);
|
|
28
223
|
}
|
|
29
224
|
|
|
30
225
|
function UserMessage({ msg }) {
|
|
31
226
|
return (
|
|
32
|
-
<div className="flex justify-end animate-chat-fade-in">
|
|
33
|
-
<div className="max-w-[
|
|
34
|
-
<div className="px-3.5 py-2 bg-
|
|
35
|
-
<p className="text-
|
|
227
|
+
<div className="flex justify-end pl-8 animate-chat-fade-in">
|
|
228
|
+
<div className="max-w-[90%]">
|
|
229
|
+
<div className="px-3.5 py-2.5 rounded-lg border bg-info/10 border-info/25">
|
|
230
|
+
<p className="text-[13px] text-text-0 font-sans whitespace-pre-wrap break-words leading-relaxed">{msg.text}</p>
|
|
36
231
|
</div>
|
|
37
232
|
</div>
|
|
38
233
|
</div>
|
|
39
234
|
);
|
|
40
235
|
}
|
|
41
236
|
|
|
237
|
+
// ── Main component ──────────────────────────────────────────
|
|
42
238
|
export function LabAssistant() {
|
|
43
239
|
const agentId = useGrooveStore((s) => s.labAssistantAgentId);
|
|
44
240
|
const backend = useGrooveStore((s) => s.labAssistantBackend);
|
|
@@ -48,10 +244,15 @@ export function LabAssistant() {
|
|
|
48
244
|
const instructAgent = useGrooveStore((s) => s.instructAgent);
|
|
49
245
|
const dismissLabAssistant = useGrooveStore((s) => s.dismissLabAssistant);
|
|
50
246
|
const setLabAssistantMode = useGrooveStore((s) => s.setLabAssistantMode);
|
|
247
|
+
const onLabAssistantComplete = useGrooveStore((s) => s.onLabAssistantComplete);
|
|
248
|
+
const fetchLabRuntimes = useGrooveStore((s) => s.fetchLabRuntimes);
|
|
249
|
+
const activeRuntime = useGrooveStore((s) => s.labActiveRuntime);
|
|
250
|
+
const activeModel = useGrooveStore((s) => s.labActiveModel);
|
|
51
251
|
|
|
52
252
|
const [input, setInput] = useState('');
|
|
53
253
|
const scrollRef = useRef(null);
|
|
54
254
|
const inputRef = useRef(null);
|
|
255
|
+
const completionHandled = useRef(false);
|
|
55
256
|
|
|
56
257
|
const agent = agents.find((a) => a.id === agentId);
|
|
57
258
|
const messages = chatHistory[agentId] || [];
|
|
@@ -59,19 +260,33 @@ export function LabAssistant() {
|
|
|
59
260
|
const isRunning = agent?.status === 'running';
|
|
60
261
|
const isComplete = agent && agent.status !== 'running';
|
|
61
262
|
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
if (isComplete && !completionHandled.current) {
|
|
265
|
+
completionHandled.current = true;
|
|
266
|
+
onLabAssistantComplete();
|
|
267
|
+
}
|
|
268
|
+
if (isRunning) completionHandled.current = false;
|
|
269
|
+
}, [isComplete, isRunning, onLabAssistantComplete]);
|
|
270
|
+
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (!isRunning || activeRuntime) return;
|
|
273
|
+
const interval = setInterval(fetchLabRuntimes, 5000);
|
|
274
|
+
return () => clearInterval(interval);
|
|
275
|
+
}, [isRunning, activeRuntime, fetchLabRuntimes]);
|
|
276
|
+
|
|
62
277
|
useEffect(() => {
|
|
63
278
|
if (scrollRef.current) {
|
|
64
279
|
const el = scrollRef.current.querySelector('[data-radix-scroll-area-viewport]');
|
|
65
280
|
if (el) el.scrollTop = el.scrollHeight;
|
|
66
281
|
}
|
|
67
|
-
}, [messages.length, messages[messages.length - 1]?.text]);
|
|
282
|
+
}, [messages.length, messages[messages.length - 1]?.text, isThinking]);
|
|
68
283
|
|
|
69
284
|
const handleSend = useCallback(() => {
|
|
70
285
|
const text = input.trim();
|
|
71
|
-
if (!text || !agentId
|
|
286
|
+
if (!text || !agentId) return;
|
|
72
287
|
setInput('');
|
|
73
288
|
instructAgent(agentId, text);
|
|
74
|
-
}, [input, agentId,
|
|
289
|
+
}, [input, agentId, instructAgent]);
|
|
75
290
|
|
|
76
291
|
function handleKeyDown(e) {
|
|
77
292
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
@@ -84,39 +299,31 @@ export function LabAssistant() {
|
|
|
84
299
|
|
|
85
300
|
return (
|
|
86
301
|
<div className="h-full flex flex-col">
|
|
87
|
-
{/*
|
|
88
|
-
|
|
89
|
-
<div className="flex items-center gap-
|
|
90
|
-
|
|
91
|
-
<span className="
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
</Badge>
|
|
100
|
-
)}
|
|
302
|
+
{/* Streaming status bar */}
|
|
303
|
+
{isRunning && (
|
|
304
|
+
<div className="flex-shrink-0 flex items-center gap-3 px-4 h-8 border-b border-border-subtle bg-surface-1/80">
|
|
305
|
+
<div className="relative flex items-center justify-center w-4 h-4">
|
|
306
|
+
<span className="absolute inset-0 rounded-full bg-accent/15 animate-ping [animation-duration:2s]" />
|
|
307
|
+
<span className="relative w-1.5 h-1.5 rounded-full bg-accent" />
|
|
308
|
+
</div>
|
|
309
|
+
<span className="text-2xs font-sans text-text-2">
|
|
310
|
+
Lab Assistant is setting up <span className="font-medium text-text-1">{backend?.toUpperCase()}</span>
|
|
311
|
+
</span>
|
|
312
|
+
<div className="flex-1" />
|
|
313
|
+
<Badge variant="success" className="text-2xs">running</Badge>
|
|
101
314
|
</div>
|
|
102
|
-
|
|
103
|
-
onClick={dismissLabAssistant}
|
|
104
|
-
className="p-1 text-text-4 hover:text-text-1 transition-colors cursor-pointer"
|
|
105
|
-
>
|
|
106
|
-
<X size={14} />
|
|
107
|
-
</button>
|
|
108
|
-
</div>
|
|
315
|
+
)}
|
|
109
316
|
|
|
110
317
|
{/* Messages */}
|
|
111
318
|
<ScrollArea ref={scrollRef} className="flex-1 min-h-0">
|
|
112
|
-
<div className="px-
|
|
319
|
+
<div className="px-5 py-4 space-y-6">
|
|
113
320
|
{messages.length === 0 && !isThinking ? (
|
|
114
321
|
<div className="flex flex-col items-center justify-center py-20 text-center">
|
|
115
|
-
<div className="w-
|
|
116
|
-
<
|
|
322
|
+
<div className="w-12 h-12 rounded-lg bg-accent/10 flex items-center justify-center mb-3">
|
|
323
|
+
<FlaskConical size={22} className="text-accent" />
|
|
117
324
|
</div>
|
|
118
|
-
<p className="text-sm text-text-
|
|
119
|
-
<p className="text-
|
|
325
|
+
<p className="text-sm text-text-1 font-sans font-medium">Setting up {backend?.toUpperCase()}</p>
|
|
326
|
+
<p className="text-[13px] text-text-3 font-sans mt-1.5">The assistant is starting up and will begin configuring your runtime...</p>
|
|
120
327
|
</div>
|
|
121
328
|
) : (
|
|
122
329
|
messages.map((msg, i) =>
|
|
@@ -128,69 +335,96 @@ export function LabAssistant() {
|
|
|
128
335
|
)
|
|
129
336
|
)}
|
|
130
337
|
|
|
131
|
-
{isThinking &&
|
|
132
|
-
<div className="animate-chat-fade-in">
|
|
133
|
-
<div className="flex items-center gap-1.5 mb-1">
|
|
134
|
-
<div className="w-4 h-4 rounded-sm bg-surface-4 flex items-center justify-center">
|
|
135
|
-
<Bot size={10} className="text-text-3" />
|
|
136
|
-
</div>
|
|
137
|
-
<span className="text-2xs text-text-3 font-sans font-medium">Lab Assistant</span>
|
|
138
|
-
</div>
|
|
139
|
-
<div className="ml-5 flex items-center gap-1.5 py-1">
|
|
140
|
-
<span className="w-1 h-1 rounded-full bg-text-4 animate-pulse" />
|
|
141
|
-
<span className="w-1 h-1 rounded-full bg-text-4 animate-pulse" style={{ animationDelay: '150ms' }} />
|
|
142
|
-
<span className="w-1 h-1 rounded-full bg-text-4 animate-pulse" style={{ animationDelay: '300ms' }} />
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
)}
|
|
338
|
+
{isThinking && <LabThinkingIndicator />}
|
|
146
339
|
</div>
|
|
147
340
|
</ScrollArea>
|
|
148
341
|
|
|
149
342
|
{/* Completion banner */}
|
|
150
343
|
{isComplete && messages.length > 0 && (
|
|
151
|
-
<div className=
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
344
|
+
<div className={cn(
|
|
345
|
+
'flex-shrink-0 px-4 py-3 border-t',
|
|
346
|
+
activeRuntime && activeModel
|
|
347
|
+
? 'bg-success/10 border-success/20'
|
|
348
|
+
: activeRuntime
|
|
349
|
+
? 'bg-warning/10 border-warning/20'
|
|
350
|
+
: 'bg-surface-2 border-border',
|
|
351
|
+
)}>
|
|
352
|
+
{activeRuntime && activeModel ? (
|
|
353
|
+
<div className="flex items-center justify-between">
|
|
354
|
+
<div>
|
|
355
|
+
<span className="text-[13px] font-sans text-success font-semibold">Runtime ready</span>
|
|
356
|
+
<p className="text-xs text-text-2 font-sans mt-0.5">Your model is loaded and ready to chat</p>
|
|
357
|
+
</div>
|
|
358
|
+
<button
|
|
359
|
+
onClick={() => setLabAssistantMode(false)}
|
|
360
|
+
className="flex items-center gap-1.5 px-4 py-2 text-[13px] font-sans font-medium text-surface-0 bg-accent hover:bg-accent/90 rounded-md transition-colors cursor-pointer"
|
|
361
|
+
>
|
|
362
|
+
<ArrowRight size={14} /> Open Playground
|
|
363
|
+
</button>
|
|
364
|
+
</div>
|
|
365
|
+
) : activeRuntime ? (
|
|
366
|
+
<div className="flex items-center justify-between">
|
|
367
|
+
<div>
|
|
368
|
+
<span className="text-[13px] font-sans text-warning font-semibold">Runtime registered</span>
|
|
369
|
+
<p className="text-xs text-text-2 font-sans mt-0.5">Model may still be loading — test the runtime in the sidebar</p>
|
|
370
|
+
</div>
|
|
371
|
+
<button
|
|
372
|
+
onClick={() => setLabAssistantMode(false)}
|
|
373
|
+
className="flex items-center gap-1.5 px-4 py-2 text-[13px] font-sans font-medium text-text-0 bg-surface-3 hover:bg-surface-4 rounded-md transition-colors cursor-pointer"
|
|
374
|
+
>
|
|
375
|
+
<ArrowRight size={14} /> View Playground
|
|
376
|
+
</button>
|
|
377
|
+
</div>
|
|
378
|
+
) : (
|
|
379
|
+
<div className="flex items-center justify-between">
|
|
380
|
+
<div>
|
|
381
|
+
<span className="text-[13px] font-sans text-text-1 font-semibold">Setup finished</span>
|
|
382
|
+
<p className="text-xs text-text-2 font-sans mt-0.5">
|
|
383
|
+
If the model is still downloading, the runtime will appear once the server starts.
|
|
384
|
+
</p>
|
|
385
|
+
</div>
|
|
386
|
+
<button
|
|
387
|
+
onClick={fetchLabRuntimes}
|
|
388
|
+
className="flex items-center gap-1.5 px-4 py-2 text-[13px] font-sans font-medium text-text-0 bg-surface-3 hover:bg-surface-4 rounded-md transition-colors cursor-pointer"
|
|
389
|
+
>
|
|
390
|
+
Check Runtimes
|
|
391
|
+
</button>
|
|
392
|
+
</div>
|
|
393
|
+
)}
|
|
161
394
|
</div>
|
|
162
395
|
)}
|
|
163
396
|
|
|
164
397
|
{/* Input */}
|
|
165
398
|
<div className="flex-shrink-0 px-4 py-3">
|
|
166
|
-
<div className="flex items-end gap-2 bg-surface-1
|
|
399
|
+
<div className="flex items-end gap-2 border rounded-xl bg-surface-0 p-1.5 transition-colors border-border-subtle focus-within:border-accent/30">
|
|
167
400
|
<textarea
|
|
168
401
|
ref={inputRef}
|
|
169
402
|
value={input}
|
|
170
403
|
onChange={(e) => setInput(e.target.value)}
|
|
171
404
|
onKeyDown={handleKeyDown}
|
|
172
|
-
placeholder={isRunning ? '
|
|
173
|
-
disabled={!isRunning}
|
|
405
|
+
placeholder={isRunning ? 'Ask the assistant anything...' : 'Send a message to continue...'}
|
|
174
406
|
rows={1}
|
|
175
407
|
className={cn(
|
|
176
|
-
'flex-1 resize-none bg-transparent px-2 py-1.5',
|
|
177
|
-
'text-
|
|
408
|
+
'flex-1 resize-none bg-transparent px-2.5 py-1.5',
|
|
409
|
+
'text-[13px] text-text-0 font-sans placeholder:text-text-4',
|
|
178
410
|
'focus:outline-none',
|
|
179
|
-
'
|
|
180
|
-
'min-h-[28px] max-h-32',
|
|
411
|
+
'min-h-[32px] max-h-32',
|
|
181
412
|
)}
|
|
182
413
|
style={{ height: 'auto', overflowY: input.split('\n').length > 4 ? 'auto' : 'hidden' }}
|
|
183
414
|
onInput={(e) => { e.target.style.height = 'auto'; e.target.style.height = `${Math.min(e.target.scrollHeight, 128)}px`; }}
|
|
184
415
|
/>
|
|
185
416
|
<button
|
|
186
|
-
disabled={!input.trim()
|
|
417
|
+
disabled={!input.trim()}
|
|
187
418
|
onClick={handleSend}
|
|
188
419
|
className={cn(
|
|
189
|
-
'flex-shrink-0 w-
|
|
190
|
-
|
|
420
|
+
'flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-lg transition-all cursor-pointer',
|
|
421
|
+
'disabled:opacity-15 disabled:cursor-not-allowed',
|
|
422
|
+
input.trim()
|
|
423
|
+
? 'bg-accent/15 text-accent hover:bg-accent/25 border border-accent/25'
|
|
424
|
+
: 'bg-transparent text-text-4',
|
|
191
425
|
)}
|
|
192
426
|
>
|
|
193
|
-
<Send size={
|
|
427
|
+
<Send size={13} />
|
|
194
428
|
</button>
|
|
195
429
|
</div>
|
|
196
430
|
</div>
|