mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-17 13:30:53 -07:00
Merge branch 'main' into gemini-cli-policies-ui
This commit is contained in:
@@ -156,7 +156,7 @@ export function colorizeCode({
|
||||
try {
|
||||
// Render the HAST tree using the adapted theme
|
||||
// Apply the theme's default foreground color to the top-level Text element
|
||||
let lines = codeToHighlight.split('\n');
|
||||
let lines = codeToHighlight.split(/\r?\n/);
|
||||
const padWidth = String(lines.length).length; // Calculate padding width based on number of lines
|
||||
|
||||
let hiddenLinesCount = 0;
|
||||
@@ -225,7 +225,7 @@ export function colorizeCode({
|
||||
);
|
||||
// Fall back to plain text with default color on error
|
||||
// Also display line numbers in fallback
|
||||
const lines = codeToHighlight.split('\n');
|
||||
const lines = codeToHighlight.split(/\r?\n/);
|
||||
const padWidth = String(lines.length).length; // Calculate padding width based on number of lines
|
||||
const fallbackLines = lines.map((line, index) => (
|
||||
<Box key={index} minHeight={1}>
|
||||
|
||||
@@ -414,6 +414,7 @@ const RenderListItemInternal: React.FC<RenderListItemProps> = ({
|
||||
}) => {
|
||||
const prefix = type === 'ol' ? `${marker}. ` : `${marker} `;
|
||||
const prefixWidth = prefix.length;
|
||||
// Account for leading whitespace (indentation level) plus the standard prefix padding
|
||||
const indentation = leadingWhitespace.length;
|
||||
const listResponseColor = theme.text.response ?? theme.text.primary;
|
||||
|
||||
@@ -422,7 +423,7 @@ const RenderListItemInternal: React.FC<RenderListItemProps> = ({
|
||||
paddingLeft={indentation + LIST_ITEM_PREFIX_PADDING}
|
||||
flexDirection="row"
|
||||
>
|
||||
<Box width={prefixWidth}>
|
||||
<Box width={prefixWidth} flexShrink={0}>
|
||||
<Text color={listResponseColor}>{prefix}</Text>
|
||||
</Box>
|
||||
<Box flexGrow={LIST_ITEM_TEXT_FLEX_GROW}>
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { Mock } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import clipboardy from 'clipboardy';
|
||||
import {
|
||||
|
||||
@@ -19,6 +19,9 @@ export const CACHE_EFFICIENCY_MEDIUM = 15;
|
||||
export const QUOTA_THRESHOLD_HIGH = 20;
|
||||
export const QUOTA_THRESHOLD_MEDIUM = 5;
|
||||
|
||||
export const QUOTA_USED_WARNING_THRESHOLD = 80;
|
||||
export const QUOTA_USED_CRITICAL_THRESHOLD = 95;
|
||||
|
||||
// --- Color Logic ---
|
||||
export const getStatusColor = (
|
||||
value: number,
|
||||
@@ -36,3 +39,19 @@ export const getStatusColor = (
|
||||
}
|
||||
return options.defaultColor ?? theme.status.error;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the status color based on "used" percentage (where higher is worse).
|
||||
*/
|
||||
export const getUsedStatusColor = (
|
||||
usedPercentage: number,
|
||||
thresholds: { warning: number; critical: number },
|
||||
) => {
|
||||
if (usedPercentage >= thresholds.critical) {
|
||||
return theme.status.error;
|
||||
}
|
||||
if (usedPercentage >= thresholds.warning) {
|
||||
return theme.status.warning;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
@@ -10,9 +10,45 @@ import {
|
||||
formatBytes,
|
||||
formatTimeAgo,
|
||||
stripReferenceContent,
|
||||
formatResetTime,
|
||||
} from './formatters.js';
|
||||
|
||||
describe('formatters', () => {
|
||||
describe('formatResetTime', () => {
|
||||
const NOW = new Date('2025-01-01T12:00:00Z');
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(NOW);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should format full time correctly', () => {
|
||||
const resetTime = new Date(NOW.getTime() + 90 * 60 * 1000).toISOString(); // 1h 30m
|
||||
const result = formatResetTime(resetTime);
|
||||
expect(result).toMatch(/1 hour 30 minutes at \d{1,2}:\d{2} [AP]M/);
|
||||
});
|
||||
|
||||
it('should format terse time correctly', () => {
|
||||
const resetTime = new Date(NOW.getTime() + 90 * 60 * 1000).toISOString(); // 1h 30m
|
||||
expect(formatResetTime(resetTime, 'terse')).toBe('1h 30m');
|
||||
});
|
||||
|
||||
it('should format column time correctly', () => {
|
||||
const resetTime = new Date(NOW.getTime() + 90 * 60 * 1000).toISOString(); // 1h 30m
|
||||
const result = formatResetTime(resetTime, 'column');
|
||||
expect(result).toMatch(/\d{1,2}:\d{2} [AP]M \(1h 30m\)/);
|
||||
});
|
||||
|
||||
it('should handle zero or negative diff by returning empty string', () => {
|
||||
const resetTime = new Date(NOW.getTime() - 1000).toISOString();
|
||||
expect(formatResetTime(resetTime)).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatBytes', () => {
|
||||
it('should format bytes into KB', () => {
|
||||
expect(formatBytes(12345)).toBe('12.1 KB');
|
||||
|
||||
@@ -98,26 +98,58 @@ export function stripReferenceContent(text: string): string {
|
||||
return text.replace(pattern, '').trim();
|
||||
}
|
||||
|
||||
export const formatResetTime = (resetTime: string): string => {
|
||||
const diff = new Date(resetTime).getTime() - Date.now();
|
||||
export const formatResetTime = (
|
||||
resetTime: string | undefined,
|
||||
format: 'terse' | 'column' | 'full' = 'full',
|
||||
): string => {
|
||||
if (!resetTime) return '';
|
||||
const resetDate = new Date(resetTime);
|
||||
if (isNaN(resetDate.getTime())) return '';
|
||||
|
||||
const diff = resetDate.getTime() - Date.now();
|
||||
if (diff <= 0) return '';
|
||||
|
||||
const totalMinutes = Math.ceil(diff / (1000 * 60));
|
||||
const hours = Math.floor(totalMinutes / 60);
|
||||
const minutes = totalMinutes % 60;
|
||||
|
||||
const fmt = (val: number, unit: 'hour' | 'minute') =>
|
||||
new Intl.NumberFormat('en', {
|
||||
style: 'unit',
|
||||
unit,
|
||||
unitDisplay: 'narrow',
|
||||
}).format(val);
|
||||
const isTerse = format === 'terse';
|
||||
const isColumn = format === 'column';
|
||||
|
||||
if (hours > 0 && minutes > 0) {
|
||||
return `resets in ${fmt(hours, 'hour')} ${fmt(minutes, 'minute')}`;
|
||||
} else if (hours > 0) {
|
||||
return `resets in ${fmt(hours, 'hour')}`;
|
||||
if (isTerse || isColumn) {
|
||||
const hoursStr = hours > 0 ? `${hours}h` : '';
|
||||
const minutesStr = minutes > 0 ? `${minutes}m` : '';
|
||||
const duration =
|
||||
hoursStr && minutesStr
|
||||
? `${hoursStr} ${minutesStr}`
|
||||
: hoursStr || minutesStr;
|
||||
|
||||
if (isColumn) {
|
||||
const timeStr = new Intl.DateTimeFormat('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
}).format(resetDate);
|
||||
return duration ? `${timeStr} (${duration})` : timeStr;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
return `resets in ${fmt(minutes, 'minute')}`;
|
||||
let duration = '';
|
||||
if (hours > 0) {
|
||||
duration = `${hours} hour${hours > 1 ? 's' : ''}`;
|
||||
if (minutes > 0) {
|
||||
duration += ` ${minutes} minute${minutes > 1 ? 's' : ''}`;
|
||||
}
|
||||
} else {
|
||||
duration = `${minutes} minute${minutes > 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
const timeStr = new Intl.DateTimeFormat('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
timeZoneName: 'short',
|
||||
}).format(resetDate);
|
||||
|
||||
return `${duration} at ${timeStr}`;
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export type HighlightToken = {
|
||||
// It matches any character except strict delimiters (ASCII whitespace, comma, etc.).
|
||||
// This supports URIs like `@file:///example.txt` and filenames with Unicode spaces (like NNBSP).
|
||||
const HIGHLIGHT_REGEX = new RegExp(
|
||||
`(^/[a-zA-Z0-9_-]+|@${AT_COMMAND_PATH_REGEX_SOURCE}|${PASTED_TEXT_PLACEHOLDER_REGEX.source})`,
|
||||
`(^/[a-zA-Z0-9_-]+|(?<!\\\\)@${AT_COMMAND_PATH_REGEX_SOURCE}|${PASTED_TEXT_PLACEHOLDER_REGEX.source})`,
|
||||
'g',
|
||||
);
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ import type { Content } from '@google/genai';
|
||||
/**
|
||||
* Serializes chat history to a Markdown string.
|
||||
*/
|
||||
export function serializeHistoryToMarkdown(history: Content[]): string {
|
||||
export function serializeHistoryToMarkdown(
|
||||
history: readonly Content[],
|
||||
): string {
|
||||
return history
|
||||
.map((item) => {
|
||||
const text =
|
||||
@@ -49,7 +51,7 @@ export function serializeHistoryToMarkdown(history: Content[]): string {
|
||||
* Options for exporting chat history.
|
||||
*/
|
||||
export interface ExportHistoryOptions {
|
||||
history: Content[];
|
||||
history: readonly Content[];
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { formatKeyBinding, formatCommand } from './keybindingUtils.js';
|
||||
import { Command } from '../../config/keyBindings.js';
|
||||
import type { KeyBinding } from '../../config/keyBindings.js';
|
||||
|
||||
describe('keybindingUtils', () => {
|
||||
describe('formatKeyBinding', () => {
|
||||
const testCases: Array<{
|
||||
name: string;
|
||||
binding: KeyBinding;
|
||||
expected: {
|
||||
darwin: string;
|
||||
win32: string;
|
||||
linux: string;
|
||||
default: string;
|
||||
};
|
||||
}> = [
|
||||
{
|
||||
name: 'simple key',
|
||||
binding: { key: 'a' },
|
||||
expected: { darwin: 'A', win32: 'A', linux: 'A', default: 'A' },
|
||||
},
|
||||
{
|
||||
name: 'named key (return)',
|
||||
binding: { key: 'return' },
|
||||
expected: {
|
||||
darwin: 'Enter',
|
||||
win32: 'Enter',
|
||||
linux: 'Enter',
|
||||
default: 'Enter',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'named key (escape)',
|
||||
binding: { key: 'escape' },
|
||||
expected: { darwin: 'Esc', win32: 'Esc', linux: 'Esc', default: 'Esc' },
|
||||
},
|
||||
{
|
||||
name: 'ctrl modifier',
|
||||
binding: { key: 'c', ctrl: true },
|
||||
expected: {
|
||||
darwin: 'Ctrl+C',
|
||||
win32: 'Ctrl+C',
|
||||
linux: 'Ctrl+C',
|
||||
default: 'Ctrl+C',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'cmd modifier',
|
||||
binding: { key: 'z', cmd: true },
|
||||
expected: {
|
||||
darwin: 'Cmd+Z',
|
||||
win32: 'Win+Z',
|
||||
linux: 'Super+Z',
|
||||
default: 'Cmd/Win+Z',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'alt/option modifier',
|
||||
binding: { key: 'left', alt: true },
|
||||
expected: {
|
||||
darwin: 'Option+Left',
|
||||
win32: 'Alt+Left',
|
||||
linux: 'Alt+Left',
|
||||
default: 'Alt+Left',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'shift modifier',
|
||||
binding: { key: 'up', shift: true },
|
||||
expected: {
|
||||
darwin: 'Shift+Up',
|
||||
win32: 'Shift+Up',
|
||||
linux: 'Shift+Up',
|
||||
default: 'Shift+Up',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'multiple modifiers (ctrl+shift)',
|
||||
binding: { key: 'z', ctrl: true, shift: true },
|
||||
expected: {
|
||||
darwin: 'Ctrl+Shift+Z',
|
||||
win32: 'Ctrl+Shift+Z',
|
||||
linux: 'Ctrl+Shift+Z',
|
||||
default: 'Ctrl+Shift+Z',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'all modifiers',
|
||||
binding: { key: 'a', ctrl: true, alt: true, shift: true, cmd: true },
|
||||
expected: {
|
||||
darwin: 'Ctrl+Option+Shift+Cmd+A',
|
||||
win32: 'Ctrl+Alt+Shift+Win+A',
|
||||
linux: 'Ctrl+Alt+Shift+Super+A',
|
||||
default: 'Ctrl+Alt+Shift+Cmd/Win+A',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
testCases.forEach(({ name, binding, expected }) => {
|
||||
describe(`${name}`, () => {
|
||||
it('formats correctly for darwin', () => {
|
||||
expect(formatKeyBinding(binding, 'darwin')).toBe(expected.darwin);
|
||||
});
|
||||
it('formats correctly for win32', () => {
|
||||
expect(formatKeyBinding(binding, 'win32')).toBe(expected.win32);
|
||||
});
|
||||
it('formats correctly for linux', () => {
|
||||
expect(formatKeyBinding(binding, 'linux')).toBe(expected.linux);
|
||||
});
|
||||
it('formats correctly for default', () => {
|
||||
expect(formatKeyBinding(binding, 'default')).toBe(expected.default);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatCommand', () => {
|
||||
it('formats default commands (using default platform behavior)', () => {
|
||||
expect(formatCommand(Command.QUIT, undefined, 'default')).toBe('Ctrl+C');
|
||||
expect(formatCommand(Command.SUBMIT, undefined, 'default')).toBe('Enter');
|
||||
expect(
|
||||
formatCommand(Command.TOGGLE_BACKGROUND_SHELL, undefined, 'default'),
|
||||
).toBe('Ctrl+B');
|
||||
});
|
||||
|
||||
it('returns empty string for unknown commands', () => {
|
||||
expect(
|
||||
formatCommand(
|
||||
'unknown.command' as unknown as Command,
|
||||
undefined,
|
||||
'default',
|
||||
),
|
||||
).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,111 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import process from 'node:process';
|
||||
import {
|
||||
type Command,
|
||||
type KeyBinding,
|
||||
type KeyBindingConfig,
|
||||
defaultKeyBindings,
|
||||
} from '../../config/keyBindings.js';
|
||||
|
||||
/**
|
||||
* Maps internal key names to user-friendly display names.
|
||||
*/
|
||||
const KEY_NAME_MAP: Record<string, string> = {
|
||||
return: 'Enter',
|
||||
escape: 'Esc',
|
||||
backspace: 'Backspace',
|
||||
delete: 'Delete',
|
||||
up: 'Up',
|
||||
down: 'Down',
|
||||
left: 'Left',
|
||||
right: 'Right',
|
||||
pageup: 'Page Up',
|
||||
pagedown: 'Page Down',
|
||||
home: 'Home',
|
||||
end: 'End',
|
||||
tab: 'Tab',
|
||||
space: 'Space',
|
||||
'double escape': 'Double Esc',
|
||||
};
|
||||
|
||||
interface ModifierMap {
|
||||
ctrl: string;
|
||||
alt: string;
|
||||
shift: string;
|
||||
cmd: string;
|
||||
}
|
||||
|
||||
const MODIFIER_MAPS: Record<string, ModifierMap> = {
|
||||
darwin: {
|
||||
ctrl: 'Ctrl',
|
||||
alt: 'Option',
|
||||
shift: 'Shift',
|
||||
cmd: 'Cmd',
|
||||
},
|
||||
win32: {
|
||||
ctrl: 'Ctrl',
|
||||
alt: 'Alt',
|
||||
shift: 'Shift',
|
||||
cmd: 'Win',
|
||||
},
|
||||
linux: {
|
||||
ctrl: 'Ctrl',
|
||||
alt: 'Alt',
|
||||
shift: 'Shift',
|
||||
cmd: 'Super',
|
||||
},
|
||||
default: {
|
||||
ctrl: 'Ctrl',
|
||||
alt: 'Alt',
|
||||
shift: 'Shift',
|
||||
cmd: 'Cmd/Win',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a single KeyBinding into a human-readable string (e.g., "Ctrl+C").
|
||||
*/
|
||||
export function formatKeyBinding(
|
||||
binding: KeyBinding,
|
||||
platform?: string,
|
||||
): string {
|
||||
const activePlatform =
|
||||
platform ??
|
||||
(process.env['FORCE_GENERIC_KEYBINDING_HINTS']
|
||||
? 'default'
|
||||
: process.platform);
|
||||
const modMap = MODIFIER_MAPS[activePlatform] || MODIFIER_MAPS['default'];
|
||||
const parts: string[] = [];
|
||||
|
||||
if (binding.ctrl) parts.push(modMap.ctrl);
|
||||
if (binding.alt) parts.push(modMap.alt);
|
||||
if (binding.shift) parts.push(modMap.shift);
|
||||
if (binding.cmd) parts.push(modMap.cmd);
|
||||
|
||||
const keyName = KEY_NAME_MAP[binding.key] || binding.key.toUpperCase();
|
||||
parts.push(keyName);
|
||||
|
||||
return parts.join('+');
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the primary keybinding for a command.
|
||||
*/
|
||||
export function formatCommand(
|
||||
command: Command,
|
||||
config: KeyBindingConfig = defaultKeyBindings,
|
||||
platform?: string,
|
||||
): string {
|
||||
const bindings = config[command];
|
||||
if (!bindings || bindings.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Use the first binding as the primary one for display
|
||||
return formatKeyBinding(bindings[0], platform);
|
||||
}
|
||||
@@ -4,9 +4,12 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Command, keyMatchers } from '../keyMatchers.js';
|
||||
import { Command } from '../key/keyMatchers.js';
|
||||
import type { Key } from '../hooks/useKeypress.js';
|
||||
import { useKeyMatchers } from '../hooks/useKeyMatchers.js';
|
||||
|
||||
export function shouldDismissShortcutsHelpOnHotkey(key: Key): boolean {
|
||||
return Object.values(Command).some((command) => keyMatchers[command](key));
|
||||
export function useIsHelpDismissKey(): (key: Key) => boolean {
|
||||
const keyMatchers = useKeyMatchers();
|
||||
return (key: Key) =>
|
||||
Object.values(Command).some((command) => keyMatchers[command](key));
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
/// <reference types="vitest/globals" />
|
||||
|
||||
import type { MockInstance } from 'vitest';
|
||||
import { vi } from 'vitest';
|
||||
import { vi, type MockInstance } from 'vitest';
|
||||
import { TextOutput } from './textOutput.js';
|
||||
|
||||
describe('TextOutput', () => {
|
||||
|
||||
@@ -48,12 +48,14 @@ describe('textUtils', () => {
|
||||
it('should handle unicode characters that crash string-width', () => {
|
||||
// U+0602 caused string-width to crash (see #16418)
|
||||
const char = '';
|
||||
expect(getCachedStringWidth(char)).toBe(0);
|
||||
expect(() => getCachedStringWidth(char)).not.toThrow();
|
||||
expect(typeof getCachedStringWidth(char)).toBe('number');
|
||||
});
|
||||
|
||||
it('should handle unicode characters that crash string-width with ANSI codes', () => {
|
||||
const charWithAnsi = '\u001b[31m' + '' + '\u001b[0m';
|
||||
expect(getCachedStringWidth(charWithAnsi)).toBe(0);
|
||||
expect(() => getCachedStringWidth(charWithAnsi)).not.toThrow();
|
||||
expect(typeof getCachedStringWidth(charWithAnsi)).toBe('number');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,13 @@ export const TOOL_RESULT_ASB_RESERVED_LINE_COUNT = 6;
|
||||
export const TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT = 2;
|
||||
export const TOOL_RESULT_MIN_LINES_SHOWN = 2;
|
||||
|
||||
/**
|
||||
* The vertical space (in lines) consumed by the shell UI elements
|
||||
* (1 line for the shell title/header and 2 lines for the top and bottom borders).
|
||||
*/
|
||||
export const SHELL_CONTENT_OVERHEAD =
|
||||
TOOL_RESULT_STATIC_HEIGHT + TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT;
|
||||
|
||||
/**
|
||||
* Calculates the final height available for the content of a tool result display.
|
||||
*
|
||||
@@ -46,7 +53,7 @@ export function calculateToolContentMaxLines(options: {
|
||||
)
|
||||
: undefined;
|
||||
|
||||
if (maxLinesLimit) {
|
||||
if (maxLinesLimit !== undefined) {
|
||||
contentHeight =
|
||||
contentHeight !== undefined
|
||||
? Math.min(contentHeight, maxLinesLimit)
|
||||
@@ -88,7 +95,9 @@ export function calculateShellMaxLines(options: {
|
||||
|
||||
// 2. Handle cases where height is unknown (Standard mode history).
|
||||
if (availableTerminalHeight === undefined) {
|
||||
return isAlternateBuffer ? ACTIVE_SHELL_MAX_LINES : undefined;
|
||||
return isAlternateBuffer
|
||||
? ACTIVE_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const maxLinesBasedOnHeight = Math.max(1, availableTerminalHeight - 2);
|
||||
@@ -103,8 +112,8 @@ export function calculateShellMaxLines(options: {
|
||||
// 4. Fall back to process-based constants.
|
||||
const isExecuting = status === CoreToolCallStatus.Executing;
|
||||
const shellMaxLinesLimit = isExecuting
|
||||
? ACTIVE_SHELL_MAX_LINES
|
||||
: COMPLETED_SHELL_MAX_LINES;
|
||||
? ACTIVE_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD
|
||||
: COMPLETED_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD;
|
||||
|
||||
return Math.min(maxLinesBasedOnHeight, shellMaxLinesLimit);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user