Refactor selectors: replace show/hide pairs with single showSelector helper

This commit is contained in:
Mario Zechner 2025-12-09 01:04:55 +01:00
parent fd7f20f968
commit dbd5f5eb0b

View file

@ -10,6 +10,7 @@ import type { AssistantMessage, Message } from "@mariozechner/pi-ai";
import type { SlashCommand } from "@mariozechner/pi-tui";
import {
CombinedAutocompleteProvider,
type Component,
Container,
Input,
Loader,
@ -72,15 +73,6 @@ export class InteractiveMode {
// Tool execution tracking: toolCallId -> component
private pendingTools = new Map<string, ToolExecutionComponent>();
// Selector components
private thinkingSelector: ThinkingSelectorComponent | null = null;
private queueModeSelector: QueueModeSelectorComponent | null = null;
private themeSelector: ThemeSelectorComponent | null = null;
private modelSelector: ModelSelectorComponent | null = null;
private userMessageSelector: UserMessageSelectorComponent | null = null;
private sessionSelector: SessionSelectorComponent | null = null;
private oauthSelector: OAuthSelectorComponent | null = null;
// Track if this is the first user message (to skip spacer)
private isFirstUserMessage = true;
@ -891,66 +883,68 @@ export class InteractiveMode {
// Selectors
// =========================================================================
/**
* Shows a selector component in place of the editor.
* @param create Factory that receives a `done` callback and returns the component and focus target
*/
private showSelector(create: (done: () => void) => { component: Component; focus: Component }): void {
const done = () => {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.ui.setFocus(this.editor);
};
const { component, focus } = create(done);
this.editorContainer.clear();
this.editorContainer.addChild(component);
this.ui.setFocus(focus);
this.ui.requestRender();
}
private showThinkingSelector(): void {
this.thinkingSelector = new ThinkingSelectorComponent(
this.showSelector((done) => {
const selector = new ThinkingSelectorComponent(
this.session.thinkingLevel,
(level) => {
this.session.setThinkingLevel(level);
this.updateEditorBorderColor();
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(new Text(theme.fg("dim", `Thinking level: ${level}`), 1, 0));
this.hideThinkingSelector();
done();
this.ui.requestRender();
},
() => {
this.hideThinkingSelector();
done();
this.ui.requestRender();
},
);
this.editorContainer.clear();
this.editorContainer.addChild(this.thinkingSelector);
this.ui.setFocus(this.thinkingSelector.getSelectList());
this.ui.requestRender();
}
private hideThinkingSelector(): void {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.thinkingSelector = null;
this.ui.setFocus(this.editor);
return { component: selector, focus: selector.getSelectList() };
});
}
private showQueueModeSelector(): void {
this.queueModeSelector = new QueueModeSelectorComponent(
this.showSelector((done) => {
const selector = new QueueModeSelectorComponent(
this.session.queueMode,
(mode) => {
this.session.setQueueMode(mode);
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(new Text(theme.fg("dim", `Queue mode: ${mode}`), 1, 0));
this.hideQueueModeSelector();
done();
this.ui.requestRender();
},
() => {
this.hideQueueModeSelector();
done();
this.ui.requestRender();
},
);
this.editorContainer.clear();
this.editorContainer.addChild(this.queueModeSelector);
this.ui.setFocus(this.queueModeSelector.getSelectList());
this.ui.requestRender();
}
private hideQueueModeSelector(): void {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.queueModeSelector = null;
this.ui.setFocus(this.editor);
return { component: selector, focus: selector.getSelectList() };
});
}
private showThemeSelector(): void {
const currentTheme = this.settingsManager.getTheme() || "dark";
this.themeSelector = new ThemeSelectorComponent(
this.showSelector((done) => {
const selector = new ThemeSelectorComponent(
currentTheme,
(themeName) => {
const result = setTheme(themeName);
@ -971,11 +965,11 @@ export class InteractiveMode {
),
);
}
this.hideThemeSelector();
done();
this.ui.requestRender();
},
() => {
this.hideThemeSelector();
done();
this.ui.requestRender();
},
(themeName) => {
@ -986,21 +980,13 @@ export class InteractiveMode {
}
},
);
this.editorContainer.clear();
this.editorContainer.addChild(this.themeSelector);
this.ui.setFocus(this.themeSelector.getSelectList());
this.ui.requestRender();
}
private hideThemeSelector(): void {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.themeSelector = null;
this.ui.setFocus(this.editor);
return { component: selector, focus: selector.getSelectList() };
});
}
private showModelSelector(): void {
this.modelSelector = new ModelSelectorComponent(
this.showSelector((done) => {
const selector = new ModelSelectorComponent(
this.ui,
this.session.model,
this.settingsManager,
@ -1009,25 +995,16 @@ export class InteractiveMode {
this.sessionManager.saveModelChange(model.provider, model.id);
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(new Text(theme.fg("dim", `Model: ${model.id}`), 1, 0));
this.hideModelSelector();
done();
this.ui.requestRender();
},
() => {
this.hideModelSelector();
done();
this.ui.requestRender();
},
);
this.editorContainer.clear();
this.editorContainer.addChild(this.modelSelector);
this.ui.setFocus(this.modelSelector);
this.ui.requestRender();
}
private hideModelSelector(): void {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.modelSelector = null;
this.ui.setFocus(this.editor);
return { component: selector, focus: selector };
});
}
private showUserMessageSelector(): void {
@ -1040,7 +1017,8 @@ export class InteractiveMode {
return;
}
this.userMessageSelector = new UserMessageSelectorComponent(
this.showSelector((done) => {
const selector = new UserMessageSelectorComponent(
userMessages.map((m) => ({ index: m.entryIndex, text: m.text })),
(entryIndex) => {
const selectedText = this.session.branch(entryIndex);
@ -1050,43 +1028,33 @@ export class InteractiveMode {
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(new Text(theme.fg("dim", "Branched to new session"), 1, 0));
this.editor.setText(selectedText);
this.hideUserMessageSelector();
done();
this.ui.requestRender();
},
() => {
this.hideUserMessageSelector();
done();
this.ui.requestRender();
},
);
this.editorContainer.clear();
this.editorContainer.addChild(this.userMessageSelector);
this.ui.setFocus(this.userMessageSelector.getMessageList());
this.ui.requestRender();
}
private hideUserMessageSelector(): void {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.userMessageSelector = null;
this.ui.setFocus(this.editor);
return { component: selector, focus: selector.getMessageList() };
});
}
private showSessionSelector(): void {
this.sessionSelector = new SessionSelectorComponent(
this.showSelector((done) => {
const selector = new SessionSelectorComponent(
this.sessionManager,
async (sessionPath) => {
this.hideSessionSelector();
done();
await this.handleResumeSession(sessionPath);
},
() => {
this.hideSessionSelector();
done();
this.ui.requestRender();
},
);
this.editorContainer.clear();
this.editorContainer.addChild(this.sessionSelector);
this.ui.setFocus(this.sessionSelector.getSessionList());
this.ui.requestRender();
return { component: selector, focus: selector.getSessionList() };
});
}
private async handleResumeSession(sessionPath: string): Promise<void> {
@ -1115,13 +1083,6 @@ export class InteractiveMode {
this.ui.requestRender();
}
private hideSessionSelector(): void {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.sessionSelector = null;
this.ui.setFocus(this.editor);
}
private async showOAuthSelector(mode: "login" | "logout"): Promise<void> {
if (mode === "logout") {
const loggedInProviders = listOAuthProviders();
@ -1135,10 +1096,11 @@ export class InteractiveMode {
}
}
this.oauthSelector = new OAuthSelectorComponent(
this.showSelector((done) => {
const selector = new OAuthSelectorComponent(
mode,
async (providerId: string) => {
this.hideOAuthSelector();
done();
if (mode === "login") {
this.chatContainer.addChild(new Spacer(1));
@ -1159,7 +1121,11 @@ export class InteractiveMode {
this.ui.requestRender();
const openCmd =
process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
process.platform === "darwin"
? "open"
: process.platform === "win32"
? "start"
: "xdg-open";
exec(`${openCmd} "${url}"`);
},
async () => {
@ -1208,22 +1174,12 @@ export class InteractiveMode {
}
},
() => {
this.hideOAuthSelector();
done();
this.ui.requestRender();
},
);
this.editorContainer.clear();
this.editorContainer.addChild(this.oauthSelector);
this.ui.setFocus(this.oauthSelector);
this.ui.requestRender();
}
private hideOAuthSelector(): void {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.oauthSelector = null;
this.ui.setFocus(this.editor);
return { component: selector, focus: selector };
});
}
// =========================================================================