Skip to content

refactor: restructure lib.rs into theme + router modules (#89)#107

Merged
GeneralD merged 5 commits into
mainfrom
refactor/89-lib-architecture
Jun 20, 2026
Merged

refactor: restructure lib.rs into theme + router modules (#89)#107
GeneralD merged 5 commits into
mainfrom
refactor/89-lib-architecture

Conversation

@GeneralD

@GeneralD GeneralD commented Jun 20, 2026

Copy link
Copy Markdown
Owner

type breaking scope diff files tests coverage review

Restructures src/lib.rs by extracting two focused modules, so the plugin's
zellij-facing glue, its pure click routing, and its theme adapter no longer
share one file. Pure mechanical-equivalence refactor — no behavior change,
no public API change, no new permission. Done as three independently
green commits, never a big-bang (the issue's core ask).

Closes #89.

What moved, and why

Commit Module Content Rationale
1 src/theme.rs (new) rgb + palette_from_style The lone boundary touching both zellij's color types and the dependency-free color module — isolated as an adapter (rule #8)
2 src/router.rs (new) pane_at / clicked_new_tab_button / clicked_close_button (now free functions) + TabPaneGeom The pure, zellij-type-free click decisions a left-click resolves against the recorded frame
3 src/router.rs new ClickIntent enum + route_click One pure function resolves a click to a single intent; update() becomes the sole host-effect dispatcher

After commit 3, every host effect (new_tab, close_tab_with_index,
focus_terminal_pane, switch_tab_to) lives in exactly one place — the
update() LeftClick match arm — dispatched off the pure route_click
decision. The now-redundant focus_or_switch_at / switch_to_tab_at methods
are deleted.

Click routing priority (unchanged)

route_click composes the affordances in the order the bar paints them:

+ button (#76)  >  close × cell (#86)  >  minimap pane (#74)  >  tab block (#8)  >  no-op

Verification

All three commits build (wasm32-wasip1) and pass cargo test --lib green;
final state 243 tests pass, clippy adds no new warnings over baseline.

Four independent adversarial reviews (behavior-equivalence, #54 host-count
contract, rule-#8 boundary, coverage parity) all came back clean:

Review verdicts
  • Behavior-equivalence: EQUIVALENT — priority order, false return on every path, host-effect args, the close_layout.clear()-before-close ordering, and the SwitchTab(u32) type all identical pre/post; no divergent-input counterexample found.
  • Permission prompt is unfocusable: set_selectable(false) runs before the grant resolves #54 host-count contract: PRESERVED — the native-test stub trio is byte-identical, load still emits exactly 2 host commands and each PermissionRequestResult exactly 1, and the permission set stays ReadApplicationState + ChangeApplicationState (no third permission → no feat(title): pane-title summarization module (icon table + width-aware truncation) #15 freeze).
  • Rule mouse: click a tab block to switch tabs #8 boundary: CLEANrouter.rs references zero zellij types; theme.rs is the only new module touching zellij_tile::prelude and leaks nothing past its color::Palette return; visibility stays pub(crate) (public API unchanged); content is English.
  • Coverage: NO REGRESSION — TOTAL 99.52% vs 99.53% baseline (−0.01%, within the 1% project gate); router.rs and theme.rs both 100% line-covered; all moved tests keep their assertions; the deleted focus_or_switch_at smoke test is replaced by a stronger update()-driven focus/no-op test; a new route_click test covers all five ClickIntent arms.

Coverage note

The single off-wasm-uncovered line stays the new_tab dispatch arm — new_tab
reads a return value from stdin and panics under the native test host, so it is
inherently wasm-only (this was already uncovered at baseline). Its routing
decision
(route_clickClickIntent::NewTab) is fully covered in
router.rs; only the host call itself can't run off-wasm.

Deferred

The RenderGeometry bundle (folding the per-render geometry args into one
struct, the design's Step 4) is intentionally out of scope here and left to
a follow-up PR, to keep this change a clean extraction with no signature churn.

Summary by CodeRabbit

  • Refactor

    • Reorganized internal click-handling logic and theme palette conversion into dedicated modules for improved code maintainability.
  • Tests

    • Updated and expanded unit test coverage for click routing and theme palette generation.

GeneralD added 3 commits June 20, 2026 14:14
Move rgb() and palette_from_style() — the lone boundary touching both
zellij's color types and the dependency-free color module — out of lib.rs
into a focused theme adapter (rule #8). The ModeUpdate arm now calls
theme::palette_from_style(); the palette_slots_come_from_multiplayer_user_colors
test moves alongside its subject. No public API change (pub(crate) mod).

Part of #89.
…er.rs

Move pane_at / clicked_new_tab_button / clicked_close_button — the three
zellij-type-free decisions a left-click resolves against the recorded frame
— out of impl State into free functions in a new router module (rule #8),
carrying TabPaneGeom (now pub(crate)) and the predicates' tests alongside.
update() and focus_or_switch_at now call router::; the host effects stay in
lib.rs, dispatched past these predicates. No public API change.

Part of #89.
…e sole host-effect dispatcher

Add ClickIntent + route_click to the router: one pure function resolves a
left click against the recorded frame, in the bar's paint priority (+ button >
close × > minimap pane > tab block > nothing), returning the single intent it
triggers. update()'s LeftClick arm now matches that intent and fires the one
matching host effect, so every host call (new_tab, close_tab_with_index,
focus_terminal_pane, switch_tab_to) lives in exactly one place. Deletes the
now-redundant focus_or_switch_at / switch_to_tab_at methods; the focus/no-op
dispatch is covered by driving update() directly, and route_click's five arms
by a dedicated router test. load() and the permission flow are untouched.

The only off-wasm-uncovered line stays the new_tab dispatch arm (new_tab reads
stdin, so it cannot run natively) — its routing decision is covered in router.

Part of #89.
Copilot AI review requested due to automatic review settings June 20, 2026 05:31
@GeneralD GeneralD self-assigned this Jun 20, 2026
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@GeneralD, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 35 minutes and 17 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8af125d9-27e7-4cd5-b277-3e8201c7ba6c

📥 Commits

Reviewing files that changed from the base of the PR and between c289641 and 0f17f46.

📒 Files selected for processing (2)
  • .claude/rules/zellij-plugin-development.md
  • src/theme.rs
📝 Walkthrough

Walkthrough

Two new crate-internal modules are introduced: src/router.rs provides pure click routing (hit-test predicates, TabPaneGeom, ClickIntent, and route_click), and src/theme.rs provides palette_from_style. src/lib.rs is updated to declare these modules, migrate State::tab_panes to router::TabPaneGeom, rewrite click dispatch through router::route_click, and remove five replaced in-file helpers and their associated tests.

Changes

Router and Theme Extraction

Layer / File(s) Summary
Theme palette adapter
src/theme.rs
Adds a private rgb helper and palette_from_style that converts a zellij Style into a renderer color::Palette using multiplayer_user_colors for slots, frame_highlight for the ring seed, and exit_code_error for the alert color. Unit tests verify slot ordering and ring derivation.
Router contracts: TabPaneGeom, predicates, ClickIntent
src/router.rs
Defines TabPaneGeom to store per-render minimap geometry, adds three hit-test predicates (pane_at, clicked_new_tab_button, clicked_close_button), and introduces the ClickIntent enum enumerating all routing outcomes.
route_click orchestration and router tests
src/router.rs
Adds route_click which applies a fixed priority order (new-tab button → close cell → pane focus → tab switch → NoOp), plus an extensive off-wasm test suite covering all predicates and priority resolution edge cases.
lib.rs wiring: module declarations, State update, helper removal
src/lib.rs
Declares pub(crate) mod router and updates pub(crate) mod theme; migrates State::tab_panes to router::TabPaneGeom; rewrites update() to dispatch via router::route_click and router::ClickIntent; updates render() geometry construction; removes five replaced in-file helpers and their associated tests.

Sequence Diagram(s)

sequenceDiagram
  participant Plugin as ZellijPlugin::update()
  participant Theme as theme::palette_from_style
  participant Router as router::route_click
  participant Host as Zellij Host

  Plugin->>Theme: palette_from_style(&mode_info.style)
  Theme-->>Plugin: color::Palette
  Plugin->>Router: route_click(button_layout, close_layout, tab_layout, tab_panes, row, col)
  Router-->>Plugin: ClickIntent
  alt ClickIntent::NewTab
    Plugin->>Host: new_tab()
  else ClickIntent::CloseTab(idx)
    Plugin->>Host: close_tab_with_index(idx)
    Plugin->>Plugin: clear close_layout
  else ClickIntent::FocusPane(id)
    Plugin->>Host: focus_pane(id)
  else ClickIntent::SwitchTab(n)
    Plugin->>Host: switch_tab_to(n)
  else ClickIntent::NoOp
    Plugin->>Plugin: no action taken
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • GeneralD/zellij-tabmap#79: Implements the minimap-pane click hit-testing via tab_panes geometry that this PR refactors into the new router module.
  • GeneralD/zellij-tabmap#81: Adds the "+" new-tab button line::ButtonHit-based recording that this PR migrates into router::clicked_new_tab_button and router::route_click.
  • GeneralD/zellij-tabmap#53: Adds the src/lib.rs unit tests for click-geometry and pane-routing behavior that this PR replaces with router-module-driven equivalents.

Poem

🐇 Hop hop, the router's born today,
No more click-helpers in lib's fray!
TabPaneGeom now in its own nest,
ClickIntent dispatches each request.
Pure logic, tested, clean as a carrot—
The warren grows tidy, every pixel! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: refactoring lib.rs by extracting theme and router modules.
Linked Issues check ✅ Passed The PR implements the incremental extraction approach from #89, isolating click-routing logic and theme-adapter boundaries while maintaining test coverage and preserving module APIs.
Out of Scope Changes check ✅ Passed All changes align with #89's goal of refactoring lib.rs into layered modules; no unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/89-lib-architecture

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.77049% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/lib.rs 90.62% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the crate’s src/lib.rs by extracting two focused, unit-testable modules—theme (zellij theme → renderer palette adapter) and router (pure click intent routing)—so zellij-host effects are dispatched in one place while pure decisions remain dependency-free.

Changes:

  • Added src/theme.rs to isolate theme-to-color::Palette conversion (and its tests) from lib.rs.
  • Added src/router.rs to consolidate click hit-testing into a pure route_click function returning a single ClickIntent.
  • Updated src/lib.rs to use theme::palette_from_style and to dispatch mouse left-click host effects via router::route_click.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/theme.rs New adapter module converting Stylecolor::Palette plus migrated test coverage.
src/router.rs New pure click-routing module (ClickIntent + route_click) with focused unit tests.
src/lib.rs Wires in the new modules; centralizes left-click host-effect dispatch via the router.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/theme.rs Outdated
GeneralD and others added 2 commits June 20, 2026 14:55
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@GeneralD GeneralD merged commit f6e082c into main Jun 20, 2026
6 checks passed
@GeneralD GeneralD deleted the refactor/89-lib-architecture branch June 20, 2026 10:24
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.

refactor: restructure lib.rs with a proper architecture (coverage ≥ 100%)

2 participants