groove-dev 0.27.7 → 0.27.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/CLAUDE.md +0 -7
- package/node_modules/@groove-dev/daemon/src/api.js +496 -44
- package/node_modules/@groove-dev/daemon/src/gateways/manager.js +25 -12
- package/node_modules/@groove-dev/daemon/src/index.js +7 -0
- package/node_modules/@groove-dev/daemon/src/introducer.js +72 -4
- package/node_modules/@groove-dev/daemon/src/journalist.js +66 -11
- package/node_modules/@groove-dev/daemon/src/process.js +128 -104
- package/node_modules/@groove-dev/daemon/src/registry.js +1 -1
- package/node_modules/@groove-dev/daemon/src/repo-import.js +541 -0
- package/node_modules/@groove-dev/daemon/src/rotator.js +28 -1
- package/node_modules/@groove-dev/daemon/src/supervisor.js +2 -1
- package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +504 -0
- package/node_modules/@groove-dev/daemon/src/validate.js +13 -0
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +5 -4
- package/node_modules/@groove-dev/daemon/test/rotator.test.js +4 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-BE6lYcd7.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-zdzOLAZM.js +677 -0
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/src/app.css +14 -0
- package/node_modules/@groove-dev/gui/src/app.jsx +13 -0
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +130 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/agent-mdfiles.jsx +43 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-node.jsx +16 -17
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +141 -1
- package/node_modules/@groove-dev/gui/src/components/dashboard/fleet-panel.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/dashboard/intel-panel.jsx +8 -8
- 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/layout/activity-bar.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +7 -1
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +26 -8
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +14 -4
- package/node_modules/@groove-dev/gui/src/components/layout/status-bar.jsx +46 -11
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-card.jsx +64 -0
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-import.jsx +363 -0
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-nuke-dialog.jsx +68 -0
- package/node_modules/@groove-dev/gui/src/components/pro/pro-gate.jsx +22 -0
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-card.jsx +48 -0
- package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +129 -0
- package/node_modules/@groove-dev/gui/src/components/settings/remote-server-card.jsx +243 -0
- package/node_modules/@groove-dev/gui/src/components/settings/server-dialog.jsx +192 -0
- package/node_modules/@groove-dev/gui/src/components/ui/approval-modal.jsx +63 -0
- package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +1 -1
- package/node_modules/@groove-dev/gui/src/lib/edition.js +4 -0
- package/node_modules/@groove-dev/gui/src/lib/electron.js +17 -0
- package/node_modules/@groove-dev/gui/src/lib/status.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +150 -6
- package/node_modules/@groove-dev/gui/src/views/dashboard.jsx +39 -40
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +82 -0
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +66 -0
- package/node_modules/@groove-dev/gui/vite.config.js +3 -0
- package/package.json +7 -2
- package/packages/daemon/src/api.js +496 -44
- package/packages/daemon/src/gateways/manager.js +25 -12
- package/packages/daemon/src/index.js +7 -0
- package/packages/daemon/src/introducer.js +72 -4
- package/packages/daemon/src/journalist.js +66 -11
- package/packages/daemon/src/process.js +128 -104
- package/packages/daemon/src/registry.js +1 -1
- package/packages/daemon/src/repo-import.js +541 -0
- package/packages/daemon/src/rotator.js +28 -1
- package/packages/daemon/src/supervisor.js +2 -1
- package/packages/daemon/src/tunnel-manager.js +504 -0
- package/packages/daemon/src/validate.js +13 -0
- package/packages/gui/dist/assets/index-BE6lYcd7.css +1 -0
- package/packages/gui/dist/assets/index-zdzOLAZM.js +677 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-html.js +3 -3
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-javascript.js +2 -2
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-markdown.js +3 -3
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-python.js +5 -5
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-dialog.js +3 -3
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-scroll-area.js +1 -1
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tabs.js +5 -5
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tooltip.js +3 -3
- package/packages/gui/node_modules/.vite/deps/_metadata.json +53 -53
- package/packages/gui/node_modules/.vite/deps/{chunk-WYSQD5ZG.js → chunk-DH7AESXW.js} +2 -2
- package/packages/gui/node_modules/.vite/deps/{chunk-KXLIKZFX.js → chunk-GFE3G4IN.js} +133 -133
- package/packages/gui/node_modules/.vite/deps/chunk-GFE3G4IN.js.map +7 -0
- package/packages/gui/node_modules/.vite/deps/{chunk-3LBP22MX.js → chunk-LKZVMLRH.js} +6 -6
- package/packages/gui/node_modules/.vite/deps/{chunk-J6DMOQWP.js → chunk-MCVDVNE5.js} +2 -2
- package/packages/gui/node_modules/.vite/deps/{chunk-3Q7HT7ZF.js → chunk-SPKVQGZX.js} +6 -6
- package/packages/gui/src/app.css +14 -0
- package/packages/gui/src/app.jsx +13 -0
- package/packages/gui/src/components/agents/agent-config.jsx +130 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +2 -2
- package/packages/gui/src/components/agents/agent-mdfiles.jsx +43 -1
- package/packages/gui/src/components/agents/agent-node.jsx +16 -17
- package/packages/gui/src/components/agents/spawn-wizard.jsx +141 -1
- package/packages/gui/src/components/dashboard/fleet-panel.jsx +3 -3
- package/packages/gui/src/components/dashboard/intel-panel.jsx +8 -8
- 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/layout/activity-bar.jsx +4 -4
- package/packages/gui/src/components/layout/app-shell.jsx +7 -1
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +26 -8
- package/packages/gui/src/components/layout/command-palette.jsx +14 -4
- package/packages/gui/src/components/layout/status-bar.jsx +46 -11
- package/packages/gui/src/components/marketplace/repo-card.jsx +64 -0
- package/packages/gui/src/components/marketplace/repo-import.jsx +363 -0
- package/packages/gui/src/components/marketplace/repo-nuke-dialog.jsx +68 -0
- package/packages/gui/src/components/pro/pro-gate.jsx +22 -0
- package/packages/gui/src/components/pro/upgrade-card.jsx +48 -0
- package/packages/gui/src/components/settings/quick-connect.jsx +129 -0
- package/packages/gui/src/components/settings/remote-server-card.jsx +243 -0
- package/packages/gui/src/components/settings/server-dialog.jsx +192 -0
- package/packages/gui/src/components/ui/approval-modal.jsx +63 -0
- package/packages/gui/src/components/ui/toast.jsx +1 -1
- package/packages/gui/src/lib/edition.js +4 -0
- package/packages/gui/src/lib/electron.js +17 -0
- package/packages/gui/src/lib/status.js +1 -0
- package/packages/gui/src/stores/groove.js +150 -6
- package/packages/gui/src/views/dashboard.jsx +39 -40
- package/packages/gui/src/views/marketplace.jsx +82 -0
- package/packages/gui/src/views/settings.jsx +66 -0
- package/packages/gui/vite.config.js +3 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-Bl1_J0sN.js +0 -652
- package/node_modules/@groove-dev/gui/dist/assets/index-DjORRpF0.css +0 -1
- package/packages/gui/dist/assets/index-Bl1_J0sN.js +0 -652
- package/packages/gui/dist/assets/index-DjORRpF0.css +0 -1
- package/packages/gui/node_modules/.vite/deps/chunk-KXLIKZFX.js.map +0 -7
- package/test-slack.mjs +0 -28
- /package/packages/gui/node_modules/.vite/deps/{chunk-WYSQD5ZG.js.map → chunk-DH7AESXW.js.map} +0 -0
- /package/packages/gui/node_modules/.vite/deps/{chunk-3LBP22MX.js.map → chunk-LKZVMLRH.js.map} +0 -0
- /package/packages/gui/node_modules/.vite/deps/{chunk-J6DMOQWP.js.map → chunk-MCVDVNE5.js.map} +0 -0
- /package/packages/gui/node_modules/.vite/deps/{chunk-3Q7HT7ZF.js.map → chunk-SPKVQGZX.js.map} +0 -0
|
@@ -61,8 +61,8 @@ function QualityBar({ score }) {
|
|
|
61
61
|
const pct = Math.max(0, Math.min(100, score || 0));
|
|
62
62
|
const color = qualityColor(score);
|
|
63
63
|
return (
|
|
64
|
-
<div className="h-
|
|
65
|
-
<div className="h-full rounded-
|
|
64
|
+
<div className="h-0.5 rounded-sm overflow-hidden flex-1" style={{ background: 'rgba(51,175,188,0.08)' }}>
|
|
65
|
+
<div className="h-full rounded-sm transition-all duration-700" style={{ width: `${pct}%`, background: color }} />
|
|
66
66
|
</div>
|
|
67
67
|
);
|
|
68
68
|
}
|
|
@@ -81,9 +81,9 @@ function ProgressBar({ label, value, total, color }) {
|
|
|
81
81
|
<span className="text-2xs font-mono text-text-3 tabular-nums w-10 text-right">{fmtNum(value)}</span>
|
|
82
82
|
</div>
|
|
83
83
|
</div>
|
|
84
|
-
<div className="h-
|
|
84
|
+
<div className="h-0.5 rounded-sm overflow-hidden" style={{ background: 'rgba(51,175,188,0.08)' }}>
|
|
85
85
|
<div
|
|
86
|
-
className="h-full rounded-
|
|
86
|
+
className="h-full rounded-sm transition-all duration-500"
|
|
87
87
|
style={{ width: `${Math.min(pct, 100)}%`, background: color }}
|
|
88
88
|
/>
|
|
89
89
|
</div>
|
|
@@ -118,7 +118,7 @@ function HealthTab({ tokens, rotation, agentBreakdown }) {
|
|
|
118
118
|
Quality
|
|
119
119
|
<InfoTip text="Average session quality score (0-100). Based on error rate, tool failures, repetitions, and file churn. Below 40 triggers auto-rotation to prevent wasted tokens." />
|
|
120
120
|
</div>
|
|
121
|
-
<div className="text-
|
|
121
|
+
<div className="text-base font-mono font-bold text-text-1 tabular-nums leading-none">
|
|
122
122
|
{avgQuality ?? '—'}
|
|
123
123
|
</div>
|
|
124
124
|
</div>
|
|
@@ -127,7 +127,7 @@ function HealthTab({ tokens, rotation, agentBreakdown }) {
|
|
|
127
127
|
Rotations
|
|
128
128
|
<InfoTip text="Context rotations: quality-based (q), context threshold (c), and natural compactions (n) from provider-managed context resets. Each rotation preserves progress via a journalist handoff brief." />
|
|
129
129
|
</div>
|
|
130
|
-
<div className="text-
|
|
130
|
+
<div className="text-base font-mono font-bold text-text-1 tabular-nums leading-none">
|
|
131
131
|
{rotation?.totalRotations || 0}
|
|
132
132
|
</div>
|
|
133
133
|
{(rotation?.qualityRotations > 0 || rotation?.contextRotations > 0 || rotation?.naturalCompactions > 0) && (
|
|
@@ -141,7 +141,7 @@ function HealthTab({ tokens, rotation, agentBreakdown }) {
|
|
|
141
141
|
Cache
|
|
142
142
|
<InfoTip text="Prompt cache hit rate. Cache reads are ~90% cheaper than regular input tokens. Managed by your AI provider — GROOVE tracks it, doesn't control it." />
|
|
143
143
|
</div>
|
|
144
|
-
<div className="text-
|
|
144
|
+
<div className="text-base font-mono font-bold text-text-1 tabular-nums leading-none">
|
|
145
145
|
{fmtPct((tokens?.cacheHitRate || 0) * 100)}
|
|
146
146
|
</div>
|
|
147
147
|
</div>
|
|
@@ -150,7 +150,7 @@ function HealthTab({ tokens, rotation, agentBreakdown }) {
|
|
|
150
150
|
Success
|
|
151
151
|
<InfoTip text="Agent completion rate. Completed agents vs. crashed agents. High success rate means agents are finishing tasks without errors." />
|
|
152
152
|
</div>
|
|
153
|
-
<div className="text-
|
|
153
|
+
<div className="text-base font-mono font-bold text-text-1 tabular-nums leading-none">
|
|
154
154
|
{completionRate}%
|
|
155
155
|
</div>
|
|
156
156
|
</div>
|
|
@@ -34,7 +34,7 @@ const RoutingChart = memo(function RoutingChart({ routing, agentBreakdown }) {
|
|
|
34
34
|
<span className="text-2xs font-mono text-text-4 ml-auto tabular-nums">{fmtNum(total)} decisions</span>
|
|
35
35
|
</div>
|
|
36
36
|
{/* Stacked horizontal bar */}
|
|
37
|
-
<div className="h-
|
|
37
|
+
<div className="h-0.5 bg-surface-2 rounded-sm overflow-hidden flex">
|
|
38
38
|
{tiers.map((tier) => {
|
|
39
39
|
const count = byTier[tier] || 0;
|
|
40
40
|
if (count === 0) return null;
|
|
@@ -86,9 +86,9 @@ const RoutingChart = memo(function RoutingChart({ routing, agentBreakdown }) {
|
|
|
86
86
|
<span className="text-2xs font-mono text-text-3 tabular-nums">{usage.agents} agent{usage.agents !== 1 ? 's' : ''}</span>
|
|
87
87
|
<span className="text-xs font-mono text-text-1 tabular-nums">{fmtNum(usage.tokens)}</span>
|
|
88
88
|
</div>
|
|
89
|
-
<div className="h-
|
|
89
|
+
<div className="h-0.5 bg-surface-4 rounded-sm overflow-hidden">
|
|
90
90
|
<div
|
|
91
|
-
className="h-full rounded-
|
|
91
|
+
className="h-full rounded-sm transition-all duration-500"
|
|
92
92
|
style={{ width: `${Math.max(barPct, 2)}%`, background: HEX.accent }}
|
|
93
93
|
/>
|
|
94
94
|
</div>
|
|
@@ -18,7 +18,7 @@ export const TeamBurnPanel = memo(function TeamBurnPanel({ teams = [] }) {
|
|
|
18
18
|
{teams.length === 0 ? (
|
|
19
19
|
<div className="px-3 py-6 text-center text-xs text-text-3 font-mono">No team activity yet</div>
|
|
20
20
|
) : (
|
|
21
|
-
<div className="px-3
|
|
21
|
+
<div className="px-3 py-3 space-y-3">
|
|
22
22
|
{teams.map((t) => {
|
|
23
23
|
const pct = maxTokens > 0 ? (t.totalTokens / maxTokens) * 100 : 0;
|
|
24
24
|
return (
|
|
@@ -27,13 +27,13 @@ export function ActivityBar({ activeView, detailPanel, onNavigate, onTogglePanel
|
|
|
27
27
|
<button
|
|
28
28
|
onClick={() => onNavigate(item.id)}
|
|
29
29
|
className={cn(
|
|
30
|
-
'w-
|
|
30
|
+
'w-8 h-8 flex items-center justify-center rounded-md transition-colors cursor-pointer',
|
|
31
31
|
activeView === item.id
|
|
32
32
|
? 'text-text-0 bg-surface-5'
|
|
33
33
|
: 'text-text-3 hover:text-text-1 hover:bg-surface-4',
|
|
34
34
|
)}
|
|
35
35
|
>
|
|
36
|
-
<item.icon size={
|
|
36
|
+
<item.icon size={16} strokeWidth={activeView === item.id ? 2 : 1.5} />
|
|
37
37
|
</button>
|
|
38
38
|
</Tooltip>
|
|
39
39
|
))}
|
|
@@ -53,13 +53,13 @@ export function ActivityBar({ activeView, detailPanel, onNavigate, onTogglePanel
|
|
|
53
53
|
<button
|
|
54
54
|
onClick={() => item.panel ? onTogglePanel(item.id) : onNavigate(item.id)}
|
|
55
55
|
className={cn(
|
|
56
|
-
'w-
|
|
56
|
+
'w-8 h-8 flex items-center justify-center rounded-md transition-colors cursor-pointer',
|
|
57
57
|
isActive
|
|
58
58
|
? 'text-text-0 bg-surface-5'
|
|
59
59
|
: 'text-text-3 hover:text-text-1 hover:bg-surface-4',
|
|
60
60
|
)}
|
|
61
61
|
>
|
|
62
|
-
<item.icon size={
|
|
62
|
+
<item.icon size={16} strokeWidth={isActive ? 2 : 1.5} />
|
|
63
63
|
</button>
|
|
64
64
|
</Tooltip>
|
|
65
65
|
);
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { useGrooveStore } from '../../stores/groove';
|
|
4
4
|
import { useKeyboard } from '../../lib/hooks/use-keyboard';
|
|
5
|
+
import { isElectron } from '../../lib/electron';
|
|
6
|
+
import { cn } from '../../lib/cn';
|
|
5
7
|
import { TooltipProvider } from '../ui/tooltip';
|
|
6
8
|
import { ToastContainer } from '../ui/toast';
|
|
7
9
|
import { ActivityBar } from './activity-bar';
|
|
@@ -9,6 +11,8 @@ import { BreadcrumbBar } from './breadcrumb-bar';
|
|
|
9
11
|
import { StatusBar } from './status-bar';
|
|
10
12
|
import { DetailPanel } from './detail-panel';
|
|
11
13
|
import { CommandPalette } from './command-palette';
|
|
14
|
+
import { ApprovalModal } from '../ui/approval-modal';
|
|
15
|
+
import { QuickConnect } from '../settings/quick-connect';
|
|
12
16
|
import { TeamTabBar } from '../../views/agents';
|
|
13
17
|
|
|
14
18
|
export function AppShell({ children, detailContent, terminalContent }) {
|
|
@@ -55,7 +59,7 @@ export function AppShell({ children, detailContent, terminalContent }) {
|
|
|
55
59
|
|
|
56
60
|
return (
|
|
57
61
|
<TooltipProvider>
|
|
58
|
-
<div className=
|
|
62
|
+
<div className={cn('w-full h-full flex flex-col bg-surface-2 text-text-1 font-sans', isElectron() && 'electron-app')}>
|
|
59
63
|
<BreadcrumbBar
|
|
60
64
|
activeView={activeView}
|
|
61
65
|
connected={connected}
|
|
@@ -113,6 +117,8 @@ export function AppShell({ children, detailContent, terminalContent }) {
|
|
|
113
117
|
/>
|
|
114
118
|
|
|
115
119
|
<CommandPalette />
|
|
120
|
+
<QuickConnect />
|
|
121
|
+
<ApprovalModal />
|
|
116
122
|
<ToastContainer />
|
|
117
123
|
</div>
|
|
118
124
|
</TooltipProvider>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
2
|
import { Search, Plus, ChevronRight } from 'lucide-react';
|
|
3
3
|
import { cn } from '../../lib/cn';
|
|
4
|
+
import { isElectron, getPlatform } from '../../lib/electron';
|
|
4
5
|
|
|
5
6
|
const VIEW_LABELS = {
|
|
6
7
|
agents: 'Agents',
|
|
@@ -23,17 +24,34 @@ export function BreadcrumbBar({
|
|
|
23
24
|
crumbs.push(editorActiveFile.split('/').pop());
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
const electron = isElectron();
|
|
28
|
+
const darwinDrag = electron && getPlatform() === 'darwin';
|
|
29
|
+
|
|
26
30
|
return (
|
|
27
|
-
<header
|
|
31
|
+
<header
|
|
32
|
+
className={cn(
|
|
33
|
+
'h-11 flex-shrink-0 flex items-center gap-3 px-4 bg-surface-3 border-b border-border',
|
|
34
|
+
darwinDrag && 'pl-20 electron-drag electron-no-drag-children',
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
28
37
|
{/* Logo */}
|
|
29
38
|
<img src="/favicon.png" alt="Groove" className="h-7 w-7 rounded-full flex-shrink-0" />
|
|
30
39
|
|
|
31
|
-
{/* Host badge */}
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
{/* Host badge — show instance name from ?instance= or raw host */}
|
|
41
|
+
{(() => {
|
|
42
|
+
const instance = new URLSearchParams(window.location.search).get('instance');
|
|
43
|
+
if (instance) return (
|
|
44
|
+
<span className="text-2xs font-mono font-semibold text-accent bg-accent/10 px-1.5 py-0.5 rounded flex-shrink-0">
|
|
45
|
+
{instance}
|
|
46
|
+
</span>
|
|
47
|
+
);
|
|
48
|
+
if (daemonHost) return (
|
|
49
|
+
<span className="text-2xs font-mono font-semibold text-text-3 bg-surface-5 px-1.5 py-0.5 rounded flex-shrink-0">
|
|
50
|
+
{daemonHost}
|
|
51
|
+
</span>
|
|
52
|
+
);
|
|
53
|
+
return null;
|
|
54
|
+
})()}
|
|
37
55
|
|
|
38
56
|
<div className="flex-1 min-w-4" />
|
|
39
57
|
|
|
@@ -43,7 +61,7 @@ export function BreadcrumbBar({
|
|
|
43
61
|
className={cn(
|
|
44
62
|
'flex items-center gap-2.5 h-8 px-4 rounded-full w-full max-w-md',
|
|
45
63
|
'bg-surface-1 border border-border-subtle',
|
|
46
|
-
'text-
|
|
64
|
+
'text-xs text-text-4 font-sans',
|
|
47
65
|
'hover:border-border hover:text-text-3 transition-colors cursor-pointer',
|
|
48
66
|
)}
|
|
49
67
|
>
|
|
@@ -4,7 +4,7 @@ import { useGrooveStore } from '../../stores/groove';
|
|
|
4
4
|
import {
|
|
5
5
|
Network, Code2, ChartSpline, Puzzle, Users, Plus,
|
|
6
6
|
RotateCw, Skull, MessageSquare, Terminal, Newspaper,
|
|
7
|
-
Search,
|
|
7
|
+
Search, Radio, ExternalLink,
|
|
8
8
|
} from 'lucide-react';
|
|
9
9
|
import { cn } from '../../lib/cn';
|
|
10
10
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
@@ -26,6 +26,7 @@ export function CommandPalette() {
|
|
|
26
26
|
const open = useGrooveStore((s) => s.commandPaletteOpen);
|
|
27
27
|
const toggle = useGrooveStore((s) => s.toggleCommandPalette);
|
|
28
28
|
const agents = useGrooveStore((s) => s.agents);
|
|
29
|
+
const savedTunnels = useGrooveStore((s) => s.savedTunnels);
|
|
29
30
|
const store = useGrooveStore;
|
|
30
31
|
|
|
31
32
|
const [query, setQuery] = useState('');
|
|
@@ -41,8 +42,17 @@ export function CommandPalette() {
|
|
|
41
42
|
{ id: `kill:${a.id}`, label: `Kill ${a.name}`, icon: Skull, category: 'Agents', action: (s) => { s.killAgent(a.id); } },
|
|
42
43
|
] : []),
|
|
43
44
|
]);
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
const tunnelCommands = [
|
|
46
|
+
{ id: 'action:quickconnect', label: 'Quick Connect', icon: Radio, category: 'Remote', action: (s) => { s.toggleQuickConnect(); } },
|
|
47
|
+
...savedTunnels.map((t) => t.active
|
|
48
|
+
? { id: `tunnel:open:${t.id}`, label: `Open ${t.name}`, icon: ExternalLink, category: 'Remote', action: () => {
|
|
49
|
+
window.open(`http://localhost:${t.localPort}?instance=${encodeURIComponent(t.name)}`, '_blank');
|
|
50
|
+
}}
|
|
51
|
+
: { id: `tunnel:connect:${t.id}`, label: `Connect to ${t.name}`, icon: Radio, category: 'Remote', action: (s) => { s.connectTunnel(t.id); } }
|
|
52
|
+
),
|
|
53
|
+
];
|
|
54
|
+
return [...STATIC_COMMANDS, ...agentCommands, ...tunnelCommands];
|
|
55
|
+
}, [agents, savedTunnels]);
|
|
46
56
|
|
|
47
57
|
// Filter
|
|
48
58
|
const filtered = useMemo(() => {
|
|
@@ -104,7 +114,7 @@ export function CommandPalette() {
|
|
|
104
114
|
value={query}
|
|
105
115
|
onChange={(e) => { setQuery(e.target.value); setSelectedIndex(0); }}
|
|
106
116
|
placeholder="Type a command..."
|
|
107
|
-
className="flex-1 bg-transparent text-
|
|
117
|
+
className="flex-1 bg-transparent text-xs text-text-0 font-sans placeholder:text-text-4 focus:outline-none"
|
|
108
118
|
/>
|
|
109
119
|
</div>
|
|
110
120
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import { Terminal, BookOpen } from 'lucide-react';
|
|
2
|
+
import { Terminal, BookOpen, Radio, Plug } from 'lucide-react';
|
|
3
3
|
import { cn } from '../../lib/cn';
|
|
4
4
|
import { StatusDot } from '../ui/status-dot';
|
|
5
5
|
import { fmtUptime } from '../../lib/format';
|
|
6
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
7
|
+
import { isElectron, openExternal } from '../../lib/electron';
|
|
6
8
|
|
|
7
9
|
export function StatusBar({
|
|
8
10
|
connected,
|
|
@@ -12,6 +14,10 @@ export function StatusBar({
|
|
|
12
14
|
terminalVisible,
|
|
13
15
|
onToggleTerminal,
|
|
14
16
|
}) {
|
|
17
|
+
const savedTunnels = useGrooveStore((s) => s.savedTunnels);
|
|
18
|
+
const activeTunnel = savedTunnels.find((t) => t.active);
|
|
19
|
+
const electron = isElectron();
|
|
20
|
+
|
|
15
21
|
return (
|
|
16
22
|
<footer className="h-6 flex-shrink-0 flex items-center px-3 bg-surface-3 border-t border-border text-2xs font-sans select-none">
|
|
17
23
|
{/* Left: connection + stats */}
|
|
@@ -19,7 +25,7 @@ export function StatusBar({
|
|
|
19
25
|
<div className="flex items-center gap-1.5">
|
|
20
26
|
<StatusDot status={connected ? 'running' : 'crashed'} size="sm" />
|
|
21
27
|
<span className={connected ? 'text-text-2' : 'text-danger'}>
|
|
22
|
-
{connected ? 'Connected' : 'Offline'}
|
|
28
|
+
{connected ? (electron ? 'Desktop' : 'Connected') : 'Offline'}
|
|
23
29
|
</span>
|
|
24
30
|
</div>
|
|
25
31
|
{connected && uptime > 0 && (
|
|
@@ -28,20 +34,49 @@ export function StatusBar({
|
|
|
28
34
|
{connected && agentCount > 0 && (
|
|
29
35
|
<span className="text-text-4">{runningCount}/{agentCount} agents</span>
|
|
30
36
|
)}
|
|
37
|
+
{activeTunnel ? (
|
|
38
|
+
<button
|
|
39
|
+
onClick={() => {
|
|
40
|
+
const port = activeTunnel.localPort;
|
|
41
|
+
const name = encodeURIComponent(activeTunnel.name);
|
|
42
|
+
openExternal(`http://localhost:${port}?instance=${name}`);
|
|
43
|
+
}}
|
|
44
|
+
className="flex items-center gap-1.5 text-text-3 hover:text-text-1 cursor-pointer transition-colors"
|
|
45
|
+
title="Open remote GUI"
|
|
46
|
+
>
|
|
47
|
+
<Radio size={10} className="text-success" />
|
|
48
|
+
<span>{activeTunnel.name}</span>
|
|
49
|
+
<span className="w-1.5 h-1.5 rounded-full bg-success" />
|
|
50
|
+
{activeTunnel.latencyMs != null && (
|
|
51
|
+
<span className="text-text-4">{activeTunnel.latencyMs}ms</span>
|
|
52
|
+
)}
|
|
53
|
+
</button>
|
|
54
|
+
) : savedTunnels.length > 0 && (
|
|
55
|
+
<button
|
|
56
|
+
onClick={() => useGrooveStore.getState().toggleQuickConnect()}
|
|
57
|
+
className="flex items-center gap-1.5 text-text-4 hover:text-text-1 cursor-pointer transition-colors"
|
|
58
|
+
title="Quick Connect to remote server"
|
|
59
|
+
>
|
|
60
|
+
<Plug size={10} />
|
|
61
|
+
<span>Connect</span>
|
|
62
|
+
</button>
|
|
63
|
+
)}
|
|
31
64
|
</div>
|
|
32
65
|
|
|
33
66
|
<div className="flex-1" />
|
|
34
67
|
|
|
35
68
|
{/* Right: docs + terminal toggle */}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
{!electron && (
|
|
70
|
+
<a
|
|
71
|
+
href="https://docs.groovedev.ai"
|
|
72
|
+
target="_blank"
|
|
73
|
+
rel="noopener noreferrer"
|
|
74
|
+
className="flex items-center gap-1.5 px-2 h-full text-text-3 hover:text-text-1 hover:bg-surface-5 transition-colors no-underline"
|
|
75
|
+
>
|
|
76
|
+
<BookOpen size={12} />
|
|
77
|
+
<span>Docs</span>
|
|
78
|
+
</a>
|
|
79
|
+
)}
|
|
45
80
|
<button
|
|
46
81
|
onClick={onToggleTerminal}
|
|
47
82
|
className={cn(
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
import { FolderOpen, Trash2, Bomb } from 'lucide-react';
|
|
3
|
+
import { Badge } from '../ui/badge';
|
|
4
|
+
import { cn } from '../../lib/cn';
|
|
5
|
+
import { timeAgo } from '../../lib/format';
|
|
6
|
+
|
|
7
|
+
export function RepoCard({ repo, onRemove, onNuke, onOpen }) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="rounded-lg border border-border-subtle bg-surface-2 p-3 flex items-center gap-3">
|
|
10
|
+
<div className="flex-1 min-w-0">
|
|
11
|
+
<div className="flex items-center gap-2">
|
|
12
|
+
<span className="text-xs font-semibold text-text-0 font-sans truncate">{repo.repoName || repo.name}</span>
|
|
13
|
+
<span className="text-2xs text-text-3 font-sans">{repo.repoOwner || repo.owner}</span>
|
|
14
|
+
</div>
|
|
15
|
+
<div className="flex items-center gap-2 mt-0.5">
|
|
16
|
+
{repo.language && <Badge variant="outline" className="text-2xs">{repo.language}</Badge>}
|
|
17
|
+
<span className="text-2xs text-text-4 font-mono truncate max-w-[180px]">{repo.clonedTo || repo.path}</span>
|
|
18
|
+
</div>
|
|
19
|
+
<span className="text-2xs text-text-4 font-sans mt-0.5 block">
|
|
20
|
+
{repo.clonedAt ? `Imported ${timeAgo(repo.clonedAt)}` : repo.status || ''}
|
|
21
|
+
</span>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div className="flex items-center gap-1 flex-shrink-0">
|
|
25
|
+
{onOpen && (
|
|
26
|
+
<button
|
|
27
|
+
onClick={() => onOpen(repo)}
|
|
28
|
+
className={cn(
|
|
29
|
+
'flex items-center gap-1 px-2 py-1 rounded text-2xs font-sans cursor-pointer',
|
|
30
|
+
'text-accent bg-accent/10 hover:bg-accent/20 border-0 transition-colors',
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
<FolderOpen size={11} />
|
|
34
|
+
Open
|
|
35
|
+
</button>
|
|
36
|
+
)}
|
|
37
|
+
{onRemove && (
|
|
38
|
+
<button
|
|
39
|
+
onClick={() => onRemove(repo)}
|
|
40
|
+
className={cn(
|
|
41
|
+
'flex items-center gap-1 px-2 py-1 rounded text-2xs font-sans cursor-pointer',
|
|
42
|
+
'text-text-3 hover:text-text-1 hover:bg-surface-4 bg-transparent border-0 transition-colors',
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
<Trash2 size={11} />
|
|
46
|
+
Remove
|
|
47
|
+
</button>
|
|
48
|
+
)}
|
|
49
|
+
{onNuke && (
|
|
50
|
+
<button
|
|
51
|
+
onClick={() => onNuke(repo)}
|
|
52
|
+
className={cn(
|
|
53
|
+
'flex items-center gap-1 px-2 py-1 rounded text-2xs font-sans cursor-pointer',
|
|
54
|
+
'text-danger bg-danger/10 hover:bg-danger/20 border-0 transition-colors',
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
<Bomb size={11} />
|
|
58
|
+
Nuke
|
|
59
|
+
</button>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|