Merge branch 'feat/background-agent-control' into feat/background-agent-ui

# Conflicts:
#	packages/cli/src/ui/AppContainer.tsx
#	packages/cli/src/ui/components/DialogManager.tsx
This commit is contained in:
tanzhenxin 2026-04-23 10:35:51 +08:00
commit 9802b03c7f
280 changed files with 16325 additions and 2124 deletions

View file

@ -201,6 +201,13 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
!justNavigatedHistory,
);
// Ref so renderLineWithHighlighting (stable useCallback) can access fresh ghost text
const midInputGhostTextRef = useRef<{
text: string;
insertPosition: number;
} | null>(null);
midInputGhostTextRef.current = completion.midInputGhostText;
const reverseSearchCompletion = useReverseSearchCompletion(
buffer,
shellHistoryData,
@ -824,6 +831,18 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
}
}
// Accept mid-input ghost text with Tab (when no dropdown is visible)
if (
key.name === 'tab' &&
!key.paste &&
!key.shift &&
!completion.showSuggestions &&
midInputGhostTextRef.current
) {
buffer.insert(midInputGhostTextRef.current.text);
return true;
}
// Attachment mode handling - process before history navigation
if (isAttachmentMode && attachments.length > 0) {
if (key.name === 'left') {
@ -1171,12 +1190,31 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
});
if (isOnCursorLine && cursorVisualColAbsolute === cpLen(lineText)) {
// Add zero-width space after cursor to prevent Ink from trimming trailing whitespace
renderedLine.push(
<Text key={`cursor-end-${cursorVisualColAbsolute}`}>
{showCursorOpt ? chalk.inverse(' ') + '\u200B' : ' \u200B'}
</Text>,
);
// Check for mid-input ghost text (only renders when cursor is at end of input)
const ghostText = midInputGhostTextRef.current;
if (ghostText && showCursorOpt && ghostText.text.length > 0) {
// First ghost char: inverted (as cursor). Rest: dimmed gray.
const firstChar = ghostText.text[0]!;
const rest = ghostText.text.slice(firstChar.length);
renderedLine.push(
<Text key="ghost-cursor">{chalk.inverse(firstChar)}</Text>,
);
if (rest.length > 0) {
renderedLine.push(
<Text key="ghost-rest" color={theme.text.secondary}>
{rest}
</Text>,
);
}
renderedLine.push(<Text key="ghost-zwsp">{`\u200B`}</Text>);
} else {
// Add zero-width space after cursor to prevent Ink from trimming trailing whitespace
renderedLine.push(
<Text key={`cursor-end-${cursorVisualColAbsolute}`}>
{showCursorOpt ? chalk.inverse(' ') + '\u200B' : ' \u200B'}
</Text>,
);
}
}
return <Text>{renderedLine}</Text>;
@ -1283,6 +1321,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
}
prefix={prefixNode}
borderColor={borderColor}
topRightLabel={uiState.sessionName || undefined}
isActive={!isEmbeddedShellFocused}
renderLine={renderLineWithHighlighting}
/>