codemaxxing 1.0.11 → 1.0.13
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/index.js +22 -1
- package/dist/utils/paste.d.ts +9 -0
- package/dist/utils/paste.js +48 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import React, { useState, useEffect, useCallback } from "react";
|
|
|
4
4
|
import { render, Box, Text, useInput, useApp, useStdout } from "ink";
|
|
5
5
|
import { EventEmitter } from "events";
|
|
6
6
|
import TextInput from "ink-text-input";
|
|
7
|
+
import { consumePendingPasteEndMarkerChunk } from "./utils/paste.js";
|
|
7
8
|
import { CodingAgent } from "./agent.js";
|
|
8
9
|
import { loadConfig, saveConfig, detectLocalProvider, detectLocalProviderDetailed, parseCLIArgs, applyOverrides, listModels } from "./config.js";
|
|
9
10
|
import { listSessions, getSession, loadMessages, deleteSession } from "./utils/sessions.js";
|
|
@@ -1757,6 +1758,7 @@ let bracketedBuffer = "";
|
|
|
1757
1758
|
let inBracketedPaste = false;
|
|
1758
1759
|
let burstBuffer = "";
|
|
1759
1760
|
let burstTimer = null;
|
|
1761
|
+
let pendingPasteEndMarker = { active: false, buffer: "" };
|
|
1760
1762
|
const BURST_WINDOW_MS = 50; // Long enough for slow terminals to finish delivering paste
|
|
1761
1763
|
// Debug paste: set CODEMAXXING_DEBUG_PASTE=1 to log all stdin chunks to /tmp/codemaxxing-paste-debug.log
|
|
1762
1764
|
const PASTE_DEBUG = process.env.CODEMAXXING_DEBUG_PASTE === "1";
|
|
@@ -1778,6 +1780,10 @@ function handlePasteContent(content) {
|
|
|
1778
1780
|
const lineCount = normalized.split("\n").length;
|
|
1779
1781
|
if (lineCount > 2) {
|
|
1780
1782
|
// Real multiline paste → badge it
|
|
1783
|
+
// Some terminals dribble the closing bracketed-paste marker (`[201~`)
|
|
1784
|
+
// one character at a time *after* the paste payload. Arm a tiny
|
|
1785
|
+
// swallow-state so those trailing fragments never leak into the input.
|
|
1786
|
+
pendingPasteEndMarker = { active: true, buffer: "" };
|
|
1781
1787
|
pasteEvents.emit("paste", { content: normalized, lines: lineCount });
|
|
1782
1788
|
return;
|
|
1783
1789
|
}
|
|
@@ -1798,8 +1804,16 @@ function looksLikeMultilinePaste(data) {
|
|
|
1798
1804
|
function flushBurst() {
|
|
1799
1805
|
if (!burstBuffer)
|
|
1800
1806
|
return;
|
|
1801
|
-
|
|
1807
|
+
let buffered = burstBuffer;
|
|
1802
1808
|
burstBuffer = "";
|
|
1809
|
+
// Strip any bracketed paste marker fragments that accumulated across
|
|
1810
|
+
// individual character chunks (terminal sends [, 2, 0, 1, ~ separately)
|
|
1811
|
+
buffered = buffered.replace(/\x1b?\[?20[01]~/g, "");
|
|
1812
|
+
buffered = buffered.replace(/20[01]~/g, "");
|
|
1813
|
+
if (!buffered || !buffered.trim()) {
|
|
1814
|
+
pasteLog("BURST FLUSH stripped to empty — swallowed marker");
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1803
1817
|
const isMultiline = looksLikeMultilinePaste(buffered);
|
|
1804
1818
|
pasteLog(`BURST FLUSH len=${buffered.length} multiline=${isMultiline}`);
|
|
1805
1819
|
if (isMultiline) {
|
|
@@ -1818,6 +1832,13 @@ process.stdin.emit = function (event, ...args) {
|
|
|
1818
1832
|
const chunk = args[0];
|
|
1819
1833
|
let data = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : String(chunk);
|
|
1820
1834
|
pasteLog(`CHUNK len=${data.length} raw=${data.substring(0, 200)}`);
|
|
1835
|
+
const pendingResult = consumePendingPasteEndMarkerChunk(data, pendingPasteEndMarker);
|
|
1836
|
+
pendingPasteEndMarker = pendingResult.nextState;
|
|
1837
|
+
data = pendingResult.remaining;
|
|
1838
|
+
if (!data) {
|
|
1839
|
+
pasteLog("PENDING END MARKER swallowed chunk");
|
|
1840
|
+
return true;
|
|
1841
|
+
}
|
|
1821
1842
|
// Aggressively strip ALL bracketed paste escape sequences from every chunk,
|
|
1822
1843
|
// regardless of context. Some terminals split markers across chunks or send
|
|
1823
1844
|
// them in unexpected positions. We never want \x1b[200~ or \x1b[201~ (or
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface PendingPasteEndState {
|
|
2
|
+
active: boolean;
|
|
3
|
+
buffer: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function consumePendingPasteEndMarkerChunk(chunk: string, state: PendingPasteEndState): {
|
|
6
|
+
remaining: string;
|
|
7
|
+
nextState: PendingPasteEndState;
|
|
8
|
+
swallowed: boolean;
|
|
9
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const END_MARKERS = ["\x1b[201~", "[201~", "201~"];
|
|
2
|
+
function isPrefixOfAnyMarker(value) {
|
|
3
|
+
return END_MARKERS.some((marker) => marker.startsWith(value));
|
|
4
|
+
}
|
|
5
|
+
function stripLeadingFullMarkers(value) {
|
|
6
|
+
let remaining = value;
|
|
7
|
+
let stripped = false;
|
|
8
|
+
outer: while (remaining.length > 0) {
|
|
9
|
+
for (const marker of END_MARKERS) {
|
|
10
|
+
if (remaining.startsWith(marker)) {
|
|
11
|
+
remaining = remaining.slice(marker.length);
|
|
12
|
+
stripped = true;
|
|
13
|
+
continue outer;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
return { remaining, stripped };
|
|
19
|
+
}
|
|
20
|
+
export function consumePendingPasteEndMarkerChunk(chunk, state) {
|
|
21
|
+
if (!state.active) {
|
|
22
|
+
return { remaining: chunk, nextState: state, swallowed: false };
|
|
23
|
+
}
|
|
24
|
+
let combined = state.buffer + chunk;
|
|
25
|
+
let swallowed = false;
|
|
26
|
+
const stripped = stripLeadingFullMarkers(combined);
|
|
27
|
+
combined = stripped.remaining;
|
|
28
|
+
swallowed = stripped.stripped;
|
|
29
|
+
if (!combined) {
|
|
30
|
+
return {
|
|
31
|
+
remaining: "",
|
|
32
|
+
nextState: { active: false, buffer: "" },
|
|
33
|
+
swallowed,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (isPrefixOfAnyMarker(combined)) {
|
|
37
|
+
return {
|
|
38
|
+
remaining: "",
|
|
39
|
+
nextState: { active: true, buffer: combined },
|
|
40
|
+
swallowed: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
remaining: combined,
|
|
45
|
+
nextState: { active: false, buffer: "" },
|
|
46
|
+
swallowed,
|
|
47
|
+
};
|
|
48
|
+
}
|