Archived
fix(actions): stabilize plugin action execution
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@jmfederico/pi-web": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Keep action-palette plugin actions responsive when they change workspace tools or routes.
|
||||||
@@ -8,7 +8,7 @@ import { actionPaletteStyles } from "./shared";
|
|||||||
@customElement("action-palette")
|
@customElement("action-palette")
|
||||||
export class ActionPalette extends LitElement {
|
export class ActionPalette extends LitElement {
|
||||||
@property({ attribute: false }) actions: AppAction[] = [];
|
@property({ attribute: false }) actions: AppAction[] = [];
|
||||||
@property({ attribute: false }) onRun?: (actionId: string) => void;
|
@property({ attribute: false }) onRun?: (action: AppAction) => void;
|
||||||
@property({ attribute: false }) onCancel?: () => void;
|
@property({ attribute: false }) onCancel?: () => void;
|
||||||
@query("input") private input?: HTMLInputElement;
|
@query("input") private input?: HTMLInputElement;
|
||||||
@state() private queryText = "";
|
@state() private queryText = "";
|
||||||
@@ -89,7 +89,7 @@ export class ActionPalette extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private run(action: AppAction) {
|
private run(action: AppAction) {
|
||||||
this.onRun?.(action.id);
|
this.onRun?.(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
static override styles = actionPaletteStyles;
|
static override styles = actionPaletteStyles;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { themePackPlugin } from "../plugins/themes";
|
|||||||
import { loadExternalPlugins } from "../plugins/external";
|
import { loadExternalPlugins } from "../plugins/external";
|
||||||
import { PluginRegistry } from "../plugins/registry";
|
import { PluginRegistry } from "../plugins/registry";
|
||||||
import { queryNamespace, readNamespacedString } from "../namespacedQueryArgs";
|
import { queryNamespace, readNamespacedString } from "../namespacedQueryArgs";
|
||||||
import { readRoute, writeRoute } from "../route";
|
import { readRoute, writeRoute, type AppRoute } from "../route";
|
||||||
import "./ProjectList";
|
import "./ProjectList";
|
||||||
import "./WorkspaceList";
|
import "./WorkspaceList";
|
||||||
import "./SessionList";
|
import "./SessionList";
|
||||||
@@ -229,16 +229,33 @@ export class PiWebApp extends LitElement {
|
|||||||
const selectedDiffPath = readNamespacedString(queryNamespace("core:workspace.git"), "diff");
|
const selectedDiffPath = readNamespacedString(queryNamespace("core:workspace.git"), "diff");
|
||||||
this.setState({ workspaceTool: route.tool ?? this.state.workspaceTool, mainView: route.view ?? this.defaultRouteView(), selectedFilePath, selectedDiffPath });
|
this.setState({ workspaceTool: route.tool ?? this.state.workspaceTool, mainView: route.view ?? this.defaultRouteView(), selectedFilePath, selectedDiffPath });
|
||||||
if (route.projectId === undefined || route.projectId === "") return;
|
if (route.projectId === undefined || route.projectId === "") return;
|
||||||
|
if (this.routeMatchesCurrentSelection(route)) {
|
||||||
|
await this.refreshRestoredWorkspaceTool(route.tool, selectedFilePath);
|
||||||
|
this.git.updatePolling();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const project = this.state.projects.find((p) => p.id === route.projectId);
|
const project = this.state.projects.find((p) => p.id === route.projectId);
|
||||||
if (!project) return;
|
if (!project) return;
|
||||||
await this.workspaces.selectProject(project, { workspaceId: route.workspaceId, sessionId: route.sessionId, updateUrl });
|
await this.workspaces.selectProject(project, { workspaceId: route.workspaceId, sessionId: route.sessionId, updateUrl });
|
||||||
this.setState({ selectedFilePath, selectedDiffPath });
|
this.setState({ selectedFilePath, selectedDiffPath });
|
||||||
if (route.tool === "core:workspace.files") await this.files.refreshFiles();
|
await this.refreshRestoredWorkspaceTool(route.tool, selectedFilePath);
|
||||||
if (route.tool === "core:workspace.files" && selectedFilePath !== undefined) await this.files.restoreFile(selectedFilePath);
|
|
||||||
if (route.tool === "core:workspace.git") await this.git.refreshGit();
|
|
||||||
this.git.updatePolling();
|
this.git.updatePolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private routeMatchesCurrentSelection(route: AppRoute): boolean {
|
||||||
|
return route.workspaceId !== undefined
|
||||||
|
&& route.workspaceId !== ""
|
||||||
|
&& this.state.selectedProject?.id === route.projectId
|
||||||
|
&& this.state.selectedWorkspace?.id === route.workspaceId
|
||||||
|
&& this.state.selectedSession?.id === route.sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async refreshRestoredWorkspaceTool(tool: QualifiedContributionId | undefined, selectedFilePath: string | undefined): Promise<void> {
|
||||||
|
if (tool === "core:workspace.files") await this.files.refreshFiles();
|
||||||
|
if (tool === "core:workspace.files" && selectedFilePath !== undefined) await this.files.restoreFile(selectedFilePath);
|
||||||
|
if (tool === "core:workspace.git") await this.git.refreshGit();
|
||||||
|
}
|
||||||
|
|
||||||
private async withChatScrollTransition(action: () => Promise<void>) {
|
private async withChatScrollTransition(action: () => Promise<void>) {
|
||||||
this.chatView?.saveScrollPosition();
|
this.chatView?.saveScrollPosition();
|
||||||
await action();
|
await action();
|
||||||
@@ -518,9 +535,14 @@ export class PiWebApp extends LitElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private runAction(actionId: string) {
|
private runAction(action: AppAction): void {
|
||||||
const action = this.getActions().find((candidate) => candidate.id === actionId && candidate.enabled !== false);
|
void Promise.resolve()
|
||||||
if (action !== undefined) void action.run();
|
.then(() => action.run())
|
||||||
|
.catch((error: unknown) => {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
console.warn(`Action failed: ${action.id}`, error);
|
||||||
|
this.setState({ error: `Action failed: ${message}` });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openModelDialog() {
|
private async openModelDialog() {
|
||||||
@@ -778,7 +800,7 @@ export class PiWebApp extends LitElement {
|
|||||||
` : html`<div class="empty">Select or start a session.</div>`}
|
` : html`<div class="empty">Select or start a session.</div>`}
|
||||||
</main>
|
</main>
|
||||||
${this.renderWorkspacePanel()}
|
${this.renderWorkspacePanel()}
|
||||||
${state.actionPaletteOpen ? html`<action-palette .actions=${this.getActions()} .onRun=${(actionId: string) => { this.setState({ actionPaletteOpen: false }); this.runAction(actionId); }} .onCancel=${() => { this.setState({ actionPaletteOpen: false }); }}></action-palette>` : null}
|
${state.actionPaletteOpen ? html`<action-palette .actions=${this.getActions()} .onRun=${(action: AppAction) => { this.setState({ actionPaletteOpen: false }); this.runAction(action); }} .onCancel=${() => { this.setState({ actionPaletteOpen: false }); }}></action-palette>` : null}
|
||||||
${state.projectDialogOpen ? html`<project-dialog .onSubmit=${(path: string, create: boolean) => this.projects.addProject(path, create)} .onCancel=${() => { this.setState({ projectDialogOpen: false }); }}></project-dialog>` : null}
|
${state.projectDialogOpen ? html`<project-dialog .onSubmit=${(path: string, create: boolean) => this.projects.addProject(path, create)} .onCancel=${() => { this.setState({ projectDialogOpen: false }); }}></project-dialog>` : null}
|
||||||
${state.themeDialog !== undefined ? html`<command-picker title=${state.themeDialog.title} .options=${state.themeDialog.options} .selectedValue=${state.themeDialog.selectedValue} .onPick=${(value: string) => { this.pickTheme(value); }} .onCancel=${() => { this.setState({ themeDialog: undefined }); }}></command-picker>` : null}
|
${state.themeDialog !== undefined ? html`<command-picker title=${state.themeDialog.title} .options=${state.themeDialog.options} .selectedValue=${state.themeDialog.selectedValue} .onPick=${(value: string) => { this.pickTheme(value); }} .onCancel=${() => { this.setState({ themeDialog: undefined }); }}></command-picker>` : null}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user