Files
road-compiler/.trellis/tasks/08-26-react-workbench-migration/design.md

3.0 KiB

Technical Design

Frontend Boundary

Create a Vite app under workbench/client/ (or a clearly scoped workbench/frontend/ directory) with React 19 and TypeScript. Keep Node/server code CommonJS and keep /api/* payloads unchanged. The production build should emit a deterministic directory consumed by workbench/server.js; development mode may use a Vite proxy to the workbench API.

OpenLayers Migration Strategy

  • Import OpenLayers modules directly from the installed ol package in TypeScript; remove the runtime /vendor/ol/*.js import map from the React build.
  • Keep one Map instance per mounted map component. A useOpenLayersMap hook creates it once after the container ref is available, registers interactions/listeners, and calls map.setTarget(undefined) plus listener cleanup on unmount.
  • Keep the layer registry in an adapter module/hook (map/layers.ts), created once with stable VectorSource and VectorLayer instances. React state changes update sources, styles, visibility, and selection overlays through explicit adapter methods rather than recreating the map.
  • Use typed GeoJSON/read helpers at the API boundary; preserve EPSG:4326 to EPSG:3857 conversion and current fit/selection behavior.
  • OpenLayers event callbacks publish typed selection/pointer events to React state. React panels render from that state and issue API mutations; they do not query or mutate DOM nodes owned by OpenLayers.
  • Import ol/ol.css from the Vite entry and keep map container dimensions in application CSS.

UI and State

  • Use shadcn/ui primitives for buttons, inputs, checkboxes, select, tabs/segmented filters, dialogs, sheets, alerts, and toast feedback. Keep the existing dense three-column workbench layout.
  • Define API/state types in src/types/ based on the actual /api/state and mutation payloads. Use a small typed client in src/lib/api.ts.
  • Use React hooks/context for session and editor state. Keep transient map selection and staged overrides separate from server state; avoid introducing a large state library unless the migration proves one necessary.
  • Split components by existing workflow boundaries: ImportScreen, WorkbenchShell, LayerPanel, DiagnosticsPanel, MapCanvas, RoadInspector, SignalEditor, and shared ui/ primitives.

Static Hosting

  • Update workbench/server.js to serve Vite index.html and hashed assets in production, with a safe fallback for the SPA route while retaining /vendor/* only for compatibility during transition.
  • Keep npm run road:workbench self-contained for production assets. Add Vite dev/proxy scripts without requiring the Node workbench server to become an ESM application.

Risks and Compatibility

  • OpenLayers owns imperative objects and must not be recreated on every React render; the hook/adapter boundary is mandatory.
  • Existing style functions close over selected road state. Convert them to adapter-managed mutable selection refs or call layer.changed() after selection updates.
  • The first migration should preserve behavior and layout; visual redesign is explicitly deferred.