mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 00:49:41 +02:00
1. Regex global flag safety — strip `g` flag before matching to prevent
lastIndex mutation and missing .index on match results
2. state.visible false before activate — null overlay no longer reports
visible: true
3. removeChar() returns 'pending' | 'flushed' | false instead of boolean
so consumers can distinguish whether to send backspace to PTY
4. removeChar() implements buffer detection cascade (step 3) — detects
existing prompt text when both pending and flushed are empty
5. RenderKey includes text content, not just length — prevents stale
renders when setFlushed() called with same count but different text
6. Remove dead `import type { Terminal }` from test file
7. Remove internal `FontStyle` from public exports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
import type { XtermTerminal, PromptFinder, PromptPosition } from './types.js';
|
|
|
|
/**
|
|
* Find the prompt in the terminal buffer using the configured strategy.
|
|
* Scans bottom-up through the viewport to find the most recent prompt.
|
|
*
|
|
* @returns The prompt position (viewport-relative), or `null` if not found.
|
|
*/
|
|
export function findPrompt(
|
|
terminal: XtermTerminal,
|
|
finder: PromptFinder,
|
|
): PromptPosition | null {
|
|
try {
|
|
const buffer = terminal.buffer.active;
|
|
const viewportTop = buffer.viewportY;
|
|
|
|
switch (finder.type) {
|
|
case 'character': {
|
|
for (let row = terminal.rows - 1; row >= 0; row--) {
|
|
const line = buffer.getLine(viewportTop + row);
|
|
if (!line) continue;
|
|
const text = line.translateToString(true);
|
|
const idx = text.lastIndexOf(finder.char);
|
|
if (idx >= 0) return { row, col: idx };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
case 'regex': {
|
|
// Create a fresh non-global regex to avoid lastIndex mutation
|
|
// and ensure .match() returns a single result with .index
|
|
const pattern = finder.pattern;
|
|
const safePattern = pattern.global
|
|
? new RegExp(pattern.source, pattern.flags.replace('g', ''))
|
|
: pattern;
|
|
for (let row = terminal.rows - 1; row >= 0; row--) {
|
|
const line = buffer.getLine(viewportTop + row);
|
|
if (!line) continue;
|
|
const text = line.translateToString(true);
|
|
const match = text.match(safePattern);
|
|
if (match) {
|
|
const col = match.index ?? 0;
|
|
return { row, col };
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
case 'custom':
|
|
return finder.find(terminal);
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read text after the prompt position on the same line.
|
|
*
|
|
* @param terminal - The xterm.js terminal instance
|
|
* @param prompt - The prompt position in the viewport
|
|
* @param offset - Characters to skip after the prompt marker (e.g., 2 for "> ")
|
|
* @returns The text after the prompt, trimmed. Empty string if nothing found.
|
|
*/
|
|
export function readTextAfterPrompt(
|
|
terminal: XtermTerminal,
|
|
prompt: PromptPosition,
|
|
offset: number,
|
|
): string {
|
|
try {
|
|
const buffer = terminal.buffer.active;
|
|
const absRow = buffer.viewportY + prompt.row;
|
|
const line = buffer.getLine(absRow);
|
|
if (!line) return '';
|
|
const lineText = line.translateToString(true);
|
|
return lineText.slice(prompt.col + offset).trimEnd();
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|