Skip to content

experimental: ratatui → frankentui migration (Phase 1+2, 1/8 crates done)#312

Open
quangdang46 wants to merge 6 commits into
masterfrom
experimental/ratatui-to-frankentui
Open

experimental: ratatui → frankentui migration (Phase 1+2, 1/8 crates done)#312
quangdang46 wants to merge 6 commits into
masterfrom
experimental/ratatui-to-frankentui

Conversation

@quangdang46
Copy link
Copy Markdown
Owner

Summary

Experimental, multi-phase migration from ratatui to frankentui (ftui-style/ftui-render/etc.). Pinned against quangdang46/frankentui@33ad1c57. This PR is incremental — it lands the work that can ship green now, and documents exactly what each remaining crate needs.

Crate count: 1 of 8 ratatui-dependent crates is now ratatui-free.

What's in this PR

Phase 1 (commit 41f79c72)

  • Added ftui-style as an optional git dep in jcode-tui-usage-overlay, gated by a frankentui cargo feature.
  • Refactored UsageOverlayStatus to make pub const fn rgb() -> (u8,u8,u8) the single source of truth; both ratatui-shaped color() and the new color_ftui() delegate to it.
  • Lockfile bumps inside existing semver bounds: unicode-width 0.2.0→0.2.2, bitflags 2.10.0→2.11.1, bumpalo 3.19.1→3.20.3.

Phase 2 (commit 67fc2c35)

  • Dropped ratatui from jcode-tui-usage-overlay entirely. Its Cargo.toml no longer mentions ratatui.
  • Public API now returns ftui_style::Color only.
  • New shared shim src/tui/ftui_compat.rs exports ftui_color_to_rata(c: ftui_style::Color) -> ratatui::style::Color. It maps every variant of ftui_style::Color (Rgb, Ansi256, Ansi16, Mono) onto the closest ratatui Color. 5 unit tests cover the mapping.
  • src/tui/usage_overlay.rs (the consumer) wraps the conversion via a private status_color_rata() helper at the three Style::fg boundary sites.
  • Top-level jcode/Cargo.toml gains the ftui-style git dep.

Docs (commit 344493ad)

  • docs/RATATUI_MIGRATION_FEASIBILITY.md updated with Phase 2 results and a precise crate-by-crate blocker analysis (see below).

Verification

$ cargo test -p jcode-tui-usage-overlay   # 4/4 pass
$ cargo check -p jcode --lib              # clean (only pre-existing dead-code warnings)

Remaining work — honest crate-by-crate status

Crate Status Blocking issue
jcode-tui-usage-overlay ✅ ratatui-free none
jcode-tui-style ⏳ deferred 502 call sites in 50 consumer files; ratatui's Style::fg takes Color directly (no Into) and the orphan rule blocks From impl from a third crate
jcode-tui-workspace ⏳ deferred render_workspace_map(buf: &mut ratatui::Buffer, …) is a ratatui-Buffer-based widget; can't migrate without rendering layer first
jcode-tui-render ⏳ deferred Buffer cell layout swap (16-byte aligned, grapheme-pool ID) is the structural hard step
jcode-tui-messages ⏳ deferred widget-heavy on ratatui Buffer/Span/Line
jcode-tui-markdown 🚫 hard-blocked upstream frankentui's own docs/migration-map.md defers markdown rendering to Phase 5 / ftui-extras; not yet implemented
jcode-tui-mermaid 🚫 hard-blocked upstream frankentui has no ratatui-image equivalent (also Phase 5 deferred)
jcode (top-level) ⏳ deferred ~52 Terminal::new + 51 TestBackend::new + widget consumers across 80+ files

Why this PR doesn't finish the migration

Two distinct reasons:

  1. Type-system rippling. ratatui 0.30's Style::fg is pub const fn fg(self, color: Color) -> Self (takes Color exactly, not impl Into<Color>). The orphan rule blocks an impl From<ftui_style::Color> for ratatui::style::Color from a third crate. So migrating any leaf crate that returns Color ripples into hundreds of explicit conversion calls at every Style::fg/bg site. jcode-tui-style alone has 502 such sites in 50 files.

  2. Hard upstream blockers. Frankentui's own docs/migration-map.md explicitly lists markdown rendering and image protocols as Phase 5 / not implemented. jcode-tui-markdown and jcode-tui-mermaid therefore have no migration target until frankentui ships those features (or jcode forks its own equivalents).

Path forward (next PRs in this series)

  • PR2: introduce a newtype + extension trait pattern in src/tui/ftui_compat.rs so a bulk pattern_rewrite can mechanically transform 500+ call sites with safe local edits. Then migrate jcode-tui-style.
  • PR3: structural rewrite of jcode-tui-render — swap ratatui::Buffer for ftui_render::Buffer/Frame. This is the first hard step where downstream signatures change.
  • PR4–6: widget-heavy crates (jcode-tui-workspace, jcode-tui-messages, plus the top-level src/tui/* widget consumers).
  • PR7: Terminal lifecycle migration (Terminal::new / ratatui::init / restoreTerminalSession).
  • PR8: test backend migration (TestBackend::new × 51 → ftui-harness).
  • Out of scope until upstream lands: jcode-tui-markdown, jcode-tui-mermaid.

The original 2–4 week estimate at the top of the feasibility doc still stands for the remaining 5 deferred crates.

quangdang46 and others added 6 commits May 25, 2026 08:19
Investigation results on experimental branch.

Verdict: NOT recommended as 1:1 migration. Frankentui's authors
explicitly state 'no backwards compatibility, no upgrade path'
and the type shapes differ enough that every Widget impl,
Color usage, and Buffer interaction needs rewrite.

Findings:
- jcode has 275 ratatui::* references across 66 files / 9 crates
- Color enum nesting differs: Color::Red → Color::Ansi16(Ansi16::Red)
- Widget::render signature: takes &Frame instead of &mut Buffer
- Buffer cell layout: 16-byte aligned, grapheme pool ID, no string symbols
- Test backend pattern is incompatible: 51 sites use ratatui::TestBackend
- ratatui-image (used by mermaid renderer) has no frankentui equivalent

Surprise: ftui-style compiles on stable Rust despite nightly toolchain
file, though deeper subcrates may still need nightly.

Realistic migration estimate: 2-4 weeks dev + 1-2 weeks UI regression
hunt. ROI unclear without measured perf bottleneck pointing at ratatui.

Recommendation: skip migration. ratatui is mature + stable; frankentui
is WIP 0.4.0 + 850K LOC dependency surface increase. Higher-ROI
alternatives exist (port more upstream PRs, ship deferred features).

If forced to prototype, start with jcode-tui-usage-overlay (1 ratatui
ref, 134 lines) behind a feature flag — 1-2 days throwaway.
Phase 1 of the ratatui → frankentui experimental migration. The smallest
ratatui consumer in the workspace (jcode-tui-usage-overlay, 134 LOC, 1
ratatui ref) now exposes a parallel frankentui-shaped color API behind
the new `frankentui` cargo feature, without removing the existing
ratatui-shaped API that downstream callers already rely on.

Design:
- Introduce `pub const fn rgb(self) -> (u8, u8, u8)` as the single source
  of truth for status colors.
- `color() -> ratatui::style::Color` (existing API, untouched return type)
  delegates to `rgb()`.
- New `color_ftui() -> ftui_style::Color`, gated by `#[cfg(feature =
  "frankentui")]`, delegates to the same `rgb()`.
- Locked the contract with two new tests (one feature-gated) that round-
  trip through both backends and assert RGB equivalence via
  `ftui_style::Color::to_rgb()`.

Dependency:
- `ftui-style` is pulled as an optional git dep pinned to
  quangdang46/frankentui@33ad1c57 — no path dep so other machines do not
  need a sibling /data/projects/frankentui checkout. Frankentui's
  ftui-style crate compiles cleanly on jcode's existing nightly toolchain
  (rustc 1.98.0-nightly).

Cargo.lock:
- unicode-width 0.2.0 → 0.2.2 (required by ftui-core; jcode's `= "0.2"`
  requirement already permits this).
- bitflags 2.10.0 → 2.11.1 (required by ftui-render; crossterm's `= "2.9"`
  permits it).
- bumpalo 3.19.1 → 3.20.3 (required by ftui-render; wasm-bindgen-macro-
  support's `= "3.0"` permits it).
No Cargo.toml version requirements were widened.

Verified:
- `cargo check -p jcode-tui-usage-overlay` (default features): clean.
- `cargo check -p jcode-tui-usage-overlay --features frankentui`: clean.
- `cargo test -p jcode-tui-usage-overlay`: 4/4 pass (default), 5/5 pass
  (--features frankentui).
- `cargo check -p jcode --lib`: clean (consumer src/tui/usage_overlay.rs
  unchanged, still uses ratatui Color via color()).

See docs/RATATUI_MIGRATION_FEASIBILITY.md for the full Phase 1 writeup,
the list of questions this prototype did and did not answer, and the
recommended next slice (jcode-tui-style).
Phase 2 of ratatui → frankentui migration. The crate is now fully
ratatui-free — its Cargo.toml has no ratatui dep. Public API:

  UsageOverlayStatus::color() -> ftui_style::Color

Phase 1's dual-color API (color() ratatui + color_ftui() frankentui
behind a feature flag) is collapsed.

Consumer adaptation (src/tui/usage_overlay.rs still drives ratatui
rendering):
- New module src/tui/ftui_compat.rs with ftui_color_to_rata() that
  matches every ftui_style::Color variant onto the closest ratatui
  Color variant (Rgb 1:1, Ansi256 numeric, Ansi16 named, Mono → black/
  white). 5 unit tests cover the variant mapping.
- Local helper status_color_rata(status) wraps the conversion at
  the three Style::fg() call sites in usage_overlay.rs.

Top-level Cargo.toml gains an ftui-style git dep pinned to the same
quangdang46/frankentui@33ad1c57 commit Phase 1 used.

ftui-compat is intentionally an explicit, easy-to-grep boundary — it
disappears once the rendering layer itself stops using ratatui.

Verified:
- cargo test -p jcode-tui-usage-overlay: 4/4 pass.
- cargo check -p jcode --lib: clean (existing 7 dead-code warnings
  unrelated to this change).

Crate count update: 1 of 8 crates is now ratatui-free.
Documents what landed in commit 67fc2c3 (jcode-tui-usage-overlay full
migration), what was attempted and reverted (jcode-tui-style had 687
type errors across 502 call sites in 50 consumer files), and why each
remaining crate is blocked.

Key findings:
- ratatui 0.30's Style::fg signature is `pub const fn fg(self, color:
  Color) -> Self` — takes Color exactly, not impl Into<Color>. So
  every leaf crate that returns ftui_style::Color triggers explicit
  conversion at every Style::fg/bg call site.
- The orphan rule prevents adding From<ftui_style::Color> for
  ratatui::style::Color from a third crate.
- jcode-tui-markdown has no migration target — frankentui's own
  migration-map.md explicitly defers markdown rendering to Phase 5
  (`ftui-extras`), not yet implemented.
- jcode-tui-mermaid has no migration target — frankentui has no
  ratatui-image equivalent (also Phase 5).

Final status: 1 of 8 crates migrated (usage-overlay), 5 deferred to
structural-rewrite work that must happen in dedicated PRs (style,
workspace, render, messages, top-level jcode), 2 hard-blocked on
upstream frankentui Phase 5 work (markdown, mermaid).

Phase 1 and Phase 2 each took ~1 hour and represent the easy work;
the original 2–4 week estimate at the top of this doc still stands
for the remaining 5 deferred crates.
…_support

Phase 3 + Phase 4 of ratatui → frankentui migration.

Phase 3: jcode-tui-style is fully ratatui-free.
- Cargo.toml: drop ratatui = "0.30", add ftui-style git dep.
- color.rs/theme.rs: every public API returns ftui_style::Color.
- clear_buf removed (it operated on ratatui Buffer; not the leaf
  crate's responsibility).

All 502 caller sites in 50 files keep working unchanged because
src/tui/color_support.rs and src/tui/ui_theme.rs wrap the leaf-crate
output back to ratatui::Color via new .rata() / .ftui() extension
traits in src/tui/ftui_compat.rs.

ftui_compat gains:
- FtuiColorExt trait (.rata() on ftui_style::Color)
- RataColorExt trait (.ftui() on ratatui::Color)
- Total rata_color_to_ftui() covering every variant including Reset
  → Mono(White)
- 8 round-trip tests

Phase 4: jcode-tui-workspace::color_support deduplicated.
- ~220 LOC of duplicate cube/gray/distance helpers deleted.
- Now a thin wrapper that delegates to jcode_tui_style::color::rgb
  and converts to ratatui::Color at the boundary.
- workspace's Cargo.toml gains jcode-tui-style + ftui-style deps but
  keeps ratatui because clear_buf and workspace_map_widget still
  operate on ratatui Buffer.

Crate count: 2 of 8 ratatui-free (jcode-tui-usage-overlay + jcode-
tui-style). The remaining 6 crates are blocked by either:
- Architectural rewrite needed (Widget signature, Buffer cell layout,
  Terminal lifecycle, TestBackend pattern) — 5–10+ days each, must
  be done with shell verification per step.
- Upstream frankentui Phase 5 features not yet shipped (markdown
  rendering, ratatui-image equivalent).

See docs/RATATUI_MIGRATION_FEASIBILITY.md Phase 3 and Phase 4
sections for full writeup including the wrapper-funnel pattern and
why it stops being usable at the rendering-layer boundary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@quangdang46 quangdang46 reopened this May 26, 2026
@quangdang46 quangdang46 force-pushed the experimental/ratatui-to-frankentui branch from d342dba to 0735654 Compare May 26, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant