feat(tui): retire the tui2 experiment (#9640)

## Summary
- Retire the experimental TUI2 implementation and its feature flag.
- Remove TUI2-only config/schema/docs so the CLI stays on the
terminal-native path.
- Keep docs aligned with the legacy TUI while we focus on redraw-based
improvements.

## Customer impact
- Retires the TUI2 experiment and keeps Codex on the proven
terminal-native UI while we invest in redraw-based improvements to the
existing experience.

## Migration / compatibility
- If you previously set tui2-related options in config.toml, they are
now ignored and Codex continues using the existing terminal-native TUI
(no action required).

## Context
- What worked: a transcript-owned viewport delivered excellent resize
rewrap and high-fidelity copy (especially for code).
- Why stop: making that experience feel fully native across the
environment matrix (terminal emulator, OS, input modality, multiplexer,
font/theme, alt-screen behavior) creates a combinatorial explosion of
edge cases.
- What next: we are focusing on redraw-based improvements to the
existing terminal-native TUI so scrolling, selection, and copy remain
native while resize/redraw correctness improves.

## Testing
- just write-config-schema
- just fmt
- cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs
-p codex-core
- cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs
-p codex-cli
- cargo check
- cargo test -p codex-core
- cargo test -p codex-cli
This commit is contained in:
Josh McKinney
2026-01-21 17:02:29 -08:00
committed by GitHub
Unverified
parent 41e38856f6
commit a489b64cb5
808 changed files with 8 additions and 82802 deletions
-7
View File
@@ -641,7 +641,6 @@
"colorchoice_1.0.4": "{\"dependencies\":[],\"features\":{}}",
"combine_4.6.7": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"bytes\",\"req\":\"^1\"},{\"name\":\"bytes_05\",\"optional\":true,\"package\":\"bytes\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"bytes_05\",\"package\":\"bytes\",\"req\":\"^0.5\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"futures-03-dep\",\"package\":\"futures\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"futures-core-03\",\"optional\":true,\"package\":\"futures-core\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"futures-io-03\",\"optional\":true,\"package\":\"futures-io\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"memchr\",\"req\":\"^2.3\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.0\"},{\"features\":[\"tokio\",\"quickcheck\"],\"kind\":\"dev\",\"name\":\"partial-io\",\"req\":\"^0.3\"},{\"name\":\"pin-project-lite\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"quick-error\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.6\"},{\"name\":\"regex\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"io-util\"],\"name\":\"tokio-02-dep\",\"optional\":true,\"package\":\"tokio\",\"req\":\"^0.2.3\"},{\"features\":[\"fs\",\"io-driver\",\"io-util\",\"macros\"],\"kind\":\"dev\",\"name\":\"tokio-02-dep\",\"package\":\"tokio\",\"req\":\"^0.2\"},{\"default_features\":false,\"name\":\"tokio-03-dep\",\"optional\":true,\"package\":\"tokio\",\"req\":\"^0.3\"},{\"features\":[\"fs\",\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio-03-dep\",\"package\":\"tokio\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"tokio-dep\",\"optional\":true,\"package\":\"tokio\",\"req\":\"^1\"},{\"features\":[\"fs\",\"macros\",\"rt\",\"rt-multi-thread\",\"io-util\"],\"kind\":\"dev\",\"name\":\"tokio-dep\",\"package\":\"tokio\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"codec\"],\"name\":\"tokio-util\",\"optional\":true,\"req\":\"^0.7\"}],\"features\":{\"alloc\":[],\"default\":[\"std\"],\"futures-03\":[\"pin-project\",\"std\",\"futures-core-03\",\"futures-io-03\",\"pin-project-lite\"],\"mp4\":[],\"pin-project\":[\"pin-project-lite\"],\"std\":[\"memchr/std\",\"bytes\",\"alloc\"],\"tokio\":[\"tokio-dep\",\"tokio-util/io\",\"futures-core-03\",\"pin-project-lite\"],\"tokio-02\":[\"pin-project\",\"std\",\"tokio-02-dep\",\"futures-core-03\",\"pin-project-lite\",\"bytes_05\"],\"tokio-03\":[\"pin-project\",\"std\",\"tokio-03-dep\",\"futures-core-03\",\"pin-project-lite\"]}}",
"compact_str_0.8.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"borsh\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"castaway\",\"req\":\"^0.2.3\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"diesel\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"markup\",\"optional\":true,\"req\":\"^0.13\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"proptest\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"size_32\"],\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"alloc\",\"size_32\"],\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.7\"},{\"name\":\"rustversion\",\"req\":\"^1\"},{\"name\":\"ryu\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\",\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"features\":[\"union\"],\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"sqlx\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"static_assertions\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"test-case\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"test-strategy\",\"req\":\"^0.3\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"borsh\":[\"dep:borsh\"],\"bytes\":[\"dep:bytes\"],\"default\":[\"std\"],\"diesel\":[\"dep:diesel\"],\"markup\":[\"dep:markup\"],\"proptest\":[\"dep:proptest\"],\"quickcheck\":[\"dep:quickcheck\"],\"rkyv\":[\"dep:rkyv\"],\"serde\":[\"dep:serde\"],\"smallvec\":[\"dep:smallvec\"],\"sqlx\":[\"dep:sqlx\",\"std\"],\"sqlx-mysql\":[\"sqlx\",\"sqlx/mysql\"],\"sqlx-postgres\":[\"sqlx\",\"sqlx/postgres\"],\"sqlx-sqlite\":[\"sqlx\",\"sqlx/sqlite\"],\"std\":[]}}",
"compact_str_0.9.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"borsh\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"castaway\",\"req\":\"^0.2.3\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"diesel\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"markup\",\"optional\":true,\"req\":\"^0.15\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"proptest\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.8\"},{\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.8.8\"},{\"name\":\"rustversion\",\"req\":\"^1\"},{\"name\":\"ryu\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\",\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"features\":[\"union\"],\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"sqlx\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"static_assertions\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"test-case\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"test-strategy\",\"req\":\"^0.3\"},{\"default_features\":false,\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"borsh\":[\"dep:borsh\"],\"bytes\":[\"dep:bytes\"],\"default\":[\"std\"],\"diesel\":[\"dep:diesel\"],\"markup\":[\"dep:markup\"],\"proptest\":[\"dep:proptest\"],\"quickcheck\":[\"dep:quickcheck\"],\"rkyv\":[\"dep:rkyv\"],\"serde\":[\"dep:serde\"],\"smallvec\":[\"dep:smallvec\"],\"sqlx\":[\"dep:sqlx\",\"std\"],\"sqlx-mysql\":[\"sqlx\",\"sqlx/mysql\"],\"sqlx-postgres\":[\"sqlx\",\"sqlx/postgres\"],\"sqlx-sqlite\":[\"sqlx\",\"sqlx/sqlite\"],\"std\":[],\"zeroize\":[\"dep:zeroize\"]}}",
"concurrent-queue_2.5.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"crossbeam-utils\",\"req\":\"^0.8.11\"},{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3.1.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"console_0.15.11": "{\"dependencies\":[{\"name\":\"encode_unicode\",\"req\":\"^1\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2.99\"},{\"name\":\"once_cell\",\"req\":\"^1.8\"},{\"default_features\":false,\"features\":[\"std\",\"bit-set\",\"break-dead-code\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.4.2\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Console\",\"Win32_Storage_FileSystem\",\"Win32_UI_Input_KeyboardAndMouse\"],\"name\":\"windows-sys\",\"req\":\"^0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"ansi-parsing\":[],\"default\":[\"unicode-width\",\"ansi-parsing\"],\"windows-console-colors\":[\"ansi-parsing\"]}}",
"const-hex_1.17.0": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"},{\"kind\":\"dev\",\"name\":\"divan\",\"package\":\"codspeed-divan-compat\",\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"faster-hex\",\"req\":\"^0.10.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"~0.4.2\"},{\"default_features\":false,\"name\":\"proptest\",\"optional\":true,\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"rustc-hex\",\"req\":\"^2.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"__fuzzing\":[\"dep:proptest\",\"std\"],\"alloc\":[\"serde_core?/alloc\",\"proptest?/alloc\"],\"core-error\":[],\"default\":[\"std\"],\"force-generic\":[],\"hex\":[],\"nightly\":[],\"portable-simd\":[],\"serde\":[\"dep:serde_core\"],\"std\":[\"serde_core?/std\",\"proptest?/std\",\"alloc\"]}}",
@@ -697,7 +696,6 @@
"display_container_0.9.0": "{\"dependencies\":[{\"name\":\"either\",\"req\":\"^1.8\"},{\"name\":\"indenter\",\"req\":\"^0.3.3\"}],\"features\":{}}",
"displaydoc_0.2.5": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^0.6.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^1.0.24\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"doc-comment_0.3.3": "{\"dependencies\":[],\"features\":{\"no_core\":[],\"old_macros\":[]}}",
"document-features_0.2.12": "{\"dependencies\":[{\"name\":\"litrs\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[],\"self-test\":[]}}",
"dotenvy_0.15.7": "{\"dependencies\":[{\"name\":\"clap\",\"optional\":true,\"req\":\"^3.2\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.16.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.3.0\"}],\"features\":{\"cli\":[\"clap\"]}}",
"downcast-rs_1.2.1": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"dtor-proc-macro_0.0.6": "{\"dependencies\":[],\"features\":{\"default\":[]}}",
@@ -836,7 +834,6 @@
"jni_0.21.1": "{\"dependencies\":[{\"name\":\"cesu8\",\"req\":\"^1.1.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"combine\",\"req\":\"^4.1.0\"},{\"name\":\"java-locator\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"jni-sys\",\"req\":\"^0.3.0\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"name\":\"thiserror\",\"req\":\"^1.0.20\"},{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rusty-fork\",\"req\":\"^0.3.0\"},{\"kind\":\"build\",\"name\":\"walkdir\",\"req\":\"^2\"},{\"features\":[\"Win32_Globalization\"],\"name\":\"windows-sys\",\"req\":\"^0.45.0\",\"target\":\"cfg(windows)\"},{\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"invocation\":[\"java-locator\",\"libloading\"]}}",
"jobserver_0.1.34": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.3.2\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2.171\",\"target\":\"cfg(unix)\"},{\"features\":[\"fs\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.28.0\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.1\"}],\"features\":{}}",
"js-sys_0.3.77": "{\"dependencies\":[{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.100\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"wasm-bindgen/std\"]}}",
"kasuari_0.4.11": "{\"dependencies\":[{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"hashbrown\",\"req\":\"^0.16\"},{\"default_features\":false,\"features\":[\"require-cas\"],\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1.11\"},{\"features\":[\"alloc\"],\"name\":\"portable-atomic-util\",\"optional\":true,\"req\":\"^0.2.4\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.26\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2.0\"}],\"features\":{\"default\":[\"std\"],\"document-features\":[\"dep:document-features\"],\"portable-atomic\":[\"dep:portable-atomic\",\"dep:portable-atomic-util\"],\"std\":[\"thiserror/std\",\"portable-atomic?/std\"]}}",
"keyring_3.6.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"byteorder\",\"optional\":true,\"req\":\"^1.2\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"features\":[\"derive\",\"wrap_help\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.0-rc.1\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.0-rc.2\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.1\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.5\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"features\":[\"std\"],\"name\":\"linux-keyutils\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"log\",\"req\":\"^0.4.22\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.66\"},{\"kind\":\"dev\",\"name\":\"rpassword\",\"req\":\"^7\"},{\"kind\":\"dev\",\"name\":\"rprompt\",\"req\":\"^2\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"security-framework\",\"optional\":true,\"req\":\"^2\",\"target\":\"cfg(target_os = \\\"ios\\\")\"},{\"name\":\"security-framework\",\"optional\":true,\"req\":\"^3\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"whoami\",\"req\":\"^1.5\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Credentials\"],\"name\":\"windows-sys\",\"optional\":true,\"req\":\"^0.60\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"zeroize\",\"req\":\"^1.8.1\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"apple-native\":[\"dep:security-framework\"],\"async-io\":[\"zbus?/async-io\"],\"async-secret-service\":[\"dep:secret-service\",\"dep:zbus\"],\"crypto-openssl\":[\"dbus-secret-service?/crypto-openssl\",\"secret-service?/crypto-openssl\"],\"crypto-rust\":[\"dbus-secret-service?/crypto-rust\",\"secret-service?/crypto-rust\"],\"linux-native\":[\"dep:linux-keyutils\"],\"linux-native-async-persistent\":[\"linux-native\",\"async-secret-service\"],\"linux-native-sync-persistent\":[\"linux-native\",\"sync-secret-service\"],\"sync-secret-service\":[\"dep:dbus-secret-service\"],\"tokio\":[\"zbus?/tokio\"],\"vendored\":[\"dbus-secret-service?/vendored\",\"openssl?/vendored\"],\"windows-native\":[\"dep:windows-sys\",\"dep:byteorder\"]}}",
"kqueue-sys_1.0.4": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^1.2.1\"},{\"name\":\"libc\",\"req\":\"^0.2.74\"}],\"features\":{}}",
"kqueue_1.1.1": "{\"dependencies\":[{\"features\":[\"html_reports\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"dhat\",\"req\":\"^0.3.2\"},{\"name\":\"kqueue-sys\",\"req\":\"^1.0.4\"},{\"name\":\"libc\",\"req\":\"^0.2.17\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.1.0\"}],\"features\":{}}",
@@ -852,7 +849,6 @@
"linux-raw-sys_0.4.15": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.49\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.100\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"bootparam\":[],\"btrfs\":[],\"default\":[\"std\",\"general\",\"errno\"],\"elf\":[],\"elf_uapi\":[],\"errno\":[],\"general\":[],\"if_arp\":[],\"if_ether\":[],\"if_packet\":[],\"io_uring\":[],\"ioctl\":[],\"landlock\":[],\"loop_device\":[],\"mempolicy\":[],\"net\":[],\"netlink\":[],\"no_std\":[],\"prctl\":[],\"ptrace\":[],\"rustc-dep-of-std\":[\"core\",\"compiler_builtins\",\"no_std\"],\"std\":[],\"system\":[],\"xdp\":[]}}",
"linux-raw-sys_0.9.4": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.49\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.100\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"bootparam\":[],\"btrfs\":[],\"default\":[\"std\",\"general\",\"errno\"],\"elf\":[],\"elf_uapi\":[],\"errno\":[],\"general\":[],\"if_arp\":[],\"if_ether\":[],\"if_packet\":[],\"image\":[],\"io_uring\":[],\"ioctl\":[],\"landlock\":[],\"loop_device\":[],\"mempolicy\":[],\"net\":[],\"netlink\":[],\"no_std\":[],\"prctl\":[],\"ptrace\":[],\"rustc-dep-of-std\":[\"core\",\"compiler_builtins\",\"no_std\"],\"std\":[],\"system\":[],\"xdp\":[]}}",
"litemap_0.8.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"use-std\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"features\":[\"validation\"],\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.110\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0.110\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.0\"}],\"features\":{\"alloc\":[],\"databake\":[\"dep:databake\"],\"default\":[\"alloc\"],\"serde\":[\"dep:serde\",\"alloc\"],\"testing\":[\"alloc\"],\"yoke\":[\"dep:yoke\"]}}",
"litrs_1.0.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"optional\":true,\"req\":\"^1.0.63\"},{\"name\":\"unicode-xid\",\"optional\":true,\"req\":\"^0.2.4\"}],\"features\":{\"check_suffix\":[\"unicode-xid\"]}}",
"local-waker_0.1.4": "{\"dependencies\":[],\"features\":{}}",
"lock_api_0.4.13": "{\"dependencies\":[{\"kind\":\"build\",\"name\":\"autocfg\",\"req\":\"^1.1.0\"},{\"name\":\"owning_ref\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"scopeguard\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.126\"}],\"features\":{\"arc_lock\":[],\"atomic_usize\":[],\"default\":[\"atomic_usize\"],\"nightly\":[]}}",
"log_0.4.29": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.63\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval\",\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval_derive\",\"req\":\"^2.16\"},{\"default_features\":false,\"name\":\"sval_ref\",\"optional\":true,\"req\":\"^2.16\"},{\"default_features\":false,\"features\":[\"inline-i128\"],\"name\":\"value-bag\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"test\"],\"kind\":\"dev\",\"name\":\"value-bag\",\"req\":\"^1.12\"}],\"features\":{\"kv\":[],\"kv_serde\":[\"kv_std\",\"value-bag/serde\",\"serde\"],\"kv_std\":[\"std\",\"kv\",\"value-bag/error\"],\"kv_sval\":[\"kv\",\"value-bag/sval\",\"sval\",\"sval_ref\"],\"kv_unstable\":[\"kv\",\"value-bag\"],\"kv_unstable_serde\":[\"kv_serde\",\"kv_unstable_std\"],\"kv_unstable_std\":[\"kv_std\",\"kv_unstable\"],\"kv_unstable_sval\":[\"kv_sval\",\"kv_unstable\"],\"max_level_debug\":[],\"max_level_error\":[],\"max_level_info\":[],\"max_level_off\":[],\"max_level_trace\":[],\"max_level_warn\":[],\"release_max_level_debug\":[],\"release_max_level_error\":[],\"release_max_level_info\":[],\"release_max_level_off\":[],\"release_max_level_trace\":[],\"release_max_level_warn\":[],\"serde\":[\"serde_core\"],\"std\":[]}}",
@@ -983,7 +979,6 @@
"rand_core_0.6.4": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"alloc\":[],\"serde1\":[\"serde\"],\"std\":[\"alloc\",\"getrandom\",\"getrandom/std\"]}}",
"rand_core_0.9.3": "{\"dependencies\":[{\"name\":\"getrandom\",\"optional\":true,\"req\":\"^0.3.0\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"os_rng\":[\"dep:getrandom\"],\"serde\":[\"dep:serde\"],\"std\":[\"getrandom?/std\"]}}",
"rand_xorshift_0.4.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1\"},{\"name\":\"rand_core\",\"req\":\"^0.9.0\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.118\"}],\"features\":{\"serde\":[\"dep:serde\"]}}",
"ratatui-core_0.1.0": "{\"dependencies\":[{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"bitflags\",\"req\":\"^2.10\"},{\"default_features\":false,\"name\":\"compact_str\",\"req\":\"^0.9\"},{\"name\":\"document-features\",\"optional\":true,\"req\":\"^0.2\"},{\"name\":\"hashbrown\",\"req\":\"^0.16\"},{\"name\":\"indoc\",\"req\":\"^2\"},{\"default_features\":false,\"features\":[\"use_alloc\"],\"name\":\"itertools\",\"req\":\"^0.14\"},{\"default_features\":false,\"name\":\"kasuari\",\"req\":\"^0.4\"},{\"name\":\"lru\",\"req\":\"^0.16\"},{\"name\":\"palette\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rstest\",\"req\":\"^0.26\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"strum\",\"req\":\"^0.27\"},{\"default_features\":false,\"name\":\"thiserror\",\"req\":\"^2\"},{\"name\":\"unicode-segmentation\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"unicode-truncate\",\"req\":\"^2\"},{\"name\":\"unicode-width\",\"req\":\">=0.2.0, <=0.2.2\"}],\"features\":{\"anstyle\":[\"dep:anstyle\"],\"default\":[],\"layout-cache\":[\"std\"],\"palette\":[\"std\",\"dep:palette\"],\"portable-atomic\":[\"kasuari/portable-atomic\"],\"scrolling-regions\":[],\"serde\":[\"std\",\"dep:serde\",\"bitflags/serde\",\"compact_str/serde\"],\"std\":[\"itertools/use_std\",\"thiserror/std\",\"kasuari/std\",\"compact_str/std\",\"unicode-truncate/std\",\"strum/std\"],\"underline-color\":[]}}",
"ratatui-macros_0.6.0": "{\"dependencies\":[{\"features\":[\"user-hooks\"],\"kind\":\"dev\",\"name\":\"cargo-husky\",\"req\":\"^1.5.0\"},{\"name\":\"ratatui\",\"req\":\"^0.29.0\"},{\"features\":[\"diff\"],\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0.101\"}],\"features\":{}}",
"redox_syscall_0.5.15": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^2.4\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"loom\",\"req\":\"^0.7\",\"target\":\"cfg(loom)\"}],\"features\":{\"default\":[\"userspace\"],\"rustc-dep-of-std\":[\"core\",\"bitflags/rustc-dep-of-std\"],\"std\":[],\"userspace\":[]}}",
"redox_users_0.4.6": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.2\"},{\"default_features\":false,\"features\":[\"std\",\"call\"],\"name\":\"libredox\",\"req\":\"^0.1.3\"},{\"name\":\"rust-argon2\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"thiserror\",\"req\":\"^1.0\"},{\"features\":[\"zeroize_derive\"],\"name\":\"zeroize\",\"optional\":true,\"req\":\"^1.4\"}],\"features\":{\"auth\":[\"rust-argon2\",\"zeroize\"],\"default\":[\"auth\"]}}",
@@ -1162,7 +1157,6 @@
"try-lock_0.2.5": "{\"dependencies\":[],\"features\":{}}",
"ts-rs-macros_11.1.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"name\":\"quote\",\"req\":\"^1\"},{\"features\":[\"full\",\"extra-traits\"],\"name\":\"syn\",\"req\":\"^2.0.28\"},{\"name\":\"termcolor\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"no-serde-warnings\":[],\"serde-compat\":[\"termcolor\"]}}",
"ts-rs_11.1.0": "{\"dependencies\":[{\"features\":[\"serde\"],\"name\":\"bigdecimal\",\"optional\":true,\"req\":\">=0.0.13, <0.5\"},{\"name\":\"bson\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"features\":[\"serde\"],\"kind\":\"dev\",\"name\":\"chrono\",\"req\":\"^0.4\"},{\"name\":\"dprint-plugin-typescript\",\"optional\":true,\"req\":\"=0.95\"},{\"name\":\"heapless\",\"optional\":true,\"req\":\">=0.7, <0.9\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"ordered-float\",\"optional\":true,\"req\":\">=3, <6\"},{\"name\":\"semver\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"name\":\"smol_str\",\"optional\":true,\"req\":\"^0.3\"},{\"name\":\"thiserror\",\"req\":\"^2\"},{\"features\":[\"sync\"],\"name\":\"tokio\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"sync\",\"rt\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.40\"},{\"name\":\"ts-rs-macros\",\"req\":\"=11.1.0\"},{\"name\":\"url\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"bigdecimal-impl\":[\"bigdecimal\"],\"bson-uuid-impl\":[\"bson\"],\"bytes-impl\":[\"bytes\"],\"chrono-impl\":[\"chrono\"],\"default\":[\"serde-compat\"],\"format\":[\"dprint-plugin-typescript\"],\"heapless-impl\":[\"heapless\"],\"import-esm\":[],\"indexmap-impl\":[\"indexmap\"],\"no-serde-warnings\":[\"ts-rs-macros/no-serde-warnings\"],\"ordered-float-impl\":[\"ordered-float\"],\"semver-impl\":[\"semver\"],\"serde-compat\":[\"ts-rs-macros/serde-compat\"],\"serde-json-impl\":[\"serde_json\"],\"smol_str-impl\":[\"smol_str\"],\"tokio-impl\":[\"tokio\"],\"url-impl\":[\"url\"],\"uuid-impl\":[\"uuid\"]}}",
"tui-scrollbar_0.2.2": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"color-eyre\",\"req\":\"^0.6\"},{\"name\":\"crossterm_0_28\",\"optional\":true,\"package\":\"crossterm\",\"req\":\"^0.28\"},{\"name\":\"crossterm_0_29\",\"optional\":true,\"package\":\"crossterm\",\"req\":\"^0.29\"},{\"name\":\"document-features\",\"req\":\"^0.2.11\"},{\"kind\":\"dev\",\"name\":\"ratatui\",\"req\":\"^0.30.0\"},{\"name\":\"ratatui-core\",\"req\":\"^0.1\"}],\"features\":{\"crossterm\":[\"crossterm_0_29\"],\"crossterm_0_28\":[\"dep:crossterm_0_28\"],\"crossterm_0_29\":[\"dep:crossterm_0_29\"],\"default\":[]}}",
"typenum_1.18.0": "{\"dependencies\":[{\"default_features\":false,\"name\":\"scale-info\",\"optional\":true,\"req\":\"^1.0\"}],\"features\":{\"const-generics\":[],\"force_unix_path_separator\":[],\"i128\":[],\"no_std\":[],\"scale_info\":[\"scale-info/derive\"],\"strict\":[]}}",
"uds_windows_1.1.0": "{\"dependencies\":[{\"name\":\"memoffset\",\"req\":\"^0.9.0\"},{\"name\":\"tempfile\",\"req\":\"^3\",\"target\":\"cfg(windows)\"},{\"features\":[\"winsock2\",\"ws2def\",\"minwinbase\",\"ntdef\",\"processthreadsapi\",\"handleapi\",\"ws2tcpip\",\"winbase\"],\"name\":\"winapi\",\"req\":\"^0.3.9\",\"target\":\"cfg(windows)\"}],\"features\":{}}",
"uname_0.1.1": "{\"dependencies\":[{\"name\":\"libc\",\"req\":\"^0.2\"}],\"features\":{}}",
@@ -1172,7 +1166,6 @@
"unicode-linebreak_0.1.5": "{\"dependencies\":[],\"features\":{}}",
"unicode-segmentation_1.12.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^0.7\"}],\"features\":{\"no_std\":[]}}",
"unicode-truncate_1.1.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"itertools\",\"req\":\"^0.13\"},{\"default_features\":false,\"name\":\"unicode-segmentation\",\"req\":\"^1\"},{\"name\":\"unicode-width\",\"req\":\"^0.1\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"unicode-truncate_2.0.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"itertools\",\"req\":\"^0.13\"},{\"default_features\":false,\"name\":\"unicode-segmentation\",\"req\":\"^1\"},{\"name\":\"unicode-width\",\"req\":\"^0.2\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"unicode-width_0.1.14": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"std\",\"optional\":true,\"package\":\"rustc-std-workspace-std\",\"req\":\"^1.0\"}],\"features\":{\"cjk\":[],\"default\":[\"cjk\"],\"no_std\":[],\"rustc-dep-of-std\":[\"std\",\"core\",\"compiler_builtins\"]}}",
"unicode-width_0.2.1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0\"},{\"name\":\"std\",\"optional\":true,\"package\":\"rustc-std-workspace-std\",\"req\":\"^1.0\"}],\"features\":{\"cjk\":[],\"default\":[\"cjk\"],\"no_std\":[],\"rustc-dep-of-std\":[\"std\",\"core\"]}}",
"unicode-xid_0.2.6": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.3\"}],\"features\":{\"bench\":[],\"default\":[],\"no_std\":[]}}",
+2 -162
View File
@@ -1166,8 +1166,6 @@ dependencies = [
"codex-rmcp-client",
"codex-stdio-to-uds",
"codex-tui",
"codex-tui2",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"codex-windows-sandbox",
"libc",
@@ -1811,81 +1809,6 @@ dependencies = [
"winsplit",
]
[[package]]
name = "codex-tui2"
version = "0.0.0"
dependencies = [
"anyhow",
"arboard",
"assert_matches",
"async-stream",
"base64",
"chrono",
"clap",
"codex-ansi-escape",
"codex-app-server-protocol",
"codex-arg0",
"codex-backend-client",
"codex-cli",
"codex-common",
"codex-core",
"codex-feedback",
"codex-file-search",
"codex-login",
"codex-otel",
"codex-protocol",
"codex-tui",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"codex-utils-pty",
"codex-windows-sandbox",
"color-eyre",
"crossterm",
"derive_more 2.1.1",
"diffy",
"dirs",
"dunce",
"image",
"insta",
"itertools 0.14.0",
"lazy_static",
"libc",
"mcp-types",
"pathdiff",
"pretty_assertions",
"pulldown-cmark",
"rand 0.9.2",
"ratatui",
"ratatui-core",
"ratatui-macros",
"regex-lite",
"reqwest",
"serde",
"serde_json",
"serial_test",
"shlex",
"strum 0.27.2",
"strum_macros 0.27.2",
"supports-color 3.0.2",
"tempfile",
"textwrap 0.16.2",
"tokio",
"tokio-stream",
"tokio-util",
"toml 0.9.5",
"tracing",
"tracing-appender",
"tracing-subscriber",
"tree-sitter-bash",
"tree-sitter-highlight",
"tui-scrollbar",
"unicode-segmentation",
"unicode-width 0.2.1",
"url",
"uuid",
"vt100",
]
[[package]]
name = "codex-utils-absolute-path"
version = "0.0.0"
@@ -2048,20 +1971,6 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "compact_str"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"rustversion",
"ryu",
"static_assertions",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@@ -2648,15 +2557,6 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]]
name = "dotenvy"
version = "0.15.7"
@@ -3988,16 +3888,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kasuari"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b"
dependencies = [
"hashbrown 0.16.0",
"thiserror 2.0.17",
]
[[package]]
name = "keyring"
version = "3.6.3"
@@ -4143,12 +4033,6 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "local-waker"
version = "0.1.4"
@@ -5488,7 +5372,7 @@ source = "git+https://github.com/nornagon/ratatui?branch=nornagon-v0.29.0-patch#
dependencies = [
"bitflags 2.10.0",
"cassowary",
"compact_str 0.8.1",
"compact_str",
"crossterm",
"indoc",
"instability",
@@ -5497,27 +5381,7 @@ dependencies = [
"paste",
"strum 0.26.3",
"unicode-segmentation",
"unicode-truncate 1.1.0",
"unicode-width 0.2.1",
]
[[package]]
name = "ratatui-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293"
dependencies = [
"bitflags 2.10.0",
"compact_str 0.9.0",
"hashbrown 0.16.0",
"indoc",
"itertools 0.14.0",
"kasuari",
"lru 0.16.3",
"strum 0.27.2",
"thiserror 2.0.17",
"unicode-segmentation",
"unicode-truncate 2.0.0",
"unicode-truncate",
"unicode-width 0.2.1",
]
@@ -6684,9 +6548,6 @@ name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros 0.27.2",
]
[[package]]
name = "strum_macros"
@@ -7549,16 +7410,6 @@ dependencies = [
"termcolor",
]
[[package]]
name = "tui-scrollbar"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4267311b5c7999a996ea94939b6d2b1b44a9e5cc11e76cbbb6dcca4c281df4"
dependencies = [
"document-features",
"ratatui-core",
]
[[package]]
name = "tungstenite"
version = "0.28.0"
@@ -7644,17 +7495,6 @@ dependencies = [
"unicode-width 0.1.14",
]
[[package]]
name = "unicode-truncate"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fbf03860ff438702f3910ca5f28f8dac63c1c11e7efb5012b8b175493606330"
dependencies = [
"itertools 0.13.0",
"unicode-segmentation",
"unicode-width 0.2.1",
]
[[package]]
name = "unicode-width"
version = "0.1.14"
-4
View File
@@ -35,7 +35,6 @@ members = [
"stdio-to-uds",
"otel",
"tui",
"tui2",
"utils/absolute-path",
"utils/cargo-bin",
"utils/git",
@@ -93,7 +92,6 @@ codex-responses-api-proxy = { path = "responses-api-proxy" }
codex-rmcp-client = { path = "rmcp-client" }
codex-stdio-to-uds = { path = "stdio-to-uds" }
codex-tui = { path = "tui" }
codex-tui2 = { path = "tui2" }
codex-utils-absolute-path = { path = "utils/absolute-path" }
codex-utils-cache = { path = "utils/cache" }
codex-utils-cargo-bin = { path = "utils/cargo-bin" }
@@ -178,7 +176,6 @@ pretty_assertions = "1.4.1"
pulldown-cmark = "0.10"
rand = "0.9"
ratatui = "0.29.0"
ratatui-core = "0.1.0"
ratatui-macros = "0.6.0"
regex = "1.12.2"
regex-lite = "0.1.8"
@@ -225,7 +222,6 @@ tree-sitter-bash = "0.25"
zstd = "0.13"
tree-sitter-highlight = "0.25.10"
ts-rs = "11"
tui-scrollbar = "0.2.2"
uds_windows = "1.1.0"
unicode-segmentation = "1.12.0"
unicode-width = "0.2"
-2
View File
@@ -35,8 +35,6 @@ codex-responses-api-proxy = { workspace = true }
codex-rmcp-client = { workspace = true }
codex-stdio-to-uds = { workspace = true }
codex-tui = { workspace = true }
codex-tui2 = { workspace = true }
codex-utils-absolute-path = { workspace = true }
libc = { workspace = true }
owo-colors = { workspace = true }
regex-lite = { workspace = true }
+1 -41
View File
@@ -26,7 +26,6 @@ use codex_tui::AppExitInfo;
use codex_tui::Cli as TuiCli;
use codex_tui::ExitReason;
use codex_tui::update_action::UpdateAction;
use codex_tui2 as tui2;
use owo_colors::OwoColorize;
use std::io::IsTerminal;
use std::path::PathBuf;
@@ -40,14 +39,8 @@ use crate::mcp_cmd::McpCli;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_core::config::find_codex_home;
use codex_core::config::load_config_as_toml_with_cli_overrides;
use codex_core::features::Feature;
use codex_core::features::FeatureOverrides;
use codex_core::features::Features;
use codex_core::features::is_known_feature_key;
use codex_core::terminal::TerminalName;
use codex_utils_absolute_path::AbsolutePathBuf;
/// Codex CLI
///
@@ -727,8 +720,6 @@ fn prepend_config_flags(
.splice(0..0, cli_config_overrides.raw_overrides);
}
/// Run the interactive Codex TUI, dispatching to either the legacy implementation or the
/// experimental TUI v2 shim based on feature flags resolved from config.
async fn run_interactive_tui(
mut interactive: TuiCli,
codex_linux_sandbox_exe: Option<PathBuf>,
@@ -756,12 +747,7 @@ async fn run_interactive_tui(
}
}
if is_tui2_enabled(&interactive).await? {
let result = tui2::run_main(interactive.into(), codex_linux_sandbox_exe).await?;
Ok(result.into())
} else {
codex_tui::run_main(interactive, codex_linux_sandbox_exe).await
}
codex_tui::run_main(interactive, codex_linux_sandbox_exe).await
}
fn confirm(prompt: &str) -> std::io::Result<bool> {
@@ -773,32 +759,6 @@ fn confirm(prompt: &str) -> std::io::Result<bool> {
Ok(answer.eq_ignore_ascii_case("y") || answer.eq_ignore_ascii_case("yes"))
}
/// Returns `Ok(true)` when the resolved configuration enables the `tui2` feature flag.
///
/// This performs a lightweight config load (honoring the same precedence as the lower-level TUI
/// bootstrap: `$CODEX_HOME`, config.toml, profile, and CLI `-c` overrides) solely to decide which
/// TUI frontend to launch. The full configuration is still loaded later by the interactive TUI.
async fn is_tui2_enabled(cli: &TuiCli) -> std::io::Result<bool> {
let raw_overrides = cli.config_overrides.raw_overrides.clone();
let overrides_cli = codex_common::CliConfigOverrides { raw_overrides };
let cli_kv_overrides = overrides_cli
.parse_overrides()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
let codex_home = find_codex_home()?;
let cwd = cli.cwd.clone();
let config_cwd = match cwd.as_deref() {
Some(path) => AbsolutePathBuf::from_absolute_path(path)?,
None => AbsolutePathBuf::current_dir()?,
};
let config_toml =
load_config_as_toml_with_cli_overrides(&codex_home, &config_cwd, cli_kv_overrides).await?;
let config_profile = config_toml.get_config_profile(cli.config_profile.clone())?;
let overrides = FeatureOverrides::default();
let features = Features::from_config(&config_toml, &config_profile, overrides);
Ok(features.enabled(Feature::Tui2))
}
/// Build the final `TuiCli` for a `codex resume` invocation.
fn finalize_resume_interactive(
mut interactive: TuiCli,
-88
View File
@@ -198,9 +198,6 @@
"steer": {
"type": "boolean"
},
"tui2": {
"type": "boolean"
},
"undo": {
"type": "boolean"
},
@@ -826,32 +823,6 @@
},
"type": "object"
},
"ScrollInputMode": {
"description": "How TUI2 should interpret mouse scroll events.\n\nTerminals generally encode both mouse wheels and trackpads as the same \"scroll up/down\" mouse button events, without a magnitude. This setting controls whether Codex uses a heuristic to infer wheel vs trackpad per stream, or forces a specific behavior.",
"oneOf": [
{
"description": "Infer wheel vs trackpad behavior per scroll stream.",
"enum": [
"auto"
],
"type": "string"
},
{
"description": "Always treat scroll events as mouse-wheel input (fixed lines per tick).",
"enum": [
"wheel"
],
"type": "string"
},
{
"description": "Always treat scroll events as trackpad input (fractional accumulation).",
"enum": [
"trackpad"
],
"type": "string"
}
]
},
"ShellEnvironmentPolicyInherit": {
"oneOf": [
{
@@ -991,62 +962,6 @@
"default": true,
"description": "Enable desktop notifications from the TUI when the terminal is unfocused. Defaults to `true`."
},
"scroll_events_per_tick": {
"description": "Override the *wheel* event density used to normalize TUI2 scrolling.\n\nTerminals generally deliver both mouse wheels and trackpads as discrete `scroll up/down` mouse events with direction but no magnitude. Unfortunately, the *number* of raw events per physical wheel notch varies by terminal (commonly 1, 3, or 9+). TUI2 uses this value to normalize that raw event density into consistent \"wheel tick\" behavior.\n\nWheel math (conceptually):\n\n- A single event contributes `1 / scroll_events_per_tick` tick-equivalents. - Wheel-like streams then scale that by `scroll_wheel_lines` so one physical notch scrolls a fixed number of lines.\n\nTrackpad math is intentionally *not* fully tied to this value: in trackpad-like mode, TUI2 uses `min(scroll_events_per_tick, 3)` as the divisor so terminals with dense wheel ticks (e.g. 9 events per notch) do not make trackpads feel artificially slow.\n\nDefaults are derived per terminal from [`crate::terminal::TerminalInfo`] when TUI2 starts. See `codex-rs/tui2/docs/scroll_input_model.md` for the probe data and rationale.",
"format": "uint16",
"minimum": 0.0,
"type": "integer"
},
"scroll_invert": {
"default": false,
"description": "Invert mouse scroll direction in TUI2.\n\nThis flips the scroll sign after terminal detection. It is applied consistently to both wheel and trackpad input.",
"type": "boolean"
},
"scroll_mode": {
"allOf": [
{
"$ref": "#/definitions/ScrollInputMode"
}
],
"default": "auto",
"description": "Select how TUI2 interprets mouse scroll input.\n\n- `auto` (default): infer wheel vs trackpad per scroll stream. - `wheel`: always use wheel behavior (fixed lines per wheel notch). - `trackpad`: always use trackpad behavior (fractional accumulation; wheel may feel slow)."
},
"scroll_trackpad_accel_events": {
"description": "Trackpad acceleration: approximate number of events required to gain +1x speed in TUI2.\n\nThis keeps small swipes precise while allowing large/faster swipes to cover more content. Defaults are chosen to address terminals where trackpad event density is comparatively low.\n\nConcretely, TUI2 computes an acceleration multiplier for trackpad-like streams:\n\n- `multiplier = clamp(1 + abs(events) / scroll_trackpad_accel_events, 1..scroll_trackpad_accel_max)`\n\nThe multiplier is applied to the streams computed line delta (including any carried fractional remainder).",
"format": "uint16",
"minimum": 0.0,
"type": "integer"
},
"scroll_trackpad_accel_max": {
"description": "Trackpad acceleration: maximum multiplier applied to trackpad-like streams.\n\nSet to 1 to effectively disable trackpad acceleration.\n\nSee [`Tui::scroll_trackpad_accel_events`] for the exact multiplier formula.",
"format": "uint16",
"minimum": 0.0,
"type": "integer"
},
"scroll_trackpad_lines": {
"description": "Override baseline trackpad scroll sensitivity in TUI2.\n\nTrackpads do not have discrete notches, but terminals still emit discrete `scroll up/down` events. In trackpad-like mode, TUI2 accumulates fractional scroll and only applies whole lines to the viewport.\n\nTrackpad per-event contribution is:\n\n- `scroll_trackpad_lines / min(scroll_events_per_tick, 3)`\n\n(plus optional bounded acceleration; see `scroll_trackpad_accel_*`). The `min(..., 3)` divisor is deliberate: `scroll_events_per_tick` is calibrated from *wheel* behavior and can be much larger than trackpad event density, which would otherwise make trackpads feel too slow in dense-wheel terminals.\n\nDefaults to 1, meaning one tick-equivalent maps to one transcript line.",
"format": "uint16",
"minimum": 0.0,
"type": "integer"
},
"scroll_wheel_like_max_duration_ms": {
"description": "Auto-mode fallback: maximum duration (ms) that a very small stream is still treated as wheel-like.\n\nThis is only used when `scroll_events_per_tick` is effectively 1 (one event per wheel notch). In that case, we cannot observe a \"tick completion time\", so TUI2 treats a short-lived, small stream (<= 2 events) as wheel-like to preserve classic wheel behavior.",
"format": "uint64",
"minimum": 0.0,
"type": "integer"
},
"scroll_wheel_lines": {
"description": "Override how many transcript lines one physical *wheel notch* should scroll in TUI2.\n\nThis is the \"classic feel\" knob. Defaults to 3.\n\nWheel-like per-event contribution is `scroll_wheel_lines / scroll_events_per_tick`. For example, in a terminal that emits 9 events per notch, the default `3 / 9` yields 1/3 of a line per event and totals 3 lines once the full notch burst arrives.\n\nSee `codex-rs/tui2/docs/scroll_input_model.md` for details on the stream model and the wheel/trackpad heuristic.",
"format": "uint16",
"minimum": 0.0,
"type": "integer"
},
"scroll_wheel_tick_detect_max_ms": {
"description": "Auto-mode threshold: maximum time (ms) for the first tick-worth of events to arrive.\n\nIn `scroll_mode = \"auto\"`, TUI2 starts a stream as trackpad-like (to avoid overshoot) and promotes it to wheel-like if `scroll_events_per_tick` events arrive \"quickly enough\". This threshold controls what \"quickly enough\" means.\n\nMost users should leave this unset; it is primarily for terminals that emit wheel ticks batched over longer time spans.",
"format": "uint64",
"minimum": 0.0,
"type": "integer"
},
"show_tooltips": {
"default": true,
"description": "Show startup tooltips in the TUI welcome screen. Defaults to `true`.",
@@ -1246,9 +1161,6 @@
"steer": {
"type": "boolean"
},
"tui2": {
"type": "boolean"
},
"undo": {
"type": "boolean"
},
-119
View File
@@ -13,7 +13,6 @@ use crate::config::types::OtelConfigToml;
use crate::config::types::OtelExporterKind;
use crate::config::types::Personality;
use crate::config::types::SandboxWorkspaceWrite;
use crate::config::types::ScrollInputMode;
use crate::config::types::ShellEnvironmentPolicy;
use crate::config::types::ShellEnvironmentPolicyToml;
use crate::config::types::SkillsConfig;
@@ -200,58 +199,6 @@ pub struct Config {
/// Show startup tooltips in the TUI welcome screen.
pub show_tooltips: bool,
/// Override the events-per-wheel-tick factor for TUI2 scroll normalization.
///
/// This is the same `tui.scroll_events_per_tick` value from `config.toml`, plumbed through the
/// merged [`Config`] object (see [`Tui`]) so TUI2 can normalize scroll event density per
/// terminal.
pub tui_scroll_events_per_tick: Option<u16>,
/// Override the number of lines applied per wheel tick in TUI2.
///
/// This is the same `tui.scroll_wheel_lines` value from `config.toml` (see [`Tui`]). TUI2
/// applies it to wheel-like scroll streams. Trackpad-like scrolling uses a separate
/// `tui.scroll_trackpad_lines` setting.
pub tui_scroll_wheel_lines: Option<u16>,
/// Override the number of lines per tick-equivalent used for trackpad scrolling in TUI2.
///
/// This is the same `tui.scroll_trackpad_lines` value from `config.toml` (see [`Tui`]).
pub tui_scroll_trackpad_lines: Option<u16>,
/// Trackpad acceleration: approximate number of events required to gain +1x speed in TUI2.
///
/// This is the same `tui.scroll_trackpad_accel_events` value from `config.toml` (see [`Tui`]).
pub tui_scroll_trackpad_accel_events: Option<u16>,
/// Trackpad acceleration: maximum multiplier applied to trackpad-like streams in TUI2.
///
/// This is the same `tui.scroll_trackpad_accel_max` value from `config.toml` (see [`Tui`]).
pub tui_scroll_trackpad_accel_max: Option<u16>,
/// Control how TUI2 interprets mouse scroll input (wheel vs trackpad).
///
/// This is the same `tui.scroll_mode` value from `config.toml` (see [`Tui`]).
pub tui_scroll_mode: ScrollInputMode,
/// Override the wheel tick detection threshold (ms) for TUI2 auto scroll mode.
///
/// This is the same `tui.scroll_wheel_tick_detect_max_ms` value from `config.toml` (see
/// [`Tui`]).
pub tui_scroll_wheel_tick_detect_max_ms: Option<u64>,
/// Override the wheel-like end-of-stream threshold (ms) for TUI2 auto scroll mode.
///
/// This is the same `tui.scroll_wheel_like_max_duration_ms` value from `config.toml` (see
/// [`Tui`]).
pub tui_scroll_wheel_like_max_duration_ms: Option<u64>,
/// Invert mouse scroll direction for TUI2.
///
/// This is the same `tui.scroll_invert` value from `config.toml` (see [`Tui`]) and is applied
/// consistently to both mouse wheels and trackpads.
pub tui_scroll_invert: bool,
/// Controls whether the TUI uses the terminal's alternate screen buffer.
///
/// This is the same `tui.alternate_screen` value from `config.toml` (see [`Tui`]).
@@ -1613,27 +1560,6 @@ impl Config {
.unwrap_or_default(),
animations: cfg.tui.as_ref().map(|t| t.animations).unwrap_or(true),
show_tooltips: cfg.tui.as_ref().map(|t| t.show_tooltips).unwrap_or(true),
tui_scroll_events_per_tick: cfg.tui.as_ref().and_then(|t| t.scroll_events_per_tick),
tui_scroll_wheel_lines: cfg.tui.as_ref().and_then(|t| t.scroll_wheel_lines),
tui_scroll_trackpad_lines: cfg.tui.as_ref().and_then(|t| t.scroll_trackpad_lines),
tui_scroll_trackpad_accel_events: cfg
.tui
.as_ref()
.and_then(|t| t.scroll_trackpad_accel_events),
tui_scroll_trackpad_accel_max: cfg
.tui
.as_ref()
.and_then(|t| t.scroll_trackpad_accel_max),
tui_scroll_mode: cfg.tui.as_ref().map(|t| t.scroll_mode).unwrap_or_default(),
tui_scroll_wheel_tick_detect_max_ms: cfg
.tui
.as_ref()
.and_then(|t| t.scroll_wheel_tick_detect_max_ms),
tui_scroll_wheel_like_max_duration_ms: cfg
.tui
.as_ref()
.and_then(|t| t.scroll_wheel_like_max_duration_ms),
tui_scroll_invert: cfg.tui.as_ref().map(|t| t.scroll_invert).unwrap_or(false),
tui_alternate_screen: cfg
.tui
.as_ref()
@@ -1886,15 +1812,6 @@ persistence = "none"
notifications: Notifications::Enabled(true),
animations: true,
show_tooltips: true,
scroll_events_per_tick: None,
scroll_wheel_lines: None,
scroll_trackpad_lines: None,
scroll_trackpad_accel_events: None,
scroll_trackpad_accel_max: None,
scroll_mode: ScrollInputMode::Auto,
scroll_wheel_tick_detect_max_ms: None,
scroll_wheel_like_max_duration_ms: None,
scroll_invert: false,
alternate_screen: AltScreenMode::Auto,
}
);
@@ -3782,15 +3699,6 @@ model_verbosity = "high"
show_tooltips: true,
analytics_enabled: Some(true),
feedback_enabled: true,
tui_scroll_events_per_tick: None,
tui_scroll_wheel_lines: None,
tui_scroll_trackpad_lines: None,
tui_scroll_trackpad_accel_events: None,
tui_scroll_trackpad_accel_max: None,
tui_scroll_mode: ScrollInputMode::Auto,
tui_scroll_wheel_tick_detect_max_ms: None,
tui_scroll_wheel_like_max_duration_ms: None,
tui_scroll_invert: false,
tui_alternate_screen: AltScreenMode::Auto,
otel: OtelConfig::default(),
},
@@ -3871,15 +3779,6 @@ model_verbosity = "high"
show_tooltips: true,
analytics_enabled: Some(true),
feedback_enabled: true,
tui_scroll_events_per_tick: None,
tui_scroll_wheel_lines: None,
tui_scroll_trackpad_lines: None,
tui_scroll_trackpad_accel_events: None,
tui_scroll_trackpad_accel_max: None,
tui_scroll_mode: ScrollInputMode::Auto,
tui_scroll_wheel_tick_detect_max_ms: None,
tui_scroll_wheel_like_max_duration_ms: None,
tui_scroll_invert: false,
tui_alternate_screen: AltScreenMode::Auto,
otel: OtelConfig::default(),
};
@@ -3975,15 +3874,6 @@ model_verbosity = "high"
show_tooltips: true,
analytics_enabled: Some(false),
feedback_enabled: true,
tui_scroll_events_per_tick: None,
tui_scroll_wheel_lines: None,
tui_scroll_trackpad_lines: None,
tui_scroll_trackpad_accel_events: None,
tui_scroll_trackpad_accel_max: None,
tui_scroll_mode: ScrollInputMode::Auto,
tui_scroll_wheel_tick_detect_max_ms: None,
tui_scroll_wheel_like_max_duration_ms: None,
tui_scroll_invert: false,
tui_alternate_screen: AltScreenMode::Auto,
otel: OtelConfig::default(),
};
@@ -4065,15 +3955,6 @@ model_verbosity = "high"
show_tooltips: true,
analytics_enabled: Some(true),
feedback_enabled: true,
tui_scroll_events_per_tick: None,
tui_scroll_wheel_lines: None,
tui_scroll_trackpad_lines: None,
tui_scroll_trackpad_accel_events: None,
tui_scroll_trackpad_accel_max: None,
tui_scroll_mode: ScrollInputMode::Auto,
tui_scroll_wheel_tick_detect_max_ms: None,
tui_scroll_wheel_like_max_duration_ms: None,
tui_scroll_invert: false,
tui_alternate_screen: AltScreenMode::Auto,
otel: OtelConfig::default(),
};
-120
View File
@@ -418,23 +418,6 @@ impl Default for Notifications {
}
}
/// How TUI2 should interpret mouse scroll events.
///
/// Terminals generally encode both mouse wheels and trackpads as the same "scroll up/down" mouse
/// button events, without a magnitude. This setting controls whether Codex uses a heuristic to
/// infer wheel vs trackpad per stream, or forces a specific behavior.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, Default)]
#[serde(rename_all = "snake_case")]
pub enum ScrollInputMode {
/// Infer wheel vs trackpad behavior per scroll stream.
#[default]
Auto,
/// Always treat scroll events as mouse-wheel input (fixed lines per tick).
Wheel,
/// Always treat scroll events as trackpad input (fractional accumulation).
Trackpad,
}
/// Collection of settings that are specific to the TUI.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
#[schemars(deny_unknown_fields)]
@@ -454,109 +437,6 @@ pub struct Tui {
#[serde(default = "default_true")]
pub show_tooltips: bool,
/// Override the *wheel* event density used to normalize TUI2 scrolling.
///
/// Terminals generally deliver both mouse wheels and trackpads as discrete `scroll up/down`
/// mouse events with direction but no magnitude. Unfortunately, the *number* of raw events
/// per physical wheel notch varies by terminal (commonly 1, 3, or 9+). TUI2 uses this value
/// to normalize that raw event density into consistent "wheel tick" behavior.
///
/// Wheel math (conceptually):
///
/// - A single event contributes `1 / scroll_events_per_tick` tick-equivalents.
/// - Wheel-like streams then scale that by `scroll_wheel_lines` so one physical notch scrolls
/// a fixed number of lines.
///
/// Trackpad math is intentionally *not* fully tied to this value: in trackpad-like mode, TUI2
/// uses `min(scroll_events_per_tick, 3)` as the divisor so terminals with dense wheel ticks
/// (e.g. 9 events per notch) do not make trackpads feel artificially slow.
///
/// Defaults are derived per terminal from [`crate::terminal::TerminalInfo`] when TUI2 starts.
/// See `codex-rs/tui2/docs/scroll_input_model.md` for the probe data and rationale.
pub scroll_events_per_tick: Option<u16>,
/// Override how many transcript lines one physical *wheel notch* should scroll in TUI2.
///
/// This is the "classic feel" knob. Defaults to 3.
///
/// Wheel-like per-event contribution is `scroll_wheel_lines / scroll_events_per_tick`. For
/// example, in a terminal that emits 9 events per notch, the default `3 / 9` yields 1/3 of a
/// line per event and totals 3 lines once the full notch burst arrives.
///
/// See `codex-rs/tui2/docs/scroll_input_model.md` for details on the stream model and the
/// wheel/trackpad heuristic.
pub scroll_wheel_lines: Option<u16>,
/// Override baseline trackpad scroll sensitivity in TUI2.
///
/// Trackpads do not have discrete notches, but terminals still emit discrete `scroll up/down`
/// events. In trackpad-like mode, TUI2 accumulates fractional scroll and only applies whole
/// lines to the viewport.
///
/// Trackpad per-event contribution is:
///
/// - `scroll_trackpad_lines / min(scroll_events_per_tick, 3)`
///
/// (plus optional bounded acceleration; see `scroll_trackpad_accel_*`). The `min(..., 3)`
/// divisor is deliberate: `scroll_events_per_tick` is calibrated from *wheel* behavior and
/// can be much larger than trackpad event density, which would otherwise make trackpads feel
/// too slow in dense-wheel terminals.
///
/// Defaults to 1, meaning one tick-equivalent maps to one transcript line.
pub scroll_trackpad_lines: Option<u16>,
/// Trackpad acceleration: approximate number of events required to gain +1x speed in TUI2.
///
/// This keeps small swipes precise while allowing large/faster swipes to cover more content.
/// Defaults are chosen to address terminals where trackpad event density is comparatively low.
///
/// Concretely, TUI2 computes an acceleration multiplier for trackpad-like streams:
///
/// - `multiplier = clamp(1 + abs(events) / scroll_trackpad_accel_events, 1..scroll_trackpad_accel_max)`
///
/// The multiplier is applied to the streams computed line delta (including any carried
/// fractional remainder).
pub scroll_trackpad_accel_events: Option<u16>,
/// Trackpad acceleration: maximum multiplier applied to trackpad-like streams.
///
/// Set to 1 to effectively disable trackpad acceleration.
///
/// See [`Tui::scroll_trackpad_accel_events`] for the exact multiplier formula.
pub scroll_trackpad_accel_max: Option<u16>,
/// Select how TUI2 interprets mouse scroll input.
///
/// - `auto` (default): infer wheel vs trackpad per scroll stream.
/// - `wheel`: always use wheel behavior (fixed lines per wheel notch).
/// - `trackpad`: always use trackpad behavior (fractional accumulation; wheel may feel slow).
#[serde(default)]
pub scroll_mode: ScrollInputMode,
/// Auto-mode threshold: maximum time (ms) for the first tick-worth of events to arrive.
///
/// In `scroll_mode = "auto"`, TUI2 starts a stream as trackpad-like (to avoid overshoot) and
/// promotes it to wheel-like if `scroll_events_per_tick` events arrive "quickly enough". This
/// threshold controls what "quickly enough" means.
///
/// Most users should leave this unset; it is primarily for terminals that emit wheel ticks
/// batched over longer time spans.
pub scroll_wheel_tick_detect_max_ms: Option<u64>,
/// Auto-mode fallback: maximum duration (ms) that a very small stream is still treated as wheel-like.
///
/// This is only used when `scroll_events_per_tick` is effectively 1 (one event per wheel
/// notch). In that case, we cannot observe a "tick completion time", so TUI2 treats a
/// short-lived, small stream (<= 2 events) as wheel-like to preserve classic wheel behavior.
pub scroll_wheel_like_max_duration_ms: Option<u64>,
/// Invert mouse scroll direction in TUI2.
///
/// This flips the scroll sign after terminal detection. It is applied consistently to both
/// wheel and trackpad input.
#[serde(default)]
pub scroll_invert: bool,
/// Controls whether the TUI uses the terminal's alternate screen buffer.
///
/// - `auto` (default): Disable alternate screen in Zellij, enable elsewhere.
-8
View File
@@ -95,8 +95,6 @@ pub enum Feature {
ShellSnapshot,
/// Append additional AGENTS.md guidance to user instructions.
ChildAgentsMd,
/// Experimental TUI v2 (viewport) implementation.
Tui2,
/// Enforce UTF8 output in Powershell.
PowershellUtf8,
/// Compress request bodies (zstd) when sending streaming requests to codex-backend.
@@ -441,12 +439,6 @@ pub const FEATURES: &[FeatureSpec] = &[
},
default_enabled: false,
},
FeatureSpec {
id: Feature::Tui2,
key: "tui2",
stage: Stage::Beta,
default_enabled: false,
},
FeatureSpec {
id: Feature::Steer,
key: "steer",
+1 -2
View File
@@ -6,8 +6,7 @@ When changing the paste-burst or chat-composer state machines in this folder, ke
readable, top-down explanation of the current behavior.
- Update the narrative doc `docs/tui-chat-composer.md` whenever behavior/assumptions change (Enter
handling, retro-capture, flush/clear rules, `disable_paste_burst`, non-ASCII/IME handling).
- Keep `codex-rs/tui` and `codex-rs/tui2` implementations/docstrings aligned unless the divergence
is intentional and documented.
- Keep implementations/docstrings aligned unless a divergence is intentional and documented.
Practical check:
-20
View File
@@ -1,20 +0,0 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "tui2",
crate_name = "codex_tui2",
compile_data = glob(
include = ["**"],
exclude = [
"**/* *",
"BUILD.bazel",
"Cargo.toml",
],
allow_empty = True,
),
test_data_extra = glob(["src/**/snapshots/**"]),
integration_compile_data_extra = ["src/test_backend.rs"],
extra_binaries = [
"//codex-rs/cli:codex",
],
)
-123
View File
@@ -1,123 +0,0 @@
[package]
name = "codex-tui2"
version.workspace = true
edition.workspace = true
license.workspace = true
[[bin]]
name = "codex-tui2"
path = "src/main.rs"
[lib]
name = "codex_tui2"
path = "src/lib.rs"
[features]
# Enable vt100-based tests (emulator) when running with `--features vt100-tests`.
vt100-tests = []
# Gate verbose debug logging inside the TUI implementation.
debug-logs = []
[lints]
workspace = true
[dependencies]
anyhow = { workspace = true }
async-stream = { workspace = true }
base64 = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
clap = { workspace = true, features = ["derive"] }
codex-ansi-escape = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-arg0 = { workspace = true }
codex-backend-client = { workspace = true }
codex-common = { workspace = true, features = [
"cli",
"elapsed",
"sandbox_summary",
] }
codex-core = { workspace = true }
codex-feedback = { workspace = true }
codex-file-search = { workspace = true }
codex-login = { workspace = true }
codex-otel = { workspace = true }
codex-protocol = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-tui = { workspace = true }
color-eyre = { workspace = true }
crossterm = { workspace = true, features = ["bracketed-paste", "event-stream"] }
derive_more = { workspace = true, features = ["is_variant"] }
diffy = { workspace = true }
dirs = { workspace = true }
dunce = { workspace = true }
image = { workspace = true, features = ["jpeg", "png"] }
itertools = { workspace = true }
lazy_static = { workspace = true }
mcp-types = { workspace = true }
pathdiff = { workspace = true }
pulldown-cmark = { workspace = true }
rand = { workspace = true }
ratatui = { workspace = true, features = [
"scrolling-regions",
"unstable-backend-writer",
"unstable-rendered-line-info",
"unstable-widget-ref",
] }
ratatui-core = { workspace = true }
ratatui-macros = { workspace = true }
regex-lite = { workspace = true }
reqwest = { version = "0.12", features = ["json"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["preserve_order"] }
shlex = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
supports-color = { workspace = true }
tempfile = { workspace = true }
textwrap = { workspace = true }
tui-scrollbar = { workspace = true }
tokio = { workspace = true, features = [
"io-std",
"macros",
"process",
"rt-multi-thread",
"signal",
"test-util",
"time",
] }
tokio-stream = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true, features = ["log"] }
tracing-appender = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
tree-sitter-bash = { workspace = true }
tree-sitter-highlight = { workspace = true }
unicode-segmentation = { workspace = true }
unicode-width = { workspace = true }
url = { workspace = true }
codex-windows-sandbox = { workspace = true }
tokio-util = { workspace = true, features = ["time"] }
[target.'cfg(unix)'.dependencies]
libc = { workspace = true }
# Clipboard support via `arboard` is not available on Android/Termux.
# Only include it for non-Android targets so the crate builds on Android.
[target.'cfg(not(target_os = "android"))'.dependencies]
arboard = { workspace = true }
[dev-dependencies]
assert_matches = { workspace = true }
codex-cli = { workspace = true }
codex-core = { workspace = true, features = ["test-support"] }
codex-utils-cargo-bin = { workspace = true }
codex-utils-pty = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
insta = { workspace = true }
pretty_assertions = { workspace = true }
rand = { workspace = true }
serial_test = { workspace = true }
vt100 = { workspace = true }
uuid = { workspace = true }
-610
View File
@@ -1,610 +0,0 @@
# TUI2 Scroll Input: Model and Implementation
This is the single "scrolling doc of record" for TUI2.
It describes what we implemented, why it works, and what we tried before this approach.
It also preserves the scroll-probe findings (see Appendix) that motivated the model.
Code reference: `codex-rs/tui2/src/tui/scrolling/mouse.rs`.
## Goals and constraints
Goals:
- Mouse wheel: scroll about **3 transcript lines per physical wheel tick** regardless of terminal
event density (classic feel).
- Trackpad: remain **higher fidelity**, meaning small movements can accumulate fractionally and
should not be forced into wheel behavior.
- Work across terminals where a single wheel tick may produce 1, 3, 9, or more raw events.
Constraints:
- Terminals typically encode both wheels and trackpads as the same "scroll up/down" mouse button
events without a magnitude. We cannot reliably observe device type directly.
- Timing alone is not a reliable discriminator (wheel and trackpad bursts overlap).
## Current implementation (stream-based; data-driven)
TUI2 uses a stream model: scroll events are grouped into short streams separated by silence.
Within a stream, we normalize by a per-terminal "events per tick" factor and then apply either
wheel-like (fixed lines per tick) or trackpad-like (fractional) semantics.
### 1. Stream detection
- A stream begins on the first scroll event.
- A stream ends when the gap since the last event exceeds `STREAM_GAP_MS` or when direction flips.
- Direction flips always close the current stream and start a new one, so we never blend "up" and
"down" into a single accumulator.
This makes behavior stable across:
- Dense bursts (Warp/Ghostty-style sub-ms intervals).
- Sparse bursts (single events separated by tens or hundreds of ms).
- Mixed wheel + trackpad input where direction changes quickly.
### 2. Normalization: events-per-tick
Different terminals emit different numbers of raw events per physical wheel notch.
We normalize by converting raw events into tick-equivalents:
`tick_equivalents = raw_events / events_per_tick`
Per-terminal defaults come from the probe logs (Appendix), and users can override them.
Config key: `tui.scroll_events_per_tick`.
### 3. Wheel vs trackpad behavior (and why it is heuristic)
Because device type is not directly observable, the implementation provides a mode setting:
- `tui.scroll_mode = "auto"` (default): infer wheel-like vs trackpad-like behavior per stream.
- `tui.scroll_mode = "wheel"`: always treat streams as wheel-like.
- `tui.scroll_mode = "trackpad"`: always treat streams as trackpad-like.
In auto mode:
- Streams start trackpad-like (safer: avoids overshoot when we guess wrong).
- Streams promote to wheel-like when the first tick-worth of events arrives quickly.
- For 1-event-per-tick terminals, "first tick completion time" is not observable, so there is a
conservative end-of-stream fallback for very small bursts.
This design assumes that auto classification is a best-effort heuristic and must be overridable.
### 4. Applying scroll: wheel-like streams
Wheel-like streams target the "classic feel" requirement.
- Each raw event contributes `tui.scroll_wheel_lines / events_per_tick` lines.
- Deltas flush immediately (not cadence-gated) so wheels feel snappy even on dense streams.
- Wheel-like streams apply a minimum +/- 1 line when events were received but rounding would yield 0.
Defaults:
- `tui.scroll_wheel_lines = 3`
### 5. Applying scroll: trackpad-like streams
Trackpad-like streams are designed for fidelity first.
- Each raw event contributes `tui.scroll_trackpad_lines / trackpad_events_per_tick` lines.
- Fractional remainder is carried across streams, so tiny gestures accumulate instead of being lost.
- Trackpad deltas are cadence-gated to ~60 Hz (`REDRAW_CADENCE_MS`) to avoid redraw floods and to
reduce "stop lag" / overshoot.
- Trackpad streams intentionally do not apply a minimum +/- 1 line at stream end; if a gesture is
small enough to round to 0, it should feel like "no movement", not a forced jump.
Dense wheel terminals (e.g. Ghostty/Warp) can emit trackpad streams with high event density.
Using a wheel-derived `events_per_tick = 9` for trackpad would make trackpads feel slow, so we use
a capped divisor for trackpad normalization:
- `trackpad_events_per_tick = min(events_per_tick, 3)`
Additionally, to keep small gestures precise while making large/fast swipes cover more content,
trackpad-like streams apply bounded acceleration based on event count:
- `tui.scroll_trackpad_accel_events`: how many events correspond to +1x multiplier.
- `tui.scroll_trackpad_accel_max`: maximum multiplier.
### 6. Guard rails and axis handling
- Horizontal scroll events are ignored for vertical scrolling.
- Streams clamp event counts and accumulated line deltas to avoid floods.
## Terminal defaults and per-terminal tuning
Defaults are keyed by `TerminalName` (terminal family), not exact version.
Probe data is version-specific, so defaults should be revalidated as more logs arrive.
Events-per-tick defaults derived from `wheel_single` medians:
- AppleTerminal: 3
- WarpTerminal: 9
- WezTerm: 1
- Alacritty: 3
- Ghostty: 3
- Iterm2: 1
- VsCode: 1
- Kitty: 3
- Unknown: 3
Note: probe logs measured Ghostty at ~9 events per tick, but we default to 3 because an upstream
Ghostty change is expected to reduce wheel event density. Users can override with
`tui.scroll_events_per_tick`.
Auto-mode wheel promotion thresholds can also be tuned per terminal if needed (see config below).
## Configuration knobs (TUI2)
These are user-facing knobs in `config.toml` under `[tui]`:
In this repo, "tick" always refers to a physical mouse wheel notch. Trackpads do not have ticks, so
trackpad settings are expressed in terms of "tick-equivalents" (raw events normalized to a common
scale).
The core normalization formulas are:
- Wheel-like streams:
- `lines_per_event = scroll_wheel_lines / scroll_events_per_tick`
- Trackpad-like streams:
- `lines_per_event = scroll_trackpad_lines / min(scroll_events_per_tick, 3)`
- (plus bounded acceleration from `scroll_trackpad_accel_*` and fractional carry across streams)
Keys:
- `scroll_events_per_tick` (number):
- Raw vertical scroll events per physical wheel notch in your terminal (normalization input).
- Affects wheel-like scroll speed and auto-mode wheel promotion timing.
- Trackpad-like mode uses `min(..., 3)` as the divisor so dense wheel ticks (e.g. 9 events per
notch) do not make trackpads feel artificially slow.
- `scroll_wheel_lines` (number):
- Lines per physical wheel notch (default 3).
- Change this if you want "classic" wheel scrolling to be more/less aggressive globally.
- `scroll_trackpad_lines` (number):
- Baseline trackpad sensitivity in trackpad-like mode (default 1).
- Change this if your trackpad feels consistently too slow/fast for small motions.
- `scroll_trackpad_accel_events` (number):
- Trackpad acceleration tuning (default 30). Smaller values accelerate earlier.
- Trackpad-like streams compute a multiplier:
- `multiplier = clamp(1 + abs(events) / scroll_trackpad_accel_events, 1..scroll_trackpad_accel_max)`
- The multiplier is applied to the trackpad streams computed line delta (including any carried
fractional remainder).
- `scroll_trackpad_accel_max` (number):
- Trackpad acceleration cap (default 3). Set to 1 to effectively disable acceleration.
- `scroll_mode` (`auto` | `wheel` | `trackpad`):
- `auto` (default): infer wheel-like vs trackpad-like per stream.
- `wheel`: always wheel-like (good for wheel-only setups; trackpads will feel jumpy).
- `trackpad`: always trackpad-like (good if auto misclassifies; wheels may feel slow).
- `scroll_wheel_tick_detect_max_ms` (number):
- Auto-mode promotion threshold: how quickly the first tick-worth of events must arrive to
consider the stream wheel-like.
- If wheel feels slow in a dense-wheel terminal, increasing this is usually better than changing
`scroll_events_per_tick`.
- `scroll_wheel_like_max_duration_ms` (number):
- Auto-mode fallback for 1-event-per-tick terminals (WezTerm/iTerm/VS Code).
- If wheel feels like trackpad (too slow) in those terminals, increasing this can help.
- `scroll_invert` (bool):
- Invert direction after terminal detection; applies consistently to wheel and trackpad.
## Previous approaches tried (and why they were replaced)
1. Cadence-based inference (rolling inter-event thresholds)
- Approach: infer wheel vs trackpad using inter-event timing thresholds (burst vs frame cadence vs slow),
with terminal-specific tuning.
- Problem: terminals differ more in event density and batching than in timing; timing overlaps heavily
between wheel and trackpad. Small threshold changes had outsized, terminal-specific effects.
2. Pure event-count or pure duration classification
- Approach: classify wheel-like vs trackpad-like by event count <= N or duration <= M.
- Problem: burst length overlaps heavily across devices/terminals; duration is more separable but still
not strong enough to be authoritative.
3. Why streams + normalization won
- Streams give a stable unit ("what did the user do in one gesture?") that we can bound and reason about.
- Normalization directly addresses the main cross-terminal source of variation: raw event density.
- Classification remains heuristic, but is isolated and configurable.
## Appendix A: Follow-up analysis (latest log per terminal; 2025-12-20)
This section is derived from a "latest log per terminal" subset analysis. The exact event count is
not significant; it is included only as a note about which subset was used.
Key takeaways:
- Burst length overlaps heavily between wheel and trackpad. Simple "event count <= N" classifiers perform poorly.
- Burst span (duration) is more separable: wheel bursts typically complete in < ~180-200 ms, while trackpad
bursts are often hundreds of milliseconds.
- Conclusion: explicit wheel vs trackpad classification is inherently weak from these events; prefer a
stream model, plus a small heuristic and a config override (`tui.scroll_mode`) for edge cases.
Data notes (latest per terminal label):
- Logs used (one per terminal, by filename timestamp):
- mouse_scroll_log_Apple_Terminal_2025-12-19T19-53-54Z.jsonl
- mouse_scroll_log_WarpTerminal_2025-12-19T19-59-38Z.jsonl
- mouse_scroll_log_WezTerm_2025-12-19T20-00-36Z.jsonl
- mouse_scroll_log_alacritty_2025-12-19T19-56-45Z.jsonl
- mouse_scroll_log_ghostty_2025-12-19T19-52-44Z.jsonl
- mouse_scroll_log_iTerm_app_2025-12-19T19-55-08Z.jsonl
- mouse_scroll_log_vscode_2025-12-19T19-51-20Z.jsonl
- mouse_scroll_log_xterm-kitty_2025-12-19T19-58-19Z.jsonl
Per-terminal burst separability (wheel vs trackpad), summarized as median and p90:
- Apple Terminal:
- Wheel: length median 9.5 (p90 49), span median 94 ms (p90 136)
- Trackpad: length median 13.5 (p90 104), span median 238 ms (p90 616)
- Warp:
- Wheel: length median 43 (p90 169), span median 88 ms (p90 178)
- Trackpad: length median 60 (p90 82), span median 358 ms (p90 721)
- WezTerm:
- Wheel: length median 4 (p90 10), span median 91 ms (p90 156)
- Trackpad: length median 10.5 (p90 36), span median 270 ms (p90 348)
- alacritty:
- Wheel: length median 14 (p90 63), span median 109 ms (p90 158)
- Trackpad: length median 12.5 (p90 63), span median 372 ms (p90 883)
- ghostty:
- Wheel: length median 32.5 (p90 163), span median 99 ms (p90 157)
- Trackpad: length median 14.5 (p90 60), span median 366 ms (p90 719)
- iTerm:
- Wheel: length median 4 (p90 9), span median 91 ms (p90 230)
- Trackpad: length median 9 (p90 36), span median 223 ms (p90 540)
- VS Code:
- Wheel: length median 3 (p90 9), span median 94 ms (p90 120)
- Trackpad: length median 3 (p90 12), span median 192 ms (p90 468)
- Kitty:
- Wheel: length median 15.5 (p90 59), span median 87 ms (p90 233)
- Trackpad: length median 15.5 (p90 68), span median 292 ms (p90 563)
Wheel_single medians (events per tick) in the latest logs:
- Apple: 3
- Warp: 9
- WezTerm: 1
- alacritty: 3
- ghostty: 9 (measured); TUI2 defaults use 3 because an upstream Ghostty change is expected to
reduce wheel event density. If your Ghostty build still emits ~9 events per wheel tick, set
`tui.scroll_events_per_tick = 9`.
- iTerm: 1
- VS Code: 1
- Kitty: 3
## Appendix B: Scroll probe findings (authoritative; preserved verbatim)
The remainder of this document is preserved from the original scroll-probe spec.
It is intentionally not rewritten so the data and rationale remain auditable.
Note: the original text uses "events per line" terminology; the implementation treats this as an
events-per-wheel-tick normalization factor (see "Normalization: events-per-tick").
Note: the pseudocode in the preserved spec is not the exact current implementation; it is kept as
historical context for how the probe data originally mapped into an algorithm. The current
implementation is described in the sections above.
## 1. TL;DR
Analysis of 16 scroll-probe logs (13,734 events) across 8 terminals shows large per-terminal variation in how many raw events are emitted per physical wheel tick (1-9+ events). Timing alone does not distinguish wheel vs trackpad; event counts and burst duration are more reliable. The algorithm below treats scroll input as short streams separated by gaps, normalizes events into line deltas using a per-terminal events-per-line factor, coalesces redraws at 60 Hz, and applies a minimum 1-line delta for discrete bursts. This yields stable behavior across dense streams, sparse bursts, and terminals that emit horizontal events.
## 2. Data overview
- Logs analyzed: 16
- Total events: 13,734
- Terminals covered:
- Apple_Terminal 455.1
- WarpTerminal v0.2025.12.17.17.stable_02
- WezTerm 20240203-110809-5046fc22
- alacritty
- ghostty 1.2.3
- iTerm.app 3.6.6
- vscode 1.107.1
- xterm-kitty
- Scenarios captured: `wheel_single`, `wheel_small`, `wheel_long`, `trackpad_single`, `trackpad_slow`, `trackpad_fast` (directional up/down variants treated as distinct bursts).
- Legacy `wheel_scroll_*` logs are mapped to `wheel_small` in analysis.
## 3. Cross-terminal comparison table
| Terminal | Scenario | Median Dt (ms) | P95 Dt (ms) | Typical burst | Notes |
| --------------------------------------- | --------------- | -------------: | ----------: | ------------: | ----------- |
| Apple_Terminal 455.1 | wheel_single | 0.14 | 97.68 | 3 |
| Apple_Terminal 455.1 | wheel_small | 0.12 | 23.81 | 19 |
| Apple_Terminal 455.1 | wheel_long | 0.03 | 15.93 | 48 |
| Apple_Terminal 455.1 | trackpad_single | 92.35 | 213.15 | 2 |
| Apple_Terminal 455.1 | trackpad_slow | 11.30 | 75.46 | 14 |
| Apple_Terminal 455.1 | trackpad_fast | 0.13 | 8.92 | 96 |
| WarpTerminal v0.2025.12.17.17.stable_02 | wheel_single | 0.07 | 0.34 | 9 |
| WarpTerminal v0.2025.12.17.17.stable_02 | wheel_small | 0.05 | 5.04 | 65 |
| WarpTerminal v0.2025.12.17.17.stable_02 | wheel_long | 0.01 | 0.42 | 166 |
| WarpTerminal v0.2025.12.17.17.stable_02 | trackpad_single | 9.77 | 32.64 | 10 |
| WarpTerminal v0.2025.12.17.17.stable_02 | trackpad_slow | 7.93 | 16.44 | 74 |
| WarpTerminal v0.2025.12.17.17.stable_02 | trackpad_fast | 5.40 | 10.04 | 74 |
| WezTerm 20240203-110809-5046fc22 | wheel_single | 416.07 | 719.64 | 1 |
| WezTerm 20240203-110809-5046fc22 | wheel_small | 19.41 | 50.19 | 6 |
| WezTerm 20240203-110809-5046fc22 | wheel_long | 13.19 | 29.96 | 10 |
| WezTerm 20240203-110809-5046fc22 | trackpad_single | 237.56 | 237.56 | 1 |
| WezTerm 20240203-110809-5046fc22 | trackpad_slow | 23.54 | 76.10 | 10 | 12.5% horiz |
| WezTerm 20240203-110809-5046fc22 | trackpad_fast | 7.10 | 24.86 | 32 | 12.6% horiz |
| alacritty | wheel_single | 0.09 | 0.33 | 3 |
| alacritty | wheel_small | 0.11 | 37.24 | 24 |
| alacritty | wheel_long | 0.01 | 15.96 | 56 |
| alacritty | trackpad_single | n/a | n/a | 1 |
| alacritty | trackpad_slow | 41.90 | 97.36 | 11 |
| alacritty | trackpad_fast | 3.07 | 25.13 | 62 |
| ghostty 1.2.3 | wheel_single | 0.05 | 0.20 | 9 |
| ghostty 1.2.3 | wheel_small | 0.05 | 7.18 | 52 |
| ghostty 1.2.3 | wheel_long | 0.02 | 1.16 | 146 |
| ghostty 1.2.3 | trackpad_single | 61.28 | 124.28 | 3 | 23.5% horiz |
| ghostty 1.2.3 | trackpad_slow | 23.10 | 76.30 | 14 | 34.7% horiz |
| ghostty 1.2.3 | trackpad_fast | 3.84 | 37.72 | 47 | 23.4% horiz |
| iTerm.app 3.6.6 | wheel_single | 74.96 | 80.61 | 1 |
| iTerm.app 3.6.6 | wheel_small | 20.79 | 84.83 | 6 |
| iTerm.app 3.6.6 | wheel_long | 16.70 | 50.91 | 9 |
| iTerm.app 3.6.6 | trackpad_single | n/a | n/a | 1 |
| iTerm.app 3.6.6 | trackpad_slow | 17.25 | 94.05 | 9 |
| iTerm.app 3.6.6 | trackpad_fast | 7.12 | 24.54 | 33 |
| vscode 1.107.1 | wheel_single | 58.01 | 58.01 | 1 |
| vscode 1.107.1 | wheel_small | 16.76 | 66.79 | 5 |
| vscode 1.107.1 | wheel_long | 9.86 | 32.12 | 8 |
| vscode 1.107.1 | trackpad_single | n/a | n/a | 1 |
| vscode 1.107.1 | trackpad_slow | 164.19 | 266.90 | 3 |
| vscode 1.107.1 | trackpad_fast | 16.78 | 61.05 | 11 |
| xterm-kitty | wheel_single | 0.16 | 51.74 | 3 |
| xterm-kitty | wheel_small | 0.10 | 24.12 | 26 |
| xterm-kitty | wheel_long | 0.01 | 16.10 | 56 |
| xterm-kitty | trackpad_single | 155.65 | 289.87 | 1 | 12.5% horiz |
| xterm-kitty | trackpad_slow | 16.89 | 67.04 | 16 | 30.4% horiz |
| xterm-kitty | trackpad_fast | 0.23 | 16.37 | 78 | 20.6% horiz |
## 4. Key findings
- Raw wheel ticks vary by terminal: median events per tick are 1 (WezTerm/iTerm/vscode), 3 (Apple/alacritty/kitty), and 9 (Warp/ghostty).
- Trackpad bursts are longer than wheel ticks but overlap in timing; inter-event timing alone does not distinguish device type.
- Continuous streams have short gaps: overall inter-event p99 is 70.67 ms; trackpad_slow p95 is 66.98 ms.
- Horizontal events appear only in trackpad scenarios and only in WezTerm/ghostty/kitty; ignore horizontal events for vertical scrolling.
- Burst duration is a reliable discrete/continuous signal:
- wheel_single median 0.15 ms (p95 80.61 ms)
- trackpad_single median 0 ms (p95 237.56 ms)
- wheel_small median 96.88 ms (p95 182.90 ms)
- trackpad_slow median 320.69 ms (p95 812.10 ms)
## 5. Scrolling model (authoritative)
**Stream detection.** Treat scroll input as short streams separated by silence. A stream begins on the first scroll event and ends when the gap since the last event exceeds `STREAM_GAP_MS` or the direction flips. Direction flip immediately closes the current stream and starts a new one.
**Normalization.** Convert raw events into line deltas using a per-terminal `EVENTS_PER_LINE` factor derived from the terminal's median `wheel_single` burst length. If no terminal override matches, use the global default (`3`).
**Discrete vs continuous.** Classify the stream after it ends:
- If `event_count <= DISCRETE_MAX_EVENTS` **and** `duration_ms <= DISCRETE_MAX_DURATION_MS`, treat as discrete.
- Otherwise treat as continuous.
**Discrete streams.** Apply the accumulated line delta immediately. If the stream's accumulated lines rounds to 0 but events were received, apply a minimum +/-1 line (respecting direction).
**Continuous streams.** Accumulate fractional lines and coalesce redraws to `REDRAW_CADENCE_MS`. Flush any remaining fractional lines on stream end (with the same +/-1 minimum if the stream had events but rounded to 0).
**Direction.** Always use the raw event direction. Provide a separate user-level invert option if needed; do not infer inversion from timing.
**Horizontal events.** Ignore horizontal events in vertical scroll logic.
## 6. Concrete constants (data-derived)
```text
STREAM_GAP_MS = 80
DISCRETE_MAX_EVENTS = 10
DISCRETE_MAX_DURATION_MS = 250
REDRAW_CADENCE_MS = 16
DEFAULT_EVENTS_PER_LINE = 3
MAX_EVENTS_PER_STREAM = 256
MAX_ACCUMULATED_LINES = 256
MIN_LINES_PER_DISCRETE_STREAM = 1
DEFAULT_WHEEL_LINES_PER_TICK = 3
```
Why these values:
- `STREAM_GAP_MS=80`: overall p99 inter-event gap is 70.67 ms; trackpad_slow p95 is 66.98 ms. 80 ms ends streams without splitting most continuous input.
- `DISCRETE_MAX_EVENTS=10`: wheel_single p95 burst = 9; trackpad_single p95 burst = 10.
- `DISCRETE_MAX_DURATION_MS=250`: trackpad_single p95 duration = 237.56 ms.
- `REDRAW_CADENCE_MS=16`: coalesces dense streams to ~60 Hz; trackpad_fast p95 Dt = 19.83 ms.
- `DEFAULT_EVENTS_PER_LINE=3`: global median wheel_single burst length.
- `MAX_EVENTS_PER_STREAM=256` and `MAX_ACCUMULATED_LINES=256`: highest observed burst is 206; cap to avoid floods.
- `DEFAULT_WHEEL_LINES_PER_TICK=3`: restores classic wheel speed; this is a UX choice rather than a data-derived constant.
## 7. Pseudocode (Rust-oriented)
```rust
// This is intentionally a simplified sketch of the current implementation.
// For the authoritative behavior, see `codex-rs/tui2/src/tui/scrolling/mouse.rs`.
enum StreamKind {
Unknown,
Wheel,
Trackpad,
}
struct Stream {
start: Instant,
last: Instant,
dir: i32,
event_count: usize,
accumulated_events: i32,
applied_lines: i32,
kind: StreamKind,
just_promoted: bool,
}
struct State {
stream: Option<Stream>,
carry_lines: f32,
last_redraw_at: Instant,
cfg: Config,
}
struct Config {
events_per_tick: u16,
wheel_lines_per_tick: u16,
trackpad_lines_per_tick: u16,
trackpad_accel_events: u16,
trackpad_accel_max: u16,
wheel_tick_detect_max: Duration,
}
fn on_scroll_event(dir: i32, now: Instant, st: &mut State) -> i32 {
// Close stream on idle gap or direction flip.
if let Some(stream) = st.stream.as_ref() {
let gap = now.duration_since(stream.last);
if gap > STREAM_GAP || stream.dir != dir {
finalize_stream(now, st);
st.stream = None;
}
}
let stream = st.stream.get_or_insert_with(|| Stream {
start: now,
last: now,
dir,
event_count: 0,
accumulated_events: 0,
applied_lines: 0,
kind: StreamKind::Unknown,
just_promoted: false,
});
stream.last = now;
stream.dir = dir;
stream.event_count = (stream.event_count + 1).min(MAX_EVENTS_PER_STREAM);
stream.accumulated_events =
(stream.accumulated_events + dir).clamp(-(MAX_EVENTS_PER_STREAM as i32), MAX_EVENTS_PER_STREAM as i32);
// Auto-mode promotion: promote to wheel-like when the first tick-worth of events arrives quickly.
if matches!(stream.kind, StreamKind::Unknown) {
let ept = st.cfg.events_per_tick.max(1) as usize;
if ept >= 2 && stream.event_count >= ept && now.duration_since(stream.start) <= st.cfg.wheel_tick_detect_max {
stream.kind = StreamKind::Wheel;
stream.just_promoted = true;
}
}
flush_lines(now, st)
}
fn on_tick(now: Instant, st: &mut State) -> i32 {
if let Some(stream) = st.stream.as_ref() {
let gap = now.duration_since(stream.last);
if gap > STREAM_GAP {
return finalize_stream(now, st);
}
}
flush_lines(now, st)
}
fn finalize_stream(now: Instant, st: &mut State) -> i32 {
// In auto mode, any stream that isn't wheel-like by promotion stays trackpad-like.
if let Some(stream) = st.stream.as_mut() {
if matches!(stream.kind, StreamKind::Unknown) {
stream.kind = StreamKind::Trackpad;
}
}
let lines = flush_lines(now, st);
// Carry fractional remainder across streams for trackpad-like input.
if let Some(stream) = st.stream.as_ref() {
if matches!(stream.kind, StreamKind::Trackpad) {
st.carry_lines = desired_lines_f32(st, stream) - stream.applied_lines as f32;
} else {
st.carry_lines = 0.0;
}
}
lines
}
fn flush_lines(now: Instant, st: &mut State) -> i32 {
let Some(stream) = st.stream.as_mut() else { return 0; };
let wheel_like = matches!(stream.kind, StreamKind::Wheel);
let cadence_elapsed = now.duration_since(st.last_redraw_at) >= REDRAW_CADENCE;
let should_flush = wheel_like || cadence_elapsed || stream.just_promoted;
if !should_flush {
return 0;
}
let desired_total = desired_lines_f32(st, stream);
let mut desired_lines = desired_total.trunc() as i32;
// Wheel guardrail: ensure we never produce a "dead tick" for non-zero input.
if wheel_like && desired_lines == 0 && stream.accumulated_events != 0 {
desired_lines = stream.accumulated_events.signum() * MIN_LINES_PER_DISCRETE_STREAM;
}
let mut delta = desired_lines - stream.applied_lines;
if delta == 0 {
return 0;
}
delta = delta.clamp(-MAX_ACCUMULATED_LINES, MAX_ACCUMULATED_LINES);
stream.applied_lines += delta;
stream.just_promoted = false;
st.last_redraw_at = now;
delta
}
fn desired_lines_f32(st: &State, stream: &Stream) -> f32 {
let wheel_like = matches!(stream.kind, StreamKind::Wheel);
let events_per_tick = if wheel_like {
st.cfg.events_per_tick.max(1) as f32
} else {
// Trackpad divisor is capped so dense wheel terminals don't feel slow for trackpads.
st.cfg.events_per_tick.clamp(1, DEFAULT_EVENTS_PER_LINE).max(1) as f32
};
let lines_per_tick = if wheel_like {
st.cfg.wheel_lines_per_tick.max(1) as f32
} else {
st.cfg.trackpad_lines_per_tick.max(1) as f32
};
let mut total = (stream.accumulated_events as f32 * (lines_per_tick / events_per_tick))
.clamp(-(MAX_ACCUMULATED_LINES as f32), MAX_ACCUMULATED_LINES as f32);
if !wheel_like {
total = (total + st.carry_lines).clamp(-(MAX_ACCUMULATED_LINES as f32), MAX_ACCUMULATED_LINES as f32);
// Bounded acceleration for large swipes (keep small swipes precise).
let event_count = stream.accumulated_events.abs() as f32;
let accel = (1.0 + (event_count / st.cfg.trackpad_accel_events.max(1) as f32))
.clamp(1.0, st.cfg.trackpad_accel_max.max(1) as f32);
total = (total * accel).clamp(-(MAX_ACCUMULATED_LINES as f32), MAX_ACCUMULATED_LINES as f32);
}
total
}
```
## 8. Terminal-specific adjustments (minimal)
Use per-terminal `EVENTS_PER_LINE` overrides derived from median `wheel_single` bursts:
```text
Apple_Terminal 455.1 = 3
WarpTerminal v0.2025.12.17.17.stable_02 = 9
WezTerm 20240203-110809-5046fc22 = 1
alacritty = 3
ghostty 1.2.3 = 9
iTerm.app 3.6.6 = 1
vscode 1.107.1 = 1
xterm-kitty = 3
```
If terminal is not matched, use `DEFAULT_EVENTS_PER_LINE = 3`.
## 9. Known weird cases and guardrails
- Extremely dense streams (sub-ms Dt) occur in Warp/ghostty/kitty; redraw coalescing is mandatory.
- Sparse bursts (hundreds of ms between events) occur in trackpad_single; do not merge them into long streams.
- Horizontal scroll events (12-35% of trackpad events in some terminals) must be ignored for vertical scrolling.
- Direction inversion is user-configurable in terminals; always use event direction and expose an application-level invert setting.
- Guard against floods: cap event counts and accumulated line deltas per stream.
@@ -1,169 +0,0 @@
# Streaming Wrapping Reflow (tui2)
This document describes a correctness bug in `codex-rs/tui2` and the chosen fix:
while streaming assistant markdown, soft-wrap decisions were effectively persisted as hard line
breaks, so resizing the viewport could not reflow prose.
## Goal
- Resizing the viewport reflows transcript prose (including streaming assistant output).
- Width-derived breaks are always treated as *soft wraps* (not logical newlines).
- Copy/paste continues to treat soft wraps as joinable (via joiners), and hard breaks as newlines.
Non-goals:
- Reflowing terminal scrollback that has already been printed.
- Reflowing content that is intentionally treated as preformatted (e.g., code blocks, raw stdout).
## Background: where reflow happens in tui2
TUI2 renders the transcript as a list of `HistoryCell`s:
1. A cell stores width-agnostic content (string, diff, logical lines, etc.).
2. At draw time (and on resize), `transcript_render` asks each cell for lines at the *current*
width (ideally via `HistoryCell::transcript_lines_with_joiners(width)`).
3. `TranscriptViewCache` caches the wrapped visual lines keyed by width; a width change triggers a
rebuild.
This only works if cells do *not* persist width-derived wrapping inside their stored state.
## The bug: soft wraps became hard breaks during streaming
Ratatui represents multi-line content as `Vec<Line>`. If we split a paragraph into multiple `Line`s
because the viewport is narrow, that split is indistinguishable from an explicit newline unless we
also carry metadata describing which breaks were “soft”.
Streaming assistant output used to generate already-wrapped `Line`s and store them inside the
history cell. Later, when the viewport became wider, the transcript renderer could not “un-split”
those baked lines — they looked like hard breaks.
## Chosen solution (A, F1): stream logical markdown lines; wrap in the cell at render-time
User choice recap:
- **A**: Keep append-only streaming (new history cell per commit tick), but make the streamed data
width-agnostic.
- **F1**: Make the agent message cell responsible for wrapping-to-width so transcript-level wrapping
can be a no-op for it.
### Key idea: separate markdown parsing from wrapping
We introduce a width-agnostic “logical markdown line” representation that preserves the metadata
needed to wrap correctly later:
- `codex-rs/tui2/src/markdown_render.rs`
- `MarkdownLogicalLine { content, initial_indent, subsequent_indent, line_style, is_preformatted }`
- `render_markdown_logical_lines(input: &str) -> Vec<MarkdownLogicalLine>`
This keeps:
- hard breaks (paragraph/list boundaries, explicit newlines),
- markdown indentation rules for wraps (list markers, nested lists, blockquotes),
- preformatted runs (code blocks) stable.
### Updated streaming pipeline
- `codex-rs/tui2/src/markdown_stream.rs`
- `MarkdownStreamCollector` is newline-gated (no change), but now commits
`Vec<MarkdownLogicalLine>` instead of already-wrapped `Vec<Line>`.
- Width is removed from the collector; wrapping is not performed during streaming.
- `codex-rs/tui2/src/streaming/controller.rs`
- Emits `AgentMessageCell::new_logical(...)` containing logical lines.
- `codex-rs/tui2/src/history_cell.rs`
- `AgentMessageCell` stores `Vec<MarkdownLogicalLine>`.
- `HistoryCell::transcript_lines_with_joiners(width)` wraps each logical line at the current
width using `word_wrap_line_with_joiners` and composes indents as:
- transcript gutter prefix (`• ` / ` `), plus
- markdown-provided initial/subsequent indents.
- Preformatted logical lines are rendered without wrapping.
Result: on resize, the transcript cache rebuilds against the new width and the agent output reflows
correctly because the stored content contains no baked soft wraps.
## Overlay deferral fix (D): defer cells, not rendered lines
When an overlay (transcript/static) is active, TUI2 is in alt screen and the normal terminal buffer
is not visible. Historically, `tui2` attempted to queue “history to print” for the normal buffer by
deferring *rendered lines*, which baked the then-current width.
User choice recap:
- **D**: Store deferred *cells* and render them at overlay close time.
Implementation:
- `codex-rs/tui2/src/app.rs`
- `deferred_history_cells: Vec<Arc<dyn HistoryCell>>` (replaces `deferred_history_lines`).
- `AppEvent::InsertHistoryCell` pushes cells into the deferral list when `overlay.is_some()`.
- `codex-rs/tui2/src/app_backtrack.rs`
- `close_transcript_overlay` renders deferred cells at the *current* width when closing the
overlay, then queues the resulting lines for the normal terminal buffer.
Note: as of today, `Tui::insert_history_lines` queues lines but `Tui::draw` does not flush them into
the terminal (see `codex-rs/tui2/src/tui.rs`). This section is therefore best read as “behavior we
want when/if scrollback printing is re-enabled”, not a guarantee that content is printed during the
main TUI loop. For the current intended behavior around printing, see
`codex-rs/tui2/docs/tui_viewport_and_history.md`.
## Tests (G2)
User choice recap:
- **G2**: Add resize reflow tests + snapshot coverage.
Added coverage:
- `codex-rs/tui2/src/history_cell.rs`
- `agent_message_cell_reflows_streamed_prose_on_resize`
- `agent_message_cell_reflows_streamed_prose_vt100_snapshot`
These assert that a streamed agent cell produces fewer visual lines at wider widths and provide
snapshots showing reflow for list items and blockquotes.
## Audit: other `HistoryCell`s and width-baked paths
This section answers “what else might behave like this?” up front.
### History cells
- `AgentMessageCell` (`codex-rs/tui2/src/history_cell.rs`): **was affected**; now stores logical
markdown lines and wraps at render time.
- `UserHistoryCell` (`codex-rs/tui2/src/history_cell.rs`): wraps at render time from stored `String`
using `word_wrap_lines_with_joiners` (reflowable).
- `ReasoningSummaryCell` (`codex-rs/tui2/src/history_cell.rs`): renders from stored `String` on each
call; it does call `append_markdown(..., Some(width))`, but that wrapping is recomputed per width
(reflowable).
- `PrefixedWrappedHistoryCell` (`codex-rs/tui2/src/history_cell.rs`): wraps at render time and
returns joiners (reflowable).
- `PlainHistoryCell` (`codex-rs/tui2/src/history_cell.rs`): stores `Vec<Line>` and returns it
unchanged (not reflowable by design; used for already-structured/preformatted output).
Rule of thumb: any cell that stores already-wrapped `Vec<Line>` for prose is a candidate for the
same bug; cells that store source text or logical lines and compute wrapping inside
`display_lines(width)` are safe.
### Width-baked output outside the transcript model
Even with the streaming fix, some paths are inherently width-baked:
- Printed transcript after exit (`codex-rs/tui2/src/app.rs`): `AppExitInfo.session_lines` is rendered
once using the final width and then printed; it cannot reflow afterward.
- Optional scrollback insertion helper (`codex-rs/tui2/src/insert_history.rs`): once ANSI is written
to the terminal, that output cannot be reflowed later. This helper is currently used for
deterministic ANSI emission (`write_spans`) and tests; it is not wired into the main TUI draw
loop.
- Static overlays (`codex-rs/tui2/src/pager_overlay.rs`): reflow depends on whether callers provided
width-agnostic input; pre-split `Vec<Line>` cannot be “un-split” within the overlay.
## Deferred / follow-ups
The fix above is sufficient to unblock correct reflow on resize. Remaining choices can be deferred:
- Streaming granularity: one logical line can wrap into multiple visual lines, so “commit tick”
updates can appear in larger chunks than before. If this becomes a UX issue, we can add a render-
time “progressive reveal” layer without reintroducing width baking.
- Expand logical-line rendering to other markdown-ish cells if needed (e.g., unify `append_markdown`
usage), but only if we find a concrete reflow bug beyond `AgentMessageCell`.
@@ -1,564 +0,0 @@
# TUI2 Viewport, Transcript, and History Design Notes
This document describes the viewport and history model we are implementing in the new
`codex-rs/tui2` crate. It builds on lessons from the legacy TUI and explains why we moved away
from directly writing history into terminal scrollback.
The target audience is Codex developers and curious contributors who want to understand or
critique how TUI2 owns its viewport, scrollback, and suspend behavior.
Unless stated otherwise, references to “the TUI” in this document mean the TUI2 implementation;
when we mean the legacy TUI specifically, we call it out explicitly.
---
## 1. Problem Overview
Historically, the legacy TUI tried to “cooperate” with the terminals own scrollback:
- The inline viewport sat somewhere above the bottom of the screen.
- When new history arrived, we tried to insert it directly into the terminal scrollback above the
viewport.
- On certain transitions (e.g. switching sessions, overlays), we cleared and rewrote portions of
the screen from scratch.
This had several failure modes:
- **Terminaldependent behavior.**
- Different terminals handle scroll regions, clears, and resize semantics differently.
- What looked correct in one terminal could drop or duplicate content in another.
- **Resizes and layout churn.**
- The TUI reacts to resizes, focus changes, and overlay transitions.
- When the viewport moved or its size changed, our attempts to keep scrollback “aligned” with the
inmemory history could go out of sync.
- In practice this meant:
- Some lines were lost or overwritten.
- Others were duplicated or appeared in unexpected places.
- **“Clear and rewrite everything” didnt save us.**
- We briefly tried a strategy of clearing large regions (or the full screen) and rerendering
history when the layout changed.
- This ran into two issues:
- Terminals treat full clears differently. For example, Terminal.app often leaves the cleared
screen as a “page” at the top of scrollback, some terminals interpret only a subset of the
ANSI clear/scrollback codes, and others (like iTerm2) gate “clear full scrollback” behind
explicit user consent.
- Replaying a long session is expensive and still subject to timing/race conditions with user
output (e.g. shell prompts) when we werent in alt screen.
The net result: the legacy TUI could not reliably guarantee “the history you see on screen is complete, in
order, and appears exactly once” across terminals, resizes, suspend/resume, and overlay transitions.
---
## 2. Goals
The redesign is guided by a few explicit goals:
1. **Codex, not the terminal, owns the viewport.**
- The inmemory transcript (a list of history entries) is the single source of truth for whats
on screen.
- The TUI decides how to map that transcript into the current viewport; scrollback becomes an
output target, not an extra data structure we try to maintain.
2. **History must be correct, ordered, and never silently dropped.**
- Every logical history cell should either:
- Be visible in the TUI, or
- Have been printed into scrollback as part of a suspend/exit flow.
- We would rather (rarely) duplicate content than risk losing it.
3. **Avoid unnecessary duplication.**
- When emitting history to scrollback (on suspend or exit), print each logical cells content at
most once.
- Streaming cells are allowed to be “reseen” as they grow, but finished cells should not keep
reappearing.
4. **Behave sensibly under resizes.**
- TUI rendering should reflow to the current width on every frame.
- History printed to scrollback may have been wrapped at different widths over time; that is
acceptable, but it must not cause missing content or unbounded duplication.
5. **Suspend/altscreen interaction is predictable.**
- `Ctrl+Z` should:
- Cleanly exit alt screen, if active.
- Print a consistent transcript prefix into normal scrollback.
- Resume with the TUI fully redrawn, without stale artifacts.
---
## 3. New Viewport & Transcript Model
### 3.1 Transcript as a logical sequence of cells
At a high level, the TUI transcript is a list of “cells”, each representing one logical thing in
the conversation:
- A user prompt (with padding and a distinct background).
- An agent response (which may arrive in multiple streaming chunks).
- System or info rows (session headers, migration banners, reasoning summaries, etc.).
Each cell knows how to draw itself for a given width: how many lines it needs, what prefixes to
use, how to style its content. The transcript itself is purely logical:
- It has no scrollback coordinates or terminal state baked into it.
- It can be rerendered for any viewport width.
The TUIs job is to take this logical sequence and decide how much of it fits into the current
viewport, and how it should be wrapped and styled on screen.
### 3.2 Building viewport lines from the transcript
To render the main transcript area above the composer, the TUI:
1. Defines a “transcript region” as the full frame minus the height of the bottom input area.
2. Flattens all cells into a list of visual lines, remembering for each visual line which cell it
came from and which line within that cell it corresponds to.
3. Uses this flattened list plus a scroll position to decide which visual line should appear at the
top of the region.
4. Clears the transcript region and draws the visible slice of lines into it.
5. For user messages, paints the entire row background (including padding lines) so the user block
stands out even when it does not fill the whole width.
6. Applies selection styling and other overlays on top of the rendered lines.
Scrolling (mouse wheel, PgUp/PgDn, Home/End) operates entirely in terms of these flattened lines
and the current scroll anchor. The terminals own scrollback is not part of this calculation; it
only ever sees fully rendered frames.
### 3.3 Alternate screen, overlays, and redraw guarantees
The TUI uses the terminals alternate screen for:
- The main interactive chat session (so the viewport can cover the full terminal).
- Fullscreen overlays such as the transcript pager, diff view, model migration screen, and
onboarding.
Conceptually:
- Entering alt screen:
- Switches the terminal into alt screen and expands the viewport to cover the full terminal.
- Clears that altscreen buffer.
- Leaving alt screen:
- Disables “alternate scroll” so mouse wheel events behave predictably.
- Returns to the normal screen.
- On leaving overlays and on resuming from suspend, the TUI viewport is explicitly cleared and fully
redrawn:
- This prevents stale overlay content or shell output from lingering in the TUI area.
- The next frame reconstructs the UI entirely from the inmemory transcript and other state, not
from whatever the terminal happened to remember.
Alt screen is therefore treated as a temporary render target. The only authoritative copy of the UI
is the inmemory state.
---
## 4. Mouse, Selection, and Scrolling
Mouse interaction is a firstclass part of the new design:
- **Scrolling.**
- Mouse wheel scrolls the transcript in fixed line increments.
- Keyboard shortcuts (PgUp/PgDn/Home/End) use the same scroll model, so the footer can show
consistent hints regardless of input device.
- **Selection.**
- A clickanddrag gesture defines a linear text selection in terms of the flattened transcript
lines (not raw buffer coordinates).
- Selection tracks the _content_ rather than a fixed screen row. When the transcript scrolls, the
selection moves along with the underlying lines instead of staying glued to a particular Y
position.
- The selection only covers the “transcript text” area; it intentionally skips the left gutter
that we use for bullets/prefixes.
- **Copy.**
- When the user triggers copy, the TUI reconstructs the wrapped transcript lines using the same
flattening/wrapping rules as the visible view.
- It then reconstructs a highfidelity clipboard string from the selected logical lines:
- Preserves meaningful indentation (especially for code blocks).
- Treats soft-wrapped prose as a single logical line by joining wrap continuations instead of
inserting hard newlines.
- Emits Markdown source markers (e.g. backticks and fences) for copy/paste, even if the UI
chooses to render those constructs without showing the literal markers.
- Copy operates on the full selection range, even if the selection extends outside the current
viewport.
- The resulting text is sent to the system clipboard and a status footer indicates success or
failure.
Because scrolling, selection, and copy all operate on the same flattened transcript representation,
they remain consistent even as the viewport resizes or the chat composer grows/shrinks. Owning our
own scrolling also means we must own mouse interactions endtoend: if we left scrolling entirely
to the terminal, we could not reliably line up selections with transcript content or avoid
accidentally copying gutter/margin characters instead of just the conversation text.
Scroll normalization details and the data behind it live in
`codex-rs/tui2/docs/scroll_input_model.md`.
---
## 5. Printing History to Scrollback
We still want the final session (and suspend points) to appear in the users normal scrollback, but
we no longer try to maintain scrollback in lockstep with the TUI frame. Instead, we treat
scrollback as an **appendonly log** of logical transcript cells.
In practice this means:
- The TUI may print history both when you suspend (`Ctrl+Z`) and when you exit.
- Some users may prefer to only print on exit (for example to keep scrollback quieter during long
sessions). The current design anticipates gating suspendtime printing behind a config toggle so
that this behavior can be made optin or optout without touching the core viewport logic, but
that switch has not been implemented yet.
### 5.1 Cellbased highwater mark
Internally, the TUI keeps a simple “highwater mark” for history printing:
- Think of this as “how many cells at the front of the transcript have already been sent to
scrollback.”
- It is just a counter over the logical transcript, not over wrapped lines.
- It moves forward only when we have actually printed more history.
This means we never try to guess “how many terminal lines have already been printed”; we only
remember that “the first N logical entries are done.”
### 5.2 Rendering new cells for scrollback
When we need to print history (on suspend or exit), we:
1. Take the suffix of the transcript that lies beyond the highwater mark.
2. Render just that suffix into styled lines at the **current** terminal width.
3. Write those lines to stdout.
4. Advance the highwater mark to include all cells we just printed.
Older cells are never rerendered for scrollback; they remain in whatever wrapping they had when
they were first printed. This avoids the linecountbased bugs we had before while still allowing
the onscreen TUI to reflow freely.
### 5.3 Suspend (`Ctrl+Z`) flow
On suspend (typically `Ctrl+Z` on Unix):
- Before yielding control back to the shell, the TUI:
- Leaves alt screen if it is active and restores normal terminal modes.
- Determines which transcript cells have not yet been printed and renders them for the current
width.
- Prints those new lines once into normal scrollback.
- Marks those cells as printed in the highwater mark.
- Finally, sends the process to the background.
On `fg`, the process resumes, reenters TUI modes, and redraws the viewport from the inmemory
transcript. The history printed during suspend stays in scrollback and is not touched again.
### 5.4 Exit flow
When the TUI exits, we follow the same principle:
- We compute the suffix of the transcript that has not yet been printed (taking into account any
prior suspends).
- We render just that suffix to styled lines at the current width.
- The outer `main` function leaves alt screen, restores the terminal, and prints those lines, plus a
blank line and token usage summary.
If you never suspended, exit prints the entire transcript once. If you did suspend one or more
times, exit prints only the cells appended after the last suspend. In both cases, each logical
conversation entry reaches scrollback exactly once.
---
## 6. Streaming, Width Changes, and Tradeoffs
### 6.1 Streaming cells
Streaming agent responses are represented as a sequence of history entries:
- The first chunk produces a “first line” entry for the message.
- Subsequent chunks produce continuation entries that extend that message.
From the history/scrollback perspective:
- Each streaming chunk is just another entry in the logical transcript.
- The highwater mark is a simple count of how many entries at the _front_ of the transcript have
already been printed.
- As new streaming chunks arrive, they are appended as new entries and will be included the next
time we print history on suspend or exit.
We do **not** attempt to reprint or retroactively merge older chunks. In scrollback you will see the
streaming response as a series of discrete blocks, matching the internal history structure.
Today, streaming rendering still “bakes in” some width at the time chunks are committed: line breaks
for the streaming path are computed using the width that was active at the time, and stored in the
intermediate representation. This is a known limitation and is called out in more detail in
`codex-rs/tui2/docs/streaming_wrapping_design.md`; a followup change will make streaming behavior
match the rest of the transcript more closely (wrap only at display time, not at commit time).
### 6.2 Width changes over time
Because we now use a **celllevel** highwater mark instead of a visual linecount, width changes
are handled gracefully:
- On every suspend/exit, we render the notyetprinted suffix of the transcript at the **current**
width and append those lines.
- Previously printed entries remain in scrollback with whatever wrapping they had at the time they
were printed.
- We no longer rely on “N lines printed before, therefore skip N lines of the newly wrapped
transcript,” which was the source of dropped and duplicated content when widths changed.
This does mean scrollback can contain older cells wrapped for narrower or wider widths than the
final terminal size, but:
- Each logical cells content appears exactly once.
- New cells are appendonly and never overwrite or implicitly “shrink” earlier content.
- The onscreen TUI always reflows to the current width independently of scrollback.
If we later choose to also reemit the “currently streaming” cell when printing on suspend (to make
sure the latest chunk of a long answer is always visible in scrollback), that would intentionally
duplicate a small number of lines at the boundary of that cell. The design assumes any such behavior
would be controlled by configuration (for example, by disabling suspendtime printing entirely for
users who prefer only exittime output).
### 6.3 Why not reflow scrollback?
In theory we could try to reflow alreadyprinted content when widths change by:
- Recomputing the entire transcript at the new width, and
- Printing diffs that “rewrite” old regions in scrollback.
In practice, this runs into the same issues that motivated the redesign:
- Terminals treat full clears and scroll regions differently.
- There is no portable way to “rewrite” arbitrary portions of scrollback above the visible buffer.
- Interleaving user output (e.g. shell prompts after suspend) makes it impossible to reliably
reconstruct the original scrollback structure.
We therefore deliberately accept that scrollback is **appendonly** and not subject to reflow;
correctness is measured in terms of logical transcript content, not pixelperfect layout.
---
## 7. Backtrack and Overlays (Context)
While this document is focused on viewport and history, its worth mentioning a few related
behaviors that rely on the same model.
### 7.1 Transcript overlay and backtrack
The transcript overlay (pager) is a fullscreen view of the same logical transcript:
- When opened, it takes a snapshot of the current transcript and renders it in an altscreen
overlay.
- Backtrack mode (`Esc` sequences) walks backwards through user messages in that snapshot and
highlights the candidate “edit from here” point.
- Confirming a backtrack request forks the conversation on the server and trims the inmemory
transcript so that only history up to the chosen user message remains, then rerenders that prefix
in the main view.
The overlay is purely a different _view_ of the same transcript; it never infers anything from
scrollback.
---
## 8. Summary of Tradeoffs
**What we gain:**
- The TUI has a clear, single source of truth for history (the inmemory transcript).
- Viewport rendering is deterministic and independent of scrollback.
- Suspend and exit flows:
- Print each logical history cell exactly once.
- Are robust to terminal width changes.
- Interact cleanly with alt screen and rawmode toggling.
- Streaming, overlays, selection, and backtrack all share the same logical history model.
- Because cells are always rerendered live from the transcript, percell interactions can become
richer over time. Instead of treating the transcript as “dead text”, we can make individual
entries interactive after they are rendered: expanding or contracting tool calls, diffs, or
reasoning summaries in place, jum…truncated… \*\*\*
---
## 9. TUI2 Implementation Notes
This section maps the design above onto the `codex-rs/tui2` crate so future viewport work has
concrete code pointers.
### 9.1 Transcript state and layout
The main app struct (`codex-rs/tui2/src/app.rs`) tracks the transcript and viewport state with:
- `transcript_cells: Vec<Arc<dyn HistoryCell>>` the logical history.
- `transcript_scroll: TranscriptScroll` whether the viewport is pinned to the bottom or
anchored at a specific cell/line pair.
- `transcript_selection: TranscriptSelection` a selection expressed in content-relative
coordinates over the flattened, wrapped transcript (line index + column).
- `transcript_view_top` / `transcript_total_lines` the current viewports top line index and
total number of wrapped lines for the inline transcript area.
### 9.2 Rendering, wrapping, and selection
`App::render_transcript_cells` defines the transcript region, builds flattened lines via
`App::build_transcript_lines`, wraps them with `word_wrap_lines_borrowed` from
`codex-rs/tui2/src/wrapping.rs`, and applies selection via `apply_transcript_selection` before
writing to the frame buffer.
Streaming wrapping details live in `codex-rs/tui2/docs/streaming_wrapping_design.md`.
### 9.3 Input, selection, and footer state
Mouse handling lives in `App::handle_mouse_event`, keyboard scrolling in
`App::handle_key_event`, selection rendering in `App::apply_transcript_selection`, and copy in
`App::copy_transcript_selection` plus `codex-rs/tui2/src/transcript_selection.rs` and
`codex-rs/tui2/src/clipboard_copy.rs`. Scroll/selection UI state is forwarded through
`ChatWidget::set_transcript_ui_state`,
`BottomPane::set_transcript_ui_state`, and `ChatComposer::footer_props`, with footer text
assembled in `codex-rs/tui2/src/bottom_pane/footer.rs`.
### 9.4 Exit transcript output
`App::run` returns `session_lines` on `AppExitInfo` after flattening with
`App::build_transcript_lines` and converting to ANSI via `App::render_lines_to_ansi`. The CLI
prints those lines before the token usage and resume hints.
## 10. Future Work and Open Questions
### 10.1 Current status
This design shipped behind the `tui2` feature flag (as a separate crate, duplicating the legacy
`tui` crate to enable rollout without breaking existing behavior). The following items from early
feedback are already implemented:
- Bottom pane positioning is pegged high with an empty transcript and moves down as the transcript
fills (including on resume).
- Wheel-based transcript scrolling uses the stream-based normalization model derived from scroll
probe data (see `codex-rs/tui2/docs/scroll_input_model.md`).
- While a selection is active, streaming stops “follow latest output” so the selection remains
stable, and follow mode resumes after the selection is cleared.
- Copy operates on the full selection range (including offscreen lines), using the same wrapping as
on-screen rendering.
- Copy selection uses `Ctrl+Shift+C` (VS Code uses `Ctrl+Y` because `Ctrl+Shift+C` is unavailable in
the terminal) and shows an on-screen “copy” affordance near the selection.
### 10.2 Roadmap (prioritized)
This section captures a prioritized list of improvements we want to add to TUI2 based on early
feedback, with the goal of making scrolling/selection/copy feel as close to “native terminal” (and
Vim) behavior as we can while still owning the viewport.
**P0 — must-have (usability/correctness):**
- **Scrolling behavior.** Default to a classic multi-line wheel tick (3 lines, configurable) with
acceleration/velocity for faster navigation, and ensure we stop scrolling when the user stops
input (avoid redraw/event-loop backlog that makes scrolling feel “janky”).
- **Mouse event bounds.** Ignore mouse events outside the transcript region so clicks in the
composer/footer dont start or mutate transcript selection state.
- **Copy fidelity.** Preserve meaningful indentation (especially code blocks), treat soft-wrapped
prose as a single logical line when copying, and copy markdown _source_ (including backticks and
heading markers) even if we render it differently.
**P1 — should-have (UX polish and power user workflows):**
- **Streaming wrapping polish.** Ensure all streaming paths use display-time wrapping only, and add
tests that cover resizing after streaming has started.
- **Selection semantics.** Define and implement selection behavior across multi-step output (and
whether step boundaries should be copy boundaries), while continuing to exclude the left gutter
from copied text.
- **Auto-scroll during drag.** While dragging a selection, auto-scroll when the cursor is at/near the
top or bottom of the transcript viewport to allow selecting beyond the visible window.
- **Width-aware selection.** Ensure selection highlighting and copy reconstruction handle wide glyphs
correctly (emoji, CJK), matching terminal display width rather than raw character count.
- **Multi-click selection.** Support double/triple/quad click selection (word/line/paragraph),
implemented on top of the transcript/viewport model rather than terminal buffer coordinates.
- **Find in transcript.** Add text search over the transcript (and consider integrating match
markers with any future scroll indicator work).
- **Cross-terminal behavior checks.** Validate copy/selection behavior across common terminals (incl.
terminal-provided “override selection” modes like holding Shift) and document the tradeoffs.
**P2 — nice-to-have (polish, configuration, and interactivity):**
- **Suspend printing.** Decide whether printing history on suspend is desirable at all (it is not
implemented yet). If we keep it, finalize the config shape/defaults, wire it through TUI startup,
and document it in the appropriate config docs.
- **Terminal integration.** Consider guiding (or optionally managing) terminal-emulator-specific
settings that affect TUI behavior (for example iTerms clipboard opt-in prompts or Ghostty
keybinding quirks), so the “works well out of the box” path is consistent across terminals.
- **Interactive cells (unlocked by transcript ownership).** Because transcript entries are structured
objects (not dead text in terminal scrollback), we can attach metadata to rendered regions and map
mouse/keys back to the underlying cell reliably across resizes and reflow. Examples:
- **Drill into a specific tool/command output.** Click (or press Enter) on a tool call / command
cell to open a focused overlay that shows the command, exit status, timing, and stdout/stderr as
separate sections, with dedicated “copy output” actions. This enables copying _just_ one commands
output even when multiple commands are interleaved in a turn.
- **Copy an entire cell or entire turn.** Provide an action to copy a whole logical unit (one cell,
or “user prompt + assistant response”), without gutters and with well-defined boundaries. This is
hard to do with raw selection because step boundaries and padding arent reliably expressible in
terminal coordinates once the viewport moves or reflows.
- **Expand/collapse structured subregions with source-aware copy.** Tool calls, diffs, and
markdown can render in a compact form by default and expand in place. Copy actions can choose
between “copy rendered view” and “copy source” (e.g. raw markdown, raw JSON arguments, raw diff),
since we retain the original source alongside the rendered lines.
- **Cell-scoped actions.** Actions like “copy command”, “yank into composer”, “retry tool call”, or
“open related view” (diff/pager) can be offered per cell and behave deterministically, because the
UI can address cells by stable IDs rather than by fragile screen coordinates.
- **Additional affordances.** Consider an ephemeral scrollbar and/or a more explicit “selecting…”
status if footer hints arent sufficient.
- **UX capture.** Maintain short “golden path” clips showing scrolling (mouse + keys), selection and
copy, streaming under resize, and suspend/resume + exit printing.
### 10.3 Open questions
This section collects design questions that follow naturally from the current model and are worth
explicit discussion before we commit to further UI changes.
- **“Scroll mode” vs “live follow” UI.**
- We already distinguish “scrolled away from bottom” vs “following the latest output” in the
footer and scroll state. Do we need a more explicit “scroll mode vs live mode” affordance (e.g.,
a dedicated indicator or toggle), or is the current behavior sufficient and adding more chrome
would be noise?
- **Ephemeral scroll indicator.**
- For long sessions, a more visible sense of “where am I?” could help. One option is a minimalist
scrollbar that appears while the user is actively scrolling and fades out when idle. A full
“minimap” is probably too heavy for a TUI given the limited vertical space, but we could
imagine adding simple markers along the scrollbar to show where prior prompts occurred, or
where text search matches are, without trying to render a full preview of the buffer.
- **Selection affordances.**
- Today, the primary hint that selection is active is the reversed text plus the on-screen “copy”
affordance (`Ctrl+Shift+C`) and the footer hint. Do we want an explicit “Selecting… (Esc to
cancel)” status while a drag is in progress, or would that be redundant/clutter for most users?
- **Suspend banners in scrollback.**
- When printing history on suspend, should we also emit a small banner such as
`--- codex suspended; history up to here ---` to make those boundaries obvious in scrollback?
This would slightly increase noise but could make multisuspend sessions easier to read.
- **Configuring suspend printing behavior.**
- The design already assumes that suspendtime printing can be gated by config. Questions to
resolve:
- Should printing on suspend be on or off by default?
- Should we support multiple modes (e.g., “off”, “print all new cells”, “print streaming cell
tail only”) or keep it binary?
- **Streaming duplication at the edges.**
- If we later choose to always reemit the “currently streaming” message when printing on suspend,
we would intentionally allow a small amount of duplication at the boundary of that message (for
example, its last line appearing twice across suspends). Is that acceptable if it improves the
readability of long streaming answers in scrollback, and should the ability to disable
suspendtime printing be our escape hatch for users who care about exact deduplication?\*\*\*
---
-17
View File
@@ -1,17 +0,0 @@
▒▓▒▓▒██▒▒██▒
▒▒█▓█▒█▓█▒▒░░▒▒ ▒ █▒
█░█░███ ▒░ ░ █░ ░▒░░░█
▓█▒▒████▒ ▓█░▓░█
▒▒▓▓█▒░▒░▒▒ ▓░▒▒█
░█ █░ ░█▓▓░░█ █▓▒░░█
█▒ ▓█ █▒░█▓ ░▒ ░▓░
░░▒░░ █▓▓░▓░█ ░░
░▒░█░ ▓░░▒▒░ ▓░██████▒██ ▒ ░
▒░▓█ ▒▓█░ ▓█ ░ ░▒▒▒▓▓███░▓█▓█░
▒▒▒ ▒ ▒▒█▓▓░ ░▒████ ▒█ ▓█▓▒▓
█▒█ █ ░ ██▓█▒░
▒▒█░▒█▒ ▒▒▒█░▒█
▒██▒▒ ██▓▓▒▓▓▓▒██▒█░█
░█ █░░░▒▒▒█▒▓██
-17
View File
@@ -1,17 +0,0 @@
▒████▒██▒
██░███▒░▓▒██
▒▒█░░▓░░▓░█▒██
░▒▒▓▒░▓▒▓▒███▒▒█
▓ ▓░░ ░▒ ██▓▒▓░▓
░░ █░█░▓▓▒ ░▒ ░
▒ ░█ █░░░░█ ░▓█
░░▒█▓█░░▓▒░▓▒░░
░▒ ▒▒░▓░░█▒█▓░░
░ █░▒█░▒▓▒█▒▒▒░█░
█ ░░░░░ ▒█ ▒░░
▒░██▒██ ▒░ █▓▓
░█ ░░░░██▓█▓░▓░
▓░██▓░█▓▒ ▓▓█
██ ▒█▒▒█▓█
-17
View File
@@ -1,17 +0,0 @@
███████▒
▓ ▓░░░▒▒█
▓ ▒▒░░▓▒█▓▒█
░▒▒░░▒▓█▒▒▓▓
▒ ▓▓▒░█▒█▓▒░░█
░█░░░█▒▓▓░▒▓░░
██ █░░░░░░▒░▒▒
░ ░░▓░░▒▓ ░ ░
▓ █░▓░░█▓█░▒░
██ ▒░▓▒█ ▓░▒░▒
█░▓ ░░░░▒▓░▒▒░
▒▒▓▓░▒█▓██▓░░
▒ █░▒▒▒▒░▓
▒█ █░░█▒▓█░
▒▒ ███▒█░
-17
View File
@@ -1,17 +0,0 @@
█████▓
█▒░▒▓░█▒
░▓▒██
▓█░░░▒▒ ░
░ █░░░░▓▓░
░█▓▓█▒ ▒░
░ ░▓▒░░▒
░ ▓█▒░░
██ ░▓░░█░░
░ ▓░█▓█▒
░▓ ░ ▒██▓
█ █░ ▒█░
▓ ██░██▒░
█▒▓ █░▒░░
▒ █░▒▓▓
-17
View File
@@ -1,17 +0,0 @@
▓████
░▒▒░░
░░▒░
░██░▒
█ ░░
▓▓░░
█ ░░
█ ░
▓█ ▒░▓
░ █▒░
█░▓▓ ░░
░▒▒▒░
░██░▒
█▒▒░▒
█ ▓ ▒
-17
View File
@@ -1,17 +0,0 @@
████▓
█▓▒▒▓▒
░▒░░▓ ░
░░▓░ ▒░█
░░░▒ ░
░█░░ █░
░░░░ ▓ █
░░▒░░ ▒
░░░░
▒▓▓ ▓▓
▒░ █▓█░
░█░░▒▒▒░
▓ ░▒▒▒░
░▒▓█▒▒▓
▒█ █▒▓
-17
View File
@@ -1,17 +0,0 @@
█████░▒
░█▒░░▒▓██
▓▓░█▒▒░ █░
░▓░ ▓▓█▓▒▒░
░░▒ ▒▒░░▓ ▒░
▒░░▓░░▓▓░
░░ ░░░░░░█░
░░▓░░█░░░ █▓░
░░████░░░▒▓▓░
░▒░▓▓░▒░█▓ ▓░
░▓░░░░▒░ ░ ▓
░██▓▒░░▒▓ ▒
█░▒█ ▓▓▓░ ▓░
░▒░░▒▒▓█▒▓
▒▒█▒▒▒▒▓
░░
-17
View File
@@ -1,17 +0,0 @@
▒▒█ ███░▒
▓▒░░█░░▒░▒▒
░▓▓ ▒▓▒▒░░ █▒
▓▓▓ ▓█▒▒░▒░░██░
░░▓▒▓██▒░░█▓░░▒
░░░█░█ ░▒▒ ░ ░▓░
▒▒░ ▓░█░░░░▓█ █ ░
░▓▓ ░░░░▓░░░ ▓ ░░
▒▒░░░█░▓▒░░ ██ ▓
█ ▒▒█▒▒▒█░▓▒░ █▒░
░░░█ ▓█▒░▓ ▓▓░░░
░░█ ░░ ░▓▓█ ▓
▒░█ ░ ▓█▓▒█░
▒░░ ▒█░▓▓█▒░
█▓▓▒▒▓▒▒▓█
-17
View File
@@ -1,17 +0,0 @@
█▒███▓▓░█▒
▒▓██░░░█▒█░█ ▒█
██▒▓▒▒▒░██ ░░░▒ ▒
▓░▓▒▓░ ▒░ █░▓▒░░░▒▒
░▓▒ ░ ░ ▓▒▒▒▓▓ █
░▒██▓░ █▓▓░ ▓█▒▓░▓▓
█ ▓▓░ █▓▓░▒ █ ░░▓▒░
▓ ▒░ ▓▓░░▓░█░░▒▓█
█▓█▓▒▒▒█░▒▒░▒▒▓▒░░░ ░
░ ▒▓▒▒░▓█▒▓░░▒ ▒███▒
▒▒▒▓ ████▒▒░█▓▓▒ ▒█
▒░░▒█ ░▓░░░ ▓
▒▒▒ █▒▒ ███▓▒▒▓
█ ░██▒▒█░▒▓█▓░█
░█▓▓▒██░█▒██
-17
View File
@@ -1,17 +0,0 @@
▒▒▒█▒▒█▓░█▒
▒█ ▒▓███░▒▒█ █▓▓▒
▒▓▓░█ █▒ █ ▓▒ █▓▓▒ █
█░░█▓█▒ █ █▒░▒▓▒░▒▓▒▒▒█
▒▒▓▓ ▓░ ▒ █▒▒▓░▓░▒▒▓▒▒▒
▓▒░ ██░▓▒▒▒▓███░█▓▓▒▓░▓░
░░▒▓▓ █▓█▓░ ▒▓ █░▒░▒█
▒▓░░ ▒▒ ░░▓▒ ░▓░
▒ █▒▒▒▓▒▓█░░█░█▓▒█ ░█░░
▒▒▒░█▒█ ░░▓▒▒▒▒░░░▒▓░░▒ █
░▓░▒░ █████░ ▒▒▒▓░▓█▓░▓░
▒▒ █▒█ ░░█ ▓█▒█
▒▒██▒▒▓ ▒█▒▒▓▒█░
█░▓████▒▒▒▒██▒▓▒██
░░▒▓▒▒█▓█ ▓█
-17
View File
@@ -1,17 +0,0 @@
▒▒▒▒█░█▒▒░▓▒
▒█░░░▒▓▒▒▒▒█▒█░███
██▓▓▓ ░██░ ░█▓█░█▓▒
▓▓░██▒░ ▒▒▒██▒░██
░░▓░▓░ █░▒ ▓ ░▒ ░▒█
░▒▓██ ▒░█░▓ ▓▓ █▓█░
▒▒░░█ ▓█▒▓░██░ ▓▓▓█░
░░░░ ░▓ ▒░ █ ░ ░░░
░█░▒█▒▓▓▒▒▒░░░░██▓█░▓ ▒ ░░
▒▓▓█░▒█▓▒██▒█░█ ▒▒ ▓▒▒▒█▓▓░▒
█▒ ▓█░ ██ ▒▒▒▓░▓▓ ▓▓█
▒▒▒█▒▒ ░▓▓▒▓▓█
█ ▒▒░░██ █▓▒▓▓░▓░
█ ▓░█▓░█▒▒▒▓▓█ ▓█░█
░▓▒▓▓█▒█▓▒█▓▒
-17
View File
@@ -1,17 +0,0 @@
▒▓▒▓▒█▒▒▒██▒
▒██▓█▓█░░░▒░░▒▒█░██▒
█░█░▒██░█░░ ░ █▒█▓░░▓░█
▒░▓▒▓████▒ ▓█▒░▓░█
█▒ ▓█▒░▒▒▒▒▒ ▒█░▒░█
█▓█ ░ ░█▒█▓▒█ ▒▒░█░
█░██░ ▒▓░▓░▒░█ ▓ ░ ░
░ ▒░ █░█░░▓█ ░█▓▓░
█ ▒░ ▓░▒▒▒░ ▓░█████████░▒░░█
▒▒█░ ▓░░█ ▓█ ░▒▒▒▒▒▒▓▓▒▒░█▓ ░
▒▒▒ █ █▒▓▓░█ ░ ███████ ░██░░
█▒▒▓▓█ ░ ██▓▓██
▓▒▒▒░██ █▒▒█ ▒░
░░▒▓▒▒ ██▓▓▒▓▓▓▒█░▒░░█
░████░░▒▒▒▒░▓▓█
-17
View File
@@ -1,17 +0,0 @@
▒▒█▒░░▒█▒█▒▒
█▓▒ ▓█▒█▒▒▒░░▒▒█▒██
██ ▒██ ░█ ░ ▒ ▒██░█▒
▒░ ▒█░█ ▒██░▒▓█▒▒
▒░ █░█ ▒▓ ▒░░▒█▒░░▒
▓░█░█ ███▓░ ▓ █▒░░▒
▓░▓█░ ██ ▓██▒ █▒░▓
░▒▒▓░ ▓▓░ █ ░░ ░
░▓░░▓█▒▓▒▒▒▒▒▒▒██▓▒▒▒▒█ ▓ ░▒
█░▒░▒ ▓░░▒▒▒▒░▒ █▒▒ ░▒▒ █▓ ░░
▒█▒▒█ █ ▒█▒░░█░ ▓▒
█ ▒█▓█ ▒▓█▓░▓
▒▒▒██░▒ █▓█░▓██
▒█▓▓ ░█▒▓▓█▓ ░ ░█▓██
░██░▒ ▒▒▒▒▒░█
-17
View File
@@ -1,17 +0,0 @@
▒▒█▒█▒▒█▒██▒▒
███░░▒▒█░▒░█▓▒░▓██▒
▓█▒▒██▒ ░ ░▒░██▒░██
██░▓ █ ▒█▓██▓██
▓█▓█░ █░▓▒▒ ▒▒▒▒█
▓ ▓░ ███▒▓▓ ▒▒▒█
░█░░ ▒ ▓░█▓█ ▒▓▒
░▒ ▒▓ ░█ ░ ░
░ ░ ██▓▓▓▓▓███ ▒░█ ░█ ▓▓ ░
░ ░▒ ░▒ ▒█░ ▒ ░█░█ ▓ ▓▓
▓ ▓ ░░ █░ ██▒█▓ ▓░ █
██ ▓▓▒ ▒█ ▓
█▒ ▒▓▒ ▒▓▓██ █░
█▒▒ █ ██▓░░▓▓▒█ ▓░
███▓█▒▒▒▒█▒▓██░
-17
View File
@@ -1,17 +0,0 @@
▒██▒█▒▒█▒██▒
▒█▓█░▓▒▓░▓▒░░▓░█▓██▒
█▓█▓░▒██░ ░ █▒███▒▒██
▓█░██ ██░░░▒█▒
▒░░▓█ █▒▓░▒░▓▓▓█░
▒░█▓░ █░▓░▓▒▓░ ▒░▒▒░
░██▒▓ ░█░▒█▓█ ░░▓░
░░▒░░ ░▒░░▒▒ ░▒░ ░
░░█ █ █░▒▒▓▓▓▒██▒▒█░▒ ▒█ ▒░▓
▒░▒ █▒▒▒█ ▓█ ░▓▓░ ▒█▓▒ ░██ ▓▒▒
▒▒▒▒░ ██ ░ ░▓██▒▓▓▓ █░
▒█▒▒▒█ ▒██ ░██
█ █▓ ██▒ ▒▓██ █▒▓
█▓███ █░▓▒█▓▓▓▒█ ███
░ ░▒▓▒▒▒▓▒▒▓▒█░
-17
View File
@@ -1,17 +0,0 @@
▒██▒▒████▒█▒▒
▒▒░█░▒▒█▒▒▒█░▒░█░█▒
█ █░██▓█░ ░▓█░▒▓░░█
▓▓░█▓▓░ ▒▓▓▒░░▓▒
▓▓░░▓█ █▓████▓█▒░▒
█▒░ ▓░ ▒█████▓██░░▒░█
░░░ ░ ▓▓▓▓ ▒░░ ░██
░▓░ ░ ░ ░█▒▒█ ░ █▓░
▒ ▒ ░█░▓▒▒▒▒▒▓▒░▒█░▒ ▒▒ ░ ░░░
░▒▒▒░ ▒ ▓░▒ ▒░▒▒█░ ▒▒░
▓█░ ░ ░ █░▓▓▒░▒▓▒▓░
█░░▒░▓ █▓░▒▒▓░
▒ ░██▓▒▒ ▒▓ ▓█▓█▓
▒▒▒█▓██▒░▒▒▒██ ▓▒██░
░ █▒▒░▒▒█▒▒██░
-17
View File
@@ -1,17 +0,0 @@
▒░▒▓███▒▒█▒
█ ▒▓ ░▒▒░▒▒██▒██
█ █▓▒▓█ ░ ▓░▓█░███ ▒
██▓▓█▓░▒█▒░░▓░ ▒█▒░▒▒█
█ ▓▓▒▓█ ░ ▓▒▒░░░▒░██
░█▒█▒░ ███▓ ▓░▓ ▓ ▒
░ ░░ █▓▒█▓ ▓▒▒░▒▒░▒
░ ▒░░ ░█▒▓▒▒░░▒▓▓░░░
░▓ ░▓▓▓▓██░░░██▒██▒░ ░ ░░
▒ ▓ █░▓██▓▓██░▓▒▒██░ ░█░
▒ █▒░▒█ ░ ▒█▓█▒░▒▓█░
▒ ▒██▒ ░ ▓▓▓
▒▓█▒░░▓ ▒▒ ▒▓▓▒█
▓▓██▒▒ ░░▓▒▒▓░▒▒▓░
█▓▒██▓▒▒▒▒▒██
-17
View File
@@ -1,17 +0,0 @@
▒█▒█▓████▒
█ ███░▒▓▒░█░░█
▓░▓▓██ ▓░█▒▒▒░░░▒
░██░ ▓ ▒░ ▒░██▒▓
█▒▒▒█▓█▒▓▓▒░ ░▓▓▒▓█
▒█░░░▒██▓▒░▓ ▓░█░▓▓░█
░▓░█░ ░▒▒▓▒▒▓░▒▓▒ ░▒░
░░░▓░▓ ░▒▒▒▓░▒▒░▒░░▒
▒█▒░ ░▒▒▒▒▒▒█░░▒▒░██░▒
▓▓ ░▓░█░▒░░▓█▒░▒█▒▓▒░
▒░█▓▒░░ ██▓░▒░▓░░
░▒ ░▓█▓▒▓██▓▒▓█▓▓░▓
▒░▒░▒▒▒█▓▓█▒▓▒░░▓
▒▓▓▒▒▒█▒░██ █░█
░█ █▒██▒█░█
-17
View File
@@ -1,17 +0,0 @@
▒▓███ ██
▓█░▓▓▒░█▓░█
▓█ ░▓▒░▒ ▒█
▓█ █░░░▒░░▒█▓▒
░▒█▒░▓░ █▒▓▓░▒▓
▒ ░▓▓▓ █▒▒ ▒▒▓
░ ██▒░░▓░░▓▓ █
▓▓ ▒░░░▒▒▒░░▓░░
░ ▓▒█▓█░█▒▒▓▒░░
▓▒░▓█░▒▒██▒▒█░
░░ ▓░█ ▒█▓░█▒░░
▒▒░░▓▒ ▓▓ ░░░
█ █░▒ ▒░▓░▓█
░ █▒▒ █▒██▓
▒▓▓▒█░▒▒█
-17
View File
@@ -1,17 +0,0 @@
▓█████
░▓▓▓░▓▒
▓█░ █░▓█░
░░░▒░░▓░░
░ ░░▒▓█▒
░▒▓▒ ░░░░░
▒ ░░▒█░░
░ ░░░░▒ ░░
░▓ ▓ ░█░░░░
█▒ ▓ ▒░▒█░░
░▓ ▒▒███▓█
░░██░░▒▓░
░▒▒█▒█▓░▒
▒▒▒░▒▒▓▓
█▒ ▒▒▓
-17
View File
@@ -1,17 +0,0 @@
▓██▓
░█▒░░
▒ ▓░░
░▓░█░
░ ░░
░ ▓ ░
▒░░ ▒░
░▓ ░
▓▒ ▒░
░░▓▓░░
░ ▒░
░▒█▒░
░▒█░░
█▒▒▓░
░ ▓█░
-17
View File
@@ -1,17 +0,0 @@
██████
█░█▓ █▒
▒█░░ █ ░
░░░░▒▒█▓
▒ ░ ░ ░
░█░░░ ▒▒
░▒▒░░░ ▒
░░▒░░
░░░█░ ░
▒░▒░░ ░
█░░▓░▒ ▒
░▓░░░ ▒░
░░░░░░▒░
░▒░█▓ ░█
░░█ ▓█
-17
View File
@@ -1,17 +0,0 @@
▒▓▒▓██▒▒▒▒█▒
▒██▓▒░░█░ ▒▒░▓▒▒░██▒
█▓▓█▓░█ ░ ░ ░ ███▓▒░█
▓█▓▒░▓██▒ ░▒█ ░░▒
█▓█░▓▒▓░░█▒▒ ▒▒▒░░▒
▓░▒▒▓ ▓█░▒▓▒▒ ░ ▒▒░
▒█ ░ ██▒░▒ ░█ ▓█▓░█
█▓░█░ █▓░ ▓▒░ ░▒░▒░
▓ █░ ▓░██░░█▓░▒██▒▒▒██▒░▒ ▓░
█▒▓▒█ ▓▓█▓▓▓░ ░█░▒▒█ ▒▓█▓▒░░▒░░
█▒░ ░ ░░██ ███ ███▓▓▓█▓
██░ ▒█ ░ ▓▒█▒▓▓
▒▒▓▓█▒█ ██▓▓ █░█
▒▒██▒██▒▒▓▒▓█▓▒█▓░▒█
░███▒▓░▒▒▒▒░▓▓▒
-17
View File
@@ -1,17 +0,0 @@
▒▓ ████
▒▓▓░░▒██▒▒
█▒░█▒▒░██▒
░░▒░▓░▒▒░▒ ▒█
▒█░░░▒░█░█ ░
░█░▒█ █░░░░▓░
▒▓░░░▒▒ ▒▓▒░ ▒░
░ ██▒░█░ ░▓ ░
░▒ ▒░▒░▒▓░█ ░
░░▒░▒▒░░ ██ ░
▒░░▓▒▒█░░░█░░
░█▓▓█▓█▒░░ ░
▒░▒░░▓█░░█░▓
█▒██▒▒▓░█▓█
▒▓▓░▒▒▒▓█
░░░░
-17
View File
@@ -1,17 +0,0 @@
▒▓▓████▒█
▒██▓██▒ █▒███
█░▒▓▓█▒▒░▓ ░▒█▒
█░▓█▒▒█▓▒█▒▒░▒░░▒
▒░░░░█▓█▒▒█ ▒░▓▒▒
▓░▒░░▒░█ ▒▓██▓▓░█ ░
▓░░ ░▒█░▒▓▒▓▓█░█░▓░
▒▒█ ░░ ░▒ ░▒ ░░▒▓░
░▒█▒░█▒░░░▓█░░░▒ ░
░░░▓▓░░▒▒▒▒▒░▒░░ █
▒█▒▓█░█ ▓███░▓░█░▒
░░░▒▒▒█ ▒▒█ ░
▓░█▒▒ █ ▓ ░█░▓░
▓░▒░▓▒░░█░ █░░
█ ▒░▒██▓▓▓█
░░░░
-17
View File
@@ -1,17 +0,0 @@
█████▓▓░█▒
▓█░██░▒░░██░░█
▓▒█▒▒██▒▓▓░█░█▒███
█▓▓░▒█░▓▓ ▓ █▒▒░██ █
▓▓░█░█▒██░▓ █░█░▒▓▒█▒█
▒▓▒▒█▒█░░▓░░█▒ ░█▓ █
█░ ▓█░█▒░░██░█▒░▓▒▓▓░█▒
░░░█▒ ▒░░ ▓█░▓▓▒ ▒░ ░
▒░░▓▒ █▒░ ▒▒░███░░░▒░ ▒░
█ ▒░░█▒█▒▒▒▒▒▒░░█░▓░▓▒
█▒█░░▓ ░█ ███▒▓▓▓▓▓▓
▒█░▒▒▒ █▒░▓█░
███░░░█▒ ▒▓▒░▓ █
▒▓▒ ░█░▓▒█░▒█ ▒▓
░▓▒▒▒██▓█▒
░░░
-17
View File
@@ -1,17 +0,0 @@
▒██▒█▒█▒░▓▒
▒██░░▒█▒░▓░▓░█░█▓
▒▓▒░████▒ ░ █▓░░█ █
█▒▓░▓▒░█▒ █░░▒▒█
▒▓░▓░░░▓▒▒▒ ░█▒▒▒
▓▓█ ▒▒▒▒░▒█ ▓▒▓▒▒
░░█ ▒██░▒░▒ ░█░░
█░██ ███▒▓▒█ ▒ ░█
░░░ ░ █░ ▓████▓▒▒█░░█▓▒░▒░
▒▓░█ ▓▓█▓░░░▒▒▒▒▒░░█▒▒▒░░▓
▒▒▒█ ░▓░▓ ▓ ███ ░░█▓▒░
▒█▒██ █ ▓▓▓▓▒▓
█▒ ███▓█ ▒█░█▓█▒█
▒░ █▒█░█▓█▒ ▓█▒█░█
▒▒██▒▒▒▒██▓▓
░░░░
-17
View File
@@ -1,17 +0,0 @@
▒█▒████▒░█▒
▒███▓▒▓░ ░██▒██▓█▒▒
▒▓▓█░█ ▓░█░ ░▒▒▒█ ███
█▓▒░█▒▓█▒ █░██▒▒
▓▓░▒▓▓░ ░ █ ▒▒█▒▒
█▓▒░░▓ ▒▒ ░▒█▒ ▒█▒░▒
░█▒░▒ █▒▒█░▒▒ ░▓░▒
▒░▒ ▓ ░█▒░▓ ░ ▓ ▒▒
██▓▓ ▓▒▓▓ ▒▒▒██████░▒▒ ░▒░
░░▒█▓██▒ ▓▓█░░░▒░▓▒▒▒█▓▒░░░░▒
▓▒▒█ ░▒░█▒ ██░░░░▒ █▓█▒░█
▓█▒▓▒▒▒ ▓▓▓░▓█
▒█░░█▒▓█ ▒█▒ ▒▓█░
▓▒▓░ ░██▓██▒█▒█░██▓█
░▒▓▒▒▒▒▒▒▓▒█▒▒
░░░
-17
View File
@@ -1,17 +0,0 @@
▒██▓▒███▒██▒
██▒█▓░███ ░█░▓ ░█▒▒
▒▓▓░▓██░▒█ ░ ░ █▒█▓ ░██
█▓▓█▓█▓█▒ ██▒▒░▒
▓▓░░▓▓▒ ▒██ ░▒█░█
▓▓▓▓█░ █░▒ ▓▓█▒ ░▒▒░
▒ ▓▓ ▒▒ ██▒▓ ░▒▒▒
░░░▓ ▓▒▒▓▓█ ▓ ▓
▓ █▒ █░░▓▓ ▓░▒▒▒▓▒▒█░░ ░░▒█
░█▒▓█ ▓▓▓ ██▓░▓ ▒█▒▒▒▒▓ ░▓█ ░█
▓░▒██▓▒▒░▓▒░ ░ ▒▒▒▒█▒▒█▓▓▒█░
▓▒▒▓░ ▒▓█ █▒
▒▓░▒▓█▓█ █▓▓▒███
▒▒ ░█░▓▓░░█░▓▓█ ▒▓▓
▒░▓▒▒▒▓▒▒███ ▒
-17
View File
@@ -1,17 +0,0 @@
▒▒█▒████▒██▒
▒▒ ▒█▓▓▓█▒█▓██ ███▒
█▒█▒███▓█ ░░ ░ █░██░██░█
▒░ ██▒▒▒▒ ██░▒ ░
█▓▒▓▒█░▒░▒█▓ ▒▒▓█
▓ █▓░ █▒ ░▓█ ▒▒█
░ ▓ ░ ▒ ▒▒ ░▒░█
░░▒░ ▒▒ ▒▓▓ ▒░ ░
░█ ░ ▓▓ ██ ████▒█████▒ ░▒░░
▒█░▒ █░▒▒▓░▓ ░░▒▒▒▒▒▒▒░░ ▒▓█░
█ █░▒ █▒█▓▒ ██▒▒▒▒▒ ░█ ▓
██ ▒▓▓ █▓░ ▓
▒▓░░█░█ ███ ▓█░
██▒ ██▒▒▓░▒█░▓ ▓ █▓██
░██▓░▒██▒██████
-17
View File
@@ -1,17 +0,0 @@
▒▓▒▓█▒▒█▒██▒
▒▓ ██░▓ ░▒▒▓█▓░ ▓██▒
██▒░░░██ ░ ░▒░▒▒█░▓▒▒▒
▓▓░█░ ▓██ ░██▒█▒
▓░▓▒░▒░▒▓▒░█ ▒ ▒░█▒
▓░░▒░ █▒░░▓█▒ █ ▒▒░█
▒░▓░ ███▒█ ░█ █ ▓░
░▓▒ █░▓█▒░░ ░░░
▒░ ░▒ ▓░▓ ▒▓▓█░███▒▒▒▒██ ░░█
░▒▓ ░ █▓▓▓█▒░░▒▒░█▓▒█▓▓▒▓░▓▓ ░
░░▓█▒█▒▒█▒▓ ████████▒▓░░░░
█░▒ ░▒░ █▒▓▓███
▒▒█▓▒ █▒ ▒▓▒██▓░▓
░░░▒▒██▒▓▓▒▓██▒██▒░█░
█▒▒░▓░▒▒▒▒▒▓▓█░
-17
View File
@@ -1,17 +0,0 @@
▒▓▒▓▓█▒▒▒██▒
▒█ █▓█▓░░█░▒█▓▒░ ██
█▒▓▒█░█ ░ ▒▒░█▒ ███
█░▓░▓░▓▒█ ▓▒░░░░▒
█▒▓█▓▒▒█░▒▒█ ░ ▒░▒░▒
░░░░▓ ▒▒░▒▓▓░▒ █▓░░
░▓░ █ ░▒▒░▒ ░█ ██░█░█
░▓░▒ █▒▒░▓▒░ █░▒░
░█░▒█ ▓▒░ █░█▒▒░█░▒▒▒██▒ ░▓░
▒▒░▒██▓██ ░ ▓▓▒▒▒█▒▓█▓░▓█░░
▒█░░█░█▒▒▓█░ ██ █░▓░▒▓
▒▒█▓▒▒ ░ ▓▒▓██▒
▒▓█▒░▒█▒ ▒▒████▓█
▒░█░███▒▓░▒▒██▒█▒░▓█
▒▓█▒█ ▒▒▒▓▒███░
-17
View File
@@ -1,17 +0,0 @@
▒▓▒▓▓█▒▒██▒▒
█▒▓▓█░▒██░██▓▒███▒
███░░░█ ░ ░▓▒███▓▒▒
▓█░█░█▒▒█ ▒█░░░░█
█▒░░░█▒▒██▒ ▓▒▒░▒█
▓▓▓░▓░▒█▓░▒▒░█ ▓▒▒▓░
▒ █░░ ▒▒░▓▒▒ ▒█░▒░
░ ░░░ ▒░▒░▓░░ ░█▒░░
▒▓░▓░ ▓█░░█▓▓█▒░█░▒▒██▒▓▒▓░
░░▒█▓▒▒▒▓█ ░▓▒██░░█▓▒▒▒░█░▒
▓ ░ ▓░░░▓▓ █ ██ ░▒▒▓░
█ ▓ ▓█░ █▓▒▓▓░░
▓░▒▒███ ▒█▒▒▓███
░ ░██ █ ▓░▒▒████ ▓▓█
▒▓▓███▒▒▒░▒███
-17
View File
@@ -1,17 +0,0 @@
▒▓░▓██▒▒██▒
██░█▒░███▒▒▒▓ ░██
█ █░░░█░ ░▒░░ █▓▒██
▒▒░░░░▓█ ▒░▒█░▓█
░█░█░░▒░▓▒█ ▓ █░░▒
░ ▓░░ ░█▒▓░▒ █▓░░░
░▒ ░ ▒▒░▒░▒░ ██▒░░
▒ ▓░░ ▒█▓░█░░ █ ░░░
▓ ░█ █ ▒▓░▒▓░░▓▓▒░░▒▓█▒░░
░██░░▒▓░░▓█░▓▒░░▒▒█▒█▓▒░▒░
▒ ▒▒▓█░█▒▓ ██████ ▒▓░░
█▒ ▓▒▓▒░ █ ▓▓▓▓█
█▓██▒▒▒▒ █▒░██▓██
▒▒█▒░█▒▓░▒▒▒██░██▓
░█ ░▓░▒▒█▒▓██
-17
View File
@@ -1,17 +0,0 @@
▒▒█▒▓██▒██▒
█ █▓░░░█▒▒ ░ █
▒░▒█░▓▓█ █ ░▓░█▒█▒█
▒█▒█▓░██░ █ ▒▒░░▒
█ ▓░▓█▒░▓▒ ▓█▒░░█
░██░▒▒▒▒▒░▒█ ▒█░░░
░█░░░ █▒▓▒░░░ ░▒░▓░█
▒█░░▓ ░█▒▓░██▓ ▓░▓░░
▒ ▒░░▒▒ ▓█▒░░▓█████▒░░░
▒█▓▒▒░ █░█░░▓░▒▒▒░░▒█
▓▓▒▒░▒░░░▓█▒█▒█ ▒█ ▓▒░
██ ░▒░░░ ▓█▓▓▓█
█▒▒█▒▒▒▒ ▒▓▒▒░█▓█
▓▓█░██ ▓▓██▓▓▒█░░
░░▒██▒░▒██▓▒░
░░
-17
View File
@@ -1,17 +0,0 @@
▓▒▒█▓██▒█
▓█▒▓░░█ ▒ ▒▓▒▒
▓ █░░▓█▒▒▒▓ ▒▒░█
░░▓▓▒▒ ▒▒█░▒▒░██
▓█ ▓▒█ ░██ █▓██▓█░░
░ ░░░ ▒░▒▓▒▒ ░█░█░░░
░ ░█▒░██░▒▒█ ▓█▓ ░░░
░ ░▓▒█▒░░░▒▓▒▒▒░ ░░
█░ ▓░ ░░░░█░░█░░░
░▒░░░▒█░▒░▒░░░░▒▒░░░
░▒▓▒▒░▓ ████░░ ▓▒░
▒░░░▒█░ █▓ ▒▓░░
▒█▒░▒▒ ▓▓▒▓░▓█
▒▓ ▒▒░█▓█▒▓▓█░░
█▓▒ █▒▒░▓█▓
-17
View File
@@ -1,17 +0,0 @@
eoeddccddcoe
edoocecocedxxde ecce
oxcxccccee eccecxdxxxc
dceeccooe ocxdxo
eedocexeeee coxeeo
xc ce xcodxxo coexxo
cecoc cexcocxe xox
xxexe oooxdxc cex
xdxce dxxeexcoxcccccceco dc x
exdc edce oc xcxeeeodoooxoooox
eeece eeoooe eecccc eccoodeo
ceo co e ococex
eeoeece edecxecc
ecoee ccdddddodcceoxc
ecccxxxeeeoedccc
-17
View File
@@ -1,17 +0,0 @@
eccccecce
ccecccexoeco
eeoxxoxxoxceoo
xeeoexdeoeocceeo
o dxxcxe cooeoxo
xe cxcxooe eecx
e xcccxxxxc xoo
c xxecocxxoeeoexx
c xe eexdxxcecdxx
x oxeoxeoeceeexce
o cxxxxxcc eocexe
eecoeocc exccooo
xc xxxxcodooxoe
deccoxcde ooc
co eceeodc
-17
View File
@@ -1,17 +0,0 @@
occcccce
oc dxxxeeo
oceexxdecoeo
xeexxddoedoo
ecodexcecdexxo
xcexxceddxeoxx
cc oxxxxxxexde
x xxoxxeo xcx
o cxoxxcocxex
cc exodocoxexe
ceo xxxxdoxeex
eeooxecoccdxe
e cxeeeexdc
ec cxxoeoce
ee cccece
-17
View File
@@ -1,17 +0,0 @@
ccccco
odeeoxoe
c xoeco
ocxxxddcx
x cxxxxoox
xcoocecexc
x xoexxe
x ocexxc
co xoxxcxx
x oxcdce
xo xcdcco
o cx eox
o ccxocex
ceocoxexe
e cxeoo
-17
View File
@@ -1,17 +0,0 @@
occco
xeexx
xeexc
xccxe
c xx
cdoxx
o xx
c cx
oc exo
xc cdx
ceoo xe
xeeex
xcoxe
ceexd
o ocd
-17
View File
@@ -1,17 +0,0 @@
ccccd
ooeeoe
xexxo x
xxoxcexo
xxxe x
xcxx cx
xxxx o c
xxexe e
xxxx c
ceoo do
exccooox
xcxxeeex
o cxddde
xeoceeo
ec cdo
-17
View File
@@ -1,17 +0,0 @@
cccccxe
eodxxedco
ooxcdexccx
xoe ooooeex
xxdcdexxocex
exxoxxoox c
xx xxxxxxox
xxoxxcxxx cox
xxcoocxxxeodx
xexdoxexco ox
xoxxxxex e d
xccoexxeo d
cxeo oooe de
xexxeeoceo
eeceeeeo
ee
-17
View File
@@ -1,17 +0,0 @@
edcccccxe
oexxcxxexde
xooceodexx ce
ooo dceexexxccx
xxdeoccdxxcoxee
xxxcxc xed x xox
eex oeoxxxxocco x
xod xexxoxxxcd ex
eexxxcxoexxccc o
cceeoddecxoex oex
xxxcccocexdcdoxxe
xxc xe eooo o
exc x oooeox
exxcecxoocex
cdoeddeedc
-17
View File
@@ -1,17 +0,0 @@
odcccddxoe
edccxxxcdcxoceo
oceoeddecocxxxece
oxoeoxcee cxdexxxde
xoe x xcoedeoo o
edcooe odox oodoxoo
c dox oooxe ccxxodx
ocdx ooxxoxoxxddc
oocoeddcxeexeedexxx x
xcedeexoceoxxe eccce
eeeoccccccceexcooe ec
exxec eoxxe d
eee cee ocooeeo
o xccdeceedcdxc
ecdoeocxcecc
e
-17
View File
@@ -1,17 +0,0 @@
eddcddcdxoe
eccedoccxeeoccdde
eodxcccdcocoeccooe c
oxxcooecc ceeeodxedeeeo
eeoo ox ecceeoxoxeedeee
oex ooxoeeeoocoxcooeoeox
xxedo cocoxceoccxdxdo
ceoxx eecxxde xdxc
ecc oedddddcxxoxcoeo xcxe
eeexcec xxoeeeexxxedxee o
xoxeeccccccce eeeoxocoeoe
ee oeo eeccocec
eecceeo eceeoeoe
cxoccccdddecceoeoc
cxxeoeeooccdcc
e
-17
View File
@@ -1,17 +0,0 @@
eeddcxcddxoe
ecxxxeodddeceoxcoo
ocddocxcce ecdoecde
odxcoee eddcoexco
xxoeoe oxecocxe xeo
xeocc excxo oo cocx
edxxc oceoxcoe odocx
xxxx xdcexco x xxx
xcxeoddddddxxxxccdcxd e cxx
edooxdcoecceoeo ee deeeoooxe
cecocxcccccccc eeeoxoo ooc
eeecee eooeooc
c eexxco oddooxde
ccoxcoxceeddocc dcxc
cxoedoceooecoe
-17
View File
@@ -1,17 +0,0 @@
eoeddcdddcoe
ecoocdcxxxdxxdecxcce
oxcxeccxcee eccdcoxxdxo
exoeoccooe ooexoxo
oecocexeeeee eoxexo
cocce xcecoec eexcx
oxccx eoxdxexo ocxcx
xc ee oxcxxdc xcoox
cccdx dxeeexcoxccccccccoxexxc
edcx oxxc oc xdeeeeeooeexco x
eee c ceooxc ecccccccccxocxx
ceeooo e ocdooc
oeeexco odec exc
exedeecccdddddodceexxc
eccccxxeeeexdocc
-17
View File
@@ -1,17 +0,0 @@
eecdxxdcdoee
oddcdoeodddxxeececo
oocecccxcc ecececcxce
excecxc eocxeocee
ex oxc eo exxecexxe
oeoxc cccdxco cexxe
dxdcx oc occe oexo
xeeoe ccddxco xxcx
xoxxdoddddddddeocdeeeec o xe
cxexec oeeeeeexe ceecxde oo xx
eoeecccccccccc eodxxox oe
c ecoo eocoxo
eeecoxe odcedcc
eooocxceddodcxceoocc
eccxe deeeexccc
-17
View File
@@ -1,17 +0,0 @@
eeodcddcdcoee
occeeeecxdxcdeeocce
dceeccece eexcceeco
ocxdcc eodcodco
oooce oxoee eeeeo
ocox occeoo eeeo
xcxe e oeooc edec
ee ed cxo x x
x x ocdddddccc exocxo do x
x xe xe eox ececxo ocoo
d co eeccc ce cceod oe o
cc dde ecc o
ce eoe eodcc oe
cde ccccdxxdddccc oe
cccdceeeeoedcce
-17
View File
@@ -1,17 +0,0 @@
eocdcddcdcoe
ecocxoeoxoexxdxcocce
odcdxecce ecceccceeco
dcxccc ooxxxece
exxoc oeoxdxoodcx
excoe oxoxdeoe exedx
xcceo xcxecoc xxox
xxdxe xexxee xexcx
xxoco cxddddddcceecxe eo exdc
exd ceeeo oocxoox ecdecxoo oed
eeeex cccccccce edcceooocoe
eceeeo ecocxoc
cccd cce eococceo
cdccoccxddcddodccccoc
cxcxedeeeodeodce
-17
View File
@@ -1,17 +0,0 @@
eocedccccdcee
edxcxeeoeddoxexcxce
occxcodce cxdcxedxxo
odxcdoe eddexxde
ooxeoc ooooccocexe
oexcoe ecccccoccxxexo
exxcx odoo exe c xcc
xox x xcxoeeo x cox
ece xcxddddddddxecxecee x xxx
xeeexcdc oee exeeox eex
ocx x eccccccc ceoddxeoeoe
oxxexo ooxeeoe
e xocoee eocdcoco
edecdccexddecccoecce
cx cdexeeceecce
-17
View File
@@ -1,17 +0,0 @@
exedcccddoe
oceocxeexddcoecc
occdeoccx oedcxcco e
ocooooxdoeexoe ecexeec
o ooeoo eccoeexeeexoc
xoecee cooo oxd oce
x xx ooeoocoeexeexe
x exx xodoeexxeooexx
xo xddddccxxxccecoex x xx
e o cxoooddooxoeeccx xcx
e cexeccccccce eoocexdooe
e eoce x codo
eoceexo edceodec
oocoeecxxddddxeeoe
cdeccdeeeddcc
-17
View File
@@ -1,17 +0,0 @@
ecdcdcccce
o coceedexcxxo
oxoooocoxcedexxxe
xccx o dx cexoceo
oeeeoocedoexc xooeoc
eoxxxeccoexd oxoxooxo
xoxcx xeeoeeoxeoecxdx
xxxoxoc xedeoxeexdxxe
ecexcxeeddddcxxeexccxe
oocxoxoxexxdcexecdoex
excoexecccccccoxexoxe
xecxdcdeoocdeooooxo
eeexeeecdooeoexxo
eodeeecdxcc cxc
xoccecoecxc
-17
View File
@@ -1,17 +0,0 @@
edccccco
ocxdoexcdxo
occcxdexecceo
dccoxxxexxecoe
xeoexoxcceodxed
e cxodocceeceeo
x ccdxxoxxddcc
oo exxxeedxxoxx
x oecdcxcddoexx
oexooxeeoceecx
xecoxcceooecexx
eexxoe oocxxe
c cxe eeoxoo
xcceecceccd
eodecxeec
-17
View File
@@ -1,17 +0,0 @@
dcccco
xddoxoe
dce cxocx
xxxexxdxx
x exeocd
xeoecexxxe
d cxxecxx
x exxxdcxx
xo o xcxxxx
cd ocexecxx
xo eecccoc
xxccxxeox
xddcdooxe
eeexedoo
cec eeo
-17
View File
@@ -1,17 +0,0 @@
occd
xcexe
d dxe
xoecx
x xx
x ocx
exx ex
xoccx
oe ex
xxodxx
x ex
xdcdx
xdcxx
ceeox
x ocx
-17
View File
@@ -1,17 +0,0 @@
ccccco
oxco ce
eoxx ccx
xxxxeeoo
e xcx x
xoxxx ee
xeexxx e
xxdxx
xxxcx e
exdxx e
cxxoxe d
xoxxx ex
xxxxexex
xdxcocxc
xxc oo
-17
View File
@@ -1,17 +0,0 @@
eoddccddddoe
ecooexxcxcddxdeexcce
odocdxccce ecx cccoexo
ocoexdoce edc xxe
cocxoeoxxcee eeexxe
oxeeo ooxedee x eex
dc x ccexecxo ocoxo
ooxox ooxcoex xexdx
occx dxccxxcoxdcceeeccexecdx
oedeo oocoddx xcxeeo doodeexexe
cex x cxxcoc cccccccccoooooo
ccx ec e oeceoo
deooceo ocdocoxc
decoecceddddoddcdeecc
ecccedxeeeexdoec
-17
View File
@@ -1,17 +0,0 @@
edcccco
eodxxeccde
ccexoeexcoe
xxexoxeexe eo
dcxxeexoxo x
xcxec cxxxxox
eoxxxee eoex de
cx ccdxoxcxo e
cxecexdxeoxo e
cxxexeexx co e
exxdeecxxxcxx
xcoooocexxc x
exexxocxxoxo
oeocdeoxooc
eooxeeedc
eeee
-17
View File
@@ -1,17 +0,0 @@
eodccccdo
eccdcoeccecco
oxeooodeeocxece
oxoceecdeoeexexxe
exxxxcoceeocexoee
dxeexexccedcoooxocx
oxx xecxeododcxcxox
eeo xxcxe xeccxxeox
xeoexcexxxocxxxe x
cxxxooxxeeeeexexx c
eceocxo occceoxcxe
xxxeeeo edc x
dxcde o o xceoe
dxexoexeoxcoxe
ccdxeccoodc
eeee
-17
View File
@@ -1,17 +0,0 @@
occccddxoe
dcxccxexxccxxo
oecdeocedoecxcecco
cooxeoedo o oeexco o
ooxoxceccxd ceoxeoeceo
eoeeoecxedxxce xco c
cxcdoecexxooxodeoeooxce
xxxoe cexxcocxdoecexcce
exxoe cexceexcccxxxdxcde
ccceexceceeeeeexxcxdxoe
oecxxo xccccccedooooo
eoxeee oexocx
cccxxxce eoexo o
eoecxcxddceecceo
xddeeococecc
eee
-17
View File
@@ -1,17 +0,0 @@
eocdcdcdxoe
eccxxecdxdxoxcxco
eoexcccodce ccoxxcco
oeoxoexoe cxxeec
eoxoexxoeee xceee
ooo eeeeeeo oeoee
xxc eocxexe xcxx
cxoo occeodo ecxc
xxx x oe ocooodddcxxcoexex
eoxccodooexxeeeeexxceeexxo
edeo xoxo o ccccccc xxooee
ececoco oododo
ceccocdo ecxoocec
exccecxodcecdoecxc
cddcoeeeeccdoc
eeee
-17
View File
@@ -1,17 +0,0 @@
eodccccdxoe
ecccoedxcxccdccdode
eoooxccdxce eeeeoccco
ooexceooe cxocee
ooxeoox xc o eecee
ooexeo eecxece eoexe
xcexe ceecxee xdxe
exdcd xcexocx o ee
ocooc oeooceddccccccxeec xee
xxeooooecoocxxxexoeeeooexxexe
oeeo xexce ccceeeee oooexc
ooeddee odoxoc
ecexcedo ecdceooe
oeoxcxcodocdcdceccdc
cxeddeeeeeddcde
eee
-17
View File
@@ -1,17 +0,0 @@
eocddcccdcoe
ocdcoxccccxcxdcxcde
eooxdccxecce eccdcocxco
ooocoodoe cceexe
ooxxooe ceco xecxo
dodoce cxecooce xeex
e oo ee cceo xeee
xxxd oeedoc o co
o oe oxxodcoxddededcxx xxdc
xoedc oodcccoxd eoeeeeocxoc xc
oeeocoeexoee eceeeeceecooeox
coeeox eoc oe
edeedodo odoeccc
ceecxcxodxxcxdocceodc
cexddeeoeecccce
-17
View File
@@ -1,17 +0,0 @@
eecdccccdcoe
edccecodocecdcccccce
oeceoccoccee eccxccxocxo
exccceeee ccxecx
ooedecxeeeoo eeoc
o ooe ce cxoo ceec
x d e e cee xexo
xxex ee eoo ex x
xccx oo occcocceccccce xexx
ecxe oxeeoxo exeeeeeeexx eoce
c cxe cecoe ccceeeeec xoco
cocedo ooxco
eoxxcxo occcooe
coecccdedxecxdcocodcc
eccoxeooeooccccc
-17
View File
@@ -1,17 +0,0 @@
eoddcddcdcoe
eocccxo xedocdxcocoe
ocdxxxccce eexeecxoeee
ooxoxcoco cxccece
oxoexdxedexo ecexce
oxxex cexxoce c edxo
cexde ccceccxo o cdx
xoe oxocexx xxx
ex xe dxoceoocxccceeeeoo xxc
xeo x oooooexedexooeodoedxoocx
exdceoeeoeo ccccccccceoxxxe
oxecxee oedoccc
eeode oe eoeocdxo
xxxdecceddddocdccexce
ceexdxeeeeedoce
-17
View File
@@ -1,17 +0,0 @@
eodddcdddcoe
ecccocoxxcxdcdexcco
ceoecxcce cedxce oco
oxoxoxodo oexxxxe
oeocoeecxeeo e exexe
xxxxo eexedoxe coxx
xox c eeexecxo ccxcxo
xoxec oedxoex cxex
xoxec oexcoxcdexcxeeecoecxox
eexeoooccc xc ooedeodoooxocxe
eoxxoxoeeoce ccccccccoxdxeo
eecoee e oeocoe
eocexdce edcoccdc
excxoccedxdeocdcexdc
eocecceeeoeocce
-17
View File
@@ -1,17 +0,0 @@
eodddcddccee
oedocxdccxccdeocce
oooxxxcc ecxodcccoee
ooxcxcedo ecxxxxo
cdxxxceecce oeexeo
dooxoeecdxeexo odeox
e cxx eexoee ecxex
x xxx exdxoxx xcexx
eoxox ocxxcdocexcxeecceoeox
xxeooeeedc xodcoxxodddexoxe
o x oxxxdoc cccccccceeeox
o d ooe odeooxe
oeeecco eceeococ
ecxcocc dxeeoccc ddc
eodccoeeeeeocc
-17
View File
@@ -1,17 +0,0 @@
eoxdccddcoe
ocecexcccdded eco
c oxxxce eexe coeco
eexxxxdc execxoo
ecxcxxexoeo o cxxe
x dxxc ecedxe ooxxx
eecx eexexex ccexx
ecoxx dcoxcxe c xxx
ocxc oceoxeoxxddexxddcexx
xocxxddxxocxoexxeeododexex
e deocxceo cccccccceoxx
ce oeoee ocoodoc
cdoceeee oexococc
eecexcedxeeeccxcco
cxccxdxeeoedcc
-17
View File
@@ -1,17 +0,0 @@
eecedccdcoe
occoxxxcdd x cc
exdoxooc ocxoxceceo
ececoxocx c dexxe
c oxooexoe ocexxo
xcoxeeeeexec ecxxx
xcxxx cedexex xexoxo
eoxxo xceoxoco oeoxx
e exxee ocdxxococooexxx
ecodexcoxoxxdxdeexxdc
ooeexexxxocececceccoex
cocxexee oooooc
ceeceeee eoeexcoc
odcxoc ddccdodoxe
xxeccexeocode
ee
-17
View File
@@ -1,17 +0,0 @@
odecoccdo
oceoxxccd eoee
o oxxoceedo eexo
c xxodde eeoxeexco
occdeccxco coccdcxx
x xxxcexedee xcxcxxx
e xcexccxeeocooo exx
x xoeoexxxeodeex xx
coxc oxcxxxxcxxoxxe
xexxxeoxexexxxxeexxx
c eeoeexocccccxxcoex
exxxeoe oo eoxe
ecexee odedxoc
eoceexcocdddcxe
coe ceexdcoc
-17
View File
@@ -1,17 +0,0 @@
                                      
             _._:=++==+,_             
         _=,/*\+/+\=||=_ _"+_         
       ,|*|+**"^`   `"*`"~=~||+       
      ;*_\*',,_            /*|;|,     
     \^;/'^|\`\\            ".|\\,    
    ~* +`  |*/;||,           '.\||,   
   +^"-*    '\|*/"|_          ! |/|   
   ||_|`     ,//|;|*            "`|   
   |=~'`    ;||^\|".~++++++_+, =" |   
    _~;*  _;+` /* |"|___.:,,,|/,/,|   
    \^_"^ ^\,./`   `^*''* ^*"/,;_/    
     *^, ", `              ,'/*_|     
       ^\,`\+_          _=_+|_+"      
         ^*,\_!*+:;=;;.=*+_,|*        
           `*"*|~~___,_;+*"           
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
              _+***\++_               
             *'`+*+\~/_*,             
            ^_,||/~~-~+\,,            
           |__/\|;_.\,''\\,           
           / ;||"|^  /_/|/           
          |` '|*~//\   `_"|           
          \  ~*"*||~|*   |/,          
          "  ||\+/+||-_ .\||          
          "  ~\ \\|;~~+\+;||          
          |  ,|\,|_/_*___|*`          
           , "|||||""!\,"\|`          
           \`',\,*"  "",//           
            |' |||~*,:,/|/`           
             ;`**/|+;_!//'            
              *, _*\_,;*              
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
               ,****++_               
              /" ;|||\\,              
             /"__||;\*/\,             
             |__||=;,_=//             
            _".;\|+\';_||,            
            |+`||+_;;|_/||            
            ** ,||||||_|=\            
            |  ||/||\/ |"|            
            /  '|/||*/+|_|            
            ** _|/=,"/|_|^            
            '`- ||||=/|\\|            
             \_-/|_*/**;|`            
             !_ *|\\^_|;"             
              \+!*||,_/*`             
               \_ '*+_+`              
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
                +***+.                
               ,=`_/|,\               
               "  |/\+,               
               /+~||=="|              
              | '~|||./|              
              |'..*^"_|"              
              |   ~/\||\              
              |   /+\||"              
              *, ~/||+|~              
              |   /|*;*_              
              |.  |"=**/              
               ,  *|!_,|              
               / **|,*\|              
               '^/",|\|`              
                \ '~\./               
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
                 /***,                
                 |__||                
                 |`_|"                
                 |**|_                
                 *  ||                
                 ":-||                
                 ,  ||                
                 +  "|                
                /+  _~.               
                |"  +=|               
                '`.. ~`               
                 |___|                
                 |+,|_                
                 *__|=                
                 , ."=                
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
                 +***;                
                 ,/__.\               
                |_||. |               
                ||/|"^~,              
                |||\   |              
                ~*||  '|              
                |||| . *              
                ||\|`  \              
                |||~   "              
                "^//  ;/              
                \|"",.,|              
                |*~|___|              
                /!"|===`              
                |\/*__/               
                 _* '=/               
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
                 ++***~_              
                `,=||^:*,             
               //|*=\|"*|             
               |/` //,.__|            
              ||="=\||/"^|            
              \||-||//|  "            
              ||   ||||~~,|           
              ||/~|+||| '-|           
              ||+,,*|||_.:|           
              |_|;/|\~*. .|           
              |/||||_| ` ;            
              |**.^~|\-  =            
              '|\, ///` ;`            
               |^||\\.+\/             
                \^*^___/              
                   ``                 
-17
View File
@@ -1,17 +0,0 @@
                                      
                _=+"**+~_             
               /^||*||\|=\            
              |//"\/=\|| '\           
             /// ;' \|\||**|          
             ||;_ =||*/|`\          
            |||*|  /|= !| ~.|         
            \\|  ,||||/*", |         
            |/; |`||/|||"; `|         
            \\|~|+~/^||"*+  /         
            *"__,==\*|._| ,_|         
            |||+""/*\|;";.~|`         
             ||* |   `//,  /          
             \|*  |  /,/_,|           
              \|~"_*~//+_|            
               ':._=:__;*             
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
                ,=+++;;~,_            
             _;**|~~*=*|,"^,          
            ,*\/_==`+,"|||_"\         
           /|/_/|"   |;\~||=\        
           |/_ ~     "/\=\//  ,       
          `=*,/`   ,:/| /,=/|./       
          *!;/|   ,//|_ *"||/=|       
          -"=|!   !//||/ ,||=;*       
          ,/*/\==+~\_|\^:\||| |       
           |"_;__|/*\/||\!\+'+\       
          \\\/"""****\_|*//\ \'       
           \||_*       `/||` ;        
            _\\!*\_   ,',/^_/         
             , ~*+=\+`_;*:|'          
              `+;/_,+~*_+*            
                   `                  
-17
View File
@@ -1,17 +0,0 @@
                                      
               _==+==+;~,_            
            _+"_;,++~__,"+;;_         
          _/:|*"*=" "._"+//\ *        
         ,||*.,^" _/=~\;\\\,      
        _\// /|   _\/~/|_\;\\_      
        /\| ,, _/,*,|'-/^/`/~!     
        ||\:/     +/*/|"_/"*|=|=,     
        "\-~|     ^\"||;^   |;|"      
       \"" ,\==;=;+~|,|*/\, |*|`      
        _\\|*\* ~|/__\_~||_;~`\ ,     
        |/|\`""*****` \__/|/*/`-`     
         \\!,\,         ``*"/*_'      
          \^*+^^.      _*^_/\,`       
           '|.**++===^'*_/_,*         
             "~|_/__,.+";+"           
                    `                 
-17
View File
@@ -1,17 +0,0 @@
                                      
              __==+~+==~._            
           _+|||\/===_*_,|*,,         
         ,*;;/"|+*`    `*:,`*;\       
        /;|*,^`         _==+,^|*,     
       ||/`/`         ,|^"/"|\ |\,    
      |\/*'         _|*~/!./ '.*|     
      ^=|~'        /*^/|+,`   /:/+|   
      ||||         |;"\|",    | |||   
      |*|\,=;;===~~~|+*;*|;   \ "||   
      ^;/,|=*/^*+\,`, ^_ :\_\,/.|_    
      '\"/+|"""""**"   ^\_/|// //'    
       \\^*_\             `//_//'     
        '!_\~~*,        ,;=./|;`      
          '".|*/~+__=;/*" ;*~'        
             "~._:-'_,.^*-^           
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
             _._:=+===+,_             
         _+,/*;+||~=~|=_'|*+_         
       ,|*|\**~*``  `"*=*/||;|,       
     _|/_/*',,_           -,\|/|,     
     ,^"/*^|\_\\_           ^,|\|,    
    '/+"`  |*\+/\+           \\|*|    
   ,|'*|    ^/|;|_|,          /"|"|   
   |" \`     ,|*||;*          |'/.|   
   *""=|    ;|^^_|".~++++++++,|_|~*   
    _='|  /||' /* |=\____..__|+/!|!   
    \\\ * *\..|'   `"*******"|,*||    
     '\_./, `              ,+;/,*     
       .\__|+,          ,=_+!_|"      
        `~^;__"*+:;=;;.=*`_||*        
           `*+*+~~____~;/*"           
                                      
-17
View File
@@ -1,17 +0,0 @@
                                      
              __+=~~=+=,__            
          ,;=";,_,===||_^*\+,         
        ,,"_+*"~*"    `"^"\+*|+_      
      _|"_*|*           _,+|\/*\\     
     _| ,|*           _/!_||^*\||\    
     /`,|'           +*';|"/  '\~|\   
    ;|;+|          ,* .+*^     ,\|/   
    |_^/`          "";:|",     |~"|   
    |/||;,=;======_,';^^\\*    / |\   
    '|^|_" /``____|\  *\\"|=\ ,/ ||   
     \,^\'"""""""*"     \,=||,| /\    
      * ^*/,               _/*.|/     
        \_^*,~_          ,;*`;*'      
          ^,-."~+^;;,:"~"`,/*'        
            `*+~_!=____|*""           
                                      

Some files were not shown because too many files have changed in this diff Show More