mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-30 04:30:48 +00:00
feat(desktop): componentize workspace shell
This commit is contained in:
parent
f2bdecc489
commit
f9fc432b8e
15 changed files with 1610 additions and 882 deletions
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2026 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type {
|
||||
DesktopProject,
|
||||
DesktopSessionSummary,
|
||||
} from '../../api/client.js';
|
||||
import { ThreadList } from './ThreadList.js';
|
||||
import type { LoadState } from './types.js';
|
||||
|
||||
export function ProjectSidebar({
|
||||
activeProject,
|
||||
activeProjectId,
|
||||
activeSessionId,
|
||||
loadState,
|
||||
projects,
|
||||
sessions,
|
||||
onChooseWorkspace,
|
||||
onCreateSession,
|
||||
onSelectProject,
|
||||
onSelectSession,
|
||||
}: {
|
||||
activeProject: DesktopProject | null;
|
||||
activeProjectId: string | null;
|
||||
activeSessionId: string | null;
|
||||
loadState: LoadState;
|
||||
projects: DesktopProject[];
|
||||
sessions: DesktopSessionSummary[];
|
||||
onChooseWorkspace: () => void;
|
||||
onCreateSession: () => void;
|
||||
onSelectProject: (projectId: string) => void;
|
||||
onSelectSession: (sessionId: string) => void;
|
||||
}) {
|
||||
return (
|
||||
<aside
|
||||
className="sidebar"
|
||||
aria-label="Projects and threads"
|
||||
data-testid="project-sidebar"
|
||||
>
|
||||
<div className="brand-lockup">
|
||||
<div className="brand-mark" aria-hidden="true">
|
||||
Q
|
||||
</div>
|
||||
<div>
|
||||
<h1>Qwen Code</h1>
|
||||
<p>Desktop</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className="sidebar-section">
|
||||
<h2>Projects</h2>
|
||||
<div className="workspace-path">
|
||||
{activeProject?.path || 'No folder selected'}
|
||||
</div>
|
||||
<button
|
||||
className="secondary-button"
|
||||
type="button"
|
||||
onClick={onChooseWorkspace}
|
||||
>
|
||||
Open Project
|
||||
</button>
|
||||
<ProjectList
|
||||
activeProjectId={activeProjectId}
|
||||
projects={projects}
|
||||
onSelect={onSelectProject}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section className="sidebar-section sidebar-section-fill">
|
||||
<h2>Threads</h2>
|
||||
<button
|
||||
className="primary-button"
|
||||
disabled={loadState.state !== 'ready' || !activeProject}
|
||||
type="button"
|
||||
onClick={onCreateSession}
|
||||
>
|
||||
New Thread
|
||||
</button>
|
||||
<ThreadList
|
||||
activeSessionId={activeSessionId}
|
||||
sessions={sessions}
|
||||
onSelect={onSelectSession}
|
||||
/>
|
||||
</section>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
function ProjectList({
|
||||
activeProjectId,
|
||||
projects,
|
||||
onSelect,
|
||||
}: {
|
||||
activeProjectId: string | null;
|
||||
projects: DesktopProject[];
|
||||
onSelect: (projectId: string) => void;
|
||||
}) {
|
||||
if (projects.length === 0) {
|
||||
return <div className="empty-row">No recent projects</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="project-list"
|
||||
aria-label="Projects"
|
||||
data-testid="project-list"
|
||||
>
|
||||
{projects.map((project) => (
|
||||
<button
|
||||
className={
|
||||
project.id === activeProjectId
|
||||
? 'project-row project-row-active'
|
||||
: 'project-row'
|
||||
}
|
||||
key={project.id}
|
||||
onClick={() => onSelect(project.id)}
|
||||
type="button"
|
||||
>
|
||||
<span>{project.name}</span>
|
||||
<small>{project.gitBranch || 'No Git branch'}</small>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue