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
+1 -1
View File
@@ -3,4 +3,4 @@
"@jmfederico/pi-web-actions": patch "@jmfederico/pi-web-actions": patch
--- ---
Document and harden separate Pi Web plugin package development, including the Actions plugin refresh flow and private API dogfooding notes. Document and harden separate Pi Web plugin package development, including the Actions plugin refresh flow and public terminal navigation helper.
+3 -3
View File
@@ -334,7 +334,7 @@ interface PluginRuntimeContext {
configureAuth: () => void | Promise<void>; configureAuth: () => void | Promise<void>;
logoutAuth: () => void | Promise<void>; logoutAuth: () => void | Promise<void>;
selectWorkspaceTool: (tool: QualifiedContributionId) => void; selectWorkspaceTool: (tool: QualifiedContributionId) => void;
openTerminal?: (options?: { terminalId?: string }) => void; openTerminal: (options?: { terminalId?: string }) => void;
refreshFiles: () => void | Promise<void>; refreshFiles: () => void | Promise<void>;
refreshGit: () => void | Promise<void>; refreshGit: () => void | Promise<void>;
startSession: () => void | Promise<void>; startSession: () => void | Promise<void>;
@@ -397,11 +397,11 @@ interface WorkspacePanelContribution {
interface WorkspacePanelContext { interface WorkspacePanelContext {
workspace: Workspace; workspace: Workspace;
openTerminal?: (options?: { terminalId?: string }) => void; openTerminal: (options?: { terminalId?: string }) => void;
} }
``` ```
`workspace` and `openTerminal()` are documented as stable for panel callbacks. Other fields may exist at runtime, but they are Pi Web internals and can change quickly. Use `openTerminal?.({ terminalId })` when a panel creates a terminal and wants Pi Web to navigate to that specific terminal. If a panel needs file, git, or session data, prefer explicit `fetch()` calls and keep them isolated. `workspace` and `openTerminal()` are documented as stable for panel callbacks. Other fields may exist at runtime, but they are Pi Web internals and can change quickly. Use `openTerminal({ terminalId })` when a panel creates a terminal and wants Pi Web to navigate to that specific terminal. If a panel needs file, git, or session data, prefer explicit `fetch()` calls and keep them isolated.
Useful workspace shape: Useful workspace shape:
+2 -2
View File
@@ -47,7 +47,7 @@ export interface PluginRuntimeContext {
openThemePicker: () => void; openThemePicker: () => void;
selectMainView: (view: string) => void; selectMainView: (view: string) => void;
selectWorkspaceTool: (tool: QualifiedContributionId) => void; selectWorkspaceTool: (tool: QualifiedContributionId) => void;
openTerminal?: (options?: { terminalId?: string | undefined }) => void; openTerminal: (options?: { terminalId?: string | undefined }) => void;
refreshFiles: () => void | Promise<void>; refreshFiles: () => void | Promise<void>;
refreshGit: () => void | Promise<void>; refreshGit: () => void | Promise<void>;
startSession: () => void | Promise<void>; startSession: () => void | Promise<void>;
@@ -79,7 +79,7 @@ export interface Workspace {
export interface WorkspacePanelContext { export interface WorkspacePanelContext {
workspace: Workspace; workspace: Workspace;
state?: PluginRuntimeState; state?: PluginRuntimeState;
openTerminal?: (options?: { terminalId?: string | undefined }) => void; openTerminal: (options?: { terminalId?: string | undefined }) => void;
} }
export interface WorkspacePanelContribution { export interface WorkspacePanelContribution {
+1 -1
View File
@@ -2,7 +2,7 @@
Configurable workspace actions for Pi Web. Configurable workspace actions for Pi Web.
The plugin adds an **Actions** workspace tab. Actions create a new Pi Web terminal, send the configured shell command, and switch to the Terminal tab so the user can monitor progress or take over. The plugin adds an **Actions** workspace tab. Actions create a new Pi Web terminal, send the configured shell command, and switch to that terminal so the user can monitor progress or take over.
## Configuration ## Configuration
+10 -9
View File
@@ -1,7 +1,7 @@
import type { Workspace } from "@jmfederico/pi-web/plugin-api"; import type { Workspace } from "@jmfederico/pi-web/plugin-api";
import { ACTIONS_CONFIG_PATH, type WorkspaceAction } from "./config.js"; import { ACTIONS_CONFIG_PATH, type WorkspaceAction } from "./config.js";
import { createWorkspaceTerminal, sendTerminalCommand } from "./terminalDispatcher.js"; import { createWorkspaceTerminal, sendTerminalCommand } from "./terminalDispatcher.js";
import { openTerminalPanel, requestPiWebRender } from "./piWebPrivateUi.js"; import { requestPiWebRender } from "./piWebPrivateUi.js";
import { loadWorkspaceActionsConfig, type WorkspaceActionsConfigLoadResult } from "./workspaceActionsClient.js"; import { loadWorkspaceActionsConfig, type WorkspaceActionsConfigLoadResult } from "./workspaceActionsClient.js";
export const actionsPanelTagName = "pi-web-actions-panel"; export const actionsPanelTagName = "pi-web-actions-panel";
@@ -94,7 +94,7 @@ class PiWebActionsPanel extends HTMLElement {
} }
this.root.querySelector("button[data-open-terminal]")?.addEventListener("click", () => { this.root.querySelector("button[data-open-terminal]")?.addEventListener("click", () => {
this.openWorkspaceTerminal(workspace); this.openWorkspaceTerminal();
}); });
} }
@@ -103,7 +103,7 @@ class PiWebActionsPanel extends HTMLElement {
if (state.kind === "unavailable") return `${renderUnavailableState(state)}${this.renderStatus()}`; if (state.kind === "unavailable") return `${renderUnavailableState(state)}${this.renderStatus()}`;
if (state.config.actions.length === 0) return `<p class="muted">No actions configured in ${escapeHtml(ACTIONS_CONFIG_PATH)}.</p>${this.renderStatus()}`; if (state.config.actions.length === 0) return `<p class="muted">No actions configured in ${escapeHtml(ACTIONS_CONFIG_PATH)}.</p>${this.renderStatus()}`;
return ` return `
<p class="muted">Actions create a new workspace terminal, send the command, then switch to the Terminal tab. Edit ${escapeHtml(ACTIONS_CONFIG_PATH)} and click Refresh to reload.</p> <p class="muted">Actions create a new workspace terminal, send the command, then switch to that terminal. Edit ${escapeHtml(ACTIONS_CONFIG_PATH)} and click Refresh to reload.</p>
${renderActionGroups(state.config.actions, this.runningActionId)} ${renderActionGroups(state.config.actions, this.runningActionId)}
${this.renderStatus()} ${this.renderStatus()}
`; `;
@@ -148,7 +148,7 @@ class PiWebActionsPanel extends HTMLElement {
}; };
this.runningActionId = undefined; this.runningActionId = undefined;
this.render(); this.render();
this.openWorkspaceTerminal(workspace, terminal.id); this.openWorkspaceTerminal(terminal.id);
} catch (error) { } catch (error) {
this.runningActionId = undefined; this.runningActionId = undefined;
this.status = { kind: "error", message: error instanceof Error ? error.message : String(error) }; this.status = { kind: "error", message: error instanceof Error ? error.message : String(error) };
@@ -156,13 +156,14 @@ class PiWebActionsPanel extends HTMLElement {
} }
} }
private openWorkspaceTerminal(workspace: Workspace, terminalId?: string): void { private openWorkspaceTerminal(terminalId?: string): void {
if (this.openTerminalValue !== undefined) { if (this.openTerminalValue === undefined) {
if (terminalId === undefined) this.openTerminalValue(); this.status = { kind: "error", message: "This Pi Web version does not provide terminal navigation to plugins." };
else this.openTerminalValue({ terminalId }); this.render();
return; return;
} }
openTerminalPanel(workspace, terminalId); if (terminalId === undefined) this.openTerminalValue();
else this.openTerminalValue({ terminalId });
} }
} }
-54
View File
@@ -1,66 +1,12 @@
import type { Workspace } from "@jmfederico/pi-web/plugin-api";
import { terminalToolId, type TerminalInfo } from "./terminalDispatcher.js";
interface TerminalPanelElement {
terminals: TerminalInfo[];
selectTerminal: (terminalId: string) => void;
}
interface Updatable { interface Updatable {
requestUpdate: () => void; requestUpdate: () => void;
} }
/**
* Private Pi Web UI fallback used while the plugin API is still being dogfooded.
* The current host provides a panel `openTerminal` helper; keep this fallback contained
* for older hosts and replace/remove it once the public helper is required.
*/
export function openTerminalPanel(workspace: Workspace, terminalId?: string): void {
const url = new URL(window.location.href);
url.searchParams.set("project", workspace.projectId);
url.searchParams.set("workspace", workspace.id);
url.searchParams.set("tool", terminalToolId);
url.searchParams.set("view", terminalToolId);
window.history.pushState({}, "", url);
dispatchPopState();
if (terminalId !== undefined) selectTerminalWhenAvailable(terminalId);
}
export function requestPiWebRender(): void { export function requestPiWebRender(): void {
const app = document.querySelector("pi-web-app"); const app = document.querySelector("pi-web-app");
if (isUpdatable(app)) app.requestUpdate(); if (isUpdatable(app)) app.requestUpdate();
} }
function dispatchPopState(): void {
if (typeof PopStateEvent === "function") {
window.dispatchEvent(new PopStateEvent("popstate"));
return;
}
window.dispatchEvent(new Event("popstate"));
}
function selectTerminalWhenAvailable(terminalId: string, attempt = 0): void {
const terminalPanel = findTerminalPanel();
const terminals = terminalPanel?.terminals ?? [];
const hasTerminal = terminals.some((terminal) => terminal.id === terminalId);
if (terminalPanel !== undefined && hasTerminal) {
terminalPanel.selectTerminal(terminalId);
return;
}
if (attempt < 50) window.setTimeout(() => { selectTerminalWhenAvailable(terminalId, attempt + 1); }, 150);
}
function findTerminalPanel(): TerminalPanelElement | undefined {
const panel = document.querySelector("workspace-panel")?.shadowRoot?.querySelector("terminal-panel");
return isTerminalPanelElement(panel) ? panel : undefined;
}
function isTerminalPanelElement(value: unknown): value is TerminalPanelElement {
return isRecord(value) && Array.isArray(value["terminals"]) && typeof value["selectTerminal"] === "function";
}
function isUpdatable(value: unknown): value is Updatable { function isUpdatable(value: unknown): value is Updatable {
return isRecord(value) && typeof value["requestUpdate"] === "function"; return isRecord(value) && typeof value["requestUpdate"] === "function";
} }
@@ -1,6 +1,5 @@
import type { Workspace } from "@jmfederico/pi-web/plugin-api"; import type { Workspace } from "@jmfederico/pi-web/plugin-api";
export const terminalToolId = "core:workspace.terminal";
export const actionTerminalCols = 120; export const actionTerminalCols = 120;
export const actionTerminalRows = 32; export const actionTerminalRows = 32;
+34
View File
@@ -52,6 +52,40 @@ export type AuthDialogState =
| { step: "oauth"; flow: OAuthFlowState; responding?: boolean; inputValue?: string; error?: string } | { step: "oauth"; flow: OAuthFlowState; responding?: boolean; inputValue?: string; error?: string }
| { step: "logout"; providers: AuthProviderOption[] }; | { 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 { export function initialAppState(): AppState {
return { return {
projects: [], projects: [],
+4 -2
View File
@@ -414,8 +414,10 @@ export class PiWebApp extends LitElement {
} }
private renderWorkspacePanel() { private renderWorkspacePanel() {
const workspaceLabelItems = this.state.selectedWorkspace === undefined ? [] : this.plugins.getWorkspaceLabelItems(this.state, this.state.selectedWorkspace); const workspace = 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 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) { private renderNavigationPanel(autoSwitchToChat: boolean) {
+4 -51
View File
@@ -1,7 +1,6 @@
import { LitElement, html, type TemplateResult } from "lit"; import { LitElement, html, type TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators.js"; import { customElement, property, query, state } from "lit/decorators.js";
import type { FileContentResponse, FileTreeEntry, GitDiffResponse, GitStatusResponse, Workspace } from "../api"; import type { Workspace } from "../api";
import type { AppState } from "../appState";
import type { QualifiedContributionId, QualifiedWorkspacePanelContribution, WorkspaceLabelItem, WorkspacePanelContext } from "../plugins/types"; import type { QualifiedContributionId, QualifiedWorkspacePanelContribution, WorkspaceLabelItem, WorkspacePanelContext } from "../plugins/types";
import { workspacePanelStyles } from "./shared"; import { workspacePanelStyles } from "./shared";
import { renderWorkspaceLabel } from "./workspaceLabel"; import { renderWorkspaceLabel } from "./workspaceLabel";
@@ -9,32 +8,12 @@ import { renderWorkspaceLabel } from "./workspaceLabel";
@customElement("workspace-panel") @customElement("workspace-panel")
export class WorkspacePanel extends LitElement { export class WorkspacePanel extends LitElement {
@property({ attribute: false }) workspace: Workspace | undefined; @property({ attribute: false }) workspace: Workspace | undefined;
@property({ attribute: false }) appState!: AppState; @property({ attribute: false }) panelContext: WorkspacePanelContext | undefined;
@property() tool: QualifiedContributionId = "core:workspace.files"; @property() tool: QualifiedContributionId = "core:workspace.files";
@property({ attribute: false }) panels: QualifiedWorkspacePanelContribution[] = []; @property({ attribute: false }) panels: QualifiedWorkspacePanelContribution[] = [];
@property({ attribute: false }) workspaceLabelItems: WorkspaceLabelItem[] = []; @property({ attribute: false }) workspaceLabelItems: WorkspaceLabelItem[] = [];
@property({ type: Boolean }) hideToolTabs = false; @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 }) 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; @query(".workspace-header-strip") private workspaceHeaderStrip?: HTMLElement | null;
@state() private workspaceHeaderCanScrollLeft = false; @state() private workspaceHeaderCanScrollLeft = false;
@state() private workspaceHeaderCanScrollRight = false; @state() private workspaceHeaderCanScrollRight = false;
@@ -65,9 +44,10 @@ export class WorkspacePanel extends LitElement {
override render() { override render() {
const workspace = this.workspace; const workspace = this.workspace;
if (workspace === undefined) return html`<section class="empty">Select a workspace.</section>`; 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 visiblePanels = this.panels;
const selectedPanel = visiblePanels.find((panel) => panel.id === this.tool) ?? visiblePanels[0]; const selectedPanel = visiblePanels.find((panel) => panel.id === this.tool) ?? visiblePanels[0];
const context = this.createPanelContext(workspace);
return html` return html`
<header> <header>
<div class=${this.workspaceHeaderFrameClass()}> <div class=${this.workspaceHeaderFrameClass()}>
@@ -128,32 +108,5 @@ export class WorkspacePanel extends LitElement {
return strip instanceof HTMLElement ? strip : undefined; 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; static override styles = workspacePanelStyles;
} }
@@ -1,4 +1,5 @@
import { api, type Project, type Workspace } from "../api"; import { api, type Project, type Workspace } from "../api";
import { resetWorkspaceScopedState } from "../appState";
import { mergeCachedNewSessions } from "../cachedNewSessions"; import { mergeCachedNewSessions } from "../cachedNewSessions";
import type { GetState, RouteTarget, SetState, UpdateUrl } from "./types"; import type { GetState, RouteTarget, SetState, UpdateUrl } from "./types";
import type { SessionController } from "./sessionController"; import type { SessionController } from "./sessionController";
@@ -15,7 +16,7 @@ export class WorkspaceController {
clearSelection(options?: { updateUrl?: boolean | undefined }) { clearSelection(options?: { updateUrl?: boolean | undefined }) {
this.sessions.clearActiveSession(); 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(); if (options?.updateUrl !== false) this.updateUrl();
} }
@@ -27,7 +28,7 @@ export class WorkspaceController {
async selectProject(project: Project, target?: RouteTarget) { async selectProject(project: Project, target?: RouteTarget) {
this.sessions.clearActiveSession(); 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 { try {
const workspaces = await api.workspaces(project.id); const workspaces = await api.workspaces(project.id);
this.setState({ workspaces, workspacesByProjectId: { ...this.getState().workspacesByProjectId, [project.id]: workspaces } }); 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 }) { async selectWorkspace(workspace: Workspace, target?: { sessionId?: string | undefined; updateUrl?: boolean | undefined }) {
this.workspaceSelection.rememberWorkspace(workspace); this.workspaceSelection.rememberWorkspace(workspace);
this.sessions.clearActiveSession(); 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 { try {
const sessions = mergeCachedNewSessions(workspace.path, await api.sessions(workspace.path)); const sessions = mergeCachedNewSessions(workspace.path, await api.sessions(workspace.path));
this.setState({ sessions }); this.setState({ sessions });
+2 -2
View File
@@ -47,7 +47,7 @@ export interface PluginRuntimeContext {
openThemePicker: () => void; openThemePicker: () => void;
selectMainView: (view: AppState["mainView"]) => void; selectMainView: (view: AppState["mainView"]) => void;
selectWorkspaceTool: (tool: QualifiedContributionId) => void; selectWorkspaceTool: (tool: QualifiedContributionId) => void;
openTerminal?: (options?: { terminalId?: string | undefined }) => void; openTerminal: (options?: { terminalId?: string | undefined }) => void;
refreshFiles: () => void | Promise<void>; refreshFiles: () => void | Promise<void>;
refreshGit: () => void | Promise<void>; refreshGit: () => void | Promise<void>;
startSession: () => void | Promise<void>; startSession: () => void | Promise<void>;
@@ -91,7 +91,7 @@ export interface WorkspacePanelContext {
activeTerminalCount: number; activeTerminalCount: number;
selectedTerminalId: string | undefined; selectedTerminalId: string | undefined;
terminalAutoStart: boolean; terminalAutoStart: boolean;
openTerminal?: (options?: { terminalId?: string | undefined }) => void; openTerminal: (options?: { terminalId?: string | undefined }) => void;
onRefreshFiles: () => void; onRefreshFiles: () => void;
onExpandDir: (path: string) => void; onExpandDir: (path: string) => void;
onSelectFile: (path: string) => void; onSelectFile: (path: string) => void;