Rename the 14 workspace member crates from `deepseek-*` (and
`deepseek-tui-*`) to `codewhale-*`. Internal-only — binary names
(`deepseek` and `deepseek-tui`) are intentionally untouched in this
phase; they move in the next phase along with the deprecation shims.
Affects:
- 14 `[package] name = "..."` declarations.
- All inter-crate `[dependencies]` entries that referenced the old
package names.
- All `use deepseek_*::...` statements rewritten to `use codewhale_*`.
- Cargo.lock regenerated.
CI workflows and release scripts that pass `-p deepseek-*` still
reference the old names; those move with the binary rename phase so
that pair lands together.
Local gates green: `cargo check --workspace --all-targets --locked`,
`cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets
--all-features --locked -- -D warnings`, `cargo test --workspace
--all-features --locked` (3226+ pass, 0 fail).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workspace, all 9 path-pinned crate deps, and the npm wrapper's
package.json all advance from 0.8.31 → 0.8.32. `scripts/release/
check-versions.sh` passes (workspace ↔ npm ↔ Cargo.lock all in
sync).
Auto-tag only fires on push-to-main, so this bump on `work/v0.8.32`
doesn't accidentally cut a release; it just makes the
in-development binary identify itself correctly. When this branch
merges to main, the existing release pipeline takes over from
here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- workspace.package.version: 0.8.29 → 0.8.30
- per-crate path-dependency version pins: 0.8.29 → 0.8.30
- npm/deepseek-tui: version + deepseekBinaryVersion → 0.8.30
- Cargo.lock refreshed via `cargo update --workspace --offline`
- CHANGELOG: `[Unreleased]` → `[0.8.30] - 2026-05-11` with the full
release-theme paragraph and the new "Changed" section for the
Alt+<key> unification
Verified with `./scripts/release/check-versions.sh`:
Version state OK: workspace=0.8.30, npm=0.8.30, lockfile in sync.
Workspace + per-crate path-dep version pins, npm wrapper, and
deepseekBinaryVersion all advance 0.8.28 -> 0.8.29.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Issue #1085 ("TUI viewport drifts down inside alt-screen at end of
turn, leaving top rows blank, esp. after sub-agents") was closed in
v0.8.18 by adding `reset_terminal_viewport()` to home the cursor on
TurnComplete / focus / resize. v0.8.27's flicker fix (`abf3fa66f`)
dropped the `\x1b[2J\x1b[3J` deep-clear from that path to stop the
double-clear flicker on Ghostty / VSCode / Win10 conhost. That left
ratatui's incremental-diff renderer relying on its internal model
matching reality — which only holds while nothing else writes to
the terminal.
Two latent `eprintln!` sites had been quietly emitting raw bytes
into the alt-screen for the entire v0.8.x cycle:
* `tools/subagent/mod.rs::persist_state_best_effort` (fires whenever
the per-step sub-agent state save hits an error; under parallel
sub-agents this can fire dozens of times per turn)
* `tools/subagent/mod.rs::new_shared_subagent_manager` (fires once
on init if the prior state file fails to load)
Plus a third found during this fix:
* `network_policy.rs::record` (fires every time a network-policy
audit write fails)
Each eprintln advanced the alt-screen cursor by one row and
scrolled the buffer up by one row, but ratatui's renderer didn't
know — it kept writing to absolute row positions, which now meant
"one row higher than visible." After ~30 leaks the TUI content
appeared to drift downward, with a blank band growing above the
header. v0.8.18's periodic full-clear had been masking it; v0.8.27's
flicker fix unmasked it.
Three layers of defence so this class of bug "isn't an option
anymore":
1. **`crates/tui/src/runtime_log.rs` — file-backed tracing
subscriber + Unix fd-level stderr redirect.** A daily-rolling log
file at `~/.deepseek/logs/tui-YYYY-MM-DD.log` is created at TUI
startup (right after `EnterAlternateScreen`). A
`tracing-subscriber` registry routes `tracing::warn!` /
`tracing::error!` calls to it. On Unix, the process's stderr fd
is `dup2`'d to the same file for the lifetime of the
`TuiLogGuard`. Any future raw `eprintln!` — ours, a panic
message, a third-party crate's verbose output — lands in the log
file instead of the alt-screen. The guard restores the original
stderr fd on drop so shutdown messages still reach the user's
terminal.
2. **`tracing::warn!` replacements** for the three known leak sites
(`subagent/mod.rs` ×2, `network_policy.rs` ×1). With (1) in
place these messages now go to the log file with structured
fields (`?err`, `host`, `tool`) instead of opaque text rows in
the alt-screen.
3. **Module-level
`#![deny(clippy::print_stdout, clippy::print_stderr)]`** on
`tools/`, `core/`, `tui/`, `runtime_threads.rs`, and
`network_policy.rs`. Any future `eprintln!` / `println!` added
to a TUI runtime path fails the lint at compile time.
Legitimate CLI-print paths (`main.rs` eval / init / doctor,
`runtime_api.rs` server banners, `logging.rs` verbose helpers,
`skills/mod.rs` listing utilities, `execpolicy/execpolicycheck.rs`
JSON output, `ui::run_event_loop` post-`LeaveAlternateScreen`
resume hint, two `#[test] #[ignore]` perf benches in
`tui/transcript.rs` / `tui/widgets/mod.rs` / `core/capacity.rs`)
keep their existing prints — they all run outside the alt-screen
lifetime.
The dup2 redirect is Unix-only because there's no equivalent stable
Rust API for fd-redirecting `STDERR_FILENO` on Windows; on Windows
the tracing-subscriber layer + the clippy denies still apply, and
ratatui's own use of crossterm avoids the worst leakage classes.
Cross-platform stderr redirect via `SetStdHandle` is a follow-up.
The new `runtime_log` module ships with one test
(`log_directory_prefers_home`) that pins the `HOME` /
`USERPROFILE` / `dirs::home_dir()` resolution order — uses the
process-wide `test_support::lock_test_env()` lock for env-mutation
safety. Two `#[test] #[ignore]` benches in
`tui/transcript.rs` (rail-prefix memory) and `tui/widgets/mod.rs`
(transcript scroll bench) and one in `core/capacity.rs`
(`bench_compute_profile`) keep their stdout prints via
`#[allow(clippy::print_stdout)]` on the individual test.
New dependencies: `tracing-subscriber 0.3` (env-filter + fmt
features) and `tracing-appender 0.2` at the workspace root, both
pulled into `crates/tui` only.
Closes the v0.8.28 regression Hunter reported in screenshots:
parallel sub-agents running `exec_shell` triggered the scroll
demon with the TUI content squeezed into the bottom third of the
terminal and ~30 rows of blank above the header.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workspace + per-crate path-dep version pins, npm wrapper, and
`deepseekBinaryVersion` all advance from 0.8.27 → 0.8.28. Lockfile
refreshed via `cargo update --workspace --offline`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two responsibly-disclosed security fixes:
- GHSA-88gh-2526-gfrr (@JafarAkhondali)
- GHSA-72w5-pf8h-xfp4 (@47Cid)
Plus version bump, CHANGELOG, regression tests for both.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bump workspace version 0.8.22 → 0.8.23 across Cargo.toml, every per-crate
path-dependency pin, npm/deepseek-tui/package.json (both `version` and
`deepseekBinaryVersion`), and Cargo.lock.
- Add a 0.8.23 CHANGELOG entry covering the security hardening stack
(sanitized child env, plan-mode tool surface, sub-agent approvals,
symlink walks, runtime API auth, shell safety classification, MCP
config path traversal), the macOS Keychain prompt fix, the #1244 MCP
spawn error visibility + env passthrough work, the compact-thinking UX
change, and a Known issues callout for mid-run MCP stderr.
- Backfill missing CHANGELOG entries for v0.8.21 (community-heavy
release, contributors credited) and v0.8.22 (fetch_url redirect
validation). The gap was unintentional, so contributor work is being
reflected in-repo now.
- Add docs/RELEASE_CHECKLIST.md so future releases gate on the
CHANGELOG/version/preflight steps explicitly.
* fix(config): keep DeepSeek beta endpoint for legacy cn alias
* fix(ci): filter download-artifact to deepseek* pattern
Prevents the release aggregation job from picking up non-binary
artifacts (e.g. Docker .dockerbuild cache layers) that cause the
checksum manifest to include spurious entries and the Release to
carry files it shouldn't.
* fix(tui): enable focus events to restore IME after app-switch
On macOS, switching away (Cmd+Tab) and back suspends the IME compositor.
Without focus-event handling, the TUI never signals readiness to the
terminal, so CJK input methods (Pinyin, Zhuyin, etc.) stop working.
- EnableFocusChange on startup so the terminal reports FocusGained/FocusLost
- Re-push KeyboardEnhancementFlags on FocusGained (some terminals reset
the enhanced keyboard mode on focus-loss)
- DisableFocusChange on shutdown for clean terminal handoff
* chore: cargo fmt
* docs: add DataWhale and DeepSeek to acknowledgments
* docs: fix DeepSeek name etymology in acknowledgments
* fix(tui): recapture viewport on focus restore
* docs: thank DeepSeek and DataWhale bilingually
A community-driven reliability release. Plan-mode safety, paste-Enter
auto-submit, slash-menu skills coverage, the deepseek-cn endpoint
preset, and a handful of platform / streaming / gateway-compat fixes,
plus a small PTY-driven QA harness.
See CHANGELOG.md for the full annotated change list with credits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DeepSeek API returns gzip/brotli compressed SSE responses. Without
auto-decompression enabled, reqwest's rustls backend passed compressed
bytes directly to UTF-8 parsing, causing garbled output () for Chinese
thinking content.
Add "gzip" and "brotli" features to reqwest to enable automatic content
encoding decompression (Accept-Encoding: gzip, br).
Fixes#954
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two pieces:
**#1073 fix.** When a paste burst is currently being assembled, or when
the burst's Enter-suppression window is still open after a flush, the
trailing newline of the paste was firing `submit_input()` and the
in-flight burst buffer was getting destroyed by `clear_after_explicit_paste()`.
The PasteBurst module already exposed `newline_should_insert_instead_of_submit`
and `append_newline_if_active` for exactly this case, but no caller had
been wired up. Added `App::handle_composer_enter`, which checks the
suppression state and either appends `\n` to the burst buffer or inserts
it directly into the composer text — no submit. The `KeyCode::Enter`
arm in the composer event loop now dispatches through that helper.
Reproduces the Windows/PowerShell symptom from the report:
multi-line paste ending with `\n` no longer auto-submits AND the text
no longer leaks into the now-empty composer.
Four unit tests cover: active-burst Enter, post-flush window Enter,
normal Enter outside the window, and Enter with paste-burst detection
disabled (suppression must be off).
**PTY QA harness.** New `crates/tui/tests/support/qa_harness/` wraps
`portable-pty` (already a runtime dep) and `vt100` (new dev-dep) into
a small surface for scenarios that need a real PTY: spawn a binary,
send keys/paste/resize, parse the ANSI stream into a frame, assert
on visible text + filesystem state. The harness seals `$HOME` so
scenarios cannot read the developer's real `~/.deepseek/` and points
the base URL at 127.0.0.1:1 so no live request escapes. README under
`support/qa_harness/README.md` documents how to add a scenario.
Initial scenarios in `crates/tui/tests/qa_pty.rs`: smoke boot,
keystroke round-trip, and bracketed/unbracketed paste-with-trailing-
newline regression guards for #1073. The unbracketed scenario does
not deterministically reproduce the bug on macOS (single-syscall
PTY writes keep the burst continuously active), but the unit tests
above cover the path conclusively; the PTY test stands as a
regression guard for the visible-text invariant.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Integrates #879 safe lockfile dependency updates.
Regenerated the dependency bumps on current main so workspace crates stay on v0.8.15. The Dockerfile edits from #879 were intentionally left out because they pin stale Debian package versions and add an invalid trailing `MD []` instruction.
Co-authored-by: RinZ27 <222222878+RinZ27@users.noreply.github.com>
Final step in the v0.8.11 patch release. Bumps the workspace
`Cargo.toml`, all 9 internal path-dep version pins, and
`npm/deepseek-tui/package.json` to **0.8.11**. `Cargo.lock`
regenerated alongside.
The v0.8.11 CHANGELOG entry already landed on `main` via the
cache-maxing overhaul PR (#684). This commit only stamps the
version. Together they ship:
* **Cache-maxing for V4 1M context** — engine no longer rebuilds the
system prompt on every turn (#684's `Session::last_system_prompt_hash`),
the volatile working-set summary moved out of the system prompt
into per-turn `<turn_meta>` on the latest user message, the tool
array is anchored with `cache_control: ephemeral`, and the
`messages_with_turn_metadata` injection skips tool-result
messages so the assistant→tool_result invariant stays intact.
* **500K compaction floor** — automatic compaction refuses below
500K tokens via `MINIMUM_AUTO_COMPACTION_TOKENS`. Manual
`/compact` bypasses (explicit user agency).
* **Token-only compaction trigger** — dropped
`CompactionConfig::message_threshold` and the message-count
branch in `should_compact`; that 128K-era heuristic only fired
on long sessions of small messages, exactly the case where
rewriting the V4 prefix cache is most wasteful.
* **Legacy 128K naming** — `DEFAULT_CONTEXT_WINDOW_TOKENS` →
`LEGACY_DEEPSEEK_CONTEXT_WINDOW_TOKENS`.
* **`npm install` resilience** — `install.js` now retries with
exponential backoff, enforces per-attempt timeout + 30 s stall
detector, honors `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` (pure
Node, no new dependencies), and prints download progress to
stderr. Driven by a community report that `npm install` took 18
minutes through a CN npm mirror; the GitHub Releases binary
fetch was the bottleneck and CN mirrors don't proxy GitHub.
Verified locally:
* cargo fmt --all -- --check ✓
* cargo clippy --workspace --all-targets --all-features
--locked -- -D warnings ✓
* cargo test --workspace --all-features --locked ✓
* parity gates (snapshot, parity_protocol, parity_state) ✓
* bash scripts/release/check-versions.sh ✓
(workspace=0.8.11, npm=0.8.11, lockfile in sync)
* node scripts/release/npm-wrapper-smoke.js ✓
Reminder for the maintainer at release time: the npm publish is
manual and requires 2FA OTP on every publish. After this PR
merges and the GitHub Release is fully drafted by `auto-tag.yml`,
publish from a developer machine:
cd npm/deepseek-tui
npm publish --access public
The `prepublishOnly` hook checks all eight binaries plus the
SHA256 manifest are present on the GitHub Release before letting
`npm publish` proceed, so this must happen *after* the GitHub
Release is finalized.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The aarch64-unknown-linux-gnu release build for `deepseek-tui` failed in
release.yml run 25327475634 with:
openssl-sys v0.9.111: 'openssl/opensslconf.h' file not found
`crates/tui/src/main.rs` was the only crate in the workspace pulling
`reqwest` with `default-features = false, features = ["native-tls", ...]`
— every other crate (including the dispatcher in `crates/cli`) already
inherits the workspace default `["json", "rustls"]`. The aarch64 leg
builds with `cargo zigbuild --target aarch64-unknown-linux-gnu.2.28`,
whose zig sysroot does not ship openssl headers; the matching native-tls
job for v0.8.9 succeeded by chance against an earlier runner image but
the current `ubuntu-24.04-arm` image no longer satisfies openssl-sys's
header probe under zigbuild.
Switching the TUI's reqwest features from `native-tls` to `rustls` brings
it in line with the rest of the workspace and removes nine crates from
the build graph entirely (`openssl`, `openssl-sys`, `openssl-probe`,
`openssl-macros`, `native-tls`, `hyper-tls`, `tokio-native-tls`,
`foreign-types`, `foreign-types-shared`). reqwest 0.13.1 already uses
`rustls-platform-verifier` for OS trust-store integration, so end-user
TLS behavior against api.deepseek.com remains equivalent.
Verified locally:
- cargo clippy --workspace --all-targets --all-features --locked passes
- cargo build --release -p deepseek-tui --locked succeeds
- cargo fmt --all -- --check is clean
- no source code in `crates/` references native-tls / openssl directly
This is a release-pipeline-only fix; no user-visible feature changes.
- Workspace `version = "0.8.8"` in root `Cargo.toml`.
- 31 internal `deepseek-*` path-dep version pins across the
9 crates that declare them.
- `npm/deepseek-tui/package.json` `version` and
`deepseekBinaryVersion` both updated.
- `Cargo.lock` regenerated for the new workspace version.
- `CHANGELOG.md` `[Unreleased]` heading promoted to
`[0.8.8] - 2026-05-03`.
`scripts/release/check-versions.sh` reports the workspace, npm
wrapper, and lockfile all aligned. Pushing this to `main` should
fire `auto-tag.yml`, which creates the `v0.8.8` tag with
`RELEASE_TAG_PAT`. The tag triggers `release.yml` to build the
matrix and draft the GitHub Release. The npm wrapper publish
remains manual (npm 2FA OTP requirement).
What ships in v0.8.8
====================
The full polish stack already merged via PRs #514 (stabilization),
#515 (OSC 8 hyperlinks), #517 (inline diff render), #518 (user
memory MVP), #519 (foreground polish + per-project overlay +
security + Windows redraw fix), and #508 (Linux ARM64 prebuilts +
install docs). See `CHANGELOG.md` and the README "What's new in
v0.8.8" section for the full list.
Resolves the post-#514/#517/#518 conflicts:
- CHANGELOG.md: kept both polish-stack and Linux ARM64 entries under
[Unreleased]; reordered so the ARM64/install-message Changed/Docs
sections precede the Releases footer.
- config.example.toml: kept both the `instructions = [...]` example
and the `[memory]` opt-in stanza in sequence.
- crates/tui/src/config.rs: kept both `instructions_paths()` (#454)
and `memory_enabled()` (#489) on the Config impl.
- crates/tui/src/prompts.rs: extended
`system_prompt_for_mode_with_context_and_skills` to take BOTH
`instructions: Option<&[PathBuf]>` and `user_memory_block:
Option<&str>`. Section 2.5a renders instructions; 2.5b renders the
memory block — both above the skills block so KV prefix caching
still wins.
- crates/tui/src/core/engine.rs: thread both args through the two
call sites.
- crates/tui/src/prompts.rs: update the `system_prompt_for_mode_with_context`
forwarder and the test caller to pass `None` for the new arg.
- .gitignore: ignore `.claude/*.local.md` and `*.local.json` so
local ralph / Claude-Code notes can't leak into commits.
Folds in two valid suggestions from the gemini-code-assist review on #519:
- `client.rs`: collapse the duplicated `LlmError → label` match and the
`human_retry_reason` body into a single
`retry_reason_label_and_human(err) -> (&'static str, String)` helper.
- `widgets/footer.rs::retry_banner_spans`: merge the two separate
`match &props.retry` blocks into one that returns both `(label, color)`.
Behavior is unchanged; refactor is a pure DRY win.
`crates/tui/src/ui.rs` exposed two `#[allow(dead_code)]` helpers
(`spinner`, `progress_bar`) that nothing in the workspace called.
The `indicatif` dep was only there to back those helpers. Delete
the module file, remove `mod ui;` from `main.rs`, and drop
`indicatif` from the TUI crate's Cargo.toml.
Cargo.lock loses 4 crates (`indicatif`, `console`, `encode_unicode`,
`unit-prefix`), trimming compile time and binary size. Note that the
real TUI rendering module lives at `crates/tui/src/tui/ui.rs` and is
unaffected — the deleted file was a separate module that hadn't
been wired into anything.
`edit_file` and `write_file` now capture the file contents before and
after the mutation, generate a unified diff with `similar`, and emit it
at the head of the `ToolResult` body. The TUI's existing
`output_looks_like_diff` detector (history.rs:1335) sees the `@@`
header in the first 5 lines and routes the payload through
`diff_render::render_diff`, which already renders unified diffs with
line numbers and coloured `+`/`-` gutters.
The model also benefits — it sees exactly which lines changed instead
of just `Replaced N occurrence(s)` or `Wrote N bytes`. Identical
content produces an empty diff, in which case the body falls back to
`<summary>\n(no changes)`.
### What's wired
- New `crates/tui/src/tools/diff_format.rs` exposes
`make_unified_diff(path, old, new) -> String` using
`similar::TextDiff::from_lines(...).unified_diff().context_radius(3)`.
- `WriteFileTool::execute` snapshots prior contents (or empty for new
files), writes, then emits `<diff>\n<summary>` where summary is
`Wrote N bytes to PATH` for existing files and
`Created PATH (N bytes)` for new ones.
- `EditFileTool::execute` snapshots, replaces, writes, emits
`<diff>\nReplaced N occurrence(s) in PATH`.
- `similar = "2"` added to `crates/tui/Cargo.toml`. Pure-Rust, no
C deps; v2.7.0 in Cargo.lock.
### Tests
- 4 unit tests in `diff_format::tests` covering identical inputs,
replacement, new-file (against empty), and presence of the `@@`
header in the first 5 lines (so the TUI detector trips).
- Existing `test_write_file_tool` / `test_edit_file_tool` updated to
assert both the summary line and the unified-diff body
(`--- a/`, `-old`, `+new`).
### Verification
cargo fmt --all -- --check ✓
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings ✓
cargo test --workspace --all-features --locked ✓ (1824 + supporting; was 1820)
Closes#505
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merge the v0.8.6 feature batch and release hardening.\n\nIncludes the full #373-#380/#382-#402 milestone scope, version bump to 0.8.6, secure /share temp-file handling, Windows-safe self-update replacement, and CI portability fixes.\n\nRemote PR checks passed on the final head before merge.
* feat: add config UI support for TUI and web modes
- Introduced a new `config_ui.rs` module to handle configuration UI for TUI and web.
- Updated `TuiOptions` and `App` structures to include `config_path` and `config_profile`.
- Implemented functions to build and apply configuration documents.
- Added tests to ensure the new configuration UI behaves as expected.
- Integrated web configuration session handling into the event loop.
- Updated various modules to accommodate the new configuration options and UI.
* refactor(tui): remove local path reference for schemaui dependency
Remove the local file system path reference for schemaui in favor of
using the published crate from the registry. This change updates the
Cargo.toml to use only the version specification and adds the source
and checksum information to Cargo.lock.
* fix: add AGENTS.md guide and improve config error handling
- Add comprehensive AGENTS.md file with project instructions for AI
assistants, including build commands, dependencies, and GitHub
operations guidance
- Introduce is_error field to CommandResult struct for better error
tracking
- Refactor config application logic to properly handle errors using
the new is_error flag
- Add test utilities for WebConfigSession to support testing
- Optimize web config event polling by extracting drain logic into
separate function
- Add unit tests for session-only config application and engine sync
requirements
* fix(security): add SSRF protection to fetch_url (#261)
Block private, link-local, and cloud metadata IPs in fetch_url HTTP requests. Co-authored-by: JasonOA888
* test(portability): inject paths instead of mutating HOME (Windows fix)
CI's `Test (windows-latest)` job failed because both my new tests
(composer_history and the spawn_supervised crash-dump test) mutated
HOME to redirect `dirs::home_dir()`. That works on macOS / Linux but
not on Windows, where dirs::home_dir() reads USERPROFILE / queries
SHGetKnownFolderPath rather than HOME.
Fix: refactor both modules to expose path-injecting helpers so tests
never need to touch the env var:
- composer_history: split load_history / append_history into thin
wrappers around load_history_from(&Path) / append_history_to(&Path).
Tests use the *_to / *_from form with a tempdir path.
- utils::write_panic_dump: same pattern — write_panic_dump_to(&Path)
takes the crash dir directly. The spawn_supervised end-to-end test
splits into two: one verifies panic-doesn't-propagate (no on-disk
side effect needed), one verifies write_panic_dump_to writes the
expected log format.
Production callers continue to use the env-driven default (`HOME`/
`USERPROFILE` via `dirs::home_dir()`) so no behavior change. Tests
work identically on every platform now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tui): clear chat area each frame so stale cells don't bleed into sidebar
ChatWidget's render path was `Paragraph::new(lines).render(content_area, buf)`
with no Block and no Clear — ratatui's Paragraph only writes cells that
contain text, leaving any cell the current frame's paragraph doesn't
touch holding the *previous* frame's contents. With wide tool output
(`gh pr list`, `git log`) emitting ISO-8601 timestamps like
`2026-05-02T07:29:24Z`, then a subsequent shorter-paragraph frame, the
old timestamp tails (`:24Z`, `7:29:24Z`, etc.) persisted on the right
edge of the chat area, visually colliding with the section headers in
the sidebar (`Plan` rendering as `:24Zan`, `Agents` as `:24Zents`).
Fix: render `Clear` over the full content_area before drawing the
Paragraph. Cheap (one buffer-fill per frame) and guarantees stale cells
can never persist into the next frame's render.
Reported in v0.8.5 testing right after install. The other v0.8.5
bordered widgets (composer, sidebar sections, footer) already render
into a Block with a solid background style, so they were never
affected — only the chat area used a bare Paragraph.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(theme): vendor + theme schemaui to deepseek navy palette (config UI)
The schemaui-0.12.0 crate the contributor brought in via #365 ships
hardcoded Color::Gray / Color::DarkGray / Color::White / Color::Yellow
references across its rendering components. Visually it clashed with
the rest of deepseek-tui — the editor area read as gray-on-black on a
TUI that's otherwise navy ink + sky accents. Two ship-day options
weren't acceptable: defaulting back to the legacy modal lost the new
editor's UX, and living with gray was off-brand.
This commit forks schemaui at 0.12.0 into vendor/schemaui-0.12.0 and
themes the rendering layer to match deepseek-tui's palette. The patch
is wired in via a workspace-level [patch.crates-io] override so the
deepseek-tui Cargo.toml continues to depend on `schemaui = "0.12.0"`
and would automatically resolve back to crates.io if we ever drop the
override (e.g. once upstream lands a ColorTheme API).
Changes inside the vendored fork:
- New `src/deepseek_palette.rs` with the brand RGB values:
SURFACE_INK / SURFACE_RAISED for backgrounds, BORDER_DIM /
BORDER_ACTIVE for chrome, TEXT_PRIMARY / TEXT_MUTED / TEXT_DIM,
ACCENT_SKY / ACCENT_BLUE / ACCENT_PURPLE, and STATUS_OK / WARN /
ERROR. Values mirror crates/tui/src/palette.rs in the workspace.
- `src/lib.rs` exposes the palette module under `cfg(feature = "tui")`.
- `src/tui/view/frame.rs::draw` paints a navy backdrop across the
full frame area before any child widget renders, so any cell that
doesn't get explicitly written reads as ink instead of the terminal
default.
- `tabstrip.rs`, `overlay.rs`, `popup.rs`, `body.rs`, `sections.rs`,
`footer.rs`, `help.rs`, `fields.rs`: every Color::Gray / DarkGray /
White / Yellow / Cyan / Blue / Magenta / Red / Green / LightBlue
swapped out for a deepseek_palette token, plus explicit `bg(...)`
fills on the top-level Block styles and Paragraph wrappers.
- `Cargo.toml` adds an empty `[workspace]` so the vendored crate
builds standalone (its dev-deps don't drift into ours).
Workspace-level changes:
- `Cargo.toml` adds `[patch.crates-io] schemaui = { path =
"vendor/schemaui-0.12.0" }`. Production deepseek-tui builds pick up
the themed fork transparently.
- `.gitignore` excludes `vendor/.../web/ui/node_modules/` (15 MB of
npm artefacts the Rust build doesn't need) and the vendored
Cargo.lock (regenerated locally per build).
Verification:
- cargo build --workspace --all-features: clean
- cargo clippy --workspace --all-targets --all-features --locked: clean
- cargo test --workspace: 1777 passed, 0 failed
- /config inside `deepseek` now opens a navy-themed editor matching
the rest of the TUI; tabs, body panel, footer, popup, and help
overlay all read on brand.
Future work tracked separately: upstream a `with_theme(ColorTheme)`
builder API to schemaui so we can drop the fork. Until then, sync the
fork against new schemaui releases when we want their fixes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "feat(theme): vendor + theme schemaui to deepseek navy palette"
This reverts ed597ccc — vendoring 28,913 lines of schemaui to recolor
a config editor was the wrong tradeoff. Maintenance cost for a
cosmetic match wasn't worth it, and the recolor wasn't even fully
working (terminal-default bg kept bleeding through Style::default()
calls in the form fields).
The simpler path: keep the schemaui-driven editor available as
`/config tui` for users who want the form-style UX, but make bare
`/config` open the legacy native modal that already matches the
deepseek-tui navy chrome by inheritance. No fork, no vendored copy,
no ongoing sync burden.
Changes:
- `git rm -r vendor/schemaui-0.12.0/` (28,913 lines gone)
- Drop `[patch.crates-io]` from workspace Cargo.toml — schemaui
resolves back to crates.io v0.12.0 unmodified.
- Drop the corresponding `.gitignore` exclusions (no more vendor dir
to filter).
- `config_ui::parse_mode` default mode flipped from `Tui` to `Native`.
Bare `/config` → legacy navy modal. Explicit `/config tui` → the
contributor's schemaui editor (still available, gray-on-default
chrome, but opt-in). `/config web` and `/config <key>` /
`/config <key> <value>` unchanged.
- Help text updated to list `[native|tui|web]` in that order.
Verified: cargo build / clippy --workspace --all-features --locked
with -D warnings: clean.
The contributor's work (#365) ships and gets credit; users discover
the alternate editor via the help text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tui): paint chat area with explicit navy ink instead of Clear
The Clear-instead-of-fill in 0ae2cead reset cells to the terminal's
default background, which read as a brown-gray on most user setups
even though the rest of the TUI chrome is navy. Replace the Clear
with an explicit Block fill at palette::DEEPSEEK_INK, and pass the
same bg through to the Paragraph itself so streamed text cells
inherit ink rather than bouncing back to terminal default.
Net effect: the chat area visually unifies with the sidebar /
composer / footer instead of showing as a contrasting brown-gray
panel in the middle of an otherwise navy frame.
Stale-cell guarantee from #372-followup is preserved — the Block
fills every cell in the area on each frame, so wide tool output
(`gh pr list` ISO timestamps, etc.) still can't bleed past the
current frame's actual text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(config): update tests for Native default + fix default_model override in session-only apply
- Update test_show_config_defaults_to_native and
execute_config_opens_config_view_action to expect
OpenConfigView (Native) instead of OpenConfigEditor(Tui),
matching the parse_mode default change from ce98f054.
- Fix apply_document bug where default_model was processed
in the main key-value loop after model, causing
set_config_value('default_model') to overwrite the
runtime model. default_model is now only applied when
persist=true, preventing session-only edits from being
silently reverted.
* style: cargo fmt
* chore: remove end-of-night report (session artifact)
---------
Co-authored-by: unic <yuniqueunic@gmail.com>
Co-authored-by: Jason <jason@aveoresearchlabs.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: YuniqueUnic <YuniqueUnic@users.noreply.github.com>