Skip to content

Commit fdad29e

Browse files
committed
Improve connection tab label
1 parent 403721b commit fdad29e

4 files changed

Lines changed: 83 additions & 131 deletions

File tree

src/pages/ConnectionBar.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* and close button. Includes an "add" button to open the connect form.
66
*/
77

8+
import type { WorkspaceFolder } from '../bridge/types';
89
import type { Protocol } from '../bridge/utils';
910
import type { SessionStatus } from './RemoteSession';
1011

@@ -19,6 +20,8 @@ export interface ConnectionEntry {
1920
protocol?: Protocol;
2021
status: SessionStatus | 'idle';
2122
error?: string;
23+
/** Workspace folders reported by the server once the session is connected. */
24+
workspaceFolders?: (WorkspaceFolder | string)[];
2225
}
2326

2427
interface ConnectionBarProps {
@@ -49,15 +52,26 @@ export function ConnectionBar({
4952
<div className="conn-bar-tabs">
5053
{entries.map((entry) => {
5154
const isActive = entry.id === activeId;
55+
const ws = getWorkspaceLabel(entry.workspaceFolders);
56+
const tooltip = ws
57+
? `${ws.fullPath}\n${entry.host}`
58+
: entry.host;
5259
return (
5360
<button
5461
key={entry.id}
5562
className={`conn-tab ${isActive ? 'active' : ''}`}
5663
onClick={() => onSwitch(entry.id)}
57-
title={entry.host}
64+
title={tooltip}
5865
>
5966
<span className={`conn-dot ${dotClass(isActive ? entry.status : 'idle')}`} />
60-
<span className="conn-label">{formatHost(entry.host)}</span>
67+
{ws ? (
68+
<span className="conn-label conn-label-rich">
69+
<span className="conn-label-primary">{ws.name}</span>
70+
<span className="conn-label-secondary">{formatHost(entry.host)}</span>
71+
</span>
72+
) : (
73+
<span className="conn-label">{formatHost(entry.host)}</span>
74+
)}
6175
<span
6276
className="conn-close"
6377
role="button"
@@ -86,6 +100,27 @@ export function ConnectionBar({
86100
// Helpers
87101
// ---------------------------------------------------------------------------
88102

103+
/**
104+
* Extract a human-friendly workspace label from the first workspace folder.
105+
* Returns `null` when no workspace data is available yet (e.g. still connecting).
106+
*/
107+
function getWorkspaceLabel(
108+
folders?: (WorkspaceFolder | string)[],
109+
): { name: string; fullPath: string } | null {
110+
if (!folders || folders.length === 0) return null;
111+
const folder = folders[0];
112+
const fullPath =
113+
typeof folder === 'string'
114+
? folder
115+
: folder.uri?.replace(/^file:\/\//, '') ?? folder.name ?? '';
116+
if (!fullPath) return null;
117+
const name =
118+
(typeof folder === 'string' ? null : folder.name) ||
119+
fullPath.split('/').filter(Boolean).pop() ||
120+
fullPath;
121+
return { name, fullPath };
122+
}
123+
89124
/** Truncate a host string to fit in the tab bar. */
90125
function formatHost(host: string): string {
91126
const clean = host.replace(/^https?:\/\//, '');

src/pages/RemoteProduct.css

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
display: flex;
2727
align-items: center;
2828
gap: 0.25rem;
29-
height: 36px;
29+
height: 40px;
3030
padding: 0 0.75rem;
3131
background: #14142a;
3232
border-bottom: 1px solid #2a2a3e;
@@ -113,13 +113,35 @@
113113
50% { opacity: 0.3; }
114114
}
115115

116-
/* Label */
116+
/* Label — simple (host only, no workspace data yet) */
117117
.conn-label {
118118
max-width: 180px;
119119
overflow: hidden;
120120
text-overflow: ellipsis;
121121
}
122122

123+
/* Label — rich (workspace name + host) */
124+
.conn-label-rich {
125+
display: flex;
126+
flex-direction: column;
127+
line-height: 1.15;
128+
gap: 0;
129+
max-width: 180px;
130+
}
131+
132+
.conn-label-primary {
133+
overflow: hidden;
134+
text-overflow: ellipsis;
135+
font-weight: 500;
136+
}
137+
138+
.conn-label-secondary {
139+
overflow: hidden;
140+
text-overflow: ellipsis;
141+
font-size: 0.62rem;
142+
opacity: 0.5;
143+
}
144+
123145
/* Close button */
124146
.conn-close {
125147
display: flex;

src/pages/RemoteProduct.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,22 +218,41 @@ export function RemoteProduct() {
218218
const handleBridgeChange = useCallback((bridge: WebBridge | null) => {
219219
setActiveBridge(bridge);
220220
if (bridge) {
221+
// Capture the active connection ID for this bridge's callbacks.
222+
// Safe because RemoteSession is keyed by activeEntry.id — so the
223+
// bridge always corresponds to the connection that was active when
224+
// this callback was created.
225+
const connId = activeId;
226+
227+
/** Push workspace folders into both top-level state and the matching ConnectionEntry. */
228+
const syncFolders = () => {
229+
const folders = bridge.getWorkspaceFolders();
230+
setWorkspaceFolders(folders);
231+
if (connId && folders.length > 0) {
232+
setEntries((prev) =>
233+
prev.map((e) =>
234+
e.id === connId ? { ...e, workspaceFolders: folders } : e,
235+
),
236+
);
237+
}
238+
};
239+
221240
bridge.onChatListChanged((entries, selected) => {
222241
setChatEntries(entries);
223242
setSelectedChatId(selected);
224243
// Workspace folders become available after session:connected,
225244
// which fires before chats are restored — so pick them up here.
226-
setWorkspaceFolders(bridge.getWorkspaceFolders());
245+
syncFolders();
227246
});
228247
setChatEntries(bridge.getChatEntries());
229248
setSelectedChatId(bridge.getSelectedChatId());
230-
setWorkspaceFolders(bridge.getWorkspaceFolders());
249+
syncFolders();
231250
} else {
232251
setChatEntries([]);
233252
setSelectedChatId(null);
234253
setWorkspaceFolders([]);
235254
}
236-
}, []);
255+
}, [activeId]);
237256

238257
const toggleSidebar = useCallback(() => {
239258
setSidebarOpen((prev) => !prev);

test-lna.html

Lines changed: 0 additions & 124 deletions
This file was deleted.

0 commit comments

Comments
 (0)