fix(hermes): preserve paused editors during terminal reattachment
This commit is contained in:
parent
15c79c6da4
commit
cfcd932065
@ -1623,9 +1623,12 @@ RUN case "${HERMES_KANIKO_HEREDOC_COMPAT}" in 0) ;; 1) python /tmp/hermes-kaniko
|
|||||||
# alternate screen and current viewport after a bounded replay.
|
# alternate screen and current viewport after a bounded replay.
|
||||||
COPY dockerfiles/patch-hermes-terminal-replay.py /tmp/patch-hermes-terminal-replay.py
|
COPY dockerfiles/patch-hermes-terminal-replay.py /tmp/patch-hermes-terminal-replay.py
|
||||||
COPY dockerfiles/hermes-terminal-replay-regression.py /tmp/hermes-terminal-replay-regression.py
|
COPY dockerfiles/hermes-terminal-replay-regression.py /tmp/hermes-terminal-replay-regression.py
|
||||||
|
COPY dockerfiles/hermes-terminal-resume-regression.js /tmp/hermes-terminal-resume-regression.js
|
||||||
RUN /opt/hermes/.venv/bin/python /tmp/patch-hermes-terminal-replay.py \
|
RUN /opt/hermes/.venv/bin/python /tmp/patch-hermes-terminal-replay.py \
|
||||||
&& HERMES_SOURCE_ROOT=/opt/hermes /opt/hermes/.venv/bin/python \
|
&& HERMES_SOURCE_ROOT=/opt/hermes /opt/hermes/.venv/bin/python \
|
||||||
/tmp/hermes-terminal-replay-regression.py
|
/tmp/hermes-terminal-replay-regression.py \
|
||||||
|
&& node /tmp/hermes-terminal-resume-regression.js \
|
||||||
|
&& node --check /opt/hermes/ui-tui/dist/entry.js
|
||||||
|
|
||||||
# The dashboard keeps keyboard and paste handling unchanged, but forwards only
|
# The dashboard keeps keyboard and paste handling unchanged, but forwards only
|
||||||
# bounded SGR wheel reports when the TUI has explicitly enabled mouse tracking.
|
# bounded SGR wheel reports when the TUI has explicitly enabled mouse tracking.
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
!dockerfiles/hermes-session-migrate.py
|
!dockerfiles/hermes-session-migrate.py
|
||||||
!dockerfiles/patch-hermes-terminal-replay.py
|
!dockerfiles/patch-hermes-terminal-replay.py
|
||||||
!dockerfiles/hermes-terminal-replay-regression.py
|
!dockerfiles/hermes-terminal-replay-regression.py
|
||||||
|
!dockerfiles/hermes-terminal-resume-regression.js
|
||||||
!dockerfiles/patch-hermes-dashboard-wheel.js
|
!dockerfiles/patch-hermes-dashboard-wheel.js
|
||||||
!dockerfiles/hermes-dashboard-terminal-input.ts
|
!dockerfiles/hermes-dashboard-terminal-input.ts
|
||||||
!dockerfiles/hermes-dashboard-terminal-input.test.ts
|
!dockerfiles/hermes-dashboard-terminal-input.test.ts
|
||||||
|
|||||||
33
dockerfiles/hermes-terminal-resume-regression.js
Normal file
33
dockerfiles/hermes-terminal-resume-regression.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/** Exercise the pinned source and shipped SIGCONT handlers without starting Hermes. */
|
||||||
|
const assert = require("node:assert/strict");
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const path = require("node:path");
|
||||||
|
const root = process.env.HERMES_SOURCE_ROOT || "/opt/hermes";
|
||||||
|
|
||||||
|
const handlers = [
|
||||||
|
["ui-tui/packages/hermes-ink/src/ink/ink.tsx", /^ private handleResume = \(\) => \{[\s\S]*?^ }/m],
|
||||||
|
["ui-tui/dist/entry.js", /^ handleResume = \(\) => \{[\s\S]*?^ };/m],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const [file, pattern] of handlers) {
|
||||||
|
const source = fs.readFileSync(path.join(root, file), "utf8");
|
||||||
|
const match = source.match(pattern);
|
||||||
|
assert.ok(match, `missing pinned SIGCONT handler: ${file}`);
|
||||||
|
// Evaluate only the reviewed handler, with renderer effects replaced by spies.
|
||||||
|
const Renderer = new Function(`return class { ${match[0].replace("private ", "")} }`)();
|
||||||
|
for (const [isTTY, isPaused, expected] of [
|
||||||
|
[true, false, 1],
|
||||||
|
[true, true, 0],
|
||||||
|
[false, false, 0],
|
||||||
|
]) {
|
||||||
|
const renderer = new Renderer();
|
||||||
|
let repaints = 0;
|
||||||
|
renderer.options = { stdout: { isTTY } };
|
||||||
|
renderer.isPaused = isPaused;
|
||||||
|
renderer.altScreenActive = true;
|
||||||
|
renderer.reenterAltScreen = () => { repaints++; };
|
||||||
|
renderer.handleResume();
|
||||||
|
assert.equal(repaints, expected, `${file}: TTY=${isTTY}, paused=${isPaused}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("dashboard SIGCONT source and shipped handlers passed");
|
||||||
@ -62,6 +62,25 @@ def patch_sources(root: Path) -> None:
|
|||||||
" self._has_attached = True\n",
|
" self._has_attached = True\n",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
# The pinned image ships readable esbuild output without the TUI build
|
||||||
|
# dependencies. Keep its source and shipped handler in sync, and reject
|
||||||
|
# either anchor changing rather than resolving a new dependency graph.
|
||||||
|
"ui-tui/packages/hermes-ink/src/ink/ink.tsx": (
|
||||||
|
(
|
||||||
|
" private handleResume = () => {\n"
|
||||||
|
" if (!this.options.stdout.isTTY) {\n",
|
||||||
|
" private handleResume = () => {\n"
|
||||||
|
" if (!this.options.stdout.isTTY || this.isPaused) {\n",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"ui-tui/dist/entry.js": (
|
||||||
|
(
|
||||||
|
" handleResume = () => {\n"
|
||||||
|
" if (!this.options.stdout.isTTY) {\n",
|
||||||
|
" handleResume = () => {\n"
|
||||||
|
" if (!this.options.stdout.isTTY || this.isPaused) {\n",
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
prepared = {}
|
prepared = {}
|
||||||
for relative, replacements in patches.items():
|
for relative, replacements in patches.items():
|
||||||
@ -71,7 +90,8 @@ def patch_sources(root: Path) -> None:
|
|||||||
if source.count(before) != 1:
|
if source.count(before) != 1:
|
||||||
raise ValueError(f"Hermes terminal replay patch context changed: {relative}")
|
raise ValueError(f"Hermes terminal replay patch context changed: {relative}")
|
||||||
source = source.replace(before, after, 1)
|
source = source.replace(before, after, 1)
|
||||||
compile(source, str(path), "exec")
|
if path.suffix == ".py":
|
||||||
|
compile(source, str(path), "exec")
|
||||||
prepared[path] = source
|
prepared[path] = source
|
||||||
for path, source in prepared.items():
|
for path, source in prepared.items():
|
||||||
path.write_text(source)
|
path.write_text(source)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user