Update V8 Version
openinterpreter/openinterpreter
Bumps the pinned v8 and rusty_v8 versions in Codex, validates the release-candidate path with the v8-canary check, and traces failures to upstream build changes.
Migrate Rust TUIs from ratatui (or cursive, Python textual) to SuperLightTUI v0.23.
$ npx skills add subinium/SuperLightTUI --skill slt-migration -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install subinium/SuperLightTUI slt-migration --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/subinium/SuperLightTUI.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/slt-migration .claude/skills/slt-migration && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "slt-migration" agent skill from https://github.com/subinium/SuperLightTUI/tree/main/.agents/skills/slt-migration into .claude/skills/slt-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "slt-migration", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/subinium/SuperLightTUI/tree/main/.agents/skills/slt-migrationType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add subinium/SuperLightTUI --skill slt-migration -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install subinium/SuperLightTUI slt-migration --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/subinium/SuperLightTUI.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/slt-migration .agents/skills/slt-migration && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "slt-migration" agent skill from https://github.com/subinium/SuperLightTUI/tree/main/.agents/skills/slt-migration into .agents/skills/slt-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "slt-migration", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add subinium/SuperLightTUI --skill slt-migration -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install subinium/SuperLightTUI slt-migration --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/subinium/SuperLightTUI.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/slt-migration .cursor/skills/slt-migration && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "slt-migration" agent skill from https://github.com/subinium/SuperLightTUI/tree/main/.agents/skills/slt-migration into .cursor/skills/slt-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "slt-migration", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/subinium/SuperLightTUI.git --path .agents/skills/slt-migration--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add subinium/SuperLightTUI --skill slt-migration -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install subinium/SuperLightTUI slt-migration --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/subinium/SuperLightTUI.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/slt-migration .gemini/skills/slt-migration && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "slt-migration" agent skill from https://github.com/subinium/SuperLightTUI/tree/main/.agents/skills/slt-migration into .gemini/skills/slt-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "slt-migration", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install subinium/SuperLightTUI slt-migrationInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add subinium/SuperLightTUI --skill slt-migration -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/subinium/SuperLightTUI.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/slt-migration .github/skills/slt-migration && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "slt-migration" agent skill from https://github.com/subinium/SuperLightTUI/tree/main/.agents/skills/slt-migration into .github/skills/slt-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "slt-migration", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add subinium/SuperLightTUI --skill slt-migration -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install subinium/SuperLightTUI slt-migration --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/subinium/SuperLightTUI.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/slt-migration .opencode/skills/slt-migration && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "slt-migration" agent skill from https://github.com/subinium/SuperLightTUI/tree/main/.agents/skills/slt-migration into .opencode/skills/slt-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "slt-migration", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
slt-migrationMigrate Rust TUIs from ratatui (or cursive, Python textual) to SuperLightTUI v0.23.
Slt Migration is an agent skill from subinium/SuperLightTUI. Migrate Rust TUIs from ratatui (or cursive, Python textual) to SuperLightTUI v0.23. Use when porting an existing TUI codebase to SLT, or when the user asks "how do I do X from ratatui in SLT". Korean triggers "ratatui 마이그레이션", "SLT로 포팅", "이걸 SLT로".
Its SKILL.md is about 6.2k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It works with Rust and Python. The repository describes itself as: 🦀 An immediate-mode Rust TUI framework with flexbox layout and Tailwind-style chaining API. The licence is MIT.
9 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 15fd63a. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
cargoFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Slt Migration loads about 6.2k tokens when it runs. Until then it costs about 66 tokens; SKILL.md has 2,079 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from subinium/SuperLightTUI at commit 15fd63a, republished under its MIT licence (© subinium). 2,079 words, ~6,246 tokens.
.claude/skills/slt-migration/SKILL.md (or your agent's skills folder).This skill complements .agents/skills/slt/SKILL.md (authoring). Use this one to port an existing codebase. After migration switch to the slt skill for day-to-day work.
Targets SLT v0.23.0. Every API name below must be verified against src/lib.rs and src/context/widgets_*.rs. The v0.20 API consistency pass removed several v0.19 names; the historical table below remains relevant.
Trigger when:
ratatui, tui, or cursive, or Cargo.toml lists them.textual project is being rewritten in Rust.If starting fresh (no existing TUI), use the slt skill instead.
If you see these in any third-party doc, blog post, or AI training data — they are GONE in v0.20. Use the replacement.
| Removed | Replacement |
|---|---|
gauge_w(r, w) | ui.gauge(r).width(w) |
gauge_colored(r, c) | ui.gauge(r).color(c) |
line_gauge_with(r, opts) | ui.line_gauge(r).<chain> |
breadcrumb_sep(b, s) | ui.breadcrumb(b).separator(s) |
breadcrumb_response(b) / breadcrumb_response_with(b, s) | ui.breadcrumb(b).show() -> BreadcrumbResponse |
LineGaugeOpts | LineGauge<'_> builder (chained) |
HighlightRange::single(i) | HighlightRange::line(i) |
label_owned(s) | label(s) (accepts impl Into<String>) |
Compact side-by-side — read once before writing any code.
| Aspect | ratatui | SLT |
|---|---|---|
| Loop ownership | You own it: terminal.draw(|f| ...) | slt::run(|ui| ...) owns it |
| Layout | Layout::default().constraints(...).split(area) → Vec<Rect> | ui.row / ui.col / ui.bordered(...).col(...) (flexbox) |
| Widget API | Build a widget value, then f.render_widget(widget, rect) | Method on &mut Context: ui.text(...), ui.list(&mut state) |
| State | App struct outside the closure | Plain Rust variables outside the closure (same idiom) |
| Render mode | Retained mental model — recompute every draw | Immediate — describe every frame |
| Hit testing | Manual Rect math | Response { clicked, right_clicked, hovered, focused, gained_focus, lost_focus, rect } |
| Setup / teardown | enable_raw_mode, EnterAlternateScreen, panic hook by you | All handled by slt::run (incl. terminal-restoring panic hook) |
| Threading shared state | &mut App parameter chain | ui.provide(value, |ui| ...) + ui.use_context::<T>() |
cursive: callback-based — siv.add_global_callback, layered views, owns its loop. SLT has no callbacks; check inputs and branch in the closure.
textual (Python): retained App+Widget classes, CSS-like styling, compose() yields widgets, on_* event handlers. SLT replaces all of that with one closure and chained method calls.
ratatui (typical):
let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
terminal::enable_raw_mode()?;
crossterm::execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?;
loop {
terminal.draw(|f| ui(f, &mut app))?;
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') { break; }
// dispatch to app
}
}
crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
terminal::disable_raw_mode()?;SLT v0.20:
fn main() -> std::io::Result<()> {
let mut app = App::default();
slt::run(|ui| {
if ui.key('q') || ui.key_code(KeyCode::Esc) { ui.quit(); }
render(ui, &mut app);
})
}slt::run enters alternate screen, enables raw mode, installs a panic hook restoring terminal state, and tears down on exit. Variants:
slt::run_with(RunConfig::default().mouse(true).theme(Theme::dark()), \|ui\| ...) — mouse capture, custom themeslt::run_inline(rows, \|ui\| ...) — render below the prompt, no alt screenslt::run_static(\|ui\| ...) — append-only scrollback (use with ui.static_log(...))slt::run_async::<Message>(\|ui, messages\| ...) — tokio integration (async feature)Every SLT method below is verified against src/context/widgets_*.rs.
| ratatui | SLT v0.20 |
|---|---|
Block::default().borders(Borders::ALL).title("X") | ui.bordered(Border::Rounded).title("X").col(|ui| ...) |
Block::default().borders(Borders::ALL).border_type(BorderType::Rounded) | ui.bordered(Border::Rounded).col(...) |
Block::default().borders(Borders::TOP | Borders::BOTTOM) | ui.bordered(Border::Single).border_sides(BorderSides::vertical()).col(...) |
Paragraph::new("text") | ui.text("text") |
Paragraph::new("text").wrap(Wrap { trim: true }) | ui.text("text").wrap() |
Paragraph::new("text").alignment(Alignment::Center) | ui.text("text").text_center() |
List::new(items).highlight_style(...) | ui.list(&mut ListState) — items via ListState::set_items(...) |
Table::new(rows).widths(&[...]) | ui.table(&mut TableState) — rows via TableState::set_rows(...), auto column widths |
Tabs::new(titles).select(idx) | ui.tabs(&mut TabsState) — labels via TabsState::new(["Files", "Settings"]) |
Gauge::default().percent(75).label("75%") | ui.gauge(0.75).label("75%").width(24).color(Color::Green) (v0.20 builder) |
Gauge::default().percent(75) (no label) | ui.gauge(0.75) or ui.progress(0.75) |
LineGauge::default().ratio(0.6).label("Memory") | ui.line_gauge(0.6).label("Memory").filled('━').empty('─').width(48) |
BarChart::default().data(&[("a", 1), ("b", 2)]) | ui.bar_chart(&[("a", 1.0), ("b", 2.0)], max_width) (values are f64) |
Chart::new(datasets) | ui.chart(width, height, |c| { c.line(&data).color(c).label("X"); }) |
Sparkline::default().data(&[1, 2, 3]) | ui.sparkline(&[1.0, 2.0, 3.0], width) |
Span::styled("x", Style::default().fg(Color::Red)) | ui.text("x").fg(Color::Red) |
Line::from(vec![span1, span2]) | ui.row(|ui| { ui.text("a"); ui.text("b"); }) or ui.line(|ui| ...) |
Clear widget | usually unneeded — render at top-level or use ui.modal(|ui| ...) for overlays |
These are SLT additions; if your ratatui app hand-rolled them, replace with the built-in:
| Pattern | v0.20 SLT |
|---|---|
| Scrollable code/log with line numbers | ui.scrollable_with_gutter(&mut scroll, GutterOpts::line_numbers(total, vp_h), |ui, abs| { ui.text(lines[abs]); }) |
| Search highlights in scrollable | ScrollState::set_highlights(&[HighlightRange::line(7), HighlightRange::span(15, 3)]) + state.highlight_next/previous |
| Two-pane resizable layout | ui.split_pane(&mut SplitPaneState::new(0.5), |ui| ..., |ui| ...) (horizontal) or ui.vsplit_pane(...) (vertical) |
| Breadcrumb with click feedback | let r = ui.breadcrumb(&segs).separator(" › ").show(); if let Some(i) = r.clicked_segment { ... } |
| Tooltips | if ui.button("X").on_hover(ui, "Save").clicked { ... } |
| Animated value | let alpha = ui.animate_value("fade", target, 30); (eased, 1-line) |
| Modal with focus trap (WCAG) | ui.modal_with(ModalOptions { tab_trap: true }, |ui| ...) (plain ui.modal keeps Esc-friendly behavior) |
| Per-subtree theme override | ui.container().theme(custom_theme).col(|ui| ...) |
| Theme density | Theme::compact() / comfortable() / spacious() instead of manual spacing |
ratatui custom Widget trait impls don't translate directly. Two options:
Option A — function (preferred for one-off widgets):
fn render_my_widget(ui: &mut Context, data: &MyData) { /* method calls */ }Option B — implement SLT's Widget trait (for reusable libraries):
struct Label<'a> { text: &'a str }
impl<'a> slt::Widget for Label<'a> {
type Response = slt::Response;
fn ui(&mut self, ui: &mut slt::Context) -> Self::Response {
ui.register_focusable();
ui.text(self.text).bold();
slt::Response::default()
}
}
let mut label = Label { text: "hello" };
ui.widget(&mut label);Note: SLT's Widget trait is different from ratatui's. ratatui's Widget::render(self, area, buf) is a stateless paint into a Buffer. SLT's Widget::ui(&mut self, ui) is an immediate-mode call that returns a Response.
ratatui:
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(3), Constraint::Min(0), Constraint::Length(1)])
.split(area);
f.render_widget(header, chunks[0]);
f.render_widget(body, chunks[1]);
f.render_widget(footer, chunks[2]);SLT:
ui.col(|ui| {
ui.container().h(3).col(|ui| { /* header */ });
ui.container().fill().col(|ui| { /* body — fills remaining (v0.20 fill() == grow(1)) */ });
ui.container().h(1).col(|ui| { /* footer */ });
});Constraint translation:
| ratatui | SLT |
|---|---|
Constraint::Length(3) | .h(3) (col) or .w(3) (row) |
Constraint::Min(0) | .fill() (v0.20) or .grow(1) |
Constraint::Min(n) | .min_h(n).fill() (col) or .min_w(n).fill() (row) |
Constraint::Max(n) | .max_h(n) or .max_w(n) |
Constraint::Percentage(50) | .h_pct(50) or .w_pct(50) |
Constraint::Ratio(1, 3) | .h_ratio(1, 3) or .w_ratio(1, 3) |
.margin(1) on Layout | .p(1) on parent container |
.spacing(1) between chunks | .gap(1) on parent row / col |
Constraints value type also exposes the v0.20 WidthSpec set: Constraints::default().w_pct(50), .w_ratio(1, 3), .w_minmax(10, 30) — see examples/v020_widthspec.rs.
slt::*)| ratatui / your code | SLT |
|---|---|
ListState | slt::ListState (set_items, set_filter, selected_item, visible_indices) |
TableState | slt::TableState (set_rows, toggle_sort, sort_by, set_filter, next_page, prev_page) |
TabsState | slt::TabsState (new(["Files", "Settings"]), selected_label) |
ScrollbarState / manual offset | slt::ScrollState + ui.scrollable(&mut state).col(...) or ui.scrollable_with_gutter(...) |
your own input: String | slt::TextInputState::with_placeholder("…") (validators via add_validator) |
your own textarea: Vec<String> | slt::TextareaState::default().word_wrap(80) |
| your own selection set | slt::SelectState, slt::RadioState, slt::MultiSelectState |
| tree view | slt::TreeState, slt::DirectoryTreeState::from_paths(...) |
| modal flag | slt::Context::modal(|ui| ...) or modal_with(ModalOptions{tab_trap:true}, ...) |
| toast queue | slt::ToastState::default() + ui.notify(level, msg) |
| log/history view | slt::RichLogState::new() (capped at 10000) or RichLogState::new_unbounded() |
ratatui apps typically thread &mut App (or &App) through every render fn:
fn render(f: &mut Frame, app: &mut App) {
render_header(f, app);
render_body(f, app);
render_footer(f, app);
}In SLT v0.19+ replace read-only sharing with provide/use_context:
struct AppCtx { theme: slt::Theme, tick: u64, settings: Settings }
slt::run(|ui| {
let ctx = AppCtx { theme: *ui.theme(), tick: ui.tick(), settings: app.settings.clone() };
ui.provide(ctx, |ui| {
render_header(ui);
render_body(ui, &mut app.doc); // writes still pass &mut explicitly
render_footer(ui);
});
});
fn render_header(ui: &mut slt::Context) {
let ctx = ui.use_context::<AppCtx>();
ui.text(format!("tick {}", ctx.tick));
}Reserve explicit &mut parameters for writes (&mut MyDocState).
| ratatui | SLT |
|---|---|
KeyCode::Char('q') match | if ui.key('q') { ... } |
KeyCode::Esc match | if ui.key_code(KeyCode::Esc) { ... } |
KeyModifiers::CONTROL + Char('c') | if ui.key_mod('c', KeyModifiers::CONTROL) { ... } (Ctrl-C is also auto-handled by slt::run) |
MouseEventKind::Down(MouseButton::Left) | if let Some((x, y)) = ui.mouse_down() { ... } or Response.clicked |
MouseEventKind::Down(MouseButton::Right) | Response.right_clicked (v0.20) |
MouseEventKind::ScrollUp | if ui.scroll_up() { ... } |
| Manual hit test | if ui.button("X").clicked { ... } (Response.clicked is a public field) |
| Focus events | Response.gained_focus / Response.lost_focus (v0.20) |
paste event | if let Some(s) = ui.paste() { ... } |
| Sequence detection | if ui.key_chord("gg") { ... } |
Modal-aware: ui.key(), ui.key_code(), ui.key_mod() are filtered when a modal is open. For global shortcuts that bypass modals, use ui.raw_key_code() / ui.raw_key_mod().
Consume: ui.consume_key(c) / ui.consume_key_code(code) mark events handled so child widgets don't re-process. Useful for global shortcuts taking precedence over text input.
| ratatui | SLT |
|---|---|
Style::default().fg(Color::Red) | Style::new().fg(Color::Red) |
Style::default().add_modifier(Modifier::BOLD) | Style::new().bold() |
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD) | Style::new().fg(Color::Red).bold() |
Style::default().bg(Color::Blue) | Style::new().bg(Color::Blue) |
Per-text styling: Span::styled("x", style) | Chain on the call: ui.text("x").fg(Color::Red).bold() |
Modifier::DIM / ITALIC / UNDERLINED / REVERSED / CROSSED_OUT | .dim() / .italic() / .underline() / .reversed() / .strikethrough() |
| Conditional styling | ui.text("x").with_if(is_error, |t| { t.bold().fg(Color::Red); }) (v0.19+) |
Style is Copy in both libraries — no clone needed.
Both libraries have:
Red, Green, Blue, Yellow, Cyan, Magenta, Black, White, plus LightRed, LightGreen, etc.Color::Indexed(N) ↔ Color::Indexed(N)Color::Rgb(r, g, b) ↔ Color::Rgb(r, g, b)Color::Reset in bothDifferences:
Color::Gray and Color::DarkGray — SLT only has Color::DarkGray. Use Color::Indexed(8) (ANSI bright black) or Color::Rgb(128, 128, 128) for mid-gray.slt::palette::tailwind::* (BLUE.c500, RED.c700) — same 11-shade scale across 22 palettes, identical to Tailwind CSS.ratatui BorderType | SLT Border |
|---|---|
BorderType::Plain | Border::Single |
BorderType::Rounded | Border::Rounded |
BorderType::Double | Border::Double |
BorderType::Thick | Border::Thick |
BorderType::QuadrantInside / QuadrantOutside | no direct equivalent — use a dashed style or custom draw |
ratatui Borders | SLT BorderSides |
|---|---|
Borders::ALL | default — ui.bordered(Border::Rounded) draws all 4 |
Borders::TOP | BorderSides::top() |
Borders::BOTTOM | BorderSides::bottom() |
Borders::LEFT / RIGHT | BorderSides::left() / right() |
Borders::TOP | Borders::BOTTOM | BorderSides::vertical() |
Borders::LEFT | Borders::RIGHT | BorderSides::horizontal() |
Use via .border_sides(...): ui.bordered(Border::Single).border_sides(BorderSides::vertical()).col(...).
ratatui has no built-in theme. If you have ad-hoc Color::* constants, replace with slt::Theme and ui.color(ThemeColor::Primary) so themes can swap. v0.20 additions:
Theme::dark() / light() (base)Theme::compact() / comfortable() / spacious() (density variants of dark)Theme::dracula() / nord() / tokyo_night() / gruvbox_dark() / one_dark() / catppuccin() / solarized_dark() / solarized_light()ThemeBuilder::builder_from(Theme::nord()) — extend a presetContainerBuilder::theme(custom) — per-subtree overridecursive is callback-driven. SLT replaces both pattern and event loop with the imperative closure model.
| cursive | SLT |
|---|---|
Cursive::default().run() | slt::run(|ui| { ... }) |
siv.add_global_callback(Key::Esc, |s| s.quit()) | if ui.key_code(KeyCode::Esc) { ui.quit(); } |
TextView::new("hello") | ui.text("hello") |
EditView::new() | ui.text_input(&mut TextInputState) |
SelectView::new().item("a", 0).item("b", 1) | ui.select(&mut SelectState) |
Dialog::around(view).button("OK", |s| ...) | ui.modal(|ui| { ui.text(...); if ui.button("OK").clicked { ... } }) |
LinearLayout::vertical() | ui.col(|ui| ...) |
LinearLayout::horizontal() | ui.row(|ui| ...) |
siv.add_layer(view) | render at top-level; for overlays use ui.modal(...) / ui.overlay_at(anchor, |ui| ...) |
Cursive::set_user_data(state) | plain Rust variable outside the closure, captured by reference |
Mental shift: cursive callbacks fire on input. SLT's closure runs every frame. State updates are visible immediately.
textual is class-based with reactive state and CSS. SLT is functional with plain variables.
| textual | SLT |
|---|---|
class App(App) with compose() | slt::run(|ui| { ... }) closure |
reactive attributes | plain Rust variables outside the closure |
| CSS-like styling | ThemeBuilder + per-widget chains (.fg(Color::Red).bold()) |
Static("hello") | ui.text("hello") |
Button("Click") + on_button_pressed | if ui.button("Click").clicked { ... } inline |
Input(placeholder="...") | ui.text_input(&mut TextInputState::with_placeholder("...")) |
DataTable | ui.table(&mut TableState) |
ScrollableContainer | ui.scrollable(&mut ScrollState).col(|ui| ...) or scrollable_with_gutter |
Container(...) | ui.bordered(...).col(...) or ui.container().col(...) |
compose() yielding child widgets | the closure body — order is layout order |
| Async event handlers | slt::run_async (async feature) returns an owned AsyncRunHandle; use .sender() and .join() / .cancel_and_join() |
| CSS animations | slt::Tween / slt::Spring / slt::Keyframes / ui.animate_value("id", target, ticks) |
Widget trait." — drop ratatui's. Either rewrite as fn render_my_widget(ui: &mut Context, data: &MyData), or implement slt::Widget (different shape — see Custom widgets section).draw(&mut self, frame: &mut Frame) method." — convert to fn render(ui: &mut Context, app: &mut App) and call from slt::run(\|ui\| render(ui, &mut app)).ListState lives across frames." — same in SLT. let mut list = ListState::new(); outside the closure; pass &mut list to ui.list(&mut list) each frame.ui.key() / ui.key_code() / ui.key_mod() / ui.mouse_down() / ui.scroll_up(). Raw ui.events() is for advanced cases (key release, paste handling, custom modifier matching). For modal-aware globals use ui.raw_key_code() / ui.raw_key_mod()..split() arithmetic on Rect)." — try ui.row / ui.col + .fill / .h / .w / .h_pct / .w_pct / .align / .justify first. Flexbox handles 95% of cases. Drop to ui.container().draw(\|buf, rect\| { ... }) only when flexbox can't express it. The draw closure must be 'static (deferred execution).Constraint::Percentage(50) is everywhere." — .w_pct(50) (row child) or .h_pct(50) (col child). Both take u8.Layout::default().margin(1).split(area)." — .p(1) on the parent container.Response.rect immediately." — SLT layout runs after the closure. Frame 1 returns zero Rect. Guard with if ui.tick() > 0 { ... }.Borders::ALL." — SLT default. ui.bordered(Border::Rounded) draws all 4 sides. Subset via .border_sides(BorderSides::vertical()).Color::Gray." — doesn't exist. Use Color::Indexed(8) or Color::Rgb(128, 128, 128). Or pull from palette::tailwind::SLATE.c500.terminal.clear() between frames." — don't. SLT diffs the buffer and only emits changed cells. Manual clear breaks the diff and causes flicker.slt::run installs one on first call.&App through every render fn." — replace read-only sharing with ui.provide(ctx, \|ui\| ...) + ui.use_context::<AppCtx>(). Keep &mut for writes.ui.gauge(0.75) (no label) or ui.progress(0.75) (display widget, returns &mut Self).Inventory ratatui widgets used.
grep -rn "render_widget\|f\.render_widget" src/
grep -rn "Block::\|Paragraph::\|List::\|Table::\|Tabs::\|Gauge::\|BarChart::\|Chart::\|Sparkline::" src/Map each to an SLT method via the tables above.
Convert the run loop. Replace Terminal::new setup + draw loop + disable_raw_mode teardown with one of slt::run, slt::run_with, slt::run_inline, or slt::run_async.
Move state out of the draw closure. Most ratatui apps already do this. Keep the same shape — your App struct now feeds into one SLT closure.
Replace layout splitters. Each Layout::default().constraints(...).split(area) becomes nested ui.row / ui.col + .fill / .h / .w / .h_pct / .w_pct / .align / .justify. .gap(n) instead of .spacing(n), .p(n) instead of .margin(n).
Replace each f.render_widget(...) with the SLT method. Convert widget by widget. Verify any uncertain method via the mapping table or grep src/context/widgets_*.rs.
Adopt current builders. Where the old code hand-rolled gauges, breadcrumbs, scrollable-with-line-numbers, or split panes, use the v0.23 builders/opts directly. They handle hit-testing and accessibility.
Replace event handling. Convert raw crossterm::event::read() matches to ui.key(), ui.key_code(), ui.key_mod(), ui.mouse_down(), ui.scroll_up(). Drop manual hit-testing in favor of Response.clicked / right_clicked / hovered / gained_focus / lost_focus.
Replace &App threading with provide/use_context for read-only shared state.
Run cargo check and fix one widget at a time. Add tests with slt::TestBackend::new(80, 24).render(\|ui\| ...) once a section compiles.
After everything compiles, run the full quality gate (project AGENTS.md):
cargo fmt -- --check, cargo check --all-features, cargo clippy --all-features -- -D warnings, cargo test --all-features, cargo check --examples --all-features.
Be honest with the user — these need workarounds:
Canvas braille drawing primitive. SLT has ui.canvas(width, height, \|cv\| { cv.line(...); cv.circle(...); }) but the API takes a CanvasContext closure, not a value-typed widget. Custom point/line drawing logic needs a small rewrite. See Context::canvas in src/context/widgets_viz.rs.Wrap { trim: true } exact semantics. SLT wraps via container width and .wrap() on text; trim-leading-whitespace behavior isn't identical. Test wrap-heavy text manually.ui.modal(...) / ui.modal_with(...) / ui.overlay_at(anchor, \|ui\| ...) but not arbitrary nested view managers. Most uses fold into if state.show_modal { ui.modal(\|ui\| ...) }.theme-watch feature can reload TOML themes through ThemeWatcher; use cargo watch -x run for code and layout iteration.If a feature genuinely doesn't map, tell the user — don't fake it.
.agents/skills/slt/SKILL.md — SLT authoring skill (use after migration is done)..agents/skills/slt/REFERENCES.md — feature flags, current surface, doc pointers.examples/v020_*.rs — runnable v0.20 demos (gauge, breadcrumb, scrollable_with_gutter, split_pane, etc.).tests/v020_*.rs — regression tests showing canonical TestBackend + sequence patterns.src/lib.rs — authoritative public re-exports.src/context/widgets_*.rs — authoritative widget signatures.© subinium, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/slt-migration of subinium/SuperLightTUI.
Open the folder on GitHubat commit 15fd63a
Slt Migration next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Slt Migration this skillsubinium/SuperLightTUI | 191 | — | ~6.2k | Automated safety check: Pass | MIT | |
| Update V8 Versionopeninterpreter/openinterpreter | 69k | 2 repos | ~845 | Automated safety check: Pass | Apache-2.0 | |
| Firecrawl Page Scrape Integrationfirecrawl/firecrawl | 190k | 1 repos | ~944 | Automated safety check: Pass | ISC | |
| Fory Releaseapache/fory | 4.6k | — | ~2.9k | Automated safety check: Pass | Apache-2.0 | |
| Apple Container Test RunnerRustPython/RustPython | 22k | — | ~467 | Automated safety check: Pass | MIT | |
| Release Skillsnexmoe/eve | 421 | 3 repos | ~3.3k | Automated safety check: Pass | None |
openinterpreter/openinterpreter
Bumps the pinned v8 and rusty_v8 versions in Codex, validates the release-candidate path with the v8-canary check, and traces failures to upstream build changes.
firecrawl/firecrawl
Adds Firecrawl's /scrape endpoint to application code to pull markdown, HTML, links, screenshots or structured data from a single known URL.
apache/fory
Prepare an Apache Fory release candidate from a clean release branch, including the version bump, RC tag, JVM staging, ASF source artifacts, SVN upload, and vote email.
RustPython/RustPython
Runs RustPython tests inside a Linux container built with Apple's container CLI, so macOS users can compare Linux results with their local ones.
nexmoe/eve
Universal release workflow. An agent skill from nexmoe/eve.
RustPython/RustPython
Implements missing CPython C-API functions in RustPython's crates/capi, mapping each header to its module with the pyo3-ffi header split.
subinium/SuperLightTUI
Build Rust TUI apps with SuperLightTUI v0.23 (immediate-mode terminal UI).
subinium/SuperLightTUI
Build Rust TUI apps with SuperLightTUI v0.20 (immediate-mode terminal UI).
subinium/SuperLightTUI
Migrate Rust TUIs from ratatui (or cursive, Python textual) to SuperLightTUI v0.20.
Migrate Rust TUIs from ratatui (or cursive, Python textual) to SuperLightTUI v0.23. Slt Migration is an agent skill from subinium/SuperLightTUI.23.
Slt Migration fits situations like: porting an existing TUI codebase to SLT; the user asks how do I do X from ratatui in SLT.
Run `npx skills add subinium/SuperLightTUI --skill slt-migration -a claude-code`. Or copy the skill folder (.agents/skills/slt-migration in subinium/SuperLightTUI) into .claude/skills/slt-migration in your project. Claude Code loads it when a task matches its description.
Run `npx skills add subinium/SuperLightTUI --skill slt-migration -a codex`. Or copy the skill folder (.agents/skills/slt-migration in subinium/SuperLightTUI) into .agents/skills/slt-migration in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add subinium/SuperLightTUI --skill slt-migration -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/slt-migration, .gemini/skills/slt-migration, .github/skills/slt-migration and .opencode/skills/slt-migration in your project.
Going by SKILL.md and its folder, Slt Migration needs the command-line tools its instructions call (cargo). Our summary lists: Python 3.
SKILL.md names 1 domain. As links in the text: github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Slt Migration is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.2k tokens (SKILL.md is roughly 25k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Slt Migration: Update V8 Version (openinterpreter/openinterpreter, 69k stars), Firecrawl Page Scrape Integration (firecrawl/firecrawl, 190k stars), Fory Release (apache/fory, 4.6k stars) and Apple Container Test Runner (RustPython/RustPython, 22k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
subinium (a GitHub user) maintains it in subinium/SuperLightTUI, which has 191 GitHub stars. The repository holds 4 skills in this directory. The repository was last updated on September 9, 2026.
Source: subinium/SuperLightTUI on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.