refactor(gui): restructure apic-gui into feature slices with a design-system UI layer - #71
Merged
Merged
Conversation
Colors and spacing become separate files under ui/theme/, with TABLE_HEADER_H and TABLE_ROW_H moved in from widgets.rs and their two consumers repointed. method_color and method_badge stay put until features/contracts/ exists; typography lands in the next commit, when its tokens gain call sites. No visual change.
Button, input, text, chip, and table primitives become separate modules. Focus plumbing moves to ui/focus.rs since it renders nothing. The unused weighted_columns helper is dropped rather than carried into a new file. ui/theme/typography.rs lands here, where its size tokens gain call sites. No visual change.
All nine section functions take apic_core's EditModel, so they were never shared UI. method_color and method_badge move with them. ui/ now names no domain type. No visual change.
root, project_root, apic_dir, status, and sidebar_open move into a ShellState struct with a hand-written Default so sidebar_open keeps starting true. DialogKind moves to app/state.rs alongside it. App drops from 24 fields to 20. No behavior change.
The remaining 18 contract fields move into ContractsState, alongside Entry, Repair, DeleteTarget, MainTab, and RespTab. App is now three fields: shell, contracts, and the in-flight file dialog. TreeNode stays in main.rs until the next commit moves it with the sidebar it renders. No behavior change.
main.rs is now startup only, 60 lines down from 1756. App, the eframe impl, and the bars move to app/mod.rs; project operations to app/project.rs; SidebarAction to app/actions.rs; the contract sidebar, central view, dialogs, and TreeNode to features/contracts/view.rs; the Windows renderer test to desktop.rs. TreeNode lands in view.rs rather than state.rs because it renders and needs SidebarAction. The dispatch match stays inside ui(), which also does panel layout, so extracting it would not have been a pure move. No behavior change.
The module split widened pub(crate) across every method at impl-block indentation, including 8 that were private before and a #[test] fn. Reverted all method visibility to private, then granted pub(crate) back to only the 19 the compiler named as cross-module callers. Also drops now-stale pub(crate) from the nine section functions plus method_color and method_badge: every caller moved into view.rs when central() did, so they are file-local. No behavior change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Restructures
apic-guiinto feature slices with a design-system UI layer, so that adding a Git GUI (and whatever comes after) is an addition rather than another untangling. Behavior preserving throughout: no visual change, no dependency change, no new features.Why
main.rswas 1756 lines holding theAppstruct, every project operation, every modal dialog, the contract tree, and the whole central view. Theui/layer documented an invariant it did not keep, all 9 exported functions inui/sections.rstookapic_core'sEditModel, andui/theme.rsexportedmethod_color/method_badge, which switch on HTTP verbs. A second feature would have had to add fields to the same flat struct with no clean place for its view code.What changed
main.rsgoes from 1756 lines to 60, startup only.Appgoes from 24 fields to 3:New layout:
Layering invariant, stated in
ui/mod.rsandfeatures/mod.rs:components/may importtheme/, never the reverse;ui/imports nothing fromfeatures/and names no domain type; feature slices do not import each other.The success test: adding
git: GitStatenow touchesapp/mod.rsand the dispatch match, nothing else.Commits
Seven commits, each behavior preserving and independently revertable, with the full gate green between each.
1c5ffe3splitui/theme.rsinto a theme token modulef947407splitui/widgets.rsintoui/componentsandui/focusfe151d3move contract sections intofeatures/contracts/viewd570bcfextractShellStatefromApp9641244extractContractsStatefromApp0499848split app modules out ofmain.rs4a3782anarrow visibility to what callers actually needVerification
Gate run after every commit:
All green, 258 tests across 6 suites.
cargo run -p apic-guiwas smoke tested by hand twice, after commit 3 and after commit 6, covering open project, browse the sidebar tree, load a contract, edit and cancel, save, create a request from a template, create a template, delete, toggle the sidebar, and the Import menu.Two things the automated gate cannot cover, recorded for reviewers:
windows_defaults_to_wgpu_rendereris#[cfg(windows)]and moved todesktop.rs. Thex86_64-pc-windows-msvctarget is not installed locally, so this move is unverified on Linux and relies on CI.apic-guitests assert "renders without panicking", never pixels. Theopen_blocked_dialogandenter_repairpaths, the invalid contract flows, were not exercised in either smoke test.Deliberate deviations
weighted_columnshelper (#[allow(unused)], zero call sites) was deleted rather than moved into a newui/components/layout.rs.TreeNodelives infeatures/contracts/view.rsrather thanstate.rs, because it renders and needsSidebarAction.app/actions.rsholds only theSidebarActionenum. The dispatch match stayed insideui(), which also does panel layout, so extracting it would not have been a pure move.Apptests stayed inapp/mod.rsrather than moving to the contracts feature, since both constructApp::new().Follow-ups, deliberately not in this PR
features/contracts/view.rsis 1373 lines, now the largest file in the crate. Splitting it is the natural next step.impl Apprather thanfn view(&mut ContractsState) -> Option<ContractsAction>, because they readself.shell.statusalongsideself.contracts. Avibekit:comment inview.rsnames the upgrade path. Best done when the Git feature supplies a second consumer to generalize from.pending_dialogis its only consumer today; Git will be the second, and that is the point to build it.ShellState::default().sidebar_open == true, the one hand written default in the crate, which would fail silently if flipped.