UNPKG

pyb-ts

Version:

PYB-CLI - Minimal AI Agent with multi-model support and CLI interface

251 lines (250 loc) 6.78 kB
import { useState } from "react"; import { useDoublePress } from "./useDoublePress.js"; import { Cursor } from "@utils/Cursor"; import { getImageFromClipboard, CLIPBOARD_ERROR_MESSAGE } from "@utils/imagePaste"; const IMAGE_PLACEHOLDER = "[Image pasted]"; function mapInput(input_map) { return function(input) { const handler = new Map(input_map).get(input) ?? (() => { }); return handler(input); }; } function useTextInput({ value: originalValue, onChange, onSubmit, onExit, onExitMessage, onMessage, onHistoryUp, onHistoryDown, onHistoryReset, mask = "", multiline = false, cursorChar, invert, columns, onImagePaste, disableCursorMovementForUpDownKeys = false, externalOffset, onOffsetChange }) { const offset = externalOffset; const setOffset = onOffsetChange; const cursor = Cursor.fromText(originalValue, columns, offset); const [imagePasteErrorTimeout, setImagePasteErrorTimeout] = useState(null); function maybeClearImagePasteErrorTimeout() { if (!imagePasteErrorTimeout) { return; } clearTimeout(imagePasteErrorTimeout); setImagePasteErrorTimeout(null); onMessage?.(false); } const handleCtrlC = useDoublePress( (show) => { maybeClearImagePasteErrorTimeout(); onExitMessage?.(show, "Ctrl-C"); }, () => onExit?.(), () => { if (originalValue) { onChange(""); onHistoryReset?.(); } } ); const handleEscape = useDoublePress( (show) => { maybeClearImagePasteErrorTimeout(); onMessage?.(!!originalValue && show, `Press Escape again to clear`); }, () => { if (originalValue) { onChange(""); } } ); function clear() { return Cursor.fromText("", columns, 0); } const handleEmptyCtrlD = useDoublePress( (show) => onExitMessage?.(show, "Ctrl-D"), () => onExit?.() ); function handleCtrlD() { maybeClearImagePasteErrorTimeout(); if (cursor.text === "") { handleEmptyCtrlD(); return cursor; } return cursor.del(); } function tryImagePaste() { const base64Image = getImageFromClipboard(); if (base64Image === null) { if (process.platform !== "darwin") { return cursor; } onMessage?.(true, CLIPBOARD_ERROR_MESSAGE); maybeClearImagePasteErrorTimeout(); setImagePasteErrorTimeout( setTimeout(() => { onMessage?.(false); }, 4e3) ); return cursor; } onImagePaste?.(base64Image); return cursor.insert(IMAGE_PLACEHOLDER); } const handleCtrl = mapInput([ ["a", () => cursor.startOfLine()], ["b", () => cursor.left()], ["c", handleCtrlC], ["d", handleCtrlD], ["e", () => cursor.endOfLine()], ["f", () => cursor.right()], [ "h", () => { maybeClearImagePasteErrorTimeout(); return cursor.backspace(); } ], ["k", () => cursor.deleteToLineEnd()], ["l", () => clear()], ["n", () => downOrHistoryDown()], ["p", () => upOrHistoryUp()], ["u", () => cursor.deleteToLineStart()], ["v", tryImagePaste], ["w", () => cursor.deleteWordBefore()] ]); const handleMeta = mapInput([ ["b", () => cursor.prevWord()], ["f", () => cursor.nextWord()], ["d", () => cursor.deleteWordAfter()] ]); function handleEnter(key) { if (multiline && cursor.offset > 0 && cursor.text[cursor.offset - 1] === "\\") { return cursor.backspace().insert("\n"); } if (key.meta) { return cursor.insert("\n"); } onSubmit?.(originalValue); } function upOrHistoryUp() { if (disableCursorMovementForUpDownKeys) { onHistoryUp?.(); return cursor; } const cursorUp = cursor.up(); if (cursorUp.equals(cursor)) { onHistoryUp?.(); } return cursorUp; } function downOrHistoryDown() { if (disableCursorMovementForUpDownKeys) { onHistoryDown?.(); return cursor; } const cursorDown = cursor.down(); if (cursorDown.equals(cursor)) { onHistoryDown?.(); } return cursorDown; } function onInput(input, key) { if (key.tab) { return; } if (key.backspace || key.delete || input === "\b" || input === "\x7F" || input === "\b") { const nextCursor2 = cursor.backspace(); if (!cursor.equals(nextCursor2)) { setOffset(nextCursor2.offset); if (cursor.text !== nextCursor2.text) { onChange(nextCursor2.text); } } return; } const nextCursor = mapKey(key)(input); if (nextCursor) { if (!cursor.equals(nextCursor)) { setOffset(nextCursor.offset); if (cursor.text !== nextCursor.text) { onChange(nextCursor.text); } } } } function mapKey(key) { if (key.backspace || key.delete) { maybeClearImagePasteErrorTimeout(); return () => cursor.backspace(); } switch (true) { case key.escape: return handleEscape; case (key.leftArrow && (key.ctrl || key.meta || "fn" in key && key.fn)): return () => cursor.prevWord(); case (key.rightArrow && (key.ctrl || key.meta || "fn" in key && key.fn)): return () => cursor.nextWord(); case key.ctrl: return handleCtrl; case ("home" in key && key.home): return () => cursor.startOfLine(); case ("end" in key && key.end): return () => cursor.endOfLine(); case key.pageDown: return () => cursor.endOfLine(); case key.pageUp: return () => cursor.startOfLine(); case key.meta: return handleMeta; case key.return: return () => handleEnter(key); // Remove Tab handling - let completion system handle it case key.upArrow: return upOrHistoryUp; case key.downArrow: return downOrHistoryDown; case key.leftArrow: return () => cursor.left(); case key.rightArrow: return () => cursor.right(); } return function(input) { switch (true) { // Home key case (input == "\x1B[H" || input == "\x1B[1~"): return cursor.startOfLine(); // End key case (input == "\x1B[F" || input == "\x1B[4~"): return cursor.endOfLine(); // Handle backspace character explicitly - this is the key fix case (input === "\b" || input === "\x7F" || input === "\b"): maybeClearImagePasteErrorTimeout(); return cursor.backspace(); default: return cursor.insert(input.replace(/\r/g, "\n")); } }; } return { onInput, renderedValue: cursor.render(cursorChar, mask, invert), offset, setOffset }; } export { useTextInput }; //# sourceMappingURL=useTextInput.js.map