@mragentix/cli 4.2.38 → 4.2.53
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/README.md +15 -6
- package/defaults/agents/bee.md +22 -0
- package/defaults/agents/db-manager.md +31 -0
- package/defaults/agents/deployer.md +35 -0
- package/defaults/agents/devops.md +36 -0
- package/defaults/agents/owl.md +24 -0
- package/defaults/agents/payments.md +29 -0
- package/defaults/agents/workspace.md +37 -0
- package/defaults/skills/act.md +65 -0
- package/defaults/skills/docker.md +84 -0
- package/defaults/skills/doppler.md +89 -0
- package/defaults/skills/drizzle.md +81 -0
- package/defaults/skills/firebase.md +83 -0
- package/defaults/skills/gh.md +91 -0
- package/defaults/skills/gws.md +72 -0
- package/defaults/skills/netlify.md +69 -0
- package/defaults/skills/prisma.md +62 -0
- package/defaults/skills/railway.md +69 -0
- package/defaults/skills/stripe.md +69 -0
- package/defaults/skills/supabase.md +84 -0
- package/defaults/skills/turso.md +78 -0
- package/defaults/skills/vercel.md +72 -0
- package/defaults/skills/wrangler.md +105 -0
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -63
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +7 -7
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agents.d.ts +9 -4
- package/dist/core/agents.d.ts.map +1 -1
- package/dist/core/agents.js +53 -8
- package/dist/core/agents.js.map +1 -1
- package/dist/core/auto-update.js +1 -1
- package/dist/core/auto-update.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +1 -1
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +1 -1
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +2 -2
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/token-estimator.d.ts +1 -1
- package/dist/core/compaction/token-estimator.d.ts.map +1 -1
- package/dist/core/event-bus.d.ts +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/extensions/types.d.ts +1 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/mcp/client.d.ts +1 -1
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/defaults.d.ts +1 -1
- package/dist/core/mcp/defaults.d.ts.map +1 -1
- package/dist/core/model-registry.d.ts +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +2 -7
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/skills.d.ts +7 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +53 -7
- package/dist/core/skills.js.map +1 -1
- package/dist/core/telegram.d.ts +19 -0
- package/dist/core/telegram.d.ts.map +1 -1
- package/dist/core/telegram.js +22 -0
- package/dist/core/telegram.js.map +1 -1
- package/dist/core/voice-transcriber.d.ts +33 -0
- package/dist/core/voice-transcriber.d.ts.map +1 -0
- package/dist/core/voice-transcriber.js +113 -0
- package/dist/core/voice-transcriber.js.map +1 -0
- package/dist/core/voice-transcriber.test.d.ts +2 -0
- package/dist/core/voice-transcriber.test.d.ts.map +1 -0
- package/dist/core/voice-transcriber.test.js +88 -0
- package/dist/core/voice-transcriber.test.js.map +1 -0
- package/dist/interactive.js +6 -6
- package/dist/interactive.js.map +1 -1
- package/dist/modes/json-mode.d.ts +1 -1
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +1 -1
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc-mode.d.ts +1 -1
- package/dist/modes/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc-mode.js +1 -1
- package/dist/modes/rpc-mode.js.map +1 -1
- package/dist/modes/serve-mode.d.ts +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +41 -4
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +1 -8
- package/dist/session.js.map +1 -1
- package/dist/system-prompt.js +2 -2
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash.d.ts +1 -2
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +1 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/enter-plan.d.ts +1 -1
- package/dist/tools/enter-plan.d.ts.map +1 -1
- package/dist/tools/exit-plan.d.ts +1 -1
- package/dist/tools/exit-plan.d.ts.map +1 -1
- package/dist/tools/exit-plan.js +2 -3
- package/dist/tools/exit-plan.js.map +1 -1
- package/dist/tools/find.d.ts +1 -1
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/grep.d.ts +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/index.d.ts +5 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/ls.d.ts +1 -1
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/plan-mode.test.js +6 -6
- package/dist/tools/plan-mode.test.js.map +1 -1
- package/dist/tools/read.d.ts +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/skill.d.ts +1 -1
- package/dist/tools/skill.d.ts.map +1 -1
- package/dist/tools/subagent.d.ts +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/task-output.d.ts +1 -1
- package/dist/tools/task-output.d.ts.map +1 -1
- package/dist/tools/task-stop.d.ts +1 -1
- package/dist/tools/task-stop.d.ts.map +1 -1
- package/dist/tools/tasks.d.ts +1 -1
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +1 -1
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/web-fetch.d.ts +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +1 -1
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/write.d.ts +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +3 -1
- package/dist/tools/write.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/App.d.ts +9 -3
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +49 -12
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/activity-phrases.d.ts +10 -0
- package/dist/ui/activity-phrases.d.ts.map +1 -0
- package/dist/ui/activity-phrases.js +227 -0
- package/dist/ui/activity-phrases.js.map +1 -0
- package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +1 -225
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/components/Banner.d.ts +1 -1
- package/dist/ui/components/Banner.d.ts.map +1 -1
- package/dist/ui/components/Banner.js +1 -1
- package/dist/ui/components/Banner.js.map +1 -1
- package/dist/ui/components/InputArea.d.ts.map +1 -1
- package/dist/ui/components/InputArea.js +453 -39
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/ModelSelector.d.ts +1 -1
- package/dist/ui/components/ModelSelector.d.ts.map +1 -1
- package/dist/ui/components/PlanOverlay.js +1 -1
- package/dist/ui/components/PlanOverlay.js.map +1 -1
- package/dist/ui/components/TaskOverlay.js +2 -2
- package/dist/ui/components/TaskOverlay.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +2 -2
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +3 -2
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useSessionManager.d.ts +2 -2
- package/dist/ui/hooks/useSessionManager.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalSize.d.ts +12 -2
- package/dist/ui/hooks/useTerminalSize.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalSize.js +27 -6
- package/dist/ui/hooks/useTerminalSize.js.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.d.ts +8 -1
- package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.js +20 -23
- package/dist/ui/hooks/useTerminalTitle.js.map +1 -1
- package/dist/ui/login.d.ts +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +5 -1
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/plan-overlay.test.d.ts +2 -0
- package/dist/ui/plan-overlay.test.d.ts.map +1 -0
- package/dist/ui/plan-overlay.test.js +327 -0
- package/dist/ui/plan-overlay.test.js.map +1 -0
- package/dist/ui/render.d.ts +2 -2
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +1 -1
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/sessions.d.ts.map +1 -1
- package/dist/ui/sessions.js +5 -1
- package/dist/ui/sessions.js.map +1 -1
- package/package.json +17 -14
- package/LICENSE +0 -21
|
@@ -6,8 +6,74 @@ import { useAnimationTick, deriveFrame } from "./AnimationContext.js";
|
|
|
6
6
|
import { useTerminalSize } from "../hooks/useTerminalSize.js";
|
|
7
7
|
import { extractImagePaths, readImageFile, getClipboardImage } from "../../utils/image.js";
|
|
8
8
|
import { SlashCommandMenu, filterCommands } from "./SlashCommandMenu.js";
|
|
9
|
+
import { log } from "../../core/logger.js";
|
|
9
10
|
const MAX_VISIBLE_LINES = 5;
|
|
10
11
|
const PROMPT = "❯ ";
|
|
12
|
+
// SGR mouse sequence: ESC [ < button ; col ; row M/m
|
|
13
|
+
// M = press, m = release. Coordinates are 1-based.
|
|
14
|
+
// SGR mouse sequence (global) — used both to strip sequences from input data
|
|
15
|
+
// and to extract click coordinates. Must reset lastIndex before each use.
|
|
16
|
+
// eslint-disable-next-line no-control-regex
|
|
17
|
+
const SGR_MOUSE_RE_G = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
18
|
+
// Enable/disable escape sequences for SGR mouse tracking.
|
|
19
|
+
// ?1000h = basic click tracking, ?1006h = SGR extended mode (supports coords > 223).
|
|
20
|
+
const ENABLE_MOUSE = "\x1b[?1000h\x1b[?1006h";
|
|
21
|
+
const DISABLE_MOUSE = "\x1b[?1006l\x1b[?1000l";
|
|
22
|
+
// Option+Arrow escape sequences — terminals send these as raw input strings
|
|
23
|
+
// rather than setting key.meta + key.leftArrow reliably.
|
|
24
|
+
const OPTION_LEFT_SEQUENCES = new Set([
|
|
25
|
+
"\x1bb", // Meta+b (emacs style)
|
|
26
|
+
"\x1b[1;3D", // CSI 1;3 D (xterm with modifiers)
|
|
27
|
+
]);
|
|
28
|
+
const OPTION_RIGHT_SEQUENCES = new Set([
|
|
29
|
+
"\x1bf", // Meta+f (emacs style)
|
|
30
|
+
"\x1b[1;3C", // CSI 1;3 C (xterm with modifiers)
|
|
31
|
+
]);
|
|
32
|
+
/** Classify a character as word, punctuation, or space. */
|
|
33
|
+
function charClass(ch) {
|
|
34
|
+
if (/\s/.test(ch))
|
|
35
|
+
return "space";
|
|
36
|
+
if (/\w/.test(ch))
|
|
37
|
+
return "word";
|
|
38
|
+
return "punct";
|
|
39
|
+
}
|
|
40
|
+
/** Find the start of the previous word from `pos` in `text`. */
|
|
41
|
+
function prevWordBoundary(text, pos) {
|
|
42
|
+
if (pos <= 0)
|
|
43
|
+
return 0;
|
|
44
|
+
let i = pos - 1;
|
|
45
|
+
// Skip whitespace
|
|
46
|
+
while (i > 0 && charClass(text[i]) === "space")
|
|
47
|
+
i--;
|
|
48
|
+
if (i <= 0)
|
|
49
|
+
return 0;
|
|
50
|
+
// Skip through same character class (word or punct)
|
|
51
|
+
const cls = charClass(text[i]);
|
|
52
|
+
while (i > 0 && charClass(text[i - 1]) === cls)
|
|
53
|
+
i--;
|
|
54
|
+
return i;
|
|
55
|
+
}
|
|
56
|
+
/** Find the end of the next word from `pos` in `text`. */
|
|
57
|
+
function nextWordBoundary(text, pos) {
|
|
58
|
+
const len = text.length;
|
|
59
|
+
if (pos >= len)
|
|
60
|
+
return len;
|
|
61
|
+
let i = pos;
|
|
62
|
+
// Skip through current character class (word or punct)
|
|
63
|
+
const cls = charClass(text[i]);
|
|
64
|
+
while (i < len && charClass(text[i]) === cls)
|
|
65
|
+
i++;
|
|
66
|
+
// Skip whitespace
|
|
67
|
+
while (i < len && charClass(text[i]) === "space")
|
|
68
|
+
i++;
|
|
69
|
+
return i;
|
|
70
|
+
}
|
|
71
|
+
/** Get the normalized selection range [start, end] from anchor and cursor, or null. */
|
|
72
|
+
function getSelectionRange(anchor, cur) {
|
|
73
|
+
if (anchor === null || anchor === cur)
|
|
74
|
+
return null;
|
|
75
|
+
return [Math.min(anchor, cur), Math.max(anchor, cur)];
|
|
76
|
+
}
|
|
11
77
|
// Border (1 each side) + padding (1 each side) = 4 characters of overhead
|
|
12
78
|
const BOX_OVERHEAD = 4;
|
|
13
79
|
// Minimum content width to prevent zero/negative values that cause infinite
|
|
@@ -56,6 +122,7 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
56
122
|
const theme = useTheme();
|
|
57
123
|
const [value, setValue] = useState("");
|
|
58
124
|
const [cursor, setCursor] = useState(0);
|
|
125
|
+
const [selectionAnchor, setSelectionAnchor] = useState(null);
|
|
59
126
|
const [images, setImages] = useState([]);
|
|
60
127
|
const historyRef = useRef([]);
|
|
61
128
|
const historyIndexRef = useRef(-1);
|
|
@@ -142,6 +209,152 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
142
209
|
internal_eventEmitter.removeListener("input", onInput);
|
|
143
210
|
};
|
|
144
211
|
}, [isActive, internal_eventEmitter]);
|
|
212
|
+
// --- Mouse click-to-position-cursor ---
|
|
213
|
+
// Store layout info in a ref so the mouse handler can map terminal
|
|
214
|
+
// coordinates to character offsets without re-subscribing on every change.
|
|
215
|
+
const layoutRef = useRef({
|
|
216
|
+
value: "",
|
|
217
|
+
displayLines: [""],
|
|
218
|
+
startLine: 0,
|
|
219
|
+
contentWidth: 10,
|
|
220
|
+
columns: 80,
|
|
221
|
+
hasImages: false,
|
|
222
|
+
});
|
|
223
|
+
// Self-calibrating anchor: the terminal row (1-based) of the first
|
|
224
|
+
// display line. Set from the first single-line click (unambiguous).
|
|
225
|
+
// Ink rewrites from the same starting row on each render, so this
|
|
226
|
+
// value stays correct as text wraps to additional lines below.
|
|
227
|
+
const firstLineRowRef = useRef(-1);
|
|
228
|
+
// Enable SGR mouse tracking and intercept mouse sequences before Ink's
|
|
229
|
+
// useInput sees them (which would insert the raw escape text). We wrap
|
|
230
|
+
// the internal event emitter's `emit` so mouse data is consumed here and
|
|
231
|
+
// never forwarded to Ink's input handler.
|
|
232
|
+
const mouseEmitRef = useRef({ original: null });
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
if (!isActive || !internal_eventEmitter)
|
|
235
|
+
return;
|
|
236
|
+
process.stdout.write(ENABLE_MOUSE);
|
|
237
|
+
// Safety: ensure mouse tracking is disabled even on crash/SIGINT/unexpected exit
|
|
238
|
+
// so the terminal isn't left in a broken state sending escape sequences on every click.
|
|
239
|
+
const onProcessExit = () => process.stdout.write(DISABLE_MOUSE);
|
|
240
|
+
process.on("exit", onProcessExit);
|
|
241
|
+
const originalEmit = internal_eventEmitter.emit.bind(internal_eventEmitter);
|
|
242
|
+
mouseEmitRef.current.original = originalEmit;
|
|
243
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
244
|
+
internal_eventEmitter.emit = (event, ...args) => {
|
|
245
|
+
if (event === "input" && typeof args[0] === "string") {
|
|
246
|
+
const data = args[0];
|
|
247
|
+
// Strip all SGR mouse sequences from the data
|
|
248
|
+
const stripped = data.replace(SGR_MOUSE_RE_G, "");
|
|
249
|
+
// Process each mouse sequence for click handling
|
|
250
|
+
let match;
|
|
251
|
+
SGR_MOUSE_RE_G.lastIndex = 0;
|
|
252
|
+
while ((match = SGR_MOUSE_RE_G.exec(data)) !== null) {
|
|
253
|
+
const btnCode = parseInt(match[1], 10);
|
|
254
|
+
const termCol = parseInt(match[2], 10);
|
|
255
|
+
const termRow = parseInt(match[3], 10);
|
|
256
|
+
const isPress = match[4] === "M";
|
|
257
|
+
// Decode SGR button code with bitmask:
|
|
258
|
+
// bits 0-1: button (0=left, 1=middle, 2=right, 3=release)
|
|
259
|
+
// bit 5 (32): motion event
|
|
260
|
+
// bit 6 (64): scroll wheel
|
|
261
|
+
const button = btnCode & 3;
|
|
262
|
+
const isMotion = (btnCode & 32) !== 0;
|
|
263
|
+
// Only handle left-click press (button 0), not motion or scroll
|
|
264
|
+
if (button !== 0 || isMotion || !isPress)
|
|
265
|
+
continue;
|
|
266
|
+
const layout = layoutRef.current;
|
|
267
|
+
if (!layout.value && layout.displayLines.length <= 1 && !layout.displayLines[0])
|
|
268
|
+
continue;
|
|
269
|
+
const numDisplayLines = layout.displayLines.length;
|
|
270
|
+
// Calibrate on the first single-line click: the clicked row
|
|
271
|
+
// IS the first (and only) display line's terminal row.
|
|
272
|
+
if (firstLineRowRef.current < 0 && numDisplayLines === 1) {
|
|
273
|
+
firstLineRowRef.current = termRow;
|
|
274
|
+
}
|
|
275
|
+
// Determine which display line was clicked
|
|
276
|
+
let clickedDisplayLine;
|
|
277
|
+
if (firstLineRowRef.current > 0) {
|
|
278
|
+
clickedDisplayLine = termRow - firstLineRowRef.current;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// Not calibrated yet (multi-line before first click) — default to line 0
|
|
282
|
+
clickedDisplayLine = 0;
|
|
283
|
+
}
|
|
284
|
+
log("INFO", "mouse", "click", {
|
|
285
|
+
termRow,
|
|
286
|
+
termCol,
|
|
287
|
+
firstLineRow: firstLineRowRef.current,
|
|
288
|
+
clickedDisplayLine,
|
|
289
|
+
numDisplayLines,
|
|
290
|
+
});
|
|
291
|
+
// Clamp to valid range
|
|
292
|
+
if (clickedDisplayLine < 0)
|
|
293
|
+
clickedDisplayLine = 0;
|
|
294
|
+
if (clickedDisplayLine >= numDisplayLines)
|
|
295
|
+
clickedDisplayLine = numDisplayLines - 1;
|
|
296
|
+
// Column within the text: subtract border(1) + padding(1) + prompt(2) = 4
|
|
297
|
+
const textCol = termCol - 1 - 4;
|
|
298
|
+
const line = layout.displayLines[clickedDisplayLine];
|
|
299
|
+
const col = Math.max(0, Math.min(textCol, line.length));
|
|
300
|
+
// Convert display line + col to absolute character offset
|
|
301
|
+
const { value: val, startLine: sl, contentWidth: cw } = layout;
|
|
302
|
+
const hardLines = val.split("\n");
|
|
303
|
+
let charOffset = 0;
|
|
304
|
+
let vlIndex = 0;
|
|
305
|
+
let found = false;
|
|
306
|
+
for (let h = 0; h < hardLines.length; h++) {
|
|
307
|
+
const wrapped = wrapLine(hardLines[h], cw > 0 ? cw : val.length + 1);
|
|
308
|
+
for (let w = 0; w < wrapped.length; w++) {
|
|
309
|
+
if (vlIndex === sl + clickedDisplayLine) {
|
|
310
|
+
setCursor(Math.min(charOffset + col, val.length));
|
|
311
|
+
setSelectionAnchor(null);
|
|
312
|
+
found = true;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
charOffset += wrapped[w].length;
|
|
316
|
+
vlIndex++;
|
|
317
|
+
}
|
|
318
|
+
if (found)
|
|
319
|
+
break;
|
|
320
|
+
charOffset++; // newline
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// Forward non-mouse data (if any remains) to Ink
|
|
324
|
+
if (stripped) {
|
|
325
|
+
return originalEmit("input", stripped);
|
|
326
|
+
}
|
|
327
|
+
return true; // swallowed entirely
|
|
328
|
+
}
|
|
329
|
+
return originalEmit(event, ...args);
|
|
330
|
+
};
|
|
331
|
+
return () => {
|
|
332
|
+
process.stdout.write(DISABLE_MOUSE);
|
|
333
|
+
process.removeListener("exit", onProcessExit);
|
|
334
|
+
// Restore original emit
|
|
335
|
+
if (mouseEmitRef.current.original) {
|
|
336
|
+
internal_eventEmitter.emit = mouseEmitRef.current.original;
|
|
337
|
+
mouseEmitRef.current.original = null;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}, [isActive, internal_eventEmitter]);
|
|
341
|
+
// Helper: delete selected text and return new value + cursor position.
|
|
342
|
+
// Returns null if no selection is active.
|
|
343
|
+
const deleteSelection = () => {
|
|
344
|
+
const sel = getSelectionRange(selectionAnchor, cursor);
|
|
345
|
+
if (!sel)
|
|
346
|
+
return null;
|
|
347
|
+
const [start, end] = sel;
|
|
348
|
+
return { newValue: value.slice(0, start) + value.slice(end), newCursor: start };
|
|
349
|
+
};
|
|
350
|
+
// Helper: clear all input state (used on submit / Ctrl+C / Escape)
|
|
351
|
+
const clearInput = () => {
|
|
352
|
+
setValue("");
|
|
353
|
+
setCursor(0);
|
|
354
|
+
setSelectionAnchor(null);
|
|
355
|
+
setImages([]);
|
|
356
|
+
setPasteText("");
|
|
357
|
+
};
|
|
145
358
|
useInput((input, key) => {
|
|
146
359
|
// Ctrl+T toggles task overlay — works even while agent is running
|
|
147
360
|
if (key.ctrl && input === "t") {
|
|
@@ -167,8 +380,17 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
167
380
|
// Submitted messages will be queued by the parent component.
|
|
168
381
|
}
|
|
169
382
|
if (key.return && (key.shift || key.meta)) {
|
|
170
|
-
|
|
171
|
-
|
|
383
|
+
// If there's a selection, replace it with the newline
|
|
384
|
+
const sel = deleteSelection();
|
|
385
|
+
if (sel) {
|
|
386
|
+
setValue(sel.newValue.slice(0, sel.newCursor) + "\n" + sel.newValue.slice(sel.newCursor));
|
|
387
|
+
setCursor(sel.newCursor + 1);
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
setValue((v) => v.slice(0, cursor) + "\n" + v.slice(cursor));
|
|
391
|
+
setCursor((c) => c + 1);
|
|
392
|
+
}
|
|
393
|
+
setSelectionAnchor(null);
|
|
172
394
|
return;
|
|
173
395
|
}
|
|
174
396
|
if (key.return) {
|
|
@@ -180,10 +402,7 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
180
402
|
historyRef.current.push(cmd);
|
|
181
403
|
historyIndexRef.current = -1;
|
|
182
404
|
onSubmit(cmd, []);
|
|
183
|
-
|
|
184
|
-
setCursor(0);
|
|
185
|
-
setImages([]);
|
|
186
|
-
setPasteText("");
|
|
405
|
+
clearInput();
|
|
187
406
|
return;
|
|
188
407
|
}
|
|
189
408
|
const trimmed = value.trim();
|
|
@@ -201,10 +420,7 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
201
420
|
}
|
|
202
421
|
: undefined;
|
|
203
422
|
onSubmit(trimmed, [...images], paste);
|
|
204
|
-
|
|
205
|
-
setCursor(0);
|
|
206
|
-
setImages([]);
|
|
207
|
-
setPasteText("");
|
|
423
|
+
clearInput();
|
|
208
424
|
}
|
|
209
425
|
return;
|
|
210
426
|
}
|
|
@@ -218,10 +434,7 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
218
434
|
}
|
|
219
435
|
if (key.ctrl && input === "c") {
|
|
220
436
|
if (value || images.length > 0) {
|
|
221
|
-
|
|
222
|
-
setCursor(0);
|
|
223
|
-
setImages([]);
|
|
224
|
-
setPasteText("");
|
|
437
|
+
clearInput();
|
|
225
438
|
}
|
|
226
439
|
else {
|
|
227
440
|
onAbort();
|
|
@@ -231,16 +444,53 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
231
444
|
if (key.ctrl && input === "d") {
|
|
232
445
|
process.exit(0);
|
|
233
446
|
}
|
|
234
|
-
//
|
|
447
|
+
// Ctrl+W — delete previous word (or selection)
|
|
448
|
+
if (key.ctrl && input === "w") {
|
|
449
|
+
const sel = deleteSelection();
|
|
450
|
+
if (sel) {
|
|
451
|
+
setValue(sel.newValue);
|
|
452
|
+
setCursor(sel.newCursor);
|
|
453
|
+
}
|
|
454
|
+
else if (cursor > 0) {
|
|
455
|
+
const boundary = prevWordBoundary(value, cursor);
|
|
456
|
+
setValue((v) => v.slice(0, boundary) + v.slice(cursor));
|
|
457
|
+
setCursor(boundary);
|
|
458
|
+
}
|
|
459
|
+
setSelectionAnchor(null);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
// Home / End — Shift extends selection
|
|
235
463
|
if (key.ctrl && input === "a") {
|
|
464
|
+
if (key.shift) {
|
|
465
|
+
if (selectionAnchor === null)
|
|
466
|
+
setSelectionAnchor(cursor);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
setSelectionAnchor(null);
|
|
470
|
+
}
|
|
236
471
|
setCursor(0);
|
|
237
472
|
return;
|
|
238
473
|
}
|
|
239
474
|
if (key.ctrl && input === "e") {
|
|
475
|
+
if (key.shift) {
|
|
476
|
+
if (selectionAnchor === null)
|
|
477
|
+
setSelectionAnchor(cursor);
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
setSelectionAnchor(null);
|
|
481
|
+
}
|
|
240
482
|
setCursor(value.length);
|
|
241
483
|
return;
|
|
242
484
|
}
|
|
243
485
|
if (key.backspace || key.delete) {
|
|
486
|
+
// If selection active, delete the selection
|
|
487
|
+
const sel = deleteSelection();
|
|
488
|
+
if (sel) {
|
|
489
|
+
setValue(sel.newValue);
|
|
490
|
+
setCursor(sel.newCursor);
|
|
491
|
+
setSelectionAnchor(null);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
244
494
|
if (cursor > 0) {
|
|
245
495
|
setValue((v) => v.slice(0, cursor - 1) + v.slice(cursor));
|
|
246
496
|
setCursor((c) => c - 1);
|
|
@@ -248,6 +498,7 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
248
498
|
else if (!value && images.length > 0) {
|
|
249
499
|
setImages((prev) => prev.slice(0, -1));
|
|
250
500
|
}
|
|
501
|
+
setSelectionAnchor(null);
|
|
251
502
|
return;
|
|
252
503
|
}
|
|
253
504
|
if (key.upArrow) {
|
|
@@ -256,6 +507,7 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
256
507
|
setMenuIndex((i) => Math.max(0, i - 1));
|
|
257
508
|
return;
|
|
258
509
|
}
|
|
510
|
+
setSelectionAnchor(null);
|
|
259
511
|
const history = historyRef.current;
|
|
260
512
|
if (history.length === 0)
|
|
261
513
|
return;
|
|
@@ -273,6 +525,7 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
273
525
|
setMenuIndex((i) => Math.min(filteredCommands.length - 1, i + 1));
|
|
274
526
|
return;
|
|
275
527
|
}
|
|
528
|
+
setSelectionAnchor(null);
|
|
276
529
|
const history = historyRef.current;
|
|
277
530
|
if (historyIndexRef.current === -1) {
|
|
278
531
|
if (onDownAtEnd)
|
|
@@ -293,12 +546,15 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
293
546
|
return;
|
|
294
547
|
}
|
|
295
548
|
if (key.escape) {
|
|
549
|
+
// First escape clears selection, second clears input (double-tap)
|
|
550
|
+
if (selectionAnchor !== null) {
|
|
551
|
+
setSelectionAnchor(null);
|
|
552
|
+
lastEscRef.current = Date.now();
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
296
555
|
const now = Date.now();
|
|
297
556
|
if ((value || images.length > 0) && now - lastEscRef.current < 400) {
|
|
298
|
-
|
|
299
|
-
setCursor(0);
|
|
300
|
-
setImages([]);
|
|
301
|
-
setPasteText("");
|
|
557
|
+
clearInput();
|
|
302
558
|
}
|
|
303
559
|
lastEscRef.current = now;
|
|
304
560
|
return;
|
|
@@ -314,30 +570,104 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
314
570
|
const cmd = "/" + selected.name;
|
|
315
571
|
setValue(cmd);
|
|
316
572
|
setCursor(cmd.length);
|
|
573
|
+
setSelectionAnchor(null);
|
|
317
574
|
}
|
|
318
575
|
return;
|
|
319
576
|
}
|
|
577
|
+
// Option+Arrow word jump via raw escape sequences — many terminals send
|
|
578
|
+
// these as input strings rather than setting key.meta + arrow reliably.
|
|
579
|
+
if (OPTION_LEFT_SEQUENCES.has(input)) {
|
|
580
|
+
if (selectionAnchor !== null) {
|
|
581
|
+
const sel = getSelectionRange(selectionAnchor, cursor);
|
|
582
|
+
if (sel)
|
|
583
|
+
setCursor(sel[0]);
|
|
584
|
+
setSelectionAnchor(null);
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
setCursor(prevWordBoundary(value, cursor));
|
|
588
|
+
}
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
if (OPTION_RIGHT_SEQUENCES.has(input)) {
|
|
592
|
+
if (selectionAnchor !== null) {
|
|
593
|
+
const sel = getSelectionRange(selectionAnchor, cursor);
|
|
594
|
+
if (sel)
|
|
595
|
+
setCursor(sel[1]);
|
|
596
|
+
setSelectionAnchor(null);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
setCursor(nextWordBoundary(value, cursor));
|
|
600
|
+
}
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
// Arrow keys — Shift extends selection, Meta/Option jumps words
|
|
320
604
|
if (key.leftArrow) {
|
|
321
|
-
if (
|
|
605
|
+
if (key.shift) {
|
|
606
|
+
if (selectionAnchor === null)
|
|
607
|
+
setSelectionAnchor(cursor);
|
|
608
|
+
}
|
|
609
|
+
else if (selectionAnchor !== null) {
|
|
610
|
+
// Collapse selection to the left edge
|
|
611
|
+
const sel = getSelectionRange(selectionAnchor, cursor);
|
|
612
|
+
if (sel)
|
|
613
|
+
setCursor(sel[0]);
|
|
614
|
+
setSelectionAnchor(null);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (key.meta) {
|
|
618
|
+
setCursor(prevWordBoundary(value, cursor));
|
|
619
|
+
}
|
|
620
|
+
else if (cursor > 0) {
|
|
322
621
|
setCursor((c) => c - 1);
|
|
622
|
+
}
|
|
623
|
+
if (!key.shift)
|
|
624
|
+
setSelectionAnchor(null);
|
|
323
625
|
return;
|
|
324
626
|
}
|
|
325
627
|
if (key.rightArrow) {
|
|
326
|
-
if (
|
|
628
|
+
if (key.shift) {
|
|
629
|
+
if (selectionAnchor === null)
|
|
630
|
+
setSelectionAnchor(cursor);
|
|
631
|
+
}
|
|
632
|
+
else if (selectionAnchor !== null) {
|
|
633
|
+
// Collapse selection to the right edge
|
|
634
|
+
const sel = getSelectionRange(selectionAnchor, cursor);
|
|
635
|
+
if (sel)
|
|
636
|
+
setCursor(sel[1]);
|
|
637
|
+
setSelectionAnchor(null);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (key.meta) {
|
|
641
|
+
setCursor(nextWordBoundary(value, cursor));
|
|
642
|
+
}
|
|
643
|
+
else if (cursor < value.length) {
|
|
327
644
|
setCursor((c) => c + 1);
|
|
645
|
+
}
|
|
646
|
+
if (!key.shift)
|
|
647
|
+
setSelectionAnchor(null);
|
|
328
648
|
return;
|
|
329
649
|
}
|
|
330
650
|
if (input) {
|
|
331
651
|
const normalized = input.replace(/\r\n?/g, "\n");
|
|
332
|
-
|
|
333
|
-
|
|
652
|
+
// If there's a selection, replace it with the typed input
|
|
653
|
+
const sel = deleteSelection();
|
|
654
|
+
if (sel) {
|
|
655
|
+
setValue(sel.newValue.slice(0, sel.newCursor) + normalized + sel.newValue.slice(sel.newCursor));
|
|
656
|
+
setCursor(sel.newCursor + normalized.length);
|
|
657
|
+
setSelectionAnchor(null);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
setValue((v) => v.slice(0, cursor) + normalized + v.slice(cursor));
|
|
661
|
+
setCursor((c) => c + normalized.length);
|
|
662
|
+
}
|
|
334
663
|
// Detect paste: Ink delivers pasted text as input.length > 1
|
|
335
664
|
// For large pastes, Ink may split into multiple chunks, so we
|
|
336
665
|
// accumulate and debounce to capture the full paste.
|
|
337
666
|
if (input.length > 1) {
|
|
667
|
+
const pasteStart = sel ? sel.newCursor : cursor;
|
|
338
668
|
setPasteText((prev) => {
|
|
339
669
|
if (!prev)
|
|
340
|
-
setPasteOffset(
|
|
670
|
+
setPasteOffset(pasteStart);
|
|
341
671
|
return prev + normalized;
|
|
342
672
|
});
|
|
343
673
|
if (pasteTimerRef.current)
|
|
@@ -398,6 +728,13 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
398
728
|
}
|
|
399
729
|
const displayLines = visualLines.slice(startLine, startLine + MAX_VISIBLE_LINES);
|
|
400
730
|
const cursorDisplayLine = cursorLineInfo.line - startLine;
|
|
731
|
+
// Keep layout ref in sync for mouse click handler
|
|
732
|
+
layoutRef.current.value = value;
|
|
733
|
+
layoutRef.current.displayLines = displayLines;
|
|
734
|
+
layoutRef.current.startLine = startLine;
|
|
735
|
+
layoutRef.current.contentWidth = contentWidth;
|
|
736
|
+
layoutRef.current.columns = columns;
|
|
737
|
+
layoutRef.current.hasImages = images.length > 0;
|
|
401
738
|
// Determine if the input starts with a slash command and find command boundary
|
|
402
739
|
const isCommand = value.startsWith("/");
|
|
403
740
|
// Command portion ends at first space (e.g., "/research" in "/research some args")
|
|
@@ -406,6 +743,8 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
406
743
|
? value.length
|
|
407
744
|
: value.indexOf(" ")
|
|
408
745
|
: 0;
|
|
746
|
+
// Active selection range (absolute character offsets)
|
|
747
|
+
const selection = getSelectionRange(selectionAnchor, cursor);
|
|
409
748
|
return (_jsxs(Box, { flexDirection: "column", width: columns, children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: disabled ? theme.textDim : borderPulseColors[borderFrame], paddingLeft: 1, paddingRight: 1, children: [images.length > 0 && (_jsx(Box, { children: _jsx(Text, { color: theme.accent, children: images
|
|
410
749
|
.map((img, i) => img.kind === "text" ? `[File: ${img.fileName}]` : `[Image #${i + 1}]`)
|
|
411
750
|
.join(" ") }) })), (() => {
|
|
@@ -451,24 +790,99 @@ export function InputArea({ onSubmit, onAbort, disabled = false, isActive = true
|
|
|
451
790
|
}
|
|
452
791
|
offset++; // newline
|
|
453
792
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const
|
|
793
|
+
const lineEndOffset = lineStartOffset + line.length;
|
|
794
|
+
// Render a text segment with command coloring and optional selection highlight
|
|
795
|
+
const renderSegment = (text, absOffset, opts) => {
|
|
796
|
+
if (!text)
|
|
797
|
+
return null;
|
|
798
|
+
const inCmd = isCommand && absOffset < commandEndIndex;
|
|
799
|
+
const cmdChars = inCmd ? Math.min(text.length, commandEndIndex - absOffset) : 0;
|
|
800
|
+
const inv = opts?.inverse ?? false;
|
|
460
801
|
if (cmdChars >= text.length) {
|
|
461
|
-
return (_jsx(Text, { color: theme.commandColor, bold: true, children: text }));
|
|
802
|
+
return (_jsx(Text, { color: theme.commandColor, bold: true, inverse: inv, children: text }));
|
|
803
|
+
}
|
|
804
|
+
if (cmdChars > 0) {
|
|
805
|
+
return (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.commandColor, bold: true, inverse: inv, children: text.slice(0, cmdChars) }), _jsx(Text, { color: theme.text, inverse: inv, children: text.slice(cmdChars) })] }));
|
|
462
806
|
}
|
|
463
|
-
return (
|
|
807
|
+
return (_jsx(Text, { color: theme.text, inverse: inv, children: text }));
|
|
464
808
|
};
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
809
|
+
// Build segments for: [before-sel] [selected] [cursor] [after-sel]
|
|
810
|
+
// considering that cursor and selection can overlap on this line
|
|
811
|
+
const segments = [];
|
|
812
|
+
let pos = 0; // position within `line`
|
|
813
|
+
// Determine selection overlap with this line (in line-local coords)
|
|
814
|
+
const selLocalStart = selection
|
|
815
|
+
? Math.max(0, selection[0] - lineStartOffset)
|
|
816
|
+
: line.length;
|
|
817
|
+
const selLocalEnd = selection
|
|
818
|
+
? Math.min(line.length, selection[1] - lineStartOffset)
|
|
819
|
+
: line.length;
|
|
820
|
+
const hasSelOnLine = selection !== null && selection[0] < lineEndOffset && selection[1] > lineStartOffset;
|
|
821
|
+
if (hasSelOnLine) {
|
|
822
|
+
// Text before selection
|
|
823
|
+
if (selLocalStart > 0) {
|
|
824
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(line.slice(0, selLocalStart), lineStartOffset) }, "pre"));
|
|
825
|
+
pos = selLocalStart;
|
|
826
|
+
}
|
|
827
|
+
// Selected text — render with inverse, but split around cursor if needed
|
|
828
|
+
if (showCursor && col >= selLocalStart && col < selLocalEnd) {
|
|
829
|
+
// Cursor is inside the selection
|
|
830
|
+
if (col > pos) {
|
|
831
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(line.slice(pos, col), lineStartOffset + pos, {
|
|
832
|
+
inverse: true,
|
|
833
|
+
}) }, "sel-before"));
|
|
834
|
+
}
|
|
835
|
+
// Cursor character (blinks within selection)
|
|
836
|
+
const cursorChar = col < line.length ? line[col] : " ";
|
|
837
|
+
const cursorAbs = lineStartOffset + col;
|
|
838
|
+
const curInCmd = isCommand && cursorAbs < commandEndIndex;
|
|
839
|
+
segments.push(_jsx(Text, { color: curInCmd ? theme.commandColor : theme.text, bold: curInCmd, inverse: cursorVisible, children: cursorChar }, "cursor"));
|
|
840
|
+
const afterCursorPos = col + (col < line.length ? 1 : 0);
|
|
841
|
+
if (afterCursorPos < selLocalEnd) {
|
|
842
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(line.slice(afterCursorPos, selLocalEnd), lineStartOffset + afterCursorPos, { inverse: true }) }, "sel-after"));
|
|
843
|
+
}
|
|
844
|
+
pos = selLocalEnd;
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
// Cursor not on this selection portion — render entire selection inverse
|
|
848
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(line.slice(pos, selLocalEnd), lineStartOffset + pos, {
|
|
849
|
+
inverse: true,
|
|
850
|
+
}) }, "sel"));
|
|
851
|
+
pos = selLocalEnd;
|
|
852
|
+
}
|
|
853
|
+
// Cursor after selection on this line
|
|
854
|
+
if (showCursor && col >= selLocalEnd) {
|
|
855
|
+
// Text between selection end and cursor
|
|
856
|
+
if (col > pos) {
|
|
857
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(line.slice(pos, col), lineStartOffset + pos) }, "mid"));
|
|
858
|
+
}
|
|
859
|
+
const cursorChar = col < line.length ? line[col] : " ";
|
|
860
|
+
const cursorAbs = lineStartOffset + col;
|
|
861
|
+
const curInCmd = isCommand && cursorAbs < commandEndIndex;
|
|
862
|
+
segments.push(_jsx(Text, { color: curInCmd ? theme.commandColor : theme.text, bold: curInCmd, inverse: cursorVisible, children: cursorChar }, "cursor"));
|
|
863
|
+
pos = col + (col < line.length ? 1 : 0);
|
|
864
|
+
}
|
|
865
|
+
// Text after selection (and cursor)
|
|
866
|
+
if (pos < line.length) {
|
|
867
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(line.slice(pos), lineStartOffset + pos) }, "post"));
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
// No selection on this line — original cursor-only rendering
|
|
872
|
+
const before = showCursor ? line.slice(0, col) : line;
|
|
873
|
+
const charUnderCursor = showCursor ? (col < line.length ? line[col] : " ") : "";
|
|
874
|
+
const after = showCursor ? line.slice(col + (col < line.length ? 1 : 0)) : "";
|
|
875
|
+
const cursorCharOffset = lineStartOffset + col;
|
|
876
|
+
const cursorInCommand = isCommand && cursorCharOffset < commandEndIndex;
|
|
877
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(before, lineStartOffset) }, "before"));
|
|
878
|
+
if (showCursor) {
|
|
879
|
+
segments.push(_jsx(Text, { color: cursorInCommand ? theme.commandColor : theme.text, bold: cursorInCommand, inverse: cursorVisible, children: charUnderCursor }, "cursor"));
|
|
880
|
+
}
|
|
881
|
+
if (after) {
|
|
882
|
+
segments.push(_jsx(React.Fragment, { children: renderSegment(after, lineStartOffset + col + (col < line.length ? 1 : 0)) }, "after"));
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return (_jsxs(Box, { children: [_jsx(Text, { color: disabled ? theme.textDim : theme.inputPrompt, bold: true, children: i === 0 ? PROMPT : " " }), segments] }, i));
|
|
472
886
|
});
|
|
473
887
|
})()] }), isSlashMode && filteredCommands.length > 0 && (_jsx(SlashCommandMenu, { commands: commands, filter: slashFilter, selectedIndex: menuIndex }))] }));
|
|
474
888
|
}
|