Archived
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
Reference in New Issue
Block a user