mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
In fullscreen mode, the startup `Model scope: …` notice was still in the shell after pi exited. With `fullscreenExitOutput: "resume-hint"`, the shell showed it above the resume command. Cause: `InteractiveMode.init()` printed the notice with `console.log` before the renderer started. In fullscreen mode that output goes to the terminal's normal screen. Pi then draws on the alternate screen, and when it exits the terminal switches back to the normal screen, where the notice is still visible. The change: - Building the notice text moves into `getModelScopeNotice()`. - Regular mode prints it with `console.log` before the TUI starts, as before. - Fullscreen mode adds it to the header container below the built-in header. It uses a `ThemedText`, so the text is built after the terminal reports its colors and follows theme changes. Since fullscreen is the default `tuiMode`, this applies to every user who has `enabledModels` set and quiet startup off. Testing: - Added `packages/coding-agent/test/interactive-mode-model-scope.test.ts`. It checks two things for fullscreen: the notice renders in the header and `console.log` is not called, and nothing is added when startup details are hidden. - `npm run check` passes. - Manual check in tmux with `enabledModels` set: - fullscreen + `resume-hint`: before the change, `Model scope: claude-sonnet-4-5, claude-opus-4-5 (Ctrl+P to cycle)` was left in the shell after `/quit`. Now the line shows inside pi's header and the shell is clean after exit. - `tuiMode: "regular"`: unchanged, the line is still printed above the header. Fixes #11508 Requested by @badlogic Co-authored-by: triagent <triagent@localhost>
58 lines
2.2 KiB
TypeScript
58 lines
2.2 KiB
TypeScript
import { Container } from "@earendil-works/pi-tui";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
|
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
|
|
|
type ModelScopeContext = {
|
|
session: { scopedModels: Array<{ model: { id: string }; thinkingLevel?: string }> };
|
|
keybindings: { getKeys: (keybinding: string) => string[] };
|
|
headerContainer: Container;
|
|
shouldShowStartupDetails: () => boolean;
|
|
getModelScopeNotice: () => string | undefined;
|
|
};
|
|
|
|
type InteractiveModePrivate = {
|
|
getModelScopeNotice(this: ModelScopeContext): string | undefined;
|
|
showFullscreenModelScopeNotice(this: ModelScopeContext): void;
|
|
};
|
|
|
|
const interactiveModePrototype = InteractiveMode.prototype as unknown as InteractiveModePrivate;
|
|
|
|
function createContext(showDetails = true): ModelScopeContext {
|
|
return {
|
|
session: { scopedModels: [{ model: { id: "model-a" }, thinkingLevel: "high" }, { model: { id: "model-b" } }] },
|
|
keybindings: { getKeys: () => ["ctrl+p"] },
|
|
headerContainer: new Container(),
|
|
shouldShowStartupDetails: () => showDetails,
|
|
getModelScopeNotice: interactiveModePrototype.getModelScopeNotice,
|
|
};
|
|
}
|
|
|
|
function stripAnsi(text: string): string {
|
|
return text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
}
|
|
|
|
describe("InteractiveMode model scope notice", () => {
|
|
beforeEach(() => initTheme("dark"));
|
|
|
|
// Regression test for #11508: fullscreen must not print the notice to the shell's main screen.
|
|
it("renders the model scope inside the fullscreen header instead of printing it", () => {
|
|
const context = createContext();
|
|
const log = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
try {
|
|
interactiveModePrototype.showFullscreenModelScopeNotice.call(context);
|
|
expect(log).not.toHaveBeenCalled();
|
|
} finally {
|
|
log.mockRestore();
|
|
}
|
|
|
|
const rendered = stripAnsi(context.headerContainer.render(120).join("\n"));
|
|
expect(rendered).toContain("Model scope: model-a:high, model-b (Ctrl+P to cycle)");
|
|
});
|
|
|
|
it("adds nothing when startup details are hidden", () => {
|
|
const context = createContext(false);
|
|
interactiveModePrototype.showFullscreenModelScopeNotice.call(context);
|
|
expect(context.headerContainer.children).toHaveLength(0);
|
|
});
|
|
});
|