From 8142d72ae068173fe9c1084e2655c572c4630e4f Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 6 Sep 2026 20:25:15 -0700 Subject: [PATCH] test: run captured OpenCode redraw replay from a committed fixture --- .../opencode-small-redraw-capture.json | 16 ++++++++++++ .../terminal-foreground-redraw-freeze.spec.ts | 25 ++++++++++--------- 2 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 tests/e2e/fixtures/opencode-small-redraw-capture.json diff --git a/tests/e2e/fixtures/opencode-small-redraw-capture.json b/tests/e2e/fixtures/opencode-small-redraw-capture.json new file mode 100644 index 00000000000..bcf92a6a58a --- /dev/null +++ b/tests/e2e/fixtures/opencode-small-redraw-capture.json @@ -0,0 +1,16 @@ +{ + "provenance": { + "description": "Synthetic OpenCode spinner harness; no live agent or user session data.", + "sourceCommit": "e207624c48159b03dbe17dbc8e51bbcf23e72df5", + "spinnerSource": "packages/tui/src/ui/spinner.ts", + "opentuiCore": "0.4.5", + "opentuiSpinner": "0.0.7", + "generator": "tests/e2e/capture-opencode-tui-repro.mjs", + "setup": "Copy the pinned spinner source into src/cli/cmd/tui/ui/spinner.ts in a disposable package containing the two pinned dependencies, then pass its directory with --opencode-path.", + "cols": 120, + "rows": 40, + "durationMs": 5000, + "sha256": "025014c167b6e7407d60a8ec096c275a351bb7eed6caa3d5c89ee6ca249e6b76" + }, + "capture": "\u001b[?2031h\u001b]10;?\u0007\u001b]11;?\u0007\u001b[>0q\u001b[?25l\u001b[s\u001b[6n\u001bP+q4d73\u001b\\\u001b[?1016$p\u001b[?2027$p\u001b[?2031$p\u001b[?1004$p\u001b[?2004$p\u001b[?2026$p\u001b[?u\u001b]99;i=opentui-notifications:p=?;\u001b\\\u001b]1337;Capabilities\u001b\\\u001b[H\u001b]66;w=1; \u001b\\\u001b[6n\u001b[H\u001b]66;s=2; \u001b\\\u001b[6n\u001b[u\u001b[s\u001b[?1049h\u001b[>4;1m\u001b[?2027h\u001b[?2004h\u001b[14t\u001b]4;0;?\u0007\u001b[?2026h\u001b[?25l\u001b[1;1H\u001b[38;5;15m\u001b[49m \u001b[2;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[2;3H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;11H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[2;12H\u001b[38;5;208m\u001b[49mOpenCode synthetic active TUI redraw\u001b[0m\u001b[2;48H\u001b[38;5;15m\u001b[49m \u001b[3;1H\u001b[38;5;15m\u001b[49m \u001b[4;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[4;3H\u001b[38;5;208m\u001b[49m01\u001b[0m\u001b[4;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[4;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[4;43H\u001b[38;5;15m\u001b[49m \u001b[5;1H\u001b[38;5;15m\u001b[49m \u001b[6;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[6;3H\u001b[38;5;75m\u001b[49m02\u001b[0m\u001b[6;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[6;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[6;43H\u001b[38;5;15m\u001b[49m \u001b[7;1H\u001b[38;5;15m\u001b[49m \u001b[8;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[8;3H\u001b[38;5;208m\u001b[49m03\u001b[0m\u001b[8;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[8;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[8;43H\u001b[38;5;15m\u001b[49m \u001b[9;1H\u001b[38;5;15m\u001b[49m \u001b[10;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[10;3H\u001b[38;5;75m\u001b[49m04\u001b[0m\u001b[10;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[10;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[10;43H\u001b[38;5;15m\u001b[49m \u001b[11;1H\u001b[38;5;15m\u001b[49m \u001b[12;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[12;3H\u001b[38;5;208m\u001b[49m05\u001b[0m\u001b[12;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[12;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[12;43H\u001b[38;5;15m\u001b[49m \u001b[13;1H\u001b[38;5;15m\u001b[49m \u001b[14;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[14;3H\u001b[38;5;75m\u001b[49m06\u001b[0m\u001b[14;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[14;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[14;43H\u001b[38;5;15m\u001b[49m \u001b[15;1H\u001b[38;5;15m\u001b[49m \u001b[16;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[16;3H\u001b[38;5;208m\u001b[49m07\u001b[0m\u001b[16;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[16;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[16;43H\u001b[38;5;\u001b[1m148 | \u001b[0m \u001b[0m\u001b[32m\"bench:terminal-partial-escape-tail\"\u001b[0m: \u001b[0m\u001b[32m\"node config/scripts/terminal-partial-esca\u001b[0m\r\n \u001b[2m\u001b[1m\u001b[2m\u001b[1m^\u001b[0m\r\n\u001b[2mwarn\u001b[0m\u001b[2m: \u001b[0m\u001b[2m\u001b[1mDuplicate key \"bench:terminal-partial-escape-tail\" in object literal\u001b[0m\r\n \u001b[2mat \u001b[0m\u001b[36m/Users/nwparker/orca/workspaces/orca/deflake-tests/package.json\u001b[0m\u001b[2m:\u001b[0m\u001b[33m148\u001b[0m\u001b[2m:\u001b[0m\u001b[33m5\u001b[0m\r\n15m\u001b[49m \u001b[17;1H\u001b[38;5;15m\u001b[49m \u001b[18;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[18;3H\u001b[38;5;75m\u001b[49m08\u001b[0m\u001b[18;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[18;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[18;43H\u001b[38;5;15m\u001b[49m \u001b[19;1H\u001b[38;5;15m\u001b[49m \u001b[20;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[20;3H\u001b[38;5;208m\u001b[49m09\u001b[0m\u001b[20;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[20;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[20;43H\u001b[38;5;15m\u001b[49m \u001b[21;1H\u001b[38;5;15m\u001b[49m \u001b[22;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[22;3H\u001b[38;5;75m\u001b[49m10\u001b[0m\u001b[22;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[22;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[22;43H\u001b[38;5;15m\u001b[49m \u001b[23;1H\u001b[38;5;15m\u001b[49m \u001b[24;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[24;3H\u001b[38;5;208m\u001b[49m11\u001b[0m\u001b[24;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[24;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[24;43H\u001b[38;5;15m\u001b[49m \u001b[25;1H\u001b[38;5;15m\u001b[49m \u001b[26;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[26;3H\u001b[38;5;75m\u001b[49m12\u001b[0m\u001b[26;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[26;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[26;43H\u001b[38;5;15m\u001b[49m \u001b[27;1H\u001b[38;5;15m\u001b[49m \u001b[28;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[28;3H\u001b[38;5;208m\u001b[49m13\u001b[0m\u001b[28;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[28;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[28;43H\u001b[38;5;15m\u001b[49m \u001b[29;1H\u001b[38;5;15m\u001b[49m \u001b[30;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[30;3H\u001b[38;5;75m\u001b[49m14\u001b[0m\u001b[30;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[30;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[30;43H\u001b[38;5;15m\u001b[49m \u001b[31;1H\u001b[38;5;15m\u001b[49m \u001b[32;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[32;3H\u001b[38;5;208m\u001b[49m15\u001b[0m\u001b[32;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[32;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[32;43H\u001b[38;5;15m\u001b[49m \u001b[33;1H\u001b[38;5;15m\u001b[49m \u001b[34;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[34;3H\u001b[38;5;75m\u001b[49m16\u001b[0m\u001b[34;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[34;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[34;43H\u001b[38;5;15m\u001b[49m \u001b[35;1H\u001b[38;5;15m\u001b[49m \u001b[36;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[36;3H\u001b[38;5;208m\u001b[49m17\u001b[0m\u001b[36;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[36;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[36;43H\u001b[38;5;15m\u001b[49m \u001b[37;1H\u001b[38;5;15m\u001b[49m \u001b[38;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[38;3H\u001b[38;5;75m\u001b[49m18\u001b[0m\u001b[38;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[38;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[38;43H\u001b[38;5;15m\u001b[49m \u001b[39;1H\u001b[38;5;15m\u001b[49m \u001b[40;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[40;3H\u001b[38;5;208m\u001b[49m19\u001b[0m\u001b[40;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[40;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[40;43H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[1;1H\u001b[38;5;15m\u001b[49m \u001b[2;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;11H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[2;12H\u001b[38;5;208m\u001b[49mOpenCode synthetic active TUI redraw\u001b[0m\u001b[2;48H\u001b[38;5;15m\u001b[49m \u001b[3;1H\u001b[38;5;15m\u001b[49m \u001b[4;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[4;3H\u001b[38;5;208m\u001b[49m01\u001b[0m\u001b[4;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[4;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[4;43H\u001b[38;5;15m\u001b[49m \u001b[5;1H\u001b[38;5;15m\u001b[49m \u001b[6;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[6;3H\u001b[38;5;75m\u001b[49m02\u001b[0m\u001b[6;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[6;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[6;43H\u001b[38;5;15m\u001b[49m \u001b[7;1H\u001b[38;5;15m\u001b[49m \u001b[8;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[8;3H\u001b[38;5;208m\u001b[49m03\u001b[0m\u001b[8;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[8;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[8;43H\u001b[38;5;15m\u001b[49m \u001b[9;1H\u001b[38;5;15m\u001b[49m \u001b[10;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[10;3H\u001b[38;5;75m\u001b[49m04\u001b[0m\u001b[10;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[10;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[10;43H\u001b[38;5;15m\u001b[49m \u001b[11;1H\u001b[38;5;15m\u001b[49m \u001b[12;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[12;3H\u001b[38;5;208m\u001b[49m05\u001b[0m\u001b[12;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[12;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[12;43H\u001b[38;5;15m\u001b[49m \u001b[13;1H\u001b[38;5;15m\u001b[49m \u001b[14;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[14;3H\u001b[38;5;75m\u001b[49m06\u001b[0m\u001b[14;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[14;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[14;43H\u001b[38;5;15m\u001b[49m \u001b[15;1H\u001b[38;5;15m\u001b[49m \u001b[16;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[16;3H\u001b[38;5;208m\u001b[49m07\u001b[0m\u001b[16;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[16;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[16;43H\u001b[38;5;15m\u001b[49m \u001b[17;1H\u001b[38;5;15m\u001b[49m \u001b[18;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[18;3H\u001b[38;5;75m\u001b[49m08\u001b[0m\u001b[18;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[18;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[18;43H\u001b[38;5;15m\u001b[49m \u001b[19;1H\u001b[38;5;15m\u001b[49m \u001b[20;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[20;3H\u001b[38;5;208m\u001b[49m09\u001b[0m\u001b[20;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[20;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[20;43H\u001b[38;5;15m\u001b[49m \u001b[21;1H\u001b[38;5;15m\u001b[49m \u001b[22;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[22;3H\u001b[38;5;75m\u001b[49m10\u001b[0m\u001b[22;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[22;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[22;43H\u001b[38;5;15m\u001b[49m \u001b[23;1H\u001b[38;5;15m\u001b[49m \u001b[24;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[24;3H\u001b[38;5;208m\u001b[49m11\u001b[0m\u001b[24;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[24;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[24;43H\u001b[38;5;15m\u001b[49m \u001b[25;1H\u001b[38;5;15m\u001b[49m \u001b[26;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[26;3H\u001b[38;5;75m\u001b[49m12\u001b[0m\u001b[26;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[26;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[26;43H\u001b[38;5;15m\u001b[49m \u001b[27;1H\u001b[38;5;15m\u001b[49m \u001b[28;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[28;3H\u001b[38;5;208m\u001b[49m13\u001b[0m\u001b[28;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[28;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[28;43H\u001b[38;5;15m\u001b[49m \u001b[29;1H\u001b[38;5;15m\u001b[49m \u001b[30;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[30;3H\u001b[38;5;75m\u001b[49m14\u001b[0m\u001b[30;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[30;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[30;43H\u001b[38;5;15m\u001b[49m \u001b[31;1H\u001b[38;5;15m\u001b[49m \u001b[32;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[32;3H\u001b[38;5;208m\u001b[49m15\u001b[0m\u001b[32;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[32;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[32;43H\u001b[38;5;15m\u001b[49m \u001b[33;1H\u001b[38;5;15m\u001b[49m \u001b[34;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[34;3H\u001b[38;5;75m\u001b[49m16\u001b[0m\u001b[34;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[34;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[34;43H\u001b[38;5;15m\u001b[49m \u001b[35;1H\u001b[38;5;15m\u001b[49m \u001b[36;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[36;3H\u001b[38;5;208m\u001b[49m17\u001b[0m\u001b[36;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[36;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[36;43H\u001b[38;5;15m\u001b[49m \u001b[37;1H\u001b[38;5;15m\u001b[49m \u001b[38;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[38;3H\u001b[38;5;75m\u001b[49m18\u001b[0m\u001b[38;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[38;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[38;43H\u001b[38;5;15m\u001b[49m \u001b[39;1H\u001b[38;5;15m\u001b[49m \u001b[40;1H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[40;3H\u001b[38;5;208m\u001b[49m19\u001b[0m\u001b[40;5H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[40;6H\u001b[38;5;255m\u001b[49m#################################### \u001b[0m\u001b[40;43H\u001b[38;5;15m\u001b[49m \u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝\u001b[0m\u001b[2;6H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[2;7H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[2;8H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;9H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;10H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;9H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[2;8H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[2;7H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝\u001b[0m\u001b[2;6H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[2;5H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[2;4H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;94m\u001b[49m⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝\u001b[0m\u001b[2;6H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[2;7H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[2;8H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;9H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;10H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;9H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[2;8H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[2;7H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝\u001b[0m\u001b[2;6H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[2;5H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[2;4H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;94m\u001b[49m⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;4H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[2;4H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;5H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;94m\u001b[49m⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝\u001b[0m\u001b[2;5H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;6H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;208m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝\u001b[0m\u001b[2;6H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;7H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;172m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝\u001b[0m\u001b[2;7H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;8H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;130m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;94m\u001b[49m⬝⬝⬝⬝⬝\u001b[0m\u001b[2;8H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;9H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;58m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;9H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[2;10H\u001b[38;5;52m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;58m\u001b[49m⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[2;10H\u001b[38;5;234m\u001b[49m■\u001b[0m\u001b[0m\u001b[?2026l\u001b[?2026h\u001b[?25l\u001b[2;3H\u001b[38;5;52m\u001b[49m⬝⬝⬝⬝⬝⬝⬝⬝\u001b[0m\u001b[0m\u001b[?2026l\u001b[?25h\u001b[0m\u001b]22;\u0007\u001b[>4;0m\u001b[?2004l\u001b[?1049l\u001b[?2031l\u001b]0;\u0007\u001b]12;default\u0007\u001b]112\u0007\u001b[0 q\u001b[?25h\u001b[?25h" +} diff --git a/tests/e2e/terminal-foreground-redraw-freeze.spec.ts b/tests/e2e/terminal-foreground-redraw-freeze.spec.ts index 05630042fae..679533cbed2 100644 --- a/tests/e2e/terminal-foreground-redraw-freeze.spec.ts +++ b/tests/e2e/terminal-foreground-redraw-freeze.spec.ts @@ -1,4 +1,4 @@ -import { existsSync, readFileSync } from 'node:fs' +import { readFileSync } from 'node:fs' import path from 'node:path' import type { Page, TestInfo } from '@stablyai/playwright-test' import { test, expect } from './helpers/orca-app' @@ -8,8 +8,6 @@ import { waitForTerminalPtyDataInjector } from './helpers/terminal-pty-injection // Repro commands: // SKIP_BUILD=1 pnpm exec playwright test tests/e2e/terminal-foreground-redraw-freeze.spec.ts --config tests/playwright.config.ts --project electron-headless -g "active OpenTUI-style" -// git clone https://github.com/anomalyco/opencode.git .tmp/opencode -// node tests/e2e/capture-opencode-tui-repro.mjs // SKIP_BUILD=1 pnpm exec playwright test tests/e2e/terminal-foreground-redraw-freeze.spec.ts --config tests/playwright.config.ts --project electron-headless -g "captured OpenCode/OpenTUI" --reporter=json // The captured replay uses an artificial OpenCode source-tree harness that // imports OpenCode's spinner frames and emits real OpenTUI <=2KB redraw chunks. @@ -61,7 +59,13 @@ const TIMER_SAMPLE_MS = 16 const MAX_RENDERER_TIMER_DRIFT_MS = 500 const FOREGROUND_IMMEDIATE_BUDGET_CHARS = 128 * 1024 const OPENCODE_CAPTURE_REPLAY_CHARS = FOREGROUND_IMMEDIATE_BUDGET_CHARS * 64 -const OPENCODE_CAPTURE_PATH = path.join(process.cwd(), '.tmp', 'opencode-tui-capture.txt') +const OPENCODE_CAPTURE_PATH = path.join( + process.cwd(), + 'tests', + 'e2e', + 'fixtures', + 'opencode-small-redraw-capture.json' +) async function resetSchedulerDebug(page: Page): Promise { await page.evaluate(() => { @@ -297,10 +301,7 @@ async function disposeActivePaneRefreshProbe(page: Page): Promise { } function loadCapturedOpenCodeSmallRedrawFrames(): string[] { - if (!existsSync(OPENCODE_CAPTURE_PATH)) { - return [] - } - const capture = readFileSync(OPENCODE_CAPTURE_PATH, 'utf8') + const { capture } = JSON.parse(readFileSync(OPENCODE_CAPTURE_PATH, 'utf8')) as { capture: string } const smallFrames = capture .split('\x1b[?2026h') .slice(1) @@ -424,10 +425,10 @@ test.describe('Terminal foreground redraw freeze repro', () => { orcaPage }, testInfo) => { const frames = loadCapturedOpenCodeSmallRedrawFrames() - test.skip( - frames.length === 0, - `OpenCode PTY capture missing; run "git clone https://github.com/anomalyco/opencode.git .tmp/opencode" then "node tests/e2e/capture-opencode-tui-repro.mjs" to generate ${OPENCODE_CAPTURE_PATH}` - ) + expect( + frames.length, + 'committed OpenCode capture must contain small redraw frames' + ).toBeGreaterThan(0) await waitForSessionReady(orcaPage) await waitForActiveWorktree(orcaPage)