refactor(plugin-api): clean up workspace panel context

This commit is contained in:
Federico Jaramillo Martinez
2026-06-04 15:38:18 +02:00
parent bd8d1f1f08
commit f1c8f1f014
6 changed files with 26 additions and 13 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@jmfederico/pi-web": patch
---
Clean up the workspace panel plugin context by removing the legacy `openTerminal` alias and moving render invalidation to `context.host.requestRender()`.
+7 -6
View File
@@ -501,14 +501,15 @@ interface WorkspacePanelContext {
open?: boolean;
}): Promise<TerminalCommandRunHandle>;
};
requestRender: () => void;
openTerminal: (options?: { terminalId?: string }) => void;
host: {
requestRender(): void;
};
}
```
`icon` is optional and is used in the compact mobile tab bar. Prefer an SVG rendered with the `svg` helper from `PluginActivationContext`; use `currentColor` so PI WEB themes can style it. If `icon` is omitted, mobile tabs fall back to initials from the panel title, or to the full title when initials collide.
`machine`, `workspace`, `files`, `terminal`, `requestRender()`, and `openTerminal()` are documented as stable for panel callbacks. `terminal.open()` is equivalent to `openTerminal()`; new plugins should prefer `terminal.open()` so terminal-related helpers live under one capability.
`machine`, `workspace`, `files`, `terminal`, and `host` are documented as stable for panel callbacks. Use `terminal.open()` to switch to the built-in terminal panel; pass `{ terminalId }` to deep-link to a specific terminal. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate panel callbacks such as `badge`, `visible`, or `render`.
Useful workspace and machine shapes:
@@ -644,8 +645,8 @@ workspacePanels: [
{
id: "workspace.env",
title: "Env",
render: ({ files, requestRender }) => html`
<my-env-viewer .files=${files} .requestRender=${requestRender}></my-env-viewer>
render: ({ files }) => html`
<my-env-viewer .files=${files}></my-env-viewer>
`,
},
]
@@ -728,7 +729,7 @@ If you are an AI agent building or editing a PI WEB plugin, follow this checklis
9. Add workspace panels for larger workspace UI.
10. Add workspace labels for compact inline metadata.
11. Return arrays from workspace label `items()`; return an empty array to render nothing.
12. Use documented context helpers first: `files`, `terminal`, `requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`.
12. Use documented context helpers first: `files`, `terminal`, `host.requestRender`, `workspace`, `machine`, `state.selectedWorkspace`, `state.selectedSession`, and `state.piWebStatus`.
13. Do not fetch PI WEB `/api/...` endpoints directly. If an unstable runtime field is intentionally required, import the type from `@jmfederico/pi-web/plugin-api/unstable` and type-assert locally.
14. Treat plugins as trusted code and avoid reading or displaying secrets unless intentional.
15. After local edits, tell the user to hard reload the browser and check the console for plugin errors.
@@ -218,7 +218,7 @@ async function refreshWorkspaceConfig(context: WorkspacePanelContext): Promise<C
detail: error instanceof Error ? error.message : String(error),
}));
configCache.set(key, state);
context.requestRender();
context.host.requestRender();
window.dispatchEvent(new Event(configChangedEvent));
return state;
}
+3 -2
View File
@@ -908,7 +908,9 @@ export class PiWebApp extends LitElement {
open: (options) => { void this.openRuntimeTerminal(machineId, workspace, options); },
runCommand: (input) => terminalCommandRuns.runCommand({ ...input, workspace }),
},
requestRender: () => { this.requestUpdate(); },
host: {
requestRender: () => { this.requestUpdate(); },
},
piWebUnstable: { terminalCommandRuns },
fileTree: this.state.fileTree,
expandedDirs: this.state.expandedDirs,
@@ -923,7 +925,6 @@ export class PiWebApp extends LitElement {
activeTerminalCount: this.state.activeTerminalCount,
selectedTerminalId: this.state.selectedTerminalId,
terminalAutoStart: this.terminalAutoStartWorkspaceId === workspace.id,
openTerminal: (options) => { this.openTerminal(options); },
onRefreshFiles: () => { void this.files.refreshFiles(); },
onExpandDir: (path: string) => { void this.files.expandDir(path); },
onSelectFile: (path: string) => { void this.files.selectFile(path); },
+5 -2
View File
@@ -56,6 +56,10 @@ export interface WorkspacePanelTerminal {
runCommand(input: WorkspaceTerminalCommandInput): Promise<TerminalCommandRunHandle>;
}
export interface WorkspacePanelHost {
requestRender(): void;
}
export interface PiWebUnstableRuntimeContext {
terminalCommandRuns: TerminalCommandRunsInternalRuntime;
openSettings?: (section?: SettingsSection) => void;
@@ -116,7 +120,7 @@ export interface WorkspacePanelContext {
state: AppState;
files: WorkspacePanelFiles;
terminal: WorkspacePanelTerminal;
requestRender: () => void;
host: WorkspacePanelHost;
piWebUnstable?: Pick<PiWebUnstableRuntimeContext, "terminalCommandRuns">;
fileTree: FileTreeEntry[];
expandedDirs: Record<string, FileTreeEntry[]>;
@@ -131,7 +135,6 @@ export interface WorkspacePanelContext {
activeTerminalCount: number;
selectedTerminalId: string | undefined;
terminalAutoStart: boolean;
openTerminal: (options?: { terminalId?: string | undefined }) => void;
onRefreshFiles: () => void;
onExpandDir: (path: string) => void;
onSelectFile: (path: string) => void;
+5 -2
View File
@@ -124,14 +124,17 @@ export interface WorkspacePanelTerminal {
runCommand(input: WorkspaceTerminalCommandInput): Promise<TerminalCommandRunHandle>;
}
export interface WorkspacePanelHost {
requestRender(): void;
}
export interface WorkspacePanelContext {
machine: PluginMachine;
workspace: Workspace;
state?: PluginRuntimeState;
files: WorkspacePanelFiles;
terminal: WorkspacePanelTerminal;
requestRender: () => void;
openTerminal: (options?: { terminalId?: string | undefined }) => void;
host: WorkspacePanelHost;
}
export type WorkspacePanelIcon = TemplateResult;