refactor: simplify workspace panel terminal flow

This commit is contained in:
Federico Jaramillo Martinez
2026-05-21 09:31:33 +02:00
parent fb7903f3cb
commit c740ac3dcb
12 changed files with 65 additions and 129 deletions
+34
View File
@@ -52,6 +52,40 @@ export type AuthDialogState =
| { step: "oauth"; flow: OAuthFlowState; responding?: boolean; inputValue?: string; error?: string }
| { step: "logout"; providers: AuthProviderOption[] };
export type WorkspaceScopedStateReset = Pick<AppState,
| "sessions"
| "fileTree"
| "expandedDirs"
| "selectedFilePath"
| "selectedFileContent"
| "fileTreeStale"
| "gitStatus"
| "selectedDiffPath"
| "selectedDiff"
| "selectedStagedDiff"
| "gitStale"
| "selectedTerminalId"
| "error"
>;
export function resetWorkspaceScopedState(): WorkspaceScopedStateReset {
return {
sessions: [],
fileTree: [],
expandedDirs: {},
selectedFilePath: undefined,
selectedFileContent: undefined,
fileTreeStale: false,
gitStatus: undefined,
selectedDiffPath: undefined,
selectedDiff: undefined,
selectedStagedDiff: undefined,
gitStale: false,
selectedTerminalId: undefined,
error: "",
};
}
export function initialAppState(): AppState {
return {
projects: [],
+4 -2
View File
@@ -414,8 +414,10 @@ export class PiWebApp extends LitElement {
}
private renderWorkspacePanel() {
const workspaceLabelItems = this.state.selectedWorkspace === undefined ? [] : this.plugins.getWorkspaceLabelItems(this.state, this.state.selectedWorkspace);
return html`<workspace-panel .workspace=${this.state.selectedWorkspace} .appState=${this.state} .tool=${this.state.workspaceTool} .panels=${this.visibleWorkspacePanels()} .workspaceLabelItems=${workspaceLabelItems} .fileTree=${this.state.fileTree} .expandedDirs=${this.state.expandedDirs} .selectedFilePath=${this.state.selectedFilePath} .selectedFileContent=${this.state.selectedFileContent} .fileTreeStale=${this.state.fileTreeStale} .gitStatus=${this.state.gitStatus} .selectedDiffPath=${this.state.selectedDiffPath} .selectedDiff=${this.state.selectedDiff} .selectedStagedDiff=${this.state.selectedStagedDiff} .gitStale=${this.state.gitStale} .activeTerminalCount=${this.state.activeTerminalCount} .selectedTerminalId=${this.state.selectedTerminalId} .terminalAutoStart=${this.terminalAutoStartWorkspaceId === this.state.selectedWorkspace?.id} .openTerminal=${(options?: { terminalId?: string | undefined }) => { this.openTerminal(options); }} .onSelectTool=${(tool: QualifiedContributionId) => { this.openWorkspaceTool(tool); }} .onRefreshFiles=${() => this.files.refreshFiles()} .onExpandDir=${(path: string) => this.files.expandDir(path)} .onSelectFile=${(path: string) => this.files.selectFile(path)} .onRefreshGit=${() => this.git.refreshGit()} .onSelectDiff=${(path: string) => this.git.selectDiff(path)} .onSelectTerminal=${(terminalId: string | undefined, options?: { replace?: boolean | undefined }) => { this.selectTerminal(terminalId, options); }}></workspace-panel>`;
const workspace = this.state.selectedWorkspace;
const panelContext = workspace === undefined ? undefined : this.createWorkspacePanelContext(workspace);
const workspaceLabelItems = workspace === undefined ? [] : this.plugins.getWorkspaceLabelItems(this.state, workspace);
return html`<workspace-panel .workspace=${workspace} .panelContext=${panelContext} .tool=${this.state.workspaceTool} .panels=${this.visibleWorkspacePanels()} .workspaceLabelItems=${workspaceLabelItems} .onSelectTool=${(tool: QualifiedContributionId) => { this.openWorkspaceTool(tool); }}></workspace-panel>`;
}
private renderNavigationPanel(autoSwitchToChat: boolean) {
+4 -51
View File
@@ -1,7 +1,6 @@
import { LitElement, html, type TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import type { FileContentResponse, FileTreeEntry, GitDiffResponse, GitStatusResponse, Workspace } from "../api";
import type { AppState } from "../appState";
import type { Workspace } from "../api";
import type { QualifiedContributionId, QualifiedWorkspacePanelContribution, WorkspaceLabelItem, WorkspacePanelContext } from "../plugins/types";
import { workspacePanelStyles } from "./shared";
import { renderWorkspaceLabel } from "./workspaceLabel";
@@ -9,32 +8,12 @@ import { renderWorkspaceLabel } from "./workspaceLabel";
@customElement("workspace-panel")
export class WorkspacePanel extends LitElement {
@property({ attribute: false }) workspace: Workspace | undefined;
@property({ attribute: false }) appState!: AppState;
@property({ attribute: false }) panelContext: WorkspacePanelContext | undefined;
@property() tool: QualifiedContributionId = "core:workspace.files";
@property({ attribute: false }) panels: QualifiedWorkspacePanelContribution[] = [];
@property({ attribute: false }) workspaceLabelItems: WorkspaceLabelItem[] = [];
@property({ type: Boolean }) hideToolTabs = false;
@property({ attribute: false }) fileTree: FileTreeEntry[] = [];
@property({ attribute: false }) expandedDirs: Record<string, FileTreeEntry[]> = {};
@property({ attribute: false }) selectedFilePath: string | undefined;
@property({ attribute: false }) selectedFileContent: FileContentResponse | undefined;
@property({ type: Boolean }) fileTreeStale = false;
@property({ attribute: false }) gitStatus: GitStatusResponse | undefined;
@property({ attribute: false }) selectedDiffPath: string | undefined;
@property({ attribute: false }) selectedDiff: GitDiffResponse | undefined;
@property({ attribute: false }) selectedStagedDiff: GitDiffResponse | undefined;
@property({ type: Boolean }) gitStale = false;
@property({ attribute: false }) onSelectTool: (tool: QualifiedContributionId) => void = () => undefined;
@property({ attribute: false }) onRefreshFiles: () => void = () => undefined;
@property({ attribute: false }) onExpandDir: (path: string) => void = () => undefined;
@property({ attribute: false }) onSelectFile: (path: string) => void = () => undefined;
@property({ attribute: false }) onRefreshGit: () => void = () => undefined;
@property({ attribute: false }) onSelectDiff: (path: string) => void = () => undefined;
@property({ type: Number }) activeTerminalCount = 0;
@property({ attribute: false }) selectedTerminalId: string | undefined;
@property({ type: Boolean }) terminalAutoStart = false;
@property({ attribute: false }) openTerminal: (options?: { terminalId?: string | undefined }) => void = () => undefined;
@property({ attribute: false }) onSelectTerminal: (terminalId: string | undefined, options?: { replace?: boolean | undefined }) => void = () => undefined;
@query(".workspace-header-strip") private workspaceHeaderStrip?: HTMLElement | null;
@state() private workspaceHeaderCanScrollLeft = false;
@state() private workspaceHeaderCanScrollRight = false;
@@ -65,9 +44,10 @@ export class WorkspacePanel extends LitElement {
override render() {
const workspace = this.workspace;
if (workspace === undefined) return html`<section class="empty">Select a workspace.</section>`;
const context = this.panelContext;
if (context === undefined) return html`<section class="empty">Workspace panel unavailable.</section>`;
const visiblePanels = this.panels;
const selectedPanel = visiblePanels.find((panel) => panel.id === this.tool) ?? visiblePanels[0];
const context = this.createPanelContext(workspace);
return html`
<header>
<div class=${this.workspaceHeaderFrameClass()}>
@@ -128,32 +108,5 @@ export class WorkspacePanel extends LitElement {
return strip instanceof HTMLElement ? strip : undefined;
}
private createPanelContext(workspace: Workspace): WorkspacePanelContext {
return {
workspace,
state: this.appState,
fileTree: this.fileTree,
expandedDirs: this.expandedDirs,
selectedFilePath: this.selectedFilePath,
selectedFileContent: this.selectedFileContent,
fileTreeStale: this.fileTreeStale,
gitStatus: this.gitStatus,
selectedDiffPath: this.selectedDiffPath,
selectedDiff: this.selectedDiff,
selectedStagedDiff: this.selectedStagedDiff,
gitStale: this.gitStale,
activeTerminalCount: this.activeTerminalCount,
selectedTerminalId: this.selectedTerminalId,
terminalAutoStart: this.terminalAutoStart,
openTerminal: this.openTerminal,
onRefreshFiles: this.onRefreshFiles,
onExpandDir: this.onExpandDir,
onSelectFile: this.onSelectFile,
onRefreshGit: this.onRefreshGit,
onSelectDiff: this.onSelectDiff,
onSelectTerminal: this.onSelectTerminal,
};
}
static override styles = workspacePanelStyles;
}
@@ -1,4 +1,5 @@
import { api, type Project, type Workspace } from "../api";
import { resetWorkspaceScopedState } from "../appState";
import { mergeCachedNewSessions } from "../cachedNewSessions";
import type { GetState, RouteTarget, SetState, UpdateUrl } from "./types";
import type { SessionController } from "./sessionController";
@@ -15,7 +16,7 @@ export class WorkspaceController {
clearSelection(options?: { updateUrl?: boolean | undefined }) {
this.sessions.clearActiveSession();
this.setState({ selectedProject: undefined, selectedWorkspace: undefined, sessions: [], workspaces: [], fileTree: [], expandedDirs: {}, selectedFilePath: undefined, selectedFileContent: undefined, fileTreeStale: false, gitStatus: undefined, selectedDiffPath: undefined, selectedDiff: undefined, selectedStagedDiff: undefined, gitStale: false, selectedTerminalId: undefined, error: "" });
this.setState({ selectedProject: undefined, selectedWorkspace: undefined, workspaces: [], ...resetWorkspaceScopedState() });
if (options?.updateUrl !== false) this.updateUrl();
}
@@ -27,7 +28,7 @@ export class WorkspaceController {
async selectProject(project: Project, target?: RouteTarget) {
this.sessions.clearActiveSession();
this.setState({ selectedProject: project, selectedWorkspace: undefined, sessions: [], workspaces: [], fileTree: [], expandedDirs: {}, selectedFilePath: undefined, selectedFileContent: undefined, fileTreeStale: false, gitStatus: undefined, selectedDiffPath: undefined, selectedDiff: undefined, selectedStagedDiff: undefined, gitStale: false, selectedTerminalId: undefined, error: "" });
this.setState({ selectedProject: project, selectedWorkspace: undefined, workspaces: [], ...resetWorkspaceScopedState() });
try {
const workspaces = await api.workspaces(project.id);
this.setState({ workspaces, workspacesByProjectId: { ...this.getState().workspacesByProjectId, [project.id]: workspaces } });
@@ -42,7 +43,7 @@ export class WorkspaceController {
async selectWorkspace(workspace: Workspace, target?: { sessionId?: string | undefined; updateUrl?: boolean | undefined }) {
this.workspaceSelection.rememberWorkspace(workspace);
this.sessions.clearActiveSession();
this.setState({ selectedWorkspace: workspace, sessions: [], fileTree: [], expandedDirs: {}, selectedFilePath: undefined, selectedFileContent: undefined, fileTreeStale: false, gitStatus: undefined, selectedDiffPath: undefined, selectedDiff: undefined, selectedStagedDiff: undefined, gitStale: false, selectedTerminalId: undefined, error: "" });
this.setState({ selectedWorkspace: workspace, ...resetWorkspaceScopedState() });
try {
const sessions = mergeCachedNewSessions(workspace.path, await api.sessions(workspace.path));
this.setState({ sessions });
+2 -2
View File
@@ -47,7 +47,7 @@ export interface PluginRuntimeContext {
openThemePicker: () => void;
selectMainView: (view: AppState["mainView"]) => void;
selectWorkspaceTool: (tool: QualifiedContributionId) => void;
openTerminal?: (options?: { terminalId?: string | undefined }) => void;
openTerminal: (options?: { terminalId?: string | undefined }) => void;
refreshFiles: () => void | Promise<void>;
refreshGit: () => void | Promise<void>;
startSession: () => void | Promise<void>;
@@ -91,7 +91,7 @@ export interface WorkspacePanelContext {
activeTerminalCount: number;
selectedTerminalId: string | undefined;
terminalAutoStart: boolean;
openTerminal?: (options?: { terminalId?: string | undefined }) => void;
openTerminal: (options?: { terminalId?: string | undefined }) => void;
onRefreshFiles: () => void;
onExpandDir: (path: string) => void;
onSelectFile: (path: string) => void;