cvc-tui 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entry.js +71148 -61
- package/package.json +2 -2
- package/dist/app/completion.js +0 -102
- package/dist/app/createGatewayEventHandler.js +0 -508
- package/dist/app/createSlashHandler.js +0 -101
- package/dist/app/delegationStore.js +0 -51
- package/dist/app/gatewayContext.js +0 -17
- package/dist/app/historyStore.js +0 -123
- package/dist/app/inputBuffer.js +0 -120
- package/dist/app/inputSelectionStore.js +0 -8
- package/dist/app/inputStore.js +0 -28
- package/dist/app/interfaces.js +0 -6
- package/dist/app/overlayStore.js +0 -40
- package/dist/app/promptStore.js +0 -44
- package/dist/app/queueStore.js +0 -25
- package/dist/app/scroll.js +0 -44
- package/dist/app/setupHandoff.js +0 -28
- package/dist/app/slash/commands/core.js +0 -479
- package/dist/app/slash/commands/debug.js +0 -44
- package/dist/app/slash/commands/ops.js +0 -498
- package/dist/app/slash/commands/session.js +0 -431
- package/dist/app/slash/commands/setup.js +0 -20
- package/dist/app/slash/commands/toggles.js +0 -40
- package/dist/app/slash/registry.js +0 -18
- package/dist/app/slash/types.js +0 -1
- package/dist/app/spawnHistoryStore.js +0 -105
- package/dist/app/turnController.js +0 -650
- package/dist/app/turnStore.js +0 -48
- package/dist/app/uiStore.js +0 -36
- package/dist/app/useComposerState.js +0 -265
- package/dist/app/useConfigSync.js +0 -144
- package/dist/app/useInputHandlers.js +0 -403
- package/dist/app/useLongRunToolCharms.js +0 -50
- package/dist/app/useMainApp.js +0 -629
- package/dist/app/useSessionLifecycle.js +0 -175
- package/dist/app/useSubmission.js +0 -287
- package/dist/app.js +0 -15
- package/dist/banner.js +0 -57
- package/dist/components/agentsOverlay.js +0 -474
- package/dist/components/appChrome.js +0 -252
- package/dist/components/appLayout.js +0 -121
- package/dist/components/appOverlays.js +0 -65
- package/dist/components/branding.js +0 -97
- package/dist/components/fpsOverlay.js +0 -22
- package/dist/components/helpHint.js +0 -21
- package/dist/components/markdown.js +0 -501
- package/dist/components/maskedPrompt.js +0 -12
- package/dist/components/messageLine.js +0 -82
- package/dist/components/modelPicker.js +0 -254
- package/dist/components/overlayControls.js +0 -30
- package/dist/components/overlays/confirmPrompt.js +0 -25
- package/dist/components/overlays/helpOverlay.js +0 -76
- package/dist/components/overlays/historySearch.js +0 -49
- package/dist/components/overlays/modelPicker.js +0 -60
- package/dist/components/overlays/overlayUtils.js +0 -19
- package/dist/components/overlays/secretPrompt.js +0 -36
- package/dist/components/overlays/sessionPicker.js +0 -93
- package/dist/components/overlays/skillsHub.js +0 -71
- package/dist/components/prompts.js +0 -95
- package/dist/components/queuedMessages.js +0 -24
- package/dist/components/sessionPicker.js +0 -130
- package/dist/components/skillsHub.js +0 -165
- package/dist/components/streamingAssistant.js +0 -35
- package/dist/components/streamingMarkdown.js +0 -144
- package/dist/components/textInput.js +0 -794
- package/dist/components/themed.js +0 -12
- package/dist/components/thinking.js +0 -496
- package/dist/components/todoPanel.js +0 -40
- package/dist/components/transcript.js +0 -22
- package/dist/config/env.js +0 -18
- package/dist/config/limits.js +0 -22
- package/dist/config/timing.js +0 -18
- package/dist/content/charms.js +0 -5
- package/dist/content/faces.js +0 -21
- package/dist/content/fortunes.js +0 -29
- package/dist/content/hotkeys.js +0 -38
- package/dist/content/placeholders.js +0 -15
- package/dist/content/setup.js +0 -14
- package/dist/content/verbs.js +0 -41
- package/dist/domain/details.js +0 -53
- package/dist/domain/messages.js +0 -63
- package/dist/domain/paths.js +0 -16
- package/dist/domain/providers.js +0 -11
- package/dist/domain/roles.js +0 -6
- package/dist/domain/slash.js +0 -11
- package/dist/domain/usage.js +0 -1
- package/dist/domain/viewport.js +0 -33
- package/dist/gateway/client.js +0 -312
- package/dist/gatewayClient.js +0 -574
- package/dist/gatewayTypes.js +0 -1
- package/dist/hooks/useCompletion.js +0 -86
- package/dist/hooks/useGitBranch.js +0 -58
- package/dist/hooks/useInputHistory.js +0 -12
- package/dist/hooks/useQueue.js +0 -57
- package/dist/hooks/useVirtualHistory.js +0 -401
- package/dist/lib/circularBuffer.js +0 -43
- package/dist/lib/clipboard.js +0 -126
- package/dist/lib/editor.js +0 -41
- package/dist/lib/editor.test.js +0 -58
- package/dist/lib/emoji.js +0 -49
- package/dist/lib/externalCli.js +0 -11
- package/dist/lib/forceTruecolor.js +0 -26
- package/dist/lib/fpsStore.js +0 -36
- package/dist/lib/gracefulExit.js +0 -29
- package/dist/lib/history.js +0 -69
- package/dist/lib/inputMetrics.js +0 -143
- package/dist/lib/liveProgress.js +0 -51
- package/dist/lib/liveProgress.test.js +0 -89
- package/dist/lib/mathUnicode.js +0 -685
- package/dist/lib/memory.js +0 -123
- package/dist/lib/memoryMonitor.js +0 -76
- package/dist/lib/messages.js +0 -3
- package/dist/lib/messages.test.js +0 -25
- package/dist/lib/osc52.js +0 -53
- package/dist/lib/perfPane.js +0 -94
- package/dist/lib/platform.js +0 -312
- package/dist/lib/precisionWheel.js +0 -25
- package/dist/lib/reasoning.js +0 -39
- package/dist/lib/rpc.js +0 -26
- package/dist/lib/subagentTree.js +0 -287
- package/dist/lib/syntax.js +0 -89
- package/dist/lib/terminalModes.js +0 -46
- package/dist/lib/terminalParity.js +0 -48
- package/dist/lib/terminalSetup.js +0 -321
- package/dist/lib/text.js +0 -203
- package/dist/lib/text.test.js +0 -18
- package/dist/lib/todo.js +0 -2
- package/dist/lib/todo.test.js +0 -22
- package/dist/lib/viewportStore.js +0 -82
- package/dist/lib/virtualHeights.js +0 -61
- package/dist/lib/wheelAccel.js +0 -143
- package/dist/theme.js +0 -398
- package/dist/types.js +0 -1
|
@@ -1,794 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import * as Ink from '@cvc/ink';
|
|
3
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import { setInputSelection } from '../app/inputSelectionStore.js';
|
|
5
|
-
import { readClipboardText, writeClipboardText } from '../lib/clipboard.js';
|
|
6
|
-
import { cursorLayout, offsetFromPosition } from '../lib/inputMetrics.js';
|
|
7
|
-
import { DEFAULT_VOICE_RECORD_KEY, isActionMod, isMac, isMacActionFallback, isVoiceToggleKey } from '../lib/platform.js';
|
|
8
|
-
const ink = Ink;
|
|
9
|
-
const { Box, Text, useStdin, useInput, useStdout, stringWidth, useDeclaredCursor, useTerminalFocus } = ink;
|
|
10
|
-
const ESC = '\x1b';
|
|
11
|
-
const INV = `${ESC}[7m`;
|
|
12
|
-
const INV_OFF = `${ESC}[27m`;
|
|
13
|
-
const DIM = `${ESC}[2m`;
|
|
14
|
-
const DIM_OFF = `${ESC}[22m`;
|
|
15
|
-
const FWD_DEL_RE = new RegExp(`${ESC}\\[3(?:[~$^]|;)`);
|
|
16
|
-
const PRINTABLE = /^[ -~\u00a0-\uffff]+$/;
|
|
17
|
-
const BRACKET_PASTE = new RegExp(`${ESC}?\\[20[01]~`, 'g');
|
|
18
|
-
const MULTI_CLICK_MS = 500;
|
|
19
|
-
const invert = (s) => INV + s + INV_OFF;
|
|
20
|
-
const dim = (s) => DIM + s + DIM_OFF;
|
|
21
|
-
let _seg = null;
|
|
22
|
-
const seg = () => (_seg ??= new Intl.Segmenter(undefined, { granularity: 'grapheme' }));
|
|
23
|
-
const STOP_CACHE_MAX = 32;
|
|
24
|
-
const stopCache = new Map();
|
|
25
|
-
function graphemeStops(s) {
|
|
26
|
-
const hit = stopCache.get(s);
|
|
27
|
-
if (hit) {
|
|
28
|
-
return hit;
|
|
29
|
-
}
|
|
30
|
-
const stops = [0];
|
|
31
|
-
for (const { index } of seg().segment(s)) {
|
|
32
|
-
if (index > 0) {
|
|
33
|
-
stops.push(index);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (stops.at(-1) !== s.length) {
|
|
37
|
-
stops.push(s.length);
|
|
38
|
-
}
|
|
39
|
-
stopCache.set(s, stops);
|
|
40
|
-
if (stopCache.size > STOP_CACHE_MAX) {
|
|
41
|
-
const oldest = stopCache.keys().next().value;
|
|
42
|
-
if (oldest !== undefined) {
|
|
43
|
-
stopCache.delete(oldest);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return stops;
|
|
47
|
-
}
|
|
48
|
-
function snapPos(s, p) {
|
|
49
|
-
const pos = Math.max(0, Math.min(p, s.length));
|
|
50
|
-
let last = 0;
|
|
51
|
-
for (const stop of graphemeStops(s)) {
|
|
52
|
-
if (stop > pos) {
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
last = stop;
|
|
56
|
-
}
|
|
57
|
-
return last;
|
|
58
|
-
}
|
|
59
|
-
function prevPos(s, p) {
|
|
60
|
-
const pos = snapPos(s, p);
|
|
61
|
-
let prev = 0;
|
|
62
|
-
for (const stop of graphemeStops(s)) {
|
|
63
|
-
if (stop >= pos) {
|
|
64
|
-
return prev;
|
|
65
|
-
}
|
|
66
|
-
prev = stop;
|
|
67
|
-
}
|
|
68
|
-
return prev;
|
|
69
|
-
}
|
|
70
|
-
function nextPos(s, p) {
|
|
71
|
-
const pos = snapPos(s, p);
|
|
72
|
-
for (const stop of graphemeStops(s)) {
|
|
73
|
-
if (stop > pos) {
|
|
74
|
-
return stop;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return s.length;
|
|
78
|
-
}
|
|
79
|
-
function wordLeft(s, p) {
|
|
80
|
-
let i = snapPos(s, p) - 1;
|
|
81
|
-
while (i > 0 && /\s/.test(s[i])) {
|
|
82
|
-
i--;
|
|
83
|
-
}
|
|
84
|
-
while (i > 0 && !/\s/.test(s[i - 1])) {
|
|
85
|
-
i--;
|
|
86
|
-
}
|
|
87
|
-
return Math.max(0, i);
|
|
88
|
-
}
|
|
89
|
-
function wordRight(s, p) {
|
|
90
|
-
let i = snapPos(s, p);
|
|
91
|
-
while (i < s.length && !/\s/.test(s[i])) {
|
|
92
|
-
i++;
|
|
93
|
-
}
|
|
94
|
-
while (i < s.length && /\s/.test(s[i])) {
|
|
95
|
-
i++;
|
|
96
|
-
}
|
|
97
|
-
return i;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Move cursor one logical line up or down inside `s` while preserving the
|
|
101
|
-
* column offset from the current line's start. Returns `null` when the cursor
|
|
102
|
-
* is already on the first line (up) or last line (down) — callers use that
|
|
103
|
-
* signal to fall through to history cycling instead of eating the arrow key.
|
|
104
|
-
*/
|
|
105
|
-
export function lineNav(s, p, dir) {
|
|
106
|
-
const pos = snapPos(s, p);
|
|
107
|
-
const curStart = s.lastIndexOf('\n', pos - 1) + 1;
|
|
108
|
-
const col = pos - curStart;
|
|
109
|
-
if (dir < 0) {
|
|
110
|
-
if (curStart === 0) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
const prevStart = s.lastIndexOf('\n', curStart - 2) + 1;
|
|
114
|
-
return snapPos(s, Math.min(prevStart + col, curStart - 1));
|
|
115
|
-
}
|
|
116
|
-
const nextBreak = s.indexOf('\n', pos);
|
|
117
|
-
if (nextBreak < 0) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
const nextEnd = s.indexOf('\n', nextBreak + 1);
|
|
121
|
-
const lineEnd = nextEnd < 0 ? s.length : nextEnd;
|
|
122
|
-
return snapPos(s, Math.min(nextBreak + 1 + col, lineEnd));
|
|
123
|
-
}
|
|
124
|
-
export { offsetFromPosition };
|
|
125
|
-
function renderWithCursor(value, cursor) {
|
|
126
|
-
const pos = Math.max(0, Math.min(cursor, value.length));
|
|
127
|
-
let out = '', done = false;
|
|
128
|
-
for (const { segment, index } of seg().segment(value)) {
|
|
129
|
-
if (!done && index >= pos) {
|
|
130
|
-
out += invert(index === pos && segment !== '\n' ? segment : ' ');
|
|
131
|
-
done = true;
|
|
132
|
-
if (index === pos && segment !== '\n') {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
out += segment;
|
|
137
|
-
}
|
|
138
|
-
return done ? out : out + invert(' ');
|
|
139
|
-
}
|
|
140
|
-
function renderWithSelection(value, start, end) {
|
|
141
|
-
if (start >= end) {
|
|
142
|
-
return value;
|
|
143
|
-
}
|
|
144
|
-
return value.slice(0, start) + invert(value.slice(start, end) || ' ') + value.slice(end);
|
|
145
|
-
}
|
|
146
|
-
function useFwdDelete(active) {
|
|
147
|
-
const ref = useRef(false);
|
|
148
|
-
const { inputEmitter: ee } = useStdin();
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
if (!active) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const h = (d) => {
|
|
154
|
-
ref.current = FWD_DEL_RE.test(d);
|
|
155
|
-
};
|
|
156
|
-
ee.prependListener('input', h);
|
|
157
|
-
return () => {
|
|
158
|
-
ee.removeListener('input', h);
|
|
159
|
-
};
|
|
160
|
-
}, [active, ee]);
|
|
161
|
-
return ref;
|
|
162
|
-
}
|
|
163
|
-
const isPasteResultPromise = (value) => !!value && typeof value.then === 'function';
|
|
164
|
-
export function TextInput({ columns = 80, value, onChange, onPaste, onSubmit, mask, mouseApiRef, voiceRecordKey = DEFAULT_VOICE_RECORD_KEY, placeholder = '', focus = true }) {
|
|
165
|
-
const [cur, setCur] = useState(value.length);
|
|
166
|
-
const [sel, setSel] = useState(null);
|
|
167
|
-
const fwdDel = useFwdDelete(focus);
|
|
168
|
-
const termFocus = useTerminalFocus();
|
|
169
|
-
const { stdout } = useStdout();
|
|
170
|
-
const curRef = useRef(cur);
|
|
171
|
-
const selRef = useRef(null);
|
|
172
|
-
const vRef = useRef(value);
|
|
173
|
-
const self = useRef(false);
|
|
174
|
-
const pasteBuf = useRef('');
|
|
175
|
-
const pasteEnd = useRef(null);
|
|
176
|
-
const pasteTimer = useRef(null);
|
|
177
|
-
const pastePos = useRef(0);
|
|
178
|
-
const editVersionRef = useRef(0);
|
|
179
|
-
const parentChangeTimer = useRef(null);
|
|
180
|
-
const pendingParentValue = useRef(null);
|
|
181
|
-
const localRenderTimer = useRef(null);
|
|
182
|
-
const lineWidthRef = useRef(stringWidth(value.includes('\n') ? value.slice(value.lastIndexOf('\n') + 1) : value));
|
|
183
|
-
const mouseAnchorRef = useRef(null);
|
|
184
|
-
const lastClickRef = useRef({ at: 0, offset: -1 });
|
|
185
|
-
const undo = useRef([]);
|
|
186
|
-
const redo = useRef([]);
|
|
187
|
-
const cbChange = useRef(onChange);
|
|
188
|
-
const cbSubmit = useRef(onSubmit);
|
|
189
|
-
const cbPaste = useRef(onPaste);
|
|
190
|
-
cbChange.current = onChange;
|
|
191
|
-
cbSubmit.current = onSubmit;
|
|
192
|
-
cbPaste.current = onPaste;
|
|
193
|
-
const raw = self.current ? vRef.current : value;
|
|
194
|
-
const display = mask ? raw.replace(/[^\n]/g, mask[0] ?? '*') : raw;
|
|
195
|
-
const selected = useMemo(() => sel && sel.start !== sel.end ? { end: Math.max(sel.start, sel.end), start: Math.min(sel.start, sel.end) } : null, [sel]);
|
|
196
|
-
const layout = useMemo(() => cursorLayout(display, cur, columns), [columns, cur, display]);
|
|
197
|
-
const boxRef = useDeclaredCursor({
|
|
198
|
-
line: layout.line,
|
|
199
|
-
column: layout.column,
|
|
200
|
-
active: focus && termFocus && !selected
|
|
201
|
-
});
|
|
202
|
-
// Hide the hardware cursor while a selection is active (prevents
|
|
203
|
-
// auto-wrap onto the next row when inverted text fills the column
|
|
204
|
-
// exactly) or when the terminal loses focus (suppresses the hollow-rect
|
|
205
|
-
// ghost most terminals draw at the parked position).
|
|
206
|
-
const hideHardwareCursor = focus && !!stdout?.isTTY && (!!selected || !termFocus);
|
|
207
|
-
useEffect(() => {
|
|
208
|
-
if (!hideHardwareCursor || !stdout) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
stdout.write('\x1b[?25l');
|
|
212
|
-
return () => {
|
|
213
|
-
stdout.write('\x1b[?25h');
|
|
214
|
-
};
|
|
215
|
-
}, [hideHardwareCursor, stdout]);
|
|
216
|
-
const nativeCursor = focus && termFocus && !selected && !!stdout?.isTTY;
|
|
217
|
-
// Placeholder text is just a hint, not a selection — render it dim
|
|
218
|
-
// without inverse styling. In a TTY the hardware cursor parks at column
|
|
219
|
-
// 0 and visually marks the input start. Non-TTY surfaces still need the
|
|
220
|
-
// synthetic inverse first-char to draw a cursor at all.
|
|
221
|
-
const rendered = useMemo(() => {
|
|
222
|
-
if (!focus) {
|
|
223
|
-
return display || dim(placeholder);
|
|
224
|
-
}
|
|
225
|
-
if (!display && placeholder) {
|
|
226
|
-
return nativeCursor ? dim(placeholder) : invert(placeholder[0] ?? ' ') + dim(placeholder.slice(1));
|
|
227
|
-
}
|
|
228
|
-
if (selected) {
|
|
229
|
-
return renderWithSelection(display, selected.start, selected.end);
|
|
230
|
-
}
|
|
231
|
-
return nativeCursor ? display || ' ' : renderWithCursor(display, cur);
|
|
232
|
-
}, [cur, display, focus, nativeCursor, placeholder, selected]);
|
|
233
|
-
useEffect(() => {
|
|
234
|
-
if (self.current) {
|
|
235
|
-
self.current = false;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
setCur(value.length);
|
|
239
|
-
setSel(null);
|
|
240
|
-
curRef.current = value.length;
|
|
241
|
-
selRef.current = null;
|
|
242
|
-
vRef.current = value;
|
|
243
|
-
lineWidthRef.current = stringWidth(value.includes('\n') ? value.slice(value.lastIndexOf('\n') + 1) : value);
|
|
244
|
-
undo.current = [];
|
|
245
|
-
redo.current = [];
|
|
246
|
-
}
|
|
247
|
-
}, [value]);
|
|
248
|
-
useEffect(() => {
|
|
249
|
-
if (!focus) {
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
const dropSel = () => {
|
|
253
|
-
if (!selRef.current) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
selRef.current = null;
|
|
257
|
-
setSel(null);
|
|
258
|
-
};
|
|
259
|
-
setInputSelection({
|
|
260
|
-
clear: dropSel,
|
|
261
|
-
collapseToEnd: () => {
|
|
262
|
-
dropSel();
|
|
263
|
-
setCur(vRef.current.length);
|
|
264
|
-
curRef.current = vRef.current.length;
|
|
265
|
-
},
|
|
266
|
-
end: selected?.end ?? curRef.current,
|
|
267
|
-
start: selected?.start ?? curRef.current,
|
|
268
|
-
value: vRef.current
|
|
269
|
-
});
|
|
270
|
-
return () => setInputSelection(null);
|
|
271
|
-
}, [cur, focus, selected]);
|
|
272
|
-
useEffect(() => () => {
|
|
273
|
-
if (pasteTimer.current) {
|
|
274
|
-
clearTimeout(pasteTimer.current);
|
|
275
|
-
}
|
|
276
|
-
if (parentChangeTimer.current) {
|
|
277
|
-
clearTimeout(parentChangeTimer.current);
|
|
278
|
-
}
|
|
279
|
-
if (localRenderTimer.current) {
|
|
280
|
-
clearTimeout(localRenderTimer.current);
|
|
281
|
-
}
|
|
282
|
-
}, []);
|
|
283
|
-
const flushParentChange = () => {
|
|
284
|
-
if (parentChangeTimer.current) {
|
|
285
|
-
clearTimeout(parentChangeTimer.current);
|
|
286
|
-
parentChangeTimer.current = null;
|
|
287
|
-
}
|
|
288
|
-
const next = pendingParentValue.current;
|
|
289
|
-
pendingParentValue.current = null;
|
|
290
|
-
if (next !== null) {
|
|
291
|
-
self.current = true;
|
|
292
|
-
cbChange.current(next);
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
const scheduleParentChange = (next) => {
|
|
296
|
-
pendingParentValue.current = next;
|
|
297
|
-
if (parentChangeTimer.current) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
parentChangeTimer.current = setTimeout(flushParentChange, 16);
|
|
301
|
-
};
|
|
302
|
-
const cancelLocalRender = () => {
|
|
303
|
-
if (localRenderTimer.current) {
|
|
304
|
-
clearTimeout(localRenderTimer.current);
|
|
305
|
-
localRenderTimer.current = null;
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
const scheduleLocalRender = () => {
|
|
309
|
-
if (localRenderTimer.current) {
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
localRenderTimer.current = setTimeout(() => {
|
|
313
|
-
localRenderTimer.current = null;
|
|
314
|
-
setCur(curRef.current);
|
|
315
|
-
}, 16);
|
|
316
|
-
};
|
|
317
|
-
const canFastEchoBase = () => focus && termFocus && !selected && !mask && !!stdout?.isTTY;
|
|
318
|
-
const canFastAppend = (current, cursor, text) => {
|
|
319
|
-
const sw = stringWidth(text);
|
|
320
|
-
return (canFastEchoBase() &&
|
|
321
|
-
cursor === current.length &&
|
|
322
|
-
current.length > 0 &&
|
|
323
|
-
!current.includes('\n') &&
|
|
324
|
-
sw === text.length &&
|
|
325
|
-
lineWidthRef.current + sw < Math.max(1, columns));
|
|
326
|
-
};
|
|
327
|
-
const canFastBackspace = (current, cursor) => {
|
|
328
|
-
if (!canFastEchoBase() || cursor !== current.length || cursor <= 0 || current.includes('\n')) {
|
|
329
|
-
return false;
|
|
330
|
-
}
|
|
331
|
-
return stringWidth(current.slice(prevPos(current, cursor), cursor)) === 1;
|
|
332
|
-
};
|
|
333
|
-
const commit = (next, nextCur, track = true, syncParent = true, syncLocal = true, nextLineWidth) => {
|
|
334
|
-
const prev = vRef.current;
|
|
335
|
-
const c = snapPos(next, nextCur);
|
|
336
|
-
editVersionRef.current += 1;
|
|
337
|
-
if (selRef.current) {
|
|
338
|
-
selRef.current = null;
|
|
339
|
-
setSel(null);
|
|
340
|
-
}
|
|
341
|
-
if (track && next !== prev) {
|
|
342
|
-
undo.current.push({ cursor: curRef.current, value: prev });
|
|
343
|
-
if (undo.current.length > 200) {
|
|
344
|
-
undo.current.shift();
|
|
345
|
-
}
|
|
346
|
-
redo.current = [];
|
|
347
|
-
}
|
|
348
|
-
if (syncLocal) {
|
|
349
|
-
cancelLocalRender();
|
|
350
|
-
setCur(c);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
scheduleLocalRender();
|
|
354
|
-
}
|
|
355
|
-
curRef.current = c;
|
|
356
|
-
vRef.current = next;
|
|
357
|
-
lineWidthRef.current =
|
|
358
|
-
nextLineWidth ?? stringWidth(next.includes('\n') ? next.slice(next.lastIndexOf('\n') + 1) : next);
|
|
359
|
-
if (next !== prev) {
|
|
360
|
-
if (syncParent) {
|
|
361
|
-
flushParentChange();
|
|
362
|
-
self.current = true;
|
|
363
|
-
cbChange.current(next);
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
self.current = true;
|
|
367
|
-
scheduleParentChange(next);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
const swap = (from, to) => {
|
|
372
|
-
const entry = from.current.pop();
|
|
373
|
-
if (!entry) {
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
to.current.push({ cursor: curRef.current, value: vRef.current });
|
|
377
|
-
commit(entry.value, entry.cursor, false);
|
|
378
|
-
};
|
|
379
|
-
const emitPaste = (e) => {
|
|
380
|
-
const startVersion = editVersionRef.current;
|
|
381
|
-
const h = cbPaste.current?.(e);
|
|
382
|
-
if (isPasteResultPromise(h)) {
|
|
383
|
-
const fallbackText = e.text;
|
|
384
|
-
void h
|
|
385
|
-
.then(result => {
|
|
386
|
-
if (result && editVersionRef.current === startVersion) {
|
|
387
|
-
commit(result.value, result.cursor);
|
|
388
|
-
}
|
|
389
|
-
else if (result && fallbackText && PRINTABLE.test(fallbackText)) {
|
|
390
|
-
// User typed while async paste was in-flight — fall back to raw text insert
|
|
391
|
-
// so the pasted content is not silently lost.
|
|
392
|
-
const cur = curRef.current;
|
|
393
|
-
const v = vRef.current;
|
|
394
|
-
commit(v.slice(0, cur) + fallbackText + v.slice(cur), cur + fallbackText.length);
|
|
395
|
-
}
|
|
396
|
-
})
|
|
397
|
-
.catch(() => { });
|
|
398
|
-
return true;
|
|
399
|
-
}
|
|
400
|
-
if (h) {
|
|
401
|
-
commit(h.value, h.cursor);
|
|
402
|
-
}
|
|
403
|
-
return !!h;
|
|
404
|
-
};
|
|
405
|
-
const flushPaste = () => {
|
|
406
|
-
const text = pasteBuf.current;
|
|
407
|
-
const at = pastePos.current;
|
|
408
|
-
const end = pasteEnd.current ?? at;
|
|
409
|
-
pasteBuf.current = '';
|
|
410
|
-
pasteEnd.current = null;
|
|
411
|
-
pasteTimer.current = null;
|
|
412
|
-
if (!text) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
if (!emitPaste({ cursor: at, text, value: vRef.current }) && PRINTABLE.test(text)) {
|
|
416
|
-
commit(vRef.current.slice(0, at) + text + vRef.current.slice(end), at + text.length);
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
const clearSel = () => {
|
|
420
|
-
if (!selRef.current) {
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
selRef.current = null;
|
|
424
|
-
setSel(null);
|
|
425
|
-
};
|
|
426
|
-
const selectAll = () => {
|
|
427
|
-
const end = vRef.current.length;
|
|
428
|
-
if (!end) {
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
const next = { end, start: 0 };
|
|
432
|
-
selRef.current = next;
|
|
433
|
-
setSel(next);
|
|
434
|
-
setCur(end);
|
|
435
|
-
curRef.current = end;
|
|
436
|
-
};
|
|
437
|
-
const moveCursor = (next, extend = false) => {
|
|
438
|
-
const c = snapPos(vRef.current, next);
|
|
439
|
-
const anchor = selRef.current?.start ?? curRef.current;
|
|
440
|
-
if (!extend || anchor === c) {
|
|
441
|
-
clearSel();
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
const nextSel = { end: c, start: anchor };
|
|
445
|
-
selRef.current = nextSel;
|
|
446
|
-
setSel(nextSel);
|
|
447
|
-
}
|
|
448
|
-
setCur(c);
|
|
449
|
-
curRef.current = c;
|
|
450
|
-
};
|
|
451
|
-
const selRange = () => {
|
|
452
|
-
const range = selRef.current;
|
|
453
|
-
return range && range.start !== range.end
|
|
454
|
-
? { end: Math.max(range.start, range.end), start: Math.min(range.start, range.end) }
|
|
455
|
-
: null;
|
|
456
|
-
};
|
|
457
|
-
const ins = (v, c, s) => v.slice(0, c) + s + v.slice(c);
|
|
458
|
-
const pastePlainText = (text) => {
|
|
459
|
-
const cleaned = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
460
|
-
if (!cleaned) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
const range = selRange();
|
|
464
|
-
const nextValue = range
|
|
465
|
-
? vRef.current.slice(0, range.start) + cleaned + vRef.current.slice(range.end)
|
|
466
|
-
: vRef.current.slice(0, curRef.current) + cleaned + vRef.current.slice(curRef.current);
|
|
467
|
-
const nextCursor = range ? range.start + cleaned.length : curRef.current + cleaned.length;
|
|
468
|
-
commit(nextValue, nextCursor);
|
|
469
|
-
};
|
|
470
|
-
const startMouseSelection = (next) => {
|
|
471
|
-
const c = snapPos(vRef.current, next);
|
|
472
|
-
mouseAnchorRef.current = c;
|
|
473
|
-
selRef.current = { end: c, start: c };
|
|
474
|
-
setSel(null);
|
|
475
|
-
setCur(c);
|
|
476
|
-
curRef.current = c;
|
|
477
|
-
};
|
|
478
|
-
const dragMouseSelection = (next) => {
|
|
479
|
-
if (mouseAnchorRef.current === null) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
const c = snapPos(vRef.current, next);
|
|
483
|
-
const range = { end: c, start: mouseAnchorRef.current };
|
|
484
|
-
selRef.current = range;
|
|
485
|
-
setSel(range.start === range.end ? null : range);
|
|
486
|
-
setCur(c);
|
|
487
|
-
curRef.current = c;
|
|
488
|
-
};
|
|
489
|
-
const endMouseSelection = () => {
|
|
490
|
-
mouseAnchorRef.current = null;
|
|
491
|
-
const range = selRef.current;
|
|
492
|
-
if (range && range.start === range.end) {
|
|
493
|
-
selRef.current = null;
|
|
494
|
-
setSel(null);
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
const normalized = selRange();
|
|
498
|
-
if (isMac && normalized) {
|
|
499
|
-
void writeClipboardText(vRef.current.slice(normalized.start, normalized.end));
|
|
500
|
-
}
|
|
501
|
-
};
|
|
502
|
-
const offsetAt = (e) => offsetFromPosition(display, e.localRow ?? 0, e.localCol ?? 0, columns);
|
|
503
|
-
const isMultiClickAt = (offset) => {
|
|
504
|
-
const now = Date.now();
|
|
505
|
-
const last = lastClickRef.current;
|
|
506
|
-
lastClickRef.current = { at: now, offset };
|
|
507
|
-
return now - last.at < MULTI_CLICK_MS && offset === last.offset;
|
|
508
|
-
};
|
|
509
|
-
if (mouseApiRef) {
|
|
510
|
-
mouseApiRef.current = {
|
|
511
|
-
dragAt: (row, col) => dragMouseSelection(offsetFromPosition(display, row, col, columns)),
|
|
512
|
-
end: endMouseSelection,
|
|
513
|
-
startAtBeginning: () => startMouseSelection(0)
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
useInput((inp, k, event) => {
|
|
517
|
-
const eventRaw = event.keypress.raw;
|
|
518
|
-
// Configured voice shortcut wins over composer-level defaults like
|
|
519
|
-
// paste/copy so users who bind voice to ctrl+v / alt+v / cmd+v
|
|
520
|
-
// actually get voice toggled instead of a paste (Copilot round-7
|
|
521
|
-
// follow-up on #19835). The pass-through predicate is a no-op for
|
|
522
|
-
// ordinary typing and plain paste when voice is unbound to 'v'.
|
|
523
|
-
if (shouldPassThroughToGlobalHandler(inp, k, voiceRecordKey)) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
if (eventRaw === '\x1bv' ||
|
|
527
|
-
eventRaw === '\x1bV' ||
|
|
528
|
-
eventRaw === '\x16' ||
|
|
529
|
-
(isMac && isActionMod(k) && inp.toLowerCase() === 'v')) {
|
|
530
|
-
if (cbPaste.current) {
|
|
531
|
-
return void emitPaste({ cursor: curRef.current, hotkey: true, text: '', value: vRef.current });
|
|
532
|
-
}
|
|
533
|
-
if (isMac) {
|
|
534
|
-
void readClipboardText().then(text => {
|
|
535
|
-
if (text) {
|
|
536
|
-
pastePlainText(text);
|
|
537
|
-
}
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
if (isMac && isActionMod(k) && inp.toLowerCase() === 'c') {
|
|
543
|
-
const range = selRange();
|
|
544
|
-
if (range) {
|
|
545
|
-
const text = vRef.current.slice(range.start, range.end);
|
|
546
|
-
void writeClipboardText(text);
|
|
547
|
-
}
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
if (k.upArrow || k.downArrow) {
|
|
551
|
-
const next = lineNav(vRef.current, curRef.current, k.upArrow ? -1 : 1);
|
|
552
|
-
if (next !== null) {
|
|
553
|
-
moveCursor(next, k.shift);
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
if (k.return) {
|
|
559
|
-
if (k.shift || k.ctrl || (isMac ? isActionMod(k) : k.meta)) {
|
|
560
|
-
flushParentChange();
|
|
561
|
-
commit(ins(vRef.current, curRef.current, '\n'), curRef.current + 1);
|
|
562
|
-
}
|
|
563
|
-
else {
|
|
564
|
-
flushParentChange();
|
|
565
|
-
cbSubmit.current?.(vRef.current);
|
|
566
|
-
}
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
let c = curRef.current;
|
|
570
|
-
let v = vRef.current;
|
|
571
|
-
const mod = isActionMod(k);
|
|
572
|
-
const wordMod = mod || k.meta;
|
|
573
|
-
const actionHome = k.home || (!isMac && mod && inp === 'a') || isMacActionFallback(k, inp, 'a');
|
|
574
|
-
const actionEnd = k.end || (mod && inp === 'e') || isMacActionFallback(k, inp, 'e');
|
|
575
|
-
const actionDeleteToStart = (mod && inp === 'u') || isMacActionFallback(k, inp, 'u');
|
|
576
|
-
const actionKillToEnd = (mod && inp === 'k') || isMacActionFallback(k, inp, 'k');
|
|
577
|
-
const actionDeleteWord = (mod && inp === 'w') || isMacActionFallback(k, inp, 'w');
|
|
578
|
-
const range = selRange();
|
|
579
|
-
const delFwd = k.delete || fwdDel.current;
|
|
580
|
-
if (mod && inp === 'z') {
|
|
581
|
-
return swap(undo, redo);
|
|
582
|
-
}
|
|
583
|
-
if ((mod && inp === 'y') || (mod && k.shift && inp === 'z')) {
|
|
584
|
-
return swap(redo, undo);
|
|
585
|
-
}
|
|
586
|
-
if (isMac && mod && inp === 'a') {
|
|
587
|
-
return selectAll();
|
|
588
|
-
}
|
|
589
|
-
if (actionHome) {
|
|
590
|
-
c = 0;
|
|
591
|
-
moveCursor(c, k.shift);
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
else if (actionEnd) {
|
|
595
|
-
c = v.length;
|
|
596
|
-
moveCursor(c, k.shift);
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
else if (k.leftArrow) {
|
|
600
|
-
if (range && !wordMod && !k.shift) {
|
|
601
|
-
clearSel();
|
|
602
|
-
c = range.start;
|
|
603
|
-
}
|
|
604
|
-
else {
|
|
605
|
-
c = wordMod ? wordLeft(v, c) : prevPos(v, c);
|
|
606
|
-
}
|
|
607
|
-
moveCursor(c, k.shift);
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
else if (k.rightArrow) {
|
|
611
|
-
if (range && !wordMod && !k.shift) {
|
|
612
|
-
clearSel();
|
|
613
|
-
c = range.end;
|
|
614
|
-
}
|
|
615
|
-
else {
|
|
616
|
-
c = wordMod ? wordRight(v, c) : nextPos(v, c);
|
|
617
|
-
}
|
|
618
|
-
moveCursor(c, k.shift);
|
|
619
|
-
return;
|
|
620
|
-
}
|
|
621
|
-
else if (wordMod && inp === 'b') {
|
|
622
|
-
clearSel();
|
|
623
|
-
c = wordLeft(v, c);
|
|
624
|
-
}
|
|
625
|
-
else if (wordMod && inp === 'f') {
|
|
626
|
-
clearSel();
|
|
627
|
-
c = wordRight(v, c);
|
|
628
|
-
}
|
|
629
|
-
else if (range && (k.backspace || delFwd)) {
|
|
630
|
-
v = v.slice(0, range.start) + v.slice(range.end);
|
|
631
|
-
c = range.start;
|
|
632
|
-
}
|
|
633
|
-
else if (k.backspace && c > 0) {
|
|
634
|
-
if (wordMod) {
|
|
635
|
-
const t = wordLeft(v, c);
|
|
636
|
-
v = v.slice(0, t) + v.slice(c);
|
|
637
|
-
c = t;
|
|
638
|
-
}
|
|
639
|
-
else if (canFastBackspace(v, c)) {
|
|
640
|
-
const t = prevPos(v, c);
|
|
641
|
-
v = v.slice(0, t) + v.slice(c);
|
|
642
|
-
c = t;
|
|
643
|
-
stdout.write('\b \b');
|
|
644
|
-
commit(v, c, true, false, false, Math.max(0, lineWidthRef.current - 1));
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
const t = prevPos(v, c);
|
|
649
|
-
v = v.slice(0, t) + v.slice(c);
|
|
650
|
-
c = t;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
else if (delFwd && c < v.length) {
|
|
654
|
-
if (wordMod) {
|
|
655
|
-
const t = wordRight(v, c);
|
|
656
|
-
v = v.slice(0, c) + v.slice(t);
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
v = v.slice(0, c) + v.slice(nextPos(v, c));
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
else if (actionDeleteWord) {
|
|
663
|
-
if (range) {
|
|
664
|
-
v = v.slice(0, range.start) + v.slice(range.end);
|
|
665
|
-
c = range.start;
|
|
666
|
-
}
|
|
667
|
-
else if (c > 0) {
|
|
668
|
-
clearSel();
|
|
669
|
-
const t = wordLeft(v, c);
|
|
670
|
-
v = v.slice(0, t) + v.slice(c);
|
|
671
|
-
c = t;
|
|
672
|
-
}
|
|
673
|
-
else {
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
else if (actionDeleteToStart) {
|
|
678
|
-
if (range) {
|
|
679
|
-
v = v.slice(0, range.start) + v.slice(range.end);
|
|
680
|
-
c = range.start;
|
|
681
|
-
}
|
|
682
|
-
else {
|
|
683
|
-
v = v.slice(c);
|
|
684
|
-
c = 0;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
else if (actionKillToEnd) {
|
|
688
|
-
if (range) {
|
|
689
|
-
v = v.slice(0, range.start) + v.slice(range.end);
|
|
690
|
-
c = range.start;
|
|
691
|
-
}
|
|
692
|
-
else {
|
|
693
|
-
v = v.slice(0, c);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
else if (event.keypress.isPasted || inp.length > 0) {
|
|
697
|
-
const bracketed = event.keypress.isPasted || inp.includes('[200~');
|
|
698
|
-
const text = inp.replace(BRACKET_PASTE, '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
699
|
-
if (bracketed && emitPaste({ bracketed: true, cursor: c, text, value: v })) {
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
if (!text) {
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
if (text === '\n') {
|
|
706
|
-
return commit(ins(v, c, '\n'), c + 1);
|
|
707
|
-
}
|
|
708
|
-
if (text.length > 1 || text.includes('\n')) {
|
|
709
|
-
if (!pasteBuf.current) {
|
|
710
|
-
pastePos.current = range ? range.start : c;
|
|
711
|
-
pasteEnd.current = range ? range.end : pastePos.current;
|
|
712
|
-
}
|
|
713
|
-
pasteBuf.current += text;
|
|
714
|
-
if (pasteTimer.current) {
|
|
715
|
-
clearTimeout(pasteTimer.current);
|
|
716
|
-
}
|
|
717
|
-
pasteTimer.current = setTimeout(flushPaste, 50);
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
if (PRINTABLE.test(text)) {
|
|
721
|
-
if (range) {
|
|
722
|
-
v = v.slice(0, range.start) + text + v.slice(range.end);
|
|
723
|
-
c = range.start + text.length;
|
|
724
|
-
}
|
|
725
|
-
else {
|
|
726
|
-
const simpleAppend = canFastAppend(v, c, text);
|
|
727
|
-
v = v.slice(0, c) + text + v.slice(c);
|
|
728
|
-
c += text.length;
|
|
729
|
-
if (simpleAppend) {
|
|
730
|
-
stdout.write(text);
|
|
731
|
-
commit(v, c, true, false, false, lineWidthRef.current + stringWidth(text));
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
else {
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
else {
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
commit(v, c);
|
|
744
|
-
}, { isActive: focus });
|
|
745
|
-
return (_jsx(Box, { onClick: (e) => {
|
|
746
|
-
if (!focus) {
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
e.stopImmediatePropagation?.();
|
|
750
|
-
clearSel();
|
|
751
|
-
const next = offsetAt(e);
|
|
752
|
-
setCur(next);
|
|
753
|
-
curRef.current = next;
|
|
754
|
-
}, onMouseDown: (e) => {
|
|
755
|
-
if (!focus) {
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
// Right-click → route through the same path as Alt+V so the composer
|
|
759
|
-
// clipboard RPC (text or image) handles it.
|
|
760
|
-
if (e.button === 2) {
|
|
761
|
-
e.stopImmediatePropagation?.();
|
|
762
|
-
emitPaste({ cursor: curRef.current, hotkey: true, text: '', value: vRef.current });
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
if (e.button !== 0) {
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
e.stopImmediatePropagation?.();
|
|
769
|
-
const offset = offsetAt(e);
|
|
770
|
-
if (isMultiClickAt(offset)) {
|
|
771
|
-
mouseAnchorRef.current = null;
|
|
772
|
-
selectAll();
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
startMouseSelection(offset);
|
|
776
|
-
}, onMouseDrag: (e) => {
|
|
777
|
-
if (!focus || e.button !== 0 || mouseAnchorRef.current === null) {
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
e.stopImmediatePropagation?.();
|
|
781
|
-
dragMouseSelection(offsetAt(e));
|
|
782
|
-
}, onMouseUp: (e) => {
|
|
783
|
-
e.stopImmediatePropagation?.();
|
|
784
|
-
endMouseSelection();
|
|
785
|
-
}, ref: boxRef, width: columns, children: _jsx(Text, { wrap: "wrap", children: rendered }) }));
|
|
786
|
-
}
|
|
787
|
-
export const shouldPassThroughToGlobalHandler = (input, key, voiceRecordKey = DEFAULT_VOICE_RECORD_KEY) => (key.ctrl && input === 'c') ||
|
|
788
|
-
(key.ctrl && input === 'x') ||
|
|
789
|
-
key.tab ||
|
|
790
|
-
(key.shift && key.tab) ||
|
|
791
|
-
key.pageUp ||
|
|
792
|
-
key.pageDown ||
|
|
793
|
-
key.escape ||
|
|
794
|
-
isVoiceToggleKey(key, input, voiceRecordKey);
|