feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
[package]
|
|
|
|
|
name = "codex-tui"
|
2025-11-24 12:22:18 -08:00
|
|
|
version.workspace = true
|
|
|
|
|
edition.workspace = true
|
|
|
|
|
license.workspace = true
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
|
|
|
|
|
[[bin]]
|
|
|
|
|
name = "codex-tui"
|
|
|
|
|
path = "src/main.rs"
|
|
|
|
|
|
|
|
|
|
[lib]
|
|
|
|
|
name = "codex_tui"
|
|
|
|
|
path = "src/lib.rs"
|
|
|
|
|
|
2025-08-04 21:23:22 -07:00
|
|
|
[features]
|
2026-02-23 14:15:18 -08:00
|
|
|
default = ["voice-input"]
|
2025-08-04 21:23:22 -07:00
|
|
|
# Enable vt100-based tests (emulator) when running with `--features vt100-tests`.
|
|
|
|
|
vt100-tests = []
|
2025-08-12 17:37:28 -07:00
|
|
|
# Gate verbose debug logging inside the TUI implementation.
|
|
|
|
|
debug-logs = []
|
2026-02-23 14:15:18 -08:00
|
|
|
voice-input = ["dep:cpal", "dep:hound"]
|
2025-08-04 21:23:22 -07:00
|
|
|
|
2025-05-08 09:46:18 -07:00
|
|
|
[lints]
|
|
|
|
|
workspace = true
|
|
|
|
|
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
[dependencies]
|
2025-09-22 18:47:01 +02:00
|
|
|
anyhow = { workspace = true }
|
|
|
|
|
base64 = { workspace = true }
|
|
|
|
|
chrono = { workspace = true, features = ["serde"] }
|
|
|
|
|
clap = { workspace = true, features = ["derive"] }
|
|
|
|
|
codex-ansi-escape = { workspace = true }
|
2026-03-19 21:28:33 -07:00
|
|
|
codex-app-server-client = { workspace = true }
|
2025-11-06 10:44:42 -08:00
|
|
|
codex-app-server-protocol = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
codex-arg0 = { workspace = true }
|
2025-11-17 16:06:26 -08:00
|
|
|
codex-backend-client = { workspace = true }
|
2026-01-28 19:51:58 -08:00
|
|
|
codex-chatgpt = { workspace = true }
|
client: extend custom CA handling across HTTPS and websocket clients (#14239)
## Stacked PRs
This work is now effectively split across two steps:
- #14178: add custom CA support for browser and device-code login flows,
docs, and hermetic subprocess tests
- #14239: extend that shared custom CA handling across Codex HTTPS
clients and secure websocket TLS
Note: #14240 was merged into this branch while it was stacked on top of
this PR. This PR now subsumes that websocket follow-up and should be
treated as the combined change.
Builds on top of #14178.
## Problem
Custom CA support landed first in the login path, but the real
requirement is broader. Codex constructs outbound TLS clients in
multiple places, and both HTTPS and secure websocket paths can fail
behind enterprise TLS interception if they do not honor
`CODEX_CA_CERTIFICATE` or `SSL_CERT_FILE` consistently.
This PR broadens the shared custom-CA logic beyond login and applies the
same policy to websocket TLS, so the enterprise-proxy story is no longer
split between “HTTPS works” and “websockets still fail”.
## What This Delivers
Custom CA support is no longer limited to login. Codex outbound HTTPS
clients and secure websocket connections can now honor the same
`CODEX_CA_CERTIFICATE` / `SSL_CERT_FILE` configuration, so enterprise
proxy/intercept setups work more consistently end-to-end.
For users and operators, nothing new needs to be configured beyond the
same CA env vars introduced in #14178. The change is that more of Codex
now respects them, including websocket-backed flows that were previously
still using default trust roots.
I also manually validated the proxy path locally with mitmproxy using:
`CODEX_CA_CERTIFICATE=~/.mitmproxy/mitmproxy-ca-cert.pem
HTTPS_PROXY=http://127.0.0.1:8080 just codex`
with mitmproxy installed via `brew install mitmproxy` and configured as
the macOS system proxy.
## Mental model
`codex-client` is now the owner of shared custom-CA policy for outbound
TLS client construction. Reqwest callers start from the builder
configuration they already need, then pass that builder through
`build_reqwest_client_with_custom_ca(...)`. Websocket callers ask the
same module for a rustls client config when a custom CA bundle is
configured.
The env precedence is the same everywhere:
- `CODEX_CA_CERTIFICATE` wins
- otherwise fall back to `SSL_CERT_FILE`
- otherwise use system roots
The helper is intentionally narrow. It loads every usable certificate
from the configured PEM bundle into the appropriate root store and
returns either a configured transport or a typed error that explains
what went wrong.
## Non-goals
This does not add handshake-level integration tests against a live TLS
endpoint. It does not validate that the configured bundle forms a
meaningful certificate chain. It also does not try to force every
transport in the repo through one abstraction; it extends the shared CA
policy across the reqwest and websocket paths that actually needed it.
## Tradeoffs
The main tradeoff is centralizing CA behavior in `codex-client` while
still leaving adoption up to call sites. That keeps the implementation
additive and reviewable, but it means the rule "outbound Codex TLS that
should honor enterprise roots must use the shared helper" is still
partly enforced socially rather than by types.
For websockets, the shared helper only builds an explicit rustls config
when a custom CA bundle is configured. When no override env var is set,
websocket callers still use their ordinary default connector path.
## Architecture
`codex-client::custom_ca` now owns CA bundle selection, PEM
normalization, mixed-section parsing, certificate extraction, typed
CA-loading errors, and optional rustls client-config construction for
websocket TLS.
The affected consumers now call into that shared helper directly rather
than carrying login-local CA behavior:
- backend-client
- cloud-tasks
- RMCP client paths that use `reqwest`
- TUI voice HTTP paths
- `codex-core` default reqwest client construction
- `codex-api` websocket clients for both responses and realtime
websocket connections
The subprocess CA probe, env-sensitive integration tests, and shared PEM
fixtures also live in `codex-client`, which is now the actual owner of
the behavior they exercise.
## Observability
The shared CA path logs:
- which environment variable selected the bundle
- which path was loaded
- how many certificates were accepted
- when `TRUSTED CERTIFICATE` labels were normalized
- when CRLs were ignored
- where client construction failed
Returned errors remain user-facing and include the relevant env var,
path, and remediation hint. That same error model now applies whether
the failure surfaced while building a reqwest client or websocket TLS
configuration.
## Tests
Pure unit tests in `codex-client` cover env precedence and PEM
normalization behavior. Real client construction remains in subprocess
tests so the suite can control process env and avoid the macOS seatbelt
panic path that motivated the hermetic test split.
The subprocess coverage verifies:
- `CODEX_CA_CERTIFICATE` precedence over `SSL_CERT_FILE`
- fallback to `SSL_CERT_FILE`
- single-cert and multi-cert bundles
- malformed and empty-file errors
- OpenSSL `TRUSTED CERTIFICATE` handling
- CRL tolerance for well-formed CRL sections
The websocket side is covered by the existing `codex-api` / `codex-core`
websocket test suites plus the manual mitmproxy validation above.
---------
Co-authored-by: Ivan Zakharchanka <3axap4eHko@gmail.com>
Co-authored-by: Codex <noreply@openai.com>
2026-03-12 17:59:26 -07:00
|
|
|
codex-client = { workspace = true }
|
2026-01-30 12:03:29 +00:00
|
|
|
codex-cloud-requirements = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
codex-core = { workspace = true }
|
2026-03-19 20:12:07 -07:00
|
|
|
codex-features = { workspace = true }
|
2025-11-06 10:44:42 -08:00
|
|
|
codex-feedback = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
codex-file-search = { workspace = true }
|
|
|
|
|
codex-login = { workspace = true }
|
2026-01-21 11:07:26 -08:00
|
|
|
codex-otel = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
codex-protocol = { workspace = true }
|
2026-02-20 23:45:35 -08:00
|
|
|
codex-shell-command = { workspace = true }
|
2026-01-29 10:23:03 +01:00
|
|
|
codex-state = { workspace = true }
|
2026-03-19 14:08:04 -07:00
|
|
|
codex-terminal-detection = { workspace = true }
|
2026-03-16 10:49:19 -06:00
|
|
|
codex-tui-app-server = { workspace = true }
|
2026-02-11 04:59:24 -08:00
|
|
|
codex-utils-approval-presets = { workspace = true }
|
2025-12-12 15:25:22 -08:00
|
|
|
codex-utils-absolute-path = { workspace = true }
|
2026-02-11 04:59:24 -08:00
|
|
|
codex-utils-cli = { workspace = true }
|
|
|
|
|
codex-utils-elapsed = { workspace = true }
|
|
|
|
|
codex-utils-fuzzy-match = { workspace = true }
|
|
|
|
|
codex-utils-oss = { workspace = true }
|
|
|
|
|
codex-utils-sandbox-summary = { workspace = true }
|
2026-02-13 18:31:39 +00:00
|
|
|
codex-utils-sleep-inhibitor = { workspace = true }
|
2026-02-26 10:29:54 +00:00
|
|
|
codex-utils-string = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
color-eyre = { workspace = true }
|
2025-11-06 10:44:42 -08:00
|
|
|
crossterm = { workspace = true, features = ["bracketed-paste", "event-stream"] }
|
2025-11-07 15:54:07 -08:00
|
|
|
derive_more = { workspace = true, features = ["is_variant"] }
|
2025-09-22 18:47:01 +02:00
|
|
|
diffy = { workspace = true }
|
2025-09-26 16:35:56 -07:00
|
|
|
dirs = { workspace = true }
|
2025-10-01 14:33:19 -07:00
|
|
|
dunce = { workspace = true }
|
2026-02-09 14:47:22 -08:00
|
|
|
image = { workspace = true, features = ["jpeg", "png", "gif", "webp"] }
|
2025-09-22 18:47:01 +02:00
|
|
|
itertools = { workspace = true }
|
|
|
|
|
lazy_static = { workspace = true }
|
2025-09-26 16:35:56 -07:00
|
|
|
pathdiff = { workspace = true }
|
|
|
|
|
pulldown-cmark = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
rand = { workspace = true }
|
|
|
|
|
ratatui = { workspace = true, features = [
|
2025-07-28 07:45:49 -07:00
|
|
|
"scrolling-regions",
|
2025-09-26 16:35:56 -07:00
|
|
|
"unstable-backend-writer",
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
"unstable-rendered-line-info",
|
2025-07-28 07:45:49 -07:00
|
|
|
"unstable-widget-ref",
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
] }
|
2025-10-20 14:40:14 -07:00
|
|
|
ratatui-macros = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
regex-lite = { workspace = true }
|
2026-02-23 14:15:18 -08:00
|
|
|
reqwest = { workspace = true, features = ["json", "multipart"] }
|
feat: replace custom mcp-types crate with equivalents from rmcp (#10349)
We started working with MCP in Codex before
https://crates.io/crates/rmcp was mature, so we had our own crate for
MCP types that was generated from the MCP schema:
https://github.com/openai/codex/blob/8b95d3e082376f4cb23e92641705a22afb28a9da/codex-rs/mcp-types/README.md
Now that `rmcp` is more mature, it makes more sense to use their MCP
types in Rust, as they handle details (like the `_meta` field) that our
custom version ignored. Though one advantage that our custom types had
is that our generated types implemented `JsonSchema` and `ts_rs::TS`,
whereas the types in `rmcp` do not. As such, part of the work of this PR
is leveraging the adapters between `rmcp` types and the serializable
types that are API for us (app server and MCP) introduced in #10356.
Note this PR results in a number of changes to
`codex-rs/app-server-protocol/schema`, which merit special attention
during review. We must ensure that these changes are still
backwards-compatible, which is possible because we have:
```diff
- export type CallToolResult = { content: Array<ContentBlock>, isError?: boolean, structuredContent?: JsonValue, };
+ export type CallToolResult = { content: Array<JsonValue>, structuredContent?: JsonValue, isError?: boolean, _meta?: JsonValue, };
```
so `ContentBlock` has been replaced with the more general `JsonValue`.
Note that `ContentBlock` was defined as:
```typescript
export type ContentBlock = TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource;
```
so the deletion of those individual variants should not be a cause of
great concern.
Similarly, we have the following change in
`codex-rs/app-server-protocol/schema/typescript/Tool.ts`:
```
- export type Tool = { annotations?: ToolAnnotations, description?: string, inputSchema: ToolInputSchema, name: string, outputSchema?: ToolOutputSchema, title?: string, };
+ export type Tool = { name: string, title?: string, description?: string, inputSchema: JsonValue, outputSchema?: JsonValue, annotations?: JsonValue, icons?: Array<JsonValue>, _meta?: JsonValue, };
```
so:
- `annotations?: ToolAnnotations` ➡️ `JsonValue`
- `inputSchema: ToolInputSchema` ➡️ `JsonValue`
- `outputSchema?: ToolOutputSchema` ➡️ `JsonValue`
and two new fields: `icons?: Array<JsonValue>, _meta?: JsonValue`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/10349).
* #10357
* __->__ #10349
* #10356
2026-02-02 17:41:55 -08:00
|
|
|
rmcp = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
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 }
|
2025-12-22 15:12:23 -08:00
|
|
|
thiserror = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
tokio = { workspace = true, features = [
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
"io-std",
|
|
|
|
|
"macros",
|
|
|
|
|
"process",
|
|
|
|
|
"rt-multi-thread",
|
|
|
|
|
"signal",
|
2025-12-03 18:00:47 -08:00
|
|
|
"test-util",
|
|
|
|
|
"time",
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
] }
|
2025-12-16 01:14:03 -08:00
|
|
|
tokio-stream = { workspace = true, features = ["sync"] }
|
2025-10-16 11:23:38 -07:00
|
|
|
toml = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
tracing = { workspace = true, features = ["log"] }
|
|
|
|
|
tracing-appender = { workspace = true }
|
|
|
|
|
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
feat(tui): syntax highlighting via syntect with theme picker (#11447)
## Summary
Adds syntax highlighting to the TUI for fenced code blocks in markdown
responses and file diffs, plus a `/theme` command with live preview and
persistent theme selection. Uses syntect (~250 grammars, 32 bundled
themes, ~1 MB binary cost) — the same engine behind `bat`, `delta`, and
`xi-editor`. Includes guardrails for large inputs, graceful fallback to
plain text, and SSH-aware clipboard integration for the `/copy` command.
<img width="1554" height="1014" alt="image"
src="https://github.com/user-attachments/assets/38737a79-8717-4715-b857-94cf1ba59b85"
/>
<img width="2354" height="1374" alt="image"
src="https://github.com/user-attachments/assets/25d30a00-c487-4af8-9cb6-63b0695a4be7"
/>
## Problem
Code blocks in the TUI (markdown responses and file diffs) render
without syntax highlighting, making it hard to scan code at a glance.
Users also have no way to pick a color theme that matches their terminal
aesthetic.
## Mental model
The highlighting system has three layers:
1. **Syntax engine** (`render::highlight`) -- a thin wrapper around
syntect + two-face. It owns a process-global `SyntaxSet` (~250 grammars)
and a `RwLock<Theme>` that can be swapped at runtime. All public entry
points accept `(code, lang)` and return ratatui `Span`/`Line` vectors or
`None` when the language is unrecognized or the input exceeds safety
guardrails.
2. **Rendering consumers** -- `markdown_render` feeds fenced code blocks
through the engine; `diff_render` highlights Add/Delete content as a
whole file and Update hunks per-hunk (preserving parser state across
hunk lines). Both callers fall back to plain unstyled text when the
engine returns `None`.
3. **Theme lifecycle** -- at startup the config's `tui.theme` is
resolved to a syntect `Theme` via `set_theme_override`. At runtime the
`/theme` picker calls `set_syntax_theme` to swap themes live; on cancel
it restores the snapshot taken at open. On confirm it persists `[tui]
theme = "..."` to config.toml.
## Non-goals
- Inline diff highlighting (word-level change detection within a line).
- Semantic / LSP-backed highlighting.
- Theme authoring tooling; users supply standard `.tmTheme` files.
## Tradeoffs
| Decision | Upside | Downside |
| ------------------------------------------------ |
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------
|
| syntect over tree-sitter / arborium | ~1 MB binary increase for ~250
grammars + 32 themes; battle-tested crate powering widely-used tools
(`bat`, `delta`, `xi-editor`). tree-sitter would add ~12 MB for 20-30
languages or ~35 MB for full coverage. | Regex-based; less structurally
accurate than tree-sitter for some languages (e.g. language injections
like JS-in-HTML). |
| Global `RwLock<Theme>` | Enables live `/theme` preview without
threading Theme through every call site | Lock contention risk
(mitigated: reads vastly outnumber writes, single UI thread) |
| Skip background / italic / underline from themes | Terminal BG
preserved, avoids ugly rendering on some themes | Themes that rely on
these properties lose fidelity |
| Guardrails: 512 KB / 10k lines | Prevents pathological stalls on huge
diffs or pastes | Very large files render without color |
## Architecture
```
config.toml ─[tui.theme]─> set_theme_override() ─> THEME (RwLock)
│
┌───────────────────────────────────────────┘
│
markdown_render ─── highlight_code_to_lines(code, lang) ─> Vec<Line>
diff_render ─── highlight_code_to_styled_spans(code, lang) ─> Option<Vec<Vec<Span>>>
│
│ (None ⇒ plain text fallback)
│
/theme picker ─── set_syntax_theme(theme) // live preview swap
─── current_syntax_theme() // snapshot for cancel
─── resolve_theme_by_name(name) // lookup by kebab-case
```
Key files:
- `tui/src/render/highlight.rs` -- engine, theme management, guardrails
- `tui/src/diff_render.rs` -- syntax-aware diff line wrapping
- `tui/src/theme_picker.rs` -- `/theme` command builder
- `tui/src/bottom_pane/list_selection_view.rs` -- side content panel,
callbacks
- `core/src/config/types.rs` -- `Tui::theme` field
- `core/src/config/edit.rs` -- `syntax_theme_edit()` helper
## Observability
- `tracing::warn` when a configured theme name cannot be resolved.
- `Config::startup_warnings` surfaces the same message as a TUI banner.
- `tracing::error` when persisting theme selection fails.
## Tests
- Unit tests in `highlight.rs`: language coverage, fallback behavior,
CRLF stripping, style conversion, guardrail enforcement, theme name
mapping exhaustiveness.
- Unit tests in `diff_render.rs`: snapshot gallery at multiple terminal
sizes (80x24, 94x35, 120x40), syntax-highlighted wrapping, large-diff
guardrail, rename-to-different-extension highlighting, parser state
preservation across hunk lines.
- Unit tests in `theme_picker.rs`: preview rendering (wide + narrow),
dim overlay on deletions, subtitle truncation, cancel-restore, fallback
for unavailable configured theme.
- Unit tests in `list_selection_view.rs`: side layout geometry, stacked
fallback, buffer clearing, cancel/selection-changed callbacks.
- Integration test in `lib.rs`: theme warning uses the final
(post-resume) config.
## Cargo Deny: Unmaintained Dependency Exceptions
This PR adds two `cargo deny` advisory exceptions for transitive
dependencies pulled in by `syntect v5.3.0`:
| Advisory | Crate | Status |
|----------|-------|--------|
| RUSTSEC-2024-0320 | `yaml-rust` | Unmaintained (maintainer
unreachable) |
| RUSTSEC-2025-0141 | `bincode` | Unmaintained (development ceased;
v1.3.3 considered complete) |
**Why this is safe in our usage:**
- Neither advisory describes a known security vulnerability. Both are
"unmaintained" notices only.
- `bincode` is used by syntect to deserialize pre-compiled syntax sets.
Again, these are **static vendored artifacts** baked into the binary at
build time. No user-supplied bincode data is ever deserialized. - Attack
surface is zero for both crates; exploitation would require a
supply-chain compromise of our own build artifacts.
- These exceptions can be removed when syntect migrates to `yaml-rust2`
and drops `bincode`, or when alternative crates are available upstream.
2026-02-22 01:26:58 -03:00
|
|
|
syntect = "5"
|
|
|
|
|
two-face = { version = "0.5", default-features = false, features = ["syntect-default-onig"] }
|
2025-09-22 18:47:01 +02:00
|
|
|
unicode-segmentation = { workspace = true }
|
|
|
|
|
unicode-width = { workspace = true }
|
|
|
|
|
url = { workspace = true }
|
2026-02-10 09:59:43 -08:00
|
|
|
webbrowser = { workspace = true }
|
2026-01-30 10:40:09 +00:00
|
|
|
uuid = { workspace = true }
|
2025-05-16 11:33:08 -07:00
|
|
|
|
2025-11-06 10:44:42 -08:00
|
|
|
codex-windows-sandbox = { workspace = true }
|
2025-12-02 15:19:27 -08:00
|
|
|
tokio-util = { workspace = true, features = ["time"] }
|
2025-11-06 10:44:42 -08:00
|
|
|
|
2026-02-23 14:15:18 -08:00
|
|
|
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
|
|
|
|
cpal = { version = "0.15", optional = true }
|
|
|
|
|
hound = { version = "3.5", optional = true }
|
|
|
|
|
|
2025-08-11 22:03:58 -07:00
|
|
|
[target.'cfg(unix)'.dependencies]
|
2025-09-22 18:47:01 +02:00
|
|
|
libc = { workspace = true }
|
2025-08-11 22:03:58 -07:00
|
|
|
|
2025-12-22 15:12:23 -08:00
|
|
|
[target.'cfg(windows)'.dependencies]
|
|
|
|
|
which = { workspace = true }
|
|
|
|
|
windows-sys = { version = "0.52", features = [
|
|
|
|
|
"Win32_Foundation",
|
|
|
|
|
"Win32_System_Console",
|
|
|
|
|
] }
|
|
|
|
|
winsplit = "0.1"
|
|
|
|
|
|
2025-09-03 15:36:40 +09:00
|
|
|
# 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]
|
2025-09-22 18:47:01 +02:00
|
|
|
arboard = { workspace = true }
|
2025-09-03 15:36:40 +09:00
|
|
|
|
2025-07-31 17:30:44 -07:00
|
|
|
|
2025-05-16 11:33:08 -07:00
|
|
|
[dev-dependencies]
|
2026-01-13 15:39:34 -08:00
|
|
|
codex-cli = { workspace = true }
|
2026-02-10 22:44:02 -08:00
|
|
|
codex-core = { workspace = true }
|
2026-01-13 15:39:34 -08:00
|
|
|
codex-utils-cargo-bin = { workspace = true }
|
|
|
|
|
codex-utils-pty = { workspace = true }
|
2025-10-05 14:12:31 -07:00
|
|
|
assert_matches = { workspace = true }
|
2025-09-22 18:47:01 +02:00
|
|
|
chrono = { workspace = true, features = ["serde"] }
|
|
|
|
|
insta = { workspace = true }
|
|
|
|
|
pretty_assertions = { workspace = true }
|
|
|
|
|
rand = { workspace = true }
|
2025-11-03 11:27:45 -08:00
|
|
|
serial_test = { workspace = true }
|
2025-11-06 10:44:42 -08:00
|
|
|
vt100 = { workspace = true }
|
2025-12-03 11:25:44 -08:00
|
|
|
uuid = { workspace = true }
|