import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; test("Quick Pane polls snapshots, renders answers, and promotes runtime without a handoff", async () => { const source = await readFile( new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url), "utf8", ); assert.match(source, /get_session_runtime_snapshot/); assert.match(source, /setInterval\(\(\) => void refresh\(\), 800\)/); assert.match(source, //); assert.match(source, /className="quick-pane-notice escalation"/); assert.match(source, /className="quick-pane-assistant-footer"/); assert.match(source, /Continue in full session/); const toolbar = source.slice( source.indexOf('
'), source.indexOf( "
", source.indexOf('
'), ), ); assert.doesNotMatch(toolbar, /continueFull|Continue/); assert.match( source, /await invoke<\{ runtime: Runtime \}>\("promote_quick_runtime", \{\s+runtimeId: current\.runtimeId,/, ); assert.doesNotMatch(source, /Quick Pane transcript:/); assert.doesNotMatch(source, /create_session_runtime/); assert.doesNotMatch(source, /maximumSeconds|Quick time budget/); }); test("main Settings uses shared catalog selectors with provider derived from models", async () => { const [pane, app, catalog, persistence] = await Promise.all([ readFile( new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url), "utf8", ), readFile(new URL("../ui/src/App.tsx", import.meta.url), "utf8"), readFile(new URL("../ui/src/model-catalog.ts", import.meta.url), "utf8"), readFile( new URL("../ui/src/workspace/persistence.ts", import.meta.url), "utf8", ), ]); assert.doesNotMatch(catalog, /get_model_catalog/); assert.match(app, /create_quick_runtime/); assert.match(app, /get_session_runtime_snapshot/); assert.match(app, /close_quick_runtime/); assert.match(app, /label="Quick model"/); assert.match(app, /label="Follow-up model"/); assert.match(app, / recordInputSelection/); assert.match(pane, /onInput=\{\(event\) => recordInputChangeSelection/); assert.match(pane, / { const [pane, catalog] = await Promise.all([ readFile( new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url), "utf8", ), readFile(new URL("../ui/src/model-catalog.ts", import.meta.url), "utf8"), ]); assert.match(pane, /useState/); assert.match(pane, /provider: submittedModel\.provider/); assert.match(pane, /modelId: submittedModel\.modelId/); assert.doesNotMatch(pane, /selectedModel\.split/); assert.match(pane, /listen\("quick-pane-shown"[\s\S]*?void renewChatOnShow\(\)/); assert.match(pane, /onFocusChanged[\s\S]*?if \(event\.payload\) focusInput\(\);/); assert.match(pane, /const CHAT_RENEWAL_MS = 180_000/); assert.match(pane, /Date\.now\(\) - lastActivity > CHAT_RENEWAL_MS/); assert.match(pane, /event\.ctrlKey && event\.key\.toLowerCase\(\) === "n"/); assert.match(pane, /event\.preventDefault\(\)/); assert.doesNotMatch( pane, /setInterval[^\n]*CHAT_RENEWAL_MS|setTimeout[^\n]*CHAT_RENEWAL_MS/, ); assert.match( catalog, /JSON\.stringify\(\[identity\.provider, identity\.modelId\]\)/, ); assert.match(catalog, /JSON\.parse\(value\)/); }); test("Quick Pane dismiss and mutually exclusive window visibility stay native-serialized", async () => { const [pane, nativeSource] = await Promise.all([ readFile( new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url), "utf8", ), readFile(new URL("../ui/src-tauri/src/lib.rs", import.meta.url), "utf8"), ]); assert.match(pane, /invoke\("dismiss_quick_pane"\)/); assert.doesNotMatch(pane, /getCurrentWindow\(\)\.hide\(\)/); const dismissPane = pane.slice( pane.indexOf("const dismissPane"), pane.indexOf("const clearChatState"), ); assert.doesNotMatch(dismissPane, /closeRuntime|clearRuntime|prepareRuntime/); assert.match(nativeSource, /fn dismiss_quick_pane\(/); assert.match( nativeSource, /dismiss_quick_pane,[\s\S]*create_session_runtime/, ); assert.match( nativeSource, /struct WindowVisibility\(Mutex\)/, ); const mainShow = nativeSource.slice( nativeSource.indexOf("fn apply_main_window_action"), nativeSource.indexOf("fn get_or_create_quick_pane"), ); const mainHideQuick = mainShow.indexOf("quick_pane.hide()"); const mainClearQuick = mainShow.indexOf('visibility.hidden("quick-pane")'); const showMain = mainShow.indexOf("window.show()"); assert.ok(mainShow.indexOf('get_webview_window("quick-pane")') >= 0); assert.ok(mainHideQuick >= 0 && mainHideQuick < showMain); assert.ok(mainClearQuick >= 0 && mainClearQuick < showMain); const quickShow = nativeSource.slice( nativeSource.indexOf("fn toggle_quick_pane"), nativeSource.indexOf("fn requested_new_worktree_argument"), ); const quickHideMain = quickShow.indexOf("main.hide()"); const quickClearMain = quickShow.indexOf('visibility.hidden("main")'); const showQuick = quickShow.indexOf("window.show()"); assert.ok(quickShow.indexOf('get_webview_window("main")') >= 0); assert.ok(quickHideMain >= 0 && quickHideMain < showQuick); assert.ok(quickClearMain >= 0 && quickClearMain < showQuick); }); test("native promotion owns visibility and Main consumes pending runtime after startup refresh", async () => { const [nativeSource, workspaceSource] = await Promise.all([ readFile(new URL("../ui/src-tauri/src/lib.rs", import.meta.url), "utf8"), readFile( new URL("../ui/src/workspace/useWorkspace.ts", import.meta.url), "utf8", ), ]); const promotion = nativeSource.slice( nativeSource.indexOf("fn show_main_for_promoted_runtime"), nativeSource.indexOf("fn toggle_quick_pane"), ); assert.match(promotion, /get_or_create_main_with_visibility\(app, false\)/); assert.ok( promotion.indexOf("quick_pane\n .hide()") < promotion.indexOf("window\n .show()"), ); assert.ok( promotion.indexOf('visibility.hidden("quick-pane")') < promotion.indexOf("window\n .show()"), ); assert.match(promotion, /\*pending = Some\(runtime_id\)/); assert.match(nativeSource, /fn get_pending_promoted_runtime/); assert.match(nativeSource, /fn consume_pending_promoted_runtime/); assert.match(workspaceSource, /await promotedListenerReady/); assert.match(workspaceSource, /await refresh\(loaded\)/); assert.match(workspaceSource, /get_pending_promoted_runtime/); assert.match(workspaceSource, /consume_pending_promoted_runtime/); }); test("Quick Pane routes extension responses through existing bridge command", async () => { const source = await readFile( new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url), "utf8", ); assert.match( source, //, ); assert.match(source, /invoke\("respond_to_extension"/); assert.match(source, /requestId: extension\.id/); }); test("Quick Pane uses native dimensions and request-first compact layout", async () => { const [source, stylesheet, configSource, nativeSource, capabilitySource] = await Promise.all([ readFile( new URL("../ui/src/quick-pane/QuickPane.tsx", import.meta.url), "utf8", ), readFile( new URL("../ui/src/quick-pane/quick-pane.css", import.meta.url), "utf8", ), readFile( new URL("../ui/src-tauri/tauri.conf.json", import.meta.url), "utf8", ), readFile(new URL("../ui/src-tauri/src/lib.rs", import.meta.url), "utf8"), readFile( new URL("../ui/src-tauri/capabilities/default.json", import.meta.url), "utf8", ), ]); const config = JSON.parse(configSource); const capability = JSON.parse(capabilitySource); const quickPane = config.app.windows.find( (window) => window.label === "quick-pane", ); assert.deepEqual( { width: quickPane.width, height: quickPane.height, minWidth: quickPane.minWidth, minHeight: quickPane.minHeight, transparent: quickPane.transparent, }, { width: 640, height: 360, minWidth: 520, minHeight: 360, transparent: true, }, ); assert.ok(capability.windows.includes("quick-pane")); assert.match(nativeSource, /WindowEvent::CloseRequested/); assert.match(nativeSource, /api\.prevent_close\(\)/); assert.match( nativeSource, /WebviewWindowBuilder::from_config\(app, config\)/, ); assert.match(nativeSource, /get_or_create_quick_pane/); assert.match(nativeSource, /fn resize_quick_pane/); assert.match(nativeSource, /height\.clamp\(360\.0, 520\.0\)/); assert.match(source, /new ResizeObserver\(resizeToContent\)/); assert.match(source, /invoke\("resize_quick_pane", \{ height \}\)/); assert.doesNotMatch(source, /onCloseRequested/); assert.match(source, /className="quick-pane-titlebar"/); assert.match(source, /startDragging\(\)/); assert.match(source, /className="quick-pane-request"/); assert.match(source, /className=\{`quick-pane-exchange/); assert.match(source, /onOpen=\{handleModelMenuOpen\}/); assert.match(source, /onClose=\{handleModelMenuClose\}/); assert.match(source, /requestPaneResize\(QUICK_PANE_MAX_HEIGHT\)/); assert.match(source, /const QUICK_PANE_MIN_HEIGHT = 360/); assert.match(source, /const QUICK_PANE_MAX_HEIGHT = 520/); assert.match(source, /height = clampQuickPaneHeight\(height\)/); assert.match(source, /if \(modelMenuOpenRef\.current\) return;/); assert.match(source, /submittedPromptTurnsRef/); assert.match(source, /function elapsedLabel/); assert.match(source, /quick-pane-assistant-activity/); assert.match(source, /transcriptFollowRef/); assert.match( source, /transcript\.scrollHeight\s*-\s*transcript\.clientHeight/, ); assert.match(source, /className="quick-pane-composer"/); assert.match(source, /className="quick-pane-composer-actions"/); assert.match(source, /replaceChat\(false, true\)/); assert.match(source, /replaceChat\(false, false\)/); assert.match(source, /className="quick-pane-send"/); assert.match(source, /ref=\{inputRef\}/); assert.match(source, /focusInput\(\)/); assert.doesNotMatch(source, /if \(!event\.payload\)[\s\S]*?hide\(/); assert.match( source, /event\.key === "Enter" &&\s*!event\.shiftKey &&\s*!event\.nativeEvent\.isComposing/, ); assert.match(source, /event\.preventDefault\(\);\s*void run\(\);/); assert.match( stylesheet, /\.quick-pane-exchange[\s\S]*?flex: 1 1 auto[\s\S]*?overflow: hidden/, ); assert.match( stylesheet, /\.quick-pane-transcript[\s\S]*?flex: 1 1 auto[\s\S]*?overflow-y: auto/, ); assert.match( stylesheet, /\.quick-pane-composer[\s\S]*?height: 120px[\s\S]*?min-height: 110px[\s\S]*?max-height: 130px/, ); assert.match( stylesheet, /\.quick-pane-titlebar[\s\S]*?height:\s*20px[\s\S]*?border-bottom:[\s\S]*?cursor:\s*grab/, ); assert.match( stylesheet, /\.quick-pane-composer-actions[\s\S]*?position: absolute/, ); assert.match(stylesheet, /\.quick-pane-send[\s\S]*?position: absolute/); assert.match( stylesheet, /\.quick-pane-assistant-elapsed[\s\S]*?font-variant-numeric/, ); assert.match(stylesheet, /\.quick-pane-assistant-activity[\s\S]*?color:/); assert.match(stylesheet, /@media \(max-width: 400px\)/); assert.match(stylesheet, /html,[\s\S]*?#root\s*\{[\s\S]*?overflow: hidden/); assert.match(stylesheet, /\.quick-pane\s*\{[\s\S]*?overflow: hidden/); assert.doesNotMatch( stylesheet, /\.quick-pane-primary-controls\s*\{[^}]*overflow:\s*(?:hidden|clip)/, ); assert.doesNotMatch( stylesheet, /\.quick-pane-header-actions\s*\{[^}]*overflow:\s*(?:hidden|clip)/, ); assert.match( stylesheet, /\.quick-pane button\.quick-pane-toolbar-control,[\s\S]*?height:\s*27px[\s\S]*?min-width:\s*48px[\s\S]*?padding:\s*0 8px[\s\S]*?border:[\s\S]*?border-radius:[\s\S]*?background:[\s\S]*?font:/, ); assert.match( stylesheet, /\.quick-pane \.quick-pane-toolbar-control:hover:not\(:disabled\)/, ); assert.match( stylesheet, /\.quick-pane \.quick-pane-toolbar-control:focus-visible/, ); assert.match( stylesheet, /\.quick-pane \.quick-pane-toolbar-control:disabled/, ); assert.match( stylesheet, /\.quick-pane-thinking\s*\{[\s\S]*?flex:\s*0 0 143px[\s\S]*?grid-template-columns:\s*auto minmax\(84px, 1fr\)/, ); assert.match( stylesheet, /\.quick-pane-toolbar\s*\{[\s\S]*?flex-wrap:\s*nowrap/, ); assert.match(source, /triggerClassName="quick-pane-toolbar-control"/); assert.match(source, /controlClassName="quick-pane-toolbar-control"/); assert.match( source, /className="quick-pane-toolbar-control quick-pane-action"/, ); assert.match( source, /className="quick-pane-toolbar-control quick-pane-close"/, ); assert.match(stylesheet, /#f0a347/); assert.doesNotMatch(stylesheet, /#8aa9d6|#46494f|#23252a/); const [selectorSource, selectorStylesheet] = await Promise.all([ readFile( new URL("../ui/src/components/ModelSelector.tsx", import.meta.url), "utf8", ), readFile( new URL("../ui/src/components/ModelSelector.css", import.meta.url), "utf8", ), ]); assert.match(selectorSource, /import "\.\/ModelSelector\.css";/); assert.match(selectorSource, /onOpen\?: \(\) => void;/); assert.match(selectorSource, /onClose\?: \(\) => void;/); assert.match(selectorSource, /onCloseRef\.current\?\.\(\);/); assert.match( selectorStylesheet, /\.model-trigger\s*>\s*span\s*\{[\s\S]*?overflow:\s*hidden;[\s\S]*?text-overflow:\s*ellipsis/, ); assert.match( selectorStylesheet, /\.model-menu\s*\{[\s\S]*?grid-template-rows:\s*auto auto minmax\(0, 1fr\);[\s\S]*?overflow:\s*hidden/, ); assert.match( selectorStylesheet, /\.model-results\s*\{[\s\S]*?overflow-y:\s*auto/, ); });