groove-dev 0.27.14 → 0.27.17
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/README.md +37 -1
- package/developerID_application.cer +0 -0
- package/node_modules/@groove-dev/daemon/src/api.js +587 -68
- package/node_modules/@groove-dev/daemon/src/classifier.js +24 -0
- package/node_modules/@groove-dev/daemon/src/credentials.js +12 -2
- package/node_modules/@groove-dev/daemon/src/federation/ambassador.js +204 -0
- package/node_modules/@groove-dev/daemon/src/federation/connection.js +359 -0
- package/node_modules/@groove-dev/daemon/src/federation/contracts.js +112 -0
- package/node_modules/@groove-dev/daemon/src/federation/whitelist.js +190 -0
- package/node_modules/@groove-dev/daemon/src/federation.js +166 -7
- package/node_modules/@groove-dev/daemon/src/index.js +172 -19
- package/node_modules/@groove-dev/daemon/src/introducer.js +52 -7
- package/node_modules/@groove-dev/daemon/src/journalist.js +46 -1
- package/node_modules/@groove-dev/daemon/src/memory.js +36 -16
- package/node_modules/@groove-dev/daemon/src/process.js +140 -23
- package/node_modules/@groove-dev/daemon/src/providers/base.js +1 -0
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +1 -0
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +124 -28
- package/node_modules/@groove-dev/daemon/src/providers/gemini.js +104 -17
- package/node_modules/@groove-dev/daemon/src/providers/index.js +17 -0
- package/node_modules/@groove-dev/daemon/src/registry.js +10 -1
- package/node_modules/@groove-dev/daemon/src/rotator.js +93 -30
- package/node_modules/@groove-dev/daemon/src/skills.js +33 -3
- package/node_modules/@groove-dev/daemon/src/terminal-pty.js +9 -1
- package/node_modules/@groove-dev/daemon/src/tool-executor.js +11 -5
- package/node_modules/@groove-dev/daemon/src/toys.js +69 -0
- package/node_modules/@groove-dev/daemon/src/tunnel-manager.js +24 -5
- package/node_modules/@groove-dev/daemon/templates/toys-catalog.json +242 -0
- package/node_modules/@groove-dev/daemon/test/classifier.test.js +98 -0
- package/node_modules/@groove-dev/daemon/test/introducer.test.js +72 -1
- package/node_modules/@groove-dev/daemon/test/journalist.test.js +117 -0
- package/node_modules/@groove-dev/daemon/test/memory.test.js +37 -1
- package/node_modules/@groove-dev/daemon/test/rotator.test.js +183 -4
- package/node_modules/@groove-dev/gui/dist/assets/index-BglPgjlu.js +8607 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CGcwmmJv.css +1 -0
- package/node_modules/@groove-dev/gui/dist/index.html +3 -2
- package/node_modules/@groove-dev/gui/index.html +1 -0
- package/node_modules/@groove-dev/gui/src/app.css +7 -0
- package/node_modules/@groove-dev/gui/src/app.jsx +37 -10
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +21 -31
- package/node_modules/@groove-dev/gui/src/components/agents/agent-config.jsx +11 -6
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/components/agents/spawn-wizard.jsx +42 -1
- package/node_modules/@groove-dev/gui/src/components/editor/breadcrumbs.jsx +30 -0
- package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +33 -2
- package/node_modules/@groove-dev/gui/src/components/editor/editor-status-bar.jsx +26 -0
- package/node_modules/@groove-dev/gui/src/components/editor/editor-tabs.jsx +113 -34
- package/node_modules/@groove-dev/gui/src/components/editor/goto-line.jsx +35 -0
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +12 -6
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +13 -3
- package/node_modules/@groove-dev/gui/src/components/layout/app-shell.jsx +0 -1
- package/node_modules/@groove-dev/gui/src/components/layout/breadcrumb-bar.jsx +165 -47
- package/node_modules/@groove-dev/gui/src/components/layout/command-palette.jsx +6 -2
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +10 -9
- package/node_modules/@groove-dev/gui/src/components/marketplace/repo-import.jsx +9 -1
- package/node_modules/@groove-dev/gui/src/components/onboarding/provider-card.jsx +134 -0
- package/node_modules/@groove-dev/gui/src/components/onboarding/setup-wizard.jsx +819 -0
- package/node_modules/@groove-dev/gui/src/components/pro/pro-gate.jsx +12 -5
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-card.jsx +15 -8
- package/node_modules/@groove-dev/gui/src/components/pro/upgrade-modal.jsx +151 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-activity.jsx +98 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-panel.jsx +290 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-peers.jsx +126 -0
- package/node_modules/@groove-dev/gui/src/components/settings/federation-wizard.jsx +293 -0
- package/node_modules/@groove-dev/gui/src/components/settings/quick-connect.jsx +110 -67
- package/node_modules/@groove-dev/gui/src/components/settings/remote-server-card.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/components/settings/server-detail.jsx +310 -0
- package/node_modules/@groove-dev/gui/src/components/settings/server-dialog.jsx +4 -1
- package/node_modules/@groove-dev/gui/src/components/settings/server-list.jsx +59 -0
- package/node_modules/@groove-dev/gui/src/components/settings/ssh-wizard.jsx +549 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-card.jsx +78 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-creator.jsx +144 -0
- package/node_modules/@groove-dev/gui/src/components/toys/toy-launcher.jsx +187 -0
- package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +2 -2
- package/node_modules/@groove-dev/gui/src/lib/electron.js +15 -0
- package/node_modules/@groove-dev/gui/src/lib/format.js +1 -0
- package/node_modules/@groove-dev/gui/src/stores/groove.js +373 -58
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +148 -42
- package/node_modules/@groove-dev/gui/src/views/editor.jsx +92 -2
- package/node_modules/@groove-dev/gui/src/views/federation.jsx +37 -0
- package/node_modules/@groove-dev/gui/src/views/marketplace.jsx +2 -42
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +32 -132
- package/node_modules/@groove-dev/gui/src/views/subscription-panel.jsx +327 -0
- package/node_modules/@groove-dev/gui/src/views/teams.jsx +3 -3
- package/node_modules/@groove-dev/gui/src/views/toys.jsx +162 -0
- package/package.json +1 -1
- package/packages/daemon/src/api.js +587 -68
- package/packages/daemon/src/classifier.js +24 -0
- package/packages/daemon/src/credentials.js +12 -2
- package/packages/daemon/src/federation/ambassador.js +204 -0
- package/packages/daemon/src/federation/connection.js +359 -0
- package/packages/daemon/src/federation/contracts.js +112 -0
- package/packages/daemon/src/federation/whitelist.js +190 -0
- package/packages/daemon/src/federation.js +166 -7
- package/packages/daemon/src/index.js +172 -19
- package/packages/daemon/src/introducer.js +52 -7
- package/packages/daemon/src/journalist.js +46 -1
- package/packages/daemon/src/memory.js +36 -16
- package/packages/daemon/src/process.js +140 -23
- package/packages/daemon/src/providers/base.js +1 -0
- package/packages/daemon/src/providers/claude-code.js +1 -0
- package/packages/daemon/src/providers/codex.js +124 -28
- package/packages/daemon/src/providers/gemini.js +104 -17
- package/packages/daemon/src/providers/index.js +17 -0
- package/packages/daemon/src/registry.js +10 -1
- package/packages/daemon/src/rotator.js +93 -30
- package/packages/daemon/src/skills.js +33 -3
- package/packages/daemon/src/terminal-pty.js +9 -1
- package/packages/daemon/src/tool-executor.js +11 -5
- package/packages/daemon/src/toys.js +69 -0
- package/packages/daemon/src/tunnel-manager.js +24 -5
- package/packages/daemon/templates/toys-catalog.json +242 -0
- package/packages/gui/dist/assets/index-BglPgjlu.js +8607 -0
- package/packages/gui/dist/assets/index-CGcwmmJv.css +1 -0
- package/packages/gui/dist/index.html +3 -2
- package/packages/gui/index.html +1 -0
- package/packages/gui/src/app.css +7 -0
- package/packages/gui/src/app.jsx +37 -10
- package/packages/gui/src/components/agents/agent-chat.jsx +21 -31
- package/packages/gui/src/components/agents/agent-config.jsx +11 -6
- package/packages/gui/src/components/agents/agent-feed.jsx +2 -2
- package/packages/gui/src/components/agents/spawn-wizard.jsx +42 -1
- package/packages/gui/src/components/editor/breadcrumbs.jsx +30 -0
- package/packages/gui/src/components/editor/code-editor.jsx +33 -2
- package/packages/gui/src/components/editor/editor-status-bar.jsx +26 -0
- package/packages/gui/src/components/editor/editor-tabs.jsx +113 -34
- package/packages/gui/src/components/editor/goto-line.jsx +35 -0
- package/packages/gui/src/components/editor/terminal.jsx +12 -6
- package/packages/gui/src/components/layout/activity-bar.jsx +13 -3
- package/packages/gui/src/components/layout/app-shell.jsx +0 -1
- package/packages/gui/src/components/layout/breadcrumb-bar.jsx +165 -47
- package/packages/gui/src/components/layout/command-palette.jsx +6 -2
- package/packages/gui/src/components/layout/terminal-panel.jsx +10 -9
- package/packages/gui/src/components/marketplace/repo-import.jsx +9 -1
- package/packages/gui/src/components/onboarding/provider-card.jsx +134 -0
- package/packages/gui/src/components/onboarding/setup-wizard.jsx +819 -0
- package/packages/gui/src/components/pro/pro-gate.jsx +12 -5
- package/packages/gui/src/components/pro/upgrade-card.jsx +15 -8
- package/packages/gui/src/components/pro/upgrade-modal.jsx +151 -0
- package/packages/gui/src/components/settings/federation-activity.jsx +98 -0
- package/packages/gui/src/components/settings/federation-panel.jsx +290 -0
- package/packages/gui/src/components/settings/federation-peers.jsx +126 -0
- package/packages/gui/src/components/settings/federation-wizard.jsx +293 -0
- package/packages/gui/src/components/settings/quick-connect.jsx +110 -67
- package/packages/gui/src/components/settings/remote-server-card.jsx +3 -3
- package/packages/gui/src/components/settings/server-detail.jsx +310 -0
- package/packages/gui/src/components/settings/server-dialog.jsx +4 -1
- package/packages/gui/src/components/settings/server-list.jsx +59 -0
- package/packages/gui/src/components/settings/ssh-wizard.jsx +549 -0
- package/packages/gui/src/components/toys/toy-card.jsx +78 -0
- package/packages/gui/src/components/toys/toy-creator.jsx +144 -0
- package/packages/gui/src/components/toys/toy-launcher.jsx +187 -0
- package/packages/gui/src/components/ui/toast.jsx +2 -2
- package/packages/gui/src/lib/electron.js +15 -0
- package/packages/gui/src/lib/format.js +1 -0
- package/packages/gui/src/stores/groove.js +373 -58
- package/packages/gui/src/views/agents.jsx +148 -42
- package/packages/gui/src/views/editor.jsx +92 -2
- package/packages/gui/src/views/federation.jsx +37 -0
- package/packages/gui/src/views/marketplace.jsx +2 -42
- package/packages/gui/src/views/settings.jsx +32 -132
- package/packages/gui/src/views/subscription-panel.jsx +327 -0
- package/packages/gui/src/views/teams.jsx +3 -3
- package/packages/gui/src/views/toys.jsx +162 -0
- package/plans/chat-persistence-refactor.md +154 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-BE6lYcd7.css +0 -1
- package/node_modules/@groove-dev/gui/dist/assets/index-zdzOLAZM.js +0 -677
- package/packages/gui/dist/assets/index-BE6lYcd7.css +0 -1
- package/packages/gui/dist/assets/index-zdzOLAZM.js +0 -677
|
@@ -1,8 +1,124 @@
|
|
|
1
1
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
-
import {
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
|
+
import { Search, ChevronRight, LogIn, LogOut, User, ExternalLink, BookOpen, ChevronDown } from 'lucide-react';
|
|
3
4
|
import { cn } from '../../lib/cn';
|
|
5
|
+
import { useGrooveStore } from '../../stores/groove';
|
|
4
6
|
import { isElectron, getPlatform } from '../../lib/electron';
|
|
5
7
|
|
|
8
|
+
function ProfilePic({ user, size = 24 }) {
|
|
9
|
+
const [broken, setBroken] = useState(false);
|
|
10
|
+
const src = user?.avatar || user?.picture || user?.photoURL || user?.photo;
|
|
11
|
+
|
|
12
|
+
if (src && !broken) {
|
|
13
|
+
return (
|
|
14
|
+
<img
|
|
15
|
+
src={src}
|
|
16
|
+
alt=""
|
|
17
|
+
className="rounded-full"
|
|
18
|
+
style={{ width: size, height: size }}
|
|
19
|
+
referrerPolicy="no-referrer"
|
|
20
|
+
crossOrigin="anonymous"
|
|
21
|
+
onError={() => setBroken(true)}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
className="rounded-full bg-accent/10 flex items-center justify-center"
|
|
29
|
+
style={{ width: size, height: size }}
|
|
30
|
+
>
|
|
31
|
+
<User size={Math.round(size * 0.5)} className="text-accent" />
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function UserMenu() {
|
|
37
|
+
const authenticated = useGrooveStore((s) => s.marketplaceAuthenticated);
|
|
38
|
+
const user = useGrooveStore((s) => s.marketplaceUser);
|
|
39
|
+
const login = useGrooveStore((s) => s.marketplaceLogin);
|
|
40
|
+
const logout = useGrooveStore((s) => s.marketplaceLogout);
|
|
41
|
+
const [open, setOpen] = useState(false);
|
|
42
|
+
const ref = useRef(null);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!open) return;
|
|
46
|
+
function handleClick(e) {
|
|
47
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
48
|
+
}
|
|
49
|
+
document.addEventListener('mousedown', handleClick);
|
|
50
|
+
return () => document.removeEventListener('mousedown', handleClick);
|
|
51
|
+
}, [open]);
|
|
52
|
+
|
|
53
|
+
if (!authenticated) {
|
|
54
|
+
return (
|
|
55
|
+
<button
|
|
56
|
+
onClick={login}
|
|
57
|
+
className="flex items-center gap-1.5 h-7 px-3 rounded-md bg-surface-1 border border-border-subtle text-xs font-semibold font-sans text-text-2 hover:text-text-0 hover:border-border transition-colors cursor-pointer select-none flex-shrink-0"
|
|
58
|
+
>
|
|
59
|
+
<LogIn size={12} />
|
|
60
|
+
Sign in
|
|
61
|
+
</button>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div ref={ref} className="relative flex-shrink-0">
|
|
67
|
+
<button
|
|
68
|
+
onClick={() => setOpen(!open)}
|
|
69
|
+
className={cn(
|
|
70
|
+
'flex items-center gap-2 h-7 pl-1 pr-2 rounded-md transition-colors cursor-pointer select-none',
|
|
71
|
+
open ? 'bg-surface-1 border border-border' : 'hover:bg-surface-1 border border-transparent',
|
|
72
|
+
)}
|
|
73
|
+
>
|
|
74
|
+
<ProfilePic user={user} size={20} />
|
|
75
|
+
<span className="text-xs text-text-1 font-sans font-medium max-w-[100px] truncate">
|
|
76
|
+
{user?.displayName || user?.id || 'Account'}
|
|
77
|
+
</span>
|
|
78
|
+
<ChevronDown size={10} className={cn('text-text-4 transition-transform', open && 'rotate-180')} />
|
|
79
|
+
</button>
|
|
80
|
+
|
|
81
|
+
{open && (
|
|
82
|
+
<div className="absolute right-0 top-full mt-1 w-48 py-1 rounded-md bg-surface-1 border border-border shadow-lg z-50">
|
|
83
|
+
<div className="px-3 py-2 border-b border-border-subtle">
|
|
84
|
+
<p className="text-xs font-medium text-text-0 font-sans truncate">{user?.displayName || 'Account'}</p>
|
|
85
|
+
{user?.email && <p className="text-2xs text-text-4 font-sans truncate">{user.email}</p>}
|
|
86
|
+
</div>
|
|
87
|
+
<a
|
|
88
|
+
href="https://docs.groovedev.ai"
|
|
89
|
+
target="_blank"
|
|
90
|
+
rel="noopener noreferrer"
|
|
91
|
+
onClick={() => setOpen(false)}
|
|
92
|
+
className="flex items-center gap-2 px-3 py-1.5 text-xs text-text-2 hover:text-text-0 hover:bg-surface-3 font-sans cursor-pointer transition-colors"
|
|
93
|
+
>
|
|
94
|
+
<BookOpen size={12} />
|
|
95
|
+
Docs
|
|
96
|
+
<ExternalLink size={9} className="ml-auto text-text-4" />
|
|
97
|
+
</a>
|
|
98
|
+
<a
|
|
99
|
+
href="https://groovedev.ai"
|
|
100
|
+
target="_blank"
|
|
101
|
+
rel="noopener noreferrer"
|
|
102
|
+
onClick={() => setOpen(false)}
|
|
103
|
+
className="flex items-center gap-2 px-3 py-1.5 text-xs text-text-2 hover:text-text-0 hover:bg-surface-3 font-sans cursor-pointer transition-colors"
|
|
104
|
+
>
|
|
105
|
+
<ExternalLink size={12} />
|
|
106
|
+
groovedev.ai
|
|
107
|
+
</a>
|
|
108
|
+
<div className="my-1 h-px bg-border-subtle" />
|
|
109
|
+
<button
|
|
110
|
+
onClick={() => { setOpen(false); logout(); }}
|
|
111
|
+
className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-text-3 hover:text-danger hover:bg-surface-3 font-sans cursor-pointer transition-colors"
|
|
112
|
+
>
|
|
113
|
+
<LogOut size={12} />
|
|
114
|
+
Sign out
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
6
122
|
const VIEW_LABELS = {
|
|
7
123
|
agents: 'Agents',
|
|
8
124
|
editor: 'Editor',
|
|
@@ -17,7 +133,6 @@ export function BreadcrumbBar({
|
|
|
17
133
|
daemonHost,
|
|
18
134
|
editorActiveFile,
|
|
19
135
|
onOpenCommandPalette,
|
|
20
|
-
onSpawn,
|
|
21
136
|
}) {
|
|
22
137
|
const crumbs = ['Groove', VIEW_LABELS[activeView] || activeView];
|
|
23
138
|
if (activeView === 'editor' && editorActiveFile) {
|
|
@@ -27,48 +142,45 @@ export function BreadcrumbBar({
|
|
|
27
142
|
const electron = isElectron();
|
|
28
143
|
const darwinDrag = electron && getPlatform() === 'darwin';
|
|
29
144
|
|
|
145
|
+
const [instanceName, setInstanceName] = useState(null);
|
|
146
|
+
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (window.groove?.getInstanceInfo) {
|
|
149
|
+
window.groove.getInstanceInfo().then(info => {
|
|
150
|
+
if (info?.name) setInstanceName(info.name);
|
|
151
|
+
});
|
|
152
|
+
} else {
|
|
153
|
+
const param = new URLSearchParams(window.location.search).get('instance');
|
|
154
|
+
if (param) setInstanceName(param);
|
|
155
|
+
}
|
|
156
|
+
}, []);
|
|
157
|
+
|
|
30
158
|
return (
|
|
31
159
|
<header
|
|
32
160
|
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-
|
|
161
|
+
'h-11 flex-shrink-0 flex items-center gap-3 px-4 bg-surface-3 border-b border-border relative',
|
|
162
|
+
darwinDrag && 'pl-24 electron-drag electron-no-drag-children',
|
|
35
163
|
)}
|
|
36
164
|
>
|
|
37
|
-
{/* Logo */}
|
|
38
|
-
<img src="/favicon.png" alt="Groove" className="h-7 w-7 rounded-full flex-shrink-0" />
|
|
39
|
-
|
|
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
|
-
})()}
|
|
165
|
+
{/* Logo — web only (Electron shows it in the sidebar) */}
|
|
166
|
+
{!darwinDrag && <img src="/favicon.png" alt="Groove" className="h-7 w-7 rounded-full flex-shrink-0" />}
|
|
55
167
|
|
|
56
|
-
|
|
168
|
+
{/* Project name badge — clickable to open folder */}
|
|
169
|
+
{instanceName && (
|
|
170
|
+
<button
|
|
171
|
+
onClick={() => window.groove?.openFolder?.()}
|
|
172
|
+
className="text-2xs font-mono font-semibold text-accent bg-accent/10 px-1.5 py-0.5 rounded flex-shrink-0 hover:bg-accent/20 transition-colors cursor-pointer"
|
|
173
|
+
>
|
|
174
|
+
{instanceName}
|
|
175
|
+
</button>
|
|
176
|
+
)}
|
|
57
177
|
|
|
58
|
-
{/*
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
'text-xs text-text-4 font-sans',
|
|
65
|
-
'hover:border-border hover:text-text-3 transition-colors cursor-pointer',
|
|
66
|
-
)}
|
|
67
|
-
>
|
|
68
|
-
<Search size={14} className="flex-shrink-0" />
|
|
69
|
-
<span className="flex-1 text-left">Search commands...</span>
|
|
70
|
-
<kbd className="text-2xs font-mono bg-surface-4 px-1.5 py-0.5 rounded-full text-text-4">Cmd+K</kbd>
|
|
71
|
-
</button>
|
|
178
|
+
{/* Host badge — show raw host when no instance */}
|
|
179
|
+
{!instanceName && daemonHost && (
|
|
180
|
+
<span className="text-2xs font-mono font-semibold text-text-3 bg-surface-5 px-1.5 py-0.5 rounded flex-shrink-0">
|
|
181
|
+
{daemonHost}
|
|
182
|
+
</span>
|
|
183
|
+
)}
|
|
72
184
|
|
|
73
185
|
<div className="flex-1 min-w-4" />
|
|
74
186
|
|
|
@@ -84,16 +196,22 @@ export function BreadcrumbBar({
|
|
|
84
196
|
))}
|
|
85
197
|
</div>
|
|
86
198
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
199
|
+
<UserMenu />
|
|
200
|
+
|
|
201
|
+
{/* Command palette — absolutely centered to header */}
|
|
202
|
+
<button
|
|
203
|
+
onClick={onOpenCommandPalette}
|
|
204
|
+
className={cn(
|
|
205
|
+
'absolute left-1/2 -translate-x-1/2 flex items-center gap-2.5 h-8 px-4 rounded-md w-full max-w-md',
|
|
206
|
+
'bg-surface-1 border border-border-subtle',
|
|
207
|
+
'text-xs text-text-4 font-sans',
|
|
208
|
+
'hover:border-border hover:text-text-3 transition-colors cursor-pointer',
|
|
209
|
+
)}
|
|
210
|
+
>
|
|
211
|
+
<Search size={14} className="flex-shrink-0" />
|
|
212
|
+
<span className="flex-1 text-left">Search commands...</span>
|
|
213
|
+
<kbd className="text-2xs font-mono bg-surface-4 px-1.5 py-0.5 rounded text-text-4 ml-1">Cmd+K</kbd>
|
|
214
|
+
</button>
|
|
97
215
|
</header>
|
|
98
216
|
);
|
|
99
217
|
}
|
|
@@ -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, Radio, ExternalLink,
|
|
7
|
+
Search, Radio, ExternalLink, FolderOpen, Globe,
|
|
8
8
|
} from 'lucide-react';
|
|
9
9
|
import { cn } from '../../lib/cn';
|
|
10
10
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
@@ -15,6 +15,7 @@ const STATIC_COMMANDS = [
|
|
|
15
15
|
{ id: 'nav:dashboard', label: 'Go to Dashboard', icon: ChartSpline, category: 'Navigation', action: (s) => { s.setActiveView('dashboard'); } },
|
|
16
16
|
{ id: 'nav:marketplace', label: 'Go to Marketplace', icon: Puzzle, category: 'Navigation', action: (s) => { s.setActiveView('marketplace'); } },
|
|
17
17
|
{ id: 'nav:teams', label: 'Go to Teams', icon: Users, category: 'Navigation', action: (s) => { s.setActiveView('teams'); } },
|
|
18
|
+
{ id: 'nav:federation', label: 'Go to Federation', icon: Globe, category: 'Navigation', action: (s) => { s.setActiveView('federation'); } },
|
|
18
19
|
{ id: 'action:spawn', label: 'Spawn Agent', icon: Plus, category: 'Actions', action: (s) => { s.openDetail({ type: 'spawn' }); } },
|
|
19
20
|
{ id: 'action:terminal', label: 'Toggle Terminal', icon: Terminal, category: 'Actions', action: (s) => { s.setTerminalVisible(!s.terminalVisible); }, shortcut: 'Cmd+J' },
|
|
20
21
|
{ id: 'action:journalist', label: 'Toggle Journalist', icon: Newspaper, category: 'Actions', action: (s) => {
|
|
@@ -51,7 +52,10 @@ export function CommandPalette() {
|
|
|
51
52
|
: { id: `tunnel:connect:${t.id}`, label: `Connect to ${t.name}`, icon: Radio, category: 'Remote', action: (s) => { s.connectTunnel(t.id); } }
|
|
52
53
|
),
|
|
53
54
|
];
|
|
54
|
-
|
|
55
|
+
const windowCommands = window.groove?.openFolder ? [
|
|
56
|
+
{ id: 'action:openfolder', label: 'Open Folder', icon: FolderOpen, category: 'Window', shortcut: 'Cmd+O', action: () => window.groove.openFolder() },
|
|
57
|
+
] : [];
|
|
58
|
+
return [...STATIC_COMMANDS, ...windowCommands, ...agentCommands, ...tunnelCommands];
|
|
55
59
|
}, [agents, savedTunnels]);
|
|
56
60
|
|
|
57
61
|
// Filter
|
|
@@ -63,26 +63,27 @@ export function TerminalPanel({
|
|
|
63
63
|
)}
|
|
64
64
|
|
|
65
65
|
{/* Header bar */}
|
|
66
|
-
<div className="flex items-center h-9 bg-surface-1 border-b border-border
|
|
66
|
+
<div className="flex items-center h-9 bg-surface-1 border-b border-border flex-shrink-0 px-3">
|
|
67
67
|
{/* Tabs */}
|
|
68
|
-
<div className="flex items-center gap-0 flex-1 min-w-0 overflow-x-auto scrollbar-none">
|
|
68
|
+
<div className="flex items-center gap-0 flex-1 min-w-0 overflow-x-auto scrollbar-none h-full">
|
|
69
69
|
{tabList.map((tab) => (
|
|
70
70
|
<button
|
|
71
71
|
key={tab.id}
|
|
72
72
|
onClick={() => onSelectTab?.(tab.id)}
|
|
73
73
|
className={cn(
|
|
74
|
-
'flex items-center gap-1.5
|
|
74
|
+
'inline-flex items-center gap-1.5 px-3 h-full text-xs font-medium font-sans cursor-pointer select-none transition-colors duration-100 flex-shrink-0',
|
|
75
|
+
'border-t',
|
|
75
76
|
tab.id === activeTab
|
|
76
|
-
? 'text-text-0
|
|
77
|
-
: 'text-text-
|
|
77
|
+
? 'text-text-0 border-accent'
|
|
78
|
+
: 'text-text-2 border-transparent hover:text-text-0 hover:bg-surface-5/50',
|
|
78
79
|
)}
|
|
79
80
|
>
|
|
80
|
-
<Terminal size={
|
|
81
|
-
<span className="truncate max-w-[
|
|
81
|
+
<Terminal size={11} />
|
|
82
|
+
<span className="truncate max-w-[100px]">{tab.label}</span>
|
|
82
83
|
{tabList.length > 1 && (
|
|
83
84
|
<button
|
|
84
85
|
onClick={(e) => { e.stopPropagation(); onCloseTab?.(tab.id); }}
|
|
85
|
-
className="ml-
|
|
86
|
+
className="ml-1 p-0.5 rounded hover:bg-surface-5 text-text-4 hover:text-text-1 cursor-pointer"
|
|
86
87
|
>
|
|
87
88
|
<X size={9} />
|
|
88
89
|
</button>
|
|
@@ -91,7 +92,7 @@ export function TerminalPanel({
|
|
|
91
92
|
))}
|
|
92
93
|
<button
|
|
93
94
|
onClick={onAddTab}
|
|
94
|
-
className="flex items-center justify-center w-6 h-6 text-text-
|
|
95
|
+
className="flex items-center justify-center w-6 h-6 text-text-3 hover:text-text-0 hover:bg-surface-5/50 rounded cursor-pointer transition-colors flex-shrink-0 ml-1"
|
|
95
96
|
title="New terminal"
|
|
96
97
|
>
|
|
97
98
|
<Plus size={11} />
|
|
@@ -133,7 +133,15 @@ export function RepoImport() {
|
|
|
133
133
|
<Button
|
|
134
134
|
variant="ghost"
|
|
135
135
|
size="sm"
|
|
136
|
-
onClick={() =>
|
|
136
|
+
onClick={() => {
|
|
137
|
+
try {
|
|
138
|
+
const fullUrl = url.startsWith('http') ? url : `https://${url}`;
|
|
139
|
+
const parsed = new URL(fullUrl);
|
|
140
|
+
if (parsed.protocol === 'https:' || parsed.protocol === 'http:') {
|
|
141
|
+
window.open(fullUrl, '_blank');
|
|
142
|
+
}
|
|
143
|
+
} catch {}
|
|
144
|
+
}}
|
|
137
145
|
className="h-8 text-xs gap-1.5"
|
|
138
146
|
>
|
|
139
147
|
<ExternalLink size={12} />
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
2
|
+
|
|
3
|
+
import { motion } from 'framer-motion';
|
|
4
|
+
import { cn } from '../../lib/cn';
|
|
5
|
+
import { Badge } from '../ui/badge';
|
|
6
|
+
import { CheckCircle2, Download, Loader2, AlertCircle, Check, RotateCcw } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
export function ProviderCard({
|
|
9
|
+
id,
|
|
10
|
+
name,
|
|
11
|
+
subtitle,
|
|
12
|
+
models,
|
|
13
|
+
authType,
|
|
14
|
+
recommended,
|
|
15
|
+
installed,
|
|
16
|
+
installing,
|
|
17
|
+
failed,
|
|
18
|
+
selected,
|
|
19
|
+
onInstall,
|
|
20
|
+
gradientFrom,
|
|
21
|
+
letter,
|
|
22
|
+
statusChecking,
|
|
23
|
+
}) {
|
|
24
|
+
return (
|
|
25
|
+
<motion.div
|
|
26
|
+
initial={{ opacity: 0, y: 12 }}
|
|
27
|
+
animate={{ opacity: 1, y: 0 }}
|
|
28
|
+
transition={{ duration: 0.25 }}
|
|
29
|
+
className={cn(
|
|
30
|
+
'relative flex flex-col rounded-md border p-6 transition-all duration-200',
|
|
31
|
+
'bg-surface-2 hover:bg-surface-3 hover:shadow-lg hover:shadow-black/20',
|
|
32
|
+
selected
|
|
33
|
+
? 'border-accent ring-1 ring-accent/30 shadow-md shadow-accent/10'
|
|
34
|
+
: 'border-border-subtle',
|
|
35
|
+
installing && 'pointer-events-none opacity-70',
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
38
|
+
{/* Selected checkmark */}
|
|
39
|
+
{selected && (
|
|
40
|
+
<div className="absolute top-3 left-3 w-5 h-5 rounded-full bg-accent flex items-center justify-center">
|
|
41
|
+
<Check className="w-3 h-3 text-surface-0" strokeWidth={3} />
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{/* Header row */}
|
|
46
|
+
<div className="flex items-start gap-4 mb-5">
|
|
47
|
+
<div
|
|
48
|
+
className={cn(
|
|
49
|
+
'w-14 h-14 rounded-md flex items-center justify-center text-lg font-bold font-mono shrink-0',
|
|
50
|
+
gradientFrom,
|
|
51
|
+
)}
|
|
52
|
+
>
|
|
53
|
+
{letter}
|
|
54
|
+
</div>
|
|
55
|
+
<div className="min-w-0 flex-1">
|
|
56
|
+
<div className="flex items-center gap-2">
|
|
57
|
+
<h3 className="text-sm font-semibold text-text-0">{name}</h3>
|
|
58
|
+
{recommended && (
|
|
59
|
+
<Badge variant="purple" className="text-2xs">Recommended</Badge>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
<p className="text-xs text-text-2 mt-0.5">{subtitle}</p>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{/* Model tags */}
|
|
67
|
+
<div className="flex flex-wrap gap-1.5 mb-4">
|
|
68
|
+
{models.map((m) => (
|
|
69
|
+
<span key={m} className="text-xs text-text-2 bg-surface-4 px-2 py-0.5 rounded font-mono">
|
|
70
|
+
{m}
|
|
71
|
+
</span>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<p className="text-2xs text-text-3 mb-4">{authType}</p>
|
|
76
|
+
|
|
77
|
+
{/* Status / Action area */}
|
|
78
|
+
<div className="mt-auto pt-4 border-t border-border-subtle">
|
|
79
|
+
{installing ? (
|
|
80
|
+
<div className="space-y-2">
|
|
81
|
+
<div className="flex items-center gap-2">
|
|
82
|
+
<Loader2 className="w-4 h-4 text-accent animate-spin" />
|
|
83
|
+
<span className="text-xs text-accent font-medium">Installing...</span>
|
|
84
|
+
</div>
|
|
85
|
+
<div className="h-1 bg-surface-4 rounded-full overflow-hidden">
|
|
86
|
+
<motion.div
|
|
87
|
+
className="h-full bg-accent rounded-full"
|
|
88
|
+
initial={{ width: '0%' }}
|
|
89
|
+
animate={{ width: '90%' }}
|
|
90
|
+
transition={{ duration: 15, ease: 'easeOut' }}
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
) : installed ? (
|
|
95
|
+
<div className="flex items-center gap-2">
|
|
96
|
+
<CheckCircle2 className="w-4 h-4 text-success" />
|
|
97
|
+
<span className="text-xs text-success font-medium">Ready</span>
|
|
98
|
+
</div>
|
|
99
|
+
) : failed ? (
|
|
100
|
+
<div className="flex items-center justify-between">
|
|
101
|
+
<div className="flex items-center gap-2">
|
|
102
|
+
<AlertCircle className="w-4 h-4 text-danger" />
|
|
103
|
+
<span className="text-xs text-danger font-medium">Failed</span>
|
|
104
|
+
</div>
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
className="h-8 px-5 rounded-full text-xs font-medium bg-danger/15 text-danger hover:bg-danger/25 transition-colors duration-100 cursor-pointer focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-danger flex items-center gap-1.5"
|
|
108
|
+
onClick={(e) => { e.stopPropagation(); onInstall?.(id); }}
|
|
109
|
+
>
|
|
110
|
+
<RotateCcw className="w-3 h-3" />
|
|
111
|
+
Retry
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
) : statusChecking ? (
|
|
115
|
+
<div className="flex items-center gap-2">
|
|
116
|
+
<Loader2 className="w-3.5 h-3.5 text-text-4 animate-spin" />
|
|
117
|
+
<span className="text-xs text-text-4">Checking...</span>
|
|
118
|
+
</div>
|
|
119
|
+
) : (
|
|
120
|
+
<div className="flex items-center justify-end">
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
className="h-8 px-5 rounded-full text-xs font-medium bg-accent text-surface-0 hover:bg-accent/80 transition-colors duration-100 cursor-pointer focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent flex items-center gap-1.5"
|
|
124
|
+
onClick={(e) => { e.stopPropagation(); onInstall?.(id); }}
|
|
125
|
+
>
|
|
126
|
+
<Download className="w-3.5 h-3.5" />
|
|
127
|
+
Install
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
</motion.div>
|
|
133
|
+
);
|
|
134
|
+
}
|