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;
|
enabled?: boolean;
|
||||||
run: () => void | Promise<void>;
|
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;
|
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 {
|
export function machineGitDiffUrl(machineId: string, projectId: string, workspaceId: string, options?: { path?: string; staged?: boolean }): string {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (options?.path !== undefined) params.set("path", options.path);
|
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 {
|
function machineStatus(machine: Machine, statuses: Record<string, MachineHealth>): MachineStatus {
|
||||||
return statuses[machine.id]?.status ?? machine.status ?? "unknown";
|
return statuses[machine.id]?.status ?? machine.status ?? "unknown";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export const appStyles = css`
|
|||||||
}
|
}
|
||||||
status-bar { flex: 0 0 auto; }
|
status-bar { flex: 0 0 auto; }
|
||||||
chat-view { flex: 1 1 auto; min-height: 0; overflow: hidden; }
|
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; }
|
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); }
|
.empty { margin: auto; color: var(--pi-muted); }
|
||||||
.error { padding: 10px 16px; border-bottom: 1px solid var(--pi-border); color: var(--pi-danger); }
|
.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; }
|
button { padding: 5px 7px; }
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const composerStyles = promptEditorStyles;
|
|
||||||
|
|||||||
@@ -1,19 +1,6 @@
|
|||||||
import { html, type TemplateResult } from "lit";
|
import { html, type TemplateResult } from "lit";
|
||||||
import type { WorkspaceLabelItem } from "../plugins/types";
|
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[] {
|
export function renderWorkspaceLabelInlineItems(items: WorkspaceLabelItem[] = []): TemplateResult[] {
|
||||||
return items.map((item, index) => html`${index === 0 ? null : html`<span class="workspace-label-separator">·</span>`}${renderWorkspaceLabelItem(item)}`);
|
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> {
|
export class PersistentValueMap<T> {
|
||||||
private readonly values = new Map<string, 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);
|
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}`;
|
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 {
|
export function machineSessionKey(machineId: string, sessionId: string): string {
|
||||||
return `${machineId}:${sessionId}`;
|
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";
|
import type { GlobalSessionEvent, RealtimeEvent, SessionRef, SessionUiEvent } from "../../shared/apiTypes";
|
||||||
|
|
||||||
export type { GlobalSessionEvent, RealtimeEvent, 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 {
|
function isSessionUiEvent(event: unknown): event is SessionUiEvent {
|
||||||
const type = eventType(event);
|
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);
|
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