@code-yeongyu/senpi-tui 2026.7.25-2 → 2026.7.28
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/components/editor.d.ts +22 -6
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +56 -120
- package/dist/components/editor.js.map +1 -1
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js +7 -1
- package/dist/components/image.js.map +1 -1
- package/dist/components/select-list.d.ts +32 -0
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +15 -7
- package/dist/components/select-list.js.map +1 -1
- package/dist/editor-component.d.ts +18 -0
- package/dist/editor-component.d.ts.map +1 -1
- package/dist/editor-component.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/paste-markers.d.ts +29 -0
- package/dist/paste-markers.d.ts.map +1 -0
- package/dist/paste-markers.js +185 -0
- package/dist/paste-markers.js.map +1 -0
- package/dist/terminal-capabilities.d.ts +18 -0
- package/dist/terminal-capabilities.d.ts.map +1 -0
- package/dist/terminal-capabilities.js +62 -0
- package/dist/terminal-capabilities.js.map +1 -0
- package/dist/terminal-image.d.ts +31 -6
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js +137 -92
- package/dist/terminal-image.js.map +1 -1
- package/dist/tmux-focus.d.ts +8 -0
- package/dist/tmux-focus.d.ts.map +1 -0
- package/dist/tmux-focus.js +14 -0
- package/dist/tmux-focus.js.map +1 -0
- package/dist/tmux-image-capability.d.ts +17 -0
- package/dist/tmux-image-capability.d.ts.map +1 -0
- package/dist/tmux-image-capability.js +61 -0
- package/dist/tmux-image-capability.js.map +1 -0
- package/dist/tmux-image-probe.d.ts +39 -0
- package/dist/tmux-image-probe.d.ts.map +1 -0
- package/dist/tmux-image-probe.js +121 -0
- package/dist/tmux-image-probe.js.map +1 -0
- package/dist/tui.d.ts +3 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +63 -33
- package/dist/tui.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +19 -0
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/dist/tui.js
CHANGED
|
@@ -8,7 +8,8 @@ import { performance } from "node:perf_hooks";
|
|
|
8
8
|
import { isKeyRelease, matchesKey } from "./keys.js";
|
|
9
9
|
import { isMultiplexerSession, useLegacyMuxRender, viewportRenderEnabled } from "./mux.js";
|
|
10
10
|
import { isOsc11BackgroundColorResponse, parseOsc11BackgroundColor, parseTerminalColorSchemeReport, } from "./terminal-colors.js";
|
|
11
|
-
import { deleteKittyImage, getCapabilities, isImageLine, setCellDimensions } from "./terminal-image.js";
|
|
11
|
+
import { deleteKittyImage, getCapabilities, isImageLine, resetCapabilitiesCache, setCellDimensions, } from "./terminal-image.js";
|
|
12
|
+
import { consumeTmuxFocusEvent, DISABLE_FOCUS_REPORTING, ENABLE_FOCUS_REPORTING } from "./tmux-focus.js";
|
|
12
13
|
import { extractSegments, normalizeTerminalOutput, sliceByColumn, sliceWithWidth, visibleWidth } from "./utils.js";
|
|
13
14
|
const KITTY_SEQUENCE_PREFIX = "\x1b_G";
|
|
14
15
|
function parseKittyImageHeader(line) {
|
|
@@ -58,6 +59,7 @@ export const CURSOR_MARKER = "\x1b_pi:c\x07";
|
|
|
58
59
|
export { visibleWidth };
|
|
59
60
|
const renderErrorLoggedClasses = new Set();
|
|
60
61
|
let renderErrorLogWrites = 0;
|
|
62
|
+
let renderDiagnosticLineScans = 0;
|
|
61
63
|
const DIAGNOSTIC_LOG_MODE = 0o600;
|
|
62
64
|
const VIEWPORT_RENDER_OVERSCAN = 16;
|
|
63
65
|
// Keep scroll-region wins cheap when a few visible rows mutate during append streaming.
|
|
@@ -77,6 +79,12 @@ export function __renderErrorLogStats() {
|
|
|
77
79
|
}
|
|
78
80
|
return { writes: renderErrorLogWrites };
|
|
79
81
|
}
|
|
82
|
+
export function __renderDiagnosticStats() {
|
|
83
|
+
if (process.env.PI_TUI_TEST_SEAMS !== "1") {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
return { linesScanned: renderDiagnosticLineScans };
|
|
87
|
+
}
|
|
80
88
|
export function __viewportRenderStats() {
|
|
81
89
|
if (process.env.PI_TUI_TEST_SEAMS !== "1") {
|
|
82
90
|
return undefined;
|
|
@@ -142,6 +150,18 @@ function writeRenderDiagnosticBestEffort(logPath, data) {
|
|
|
142
150
|
throw error;
|
|
143
151
|
}
|
|
144
152
|
}
|
|
153
|
+
function formatOverWideRenderDiagnostic(lines, terminalWidth, lineIndex, lineWidth) {
|
|
154
|
+
renderDiagnosticLineScans += lines.length;
|
|
155
|
+
return [
|
|
156
|
+
`Crash at ${new Date().toISOString()}`,
|
|
157
|
+
`Terminal width: ${terminalWidth}`,
|
|
158
|
+
`Line ${lineIndex} visible width: ${lineWidth}`,
|
|
159
|
+
"",
|
|
160
|
+
"=== All rendered lines ===",
|
|
161
|
+
...lines.map((line, index) => `[${index}] (w=${visibleWidth(line)}) ${line}`),
|
|
162
|
+
"",
|
|
163
|
+
].join("\n");
|
|
164
|
+
}
|
|
145
165
|
function chmodDiagnosticLogBestEffort(logPath) {
|
|
146
166
|
try {
|
|
147
167
|
fs.chmodSync(logPath, DIAGNOSTIC_LOG_MODE);
|
|
@@ -576,6 +596,8 @@ export class TUI extends Container {
|
|
|
576
596
|
this.inputRenderPending = false;
|
|
577
597
|
this.#lastCursorVisibility = undefined;
|
|
578
598
|
this.terminal.start((data) => this.handleInput(data), () => this.requestRender());
|
|
599
|
+
if (process.env.TMUX)
|
|
600
|
+
this.terminal.write(ENABLE_FOCUS_REPORTING);
|
|
579
601
|
this.#setCursorVisibility(false);
|
|
580
602
|
if (this.terminalColorSchemeNotificationsEnabled) {
|
|
581
603
|
this.terminal.write("\x1b[?2031h");
|
|
@@ -643,6 +665,8 @@ export class TUI extends Container {
|
|
|
643
665
|
}
|
|
644
666
|
this.#lastCursorVisibility = undefined;
|
|
645
667
|
this.#setCursorVisibility(true);
|
|
668
|
+
if (process.env.TMUX)
|
|
669
|
+
this.terminal.write(DISABLE_FOCUS_REPORTING);
|
|
646
670
|
this.terminal.stop();
|
|
647
671
|
this.#lastCursorVisibility = undefined;
|
|
648
672
|
this.previousLines = [];
|
|
@@ -749,6 +773,15 @@ export class TUI extends Container {
|
|
|
749
773
|
this.doRender();
|
|
750
774
|
}
|
|
751
775
|
handleInput(data) {
|
|
776
|
+
const focus = consumeTmuxFocusEvent(data);
|
|
777
|
+
if (focus.event !== null) {
|
|
778
|
+
resetCapabilitiesCache();
|
|
779
|
+
this.invalidate();
|
|
780
|
+
this.requestRender(true);
|
|
781
|
+
if (focus.data.length === 0)
|
|
782
|
+
return;
|
|
783
|
+
data = focus.data;
|
|
784
|
+
}
|
|
752
785
|
if (this.consumeOsc11BackgroundResponse(data)) {
|
|
753
786
|
return;
|
|
754
787
|
}
|
|
@@ -1350,8 +1383,11 @@ export class TUI extends Container {
|
|
|
1350
1383
|
}
|
|
1351
1384
|
/** Splice overlay content into a base line at a specific column. Single-pass optimized. */
|
|
1352
1385
|
compositeLineAt(baseLine, overlayLine, startCol, overlayWidth, totalWidth) {
|
|
1353
|
-
if (isImageLine(baseLine))
|
|
1386
|
+
if (isImageLine(baseLine) && visibleWidth(baseLine) === 0)
|
|
1354
1387
|
return baseLine;
|
|
1388
|
+
const placeholderIndex = baseLine.indexOf("\u{10eeee}");
|
|
1389
|
+
const protocolEnd = placeholderIndex === -1 ? -1 : baseLine.lastIndexOf("\x1b\\", placeholderIndex);
|
|
1390
|
+
const protocolPrefix = protocolEnd === -1 ? "" : baseLine.slice(0, protocolEnd + 2);
|
|
1355
1391
|
// Single pass through baseLine extracts both before and after segments
|
|
1356
1392
|
const afterStart = startCol + overlayWidth;
|
|
1357
1393
|
const base = extractSegments(baseLine, startCol, afterStart, totalWidth - afterStart, true);
|
|
@@ -1382,10 +1418,10 @@ export class TUI extends Container {
|
|
|
1382
1418
|
// - Edge cases in segment extraction
|
|
1383
1419
|
const resultWidth = visibleWidth(result);
|
|
1384
1420
|
if (resultWidth <= totalWidth) {
|
|
1385
|
-
return result;
|
|
1421
|
+
return protocolPrefix + result;
|
|
1386
1422
|
}
|
|
1387
1423
|
// Truncate with strict=true to ensure we don't exceed totalWidth
|
|
1388
|
-
return sliceByColumn(result, 0, totalWidth, true);
|
|
1424
|
+
return protocolPrefix + sliceByColumn(result, 0, totalWidth, true);
|
|
1389
1425
|
}
|
|
1390
1426
|
/**
|
|
1391
1427
|
* Find and extract cursor position from rendered lines.
|
|
@@ -1758,36 +1794,30 @@ export class TUI extends Container {
|
|
|
1758
1794
|
buffer += `\x1b[2K${TUI.SEGMENT_RESET}`; // Clear current line
|
|
1759
1795
|
const lineWidth = visibleWidth(line);
|
|
1760
1796
|
if (!isImage && lineWidth > width) {
|
|
1761
|
-
// Log all lines to crash file for debugging
|
|
1762
1797
|
const crashLogPath = path.join(os.homedir(), ".senpi", "agent", "senpi-crash.log");
|
|
1763
|
-
const
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
throw new Error(errorMsg);
|
|
1787
|
-
}
|
|
1788
|
-
if (!this.overWideCrashDumpWritten) {
|
|
1789
|
-
writeRenderDiagnosticBestEffort(crashLogPath, crashData);
|
|
1790
|
-
this.overWideCrashDumpWritten = true;
|
|
1798
|
+
const strictRender = process.env.PI_TUI_STRICT_RENDER === "1";
|
|
1799
|
+
if (strictRender || !this.overWideCrashDumpWritten) {
|
|
1800
|
+
const crashData = formatOverWideRenderDiagnostic(newLines, width, i, lineWidth);
|
|
1801
|
+
if (!strictRender) {
|
|
1802
|
+
writeRenderDiagnosticBestEffort(crashLogPath, crashData);
|
|
1803
|
+
this.overWideCrashDumpWritten = true;
|
|
1804
|
+
}
|
|
1805
|
+
else {
|
|
1806
|
+
const crashDumpWritten = writeRenderDiagnosticBestEffort(crashLogPath, crashData);
|
|
1807
|
+
// Clean up terminal state before throwing
|
|
1808
|
+
this.stop();
|
|
1809
|
+
const errorMsg = [
|
|
1810
|
+
`Rendered line ${i} exceeds terminal width (${lineWidth} > ${width}).`,
|
|
1811
|
+
"",
|
|
1812
|
+
"This is likely caused by a custom TUI component not truncating its output.",
|
|
1813
|
+
"Use visibleWidth() to measure and truncateToWidth() to truncate lines.",
|
|
1814
|
+
"",
|
|
1815
|
+
crashDumpWritten
|
|
1816
|
+
? `Debug log written to: ${crashLogPath}`
|
|
1817
|
+
: `Debug log could not be written to: ${crashLogPath}`,
|
|
1818
|
+
].join("\n");
|
|
1819
|
+
throw new Error(errorMsg);
|
|
1820
|
+
}
|
|
1791
1821
|
}
|
|
1792
1822
|
const truncatedLine = sliceByColumn(line, 0, width, true) + TUI.SEGMENT_RESET;
|
|
1793
1823
|
newLines[i] = truncatedLine;
|