Remove dead/unused code

Delete orphaned files and unreferenced exports flagged by knip/tsc:
- Remove Composer.ts (chat-composer element superseded by prompt-editor)
- Remove plugins/example/index.ts (never registered)
- Drop unused exports: enabledActions, gitDiffUrl, machineWorkspaceKey,
  GlobalSessionSocket, shouldShowMachineSwitcher, renderWorkspaceLabel,
  renderWorkspaceLabelItems, composerStyles alias
- Fix unused parseValue field in PersistentValueMap
This commit is contained in:
Federico Jaramillo Martinez
2026-06-13 21:48:34 +02:00
parent cfb7493384
commit 5d335a7e72
10 changed files with 3 additions and 179 deletions
-4
View File
@@ -7,7 +7,3 @@ export interface AppAction {
enabled?: boolean;
run: () => void | Promise<void>;
}
export function enabledActions(actions: AppAction[]): AppAction[] {
return actions.filter((action) => action.enabled !== false);
}
-8
View File
@@ -10,14 +10,6 @@ function sessionCwd(session: SessionLookup): string | undefined {
return typeof session === "string" ? undefined : session.cwd;
}
export function gitDiffUrl(projectId: string, workspaceId: string, options?: { path?: string; staged?: boolean }): string {
const params = new URLSearchParams();
if (options?.path !== undefined) params.set("path", options.path);
if (options?.staged === true) params.set("staged", "true");
const query = params.toString();
return `/api/projects/${encodeURIComponent(projectId)}/workspaces/${encodeURIComponent(workspaceId)}/git/diff${query ? `?${query}` : ""}`;
}
export function machineGitDiffUrl(machineId: string, projectId: string, workspaceId: string, options?: { path?: string; staged?: boolean }): string {
const params = new URLSearchParams();
if (options?.path !== undefined) params.set("path", options.path);
-43
View File
@@ -1,43 +0,0 @@
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { composerStyles } from "./shared";
@customElement("chat-composer")
export class Composer extends LitElement {
@property({ type: Boolean }) disabled = false;
@property({ attribute: false }) onSend?: (text: string) => void;
@property({ attribute: false }) onStopSession?: () => void;
@state() private draft = "";
override render() {
return html`
<footer>
<textarea
.value=${this.draft}
?disabled=${this.disabled}
@input=${(event: Event) => {
if (event.target instanceof HTMLTextAreaElement) this.draft = event.target.value;
}}
@keydown=${(e: KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
this.send();
}
}}
placeholder="Message pi..."
></textarea>
<button ?disabled=${this.disabled} @click=${() => { this.send(); }}>Send</button>
<button ?disabled=${this.disabled} @click=${() => this.onStopSession?.()}>Stop session</button>
</footer>
`;
}
private send() {
const text = this.draft.trim();
if (text === "" || this.disabled) return;
this.draft = "";
this.onSend?.(text);
}
static override styles = composerStyles;
}
@@ -303,10 +303,6 @@ export class MachineSwitcher extends LitElement implements KeyboardNavigableSect
`;
}
export function shouldShowMachineSwitcher(machines: readonly Machine[]): boolean {
return machines.length > 1;
}
function machineStatus(machine: Machine, statuses: Record<string, MachineHealth>): MachineStatus {
return statuses[machine.id]?.status ?? machine.status ?? "unknown";
}
+1 -3
View File
@@ -144,7 +144,7 @@ export const appStyles = css`
}
status-bar { flex: 0 0 auto; }
chat-view { flex: 1 1 auto; min-height: 0; overflow: hidden; }
prompt-editor, chat-composer { flex: 0 0 auto; }
prompt-editor { flex: 0 0 auto; }
button { border: 1px solid var(--pi-border); border-radius: 8px; background: var(--pi-surface); color: var(--pi-text); padding: 7px 9px; cursor: pointer; }
.empty { margin: auto; color: var(--pi-muted); }
.error { padding: 10px 16px; border-bottom: 1px solid var(--pi-border); color: var(--pi-danger); }
@@ -486,5 +486,3 @@ export const promptEditorStyles = css`
button { padding: 5px 7px; }
}
`;
export const composerStyles = promptEditorStyles;
@@ -1,19 +1,6 @@
import { html, type TemplateResult } from "lit";
import type { WorkspaceLabelItem } from "../plugins/types";
export function renderWorkspaceLabel(label: string, items: WorkspaceLabelItem[] = [], title?: string): TemplateResult {
return html`
<span class="workspace-label">
<span class="workspace-label-base" title=${title ?? label}>${label}</span>
${renderWorkspaceLabelItems(items)}
</span>
`;
}
export function renderWorkspaceLabelItems(items: WorkspaceLabelItem[] = []): TemplateResult[] {
return items.map((item) => html`<span class="workspace-label-separator">·</span>${renderWorkspaceLabelItem(item)}`);
}
export function renderWorkspaceLabelInlineItems(items: WorkspaceLabelItem[] = []): TemplateResult[] {
return items.map((item, index) => html`${index === 0 ? null : html`<span class="workspace-label-separator">·</span>`}${renderWorkspaceLabelItem(item)}`);
}
@@ -22,7 +22,7 @@ export function browserSessionStorage(): KeyValueStorage | undefined {
export class PersistentValueMap<T> {
private readonly values = new Map<string, T>();
constructor(private readonly storageKey: string, private readonly parseValue: StorageValueParser<T>, private readonly storage = browserSessionStorage()) {
constructor(private readonly storageKey: string, parseValue: StorageValueParser<T>, private readonly storage = browserSessionStorage()) {
for (const [key, value] of loadEntries(storageKey, parseValue, storage)) this.values.set(key, value);
}
-3
View File
@@ -4,9 +4,6 @@ export function machineProjectKey(machineId: string, projectId: string): string
return `${machineId}:${projectId}`;
}
export function machineWorkspaceKey(machineId: string, projectId: string, workspaceId: string): string {
return `${machineId}:${projectId}:${workspaceId}`;
}
export function machineSessionKey(machineId: string, sessionId: string): string {
return `${machineId}:${sessionId}`;
-45
View File
@@ -1,45 +0,0 @@
import { html } from "lit";
import type { PiWebPlugin } from "../types";
export const examplePlugin: PiWebPlugin = {
apiVersion: 1,
name: "Example Plugin",
activate: () => ({
contributions: {
actions: [
{
id: "workspace.show-path",
title: "Show Current Workspace Path",
group: "Example",
enabled: (context) => context.state.selectedWorkspace !== undefined,
run: (context) => {
const path = context.state.selectedWorkspace?.path ?? "No workspace selected";
window.alert(path);
},
},
],
workspaceLabels: [
{
id: "workspace.example-label",
order: 100,
items: (context) => [{ type: "text", text: context.workspace.isGitRepo ? "git" : "folder", title: context.workspace.path }],
},
],
workspacePanels: [
{
id: "workspace.info",
title: "Info",
order: 100,
render: (context) => html`
<section class="toolbar"><strong>Info</strong></section>
<section class="viewer">
<p><strong>Workspace</strong></p>
<p class="muted">${context.workspace.label}</p>
<p class="muted">${context.workspace.path}</p>
</section>
`,
},
],
},
}),
};
+1 -55
View File
@@ -1,4 +1,4 @@
import { globalSessionEvents, realtimeEvents, sessionEvents } from "./api";
import { realtimeEvents, sessionEvents } from "./api";
import type { GlobalSessionEvent, RealtimeEvent, SessionRef, SessionUiEvent } from "../../shared/apiTypes";
export type { GlobalSessionEvent, RealtimeEvent, SessionUiEvent } from "../../shared/apiTypes";
@@ -129,60 +129,6 @@ export class RealtimeSocket {
}
}
export class GlobalSessionSocket {
private socket: WebSocket | undefined;
private onEvent: ((event: GlobalSessionEvent) => void) | undefined;
private reconnectTimer?: number;
private reconnectDelay = 500;
private shouldReconnect = false;
private machineId = "local";
connect(onEvent: (event: GlobalSessionEvent) => void, machineId = "local"): void {
this.close();
this.machineId = machineId;
this.onEvent = onEvent;
this.shouldReconnect = true;
this.open();
}
close(): void {
this.shouldReconnect = false;
window.clearTimeout(this.reconnectTimer);
closeSocketQuietly(this.socket);
this.socket = undefined;
this.onEvent = undefined;
this.machineId = "local";
}
private open(): void {
if (!this.shouldReconnect) return;
const socket = globalSessionEvents(this.machineId);
this.socket = socket;
socket.onopen = () => {
this.reconnectDelay = 500;
};
socket.onmessage = (message) => void this.handleMessage(message.data);
socket.onerror = () => { socket.close(); };
socket.onclose = () => {
if (this.socket === socket) this.socket = undefined;
this.scheduleReconnect();
};
}
private scheduleReconnect(): void {
if (!this.shouldReconnect) return;
window.clearTimeout(this.reconnectTimer);
const delay = this.reconnectDelay;
this.reconnectDelay = Math.min(this.reconnectDelay * 1.6, 5000);
this.reconnectTimer = window.setTimeout(() => { this.open(); }, delay);
}
private async handleMessage(data: MessageEvent["data"]): Promise<void> {
const event = await parseSocketEvent(data);
if (isGlobalSessionEvent(event)) this.onEvent?.(event);
}
}
function isSessionUiEvent(event: unknown): event is SessionUiEvent {
const type = eventType(event);
return ["message.append", "assistant.delta", "assistant.thinking.delta", "tool.start", "tool.update", "tool.end", "shell.start", "shell.chunk", "shell.end", "agent.start", "agent.end", "message.end", "status.update", "activity.update", "command.output", "session.error", "session.name", "pi.event"].includes(type);