feat: Migrate embedded frontend from custom MVX framework to React 18#338
Merged
feat: Migrate embedded frontend from custom MVX framework to React 18#338
Conversation
Replace the custom MVX rendering framework (BaseComponent, StateStore, Compositor, MVX orchestrator) with React 18 functional components using an adapter pattern that preserves the existing public API. ## What changed - **Adapter pattern**: ReactCaptureMenu and ReactResultView implement the exact same public API as the original classes, using ExternalStore + useSyncExternalStore for state management instead of MVX state IDs - **32 React components** replace 31 MVX components across CaptureMenu (5 components) and ResultView (18 components + JSONRenderTree) - **Visual regression tests**: 38 Playwright screenshot tests ensure pixel-level visual parity with the original UI - **SCSS unchanged**: Same 3 SCSS files, same class names, same DOM structure — zero CSS changes needed - **Ace editor**: Wrapped with useRef/useEffect for proper lifecycle management (no StrictMode — incompatible with Ace) ## Architecture - ExternalStore<T> bridges imperative API → React reactivity - Adapter classes translate method calls → store.setState() → re-render - React components are purely presentational (read store, call handlers) - Context pattern passes adapter instance to component tree - JSONRenderItem discriminated union for recursive JSON tree rendering ## Bug fixes - hide() now actually works (MVX had truthy-check bug) - Removed dead IE11 navigator.msSaveBlob code path - Fixed falsy number rendering in VisualStateListItem (textureLayer=0) ## Bundle impact - Before: 667 KB (MVX + Ace) - After: 794 KB (React + ReactDOM + Ace) — +127 KB for React Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Popov72
approved these changes
Mar 20, 2026
TSLint fixes: - Add braces to all single-line if/else bodies (curly rule) - Add public visibility modifiers to ExternalStore members (member-access) - Remove unnecessary semicolons from arrow function properties (semicolon) - Add tslint:disable-next-line for react-dom/client submodule imports - Rename shadowed variable in SourceCode.tsx (no-shadowed-variable) - Use T[] syntax instead of Array<T> in types.ts (array-type) Dependency updates: - React 18 → 19, react-dom 18 → 19 - TypeScript 4.7 → 5.9 - webpack 5.73 → 5.105, webpack-cli 4 → 7 - css-loader 6 → 7, style-loader 3 → 4, sass-loader 13 → 16 - sass 1.53 → 1.98, ts-loader 9.3 → 9.5 - exports-loader 4 → 5, livereload 0.9 → 0.10 - @shaderfrog/glsl-parser 5 → 7 - @types/webxr 0.5.1 → 0.5.24 Webpack config fixes for breaking changes: - style-loader v4: replace query string syntax with options object - sass v1.98: add silenceDeprecations for @import warnings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…node v1 → v4 TypeScript 5.9 uses nullish coalescing (??) which requires Node.js 14+. The CI was running Node.js 12 which crashes on TypeScript's own source code. Also update GitHub Actions to v4 to avoid Node.js 20 deprecation warnings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace the custom MVX rendering framework with React 18 functional components, making the codebase contributor-friendly while preserving identical visual output.
What changed
Architecture
ReactCaptureMenuandReactResultViewimplement the exact same public API as the original classes —spector.tsjust swaps an importuseSyncExternalStore(no repeatedroot.render()anti-pattern)CaptureMenuContext/ResultViewContextJSONRenderTreerecursive renderer)Frontend files
src/embeddedFrontend/react/— all new React components and adapterssrc/embeddedFrontend/styles/— unchanged SCSS (same classes, same selectors)mvx/,captureMenu/,resultView/,ux/(old MVX code)Testing
Bug fixes
hide()now actually works (MVX had a truthy-check bug on the state object)navigator.msSaveBlobcode pathVisualStateListItem(textureLayer=0leaked as text)Documentation
spec/AI_WORKING_SPEC.md— comprehensive architecture doc for AI assistantsBundle impact
How to verify