codemaxxing 1.0.12 → 1.0.14
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 +14 -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
|
@@ -3,7 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
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
|
+
import { appendFileSync } from "node:fs";
|
|
6
7
|
import TextInput from "ink-text-input";
|
|
8
|
+
import { consumePendingPasteEndMarkerChunk } from "./utils/paste.js";
|
|
7
9
|
import { CodingAgent } from "./agent.js";
|
|
8
10
|
import { loadConfig, saveConfig, detectLocalProvider, detectLocalProviderDetailed, parseCLIArgs, applyOverrides, listModels } from "./config.js";
|
|
9
11
|
import { listSessions, getSession, loadMessages, deleteSession } from "./utils/sessions.js";
|
|
@@ -1757,13 +1759,13 @@ let bracketedBuffer = "";
|
|
|
1757
1759
|
let inBracketedPaste = false;
|
|
1758
1760
|
let burstBuffer = "";
|
|
1759
1761
|
let burstTimer = null;
|
|
1762
|
+
let pendingPasteEndMarker = { active: false, buffer: "" };
|
|
1760
1763
|
const BURST_WINDOW_MS = 50; // Long enough for slow terminals to finish delivering paste
|
|
1761
1764
|
// Debug paste: set CODEMAXXING_DEBUG_PASTE=1 to log all stdin chunks to /tmp/codemaxxing-paste-debug.log
|
|
1762
1765
|
const PASTE_DEBUG = process.env.CODEMAXXING_DEBUG_PASTE === "1";
|
|
1763
1766
|
function pasteLog(msg) {
|
|
1764
1767
|
if (!PASTE_DEBUG)
|
|
1765
1768
|
return;
|
|
1766
|
-
const { appendFileSync } = require("node:fs");
|
|
1767
1769
|
const escaped = msg.replace(/\x1b/g, "\\x1b").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
1768
1770
|
try {
|
|
1769
1771
|
appendFileSync("/tmp/codemaxxing-paste-debug.log", `[${Date.now()}] ${escaped}\n`);
|
|
@@ -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
|
}
|
|
@@ -1826,6 +1832,13 @@ process.stdin.emit = function (event, ...args) {
|
|
|
1826
1832
|
const chunk = args[0];
|
|
1827
1833
|
let data = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : String(chunk);
|
|
1828
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
|
+
}
|
|
1829
1842
|
// Aggressively strip ALL bracketed paste escape sequences from every chunk,
|
|
1830
1843
|
// regardless of context. Some terminals split markers across chunks or send
|
|
1831
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
|
+
}
|