Skip to content

Evaluate Preact migration as an application-wide simplification #577

Description

@BorisTyshkevich

Goal

Determine whether migrating the UI composition layer from manually managed DOM rendering to Preact will make the application materially smaller, simpler, and easier to maintain while preserving its deployment and behavior contracts.

This is a decision and evidence issue. It does not authorize a repository-wide migration by itself.

Context

The application is currently built from vanilla ES modules and bundled as one self-contained HTML application. It already uses @preact/signals-core, while shell presentation, element identity, visibility, listeners, disposal, focus restoration, mobile projection, and surface transitions are largely coordinated imperatively.

The left-navigation work in #487 and the planned right inspector in #488 expose the same pressure point: new shell features require increasingly complicated lifecycle and focus coordination. Before adding another custom lifecycle abstraction, evaluate whether Preact should become the UI composition layer.

Decision principle

Adopt Preact only if the completed migration is expected to reduce—not merely reorganize—the application’s production UI code and lifecycle complexity.

A successful direction must demonstrate:

  • fewer production TypeScript/JavaScript UI lines after replaced vanilla code is deleted;
  • a substantially smaller and less stateful app-shell.ts responsibility;
  • fewer manual hidden, dataset, replaceChildren, listener, effect, and disposer synchronization points;
  • one clear owner for each rendered DOM subtree;
  • no permanent duplicate vanilla and Preact implementations;
  • preservation of domain reducers, persistence, schemas, controllers, generation tokens, and other proven non-rendering logic;
  • preservation of the self-contained single-file build and deployment model;
  • acceptable bundle, startup, and runtime impact;
  • equal or better Chromium and WebKit behavior.

Tests, documentation, generated files, and temporary spike code are not counted against the production-code reduction target. The final merged production implementation is.

Evaluation scope

Build a disposable or narrowly mergeable representative vertical slice covering enough complexity to make the decision credible:

  1. Application shell geometry below the header.
  2. Existing left navigation in wide, rail, and focused-drawer presentations.
  3. One real multi-frame fold transition where the source focus can disappear and must settle on a semantic destination.
  4. A minimal right-inspector host with chevron, resize boundary, and one mock tool.
  5. Desktop/mobile projection.
  6. Query/Dashboard host switching.
  7. One imperative integration boundary, preferably CodeMirror or an equivalent lifecycle-heavy widget.

The slice must compare the same behavior in the current architecture and the proposed Preact architecture. Do not compare an incomplete Preact prototype against the full production behavior.

Focus and transition evaluation

The behavioral requirement from #487 remains fixed:

  • capture user intent before a structural transition can remove the focused source;
  • avoid restoration during intermediate drag frames;
  • settle after the destination is rendered and browser layout is stable;
  • cancel stale work;
  • do not steal a newer user focus;
  • do not leave focus on <body> merely because application chrome folded or changed presentation.

The evaluation must determine the simplest implementation boundary. Possibilities include a small hook, a component-owned transition lifecycle, a tiny scheduler plus feature policy, or a small imperative service. Do not assume a global focus coordinator is required before the comparison is complete.

Measurements

Record before/after values for the representative slice and project the whole-application result:

  • production UI LOC, with the exact file set and counting method documented;
  • app-shell.ts LOC and responsibility count;
  • manual DOM mutation sites;
  • explicit listener/effect/disposer ownership sites;
  • duplicated state or DOM ownership;
  • unit and real-browser test complexity;
  • uncompressed and compressed bundle impact;
  • build time and startup impact where material;
  • memory/listener cleanup under repeated mount, workspace switch, sign-out, and disposal;
  • accessibility and focus behavior in Chromium and WebKit.

LOC is a guardrail, not the only metric, but a final implementation that is larger and retains equivalent lifecycle complexity does not satisfy the simplification goal.

Architecture constraints

  • Keep pure domain and layout reducers DOM-free.
  • Keep Signals where they remain useful; do not rewrite state solely to resemble React examples.
  • Put imperative libraries behind narrow mount/update/dispose component boundaries.
  • Do not allow vanilla code and Preact to mutate the same subtree.
  • Do not introduce a long-lived second application shell behind a feature flag.
  • Do not wrap existing imperative renderers without deleting the lifecycle code the component replaces.
  • Preserve current schema, workspace, preference, portable-bundle, and share-link boundaries.
  • Preserve the single-file distribution model.

Deliverables

Acceptance criteria

  • The comparison covers both left-navigation and right-inspector-style shell transitions.
  • The single-file build is proven, not assumed.
  • Chromium and WebKit exercise the structural focus transition.
  • The report distinguishes product-specific domain logic from framework-like rendering plumbing.
  • The recommendation is supported by reproducible measurements.
  • If Preact is selected, the phased migration umbrella issue is updated with the chosen boundaries and metrics.
  • If Preact is rejected, the prototype is absent from product branches and shipped artifacts — archival evaluation refs/tags may be retained as reproducible evidence (amended 2026-07-30: the original wording said only "removed", which would have discarded the evidence exactly as the lost spike/preact-schema branch did) — and Add a drag-foldable desktop left navigation with rail and focused drawers #487/Unify cell detail, rows and documentation in a foldable desktop right inspector #488 receive a documented smaller alternative.

Non-goals

  • Completing the full Preact migration.
  • Redesigning the product or visual language.
  • Rewriting domain controllers, reducers, schemas, or persistence without independent justification.
  • Treating framework adoption as success without deleting replaced code.
  • Starting the full right-inspector implementation before the shell decision is recorded.

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