// Implementation details of the bundled Info plugin. // // This file is NOT part of the plugin skeleton. If you copied the Info plugin // as a starting point for your own plugin, replace everything here with your // own content — the plugin contract (metadata and contribution definitions) // lives in pi-web-plugin.ts. import type { TemplateResult } from "lit"; import type { HtmlTemplateTag, MachineKind, PiWebComponentStatus, PiWebInstallationInfo, PiWebReleaseStatus, PiWebStatusResponse, PluginMachine, PluginRuntimeContext, Workspace, WorkspacePanelContext } from "@jmfederico/pi-web/plugin-api"; export type ComponentHealth = "current" | "restart needed" | "unavailable"; export function componentHealth(component: PiWebComponentStatus): ComponentHealth { if (!component.available) return "unavailable"; if (component.stale) return "restart needed"; return "current"; } export function formatVersion(version: string | undefined): string { return version === undefined || version === "" ? "unknown" : version; } export function installationLabel(installation: PiWebInstallationInfo | undefined): string { if (installation === undefined) return "installation unknown"; if (installation.kind === "pi-package") { const scope = installation.scope === undefined ? "" : ` · ${installation.scope}`; const source = installation.source ?? "Pi package"; return `${source}${scope}`; } if (installation.kind === "npm-global") return "global npm package"; if (installation.kind === "local") return "local checkout"; if (installation.kind === "docker") return installation.dockerMode === "dev" ? "Docker development runtime" : "Docker runtime"; return "installation unknown"; } export function machineKindLabel(kind: MachineKind): string { return kind === "local" ? "local machine" : "remote machine"; } export function releaseSummary(release: PiWebReleaseStatus): string { if (release.updateAvailable) { return release.latestVersion === undefined || release.latestVersion === "" ? "Update available" : `Update available: ${release.latestVersion}`; } if (release.error !== undefined && release.error !== "") return `Update check failed: ${release.error}`; if (release.skipped === true) return "Update check skipped"; return "Up to date"; } /** One-line component summary used by the panel rows and the clipboard diagnostics. */ export function componentDetails(component: PiWebComponentStatus): string { const parts = [ `running ${formatVersion(component.runtimeVersion)}`, `installed ${formatVersion(component.installedVersion)}`, componentHealth(component), installationLabel(component.installation), ]; if (component.installation?.path !== undefined && component.installation.path !== "") parts.push(component.installation.path); if (component.error !== undefined && component.error !== "") parts.push(`error: ${component.error}`); return parts.join(" · "); } export function workspaceFlags(workspace: Workspace): string[] { return [ workspace.branch === undefined || workspace.branch === "" ? undefined : `branch ${workspace.branch}`, workspace.isGitWorktree ? "git worktree" : workspace.isGitRepo ? "git repo" : "not a git repo", workspace.isMain ? "main workspace" : undefined, ].filter((flag): flag is string => flag !== undefined); } export interface DiagnosticsInput { status: PiWebStatusResponse | undefined; machine?: PluginMachine | undefined; workspace?: Workspace | undefined; } /** Plain-text status block suitable for pasting into a bug report. */ export function diagnosticsSummary({ status, machine, workspace }: DiagnosticsInput): string { const lines: string[] = ["PI WEB diagnostics"]; if (status === undefined) { lines.push("Status: unavailable"); } else { lines.push(`Package: ${status.packageName}`); lines.push(`${status.components.web.label}: ${componentDetails(status.components.web)}`); lines.push(`${status.components.sessiond.label}: ${componentDetails(status.components.sessiond)}`); const checked = status.release.checkedAt === undefined || status.release.skipped === true ? "" : ` (checked ${status.release.checkedAt})`; lines.push(`Release: ${releaseSummary(status.release)}${checked}`); lines.push(`Status generated: ${status.generatedAt}`); } if (machine !== undefined) lines.push(`Machine: ${machine.name} (${machineKindLabel(machine.kind)})`); if (workspace === undefined) { lines.push("Workspace: none selected"); } else { lines.push(`Workspace: ${workspace.label} — ${workspace.path} (${workspaceFlags(workspace).join(", ")})`); } return lines.join("\n"); } /** Action body: copy the diagnostics summary for the current runtime context. */ export async function copyDiagnostics(context: PluginRuntimeContext): Promise { const summary = diagnosticsSummary({ status: context.state.piWebStatus, machine: context.state.selectedMachine, workspace: context.state.selectedWorkspace, }); await navigator.clipboard.writeText(summary); } function renderComponent(html: HtmlTemplateTag, component: PiWebComponentStatus): TemplateResult { const health = componentHealth(component); return html`
${component.label} ${health} ${componentDetails(component)}
`; } function renderStatusSection(html: HtmlTemplateTag, status: PiWebStatusResponse | undefined): TemplateResult { if (status === undefined) { return html`
PI WEB

PI WEB status is not available yet. It refreshes automatically in the background.

`; } const web = status.components.web; const messageCount = status.messages.length; return html`
PI WEB
Version ${formatVersion(web.runtimeVersion)} ${web.installedVersion === undefined || web.installedVersion === web.runtimeVersion ? null : html`installed ${formatVersion(web.installedVersion)}`}
Package ${status.packageName}
Installation ${installationLabel(web.installation)} ${web.installation?.path === undefined || web.installation.path === "" ? null : html`${web.installation.path}`}
Release ${releaseSummary(status.release)} ${status.release.checkedAt === undefined || status.release.skipped === true ? null : html`checked ${status.release.checkedAt}`}
${messageCount === 0 ? null : html`

${String(messageCount)} status ${messageCount === 1 ? "message" : "messages"} — open the Updates tab for details.

`}

Status generated ${status.generatedAt}

Services ${renderComponent(html, status.components.web)} ${renderComponent(html, status.components.sessiond)}
`; } function renderMachineSection(html: HtmlTemplateTag, machine: PluginMachine): TemplateResult { return html`
Machine
Name ${machine.name}
Type ${machineKindLabel(machine.kind)}
`; } function renderWorkspaceSection(html: HtmlTemplateTag, workspace: Workspace): TemplateResult { return html`
Workspace
Name ${workspace.label}
Path ${workspace.path} ${workspaceFlags(workspace).length === 0 ? null : html`${workspaceFlags(workspace).join(" · ")}`}
`; } /** Panel body: render the Info tab for the current workspace panel context. */ export function renderInfoPanel(html: HtmlTemplateTag, context: WorkspacePanelContext): TemplateResult { return html`
Info
${renderStatusSection(html, context.state?.piWebStatus)} ${renderMachineSection(html, context.machine)} ${renderWorkspaceSection(html, context.workspace)}
`; }