Skip to content

refactor(tui): split 7.8k-line app.rs into cohesive app/ modules - #403

Merged
Kuberwastaken merged 1 commit into
Kuberwastaken:mainfrom
SlugThug:refactor/split-tui-app
Sep 2, 2026
Merged

refactor(tui): split 7.8k-line app.rs into cohesive app/ modules#403
Kuberwastaken merged 1 commit into
Kuberwastaken:mainfrom
SlugThug:refactor/split-tui-app

Conversation

@SlugThug

@SlugThug SlugThug commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

From SlugThugLabs — building from whatever's on hand, to standards worth aspiring to.

Summary

tui/src/app.rs was a 7,807-line file containing the full App state struct, a 5,557-line impl block, the keyboard dispatch tree, and a 950-line test suite. This PR restructures it into app/ with one module per responsibility. Pure structural move — zero behavior change. Every function body is byte-identical to the original; no logic was touched, no warnings fixed, no style "improved" while moving.

Module layout

Module Responsibility Lines
mod.rs App struct, constructor, re-exports, module wiring ~940
types.rs Pure data model (messages, tool blocks, dialogs, state) 245
keys.rs Keyboard normalization + key-event dispatch 2,727
mouse.rs Pointer input, selection, context menu, paste viewer 660
providers.rs Provider/model/config + import-config flow 442
commands.rs Slash-command catalog + intercept dispatch 392
turns.rs Turn lifecycle (snapshots, streaming flush, labels) 176
messages.rs Transcript ops (add/replace/push, notifications, scroll) 162
prompt.rs Prompt input state machine + voice hold-to-talk 158
views.rs Overlay/dialog orchestration + MCP view state 405
run.rs Main event loop, query events, jump-to-error 628
tests.rs Test suite (moved whole, use super::*) 956

Public API unchanged — crate::app::{App, ...} re-exported from mod.rs exactly as before; zero ripples into render.rs, messages/, or lib.rs.

Visibility note (the one deliberate adjustment)

Private methods moved across module boundaries became pub(super) — restoring exactly the visibility the single-module layout provided. Of the 79 items lifted: 48 have genuine cross-module callers; 31 are used only within their defining file and are candidates to return to private during the phase-two interface work below.

Placement decisions

  • try_copy_to_clipboard stays in mod.rs — it's crate-level public API, re-exported by lib.rs (pub use app::try_copy_to_clipboard).
  • Animation/mode helpers (tick_rustle_pose, cycle_agent_mode, apply_theme, …) stay near the constructor for now; their callers live inside app, and regrouping them belongs with the interface work.
  • MCP plumbing in views.rs mirrors the original file's conflation; splitting it out is flagged for phase two.

Known follow-up (phase two, separate PRs)

Honest caveats this PR does not hide:

  • keys.rs remains the largest file — handle_key_event alone is ~2,300 lines and is the next target.
  • Submodule impl blocks still share the full App state; phase two replaces &mut self-everywhere with narrow interfaces and encapsulated state, and privatizes the ~31 lazy lifts.
  • cargo fmt stays advisory per repo policy — import ordering here matches the crate's existing conventions (messages/, etc.); fmt drift is at parity with upstream (156 diffs pre-split → 171 across the same volume).

Verification (matches CI gates)

  • cargo check --workspace --locked
  • cargo test --workspace --locked ✅ 1,799 tests, 0 failures (tui: 708)
  • cargo clippy --workspace --all-targets -D warnings ✅ clean
  • 208/208 functions preserved vs. original — 0 missing, 0 extra
  • Diff: app.rs deleted, 11 new files, +7,893 / −7,807 (the +86 is module glue)

No behavior change, no test changes, no dependency changes.

Pure structural move with zero behavior change: app.rs (7,807 lines)
becomes app/ with one module per responsibility — input (keys, mouse),
state model (types), provider management (providers), command dispatch
(commands), session lifecycle (turns, messages, run), and the test
suite (tests). Public API and re-exports are unchanged; the only
visibility adjustment is pub(super) on moved private methods, which
restores exactly the visibility the single-module layout provided.

Verified: workspace cargo check clean, full test suite green (~1,000
tests), and cargo clippy --workspace --all-targets -D warnings clean.
@Kuberwastaken

Copy link
Copy Markdown
Owner

Reviewed this closely and it holds up — I diffed all 208 functions against app.rs and every body is byte-identical, with the only signature changes being the 79 pub(super) lifts you documented. Item and attribute inventories match exactly (23 structs/enums/consts, all 10 #[cfg] blocks), #[cfg(test)] gating is preserved on mod tests, and the import set is 1:1 after the flattening. CI is green on all three platforms. Really nice work, and thanks for the honest caveats in the description.

One small correction to "Public API unchanged": ContextMenuState, ContextMenuItem, and GoToLineDialog are pub in today's app.rs and reachable as claurst_tui::app::*, but they're missing from the pub use types::{…} re-export in mod.rs. Nothing in the workspace consumes them so it builds fine — I'll restore them in a follow-up commit rather than send this back for a one-liner.

On timing: seven open PRs currently touch app.rs, but every one of them needs a revision round anyway, so rather than make you rebase behind a queue that isn't moving, I'm landing this first — the module layout becomes the base everyone else re-targets. Merging now. 🎉

@Kuberwastaken
Kuberwastaken merged commit b0637c9 into Kuberwastaken:main Sep 2, 2026
3 checks passed
alecuba16 added a commit to alecuba16/claurst that referenced this pull request Sep 3, 2026
…utocomplete

- scan_dir now handles subdirectories with SKILL.md (directory-based skills)
- parse_skill_file uses parent dir name when file stem is 'skill'
- resolve_path expands ~ and ~/ paths, leaves ~user unexpanded, handles
  both / and backslash separators for Windows
- discover_skills adds ~/.agents/skills/ as a global source
- commands_from_discovered_skills is now wired into autocomplete via
  all_slash_command_names, so skills appear in the command palette
- Discovered slash commands cached on App and merged at prompt update time,
  not re-discovered per keystroke
- No CRLF/LF churn: all changes preserve original line endings

Addresses review feedback on PR Kuberwastaken#401:
1. Rebased on post-Kuberwastaken#403 main, no reformatting
2. resolve_path handles ~user paths (left unexpanded) and Windows separators
3. scan_dir collects rather than returning early on SKILL.md
4. Discovery runs once at startup (not in App::new, not per keystroke)
5. all_slash_command_names cached, not rebuilt per keystroke
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.

2 participants