mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-08 04:10:35 -07:00
128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2026 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { CoreToolCallStatus } from '@google/gemini-cli-core';
|
|
import { isShellTool } from '../components/messages/ToolShared.js';
|
|
import { theme } from '../semantic-colors.js';
|
|
import type {
|
|
HistoryItem,
|
|
HistoryItemWithoutId,
|
|
HistoryItemToolGroup,
|
|
IndividualToolCallDisplay,
|
|
} from '../types.js';
|
|
import type { BackgroundTask } from '../hooks/shellReducer.js';
|
|
import type { TrackedToolCall } from '../hooks/useToolScheduler.js';
|
|
|
|
function isTrackedToolCall(
|
|
tool: IndividualToolCallDisplay | TrackedToolCall,
|
|
): tool is TrackedToolCall {
|
|
return 'request' in tool;
|
|
}
|
|
|
|
/**
|
|
* Calculates the border color and dimming state for a tool group message.
|
|
*/
|
|
export function getToolGroupBorderAppearance(
|
|
item:
|
|
| HistoryItem
|
|
| HistoryItemWithoutId
|
|
| { type: 'tool_group'; tools: TrackedToolCall[] },
|
|
activeShellPtyId: number | null | undefined,
|
|
embeddedShellFocused: boolean | undefined,
|
|
allPendingItems: HistoryItemWithoutId[] = [],
|
|
backgroundTasks: Map<number, BackgroundTask> = new Map(),
|
|
): { borderColor: string; borderDimColor: boolean } {
|
|
if (item.type !== 'tool_group') {
|
|
return { borderColor: '', borderDimColor: false };
|
|
}
|
|
|
|
// If this item has no tools, it's a closing slice for the current batch.
|
|
// We need to look at the last pending item to determine the batch's appearance.
|
|
const toolsToInspect: Array<IndividualToolCallDisplay | TrackedToolCall> =
|
|
item.tools.length > 0
|
|
? item.tools
|
|
: allPendingItems
|
|
.filter(
|
|
(i): i is HistoryItemToolGroup =>
|
|
i !== null &&
|
|
i !== undefined &&
|
|
i.type === 'tool_group' &&
|
|
i.tools.length > 0,
|
|
)
|
|
.slice(-1)
|
|
.flatMap((i) => i.tools);
|
|
|
|
const hasPending = toolsToInspect.some((t) => {
|
|
if (isTrackedToolCall(t)) {
|
|
return (
|
|
t.status !== 'success' &&
|
|
t.status !== 'error' &&
|
|
t.status !== 'cancelled'
|
|
);
|
|
} else {
|
|
return (
|
|
t.status !== CoreToolCallStatus.Success &&
|
|
t.status !== CoreToolCallStatus.Error &&
|
|
t.status !== CoreToolCallStatus.Cancelled
|
|
);
|
|
}
|
|
});
|
|
|
|
const isEmbeddedShellFocused = toolsToInspect.some((t) => {
|
|
if (isTrackedToolCall(t)) {
|
|
return (
|
|
isShellTool(t.request.name) &&
|
|
t.status === 'executing' &&
|
|
t.pid === activeShellPtyId &&
|
|
!!embeddedShellFocused
|
|
);
|
|
} else {
|
|
return (
|
|
isShellTool(t.name) &&
|
|
t.status === CoreToolCallStatus.Executing &&
|
|
t.ptyId === activeShellPtyId &&
|
|
!!embeddedShellFocused
|
|
);
|
|
}
|
|
});
|
|
|
|
const isShellCommand = toolsToInspect.some((t) => {
|
|
if (isTrackedToolCall(t)) {
|
|
return isShellTool(t.request.name);
|
|
} else {
|
|
return isShellTool(t.name);
|
|
}
|
|
});
|
|
|
|
// If we have an active PTY that isn't a background shell, then the current
|
|
// pending batch is definitely a shell batch.
|
|
const isCurrentlyInShellTurn =
|
|
!!activeShellPtyId && !backgroundTasks.has(activeShellPtyId);
|
|
|
|
const isShell =
|
|
isShellCommand || (item.tools.length === 0 && isCurrentlyInShellTurn);
|
|
const isPending =
|
|
hasPending || (item.tools.length === 0 && isCurrentlyInShellTurn);
|
|
|
|
const isEffectivelyFocused =
|
|
isEmbeddedShellFocused ||
|
|
(item.tools.length === 0 &&
|
|
isCurrentlyInShellTurn &&
|
|
!!embeddedShellFocused);
|
|
|
|
const borderColor = isEffectivelyFocused
|
|
? theme.ui.focus
|
|
: isShell && isPending
|
|
? theme.ui.active
|
|
: isPending
|
|
? theme.status.warning
|
|
: theme.border.default;
|
|
|
|
const borderDimColor = isPending && (!isShell || !isEffectivelyFocused);
|
|
|
|
return { borderColor, borderDimColor };
|
|
}
|