import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import clipboardy from 'clipboardy';
import { useCallback, useEffect, useState, useRef } from 'react';
import { Box, Text, useStdout } from 'ink';
import { SuggestionsDisplay, MAX_WIDTH } from './SuggestionsDisplay.js';
import { theme } from '../semantic-colors.js';
import { useInputHistory } from '../hooks/useInputHistory.js';
import { logicalPosToOffset } from './shared/text-buffer.js';
import { cpSlice, cpLen, toCodePoints } from '../utils/textUtils.js';
import chalk from 'chalk';
import stringWidth from 'string-width';
import { useShellHistory } from '../hooks/useShellHistory.js';
import { useReverseSearchCompletion } from '../hooks/useReverseSearchCompletion.js';
import { useCommandCompletion, CompletionMode, } from '../hooks/useCommandCompletion.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { keyMatchers, Command } from '../keyMatchers.js';
import { ApprovalMode, debugLogger } from '@google/gemini-cli-core';
import { parseInputForHighlighting, parseSegmentsFromTokens, } from '../utils/highlight.js';
import { useKittyKeyboardProtocol } from '../hooks/useKittyKeyboardProtocol.js';
import { clipboardHasImage, saveClipboardImage, cleanupOldClipboardImages, } from '../utils/clipboardUtils.js';
import { isAutoExecutableCommand, isSlashCommand, } from '../utils/commandUtils.js';
import * as path from 'node:path';
import { SCREEN_READER_USER_PREFIX } from '../textConstants.js';
import { useShellFocusState } from '../contexts/ShellFocusContext.js';
import { useUIState } from '../contexts/UIStateContext.js';
import { useSettings } from '../contexts/SettingsContext.js';
import { StreamingState } from '../types.js';
import { useMouseClick } from '../hooks/useMouseClick.js';
import { useMouse } from '../contexts/MouseContext.js';
import { useUIActions } from '../contexts/UIActionsContext.js';
/**
 * Returns if the terminal can be trusted to handle paste events atomically
 * rather than potentially sending multiple paste events separated by line
 * breaks which could trigger unintended command execution.
 */
export function isTerminalPasteTrusted(kittyProtocolSupported) {
    // Ideally we could trust all VSCode family terminals as well but it appears
    // we cannot as Cursor users on windows reported being impacted by this
    // issue (https://github.com/google-gemini/gemini-cli/issues/3763).
    return kittyProtocolSupported;
}
// The input content, input container, and input suggestions list may have different widths
export const calculatePromptWidths = (mainContentWidth) => {
    const FRAME_PADDING_AND_BORDER = 4; // Border (2) + padding (2)
    const PROMPT_PREFIX_WIDTH = 2; // '> ' or '! '
    const FRAME_OVERHEAD = FRAME_PADDING_AND_BORDER + PROMPT_PREFIX_WIDTH;
    const suggestionsWidth = Math.max(20, mainContentWidth);
    return {
        inputWidth: Math.max(mainContentWidth - FRAME_OVERHEAD, 1),
        containerWidth: mainContentWidth,
        suggestionsWidth,
        frameOverhead: FRAME_OVERHEAD,
    };
};
export const InputPrompt = ({ buffer, onSubmit, userMessages, onClearScreen, config, slashCommands, commandContext, placeholder = '  Type your message or @path/to/file', focus = true, inputWidth, suggestionsWidth, shellModeActive, setShellModeActive, approvalMode, onEscapePromptChange, onSuggestionsVisibilityChange, vimHandleInput, isEmbeddedShellFocused, setQueueErrorMessage, streamingState, popAllMessages, suggestionsPosition = 'below', setBannerVisible, }) => {
    const { stdout } = useStdout();
    const { merged: settings } = useSettings();
    const kittyProtocol = useKittyKeyboardProtocol();
    const isShellFocused = useShellFocusState();
    const { setEmbeddedShellFocused } = useUIActions();
    const { mainAreaWidth, activePtyId } = useUIState();
    const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
    const escPressCount = useRef(0);
    const [showEscapePrompt, setShowEscapePrompt] = useState(false);
    const escapeTimerRef = useRef(null);
    const [recentUnsafePasteTime, setRecentUnsafePasteTime] = useState(null);
    const pasteTimeoutRef = useRef(null);
    const innerBoxRef = useRef(null);
    const [reverseSearchActive, setReverseSearchActive] = useState(false);
    const [commandSearchActive, setCommandSearchActive] = useState(false);
    const [textBeforeReverseSearch, setTextBeforeReverseSearch] = useState('');
    const [cursorPosition, setCursorPosition] = useState([
        0, 0,
    ]);
    const [expandedSuggestionIndex, setExpandedSuggestionIndex] = useState(-1);
    const shellHistory = useShellHistory(config.getProjectRoot());
    const shellHistoryData = shellHistory.history;
    const completion = useCommandCompletion(buffer, config.getTargetDir(), slashCommands, commandContext, reverseSearchActive, shellModeActive, config);
    const reverseSearchCompletion = useReverseSearchCompletion(buffer, shellHistoryData, reverseSearchActive);
    const commandSearchCompletion = useReverseSearchCompletion(buffer, userMessages, commandSearchActive);
    const resetCompletionState = completion.resetCompletionState;
    const resetReverseSearchCompletionState = reverseSearchCompletion.resetCompletionState;
    const resetCommandSearchCompletionState = commandSearchCompletion.resetCompletionState;
    const showCursor = focus && isShellFocused && !isEmbeddedShellFocused;
    const resetEscapeState = useCallback(() => {
        if (escapeTimerRef.current) {
            clearTimeout(escapeTimerRef.current);
            escapeTimerRef.current = null;
        }
        escPressCount.current = 0;
        setShowEscapePrompt(false);
    }, []);
    // Notify parent component about escape prompt state changes
    useEffect(() => {
        if (onEscapePromptChange) {
            onEscapePromptChange(showEscapePrompt);
        }
    }, [showEscapePrompt, onEscapePromptChange]);
    // Clear escape prompt timer on unmount
    useEffect(() => () => {
        if (escapeTimerRef.current) {
            clearTimeout(escapeTimerRef.current);
        }
        if (pasteTimeoutRef.current) {
            clearTimeout(pasteTimeoutRef.current);
        }
    }, []);
    const handleSubmitAndClear = useCallback((submittedValue) => {
        if (shellModeActive) {
            shellHistory.addCommandToHistory(submittedValue);
        }
        // Clear the buffer *before* calling onSubmit to prevent potential re-submission
        // if onSubmit triggers a re-render while the buffer still holds the old value.
        buffer.setText('');
        onSubmit(submittedValue);
        resetCompletionState();
        resetReverseSearchCompletionState();
    }, [
        onSubmit,
        buffer,
        resetCompletionState,
        shellModeActive,
        shellHistory,
        resetReverseSearchCompletionState,
    ]);
    const handleSubmit = useCallback((submittedValue) => {
        const trimmedMessage = submittedValue.trim();
        const isSlash = isSlashCommand(trimmedMessage);
        const isShell = shellModeActive;
        if ((isSlash || isShell) &&
            streamingState === StreamingState.Responding) {
            setQueueErrorMessage(`${isShell ? 'Shell' : 'Slash'} commands cannot be queued`);
            return;
        }
        handleSubmitAndClear(trimmedMessage);
    }, [
        handleSubmitAndClear,
        shellModeActive,
        streamingState,
        setQueueErrorMessage,
    ]);
    const customSetTextAndResetCompletionSignal = useCallback((newText) => {
        buffer.setText(newText);
        setJustNavigatedHistory(true);
    }, [buffer, setJustNavigatedHistory]);
    const inputHistory = useInputHistory({
        userMessages,
        onSubmit: handleSubmitAndClear,
        isActive: (!completion.showSuggestions || completion.suggestions.length === 1) &&
            !shellModeActive,
        currentQuery: buffer.text,
        onChange: customSetTextAndResetCompletionSignal,
    });
    // Effect to reset completion if history navigation just occurred and set the text
    useEffect(() => {
        if (justNavigatedHistory) {
            resetCompletionState();
            resetReverseSearchCompletionState();
            resetCommandSearchCompletionState();
            setExpandedSuggestionIndex(-1);
            setJustNavigatedHistory(false);
        }
    }, [
        justNavigatedHistory,
        buffer.text,
        resetCompletionState,
        setJustNavigatedHistory,
        resetReverseSearchCompletionState,
        resetCommandSearchCompletionState,
    ]);
    // Helper function to handle loading queued messages into input
    // Returns true if we should continue with input history navigation
    const tryLoadQueuedMessages = useCallback(() => {
        if (buffer.text.trim() === '' && popAllMessages) {
            const allMessages = popAllMessages();
            if (allMessages) {
                buffer.setText(allMessages);
            }
            else {
                // No queued messages, proceed with input history
                inputHistory.navigateUp();
            }
            return true; // We handled the up arrow key
        }
        return false;
    }, [buffer, popAllMessages, inputHistory]);
    // Handle clipboard image pasting with Ctrl+V
    const handleClipboardPaste = useCallback(async () => {
        try {
            if (await clipboardHasImage()) {
                const imagePath = await saveClipboardImage(config.getTargetDir());
                if (imagePath) {
                    // Clean up old images
                    cleanupOldClipboardImages(config.getTargetDir()).catch(() => {
                        // Ignore cleanup errors
                    });
                    // Get relative path from current directory
                    const relativePath = path.relative(config.getTargetDir(), imagePath);
                    // Insert @path reference at cursor position
                    const insertText = `@${relativePath}`;
                    const currentText = buffer.text;
                    const offset = buffer.getOffset();
                    // Add spaces around the path if needed
                    let textToInsert = insertText;
                    const charBefore = offset > 0 ? currentText[offset - 1] : '';
                    const charAfter = offset < currentText.length ? currentText[offset] : '';
                    if (charBefore && charBefore !== ' ' && charBefore !== '\n') {
                        textToInsert = ' ' + textToInsert;
                    }
                    if (!charAfter || (charAfter !== ' ' && charAfter !== '\n')) {
                        textToInsert = textToInsert + ' ';
                    }
                    // Insert at cursor position
                    buffer.replaceRangeByOffset(offset, offset, textToInsert);
                    return;
                }
            }
            if (settings.experimental?.useOSC52Paste) {
                stdout.write('\x1b]52;c;?\x07');
            }
            else {
                const textToInsert = await clipboardy.read();
                const offset = buffer.getOffset();
                buffer.replaceRangeByOffset(offset, offset, textToInsert);
            }
        }
        catch (error) {
            debugLogger.error('Error handling paste:', error);
        }
    }, [buffer, config, stdout, settings]);
    useMouseClick(innerBoxRef, (_event, relX, relY) => {
        if (isEmbeddedShellFocused) {
            setEmbeddedShellFocused(false);
        }
        const visualRow = buffer.visualScrollRow + relY;
        buffer.moveToVisualPosition(visualRow, relX);
    }, { isActive: focus });
    useMouse((event) => {
        if (event.name === 'right-release') {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            handleClipboardPaste();
        }
    }, { isActive: focus });
    const handleInput = useCallback((key) => {
        // TODO(jacobr): this special case is likely not needed anymore.
        // We should probably stop supporting paste if the InputPrompt is not
        // focused.
        /// We want to handle paste even when not focused to support drag and drop.
        if (!focus && !key.paste) {
            return;
        }
        if (key.paste) {
            // Record paste time to prevent accidental auto-submission
            if (!isTerminalPasteTrusted(kittyProtocol.enabled)) {
                setRecentUnsafePasteTime(Date.now());
                // Clear any existing paste timeout
                if (pasteTimeoutRef.current) {
                    clearTimeout(pasteTimeoutRef.current);
                }
                // Clear the paste protection after a very short delay to prevent
                // false positives.
                // Due to how we use a reducer for text buffer state updates, it is
                // reasonable to expect that key events that are really part of the
                // same paste will be processed in the same event loop tick. 40ms
                // is chosen arbitrarily as it is faster than a typical human
                // could go from pressing paste to pressing enter. The fastest typists
                // can type at 200 words per minute which roughly translates to 50ms
                // per letter.
                pasteTimeoutRef.current = setTimeout(() => {
                    setRecentUnsafePasteTime(null);
                    pasteTimeoutRef.current = null;
                }, 40);
            }
            // Ensure we never accidentally interpret paste as regular input.
            buffer.handleInput(key);
            return;
        }
        if (vimHandleInput && vimHandleInput(key)) {
            return;
        }
        // Reset ESC count and hide prompt on any non-ESC key
        if (key.name !== 'escape') {
            if (escPressCount.current > 0 || showEscapePrompt) {
                resetEscapeState();
            }
        }
        if (key.sequence === '!' &&
            buffer.text === '' &&
            !completion.showSuggestions) {
            setShellModeActive(!shellModeActive);
            buffer.setText(''); // Clear the '!' from input
            return;
        }
        if (keyMatchers[Command.ESCAPE](key)) {
            const cancelSearch = (setActive, resetCompletion) => {
                setActive(false);
                resetCompletion();
                buffer.setText(textBeforeReverseSearch);
                const offset = logicalPosToOffset(buffer.lines, cursorPosition[0], cursorPosition[1]);
                buffer.moveToOffset(offset);
                setExpandedSuggestionIndex(-1);
            };
            if (reverseSearchActive) {
                cancelSearch(setReverseSearchActive, reverseSearchCompletion.resetCompletionState);
                return;
            }
            if (commandSearchActive) {
                cancelSearch(setCommandSearchActive, commandSearchCompletion.resetCompletionState);
                return;
            }
            if (shellModeActive) {
                setShellModeActive(false);
                resetEscapeState();
                return;
            }
            if (completion.showSuggestions) {
                completion.resetCompletionState();
                setExpandedSuggestionIndex(-1);
                resetEscapeState();
                return;
            }
            // Handle double ESC for clearing input
            if (escPressCount.current === 0) {
                if (buffer.text === '') {
                    return;
                }
                escPressCount.current = 1;
                setShowEscapePrompt(true);
                if (escapeTimerRef.current) {
                    clearTimeout(escapeTimerRef.current);
                }
                escapeTimerRef.current = setTimeout(() => {
                    resetEscapeState();
                }, 500);
            }
            else {
                // clear input and immediately reset state
                buffer.setText('');
                resetCompletionState();
                resetEscapeState();
            }
            return;
        }
        if (shellModeActive && keyMatchers[Command.REVERSE_SEARCH](key)) {
            setReverseSearchActive(true);
            setTextBeforeReverseSearch(buffer.text);
            setCursorPosition(buffer.cursor);
            return;
        }
        if (keyMatchers[Command.CLEAR_SCREEN](key)) {
            setBannerVisible(false);
            onClearScreen();
            return;
        }
        if (reverseSearchActive || commandSearchActive) {
            const isCommandSearch = commandSearchActive;
            const sc = isCommandSearch
                ? commandSearchCompletion
                : reverseSearchCompletion;
            const { activeSuggestionIndex, navigateUp, navigateDown, showSuggestions, suggestions, } = sc;
            const setActive = isCommandSearch
                ? setCommandSearchActive
                : setReverseSearchActive;
            const resetState = sc.resetCompletionState;
            if (showSuggestions) {
                if (keyMatchers[Command.NAVIGATION_UP](key)) {
                    navigateUp();
                    return;
                }
                if (keyMatchers[Command.NAVIGATION_DOWN](key)) {
                    navigateDown();
                    return;
                }
                if (keyMatchers[Command.COLLAPSE_SUGGESTION](key)) {
                    if (suggestions[activeSuggestionIndex].value.length >= MAX_WIDTH) {
                        setExpandedSuggestionIndex(-1);
                        return;
                    }
                }
                if (keyMatchers[Command.EXPAND_SUGGESTION](key)) {
                    if (suggestions[activeSuggestionIndex].value.length >= MAX_WIDTH) {
                        setExpandedSuggestionIndex(activeSuggestionIndex);
                        return;
                    }
                }
                if (keyMatchers[Command.ACCEPT_SUGGESTION_REVERSE_SEARCH](key)) {
                    sc.handleAutocomplete(activeSuggestionIndex);
                    resetState();
                    setActive(false);
                    return;
                }
            }
            if (keyMatchers[Command.SUBMIT_REVERSE_SEARCH](key)) {
                const textToSubmit = showSuggestions && activeSuggestionIndex > -1
                    ? suggestions[activeSuggestionIndex].value
                    : buffer.text;
                handleSubmitAndClear(textToSubmit);
                resetState();
                setActive(false);
                return;
            }
            // Prevent up/down from falling through to regular history navigation
            if (keyMatchers[Command.NAVIGATION_UP](key) ||
                keyMatchers[Command.NAVIGATION_DOWN](key)) {
                return;
            }
        }
        // If the command is a perfect match, pressing enter should execute it.
        // We prioritize execution unless the user is explicitly selecting a different suggestion.
        if (completion.isPerfectMatch &&
            keyMatchers[Command.RETURN](key) &&
            (!completion.showSuggestions || completion.activeSuggestionIndex <= 0)) {
            handleSubmit(buffer.text);
            return;
        }
        if (completion.showSuggestions) {
            if (completion.suggestions.length > 1) {
                if (keyMatchers[Command.COMPLETION_UP](key)) {
                    completion.navigateUp();
                    setExpandedSuggestionIndex(-1); // Reset expansion when navigating
                    return;
                }
                if (keyMatchers[Command.COMPLETION_DOWN](key)) {
                    completion.navigateDown();
                    setExpandedSuggestionIndex(-1); // Reset expansion when navigating
                    return;
                }
            }
            if (keyMatchers[Command.ACCEPT_SUGGESTION](key)) {
                if (completion.suggestions.length > 0) {
                    const targetIndex = completion.activeSuggestionIndex === -1
                        ? 0 // Default to the first if none is active
                        : completion.activeSuggestionIndex;
                    if (targetIndex < completion.suggestions.length) {
                        const suggestion = completion.suggestions[targetIndex];
                        const isEnterKey = key.name === 'return' && !key.ctrl;
                        if (isEnterKey && buffer.text.startsWith('/')) {
                            const { isArgumentCompletion, leafCommand } = completion.slashCompletionRange;
                            if (isArgumentCompletion &&
                                isAutoExecutableCommand(leafCommand)) {
                                // isArgumentCompletion guarantees leafCommand exists
                                const completedText = completion.getCompletedText(suggestion);
                                if (completedText) {
                                    setExpandedSuggestionIndex(-1);
                                    handleSubmit(completedText.trim());
                                    return;
                                }
                            }
                            else if (!isArgumentCompletion) {
                                // Existing logic for command name completion
                                const command = completion.getCommandFromSuggestion(suggestion);
                                // Only auto-execute if the command has no completion function
                                // (i.e., it doesn't require an argument to be selected)
                                if (command &&
                                    isAutoExecutableCommand(command) &&
                                    !command.completion) {
                                    const completedText = completion.getCompletedText(suggestion);
                                    if (completedText) {
                                        setExpandedSuggestionIndex(-1);
                                        handleSubmit(completedText.trim());
                                        return;
                                    }
                                }
                            }
                        }
                        // Default behavior: auto-complete to prompt box
                        completion.handleAutocomplete(targetIndex);
                        setExpandedSuggestionIndex(-1); // Reset expansion after selection
                    }
                }
                return;
            }
        }
        // Handle Tab key for ghost text acceptance
        if (key.name === 'tab' &&
            !completion.showSuggestions &&
            completion.promptCompletion.text) {
            completion.promptCompletion.accept();
            return;
        }
        if (!shellModeActive) {
            if (keyMatchers[Command.REVERSE_SEARCH](key)) {
                setCommandSearchActive(true);
                setTextBeforeReverseSearch(buffer.text);
                setCursorPosition(buffer.cursor);
                return;
            }
            if (keyMatchers[Command.HISTORY_UP](key)) {
                // Check for queued messages first when input is empty
                // If no queued messages, inputHistory.navigateUp() is called inside tryLoadQueuedMessages
                if (tryLoadQueuedMessages()) {
                    return;
                }
                // Only navigate history if popAllMessages doesn't exist
                inputHistory.navigateUp();
                return;
            }
            if (keyMatchers[Command.HISTORY_DOWN](key)) {
                inputHistory.navigateDown();
                return;
            }
            // Handle arrow-up/down for history on single-line or at edges
            if (keyMatchers[Command.NAVIGATION_UP](key) &&
                (buffer.allVisualLines.length === 1 ||
                    (buffer.visualCursor[0] === 0 && buffer.visualScrollRow === 0))) {
                // Check for queued messages first when input is empty
                // If no queued messages, inputHistory.navigateUp() is called inside tryLoadQueuedMessages
                if (tryLoadQueuedMessages()) {
                    return;
                }
                // Only navigate history if popAllMessages doesn't exist
                inputHistory.navigateUp();
                return;
            }
            if (keyMatchers[Command.NAVIGATION_DOWN](key) &&
                (buffer.allVisualLines.length === 1 ||
                    buffer.visualCursor[0] === buffer.allVisualLines.length - 1)) {
                inputHistory.navigateDown();
                return;
            }
        }
        else {
            // Shell History Navigation
            if (keyMatchers[Command.NAVIGATION_UP](key)) {
                const prevCommand = shellHistory.getPreviousCommand();
                if (prevCommand !== null)
                    buffer.setText(prevCommand);
                return;
            }
            if (keyMatchers[Command.NAVIGATION_DOWN](key)) {
                const nextCommand = shellHistory.getNextCommand();
                if (nextCommand !== null)
                    buffer.setText(nextCommand);
                return;
            }
        }
        if (keyMatchers[Command.SUBMIT](key)) {
            if (buffer.text.trim()) {
                // Check if a paste operation occurred recently to prevent accidental auto-submission
                if (recentUnsafePasteTime !== null) {
                    // Paste occurred recently in a terminal where we don't trust pastes
                    // to be reported correctly so assume this paste was really a
                    // newline that was part of the paste.
                    // This has the added benefit that in the worst case at least users
                    // get some feedback that their keypress was handled rather than
                    // wondering why it was completely ignored.
                    buffer.newline();
                    return;
                }
                const [row, col] = buffer.cursor;
                const line = buffer.lines[row];
                const charBefore = col > 0 ? cpSlice(line, col - 1, col) : '';
                if (charBefore === '\\') {
                    buffer.backspace();
                    buffer.newline();
                }
                else {
                    handleSubmit(buffer.text);
                }
            }
            return;
        }
        // Newline insertion
        if (keyMatchers[Command.NEWLINE](key)) {
            buffer.newline();
            return;
        }
        // Ctrl+A (Home) / Ctrl+E (End)
        if (keyMatchers[Command.HOME](key)) {
            buffer.move('home');
            return;
        }
        if (keyMatchers[Command.END](key)) {
            buffer.move('end');
            return;
        }
        // Ctrl+C (Clear input)
        if (keyMatchers[Command.CLEAR_INPUT](key)) {
            if (buffer.text.length > 0) {
                buffer.setText('');
                resetCompletionState();
            }
            return;
        }
        // Kill line commands
        if (keyMatchers[Command.KILL_LINE_RIGHT](key)) {
            buffer.killLineRight();
            return;
        }
        if (keyMatchers[Command.KILL_LINE_LEFT](key)) {
            buffer.killLineLeft();
            return;
        }
        if (keyMatchers[Command.DELETE_WORD_BACKWARD](key)) {
            buffer.deleteWordLeft();
            return;
        }
        // External editor
        if (keyMatchers[Command.OPEN_EXTERNAL_EDITOR](key)) {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            buffer.openInExternalEditor();
            return;
        }
        // Ctrl+V for clipboard paste
        if (keyMatchers[Command.PASTE_CLIPBOARD](key)) {
            // eslint-disable-next-line @typescript-eslint/no-floating-promises
            handleClipboardPaste();
            return;
        }
        if (keyMatchers[Command.FOCUS_SHELL_INPUT](key)) {
            // If we got here, Autocomplete didn't handle the key (e.g. no suggestions).
            if (activePtyId) {
                setEmbeddedShellFocused(true);
            }
            return;
        }
        // Fall back to the text buffer's default input handling for all other keys
        buffer.handleInput(key);
        // Clear ghost text when user types regular characters (not navigation/control keys)
        if (completion.promptCompletion.text &&
            key.sequence &&
            key.sequence.length === 1 &&
            !key.ctrl &&
            !key.meta) {
            completion.promptCompletion.clear();
            setExpandedSuggestionIndex(-1);
        }
    }, [
        focus,
        buffer,
        completion,
        shellModeActive,
        setShellModeActive,
        onClearScreen,
        inputHistory,
        handleSubmitAndClear,
        handleSubmit,
        shellHistory,
        reverseSearchCompletion,
        handleClipboardPaste,
        resetCompletionState,
        showEscapePrompt,
        resetEscapeState,
        vimHandleInput,
        reverseSearchActive,
        textBeforeReverseSearch,
        cursorPosition,
        recentUnsafePasteTime,
        commandSearchActive,
        commandSearchCompletion,
        kittyProtocol.enabled,
        tryLoadQueuedMessages,
        setBannerVisible,
        activePtyId,
        setEmbeddedShellFocused,
    ]);
    useKeypress(handleInput, { isActive: !isEmbeddedShellFocused });
    const linesToRender = buffer.viewportVisualLines;
    const [cursorVisualRowAbsolute, cursorVisualColAbsolute] = buffer.visualCursor;
    const scrollVisualRow = buffer.visualScrollRow;
    const getGhostTextLines = useCallback(() => {
        if (!completion.promptCompletion.text ||
            !buffer.text ||
            !completion.promptCompletion.text.startsWith(buffer.text)) {
            return { inlineGhost: '', additionalLines: [] };
        }
        const ghostSuffix = completion.promptCompletion.text.slice(buffer.text.length);
        if (!ghostSuffix) {
            return { inlineGhost: '', additionalLines: [] };
        }
        const currentLogicalLine = buffer.lines[buffer.cursor[0]] || '';
        const cursorCol = buffer.cursor[1];
        const textBeforeCursor = cpSlice(currentLogicalLine, 0, cursorCol);
        const usedWidth = stringWidth(textBeforeCursor);
        const remainingWidth = Math.max(0, inputWidth - usedWidth);
        const ghostTextLinesRaw = ghostSuffix.split('\n');
        const firstLineRaw = ghostTextLinesRaw.shift() || '';
        let inlineGhost = '';
        let remainingFirstLine = '';
        if (stringWidth(firstLineRaw) <= remainingWidth) {
            inlineGhost = firstLineRaw;
        }
        else {
            const words = firstLineRaw.split(' ');
            let currentLine = '';
            let wordIdx = 0;
            for (const word of words) {
                const prospectiveLine = currentLine ? `${currentLine} ${word}` : word;
                if (stringWidth(prospectiveLine) > remainingWidth) {
                    break;
                }
                currentLine = prospectiveLine;
                wordIdx++;
            }
            inlineGhost = currentLine;
            if (words.length > wordIdx) {
                remainingFirstLine = words.slice(wordIdx).join(' ');
            }
        }
        const linesToWrap = [];
        if (remainingFirstLine) {
            linesToWrap.push(remainingFirstLine);
        }
        linesToWrap.push(...ghostTextLinesRaw);
        const remainingGhostText = linesToWrap.join('\n');
        const additionalLines = [];
        if (remainingGhostText) {
            const textLines = remainingGhostText.split('\n');
            for (const textLine of textLines) {
                const words = textLine.split(' ');
                let currentLine = '';
                for (const word of words) {
                    const prospectiveLine = currentLine ? `${currentLine} ${word}` : word;
                    const prospectiveWidth = stringWidth(prospectiveLine);
                    if (prospectiveWidth > inputWidth) {
                        if (currentLine) {
                            additionalLines.push(currentLine);
                        }
                        let wordToProcess = word;
                        while (stringWidth(wordToProcess) > inputWidth) {
                            let part = '';
                            const wordCP = toCodePoints(wordToProcess);
                            let partWidth = 0;
                            let splitIndex = 0;
                            for (let i = 0; i < wordCP.length; i++) {
                                const char = wordCP[i];
                                const charWidth = stringWidth(char);
                                if (partWidth + charWidth > inputWidth) {
                                    break;
                                }
                                part += char;
                                partWidth += charWidth;
                                splitIndex = i + 1;
                            }
                            additionalLines.push(part);
                            wordToProcess = cpSlice(wordToProcess, splitIndex);
                        }
                        currentLine = wordToProcess;
                    }
                    else {
                        currentLine = prospectiveLine;
                    }
                }
                if (currentLine) {
                    additionalLines.push(currentLine);
                }
            }
        }
        return { inlineGhost, additionalLines };
    }, [
        completion.promptCompletion.text,
        buffer.text,
        buffer.lines,
        buffer.cursor,
        inputWidth,
    ]);
    const { inlineGhost, additionalLines } = getGhostTextLines();
    const getActiveCompletion = () => {
        if (commandSearchActive)
            return commandSearchCompletion;
        if (reverseSearchActive)
            return reverseSearchCompletion;
        return completion;
    };
    const activeCompletion = getActiveCompletion();
    const shouldShowSuggestions = activeCompletion.showSuggestions;
    useEffect(() => {
        if (onSuggestionsVisibilityChange) {
            onSuggestionsVisibilityChange(shouldShowSuggestions);
        }
    }, [shouldShowSuggestions, onSuggestionsVisibilityChange]);
    const showAutoAcceptStyling = !shellModeActive && approvalMode === ApprovalMode.AUTO_EDIT;
    const showYoloStyling = !shellModeActive && approvalMode === ApprovalMode.YOLO;
    let statusColor;
    let statusText = '';
    if (shellModeActive) {
        statusColor = theme.ui.symbol;
        statusText = 'Shell mode';
    }
    else if (showYoloStyling) {
        statusColor = theme.status.error;
        statusText = 'YOLO mode';
    }
    else if (showAutoAcceptStyling) {
        statusColor = theme.status.warning;
        statusText = 'Accepting edits';
    }
    const suggestionsNode = shouldShowSuggestions ? (_jsx(Box, { paddingRight: 2, children: _jsx(SuggestionsDisplay, { suggestions: activeCompletion.suggestions, activeIndex: activeCompletion.activeSuggestionIndex, isLoading: activeCompletion.isLoadingSuggestions, width: suggestionsWidth, scrollOffset: activeCompletion.visibleStartIndex, userInput: buffer.text, mode: completion.completionMode === CompletionMode.AT
                ? 'reverse'
                : buffer.text.startsWith('/') &&
                    !reverseSearchActive &&
                    !commandSearchActive
                    ? 'slash'
                    : 'reverse', expandedIndex: expandedSuggestionIndex }) })) : null;
    return (_jsxs(_Fragment, { children: [suggestionsPosition === 'above' && suggestionsNode, _jsxs(Box, { borderStyle: "round", borderColor: isShellFocused && !isEmbeddedShellFocused
                    ? (statusColor ?? theme.border.focused)
                    : theme.border.default, paddingX: 1, width: mainAreaWidth, flexDirection: "row", alignItems: "flex-start", minHeight: 3, children: [_jsxs(Text, { color: statusColor ?? theme.text.accent, "aria-label": statusText || undefined, children: [shellModeActive ? (reverseSearchActive ? (_jsxs(Text, { color: theme.text.link, "aria-label": SCREEN_READER_USER_PREFIX, children: ["(r:)", ' '] })) : ('!')) : commandSearchActive ? (_jsx(Text, { color: theme.text.accent, children: "(r:) " })) : showYoloStyling ? ('*') : ('>'), ' '] }), _jsx(Box, { flexGrow: 1, flexDirection: "column", ref: innerBoxRef, children: buffer.text.length === 0 && placeholder ? (showCursor ? (_jsxs(Text, { children: [chalk.inverse(placeholder.slice(0, 1)), _jsx(Text, { color: theme.text.secondary, children: placeholder.slice(1) })] })) : (_jsx(Text, { color: theme.text.secondary, children: placeholder }))) : (linesToRender
                            .map((lineText, visualIdxInRenderedSet) => {
                            const absoluteVisualIdx = scrollVisualRow + visualIdxInRenderedSet;
                            const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx];
                            const cursorVisualRow = cursorVisualRowAbsolute - scrollVisualRow;
                            const isOnCursorLine = focus && visualIdxInRenderedSet === cursorVisualRow;
                            const renderedLine = [];
                            const [logicalLineIdx] = mapEntry;
                            const logicalLine = buffer.lines[logicalLineIdx] || '';
                            const transformations = buffer.transformationsByLine[logicalLineIdx] ?? [];
                            const tokens = parseInputForHighlighting(logicalLine, logicalLineIdx, transformations, ...(focus && buffer.cursor[0] === logicalLineIdx
                                ? [buffer.cursor[1]]
                                : []));
                            const startColInTransformed = buffer.visualToTransformedMap[absoluteVisualIdx] ?? 0;
                            const visualStartCol = startColInTransformed;
                            const visualEndCol = visualStartCol + cpLen(lineText);
                            const segments = parseSegmentsFromTokens(tokens, visualStartCol, visualEndCol);
                            let charCount = 0;
                            segments.forEach((seg, segIdx) => {
                                const segLen = cpLen(seg.text);
                                let display = seg.text;
                                if (isOnCursorLine) {
                                    const relativeVisualColForHighlight = cursorVisualColAbsolute;
                                    const segStart = charCount;
                                    const segEnd = segStart + segLen;
                                    if (relativeVisualColForHighlight >= segStart &&
                                        relativeVisualColForHighlight < segEnd) {
                                        const charToHighlight = cpSlice(display, relativeVisualColForHighlight - segStart, relativeVisualColForHighlight - segStart + 1);
                                        const highlighted = showCursor
                                            ? chalk.inverse(charToHighlight)
                                            : charToHighlight;
                                        display =
                                            cpSlice(display, 0, relativeVisualColForHighlight - segStart) +
                                                highlighted +
                                                cpSlice(display, relativeVisualColForHighlight - segStart + 1);
                                    }
                                    charCount = segEnd;
                                }
                                else {
                                    // Advance the running counter even when not on cursor line
                                    charCount += segLen;
                                }
                                const color = seg.type === 'command' || seg.type === 'file'
                                    ? theme.text.accent
                                    : theme.text.primary;
                                renderedLine.push(_jsx(Text, { color: color, children: display }, `token-${segIdx}`));
                            });
                            const currentLineGhost = isOnCursorLine ? inlineGhost : '';
                            if (isOnCursorLine &&
                                cursorVisualColAbsolute === cpLen(lineText)) {
                                if (!currentLineGhost) {
                                    renderedLine.push(_jsx(Text, { children: showCursor ? chalk.inverse(' ') : ' ' }, `cursor-end-${cursorVisualColAbsolute}`));
                                }
                            }
                            const showCursorBeforeGhost = focus &&
                                isOnCursorLine &&
                                cursorVisualColAbsolute === cpLen(lineText) &&
                                currentLineGhost;
                            return (_jsx(Box, { height: 1, children: _jsxs(Text, { children: [renderedLine, showCursorBeforeGhost &&
                                            (showCursor ? chalk.inverse(' ') : ' '), currentLineGhost && (_jsx(Text, { color: theme.text.secondary, children: currentLineGhost }))] }) }, `line-${visualIdxInRenderedSet}`));
                        })
                            .concat(additionalLines.map((ghostLine, index) => {
                            const padding = Math.max(0, inputWidth - stringWidth(ghostLine));
                            return (_jsxs(Text, { color: theme.text.secondary, children: [ghostLine, ' '.repeat(padding)] }, `ghost-line-${index}`));
                        }))) })] }), suggestionsPosition === 'below' && suggestionsNode] }));
};
//# sourceMappingURL=InputPrompt.js.map