feat: letta code
This commit is contained in:
115
vendor/ink-text-input/build/index.js
vendored
Normal file
115
vendor/ink-text-input/build/index.js
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
import chalk from 'chalk';
|
||||
import { Text, useInput } from 'ink';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
function TextInput({ value: originalValue, placeholder = '', focus = true, mask, highlightPastedText = false, showCursor = true, onChange, onSubmit, externalCursorOffset, onCursorOffsetChange }) {
|
||||
const [state, setState] = useState({ cursorOffset: (originalValue || '').length, cursorWidth: 0 });
|
||||
const { cursorOffset, cursorWidth } = state;
|
||||
useEffect(() => {
|
||||
setState(previousState => {
|
||||
if (!focus || !showCursor) {
|
||||
return previousState;
|
||||
}
|
||||
const newValue = originalValue || '';
|
||||
if (previousState.cursorOffset > newValue.length - 1) {
|
||||
return { cursorOffset: newValue.length, cursorWidth: 0 };
|
||||
}
|
||||
return previousState;
|
||||
});
|
||||
}, [originalValue, focus, showCursor]);
|
||||
useEffect(() => {
|
||||
if (typeof externalCursorOffset === 'number') {
|
||||
const newValue = originalValue || '';
|
||||
const clamped = Math.max(0, Math.min(externalCursorOffset, newValue.length));
|
||||
setState(prev => ({ cursorOffset: clamped, cursorWidth: 0 }));
|
||||
if (typeof onCursorOffsetChange === 'function') onCursorOffsetChange(clamped);
|
||||
}
|
||||
}, [externalCursorOffset, originalValue, onCursorOffsetChange]);
|
||||
const cursorActualWidth = highlightPastedText ? cursorWidth : 0;
|
||||
const value = mask ? mask.repeat(originalValue.length) : originalValue;
|
||||
let renderedValue = value;
|
||||
let renderedPlaceholder = placeholder ? chalk.grey(placeholder) : undefined;
|
||||
if (showCursor && focus) {
|
||||
renderedPlaceholder = placeholder.length > 0 ? chalk.inverse(placeholder[0]) + chalk.grey(placeholder.slice(1)) : chalk.inverse(' ');
|
||||
renderedValue = value.length > 0 ? '' : chalk.inverse(' ');
|
||||
let i = 0;
|
||||
for (const char of value) {
|
||||
renderedValue += i >= cursorOffset - cursorActualWidth && i <= cursorOffset ? chalk.inverse(char) : char;
|
||||
i++;
|
||||
}
|
||||
if (value.length > 0 && cursorOffset === value.length) {
|
||||
renderedValue += chalk.inverse(' ');
|
||||
}
|
||||
}
|
||||
useInput((input, key) => {
|
||||
if (key && key.isPasted) {
|
||||
return;
|
||||
}
|
||||
// Treat Escape as a control key (don't insert into value)
|
||||
if (key.escape || key.upArrow || key.downArrow || (key.ctrl && input === 'c') || key.tab || (key.shift && key.tab)) {
|
||||
return;
|
||||
}
|
||||
if (key.return) {
|
||||
if (onSubmit) {
|
||||
onSubmit(originalValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let nextCursorOffset = cursorOffset;
|
||||
let nextValue = originalValue;
|
||||
let nextCursorWidth = 0;
|
||||
if (key.leftArrow) {
|
||||
if (showCursor) {
|
||||
nextCursorOffset--;
|
||||
}
|
||||
}
|
||||
else if (key.rightArrow) {
|
||||
if (showCursor) {
|
||||
nextCursorOffset++;
|
||||
}
|
||||
}
|
||||
else if (key.backspace || key.delete) {
|
||||
if (cursorOffset > 0) {
|
||||
nextValue = originalValue.slice(0, cursorOffset - 1) + originalValue.slice(cursorOffset, originalValue.length);
|
||||
nextCursorOffset--;
|
||||
}
|
||||
}
|
||||
else if (key.ctrl && input === 'a') {
|
||||
// CTRL-A: jump to beginning of line
|
||||
if (showCursor) {
|
||||
nextCursorOffset = 0;
|
||||
}
|
||||
}
|
||||
else if (key.ctrl && input === 'e') {
|
||||
// CTRL-E: jump to end of line
|
||||
if (showCursor) {
|
||||
nextCursorOffset = originalValue.length;
|
||||
}
|
||||
}
|
||||
else {
|
||||
nextValue = originalValue.slice(0, cursorOffset) + input + originalValue.slice(cursorOffset, originalValue.length);
|
||||
nextCursorOffset += input.length;
|
||||
if (input.length > 1) {
|
||||
nextCursorWidth = input.length;
|
||||
}
|
||||
}
|
||||
if (cursorOffset < 0) {
|
||||
nextCursorOffset = 0;
|
||||
}
|
||||
if (cursorOffset > originalValue.length) {
|
||||
nextCursorOffset = originalValue.length;
|
||||
}
|
||||
setState({ cursorOffset: nextCursorOffset, cursorWidth: nextCursorWidth });
|
||||
if (typeof onCursorOffsetChange === 'function') onCursorOffsetChange(nextCursorOffset);
|
||||
if (nextValue !== originalValue) {
|
||||
onChange(nextValue);
|
||||
}
|
||||
}, { isActive: focus });
|
||||
return (React.createElement(Text, null, placeholder ? (value.length > 0 ? renderedValue : renderedPlaceholder) : renderedValue));
|
||||
}
|
||||
export default TextInput;
|
||||
export function UncontrolledTextInput({ initialValue = '', ...props }) {
|
||||
const [value, setValue] = useState(initialValue);
|
||||
return React.createElement(TextInput, { ...props, value: value, onChange: setValue });
|
||||
}
|
||||
|
||||
348
vendor/ink/build/components/App.js
vendored
Normal file
348
vendor/ink/build/components/App.js
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import process from 'node:process';
|
||||
import cliCursor from 'cli-cursor';
|
||||
import React, { PureComponent } from 'react';
|
||||
import AppContext from './AppContext.js';
|
||||
import ErrorOverview from './ErrorOverview.js';
|
||||
import FocusContext from './FocusContext.js';
|
||||
import StderrContext from './StderrContext.js';
|
||||
import StdinContext from './StdinContext.js';
|
||||
import StdoutContext from './StdoutContext.js';
|
||||
|
||||
const tab = '\t';
|
||||
const shiftTab = '\u001B[Z';
|
||||
const escape = '\u001B';
|
||||
export default class App extends PureComponent {
|
||||
static displayName = 'InternalApp';
|
||||
static getDerivedStateFromError(error) {
|
||||
return { error };
|
||||
}
|
||||
state = {
|
||||
isFocusEnabled: true,
|
||||
activeFocusId: undefined,
|
||||
focusables: [],
|
||||
error: undefined,
|
||||
};
|
||||
rawModeEnabledCount = 0;
|
||||
internal_eventEmitter = new EventEmitter();
|
||||
isRawModeSupported() {
|
||||
return this.props.stdin.isTTY;
|
||||
}
|
||||
render() {
|
||||
return (React.createElement(AppContext.Provider, { value: { exit: this.handleExit } },
|
||||
React.createElement(StdinContext.Provider, { value: { stdin: this.props.stdin, setRawMode: this.handleSetRawMode, isRawModeSupported: this.isRawModeSupported(), internal_exitOnCtrlC: this.props.exitOnCtrlC, internal_eventEmitter: this.internal_eventEmitter } },
|
||||
React.createElement(StdoutContext.Provider, { value: { stdout: this.props.stdout, write: this.props.writeToStdout } },
|
||||
React.createElement(StderrContext.Provider, { value: { stderr: this.props.stderr, write: this.props.writeToStderr } },
|
||||
React.createElement(FocusContext.Provider, { value: { activeId: this.state.activeFocusId, add: this.addFocusable, remove: this.removeFocusable, activate: this.activateFocusable, deactivate: this.deactivateFocusable, enableFocus: this.enableFocus, disableFocus: this.disableFocus, focusNext: this.focusNext, focusPrevious: this.focusPrevious, focus: this.focus } }, this.state.error ? (React.createElement(ErrorOverview, { error: this.state.error })) : (this.props.children)))))));
|
||||
}
|
||||
componentDidMount() {
|
||||
cliCursor.hide(this.props.stdout);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
cliCursor.show(this.props.stdout);
|
||||
if (this.isRawModeSupported()) {
|
||||
this.handleSetRawMode(false);
|
||||
}
|
||||
}
|
||||
componentDidCatch(error) {
|
||||
this.handleExit(error);
|
||||
}
|
||||
handleSetRawMode = (isEnabled) => {
|
||||
const { stdin } = this.props;
|
||||
if (!this.isRawModeSupported()) {
|
||||
if (stdin === process.stdin) {
|
||||
throw new Error('Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported');
|
||||
}
|
||||
else {
|
||||
throw new Error('Raw mode is not supported on the stdin provided to Ink.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported');
|
||||
}
|
||||
}
|
||||
stdin.setEncoding('utf8');
|
||||
if (isEnabled) {
|
||||
if (this.rawModeEnabledCount === 0) {
|
||||
stdin.ref();
|
||||
stdin.setRawMode(true);
|
||||
stdin.addListener('readable', this.handleReadable);
|
||||
// Enable bracketed paste on this TTY
|
||||
this.props.stdout?.write('\x1B[?2004h');
|
||||
}
|
||||
this.rawModeEnabledCount++;
|
||||
return;
|
||||
}
|
||||
if (--this.rawModeEnabledCount === 0) {
|
||||
this.props.stdout?.write('\x1B[?2004l');
|
||||
stdin.setRawMode(false);
|
||||
stdin.removeListener('readable', this.handleReadable);
|
||||
stdin.unref();
|
||||
}
|
||||
};
|
||||
keyParseState = { mode: 'NORMAL', incomplete: '', pasteBuffer: '' };
|
||||
fallbackPaste = { aggregating: false, buffer: '', timer: null, lastAt: 0, chunks: 0, bytes: 0, escalated: false, recentTime: 0, recentLen: 0 };
|
||||
FALLBACK_NORMAL_MS = 16;
|
||||
FALLBACK_PASTE_MS = 150;
|
||||
PLACEHOLDER_LINE_THRESHOLD = 5;
|
||||
PLACEHOLDER_CHAR_THRESHOLD = 500;
|
||||
FALLBACK_START_LEN_THRESHOLD = 200;
|
||||
parseChunk = (state, chunk) => {
|
||||
const START = '\x1B[200~';
|
||||
const END = '\x1B[201~';
|
||||
const events = [];
|
||||
let next = { ...state };
|
||||
let buf = (next.incomplete || '') + (chunk || '');
|
||||
next.incomplete = '';
|
||||
const pushText = (text) => {
|
||||
if (text && text.length > 0) {
|
||||
events.push({ type: 'text', value: text });
|
||||
}
|
||||
};
|
||||
if (next.mode === 'NORMAL') {
|
||||
let offset = 0;
|
||||
while (offset < buf.length) {
|
||||
const startIdx = buf.indexOf(START, offset);
|
||||
if (startIdx === -1) {
|
||||
const remainder = buf.slice(offset);
|
||||
let keep = 0;
|
||||
const max = Math.min(remainder.length, START.length - 1);
|
||||
// Only keep potential START prefixes of length >= 2 (e.g., "\x1B[") to avoid swallowing a bare ESC
|
||||
for (let i = max; i > 1; i--) {
|
||||
if (START.startsWith(remainder.slice(-i))) {
|
||||
keep = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (remainder.length > keep) {
|
||||
pushText(remainder.slice(0, remainder.length - keep));
|
||||
}
|
||||
next.incomplete = remainder.slice(remainder.length - keep);
|
||||
break;
|
||||
}
|
||||
if (startIdx > offset) {
|
||||
pushText(buf.slice(offset, startIdx));
|
||||
}
|
||||
offset = startIdx + START.length;
|
||||
const endIdx = buf.indexOf(END, offset);
|
||||
if (endIdx !== -1) {
|
||||
const content = buf.slice(offset, endIdx);
|
||||
events.push({ type: 'paste', value: content });
|
||||
offset = endIdx + END.length;
|
||||
continue;
|
||||
}
|
||||
next.mode = 'IN_PASTE';
|
||||
next.pasteBuffer = buf.slice(offset);
|
||||
break;
|
||||
}
|
||||
return [events, next];
|
||||
}
|
||||
if (next.mode === 'IN_PASTE') {
|
||||
next.pasteBuffer += buf;
|
||||
const endIdx = next.pasteBuffer.indexOf(END);
|
||||
if (endIdx === -1) {
|
||||
return [events, next];
|
||||
}
|
||||
const content = next.pasteBuffer.slice(0, endIdx);
|
||||
events.push({ type: 'paste', value: content });
|
||||
const after = next.pasteBuffer.slice(endIdx + END.length);
|
||||
next.mode = 'NORMAL';
|
||||
next.pasteBuffer = '';
|
||||
const [moreEvents, finalState] = this.parseChunk(next, after);
|
||||
return [events.concat(moreEvents), finalState];
|
||||
}
|
||||
return [events, next];
|
||||
};
|
||||
countLines = (text) => {
|
||||
if (!text)
|
||||
return 0;
|
||||
const m = text.match(/\r\n|\r|\n/g);
|
||||
return (m ? m.length : 0);
|
||||
};
|
||||
fallbackStart = () => {
|
||||
this.fallbackStop();
|
||||
this.fallbackPaste.aggregating = true;
|
||||
this.fallbackPaste.buffer = '';
|
||||
this.fallbackPaste.chunks = 0;
|
||||
this.fallbackPaste.bytes = 0;
|
||||
this.fallbackPaste.escalated = false;
|
||||
this.fallbackPaste.lastAt = Date.now();
|
||||
this.fallbackPaste.timer = setTimeout(this.fallbackFlush, this.FALLBACK_NORMAL_MS);
|
||||
};
|
||||
fallbackSchedule = (ms) => {
|
||||
if (this.fallbackPaste.timer)
|
||||
clearTimeout(this.fallbackPaste.timer);
|
||||
this.fallbackPaste.timer = setTimeout(this.fallbackFlush, ms);
|
||||
this.fallbackPaste.lastAt = Date.now();
|
||||
};
|
||||
fallbackStop = () => {
|
||||
if (this.fallbackPaste.timer)
|
||||
clearTimeout(this.fallbackPaste.timer);
|
||||
this.fallbackPaste.timer = null;
|
||||
this.fallbackPaste.aggregating = false;
|
||||
};
|
||||
fallbackFlush = () => {
|
||||
const txt = this.fallbackPaste.buffer;
|
||||
this.fallbackStop();
|
||||
if (!txt)
|
||||
return;
|
||||
const lines = this.countLines(txt);
|
||||
const isPaste = this.fallbackPaste.escalated || (lines > this.PLACEHOLDER_LINE_THRESHOLD) || (txt.length > this.PLACEHOLDER_CHAR_THRESHOLD);
|
||||
if (isPaste) {
|
||||
const pasteEvent = { sequence: txt, raw: txt, isPasted: true, name: '', ctrl: false, meta: false, shift: false };
|
||||
this.internal_eventEmitter.emit('input', pasteEvent);
|
||||
}
|
||||
else {
|
||||
this.handleInput(txt);
|
||||
this.internal_eventEmitter.emit('input', txt);
|
||||
}
|
||||
this.fallbackPaste.buffer = '';
|
||||
this.fallbackPaste.chunks = 0;
|
||||
this.fallbackPaste.bytes = 0;
|
||||
this.fallbackPaste.escalated = false;
|
||||
};
|
||||
handleReadable = () => {
|
||||
let chunk;
|
||||
while ((chunk = this.props.stdin.read()) !== null) {
|
||||
const [events, nextState] = this.parseChunk(this.keyParseState, chunk);
|
||||
this.keyParseState = nextState;
|
||||
for (const evt of events) {
|
||||
if (evt.type === 'paste') {
|
||||
if (this.fallbackPaste.aggregating) {
|
||||
this.fallbackFlush();
|
||||
}
|
||||
const content = evt.value;
|
||||
const pasteEvent = { sequence: content, raw: content, isPasted: true, name: '', ctrl: false, meta: false, shift: false };
|
||||
this.internal_eventEmitter.emit('input', pasteEvent);
|
||||
}
|
||||
else if (evt.type === 'text') {
|
||||
const text = evt.value;
|
||||
if (!text)
|
||||
continue;
|
||||
const hasNewline = /\r|\n/.test(text);
|
||||
if (this.fallbackPaste.aggregating) {
|
||||
this.fallbackPaste.buffer += text;
|
||||
this.fallbackPaste.chunks += 1;
|
||||
this.fallbackPaste.bytes += text.length;
|
||||
if (!this.fallbackPaste.escalated) {
|
||||
if (this.fallbackPaste.buffer.length >= 128) {
|
||||
this.fallbackPaste.escalated = true;
|
||||
}
|
||||
}
|
||||
this.fallbackSchedule(this.fallbackPaste.escalated ? this.FALLBACK_PASTE_MS : this.FALLBACK_NORMAL_MS);
|
||||
continue;
|
||||
}
|
||||
const now = Date.now();
|
||||
const quickCombo = (now - this.fallbackPaste.recentTime) <= 16 && (this.fallbackPaste.recentLen + text.length) >= 128;
|
||||
if (text.length >= 128 || quickCombo) {
|
||||
this.fallbackStart();
|
||||
this.fallbackPaste.buffer += text;
|
||||
this.fallbackPaste.chunks = 1;
|
||||
this.fallbackPaste.bytes = text.length;
|
||||
this.fallbackPaste.escalated = text.length >= 128;
|
||||
this.fallbackSchedule(this.FALLBACK_PASTE_MS);
|
||||
continue;
|
||||
}
|
||||
this.handleInput(text);
|
||||
this.internal_eventEmitter.emit('input', text);
|
||||
this.fallbackPaste.recentTime = Date.now();
|
||||
this.fallbackPaste.recentLen = text.length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
handleInput = (input) => {
|
||||
if (input === '\x03' && this.props.exitOnCtrlC) {
|
||||
this.handleExit();
|
||||
}
|
||||
// Disable ESC-based focus clearing to avoid consuming the first Escape
|
||||
// if (input === escape && this.state.activeFocusId) {
|
||||
// this.setState({ activeFocusId: undefined });
|
||||
// }
|
||||
if (this.state.isFocusEnabled && this.state.focusables.length > 0) {
|
||||
if (input === tab) {
|
||||
this.focusNext();
|
||||
}
|
||||
if (input === shiftTab) {
|
||||
this.focusPrevious();
|
||||
}
|
||||
}
|
||||
};
|
||||
handleExit = (error) => {
|
||||
if (this.isRawModeSupported()) {
|
||||
this.handleSetRawMode(false);
|
||||
}
|
||||
this.props.onExit(error);
|
||||
};
|
||||
enableFocus = () => {
|
||||
this.setState({ isFocusEnabled: true });
|
||||
};
|
||||
disableFocus = () => {
|
||||
this.setState({ isFocusEnabled: false });
|
||||
};
|
||||
focus = (id) => {
|
||||
this.setState(previousState => {
|
||||
const hasFocusableId = previousState.focusables.some(focusable => focusable?.id === id);
|
||||
if (!hasFocusableId) {
|
||||
return previousState;
|
||||
}
|
||||
return { activeFocusId: id };
|
||||
});
|
||||
};
|
||||
focusNext = () => {
|
||||
this.setState(previousState => {
|
||||
const firstFocusableId = previousState.focusables.find(focusable => focusable.isActive)?.id;
|
||||
const nextFocusableId = this.findNextFocusable(previousState);
|
||||
return { activeFocusId: nextFocusableId ?? firstFocusableId };
|
||||
});
|
||||
};
|
||||
focusPrevious = () => {
|
||||
this.setState(previousState => {
|
||||
const lastFocusableId = previousState.focusables.findLast(focusable => focusable.isActive)?.id;
|
||||
const previousFocusableId = this.findPreviousFocusable(previousState);
|
||||
return { activeFocusId: previousFocusableId ?? lastFocusableId };
|
||||
});
|
||||
};
|
||||
addFocusable = (id, { autoFocus }) => {
|
||||
this.setState(previousState => {
|
||||
let nextFocusId = previousState.activeFocusId;
|
||||
if (!nextFocusId && autoFocus) {
|
||||
nextFocusId = id;
|
||||
}
|
||||
return { activeFocusId: nextFocusId, focusables: [...previousState.focusables, { id, isActive: true }] };
|
||||
});
|
||||
};
|
||||
removeFocusable = (id) => {
|
||||
this.setState(previousState => ({ activeFocusId: previousState.activeFocusId === id ? undefined : previousState.activeFocusId, focusables: previousState.focusables.filter(focusable => focusable.id !== id) }));
|
||||
};
|
||||
activateFocusable = (id) => {
|
||||
this.setState(previousState => ({ focusables: previousState.focusables.map(focusable => (focusable.id !== id ? focusable : { id, isActive: true })) }));
|
||||
};
|
||||
deactivateFocusable = (id) => {
|
||||
this.setState(previousState => ({ activeFocusId: previousState.activeFocusId === id ? undefined : previousState.activeFocusId, focusables: previousState.focusables.map(focusable => (focusable.id !== id ? focusable : { id, isActive: false })) }));
|
||||
};
|
||||
findNextFocusable = (state) => {
|
||||
const activeIndex = state.focusables.findIndex(focusable => {
|
||||
return focusable.id === state.activeFocusId;
|
||||
});
|
||||
for (let index = activeIndex + 1; index < state.focusables.length; index++) {
|
||||
const focusable = state.focusables[index];
|
||||
if (focusable?.isActive) {
|
||||
return focusable.id;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
findPreviousFocusable = (state) => {
|
||||
const activeIndex = state.focusables.findIndex(focusable => {
|
||||
return focusable.id === state.activeFocusId;
|
||||
});
|
||||
for (let index = activeIndex - 1; index >= 0; index--) {
|
||||
const focusable = state.focusables[index];
|
||||
if (focusable?.isActive) {
|
||||
return focusable.id;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=App.js.map
|
||||
|
||||
|
||||
6
vendor/ink/build/devtools.js
vendored
Normal file
6
vendor/ink/build/devtools.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/* Patched by Letta Code: disable React DevTools hook in production builds. */
|
||||
export default {
|
||||
connectToDevTools() {
|
||||
// no-op
|
||||
},
|
||||
};
|
||||
101
vendor/ink/build/hooks/use-input.js
vendored
Normal file
101
vendor/ink/build/hooks/use-input.js
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
import { useEffect } from 'react';
|
||||
import parseKeypress, { nonAlphanumericKeys } from '../parse-keypress.js';
|
||||
import reconciler from '../reconciler.js';
|
||||
import useStdin from './use-stdin.js';
|
||||
|
||||
// Patched for bracketed paste: propagate "isPasted" and avoid leaking ESC sequences
|
||||
const useInput = (inputHandler, options = {}) => {
|
||||
const { stdin, setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdin();
|
||||
|
||||
useEffect(() => {
|
||||
if (options.isActive === false) {
|
||||
return;
|
||||
}
|
||||
setRawMode(true);
|
||||
return () => {
|
||||
setRawMode(false);
|
||||
};
|
||||
}, [options.isActive, setRawMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (options.isActive === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleData = (data) => {
|
||||
// Handle bracketed paste events emitted by Ink stdin manager
|
||||
if (data && typeof data === 'object' && data.isPasted) {
|
||||
const key = {
|
||||
upArrow: false,
|
||||
downArrow: false,
|
||||
leftArrow: false,
|
||||
rightArrow: false,
|
||||
pageDown: false,
|
||||
pageUp: false,
|
||||
return: false,
|
||||
escape: false,
|
||||
ctrl: false,
|
||||
shift: false,
|
||||
tab: false,
|
||||
backspace: false,
|
||||
delete: false,
|
||||
meta: false,
|
||||
isPasted: true
|
||||
};
|
||||
reconciler.batchedUpdates(() => {
|
||||
inputHandler(data.sequence || data.raw || '', key);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const keypress = parseKeypress(data);
|
||||
const key = {
|
||||
upArrow: keypress.name === 'up',
|
||||
downArrow: keypress.name === 'down',
|
||||
leftArrow: keypress.name === 'left',
|
||||
rightArrow: keypress.name === 'right',
|
||||
pageDown: keypress.name === 'pagedown',
|
||||
pageUp: keypress.name === 'pageup',
|
||||
return: keypress.name === 'return',
|
||||
escape: keypress.name === 'escape',
|
||||
ctrl: keypress.ctrl,
|
||||
shift: keypress.shift,
|
||||
tab: keypress.name === 'tab',
|
||||
backspace: keypress.name === 'backspace',
|
||||
delete: keypress.name === 'delete',
|
||||
meta: keypress.meta || keypress.name === 'escape' || keypress.option,
|
||||
isPasted: false
|
||||
};
|
||||
|
||||
let input = keypress.ctrl ? keypress.name : keypress.sequence;
|
||||
const seq = typeof keypress.sequence === 'string' ? keypress.sequence : '';
|
||||
// Filter xterm focus in/out sequences (ESC[I / ESC[O)
|
||||
if (seq === '\u001B[I' || seq === '\u001B[O' || input === '[I' || input === '[O' || /^(?:\[I|\[O)+$/.test(input || '')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nonAlphanumericKeys.includes(keypress.name)) {
|
||||
input = '';
|
||||
}
|
||||
|
||||
if (input.length === 1 && typeof input[0] === 'string' && /[A-Z]/.test(input[0])) {
|
||||
key.shift = true;
|
||||
}
|
||||
|
||||
if (!(input === 'c' && key.ctrl) || !internal_exitOnCtrlC) {
|
||||
reconciler.batchedUpdates(() => {
|
||||
inputHandler(input, key);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
internal_eventEmitter?.on('input', handleData);
|
||||
return () => {
|
||||
internal_eventEmitter?.removeListener('input', handleData);
|
||||
};
|
||||
}, [options.isActive, stdin, internal_exitOnCtrlC, inputHandler]);
|
||||
};
|
||||
|
||||
export default useInput;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user