Crusader_Decomp/docs/map_renderer/vue-refactor-plan.md
2026-03-30 00:19:01 +02:00

13 KiB

Vue Refactor Plan

Purpose

Refactor the map renderer from a large vanilla JavaScript application into a Vue application while preserving its two deployment modes:

  • Static mode for GitHub Pages and other read-only deployments
  • Dynamic local mode for catalog editing and local data updates

The refactor should reduce the size and complexity of the current src/public/app.js entrypoint without breaking the existing viewer behavior, static export workflow, or catalog editing workflow.

Current Baseline

The renderer already has a useful split between source files and deployment artifacts:

  • src/public/app.js currently orchestrates most of the browser app.
  • src/public/map-catalog-ui.js, src/lib/catalog.js, and related modules already isolate some catalog logic.
  • site/ contains the committed static bundle used for GitHub Pages.
  • package.json already exposes separate static and dynamic scripts.
  • The root README already documents static versus dynamic usage.

That means the migration should be an incremental extraction, not a from-scratch rewrite.

Non-Negotiable Requirements

  1. Keep a static deployable build that can run from committed artifacts with no file-system write capability.
  2. Keep a dynamic local build that can edit the Catalog and write changes back to disk.
  3. Preserve the existing viewer workflow during migration so the app remains usable at every step.
  4. Keep catalog editing opt-in and unavailable in static deployments.
  5. Avoid coupling the UI framework change to data format changes unless a boundary must move for architectural reasons.

Target Architecture

The Vue refactor should split the app into four layers:

  1. App shell and routing-like mode selection
  • Owns top-level mode detection: static versus dynamic.
  • Chooses the correct feature set, status messages, and edit affordances.
  • Remains thin and delegates real work to feature components and composables.
  1. View components
  • Replace the current procedural DOM wiring with Vue components for the map controls, viewport, catalog panel, tooltips, modal editor, and export actions.
  • Keep components small and purpose-specific.
  • Prefer composition over a single mega component.
  1. Composables and state services
  • Move map selection, build triggers, UI flags, and interaction state into composables or store-like modules.
  • Keep business rules out of templates.
  • Preserve existing helper modules where they already work.
  1. Data access adapters
  • Introduce a strict boundary between read-only data access and writable catalog access.
  • Static mode uses read-only adapters only.
  • Dynamic mode uses writable adapters for catalog updates and local server APIs.

Mode Model

The app should continue to behave as one codebase with two runtime modes.

Static Mode

  • Serves prebuilt assets from site/ or equivalent deployed output.
  • Allows viewing, filtering, inspecting, and downloading data.
  • Does not expose any actions that would write to disk or call write-enabled server endpoints.
  • Can still show the Catalog UI if the data is available, but all editing controls must be removed or disabled.

Dynamic Mode

  • Runs against the local Node server.
  • Can edit Catalog data through the UI.
  • Can write CSV updates to disk.
  • Can refresh or rebuild derived data as needed after edits.
  • Must preserve the same viewer experience as static mode wherever the data model overlaps.

Proposed Migration Phases

Phase 0: Freeze the boundary

Goal: identify the stable interfaces before moving UI code.

  • Inventory the current app.js responsibilities.
  • Group code into categories: rendering, state mutation, map loading, catalog updates, overlays, modals, downloads, and notifications.
  • Identify the minimum data contracts between the viewer, catalog, and scene APIs.
  • Decide which existing helper modules stay unchanged and which become composables or services.

Deliverable:

  • A written boundary map for the current app and a component breakdown for Vue.

Phase 1: Scaffold Vue without changing behavior

Goal: get Vue running while preserving the current app.

  • Create a Vue 3 application shell.
  • Move the current page layout into Vue root components.
  • Keep the existing data modules and API helpers in place.
  • Mount the viewer in Vue but continue using the current scene and catalog logic.
  • Add a compatibility layer so existing DOM-driven utilities can be replaced one section at a time.

Deliverable:

  • The app loads through Vue, but functionality remains equivalent to the current version.

Phase 2: Split the UI into feature components

Goal: remove the largest chunks of imperative DOM code.

  • Break the viewer into components such as header/control bar, map selector, viewport, catalog panel, export area, status area, and editor modal.
  • Move local UI state into Vue reactive state or composables.
  • Replace direct DOM updates with props, emits, and computed state.
  • Keep the map rendering and scene logic isolated so the UI migration does not change map semantics.

Deliverable:

  • The main app flow no longer depends on a monolithic procedural entrypoint.

Phase 3: Formalize static and dynamic adapters

Goal: separate read-only rendering from writable editing behavior.

  • Introduce a read-only adapter for static deploys.
  • Introduce a writable adapter for the local Catalog editor.
  • Make the app choose adapters based on mode, not scattered conditionals.
  • Ensure static mode cannot accidentally call writable endpoints.
  • Ensure dynamic mode can still perform all current editor actions.

Deliverable:

  • The same Vue UI can run in both modes without leaking write capability into static builds.

Phase 4: Migrate Catalog editing carefully

Goal: preserve catalog edit power while moving to Vue.

  • Port the current catalog editor interactions into Vue components.
  • Keep edit forms, validation, and confirmation behavior intact.
  • Preserve undo or session history behavior if it exists in the current app.
  • Verify that write operations remain local-only and mode-gated.
  • Confirm that static builds either hide the edit UI or render it inert.

Deliverable:

  • Catalog editing works in dynamic mode and is unreachable in static mode.

Phase 5: Clean up the old entrypoint

Goal: remove dead code and simplify maintenance.

  • Delete or shrink the legacy procedural bootstrap code only after feature parity is reached.
  • Move any remaining helpers into reusable modules or composables.
  • Remove duplicate state management paths.
  • Keep the public API surface stable where external scripts or tests depend on it.

Deliverable:

  • Vue owns the app; the old entrypoint is no longer the main coordination layer.

Suggested Component Breakdown

The exact component names can change, but the refactor should likely include:

  • AppShell: top-level mode detection, configuration, and page layout.
  • MapSelector: map and game selection, navigation, and load triggers.
  • ViewerViewport: canvas or scene host, pointer handling, and zoom state.
  • EditorToolbar: view toggles, overlays, and map display controls.
  • CatalogPanel: catalog browsing, download buttons, and edit affordances.
  • CatalogEditModal: edit form and validation.
  • StatusBar: loading, build, and error feedback.
  • TooltipOverlay: pinned or hover inspection UI.
  • DownloadActions: export buttons and static bundle downloads.

Data and State Boundaries

The refactor should make these boundaries explicit:

  • Scene loading state: selected map, build progress, current assets, and error state.
  • View state: zoom, pan, overlays, tooltip pinning, and selection.
  • Catalog state: active game, catalog entries, edit permissions, and pending writes.
  • Environment state: static versus dynamic mode, feature flags, and API base URLs.

Rule of thumb:

  • If state is shared across multiple UI regions, it belongs in a composable or store.
  • If state only matters inside one panel or modal, keep it local to that component.

Static Build Plan

The static build must remain a first-class deployment target.

  • Keep the committed site output deployable without server writes.
  • Make the Vue build emit a static artifact suitable for GitHub Pages.
  • Ensure all catalog editing UI is absent or disabled in the static bundle.
  • Keep download links and read-only inspection available.
  • Preserve the existing export workflow so the static bundle can be regenerated from local source assets.

Dynamic Build Plan

The dynamic build must preserve local editing capability.

  • Keep the local server as the source of truth for writable catalog changes.
  • Preserve file write paths and validation rules.
  • Continue to support local refresh or rebuild behavior after edits.
  • Keep the editing workflow explicit so the writable mode cannot be mistaken for the static deploy.

Suggested Repo Changes

The migration will probably require these changes:

  • Introduce a Vue source directory under map_renderer/src or a sibling frontend directory.
  • Add a Vue build pipeline and update package scripts.
  • Split the current DOM utility modules into composables, services, and small components.
  • Add a mode/config module that clearly states whether the app is static or dynamic.
  • Update export scripts so the static site still writes the correct committed artifacts.
  • Update the README with the new build and deployment flow once the migration is stable.

Risks

  • The biggest risk is mixing UI migration with behavior changes and losing parity.
  • The second risk is accidentally allowing edit controls into the static bundle.
  • The third risk is over-centralizing state in one large Vue store and recreating the same maintainability problem in a new framework.
  • The fourth risk is changing the export pipeline before the static and dynamic modes are fully separated.

Acceptance Criteria

The refactor is complete when all of the following are true:

  1. The app runs as a Vue application.
  2. The static bundle still deploys and remains read-only.
  3. The dynamic local build still supports Catalog editing and writes to disk.
  4. The main viewer flows match the current behavior closely enough that no feature regression is visible in normal use.
  5. The old procedural entrypoint is no longer the primary app coordinator.
  6. The codebase is easier to extend because UI, state, and data access are separated.
  1. Inventory the current app responsibilities and define component boundaries.
  2. Scaffold Vue and mount the existing app behind it.
  3. Extract the largest UI regions into Vue components.
  4. Introduce explicit static and dynamic data adapters.
  5. Port catalog editing into Vue while keeping write capability local-only.
  6. Remove the leftover procedural bootstrap code.
  7. Update the docs and deployment instructions.

Concrete Implementation Checklist

Phase 1 Checklist

  1. Add Vue 3 and Vite dependencies to the renderer package.
  2. Add Vite scripts for development and production builds without removing the existing Node server scripts.
  3. Create a new Vue entrypoint that mounts a root app shell into the current page layout.
  4. Keep the existing static HTML usable during the transition by preserving the current shell structure and mount target.
  5. Split the top-level UI into a Vue root component plus a small compatibility layer for existing state and API helpers.
  6. Keep read-only viewer behavior intact during the initial scaffold so the app still loads maps exactly as before.
  7. Confirm that static mode still hides all write-capable catalog controls.
  8. Confirm that dynamic mode still exposes catalog-edit affordances after the Vue shell is in place.

Phase 1 Exit Criteria

  1. The renderer can be built and served through Vue/Vite.
  2. The current viewer still loads and renders maps.
  3. The static deployment path remains read-only.
  4. The dynamic deployment path remains capable of catalog editing.
  5. The old entrypoint is no longer the only place that owns page-level structure.

Phase 1 Progress

  • Vue 3 and Vite scripts have been added to the renderer package.
  • A Vue shell now renders the existing viewer DOM structure.
  • The Vue shell loads the legacy browser app after mount so the current viewer logic remains active.
  • The Vue production build completes successfully.
  • The local dev and admin launchers now wait for the Vue build output and then start the server on the Vue-preferred path.

Phase 2 Progress

  • The Vue shell has been split into separate shell components for the side panel, viewport, and egg editor modal.
  • The root Vue app now composes those feature components instead of keeping the entire shell in one template file.
  • The default local runtime path now serves the Vue build when it exists, so the componentized shell is the main entry for dev and admin modes.

Final Notes

The best outcome here is not a perfect one-shot rewrite. It is a controlled migration that keeps the current app working, makes the static and dynamic deployment split explicit, and steadily moves the renderer toward a maintainable Vue architecture.