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,29 +4,29 @@ import { Tooltip } from '../ui/tooltip';
|
|
|
4
4
|
import { cn } from '../../lib/cn';
|
|
5
5
|
import { fmtNum } from '../../lib/format';
|
|
6
6
|
import { HEX, hexAlpha } from '../../lib/theme-hex';
|
|
7
|
-
import { Zap, Clock, Cpu, Hash, Timer, Link } from 'lucide-react';
|
|
7
|
+
import { Zap, Clock, Cpu, Hash, Timer, Link, MessageSquare, TrendingUp, ArrowUpRight, ArrowDownRight, Layers } from 'lucide-react';
|
|
8
8
|
|
|
9
9
|
function TtftGauge({ value }) {
|
|
10
10
|
if (value == null) {
|
|
11
11
|
return (
|
|
12
|
-
<div className="text-center py-
|
|
12
|
+
<div className="text-center py-3">
|
|
13
13
|
<div className="text-2xl font-mono font-bold text-text-4 tabular-nums">--</div>
|
|
14
|
-
<div className="text-2xs text-text-4 font-sans mt-
|
|
14
|
+
<div className="text-2xs text-text-4 font-sans mt-0.5">TTFT</div>
|
|
15
15
|
</div>
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
18
|
const color = value < 200 ? 'text-success' : value < 500 ? 'text-warning' : 'text-danger';
|
|
19
19
|
return (
|
|
20
|
-
<div className="text-center py-
|
|
20
|
+
<div className="text-center py-3">
|
|
21
21
|
<div className={cn('text-2xl font-mono font-bold tabular-nums', color)}>
|
|
22
22
|
{Math.round(value)}
|
|
23
23
|
</div>
|
|
24
|
-
<div className="text-2xs text-text-3 font-sans mt-
|
|
24
|
+
<div className="text-2xs text-text-3 font-sans mt-0.5">ms TTFT</div>
|
|
25
25
|
</div>
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function Sparkline({ data, width = 140, height =
|
|
29
|
+
function Sparkline({ data, width = 140, height = 28, color = HEX.accent }) {
|
|
30
30
|
if (!data || data.length < 2) {
|
|
31
31
|
return <div className="flex-shrink-0" style={{ width, height }} />;
|
|
32
32
|
}
|
|
@@ -54,11 +54,11 @@ function Sparkline({ data, width = 140, height = 32, color = HEX.accent }) {
|
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function MetricRow({ icon: Icon, label, value, unit, tooltip }) {
|
|
57
|
+
function MetricRow({ icon: Icon, label, value, unit, tooltip, accent }) {
|
|
58
58
|
const content = (
|
|
59
|
-
<div className="flex items-center justify-between py-
|
|
59
|
+
<div className="flex items-center justify-between py-1.5">
|
|
60
60
|
<div className="flex items-center gap-2">
|
|
61
|
-
<Icon size={11} className=
|
|
61
|
+
<Icon size={11} className={cn('flex-shrink-0', accent ? 'text-accent' : 'text-text-4')} />
|
|
62
62
|
<span className="text-2xs text-text-3 font-sans">{label}</span>
|
|
63
63
|
</div>
|
|
64
64
|
<span className="text-xs font-mono font-medium text-text-1 tabular-nums">
|
|
@@ -69,48 +69,203 @@ function MetricRow({ icon: Icon, label, value, unit, tooltip }) {
|
|
|
69
69
|
return tooltip ? <Tooltip content={tooltip}>{content}</Tooltip> : content;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
function SectionLabel({ children }) {
|
|
73
|
+
return (
|
|
74
|
+
<span className="text-2xs font-semibold font-sans text-text-4 uppercase tracking-wider">{children}</span>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function CapacityBar({ used, total, label }) {
|
|
79
|
+
if (!total) return null;
|
|
80
|
+
const pct = Math.min((used / total) * 100, 100);
|
|
81
|
+
const color = pct < 50 ? 'bg-success' : pct < 80 ? 'bg-warning' : 'bg-danger';
|
|
82
|
+
return (
|
|
83
|
+
<div className="space-y-1">
|
|
84
|
+
<div className="flex items-center justify-between">
|
|
85
|
+
<span className="text-2xs text-text-3 font-sans">{label}</span>
|
|
86
|
+
<span className="text-2xs font-mono text-text-2 tabular-nums">{Math.round(pct)}%</span>
|
|
87
|
+
</div>
|
|
88
|
+
<div className="h-1.5 bg-surface-3 rounded-full overflow-hidden">
|
|
89
|
+
<div className={cn('h-full rounded-full transition-all duration-300', color)} style={{ width: `${pct}%` }} />
|
|
90
|
+
</div>
|
|
91
|
+
<div className="text-2xs font-mono text-text-4 tabular-nums">
|
|
92
|
+
{fmtNum(used)} / {fmtNum(total)}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function SparklineSection({ icon: Icon, label, value, unit, data, color = HEX.accent }) {
|
|
99
|
+
return (
|
|
100
|
+
<div className="space-y-1.5">
|
|
101
|
+
<div className="flex items-center justify-between">
|
|
102
|
+
<div className="flex items-center gap-1.5">
|
|
103
|
+
<Icon size={11} className="text-accent" />
|
|
104
|
+
<span className="text-2xs text-text-3 font-sans">{label}</span>
|
|
105
|
+
</div>
|
|
106
|
+
<span className="text-sm font-mono font-bold text-text-0 tabular-nums">
|
|
107
|
+
{value != null ? value : '--'}{unit && value != null ? <span className="text-text-4 ml-0.5 text-xs">{unit}</span> : ''}
|
|
108
|
+
</span>
|
|
109
|
+
</div>
|
|
110
|
+
<Sparkline data={data} color={color} />
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
72
115
|
export function MetricsPanel() {
|
|
73
116
|
const metrics = useGrooveStore((s) => s.labMetrics);
|
|
74
117
|
const activeRuntime = useGrooveStore((s) => s.labActiveRuntime);
|
|
118
|
+
const activeSession = useGrooveStore((s) => s.labActiveSession);
|
|
119
|
+
const sessions = useGrooveStore((s) => s.labSessions);
|
|
120
|
+
|
|
121
|
+
const session = sessions.find((s) => s.id === activeSession);
|
|
122
|
+
const messageCount = session?.messages?.length || 0;
|
|
123
|
+
const generationCount = metrics.generationCount || 0;
|
|
124
|
+
const avgTps = metrics.tokensPerSecHistory.length > 0
|
|
125
|
+
? (metrics.tokensPerSecHistory.reduce((a, b) => a + (b || 0), 0) / metrics.tokensPerSecHistory.length)
|
|
126
|
+
: null;
|
|
127
|
+
const avgTtft = metrics.ttftHistory?.length > 0
|
|
128
|
+
? (metrics.ttftHistory.reduce((a, b) => a + (b || 0), 0) / metrics.ttftHistory.length)
|
|
129
|
+
: null;
|
|
130
|
+
const peakTps = metrics.tokensPerSecHistory.length > 0
|
|
131
|
+
? Math.max(...metrics.tokensPerSecHistory.filter((v) => v != null))
|
|
132
|
+
: null;
|
|
133
|
+
|
|
134
|
+
const sessionDuration = metrics.sessionStartTime
|
|
135
|
+
? Math.round((Date.now() - metrics.sessionStartTime) / 1000)
|
|
136
|
+
: null;
|
|
137
|
+
|
|
138
|
+
function fmtDuration(secs) {
|
|
139
|
+
if (secs == null) return null;
|
|
140
|
+
if (secs < 60) return `${secs}s`;
|
|
141
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m ${secs % 60}s`;
|
|
142
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
|
|
143
|
+
}
|
|
75
144
|
|
|
76
145
|
return (
|
|
77
|
-
<div className="space-y-
|
|
78
|
-
{/*
|
|
146
|
+
<div className="space-y-4">
|
|
147
|
+
{/* Performance */}
|
|
79
148
|
<TtftGauge value={metrics.ttft} />
|
|
80
149
|
|
|
150
|
+
<SparklineSection
|
|
151
|
+
icon={Zap}
|
|
152
|
+
label="Tokens/sec"
|
|
153
|
+
value={metrics.tokensPerSec != null ? metrics.tokensPerSec.toFixed(1) : null}
|
|
154
|
+
data={metrics.tokensPerSecHistory}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
{metrics.ttftHistory?.length > 1 && (
|
|
158
|
+
<SparklineSection
|
|
159
|
+
icon={Clock}
|
|
160
|
+
label="TTFT Trend"
|
|
161
|
+
value={metrics.ttft != null ? `${Math.round(metrics.ttft)}` : null}
|
|
162
|
+
unit="ms"
|
|
163
|
+
data={metrics.ttftHistory}
|
|
164
|
+
color={HEX.warning || HEX.accent}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
|
|
81
168
|
<div className="h-px bg-border-subtle" />
|
|
82
169
|
|
|
83
|
-
{/*
|
|
84
|
-
<div className="space-y-
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
170
|
+
{/* Token Usage */}
|
|
171
|
+
<div className="space-y-1">
|
|
172
|
+
<SectionLabel>Tokens</SectionLabel>
|
|
173
|
+
<MetricRow
|
|
174
|
+
icon={Hash}
|
|
175
|
+
label="Total"
|
|
176
|
+
value={metrics.totalTokens > 0 ? fmtNum(metrics.totalTokens) : null}
|
|
177
|
+
tooltip="Total tokens generated this session"
|
|
178
|
+
/>
|
|
179
|
+
{metrics.promptTokens > 0 && (
|
|
180
|
+
<MetricRow
|
|
181
|
+
icon={ArrowUpRight}
|
|
182
|
+
label="Prompt"
|
|
183
|
+
value={fmtNum(metrics.promptTokens)}
|
|
184
|
+
tooltip="Input/prompt tokens"
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
{metrics.completionTokens > 0 && (
|
|
188
|
+
<MetricRow
|
|
189
|
+
icon={ArrowDownRight}
|
|
190
|
+
label="Completion"
|
|
191
|
+
value={fmtNum(metrics.completionTokens)}
|
|
192
|
+
tooltip="Output/completion tokens"
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
95
195
|
</div>
|
|
96
196
|
|
|
97
197
|
<div className="h-px bg-border-subtle" />
|
|
98
198
|
|
|
99
|
-
{/* Stats */}
|
|
100
|
-
<div className="space-y-
|
|
199
|
+
{/* Session Stats */}
|
|
200
|
+
<div className="space-y-1">
|
|
201
|
+
<SectionLabel>Session</SectionLabel>
|
|
202
|
+
<MetricRow
|
|
203
|
+
icon={MessageSquare}
|
|
204
|
+
label="Messages"
|
|
205
|
+
value={messageCount > 0 ? messageCount : null}
|
|
206
|
+
tooltip="Messages in current session"
|
|
207
|
+
/>
|
|
208
|
+
<MetricRow
|
|
209
|
+
icon={Layers}
|
|
210
|
+
label="Generations"
|
|
211
|
+
value={generationCount > 0 ? generationCount : null}
|
|
212
|
+
tooltip="Number of model generations"
|
|
213
|
+
/>
|
|
214
|
+
{avgTps != null && (
|
|
215
|
+
<MetricRow
|
|
216
|
+
icon={TrendingUp}
|
|
217
|
+
label="Avg TPS"
|
|
218
|
+
value={avgTps.toFixed(1)}
|
|
219
|
+
tooltip="Average tokens/sec across session"
|
|
220
|
+
/>
|
|
221
|
+
)}
|
|
222
|
+
{avgTtft != null && (
|
|
223
|
+
<MetricRow
|
|
224
|
+
icon={Clock}
|
|
225
|
+
label="Avg TTFT"
|
|
226
|
+
value={`${Math.round(avgTtft)}`}
|
|
227
|
+
unit="ms"
|
|
228
|
+
tooltip="Average time to first token"
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
{peakTps != null && (
|
|
232
|
+
<MetricRow
|
|
233
|
+
icon={Zap}
|
|
234
|
+
label="Peak TPS"
|
|
235
|
+
value={peakTps.toFixed(1)}
|
|
236
|
+
accent
|
|
237
|
+
tooltip="Highest tokens/sec this session"
|
|
238
|
+
/>
|
|
239
|
+
)}
|
|
240
|
+
<MetricRow
|
|
241
|
+
icon={Timer}
|
|
242
|
+
label="Duration"
|
|
243
|
+
value={fmtDuration(sessionDuration)}
|
|
244
|
+
tooltip="Time since first generation"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<div className="h-px bg-border-subtle" />
|
|
249
|
+
|
|
250
|
+
{/* Resources */}
|
|
251
|
+
<div className="space-y-1">
|
|
252
|
+
<SectionLabel>Resources</SectionLabel>
|
|
101
253
|
<MetricRow
|
|
102
254
|
icon={Cpu}
|
|
103
255
|
label="Memory"
|
|
104
256
|
value={metrics.memory != null ? `${(metrics.memory / 1024 / 1024).toFixed(0)}` : null}
|
|
105
257
|
unit="MB"
|
|
106
|
-
tooltip="GPU/CPU memory usage"
|
|
107
|
-
/>
|
|
108
|
-
<MetricRow
|
|
109
|
-
icon={Hash}
|
|
110
|
-
label="Total Tokens"
|
|
111
|
-
value={metrics.totalTokens > 0 ? fmtNum(metrics.totalTokens) : null}
|
|
112
|
-
tooltip="Total tokens generated this session"
|
|
258
|
+
tooltip="Current GPU/CPU memory usage"
|
|
113
259
|
/>
|
|
260
|
+
{metrics.peakMemory != null && metrics.peakMemory > 0 && (
|
|
261
|
+
<MetricRow
|
|
262
|
+
icon={Cpu}
|
|
263
|
+
label="Peak Memory"
|
|
264
|
+
value={`${(metrics.peakMemory / 1024 / 1024).toFixed(0)}`}
|
|
265
|
+
unit="MB"
|
|
266
|
+
tooltip="Peak memory usage this session"
|
|
267
|
+
/>
|
|
268
|
+
)}
|
|
114
269
|
<MetricRow
|
|
115
270
|
icon={Timer}
|
|
116
271
|
label="Gen Time"
|
|
@@ -1,24 +1,156 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { useGrooveStore } from '../../stores/groove';
|
|
4
|
+
import { SidebarSection } from '../../views/model-lab';
|
|
3
5
|
import { TuningSlider } from '../ui/slider';
|
|
4
6
|
import { Tooltip } from '../ui/tooltip';
|
|
5
|
-
import { RotateCcw } from 'lucide-react';
|
|
7
|
+
import { RotateCcw, ChevronRight, Dices, X, Plus } from 'lucide-react';
|
|
8
|
+
import { cn } from '../../lib/cn';
|
|
6
9
|
|
|
7
10
|
const DEFAULTS = {
|
|
8
|
-
temperature: 0.7, topP: 0.9, topK: 40, repeatPenalty: 1.1,
|
|
11
|
+
temperature: 0.7, topP: 0.9, topK: 40, minP: 0, repeatPenalty: 1.1,
|
|
9
12
|
maxTokens: 2048, frequencyPenalty: 0, presencePenalty: 0,
|
|
13
|
+
thinking: false, seed: null, stopSequences: [], jsonMode: false,
|
|
10
14
|
};
|
|
11
15
|
|
|
12
|
-
const
|
|
16
|
+
const SAMPLING_SLIDERS = [
|
|
13
17
|
{ key: 'temperature', label: 'Temperature', min: 0, max: 2, step: 0.01 },
|
|
14
18
|
{ key: 'topP', label: 'Top P', min: 0, max: 1, step: 0.01 },
|
|
19
|
+
{ key: 'minP', label: 'Min P', min: 0, max: 1, step: 0.01 },
|
|
15
20
|
{ key: 'topK', label: 'Top K', min: 0, max: 200, step: 1 },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const PENALTY_SLIDERS = [
|
|
16
24
|
{ key: 'repeatPenalty', label: 'Repeat Penalty', min: 0, max: 2, step: 0.01 },
|
|
17
|
-
{ key: 'maxTokens', label: 'Max Tokens', min: 1, max: 32768, step: 1, format: (v) => v >= 1000 ? `${(v / 1000).toFixed(1)}k` : v },
|
|
18
25
|
{ key: 'frequencyPenalty', label: 'Freq Penalty', min: 0, max: 2, step: 0.01 },
|
|
19
26
|
{ key: 'presencePenalty', label: 'Pres Penalty', min: 0, max: 2, step: 0.01 },
|
|
20
27
|
];
|
|
21
28
|
|
|
29
|
+
function ParamGroup({ title, defaultOpen = false, children }) {
|
|
30
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<button
|
|
34
|
+
onClick={() => setOpen(!open)}
|
|
35
|
+
className="w-full flex items-center gap-1.5 h-7 text-left cursor-pointer group"
|
|
36
|
+
>
|
|
37
|
+
<ChevronRight
|
|
38
|
+
size={10}
|
|
39
|
+
className={cn('text-text-4 transition-transform duration-150 flex-shrink-0', open && 'rotate-90')}
|
|
40
|
+
/>
|
|
41
|
+
<span className="text-[10px] font-semibold text-text-4 font-sans uppercase tracking-widest">{title}</span>
|
|
42
|
+
</button>
|
|
43
|
+
{open && <div className="space-y-1 pl-0.5 pt-1">{children}</div>}
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function ToggleSwitch({ label, active, onClick, description }) {
|
|
49
|
+
return (
|
|
50
|
+
<Tooltip content={description} side="right">
|
|
51
|
+
<button
|
|
52
|
+
onClick={onClick}
|
|
53
|
+
className="w-full flex items-center gap-2.5 h-9 text-left cursor-pointer group"
|
|
54
|
+
>
|
|
55
|
+
<span className="text-[11px] font-sans text-text-2 flex-1">{label}</span>
|
|
56
|
+
<div className={cn(
|
|
57
|
+
'w-7 h-[16px] rounded-full transition-colors relative flex-shrink-0',
|
|
58
|
+
active ? 'bg-accent' : 'bg-surface-5',
|
|
59
|
+
)}>
|
|
60
|
+
<div className={cn(
|
|
61
|
+
'absolute top-[2px] w-3 h-3 rounded-full bg-white shadow-sm transition-all',
|
|
62
|
+
active ? 'left-[14px]' : 'left-[2px]',
|
|
63
|
+
)} />
|
|
64
|
+
</div>
|
|
65
|
+
</button>
|
|
66
|
+
</Tooltip>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function SeedInput({ value, onChange }) {
|
|
71
|
+
function handleRandomize() {
|
|
72
|
+
onChange(Math.floor(Math.random() * 2147483647));
|
|
73
|
+
}
|
|
74
|
+
return (
|
|
75
|
+
<div className="space-y-2 pt-1.5">
|
|
76
|
+
<span className="text-[11px] text-text-2 font-sans">Seed</span>
|
|
77
|
+
<div className="flex items-center gap-1.5">
|
|
78
|
+
<input
|
|
79
|
+
type="number"
|
|
80
|
+
min={0}
|
|
81
|
+
value={value ?? ''}
|
|
82
|
+
onChange={(e) => onChange(e.target.value === '' ? null : parseInt(e.target.value, 10))}
|
|
83
|
+
placeholder="Random"
|
|
84
|
+
className="flex-1 min-w-0 h-7 px-2.5 text-[11px] font-mono bg-surface-1 border border-border rounded text-text-1 placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent/50 focus:border-accent/50 tabular-nums"
|
|
85
|
+
/>
|
|
86
|
+
<Tooltip content="Randomize seed">
|
|
87
|
+
<button onClick={handleRandomize} className="p-0.5 text-text-4 hover:text-accent transition-colors cursor-pointer">
|
|
88
|
+
<Dices size={12} />
|
|
89
|
+
</button>
|
|
90
|
+
</Tooltip>
|
|
91
|
+
{value != null && (
|
|
92
|
+
<button onClick={() => onChange(null)} className="p-0.5 text-text-4 hover:text-danger transition-colors cursor-pointer">
|
|
93
|
+
<X size={10} />
|
|
94
|
+
</button>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function StopSequenceInput({ sequences, onChange }) {
|
|
102
|
+
const [input, setInput] = useState('');
|
|
103
|
+
|
|
104
|
+
function handleAdd() {
|
|
105
|
+
const val = input.trim();
|
|
106
|
+
if (!val || sequences.length >= 10 || sequences.includes(val)) return;
|
|
107
|
+
onChange([...sequences, val]);
|
|
108
|
+
setInput('');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function handleRemove(idx) {
|
|
112
|
+
onChange(sequences.filter((_, i) => i !== idx));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function handleKeyDown(e) {
|
|
116
|
+
if (e.key === 'Enter') { e.preventDefault(); handleAdd(); }
|
|
117
|
+
if (e.key === 'Backspace' && !input && sequences.length) handleRemove(sequences.length - 1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div className="space-y-2 pt-1.5">
|
|
122
|
+
<span className="text-[11px] text-text-2 font-sans">Stop Sequences</span>
|
|
123
|
+
<div className="flex items-center gap-1.5">
|
|
124
|
+
<input
|
|
125
|
+
value={input}
|
|
126
|
+
onChange={(e) => setInput(e.target.value)}
|
|
127
|
+
onKeyDown={handleKeyDown}
|
|
128
|
+
placeholder="Add sequence..."
|
|
129
|
+
maxLength={100}
|
|
130
|
+
className="flex-1 min-w-0 h-7 px-2.5 text-[11px] font-mono bg-surface-1 border border-border rounded text-text-1 placeholder:text-text-4 focus:outline-none focus:ring-1 focus:ring-accent/50 focus:border-accent/50"
|
|
131
|
+
/>
|
|
132
|
+
<button
|
|
133
|
+
onClick={handleAdd}
|
|
134
|
+
disabled={!input.trim() || sequences.length >= 10}
|
|
135
|
+
className="p-0.5 text-text-4 hover:text-accent disabled:opacity-30 transition-colors cursor-pointer"
|
|
136
|
+
>
|
|
137
|
+
<Plus size={12} />
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
{sequences.length > 0 && (
|
|
141
|
+
<div className="flex flex-wrap gap-1">
|
|
142
|
+
{sequences.map((s, i) => (
|
|
143
|
+
<span key={i} className="inline-flex items-center gap-1 px-1.5 py-0.5 bg-surface-2 border border-border-subtle rounded text-[10px] font-mono text-text-2">
|
|
144
|
+
{s.length > 12 ? s.slice(0, 12) + '...' : s}
|
|
145
|
+
<button onClick={() => handleRemove(i)} className="text-text-4 hover:text-danger cursor-pointer"><X size={8} /></button>
|
|
146
|
+
</span>
|
|
147
|
+
))}
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
22
154
|
export function ParameterPanel() {
|
|
23
155
|
const parameters = useGrooveStore((s) => s.labParameters);
|
|
24
156
|
const setParameter = useGrooveStore((s) => s.setLabParameter);
|
|
@@ -30,9 +162,11 @@ export function ParameterPanel() {
|
|
|
30
162
|
}
|
|
31
163
|
|
|
32
164
|
return (
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
165
|
+
<SidebarSection
|
|
166
|
+
label="Parameters"
|
|
167
|
+
collapsible
|
|
168
|
+
defaultOpen={false}
|
|
169
|
+
action={
|
|
36
170
|
<Tooltip content="Reset to defaults">
|
|
37
171
|
<button
|
|
38
172
|
onClick={handleReset}
|
|
@@ -41,19 +175,67 @@ export function ParameterPanel() {
|
|
|
41
175
|
<RotateCcw size={11} />
|
|
42
176
|
</button>
|
|
43
177
|
</Tooltip>
|
|
178
|
+
}
|
|
179
|
+
>
|
|
180
|
+
<div className="space-y-0.5 rounded-md bg-surface-1/50 border border-border-subtle px-3 py-2">
|
|
181
|
+
<ToggleSwitch
|
|
182
|
+
label="Thinking"
|
|
183
|
+
active={parameters.thinking}
|
|
184
|
+
onClick={() => setParameter('thinking', !parameters.thinking)}
|
|
185
|
+
description="Chain-of-thought for supported models (Qwen3, etc.)"
|
|
186
|
+
/>
|
|
187
|
+
<ToggleSwitch
|
|
188
|
+
label="JSON Mode"
|
|
189
|
+
active={parameters.jsonMode}
|
|
190
|
+
onClick={() => setParameter('jsonMode', !parameters.jsonMode)}
|
|
191
|
+
description="Constrain output to valid JSON"
|
|
192
|
+
/>
|
|
44
193
|
</div>
|
|
45
|
-
|
|
194
|
+
|
|
195
|
+
<ParamGroup title="Sampling">
|
|
196
|
+
{SAMPLING_SLIDERS.map((s) => (
|
|
197
|
+
<TuningSlider
|
|
198
|
+
key={s.key}
|
|
199
|
+
label={s.label}
|
|
200
|
+
value={parameters[s.key]}
|
|
201
|
+
onChange={(v) => setParameter(s.key, v)}
|
|
202
|
+
min={s.min}
|
|
203
|
+
max={s.max}
|
|
204
|
+
step={s.step}
|
|
205
|
+
/>
|
|
206
|
+
))}
|
|
207
|
+
</ParamGroup>
|
|
208
|
+
|
|
209
|
+
<ParamGroup title="Generation">
|
|
46
210
|
<TuningSlider
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
formatValue={s.format}
|
|
211
|
+
label="Max Tokens"
|
|
212
|
+
value={parameters.maxTokens}
|
|
213
|
+
onChange={(v) => setParameter('maxTokens', v)}
|
|
214
|
+
min={1}
|
|
215
|
+
max={32768}
|
|
216
|
+
step={1}
|
|
217
|
+
formatValue={(v) => v >= 1000 ? `${(v / 1000).toFixed(1)}k` : v}
|
|
55
218
|
/>
|
|
56
|
-
|
|
57
|
-
|
|
219
|
+
<SeedInput value={parameters.seed} onChange={(v) => setParameter('seed', v)} />
|
|
220
|
+
<StopSequenceInput
|
|
221
|
+
sequences={parameters.stopSequences || []}
|
|
222
|
+
onChange={(v) => setParameter('stopSequences', v)}
|
|
223
|
+
/>
|
|
224
|
+
</ParamGroup>
|
|
225
|
+
|
|
226
|
+
<ParamGroup title="Penalties" defaultOpen={false}>
|
|
227
|
+
{PENALTY_SLIDERS.map((s) => (
|
|
228
|
+
<TuningSlider
|
|
229
|
+
key={s.key}
|
|
230
|
+
label={s.label}
|
|
231
|
+
value={parameters[s.key]}
|
|
232
|
+
onChange={(v) => setParameter(s.key, v)}
|
|
233
|
+
min={s.min}
|
|
234
|
+
max={s.max}
|
|
235
|
+
step={s.step}
|
|
236
|
+
/>
|
|
237
|
+
))}
|
|
238
|
+
</ParamGroup>
|
|
239
|
+
</SidebarSection>
|
|
58
240
|
);
|
|
59
241
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
2
|
import { useState } 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 { Input } from '../ui/input';
|
|
6
7
|
import { Dialog, DialogContent } from '../ui/dialog';
|
|
@@ -54,33 +55,35 @@ export function PresetManager() {
|
|
|
54
55
|
const [saveOpen, setSaveOpen] = useState(false);
|
|
55
56
|
|
|
56
57
|
return (
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
<SidebarSection
|
|
59
|
+
label="Presets"
|
|
60
|
+
collapsible
|
|
61
|
+
defaultOpen={false}
|
|
62
|
+
action={
|
|
60
63
|
<Tooltip content="Save current settings as preset">
|
|
61
64
|
<button
|
|
62
65
|
onClick={() => setSaveOpen(true)}
|
|
63
66
|
className="p-1 text-text-4 hover:text-accent transition-colors cursor-pointer"
|
|
64
67
|
>
|
|
65
|
-
<Save size={
|
|
68
|
+
<Save size={12} />
|
|
66
69
|
</button>
|
|
67
70
|
</Tooltip>
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
}
|
|
72
|
+
>
|
|
70
73
|
{presets.length === 0 ? (
|
|
71
|
-
<div className="py-
|
|
72
|
-
<BookmarkCheck size={14} className="mx-auto text-text-4 mb-1" />
|
|
73
|
-
<p className="text-
|
|
74
|
+
<div className="py-5 text-center rounded-md bg-surface-1/50 border border-border-subtle">
|
|
75
|
+
<BookmarkCheck size={14} className="mx-auto text-text-4 mb-1.5" />
|
|
76
|
+
<p className="text-[10px] text-text-4 font-sans">No presets saved</p>
|
|
74
77
|
</div>
|
|
75
78
|
) : (
|
|
76
79
|
<ScrollArea className="max-h-32">
|
|
77
|
-
<div className="space-y-
|
|
80
|
+
<div className="space-y-1 rounded-md bg-surface-1/50 border border-border-subtle p-2">
|
|
78
81
|
{presets.map((preset) => (
|
|
79
82
|
<div
|
|
80
83
|
key={preset.id}
|
|
81
84
|
className={cn(
|
|
82
|
-
'flex items-center gap-2 px-2.5 py-
|
|
83
|
-
activePreset === preset.id ? 'bg-accent/
|
|
85
|
+
'flex items-center gap-2.5 px-2.5 py-2 rounded transition-colors',
|
|
86
|
+
activePreset === preset.id ? 'bg-accent/10' : 'hover:bg-surface-3',
|
|
84
87
|
)}
|
|
85
88
|
>
|
|
86
89
|
<button
|
|
@@ -88,7 +91,7 @@ export function PresetManager() {
|
|
|
88
91
|
className="flex-1 text-left min-w-0 cursor-pointer"
|
|
89
92
|
>
|
|
90
93
|
<div className={cn(
|
|
91
|
-
'text-
|
|
94
|
+
'text-[11px] font-sans truncate',
|
|
92
95
|
activePreset === preset.id ? 'text-accent font-medium' : 'text-text-2',
|
|
93
96
|
)}>
|
|
94
97
|
{preset.name}
|
|
@@ -109,6 +112,6 @@ export function PresetManager() {
|
|
|
109
112
|
)}
|
|
110
113
|
|
|
111
114
|
<SavePresetDialog open={saveOpen} onOpenChange={setSaveOpen} />
|
|
112
|
-
</
|
|
115
|
+
</SidebarSection>
|
|
113
116
|
);
|
|
114
117
|
}
|