Skip to content

Commit 17e0a28

Browse files
committed
Show workspace roots
1 parent a147706 commit 17e0a28

4 files changed

Lines changed: 60 additions & 3 deletions

File tree

src/bridge/transport.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import type {
3535
SSESessionConnectedPayload,
3636
SSESessionMessagePayload,
3737
SSETrustUpdatedPayload,
38+
WorkspaceFolder,
3839
} from './types';
3940

4041
/** Timeout for the initial SSE handshake (session:connected). */
@@ -340,6 +341,11 @@ export class WebBridge {
340341
return this.currentChatId;
341342
}
342343

344+
/** Get the workspace folders from the current session (may be plain path strings). */
345+
getWorkspaceFolders(): (WorkspaceFolder | string)[] {
346+
return (this.sessionState?.workspaceFolders as (WorkspaceFolder | string)[] | undefined) ?? [];
347+
}
348+
343349
/** Select a chat by ID — dispatches to the webview, loads messages if needed. */
344350
async selectChat(chatId: string): Promise<void> {
345351
this.currentChatId = chatId;

src/components/ChatSidebar.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,35 @@
4949
border-color: #00a5b8;
5050
}
5151

52+
/* Workspace root indicator — non-interactive label */
53+
.chat-sidebar-workspace {
54+
display: flex;
55+
align-items: center;
56+
gap: 0.4rem;
57+
padding: 0.6rem 0.75rem;
58+
border-bottom: 1px solid #2a2a3e;
59+
flex-shrink: 0;
60+
color: #5a6a7a;
61+
font-size: 0.72rem;
62+
font-weight: 600;
63+
letter-spacing: 0.03em;
64+
cursor: default;
65+
user-select: none;
66+
}
67+
68+
.chat-sidebar-workspace .codicon {
69+
font-size: 12px;
70+
flex-shrink: 0;
71+
opacity: 0.7;
72+
}
73+
74+
.chat-sidebar-workspace-name {
75+
overflow: hidden;
76+
text-overflow: ellipsis;
77+
white-space: nowrap;
78+
min-width: 0;
79+
}
80+
5281
/* Scrollable list */
5382
.chat-sidebar-list {
5483
flex: 1;

src/components/ChatSidebar.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import { useCallback } from 'react';
1313
import type { WebBridge } from '../bridge/transport';
14-
import type { ChatEntry } from '../bridge/types';
14+
import type { ChatEntry, WorkspaceFolder } from '../bridge/types';
1515
import './ChatSidebar.css';
1616

1717
// ---------------------------------------------------------------------------
@@ -22,6 +22,8 @@ interface ChatSidebarProps {
2222
bridge: WebBridge | null;
2323
chats: ChatEntry[];
2424
selectedId: string | null;
25+
/** Workspace folders from the current server session (may be plain path strings). */
26+
workspaceFolders?: (WorkspaceFolder | string)[];
2527
/** Whether the mobile drawer is open (controlled by parent). */
2628
mobileOpen: boolean;
2729
/** Called when the mobile drawer should close (backdrop tap, item select). */
@@ -49,7 +51,7 @@ export function ChatSidebarToggle({ onClick }: { onClick: () => void }) {
4951
// Main sidebar component
5052
// ---------------------------------------------------------------------------
5153

52-
export function ChatSidebar({ bridge, chats = [], selectedId, mobileOpen, onMobileClose }: ChatSidebarProps) {
54+
export function ChatSidebar({ bridge, chats = [], selectedId, workspaceFolders = [], mobileOpen, onMobileClose }: ChatSidebarProps) {
5355
const handleSelect = useCallback(
5456
(chatId: string) => {
5557
bridge?.selectChat(chatId);
@@ -81,6 +83,19 @@ export function ChatSidebar({ bridge, chats = [], selectedId, mobileOpen, onMobi
8183

8284
{/* Sidebar panel */}
8385
<div className={`chat-sidebar ${mobileOpen ? 'open' : ''}`}>
86+
{workspaceFolders.length > 0 && (() => {
87+
const folder = workspaceFolders[0];
88+
// Server sends workspace folders as plain path strings OR {name, uri} objects
89+
const fullPath = typeof folder === 'string' ? folder : (folder.uri?.replace(/^file:\/\//, '') ?? folder.name ?? '');
90+
const displayName = (typeof folder === 'string' ? null : folder.name) || fullPath.split('/').filter(Boolean).pop() || fullPath;
91+
return (
92+
<div className="chat-sidebar-workspace" title={fullPath}>
93+
<i className="codicon codicon-folder" />
94+
<span className="chat-sidebar-workspace-name">{displayName}</span>
95+
</div>
96+
);
97+
})()}
98+
8499
<div className="chat-sidebar-header">
85100
<span className="chat-sidebar-title">Chats</span>
86101
<button

src/pages/RemoteProduct.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import { useCallback, useEffect, useRef, useState } from 'react';
1414
import { probePort, testConnection } from '../bridge/connection';
1515
import type { WebBridge } from '../bridge/transport';
16-
import type { ChatEntry } from '../bridge/types';
16+
import type { ChatEntry, WorkspaceFolder } from '../bridge/types';
1717
import type { Protocol } from '../bridge/utils';
1818
import { ChatSidebar, ChatSidebarToggle } from '../components/ChatSidebar';
1919
import {
@@ -44,6 +44,7 @@ export function RemoteProduct() {
4444
const [sidebarOpen, setSidebarOpen] = useState(false);
4545
const [chatEntries, setChatEntries] = useState<ChatEntry[]>([]);
4646
const [selectedChatId, setSelectedChatId] = useState<string | null>(null);
47+
const [workspaceFolders, setWorkspaceFolders] = useState<(WorkspaceFolder | string)[]>([]);
4748
const [discovery, setDiscovery] = useState<DiscoveryProgress | null>(null);
4849
const discoveryAbortRef = useRef<AbortController | null>(null);
4950

@@ -220,12 +221,17 @@ export function RemoteProduct() {
220221
bridge.onChatListChanged((entries, selected) => {
221222
setChatEntries(entries);
222223
setSelectedChatId(selected);
224+
// Workspace folders become available after session:connected,
225+
// which fires before chats are restored — so pick them up here.
226+
setWorkspaceFolders(bridge.getWorkspaceFolders());
223227
});
224228
setChatEntries(bridge.getChatEntries());
225229
setSelectedChatId(bridge.getSelectedChatId());
230+
setWorkspaceFolders(bridge.getWorkspaceFolders());
226231
} else {
227232
setChatEntries([]);
228233
setSelectedChatId(null);
234+
setWorkspaceFolders([]);
229235
}
230236
}, []);
231237

@@ -258,6 +264,7 @@ export function RemoteProduct() {
258264
bridge={activeBridge}
259265
chats={chatEntries}
260266
selectedId={selectedChatId}
267+
workspaceFolders={workspaceFolders}
261268
mobileOpen={sidebarOpen}
262269
onMobileClose={() => setSidebarOpen(false)}
263270
/>

0 commit comments

Comments
 (0)