mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-12 07:01:09 -07:00
116 lines
3.7 KiB
TypeScript
116 lines
3.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { StreamingState } from '../ui/types.js';
|
|
|
|
export interface TerminalTitleOptions {
|
|
streamingState: StreamingState;
|
|
thoughtSubject?: string;
|
|
isConfirming: boolean;
|
|
isSilentWorking: boolean;
|
|
folderName: string;
|
|
showThoughts: boolean;
|
|
useDynamicTitle: boolean;
|
|
}
|
|
|
|
function truncate(text: string, maxLen: number): string {
|
|
if (text.length <= maxLen) {
|
|
return text;
|
|
}
|
|
return text.substring(0, maxLen - 1) + '…';
|
|
}
|
|
|
|
/**
|
|
* Computes the dynamic terminal window title based on the current CLI state.
|
|
*
|
|
* @param options - The current state of the CLI and environment context
|
|
* @returns A formatted string padded to 80 characters for the terminal title
|
|
*/
|
|
export function computeTerminalTitle({
|
|
streamingState,
|
|
thoughtSubject,
|
|
isConfirming,
|
|
isSilentWorking,
|
|
folderName,
|
|
showThoughts,
|
|
useDynamicTitle,
|
|
}: TerminalTitleOptions): string {
|
|
const MAX_LEN = 80;
|
|
|
|
// Use CLI_TITLE env var if available, otherwise use the provided folder name
|
|
let displayContext = process.env['CLI_TITLE'] || folderName;
|
|
|
|
if (!useDynamicTitle) {
|
|
const base = 'Gemini CLI ';
|
|
// Max context length is 80 - base.length - 2 (for brackets)
|
|
const maxContextLen = MAX_LEN - base.length - 2;
|
|
displayContext = truncate(displayContext, maxContextLen);
|
|
return `${base}(${displayContext})`.padEnd(MAX_LEN, ' ');
|
|
}
|
|
|
|
// Pre-calculate suffix but keep it flexible
|
|
const getSuffix = (context: string) => ` (${context})`;
|
|
|
|
let title;
|
|
if (
|
|
isConfirming ||
|
|
streamingState === StreamingState.WaitingForConfirmation
|
|
) {
|
|
const base = '✋ Action Required';
|
|
// Max context length is 80 - base.length - 3 (for ' (' and ')')
|
|
const maxContextLen = MAX_LEN - base.length - 3;
|
|
const context = truncate(displayContext, maxContextLen);
|
|
title = `${base}${getSuffix(context)}`;
|
|
} else if (isSilentWorking) {
|
|
const base = '⏲ Working…';
|
|
// Max context length is 80 - base.length - 3 (for ' (' and ')')
|
|
const maxContextLen = MAX_LEN - base.length - 3;
|
|
const context = truncate(displayContext, maxContextLen);
|
|
title = `${base}${getSuffix(context)}`;
|
|
} else if (streamingState === StreamingState.Idle) {
|
|
const base = '◇ Ready';
|
|
// Max context length is 80 - base.length - 3 (for ' (' and ')')
|
|
const maxContextLen = MAX_LEN - base.length - 3;
|
|
const context = truncate(displayContext, maxContextLen);
|
|
title = `${base}${getSuffix(context)}`;
|
|
} else {
|
|
// Active/Working state
|
|
const cleanSubject =
|
|
showThoughts && thoughtSubject?.replace(/[\r\n]+/g, ' ').trim();
|
|
|
|
// If we have a thought subject and it's too long to fit with the suffix,
|
|
// we drop the suffix to maximize space for the thought.
|
|
// Otherwise, we keep the suffix.
|
|
const suffix = getSuffix(displayContext);
|
|
const suffixLen = suffix.length;
|
|
const canFitThoughtWithSuffix = cleanSubject
|
|
? cleanSubject.length + suffixLen + 3 <= MAX_LEN
|
|
: true;
|
|
|
|
let activeSuffix = '';
|
|
let maxStatusLen = MAX_LEN - 3; // Subtract icon prefix "✦ " (3 chars)
|
|
|
|
if (!cleanSubject || canFitThoughtWithSuffix) {
|
|
activeSuffix = suffix;
|
|
maxStatusLen -= activeSuffix.length;
|
|
}
|
|
|
|
const displayStatus = cleanSubject
|
|
? truncate(cleanSubject, maxStatusLen)
|
|
: 'Working…';
|
|
|
|
title = `✦ ${displayStatus}${activeSuffix}`;
|
|
}
|
|
|
|
// Remove control characters that could cause issues in terminal titles
|
|
// eslint-disable-next-line no-control-regex
|
|
const safeTitle = title.replace(/[\x00-\x1F\x7F]/g, '');
|
|
|
|
// Pad the title to a fixed width to prevent taskbar icon resizing/jitter.
|
|
// We also slice it to ensure it NEVER exceeds MAX_LEN.
|
|
return safeTitle.padEnd(MAX_LEN, ' ').substring(0, MAX_LEN);
|
|
}
|