Skip to content

Migrate the application UI to Preact in measured simplification phases #578

Description

@BorisTyshkevich

Status and dependency

Blocked on the architecture decision in #577.

This is the umbrella issue for a full application UI migration only if #577 concludes that Preact will make the production application smaller and simpler. The phase boundaries, measurements, and retained modules must be updated from the ADR before implementation begins.

Goal

Migrate suitable UI composition and lifecycle ownership to Preact in several bounded phases while making the final production application shorter, less imperative, and easier to reason about.

The migration is not successful merely because the application renders through Preact. It must delete replaced vanilla lifecycle code and reduce total production UI complexity.

Success definition

At completion:

  • production UI TypeScript/JavaScript LOC is lower than the documented baseline, excluding tests, docs, generated files, and temporary migration tooling;
  • app-shell.ts is deleted or reduced to a narrow bootstrap/integration boundary;
  • each DOM subtree has one rendering owner;
  • manual DOM synchronization, listener cleanup, and cross-effect presentation coordination are materially reduced;
  • there is no permanent parallel vanilla/Preact implementation for migrated regions;
  • proven domain reducers, controllers, persistence, schemas, generation tokens, and async validity rules are retained unless separately justified;
  • the single-file distribution model still works;
  • bundle, startup, memory, accessibility, and browser behavior remain within the limits selected in Evaluate Preact migration as an application-wide simplification #577;
  • Chromium and WebKit pass the full interaction matrix.

A migration that increases production UI code while retaining equivalent lifecycle complexity does not meet this issue’s goal.

Migration principles

  1. Delete while migrating. A phase is incomplete until the replaced renderer, effects, listeners, and disposal paths are removed.
  2. One owner per subtree. Vanilla code and Preact must never mutate the same DOM region.
  3. Keep domain logic outside components. Pure reducers, controllers, schemas, persistence, and reusable state machines remain framework-independent.
  4. Use narrow imperative adapters. CodeMirror, Chart.js, virtualized grids, and similar libraries mount, update, and dispose behind explicit component boundaries.
  5. Avoid state churn. Keep Signals or existing controllers where they remain the simplest source of truth; do not rewrite state only to match framework conventions.
  6. Preserve distribution. Every phase must continue to produce the self-contained application artifact.
  7. Measure every phase. Record production LOC removed/added, bundle impact, deleted ownership paths, and browser test coverage.
  8. No migration-only redesign. Visual or product redesign requires its own scope.

Proposed phases

The final boundaries must follow the #577 ADR, but the expected migration sequence is:

Phase 0 — decision, baseline, and migration map

  • Complete Evaluate Preact migration as an application-wide simplification #577 and record the ADR.
  • Define the exact production LOC baseline and counting script/method.
  • Define bundle, startup, and browser-performance budgets.
  • Classify modules as retain, adapt, migrate, replace, or delete.
  • Define DOM ownership boundaries and the temporary interoperability rule.
  • Confirm how Signals integrate with Preact.
  • Confirm how the single-file build bundles Preact and JSX/TSX.

Exit criterion: the migration is approved with measurable simplification targets. Otherwise close this issue as not planned.

Phase 1 — Preact bootstrap and shell ownership

  • Add the minimal Preact build/runtime integration.
  • Establish one application root and explicit non-Preact mounting islands where temporarily required.
  • Move shell geometry, header slots, centre host selection, mobile projection, and top-level disposal into declarative ownership.
  • Prove workspace switch, sign-out, remount, and repeated disposal without leaked listeners or stale callbacks.
  • Delete the replaced shell rendering/effect paths.

Exit criterion: the shell has one owner, the single-file build works, and production code for the migrated shell is smaller than the replaced implementation.

Phase 2 — left navigation and completion of #487

Exit criterion: #487 is complete, and the migrated navigation has fewer production lines and lifecycle ownership points than the implementation it replaces.

Phase 3 — right inspector and #488

Exit criterion: one right-inspector implementation exists, migrated tool code is smaller than the removed desktop composition code, and no stale result-bound content can appear.

Phase 4 — centre surfaces and shared application chrome

  • Migrate Query/Dashboard host composition and shared toolbars where it removes lifecycle complexity.
  • Put CodeMirror, results grids, charts, and Dashboard layout engines behind narrow adapters without rewriting their domain state.
  • Migrate mobile navigation and shared shell chrome.
  • Verify resizing, fullscreen/detached views, query execution, and Dashboard execution across mount and surface transitions.
  • Delete replaced vanilla render/effect paths.

Exit criterion: shell and centre composition have declarative ownership, imperative integrations have bounded adapters, and no feature has dual DOM ownership.

Phase 5 — remaining UI regions

  • Inventory remaining manually rendered regions.
  • Migrate only regions where Preact removes more code and lifecycle complexity than it adds.
  • Keep small isolated imperative modules when they are demonstrably simpler.
  • Consolidate reusable tabs, disclosures, menus, drawers, and dialog composition without creating an oversized internal component framework.
  • Delete obsolete builders and shared chrome that no longer have callers.

Exit criterion: all remaining exceptions are documented and intentionally retained.

Phase 6 — cleanup, proof, and final cutover

  • Remove temporary interoperability bridges, feature flags, duplicate CSS, dead dependencies, and migration helpers.
  • Run architecture-boundary and dead-code checks.
  • Recalculate production UI LOC and lifecycle metrics using the Phase 0 method.
  • Compare final bundle/startup/runtime measurements with the approved budgets.
  • Run the full Chromium/WebKit interaction and accessibility matrix.
  • Update architecture, development, roadmap, and changelog documentation.

Exit criterion: the final production application is measurably shorter and simpler than the baseline, and there is no permanent migration scaffolding.

Required per-phase report

Each phase PR must include:

  • files and ownership boundaries migrated;
  • production LOC removed and added;
  • replaced listeners/effects/disposers deleted;
  • bundle delta;
  • retained domain modules;
  • temporary bridge introduced, owner, and removal phase;
  • browser tests added or updated;
  • rollback boundary;
  • confirmation that no DOM subtree has dual ownership.

Focus and accessibility

Preact is not itself the focus policy. Structural transitions must still satisfy the product behavior established in #487 and #488:

  • semantic intent is captured before a focused subtree is removed;
  • stale transition work is cancelled;
  • the destination is resolved after rendering/layout;
  • a newer user focus is not stolen;
  • modal and non-modal surfaces preserve their distinct contracts;
  • real-browser tests, not DOM-only tests, prove the behavior.

The exact implementation—component lifecycle, hook, scheduler plus policy, or small service—must follow the #577 decision and should not become a global focus manager unless evidence requires it.

Acceptance criteria

Non-goals

  • Rewriting the backend or ClickHouse-serving model.
  • Replacing CodeMirror, Chart.js, or other mature libraries solely because of the migration.
  • Moving workspace or portable document state into component-local persistence.
  • Redesigning features while migrating them.
  • Treating total repository LOC, including added tests, as the simplification metric.
  • Forcing every tiny imperative helper into a component when the result would be larger or less clear.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorRestructuring without user-facing behavior change

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions