create headerContainer for custom header (#1039)

This commit is contained in:
Ted 2026-01-29 02:52:08 +01:00 committed by GitHub
parent b54d689ec1
commit d88087334f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -230,6 +230,9 @@ export class InteractiveMode {
// Custom footer from extension (undefined = use built-in footer)
private customFooter: (Component & { dispose?(): void }) | undefined = undefined;
// Header container that holds the built-in or custom header
private headerContainer: Container;
// Built-in header (logo + keybinding hints + changelog)
private builtInHeader: Component | undefined = undefined;
@ -254,6 +257,7 @@ export class InteractiveMode {
this.session = session;
this.version = VERSION;
this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
this.headerContainer = new Container();
this.chatContainer = new Container();
this.pendingMessagesContainer = new Container();
this.statusContainer = new Container();
@ -379,6 +383,9 @@ export class InteractiveMode {
this.fdPath = await ensureTool("fd");
this.setupAutocomplete(this.fdPath);
// Add header container as first child
this.ui.addChild(this.headerContainer);
// Add header with keybindings from config (unless silenced)
if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
@ -411,36 +418,39 @@ export class InteractiveMode {
this.builtInHeader = new Text(`${logo}\n${instructions}`, 1, 0);
// Setup UI layout
this.ui.addChild(new Spacer(1));
this.ui.addChild(this.builtInHeader);
this.ui.addChild(new Spacer(1));
this.headerContainer.addChild(new Spacer(1));
this.headerContainer.addChild(this.builtInHeader);
this.headerContainer.addChild(new Spacer(1));
// Add changelog if provided
if (this.changelogMarkdown) {
this.ui.addChild(new DynamicBorder());
this.headerContainer.addChild(new DynamicBorder());
if (this.settingsManager.getCollapseChangelog()) {
const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
const latestVersion = versionMatch ? versionMatch[1] : this.version;
const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
this.ui.addChild(new Text(condensedText, 1, 0));
this.headerContainer.addChild(new Text(condensedText, 1, 0));
} else {
this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
this.ui.addChild(new Spacer(1));
this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, this.getMarkdownThemeWithSettings()));
this.ui.addChild(new Spacer(1));
this.headerContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
this.headerContainer.addChild(new Spacer(1));
this.headerContainer.addChild(
new Markdown(this.changelogMarkdown.trim(), 1, 0, this.getMarkdownThemeWithSettings()),
);
this.headerContainer.addChild(new Spacer(1));
}
this.ui.addChild(new DynamicBorder());
this.headerContainer.addChild(new DynamicBorder());
}
} else {
// Minimal header when silenced
this.builtInHeader = new Text("", 0, 0);
this.headerContainer.addChild(this.builtInHeader);
if (this.changelogMarkdown) {
// Still show changelog notification even in silent mode
this.ui.addChild(new Spacer(1));
this.headerContainer.addChild(new Spacer(1));
const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
const latestVersion = versionMatch ? versionMatch[1] : this.version;
const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
this.ui.addChild(new Text(condensedText, 1, 0));
this.headerContainer.addChild(new Text(condensedText, 1, 0));
}
}
@ -457,6 +467,9 @@ export class InteractiveMode {
this.setupKeyHandlers();
this.setupEditorSubmitHandler();
// Render initial messages before starting the UI to avoid layout jump
this.renderInitialMessages();
// Start the UI
this.ui.start();
this.isInitialized = true;
@ -513,8 +526,6 @@ export class InteractiveMode {
}
});
this.renderInitialMessages();
// Show startup warnings
const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
@ -1296,21 +1307,25 @@ export class InteractiveMode {
this.customHeader.dispose();
}
// Remove current header from UI
if (this.customHeader) {
this.ui.removeChild(this.customHeader);
} else {
this.ui.removeChild(this.builtInHeader);
}
// Find the index of the current header in the header container
const currentHeader = this.customHeader || this.builtInHeader;
const index = this.headerContainer.children.indexOf(currentHeader);
if (factory) {
// Create and add custom header at position 1 (after initial spacer)
// Create and add custom header
this.customHeader = factory(this.ui, theme);
this.ui.children.splice(1, 0, this.customHeader);
if (index !== -1) {
this.headerContainer.children[index] = this.customHeader;
} else {
// If not found (e.g. builtInHeader was never added), add at the top
this.headerContainer.children.unshift(this.customHeader);
}
} else {
// Restore built-in header at position 1
// Restore built-in header
this.customHeader = undefined;
this.ui.children.splice(1, 0, this.builtInHeader);
if (index !== -1) {
this.headerContainer.children[index] = this.builtInHeader;
}
}
this.ui.requestRender();