Files
hcie-rust-v3.05/.kilo/plans/1783517996924-egui-app-refactoring-plan.md
T
2026-07-09 02:59:53 +03:00

12 KiB
Raw Blame History

HCIE-egui-app Refactoring Plan

Goal: Reduce spaghetti score from 7.5/10 to ~3/10 by splitting god files, removing dead code, standardizing architecture.

Constraints:

  • Each phase must compile (cargo check) and pass existing tests (cargo test)
  • Do NOT change any business logic
  • Do NOT touch locked engine crates (Layer 15)
  • All new files go in hcie-egui-app/crates/hcie-gui-egui/src/
  • Panel crates go in hcie-egui-app/crates/

Phase 0: Baseline & Preparation

Files: hcie-egui-app/Cargo.toml, test files.

  1. Run full test suite, record pass/fail:
    cargo test -p hcie-gui-egui 2>&1 | tee /tmp/baseline_test.log
    cargo clippy -p hcie-gui-egui 2>&1 | tee /tmp/baseline_clippy.log
    
  2. Remove direct engine crate deps from hcie-egui-app/Cargo.toml:
    • Remove hcie-protocol, hcie-io, hcie-color, hcie-blend entries
    • If any crate in workspace actually uses them, add the dep only there
  3. Verify compile with cargo check -p hcie-gui-egui

Phase 1: Remove Grimdock Backend

Files to delete:

  • hcie-egui-app/crates/hcie-gui-egui/src/app/grim_dock.rs

Files to modify:

  • hcie-egui-app/crates/hcie-gui-egui/src/app/mod.rs:
    • Remove pub mod grim_dock;
    • Remove use_grimdock: bool field from HcieApp
    • Remove grim_dock_host: Option<GrimDockHost> field
    • Remove grim_floating_panels field
    • Remove all if self.use_grimdock { ... } else { ... } branches in drain_events, ui_main_assembly, HcieApp::new
    • Remove default_dock_state() calls guarded by grimdock toggle
    • Simplify drain_events DocClosed handler (remove grimdock branch)
  • hcie-egui-app/crates/hcie-gui-egui/src/app/dock_controller.rs:
    • Remove any grimdock references
  • hcie-egui-app/Cargo.toml:
    • Remove grimdock dependency if present
  • Any test files referencing grimdock

Verification: cargo check -p hcie-gui-egui && cargo test -p hcie-gui-egui


Phase 2: Extract State Structs from app/mod.rs

Create app/state/ module. Move struct definitions out of mod.rs.

2a — app/state/mod.rs

pub mod app_state;
pub mod document;
pub mod tool_state;

2b — app/state/document.rs

Move AppDocument struct + impl (lines ~431541) exactly as-is. Re-export from state::document.

2c — app/state/tool_state.rs

Move ToolState struct + impl (lines ~546950) exactly as-is. Includes TextEditState, AiChatMessage, AiChatConfig, ComfyStatus, ComfyConfig, ComfyMode, VisionTask, VisionBackend, SelectionTransform, TransformHandle, Settings.

2d — app/state/app_state.rs

Move HcieApp struct + fields (lines ~9601095), SaveErrorState (lines ~10971108), AppMode, constants (TOOL_SLOTS, PANEL_SIDES).

Replace in app/mod.rs

  • Remove moved struct definitions
  • Add mod state; and pub use state::*;
  • Update all references to use new paths

Verification: cargo check -p hcie-gui-egui && cargo test


Phase 3: Extract Event Handlers from drain_events

Rationale: The 1,475-line drain_events function handles 60+ event variants inline. Each domain gets its own handler file.

Create app/events/ module:

Files:

File Handles AppEvent variants
app/events/mod.rs Dispatch function + EventError type
app/events/layers.rs LayerAdd, LayerDelete, LayerSelect, LayerDuplicate, LayerMerge, LayerFlatten, LayerClear, LayerMoveUp/Down, SetLayerOpacity, SetLayerBlend, ToggleLayerVisibility
app/events/file.rs DocCreated, DocClosed, FileOpen, FileSave, FileSaveAs, FileExport, FileReload
app/events/selection.rs SelectionGrow, SelectionShrink, SelectionFeather, SelectionInvert, SelectionDeselect, SelectionFromLayer
app/events/vision.rs VisionSmartSelect, VisionSegmentAnything, VisionInpaint, VisionSuperResolution, VisionBackgroundRemove
app/events/ai.rs AiChatSend, AiChatStop, AiActionRequest, AiToolCall
app/events/edit.rs ClipboardCopy, ClipboardCut, ClipboardPaste, DeleteLayerContent, ClearSelection, TransformStart/Apply/Cancel
app/events/view.rs ZoomIn, ZoomOut, ZoomReset, PanReset, ToggleGrid, ToggleGuides, ThemeChanged

app/events/mod.rs pattern:

pub fn dispatch_event(app: &mut HcieApp, ctx: &egui::Context, event: AppEvent) {
    match event {
        AppEvent::LayerAdd { .. } => layers::handle_layer_add(app, ctx, ...),
        AppEvent::FileSave => file::handle_file_save(app, ctx),
        // ... one match arm per variant, delegating to handler fn
    }
}

Replace drain_events body:

Collapse the 1,475-line function to:

pub fn drain_events(&mut self, ctx: &egui::Context) {
    let mut count = 0;
    while let Some(event) = self.event_bus.pop() {
        count += 1;
        if count > 100 { break; }
        let _plugin_actions = self.plugin_host.dispatch_app_event(&event, &mut self.state);
        events::dispatch_event(self, ctx, event);
    }
}

Verification: cargo check -p hcie-gui-egui && cargo test -p hcie-gui-egui


Phase 4: Extract Lifecycle & Shortcuts from app/mod.rs

app/lifecycle.rs

  • HcieApp::new(cc, tablet_state, panel_screenshot_request) (~315 lines)
  • HcieApp::logic(ctx) (~165 lines)
  • enforce_constraints(...) (~202 lines)

app/shortcuts.rs

  • HcieApp::handle_shortcuts(ctx) (~195 lines)

app/theme.rs

  • apply_theme(ctx, theme) function
  • setup_custom_fonts(ctx) function
  • ThemeColors struct and ThemePreset enum
  • default_font_bytes() function
  • PANEL_SIDES constant

app/dock_layout.rs

  • default_dock_state() (~30 lines)
  • pane_from_name() (~15 lines)

After extraction, app/mod.rs should be ~400-600 lines (public module declarations, re-exports, drain_events function, ui_main_assembly).

Verification: cargo check -p hcie-gui-egui && cargo test


Phase 5: Split panels.rs (1,658 lines → individual panel crates)

Decision: Each major panel becomes a separate crate in hcie-egui-app/crates/.

New crates to create:

Crate Source code from panels.rs Approx lines
egui-panel-menubar show_menu_bar() + helpers ~120
egui-panel-toolbar show_unified_toolbar() + helpers ~250
egui-panel-statusbar show_status_bar() + helpers ~80
egui-panel-layers show_layers_ui() + helpers ~400
egui-panel-history History panel section ~80
egui-panel-properties Properties panel section ~350
egui-panel-plugins show_plugins_ui() ~80

Each crate follows:

crates/egui-panel-<name>/
├── Cargo.toml     (depends on hcie-engine-api, egui)
└── src/
    └── lib.rs     (exports pub fn show(…))

app/panels.rs after extraction:

Becomes a thin re-export module (~40 lines).

Update:

  • hcie-egui-app/Cargo.toml — add new crate deps
  • All call sites panels::show_menu_bar(app, ctx)egui_panel_menubar::show(app, ctx)

Verification: cargo check -p hcie-gui-egui && cargo test


Phase 6: Split canvas/mod.rs (1,800 lines)

Files to create in canvas/:

File Content Est. lines
canvas/navigation.rs Zoom computation, scroll-wheel zoom, middle-mouse pan ~200
canvas/drawing.rs Stroke handling, brush/pen input, pressure mapping ~400
canvas/selection.rs Lasso selection drawing, marching ants, selection rect ~250
canvas/text.rs Text overlay editing canvas ~150
canvas/interaction.rs Pointer event dispatch, tool routing, context menu ~300

After extraction, canvas/mod.rs contains only CanvasWidget struct + ui() entry point that delegates to sub-modules.

Verification: cargo check -p hcie-gui-egui && cargo test && cargo test -p hcie-gui-egui --test canvas_engine


Phase 7: Remove Dead Code

  1. Remove #![allow(dead_code)] from these files one at a time and fix what the compiler flags:

    • app/mod.rs
    • app/panels.rs
    • app/grim_dock.rs (already deleted in Phase 1)
    • app/dock.rs
    • app/dialogs.rs
    • app/gui_layout.rs
    • app/viewer.rs
    • event_bus.rs
    • plugin.rs
  2. Remove unused modules:

    • app/custom_loaders.rs (if superseded by custom_loaders_kra_v2.rs)
    • app/panel_layout.rspanel_layout.json if layout-driven is ready
    • app/scrollable_panel.rs if nothing imports it
  3. Remove dead code inside live files (commented-out blocks, TODO stubs marked dead)

Verification: cargo check -p hcie-gui-egui && cargo clippy -p hcie-gui-egui


Phase 8: Fix Panic Points

Replace unwrap() / expect() calls with proper error handling:

  1. 30 unwrap() calls — audit each:

    • Mutex::lock().unwrap() → if not poison-poison, use let Ok(guard) = ... else log::error! + continue
    • HashMap::get().unwrap().unwrap_or_else(|| { log::warn!; default })
    • Vec::last().unwrap() → pattern match or if let Some
  2. 24 expect() calls — same treatment with informative error messages in expect() or convert to Result

  3. Priority order: event handlers (user-triggered) > startup > tests

Verification: cargo check -p hcie-gui-egui && cargo test


Phase 9: Standardize Panel Architecture

After Phase 5, all panels are separate crates. Now:

  1. Define a Panel trait in hcie-gui-egui:

    pub trait Panel {
        fn id(&self) -> &'static str;
        fn title(&self) -> &'static str;
        fn show(&mut self, app: &mut HcieApp, ui: &mut egui::Ui);
    }
    
  2. Wrap each panel crate's show function in a struct implementing Panel.

  3. Register panels in a PanelRegistry owned by HcieApp, replacing the manual match dispatch in dock.rs/HcieTabViewer.

This unifies how panels are discovered, rendered, and toggled.

Verification: cargo check -p hcie-gui-egui && cargo test -p hcie-gui-egui --test dock_behavior --test invariant_guards


Dependency Graph (safe order)

Phase 0 (baseline)
    │
    ▼
Phase 1 (grimdock remove) ──► Phase 7 (dead code, partial)
    │
    ▼
Phase 2 (state extract)
    │
    ▼
Phase 3 (event handlers)
    │
    ▼
Phase 4 (lifecycle extract)
    │
    ▼
Phase 5 (panel crates) ──► Phase 9 (panel trait)
    │
    ▼
Phase 6 (canvas split)
    │
    ▼
Phase 7 (dead code, full)
    │
    ▼
Phase 8 (unwrap fix)

Phases 14 and 7 are sequential (mod.rs shrinks step by step). Phases 5, 6 can parallelize. Phase 8 is last because line numbers shift. Phase 9 depends on Phase 5.


Risk Mitigation

Risk Mitigation
app/mod.rs changes break imports everywhere Run cargo check after every file move; use IDE rename refactoring
drain_events extraction introduces subtle behavior changes Write characterization tests first: record (event → resulting state) pairs for each variant, then assert same behavior after extraction
Panel crate extraction breaks dock integration Keep egui-panel-* crates thin; pass &mut HcieApp so they have full access
unwrap() removal changes control flow Wrap each removal in debug_assert! + if let with log::error! — panic behavior becomes logged warning, but execution continues
Dead code removal removes something used only at runtime Run the app with cargo run after each removal pass; use #[cfg(not(debug_assertions))] for truly required dead code

Validation Gate (run after each phase)

cargo check -p hcie-gui-egui 2>&1
cargo clippy -p hcie-gui-egui 2>&1  # deny warnings
cargo test -p hcie-gui-egui 2>&1
# Dock-specific tests:
cargo test -p hcie-gui-egui --test dock_behavior --test invariant_guards
# Visual baseline (if CI has display):
cargo run -p hcie-gui-egui -- --screenshot-panel "Layers" /tmp/check_Layers.png