From f1c8f1f014170043a6817cf72462c947d850956d Mon Sep 17 00:00:00 2001 From: Federico Jaramillo Martinez Date: Thu, 4 Jun 2026 15:38:18 +0200 Subject: [PATCH] refactor(plugin-api): clean up workspace panel context --- .changeset/remove-panel-open-terminal.md | 5 +++++ docs/plugins.md | 13 +++++++------ pi-web-plugins/workspace-tasks/tasksPanelElement.ts | 2 +- src/client/src/components/PiWebApp.ts | 5 +++-- src/client/src/plugins/types.ts | 7 +++++-- src/plugin-api.ts | 7 +++++-- 6 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 .changeset/remove-panel-open-terminal.md diff --git a/.changeset/remove-panel-open-terminal.md b/.changeset/remove-panel-open-terminal.md new file mode 100644 index 0000000..f989483 --- /dev/null +++ b/.changeset/remove-panel-open-terminal.md @@ -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()`. diff --git a/docs/plugins.md b/docs/plugins.md index 91a4524..76dae71 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -501,14 +501,15 @@ interface WorkspacePanelContext { open?: boolean; }): Promise; }; - 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` - + render: ({ files }) => html` + `, }, ] @@ -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. diff --git a/pi-web-plugins/workspace-tasks/tasksPanelElement.ts b/pi-web-plugins/workspace-tasks/tasksPanelElement.ts index ceefc75..bcd2801 100644 --- a/pi-web-plugins/workspace-tasks/tasksPanelElement.ts +++ b/pi-web-plugins/workspace-tasks/tasksPanelElement.ts @@ -218,7 +218,7 @@ async function refreshWorkspaceConfig(context: WorkspacePanelContext): Promise { 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); }, diff --git a/src/client/src/plugins/types.ts b/src/client/src/plugins/types.ts index 7ce764a..2b67644 100644 --- a/src/client/src/plugins/types.ts +++ b/src/client/src/plugins/types.ts @@ -56,6 +56,10 @@ export interface WorkspacePanelTerminal { runCommand(input: WorkspaceTerminalCommandInput): Promise; } +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; fileTree: FileTreeEntry[]; expandedDirs: Record; @@ -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; diff --git a/src/plugin-api.ts b/src/plugin-api.ts index cc4bd9c..afe3752 100644 --- a/src/plugin-api.ts +++ b/src/plugin-api.ts @@ -124,14 +124,17 @@ export interface WorkspacePanelTerminal { runCommand(input: WorkspaceTerminalCommandInput): Promise; } +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;