groove-dev 0.27.34 → 0.27.36

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.
@@ -4,7 +4,7 @@ import { useGrooveStore } from '../../stores/groove';
4
4
  import { cn } from '../../lib/cn';
5
5
  import { AnimatePresence, motion } from 'framer-motion';
6
6
  import {
7
- Server, Radio, ExternalLink, Loader2, X, Plus, ArrowLeft,
7
+ Server, Radio, ExternalLink, Loader2, X, Plus, ArrowLeft, Unplug,
8
8
  } from 'lucide-react';
9
9
  import { StatusDot } from '../ui/status-dot';
10
10
  import { Button } from '../ui/button';
@@ -33,9 +33,12 @@ export function QuickConnect() {
33
33
  }
34
34
 
35
35
  function handleOpenRemote(server) {
36
- const port = server.localPort;
37
- const name = encodeURIComponent(server.name);
38
- window.open(`http://localhost:${port}?instance=${name}`, '_blank');
36
+ if (window.groove?.remote?.openWindow) {
37
+ window.groove.remote.openWindow(server.localPort, server.name);
38
+ } else {
39
+ const name = encodeURIComponent(server.name);
40
+ window.open(`http://localhost:${server.localPort}?instance=${name}`, '_blank');
41
+ }
39
42
  toggle();
40
43
  }
41
44
 
@@ -127,36 +130,58 @@ export function QuickConnect() {
127
130
  </div>
128
131
  ) : (
129
132
  savedTunnels.map((server) => (
130
- <button
133
+ <div
131
134
  key={server.id}
132
- onClick={() => server.active ? handleOpenRemote(server) : handleConnect(server.id)}
133
- disabled={connectingId === server.id}
134
135
  className={cn(
135
- 'w-full flex items-center gap-3 px-4 py-2.5 text-left cursor-pointer transition-colors',
136
+ 'w-full flex items-center gap-3 px-4 py-2.5 transition-colors',
136
137
  'hover:bg-surface-5',
137
138
  connectingId === server.id && 'opacity-60 pointer-events-none',
138
139
  )}
139
140
  >
140
141
  <Server size={15} className={server.active ? 'text-success' : 'text-text-4'} />
141
- <div className="flex-1 min-w-0">
142
+ <button
143
+ onClick={() => server.active ? handleOpenRemote(server) : handleConnect(server.id)}
144
+ disabled={connectingId === server.id}
145
+ className="flex-1 min-w-0 text-left cursor-pointer"
146
+ >
142
147
  <div className="flex items-center gap-2">
143
148
  <span className="text-sm font-medium text-text-0 font-sans truncate">{server.name}</span>
144
149
  {server.active && <StatusDot status="running" size="sm" />}
145
150
  </div>
146
151
  <span className="text-2xs text-text-4 font-mono">{server.user}@{server.host}</span>
147
- </div>
148
- <div className="flex-shrink-0">
152
+ </button>
153
+ <div className="flex items-center gap-1.5 flex-shrink-0">
149
154
  {connectingId === server.id ? (
150
155
  <Loader2 size={14} className="text-text-3 animate-spin" />
151
156
  ) : server.active ? (
152
- <span className="flex items-center gap-1 text-2xs text-success font-sans">
153
- <ExternalLink size={11} /> Open
154
- </span>
157
+ <>
158
+ <button
159
+ onClick={() => handleOpenRemote(server)}
160
+ className="flex items-center gap-1 text-2xs text-success font-sans hover:text-success/80 cursor-pointer transition-colors"
161
+ >
162
+ <ExternalLink size={11} /> Open
163
+ </button>
164
+ <button
165
+ onClick={async () => {
166
+ await useGrooveStore.getState().disconnectTunnel(server.id);
167
+ addToast('info', 'Disconnected', server.name);
168
+ }}
169
+ className="p-1 text-text-4 hover:text-danger cursor-pointer transition-colors rounded"
170
+ title="Disconnect"
171
+ >
172
+ <Unplug size={12} />
173
+ </button>
174
+ </>
155
175
  ) : (
156
- <span className="text-2xs text-text-3 font-sans">Connect</span>
176
+ <button
177
+ onClick={() => handleConnect(server.id)}
178
+ className="text-2xs text-text-3 font-sans hover:text-text-1 cursor-pointer transition-colors"
179
+ >
180
+ Connect
181
+ </button>
157
182
  )}
158
183
  </div>
159
- </button>
184
+ </div>
160
185
  ))
161
186
  )}
162
187
  </div>
@@ -1106,13 +1106,24 @@ export const useGrooveStore = create((set, get) => ({
1106
1106
  const result = await api.post(`/tunnels/${encodeURIComponent(id)}/connect`);
1107
1107
  set({ activeTunnelId: id });
1108
1108
  get().fetchTunnels();
1109
+ if (result.localPort && result.name) {
1110
+ if (window.groove?.remote?.openWindow) {
1111
+ window.groove.remote.openWindow(result.localPort, result.name);
1112
+ } else {
1113
+ window.open(`http://localhost:${result.localPort}?instance=${encodeURIComponent(result.name)}`, '_blank');
1114
+ }
1115
+ }
1109
1116
  return result;
1110
1117
  },
1111
1118
 
1112
1119
  async disconnectTunnel(id) {
1120
+ const tunnel = get().savedTunnels.find(t => t.id === id);
1113
1121
  await api.post(`/tunnels/${encodeURIComponent(id)}/disconnect`);
1114
1122
  set({ activeTunnelId: null });
1115
1123
  get().fetchTunnels();
1124
+ if (tunnel?.localPort && window.groove?.remote?.closeByPort) {
1125
+ window.groove.remote.closeByPort(tunnel.localPort);
1126
+ }
1116
1127
  },
1117
1128
 
1118
1129
  async installTunnel(id) {