import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; test("renders every Pi RPC extension UI method without replying to fire-and-forget requests", async () => { const source = await readFile("ui/src/App.tsx", "utf8"); for (const method of [ "select", "confirm", "input", "editor", "notify", "setStatus", "setWidget", "setTitle", "set_editor_text", ]) { assert.match(source, new RegExp(`case "${method}"`)); } assert.match(source, /setPendingExtension\(extension\)/); assert.doesNotMatch(source, /This extension UI is unsupported/); assert.match(source, /function stripAnsi/); assert.match(source, /stripAnsi\(extension\.statusText\)/); assert.match(source, /className="pi-controls"/); assert.match(source, /aria-label="Model and thinking controls"/); assert.match(source, /const uiScaleKey/); assert.match(source, /function clampUiScale/); assert.match(source, /function updateUiScale/); assert.match(source, /localStorage\.setItem\(uiScaleKey/); assert.match(source, /className="scale-settings"/); assert.match(source, /aria-label="Interface scale"/); assert.match(source, /aria-label="Decrease interface scale"/); assert.match(source, /aria-label="Increase interface scale"/); assert.match(source, /type="range"/); assert.match(source, /style=\{\{ zoom: uiScale \}\}/); assert.doesNotMatch( source, /className="pi-controls"[\s\S]*aria-label="Interface scale"/, ); assert.doesNotMatch(source, /
/); assert.doesNotMatch( source, /event\.ctrlKey && event\.key\.toLowerCase\(\) === "c"/, ); assert.match(source, /getCurrentWindow\(\)\s*\.hide\(\)\s*\.catch/); assert.match( source, /window\.addEventListener\("keydown", handleKeydown, true\)/, ); assert.match(source, /useLayoutEffect/); assert.match(source, /transcript\.scrollTop = transcript\.scrollHeight/); assert.match(source, /requestAnimationFrame\(scrollTranscriptToLatest\)/); assert.match(source, /const loadGeneration = useRef\(0\)/); assert.match( source, /const bridgeRetryTimer = useRef\(undefined\)/, ); assert.match(source, /const bridgeRetryAttempt = useRef\(0\)/); assert.match(source, /function scheduleBridgeReconnect/); assert.match(source, /Bridge unavailable; retrying in/); assert.match( source, /setStatus\(String\(error\)\);\s*scheduleBridgeReconnect\(\);/, ); assert.match(source, /bridgeRetryTimer\.current = window\.setTimeout/); assert.match(source, /void refreshAgents\(preferredPath\)/); assert.match(source, /bridgeRetryAttempt\.current = 0/); assert.match( source, /const lastEventSequence = useRef>/, ); assert.match(source, /const eventReloadInFlight = useRef\(false\)/); assert.match(source, /const queuedEventReload = useRef/); assert.match(source, /function reloadAgentAfterEvent\(agentId: string\)/); assert.match(source, /void reloadAgentAfterEvent\(selectedId\)/); assert.match(source, /let disposed = false;/); assert.match(source, /if \(disposed\) stop\(\);/); assert.match(source, /generation !== loadGeneration\.current/); assert.match(source, /current\.phase !== "working"/); assert.match(source, /bridgeEvent\.seq <= \(lastEventSequence\.current/); assert.match(source, /\.toFixed\(1\)/); assert.match(source, /formatTokens\(stats\.tokens\.input\)/); assert.match(source, /formatTokens\(stats\.tokens\.output\)/); assert.match(source, /formatTokens\(cacheTokens\)/); assert.match(source, /function updateWorkProgress/); assert.match( source, /className=\{`work-progress composer-progress \$\{workProgress\.phase\}`\}/, ); assert.match(source, /event\.type === "tool"/); assert.match(source, /event\.type === "queue"/); assert.match(source, /composer-progress/); assert.doesNotMatch( source, /
{ const [component, styles] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), ]); assert.match(component, /aria-expanded=\{view === "settings"\}/); assert.match( component, /aria-label="Managed directories"\s+className="directory-tab-list"\s+role="tablist"/, ); assert.match(component, /role="tab"/); assert.match( component, /aria-selected=\{directory\.agentId === selectedId\}/, ); assert.match(component, /aria-controls="directory-workspace"/); assert.match(component, /onKeyDown=\{\(event\) => handleDirectoryTabKeyDown/); assert.match( component, /aria-current=\{session\.isCurrent \? "page" : undefined\}/, ); assert.match(component, /role="status" aria-atomic="true"/); assert.match(component, /aria-hidden="true"/); assert.match( component, /role=\{notice\.notifyType === "error" \? "alert" : "status"\}/, ); assert.match(component, /role="dialog"/); assert.match(component, /aria-modal="true"/); assert.match(component, /aria-labelledby="extension-title"/); assert.match(component, /const extensionRef = useRef\(null\)/); assert.match(component, /function focusableExtensionControls/); assert.match(component, /event\.key !== "Tab"/); assert.match(component, /data-initial-focus/); assert.match(component, /className="extension-backdrop"/); assert.match(component, /htmlFor="extension-value"/); assert.match(component, /Close current session and start a new session/); assert.match(component, /Restart Pi for the selected directory/); assert.match(component, /window\.confirm\(/); assert.match(styles, /button:focus-visible/); assert.match(styles, /\*::before,\n\*::after/); assert.match(styles, /\.sr-only/); assert.match(styles, /\.extension-backdrop \{/); assert.match(styles, /animation-duration: 0\.01ms/); }); test("makes the window draggable from the non-interactive header", async () => { const [component, styles, capability] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), readFile("ui/src-tauri/capabilities/default.json", "utf8"), ]); assert.match(component, /startDragging\(\)/); assert.match( component, /closest\(\s*"button, input, select, textarea",?\s*\)/, ); assert.match(styles, /\.workflow-header \{[\s\S]*cursor: grab/); assert.match(capability, /core:window:allow-start-dragging/); }); test("opens local follow-up choices for /model and /resume", async () => { const [component, styles] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), ]); assert.match(component, /const controlCommands: Command\[\]/); assert.match(component, /followUp: "model"/); assert.match(component, /followUp: "resume"/); assert.match(component, /followUp: "thinking"/); assert.match(component, /const nativeCommand = controlCommands\.find/); assert.match(component, /function chooseThinking/); assert.match(component, /function openCommandFollowUp/); assert.match(component, /function chooseModel/); assert.match(component, /className="command-followup"/); assert.match(component, /New session/); assert.match(styles, /\.command-followup \{/); assert.match(styles, /\.command-choice-list \{/); }); test("shows bridge-managed directory status and sessions for the opened directory", async () => { const [component, styles] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), ]); assert.match( component, /invoke<\{ directories: Directory\[\] \}>\("list_directories"\)/, ); assert.match( component, /invoke<\{ sessions: Session\[\] \}>\("list_sessions"/, ); assert.match(component, /function chooseDirectory/); assert.match(component, /function handleDirectoryTabKeyDown/); assert.match(component, /function forgetDirectory/); assert.match(component, /window\.confirm/); assert.match(component, /"forget_directory"/); assert.match(component, /bridgeEvent\.type === "agent_state"/); assert.match(component, /setDirectories\(\(current\) =>/); assert.match( component, /state: loadedState\.isStreaming \? "streaming" : "idle"/, ); assert.match(component, /className="directory-add"/); assert.match( component, /import \{ open \} from "@tauri-apps\/plugin-dialog"/, ); assert.match(component, /function browseForFolder/); assert.match(component, /directory: true/); assert.match(component, /multiple: false/); assert.match(component, /Browse…/); assert.match(component, /const \[isAddingDirectory, setIsAddingDirectory\]/); assert.match(component, /aria-busy=\{isAddingDirectory\}/); assert.match(component, /className="directory-add-status" role="status"/); assert.match(component, /pendingDirectoryPath/); assert.match(component, /Added \$\{worktreeLabel\(pendingDirectoryPath\)\}/); assert.match(component, /Add directory/); assert.doesNotMatch(component, /className="folder-form"/); assert.doesNotMatch(component, /Remembered folders/); assert.match(component, /function startNewSession/); assert.match(component, /function closeCurrentSession/); assert.match(component, /Use \/resume to reopen it/); assert.match(component, /function switchSession/); assert.match(component, /className="session-panel"/); assert.match(component, /New session/); assert.match(component, /state\.isStreaming/); assert.match(styles, /\.directory-add \{/); assert.match(styles, /\.directory-add-status \{/); assert.match(styles, /\.directory-add-spinner \{/); assert.match(styles, /directory-add-spin/); assert.match(styles, /\.directory-tab \{/); assert.match(styles, /\.directory-tab-button \{/); assert.match(styles, /\.directory-close \{/); assert.match(styles, /\.session-panel \{/); assert.match(styles, /\.session-list \{/); }); test("renders todo-plugin state and compact context/token status", async () => { const [component, styles] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), ]); assert.match(component, /function currentTodos/); assert.match(component, /message\.toolName === "todo"/); assert.match(component, /className="todos-pane"/); assert.match(component, /className="workflow-main"/); assert.match(component, /ctx \{formatTokens\(contextTokens\)\}/); assert.match(component, /tok \{formatTokens\(stats\.tokens\.total\)\}/); assert.match(styles, /\.workflow-main \{/); assert.match(styles, /\.todos-pane \{/); assert.match(styles, /\.status-metric \{/); }); test("shows an optimistic prompt until the transcript receives it", async () => { const [component, styles] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), ]); assert.match(component, /type PendingSubmission/); assert.match( component, /const \[pendingSubmissions, setPendingSubmissions\]/, ); assert.match(component, /setStatus\("Sending prompt to Pi…"\)/); assert.match(component, /phase: "sending"/); assert.match(component, /phase: "sent"/); assert.match(component, /Sent · waiting for Pi/); assert.match(component, /receivedMessages\.some/); assert.match(component, /pending-message/); assert.match(styles, /\.pending-message \{/); assert.match(styles, /\.pending-message\.sending/); }); test("offers a lower-center control when the conversation is above its latest message", async () => { const [component, styles] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), ]); assert.match( component, /const \[isTranscriptAtBottom, setIsTranscriptAtBottom\]/, ); assert.match(component, /function updateTranscriptScrollPosition/); assert.match(component, /function scrollTranscriptToLatest/); assert.match(component, /onScroll=\{updateTranscriptScrollPosition\}/); assert.match(component, /!isTranscriptAtBottom/); assert.match(component, /aria-label="Scroll to latest message"/); assert.match(component, /className="scroll-to-latest"/); assert.match(component, /scrollTranscriptToLatest\(\);/); assert.match(styles, /\.transcript-container \{/); assert.match(styles, /\.scroll-to-latest \{[\s\S]*left: 50%/); assert.match(styles, /\.scroll-to-latest \{[\s\S]*bottom: 14px/); assert.match(styles, /\.scroll-to-latest \{[\s\S]*border-radius: 50%/); }); test("distinguishes user and assistant messages", async () => { const styles = await readFile("ui/src/App.css", "utf8"); assert.match(styles, /\.message\.user \{/); assert.match(styles, /background: #192536/); assert.match(styles, /\.message\.assistant \{/); assert.match(styles, /background: #1a241a/); }); test("keeps an accessible focus treatment for the prompt editor", async () => { const styles = await readFile("ui/src/App.css", "utf8"); assert.match(styles, /\.composer textarea:focus \{/); assert.doesNotMatch(styles, /outline: none/); assert.match(styles, /border-color: #6f8b56/); assert.match( styles, /\.composer textarea:focus \{[^}]*outline: 2px solid #c4ed8b/, ); assert.match(styles, /\.composer textarea:focus \{[^}]*outline-offset: -2px/); assert.doesNotMatch(styles, /\.composer textarea:focus \{[^}]*box-shadow:/); assert.match(styles, /textarea:focus-visible/); }); test("submits the composer with Enter and preserves Shift+Enter for newlines", async () => { const source = await readFile("ui/src/App.tsx", "utf8"); assert.match(source, /onKeyDown=\{\(event\) => \{/); assert.match(source, /event\.key !== "Enter"/); assert.match(source, /event\.shiftKey/); assert.match(source, /event\.nativeEvent\.isComposing/); assert.match(source, /event\.preventDefault\(\);\s*void submit\(\);/); assert.match(source, /Enter to send · Shift\+Enter for newline/); }); test("uses a compact workflow layout that keeps the composer and transcript stable", async () => { const [component, styles] = await Promise.all([ readFile("ui/src/App.tsx", "utf8"), readFile("ui/src/App.css", "utf8"), ]); assert.match(component, /className="workflow-header"/); assert.match(component, /className="workflow"/); assert.match(component, /className="workflow-footer"/); assert.match(component, /className="composer-stack"/); assert.match( styles, /\.workflow \{[\s\S]*grid-template-rows: minmax\(0, 1fr\) auto/, ); assert.match(styles, /\.transcript,\s*\.settings \{[\s\S]*overflow: auto/); assert.match( component, /className=\{`transcript \$\{pendingExtension \? "with-extension" : ""\} \$\{workProgress\.phase\}`\}/, ); assert.match( styles, /\.transcript\.with-extension \{[\s\S]*padding-bottom: 112px/, ); assert.match(styles, /\.transcript\.working \{/); assert.match(styles, /conic-gradient\(/); assert.match(styles, /transcript-border-orbit/); assert.match(styles, /prefers-reduced-motion: reduce/); assert.match(styles, /font-size: 18px/); assert.match(styles, /\.directory-tab-button \{[\s\S]*font-size: 13px/); assert.match(styles, /\.message pre \{[\s\S]*font-size: 14px/); assert.match( styles, /\.workflow-main \{[\s\S]*grid-template-columns: minmax\(0, 1fr\) 250px/, ); assert.match( styles, /\.pi-controls \{[\s\S]*grid-template-columns: repeat\(2, minmax\(0, 1fr\)\)/, ); assert.match(styles, /\.scale-settings \{/); assert.match( styles, /\.scale-adjustment \{[\s\S]*grid-template-columns: auto minmax\(0, 1fr\) auto/, ); assert.match(styles, /\.scale-adjustment input\[type="range"\] \{/); assert.match(styles, /\.pi-controls \{[\s\S]*padding: 4px 6px/); }); test("configures a larger desktop window and enables the folder dialog", async () => { const [config, capability, rust] = await Promise.all([ readFile("ui/src-tauri/tauri.conf.json", "utf8"), readFile("ui/src-tauri/capabilities/default.json", "utf8"), readFile("ui/src-tauri/src/lib.rs", "utf8"), ]); assert.match(config, /"width": 1100/); assert.match(config, /"height": 840/); assert.match(config, /"minWidth": 640/); assert.match(config, /"minHeight": 520/); assert.match(capability, /"dialog:default"/); assert.match(rust, /\.plugin\(tauri_plugin_dialog::init\(\)\)/); });