Archived
feat(plugins): add workspace file helpers to labels
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@jmfederico/pi-web": patch
|
||||
---
|
||||
|
||||
Add workspace file and render helpers to plugin workspace label callbacks so labels can load workspace-scoped metadata without hidden panels.
|
||||
+54
-3
@@ -596,12 +596,18 @@ interface WorkspaceLabelContext {
|
||||
machine: PluginMachine;
|
||||
workspace: Workspace;
|
||||
state?: PluginRuntimeState;
|
||||
files: {
|
||||
readFile(path: string): Promise<FileContentResponse>;
|
||||
};
|
||||
host: {
|
||||
requestRender(): void;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
`machine` and `workspace` are documented as stable for label callbacks. Include `machine.id` in any label caches that depend on workspace data.
|
||||
`machine`, `workspace`, `files`, and `host` are documented as stable for label callbacks. Include `machine.id` in any label caches that depend on workspace data. Call `host.requestRender()` when async plugin-owned state changes should make PI WEB re-evaluate label `visible` or `items` callbacks.
|
||||
|
||||
Items are sorted by `order` and then id. Return an empty array to render nothing.
|
||||
Items are sorted by `order` and then id. Return an empty array to render nothing. Keep callbacks synchronous and lightweight; start async work from the callback, return cached items, then call `host.requestRender()` when the cache changes.
|
||||
|
||||
#### Text items
|
||||
|
||||
@@ -661,7 +667,7 @@ export default {
|
||||
|
||||
## Reading workspace files
|
||||
|
||||
Workspace panels can read files through the documented `files` helper. PI WEB binds this helper to the panel's machine and workspace, so it works the same for local and federated machines.
|
||||
Workspace panels and workspace labels can read files through the documented `files` helper. PI WEB binds this helper to the callback's machine and workspace, so it works the same for local and federated machines.
|
||||
|
||||
```js
|
||||
workspacePanels: [
|
||||
@@ -691,6 +697,51 @@ class MyEnvViewer extends HTMLElement {
|
||||
}
|
||||
```
|
||||
|
||||
Labels should use the same helper through a plugin-owned cache because `items()` itself must return synchronously:
|
||||
|
||||
```js
|
||||
const envCache = new Map();
|
||||
|
||||
function envKey(machine, workspace) {
|
||||
return `${machine.id}:${workspace.id}:docker/development.be-go.local.env`;
|
||||
}
|
||||
|
||||
function loadEnvLabel(context) {
|
||||
const key = envKey(context.machine, context.workspace);
|
||||
const cached = envCache.get(key);
|
||||
if (cached !== undefined) return cached;
|
||||
|
||||
const pending = { status: "loading", label: undefined };
|
||||
envCache.set(key, pending);
|
||||
context.files.readFile("docker/development.be-go.local.env")
|
||||
.then((file) => {
|
||||
pending.status = "ready";
|
||||
pending.label = file.content.match(/^DEV_URL=(.+)$/m)?.[1];
|
||||
context.host.requestRender();
|
||||
})
|
||||
.catch(() => {
|
||||
pending.status = "missing";
|
||||
context.host.requestRender();
|
||||
});
|
||||
return pending;
|
||||
}
|
||||
|
||||
workspaceLabels: [
|
||||
{
|
||||
id: "dev-url",
|
||||
items: (context) => {
|
||||
const cached = loadEnvLabel(context);
|
||||
return cached.label === undefined ? [] : [{
|
||||
type: "link",
|
||||
text: cached.label,
|
||||
href: cached.label,
|
||||
target: "_blank",
|
||||
}];
|
||||
},
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
The file response includes fields such as `path`, `content`, `truncated`, and `binary`. Be careful with sensitive files such as `.env`: plugins are trusted browser code, and file contents are exposed to the plugin.
|
||||
|
||||
## Running workspace terminal commands
|
||||
|
||||
@@ -19,7 +19,7 @@ import { SessionStorageWorkspaceSelectionMemory } from "../controllers/workspace
|
||||
import { KeyboardShortcutDispatcher } from "../keyboardShortcuts";
|
||||
import { selectedMachineId } from "../controllers/types";
|
||||
import { RealtimeSocket } from "../sessionSocket";
|
||||
import type { PiWebPluginRegistration, PluginMachine, QualifiedContributionId, QualifiedThemeContribution, QualifiedThemePairContribution, QualifiedWorkspacePanelContribution, PluginRuntimeContext, TerminalCommandRunsInternalRuntime, WorkspacePanelContext } from "../plugins/types";
|
||||
import type { PiWebPluginRegistration, PluginMachine, QualifiedContributionId, QualifiedThemeContribution, QualifiedThemePairContribution, QualifiedWorkspacePanelContribution, PluginRuntimeContext, TerminalCommandRunsInternalRuntime, WorkspaceFiles, WorkspaceHost, WorkspaceLabelContext, WorkspaceLabelItem, WorkspacePanelContext } from "../plugins/types";
|
||||
import { CLASSIC_THEME_ID, DEFAULT_THEME_PREFERENCE, applyPiWebTheme, findThemePairForTheme, readStoredThemePreference, resolveThemePreference, writeStoredThemePreference, type ThemePreference, type ThemePreferenceResolution } from "../theme";
|
||||
import { corePlugin } from "../plugins/core";
|
||||
import { themePackPlugin } from "../plugins/themes";
|
||||
@@ -725,7 +725,7 @@ export class PiWebApp extends LitElement {
|
||||
private renderWorkspacePanel() {
|
||||
const workspace = this.state.selectedWorkspace;
|
||||
const panelContext = workspace === undefined ? undefined : this.createWorkspacePanelContext(workspace);
|
||||
const workspaceLabelItems = workspace === undefined ? [] : this.plugins.getWorkspaceLabelItems(this.state, workspace);
|
||||
const workspaceLabelItems = workspace === undefined ? [] : this.workspaceLabelItems(workspace);
|
||||
const emptyState = workspace === undefined ? this.workspacePanelEmptyState() : undefined;
|
||||
return html`
|
||||
<workspace-panel
|
||||
@@ -802,7 +802,7 @@ export class PiWebApp extends LitElement {
|
||||
.projectsCollapsed=${this.mobileNavigation.isCollapsed("projects")}
|
||||
.workspacesCollapsed=${this.mobileNavigation.isCollapsed("workspaces")}
|
||||
.sessionsCollapsed=${this.mobileNavigation.isCollapsed("sessions")}
|
||||
.workspaceLabelItems=${(workspace: Workspace) => this.plugins.getWorkspaceLabelItems(this.state, workspace)}
|
||||
.workspaceLabelItems=${(workspace: Workspace) => this.workspaceLabelItems(workspace)}
|
||||
.refreshControl=${this.appShell.shouldShowAppRefreshInHeader() ? this.renderAppRefresh() : undefined}
|
||||
.onShowActions=${() => { this.setState({ actionPaletteOpen: true }); }}
|
||||
.onToggleProjects=${() => { this.mobileNavigation.toggle("projects"); }}
|
||||
@@ -901,6 +901,33 @@ export class PiWebApp extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private workspaceLabelItems(workspace: Workspace): WorkspaceLabelItem[] {
|
||||
return this.plugins.getWorkspaceLabelItems(this.createWorkspaceLabelContext(workspace));
|
||||
}
|
||||
|
||||
private createWorkspaceLabelContext(workspace: Workspace): WorkspaceLabelContext {
|
||||
const machine = pluginMachineFromState(this.state);
|
||||
return {
|
||||
machine,
|
||||
workspace,
|
||||
state: this.state,
|
||||
files: this.createWorkspaceFiles(workspace, machine.id),
|
||||
host: this.createWorkspaceHost(),
|
||||
};
|
||||
}
|
||||
|
||||
private createWorkspaceFiles(workspace: Workspace, machineId: string): WorkspaceFiles {
|
||||
return {
|
||||
readFile: (path: string) => workspacesApi.workspaceFile(workspace.projectId, workspace.id, path, machineId),
|
||||
};
|
||||
}
|
||||
|
||||
private createWorkspaceHost(): WorkspaceHost {
|
||||
return {
|
||||
requestRender: () => { this.requestUpdate(); },
|
||||
};
|
||||
}
|
||||
|
||||
private createWorkspacePanelContext(workspace: Workspace): WorkspacePanelContext {
|
||||
const machine = pluginMachineFromState(this.state);
|
||||
const machineId = machine.id;
|
||||
@@ -910,16 +937,12 @@ export class PiWebApp extends LitElement {
|
||||
machine,
|
||||
workspace,
|
||||
state: this.state,
|
||||
files: {
|
||||
readFile: (path: string) => workspacesApi.workspaceFile(workspace.projectId, workspace.id, path, machineId),
|
||||
},
|
||||
files: this.createWorkspaceFiles(workspace, machineId),
|
||||
terminal: {
|
||||
open: (options) => { void this.openRuntimeTerminal(machineId, workspace, options); },
|
||||
runCommand: (input) => terminalCommandRuns.runCommand({ ...input, workspace }),
|
||||
},
|
||||
host: {
|
||||
requestRender: () => { this.requestUpdate(); },
|
||||
},
|
||||
host: this.createWorkspaceHost(),
|
||||
piWebUnstable: { terminalCommandRuns },
|
||||
fileTree: this.state.fileTree,
|
||||
expandedDirs: this.state.expandedDirs,
|
||||
@@ -1353,7 +1376,7 @@ export class PiWebApp extends LitElement {
|
||||
${state.selectedSession ? html`
|
||||
<chat-view .sessionId=${state.selectedSession.id} .messages=${state.messages} .messageStart=${state.messagePageStart} .messageEnd=${state.messagePageEnd} .messageTotal=${state.messagePageTotal} .hasMore=${state.messagePageStart > 0} .loadingMore=${state.isLoadingEarlierMessages} .isReceivingPartialStream=${state.isReceivingPartialStream} .isCompacting=${state.status?.isCompacting === true} .pendingMessageCount=${state.status?.pendingMessageCount ?? 0} .status=${state.status} .activity=${state.activity} .onLoadMore=${() => this.withChatPrependTransition(() => this.sessions.loadEarlierMessages())}></chat-view>
|
||||
<prompt-editor .sessionId=${state.selectedSession.id} .cwd=${state.selectedWorkspace?.path} .machineId=${selectedMachineId(state)} .disabled=${state.selectedSession.archived === true} .canSteer=${state.status?.isStreaming === true} .isCompacting=${state.status?.isCompacting === true} .canStop=${state.status?.isStreaming === true || state.status?.isBashRunning === true || state.status?.isCompacting === true || (state.status?.pendingMessageCount ?? 0) > 0} .status=${state.status} .onSend=${(text: string, streamingBehavior?: "steer" | "followUp") => { this.sendPrompt(text, streamingBehavior); }} .onStop=${() => this.sessions.stopActiveWork()} .onSelectModel=${() => { void this.openModelDialog(); }} .onSelectThinking=${() => { void this.openThinkingDialog(); }}></prompt-editor>
|
||||
<status-bar .status=${state.status} .machine=${state.selectedMachine} .workspace=${state.selectedWorkspace} .workspaceLabelItems=${state.selectedWorkspace === undefined ? [] : this.plugins.getWorkspaceLabelItems(state, state.selectedWorkspace)}></status-bar>
|
||||
<status-bar .status=${state.status} .machine=${state.selectedMachine} .workspace=${state.selectedWorkspace} .workspaceLabelItems=${state.selectedWorkspace === undefined ? [] : this.workspaceLabelItems(state.selectedWorkspace)}></status-bar>
|
||||
${state.commandDialog !== undefined ? html`<command-picker .title=${state.commandDialog.title} .options=${state.commandDialog.options} .onPick=${(value: string) => this.sessions.respondToCommand(state.commandDialog?.requestId ?? "", value)} .onCancel=${() => { this.sessions.cancelCommand(); }}></command-picker>` : null}
|
||||
${state.modelDialog !== undefined ? html`<command-picker title=${state.modelDialog.title} .searchable=${true} .options=${state.modelDialog.options} .selectedValue=${state.modelDialog.selectedValue} .onPick=${(value: string) => { void this.pickModel(value); }} .onCancel=${() => { this.setState({ modelDialog: undefined }); }}></command-picker>` : null}
|
||||
${state.thinkingDialog !== undefined ? html`<command-picker title=${state.thinkingDialog.title} .options=${state.thinkingDialog.options} .selectedValue=${state.thinkingDialog.selectedValue} .onPick=${(value: string) => { void this.pickThinking(value); }} .onCancel=${() => { this.setState({ thinkingDialog: undefined }); }}></command-picker>` : null}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { html } from "lit";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { SessionInfo, Workspace } from "../api";
|
||||
import type { FileContentResponse, SessionInfo, Workspace } from "../api";
|
||||
import { initialAppState, type AppState } from "../appState";
|
||||
import { markCachedNewSessionInfo } from "../cachedNewSessions";
|
||||
import { machineScopedPluginId } from "../../../shared/machinePluginIds";
|
||||
import { corePlugin } from "./core";
|
||||
import { PluginRegistry } from "./registry";
|
||||
import { themePackPlugin } from "./themes";
|
||||
import type { PluginRuntimeContext, ThemeTokens, WorkspacePanelContext } from "./types";
|
||||
import type { PluginRuntimeContext, ThemeTokens, WorkspaceFiles, WorkspaceHost, WorkspaceLabelContext, WorkspaceLabelItem, WorkspacePanelContext } from "./types";
|
||||
|
||||
function createContext(statePatch: Partial<AppState> = {}) {
|
||||
const calls: string[] = [];
|
||||
@@ -286,12 +286,45 @@ describe("PluginRegistry", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(registry.getWorkspaceLabelItems(initialAppState(), workspace)).toEqual([
|
||||
expect(registry.getWorkspaceLabelItems(createWorkspaceLabelContext("local", workspace))).toEqual([
|
||||
{ type: "link", text: "web", href: "http://localhost:5173" },
|
||||
{ type: "text", text: "last" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("passes workspace label file and host helpers to callbacks", () => {
|
||||
const registry = new PluginRegistry();
|
||||
const workspace = testWorkspace();
|
||||
const readFile = vi.fn<WorkspaceFiles["readFile"]>(() => Promise.resolve(testFileContent("docker/development.be-go.local.env")));
|
||||
const requestRender = vi.fn<WorkspaceHost["requestRender"]>();
|
||||
const visible = vi.fn<(context: WorkspaceLabelContext) => boolean>(() => true);
|
||||
const items = vi.fn<(context: WorkspaceLabelContext) => WorkspaceLabelItem[]>((context) => {
|
||||
void context.files.readFile("docker/development.be-go.local.env");
|
||||
context.host.requestRender();
|
||||
return [{ type: "text", text: context.machine.id }];
|
||||
});
|
||||
const context = createWorkspaceLabelContext("remote-1", workspace, { files: { readFile }, host: { requestRender } });
|
||||
|
||||
registry.register({
|
||||
id: "example",
|
||||
plugin: {
|
||||
apiVersion: 1,
|
||||
name: "Example",
|
||||
activate: () => ({
|
||||
contributions: {
|
||||
workspaceLabels: [{ id: "env", visible, items }],
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
expect(registry.getWorkspaceLabelItems(context)).toEqual([{ type: "text", text: "remote-1" }]);
|
||||
expect(visible).toHaveBeenCalledWith(context);
|
||||
expect(items).toHaveBeenCalledWith(context);
|
||||
expect(readFile).toHaveBeenCalledWith("docker/development.be-go.local.env");
|
||||
expect(requestRender).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("only exposes machine-scoped plugin contributions for their machine", () => {
|
||||
const registry = new PluginRegistry();
|
||||
const pluginId = machineScopedPluginId("remote-1", "project-tools");
|
||||
@@ -321,8 +354,8 @@ describe("PluginRegistry", () => {
|
||||
expect(panel?.visible?.(createWorkspacePanelContext("local"))).toBe(false);
|
||||
expect(panel?.visible?.(createWorkspacePanelContext("remote-1"))).toBe(true);
|
||||
|
||||
expect(registry.getWorkspaceLabelItems(initialAppState(), workspace)).toEqual([]);
|
||||
expect(registry.getWorkspaceLabelItems({ ...initialAppState(), selectedMachine: testMachine("remote-1") }, workspace)).toEqual([{ type: "text", text: "remote" }]);
|
||||
expect(registry.getWorkspaceLabelItems(createWorkspaceLabelContext("local", workspace))).toEqual([]);
|
||||
expect(registry.getWorkspaceLabelItems(createWorkspaceLabelContext("remote-1", workspace))).toEqual([{ type: "text", text: "remote" }]);
|
||||
expect(registry.getThemes()).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -371,7 +404,7 @@ describe("PluginRegistry", () => {
|
||||
const panels = registry.getWorkspacePanels();
|
||||
expect(panels.find((panel) => panel.id === `${remotePluginId}:workspace.remote`)?.visible?.(createWorkspacePanelContext("remote-1"))).toBe(false);
|
||||
expect(panels.find((panel) => panel.id === "shared-tools:workspace.gateway")?.visible?.(createWorkspacePanelContext("remote-1"))).toBe(true);
|
||||
expect(registry.getWorkspaceLabelItems({ ...initialAppState(), selectedMachine: testMachine("remote-1") }, workspace)).toEqual([{ type: "text", text: "gateway" }]);
|
||||
expect(registry.getWorkspaceLabelItems(createWorkspaceLabelContext("remote-1", workspace))).toEqual([{ type: "text", text: "gateway" }]);
|
||||
});
|
||||
|
||||
it("does not activate remote duplicates when the gateway plugin is already registered", () => {
|
||||
@@ -394,6 +427,18 @@ function testWorkspace(patch: Partial<Workspace> = {}): Workspace {
|
||||
return { id: "w1", projectId: "p1", path: "/tmp/project", label: "main", isMain: true, isGitRepo: true, isGitWorktree: false, ...patch };
|
||||
}
|
||||
|
||||
function createWorkspaceLabelContext(machineId: string, workspace = testWorkspace(), helpers: Partial<Pick<WorkspaceLabelContext, "files" | "host">> = {}): WorkspaceLabelContext {
|
||||
const files: WorkspaceFiles = helpers.files ?? { readFile: vi.fn<WorkspaceFiles["readFile"]>(() => Promise.resolve(testFileContent())) };
|
||||
const host: WorkspaceHost = helpers.host ?? { requestRender: vi.fn<WorkspaceHost["requestRender"]>() };
|
||||
return {
|
||||
machine: { id: machineId, name: machineId, kind: machineId === "local" ? "local" : "remote" },
|
||||
workspace,
|
||||
state: { ...initialAppState(), selectedMachine: testMachine(machineId) },
|
||||
files,
|
||||
host,
|
||||
};
|
||||
}
|
||||
|
||||
function createWorkspacePanelContext(machineId: string): WorkspacePanelContext {
|
||||
const workspace = testWorkspace();
|
||||
return {
|
||||
@@ -425,6 +470,18 @@ function createWorkspacePanelContext(machineId: string): WorkspacePanelContext {
|
||||
};
|
||||
}
|
||||
|
||||
function testFileContent(path = "README.md"): FileContentResponse {
|
||||
return {
|
||||
path,
|
||||
encoding: "utf8",
|
||||
size: 0,
|
||||
modifiedAt: "2026-05-20T00:00:00.000Z",
|
||||
content: "",
|
||||
truncated: false,
|
||||
binary: false,
|
||||
};
|
||||
}
|
||||
|
||||
function testMachine(id: string) {
|
||||
return { id, name: id, kind: id === "local" ? "local" as const : "remote" as const, createdAt: "2026-05-20T00:00:00.000Z", updatedAt: "2026-05-20T00:00:00.000Z" };
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { html, svg } from "lit";
|
||||
import type { AppState } from "../appState";
|
||||
import type { Workspace } from "../api";
|
||||
import type { PiWebPluginRegistration, PluginAction, PluginMachine, PluginRuntimeContext, QualifiedContributionId, QualifiedPluginAction, QualifiedThemeContribution, QualifiedThemePairContribution, QualifiedWorkspaceLabelContribution, QualifiedWorkspacePanelContribution, ThemeContribution, ThemePairContribution, WorkspaceLabelContribution, WorkspaceLabelItem, WorkspacePanelContext, WorkspacePanelContribution } from "./types";
|
||||
import type { PiWebPluginRegistration, PluginAction, PluginRuntimeContext, QualifiedContributionId, QualifiedPluginAction, QualifiedThemeContribution, QualifiedThemePairContribution, QualifiedWorkspaceLabelContribution, QualifiedWorkspacePanelContribution, ThemeContribution, ThemePairContribution, WorkspaceLabelContext, WorkspaceLabelContribution, WorkspaceLabelItem, WorkspacePanelContext, WorkspacePanelContribution } from "./types";
|
||||
|
||||
const idPattern = /^[a-z][a-z0-9.-]*$/u;
|
||||
const localIdPattern = /^[a-z][a-z0-9.-]*$/u;
|
||||
@@ -79,8 +77,7 @@ export class PluginRegistry {
|
||||
return [...this.themePairs].sort((left, right) => (left.order ?? 1000) - (right.order ?? 1000) || left.name.localeCompare(right.name));
|
||||
}
|
||||
|
||||
getWorkspaceLabelItems(state: AppState, workspace: Workspace): WorkspaceLabelItem[] {
|
||||
const context = { machine: pluginMachineFromState(state), state, workspace };
|
||||
getWorkspaceLabelItems(context: WorkspaceLabelContext): WorkspaceLabelItem[] {
|
||||
return [...this.workspaceLabels]
|
||||
.sort((left, right) => (left.order ?? 1000) - (right.order ?? 1000) || left.id.localeCompare(right.id))
|
||||
.flatMap((contribution) => {
|
||||
@@ -197,9 +194,3 @@ function isHiddenByGatewayPlugin(sourcePluginId: string | undefined, gatewayPlug
|
||||
function runtimeContextMachineId(context: PluginRuntimeContext): string {
|
||||
return context.state.selectedMachine?.id ?? "local";
|
||||
}
|
||||
|
||||
function pluginMachineFromState(state: Pick<AppState, "selectedMachine">): PluginMachine {
|
||||
const machine = state.selectedMachine;
|
||||
if (machine !== undefined) return { id: machine.id, name: machine.name, kind: machine.kind };
|
||||
return { id: "local", name: "local", kind: "local" };
|
||||
}
|
||||
|
||||
@@ -47,10 +47,26 @@ export interface PluginMachine {
|
||||
kind: Machine["kind"];
|
||||
}
|
||||
|
||||
export interface WorkspacePanelFiles {
|
||||
export interface WorkspaceFiles {
|
||||
readFile(path: string): Promise<FileContentResponse>;
|
||||
}
|
||||
|
||||
export type WorkspacePanelFiles = WorkspaceFiles;
|
||||
|
||||
export interface WorkspaceHost {
|
||||
requestRender(): void;
|
||||
}
|
||||
|
||||
export type WorkspacePanelHost = WorkspaceHost;
|
||||
|
||||
export interface WorkspaceContext {
|
||||
machine: PluginMachine;
|
||||
workspace: Workspace;
|
||||
state: AppState;
|
||||
files: WorkspaceFiles;
|
||||
host: WorkspaceHost;
|
||||
}
|
||||
|
||||
export type WorkspaceTerminalCommandInput = Omit<RunTerminalCommandInput, "workspace">;
|
||||
|
||||
export interface WorkspacePanelTerminal {
|
||||
@@ -58,10 +74,6 @@ export interface WorkspacePanelTerminal {
|
||||
runCommand(input: WorkspaceTerminalCommandInput): Promise<TerminalCommandRunHandle>;
|
||||
}
|
||||
|
||||
export interface WorkspacePanelHost {
|
||||
requestRender(): void;
|
||||
}
|
||||
|
||||
export interface PiWebUnstableRuntimeContext {
|
||||
terminalCommandRuns: TerminalCommandRunsInternalRuntime;
|
||||
openSettings?: (section?: SettingsSection) => void;
|
||||
@@ -117,13 +129,8 @@ export interface QualifiedPluginAction extends AppAction {
|
||||
machineId?: string;
|
||||
}
|
||||
|
||||
export interface WorkspacePanelContext {
|
||||
machine: PluginMachine;
|
||||
workspace: Workspace;
|
||||
state: AppState;
|
||||
files: WorkspacePanelFiles;
|
||||
export interface WorkspacePanelContext extends WorkspaceContext {
|
||||
terminal: WorkspacePanelTerminal;
|
||||
host: WorkspacePanelHost;
|
||||
piWebUnstable?: Pick<PiWebUnstableRuntimeContext, "terminalCommandRuns">;
|
||||
fileTree: FileTreeEntry[];
|
||||
expandedDirs: Record<string, FileTreeEntry[]>;
|
||||
@@ -165,10 +172,12 @@ export interface QualifiedWorkspacePanelContribution extends WorkspacePanelContr
|
||||
machineId?: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceLabelContext {
|
||||
export interface WorkspaceLabelContext extends WorkspaceContext {
|
||||
machine: PluginMachine;
|
||||
workspace: Workspace;
|
||||
state: AppState;
|
||||
files: WorkspaceFiles;
|
||||
host: WorkspaceHost;
|
||||
}
|
||||
|
||||
export type WorkspaceLabelItem = WorkspaceLabelTextItem | WorkspaceLabelLinkItem | WorkspaceLabelRenderItem;
|
||||
|
||||
+21
-12
@@ -108,10 +108,26 @@ export interface Workspace {
|
||||
isGitWorktree: boolean;
|
||||
}
|
||||
|
||||
export interface WorkspacePanelFiles {
|
||||
export interface WorkspaceFiles {
|
||||
readFile(path: string): Promise<FileContentResponse>;
|
||||
}
|
||||
|
||||
export type WorkspacePanelFiles = WorkspaceFiles;
|
||||
|
||||
export interface WorkspaceHost {
|
||||
requestRender(): void;
|
||||
}
|
||||
|
||||
export type WorkspacePanelHost = WorkspaceHost;
|
||||
|
||||
export interface WorkspaceContext {
|
||||
machine: PluginMachine;
|
||||
workspace: Workspace;
|
||||
state?: PluginRuntimeState;
|
||||
files: WorkspaceFiles;
|
||||
host: WorkspaceHost;
|
||||
}
|
||||
|
||||
export interface WorkspaceTerminalCommandInput {
|
||||
title: string;
|
||||
command: string;
|
||||
@@ -124,17 +140,8 @@ export interface WorkspacePanelTerminal {
|
||||
runCommand(input: WorkspaceTerminalCommandInput): Promise<TerminalCommandRunHandle>;
|
||||
}
|
||||
|
||||
export interface WorkspacePanelHost {
|
||||
requestRender(): void;
|
||||
}
|
||||
|
||||
export interface WorkspacePanelContext {
|
||||
machine: PluginMachine;
|
||||
workspace: Workspace;
|
||||
state?: PluginRuntimeState;
|
||||
files: WorkspacePanelFiles;
|
||||
export interface WorkspacePanelContext extends WorkspaceContext {
|
||||
terminal: WorkspacePanelTerminal;
|
||||
host: WorkspacePanelHost;
|
||||
}
|
||||
|
||||
export type WorkspacePanelIcon = TemplateResult;
|
||||
@@ -149,10 +156,12 @@ export interface WorkspacePanelContribution {
|
||||
render: (context: WorkspacePanelContext) => TemplateResult;
|
||||
}
|
||||
|
||||
export interface WorkspaceLabelContext {
|
||||
export interface WorkspaceLabelContext extends WorkspaceContext {
|
||||
machine: PluginMachine;
|
||||
workspace: Workspace;
|
||||
state?: PluginRuntimeState;
|
||||
files: WorkspaceFiles;
|
||||
host: WorkspaceHost;
|
||||
}
|
||||
|
||||
export type WorkspaceLabelItem = WorkspaceLabelTextItem | WorkspaceLabelLinkItem | WorkspaceLabelRenderItem;
|
||||
|
||||
Reference in New Issue
Block a user