Skip to content

feat(spa): vendor Vue frontend + emit bundle in both output modes#26

Open
ronaldtse wants to merge 7 commits into
mainfrom
feat/spa-frontend-bundle
Open

feat(spa): vendor Vue frontend + emit bundle in both output modes#26
ronaldtse wants to merge 7 commits into
mainfrom
feat/spa-frontend-bundle

Conversation

@ronaldtse

Copy link
Copy Markdown
Contributor

Summary

The native SPA pipeline landed without a frontend — ea spa produced a 405-byte shell with window.__SPA_SKELETON_URL__ set but no JS to mount, so opening the output rendered a blank page. Both SingleFileStrategy and ShardedMultiFileStrategy had the same hole.

Vendor the full lutaml-uml frontend (source + dist + build pipeline) so the ea gem ships with a working SPA bundle and no longer depends on lutaml-uml for rendering. Dist is committed so gem install ea gets a working SPA without needing npm at install time.

What landed

  • frontend/ copied from lutaml-uml/lutaml-uml@0.5.2 wholesale: src/, dist/, tests/e2e/, package.json, vite.config.ts, tsconfig.json, vitest.config.ts, package-lock.json (package renamed lutaml-uml-spaea-spa)
  • frontend/README.md documenting when/how to rebuild and the data contract the shell has to honour
  • .gitignore: /frontend/node_modules/ (local only)
  • .github/workflows/frontend.yml: CI check that rebuilds the bundle and fails if dist/ drifts from src/
  • lib/ea/spa/output/strategy.rb: FRONTEND_APP_JS / FRONTEND_STYLE_CSS constants + assert_frontend_bundle! helper that raises a clear error if the bundle is missing
  • lib/ea/spa/output/single_file_strategy.rb: inline <style> and <script> into the single HTML file (stays self-contained)
  • lib/ea/spa/output/sharded_multi_file_strategy.rb: copy app.js and style.css next to index.html; shell now has <link href="style.css"> and <script src="app.js" defer>
  • spec/ea/spa/output_strategy_spec.rb: assert the bundle (JS, CSS, reference) is present in both layouts

Test plan

  • All 6 output_strategy_spec.rb specs green (4 rewritten to assert bundle presence)
  • ea spa models/.../plateau_v5.0.qea --mode=sharded produces: index.html (487 B shell with <link>/<script src> tags), app.js (109 KB Vue IIFE), style.css (24.9 KB), skeleton.json, search.json, data/{packages,classes,enumerations}/*.json
  • All assets serve 200 OK via local HTTP server
  • app.js contains Vue markers (createApp, pinia, Vue.)

The native SPA pipeline landed without a frontend — `ea spa` produced
a 405-byte shell with `window.__SPA_SKELETON_URL__` set but no JS to
mount, so opening the output rendered a blank page. Both
SingleFileStrategy and ShardedMultiFileStrategy had the same hole.

Vendor the full lutaml-uml frontend (source + dist + build pipeline)
so the ea gem ships with a working SPA bundle and no longer depends
on lutaml-uml for rendering. Dist is committed so `gem install ea`
gets a working SPA without needing npm at install time.

What landed
- frontend/ copied from lutaml-uml/lutaml-uml@0.5.2 wholesale:
  - src/: Vue 3 + Pinia source (App.vue, components/, stores/, styles/)
  - dist/: pre-built app.iife.js (109 KB) + style.css (24.9 KB)
  - tests/e2e/: Vitest browser tests + reference screenshots
  - package.json / vite.config.ts / tsconfig.json / vitest.config.ts
  - package-lock.json (renamed package: lutaml-uml-spa → ea-spa)
- frontend/README.md documenting when/how to rebuild and the data
  contract the shell has to honour
- .gitignore: /frontend/node_modules/ (local only)
- .github/workflows/frontend.yml: CI check that rebuilds the bundle
  and fails if dist/ drifts from src
- lib/ea/spa/output/strategy.rb: constants for FRONTEND_APP_JS /
  FRONTEND_STYLE_CSS plus assert_frontend_bundle! helper that raises
  a clear error if the bundle is missing (e.g. running from a source
  checkout before npm build)
- lib/ea/spa/output/single_file_strategy.rb: inline <style> and
  <script> into the single HTML file so it stays self-contained
- lib/ea/spa/output/sharded_multi_file_strategy.rb: copy app.js and
  style.css next to index.html; shell now has <link href="style.css">
  and <script src="app.js" defer>
- spec/ea/spa/output_strategy_spec.rb: assert the bundle (JS, CSS,
  reference) is present in both layouts

Verified locally: `ea spa models/20260227_current_plateau_v5.0.qea
--mode=sharded` produces an output directory whose index.html loads
style.css + app.js + skeleton.json + search.json + data/ shards.
All 6 output-strategy specs green.
…ions

Three coupled fixes for the SVG rendering:

1. Type name resolution: XMI properties reference types via
   xmi:idref (EAID_...). Added a post-build resolve_type_names
   pass in Xmi::Adapter that looks up each EAID in the model's
   classifier index and replaces it with the actual name.
   'class: EAID_0F5E62CD_...' → 'class: gml:CodeType'.

2. Font size 0: EA uses fontsz=0 to mean 'no override' (use
   default). ExtensionStyleParser now returns 13 for zero values
   instead of computing (0 * 13 / 100) = 0px which made text
   invisible.

3. Dynamic text positions: header/divider/attribute y-positions
   are now computed from the actual font_size + header line count
   instead of hardcoded constants (17, 50, 68). Uses
   line_height = font_size + 4, divider at header_bottom + 8,
   attributes at divider + font_size + 5.
…lution

Consolidates all SVG fixes onto one branch:

1. Canvas translation: all coordinates translated to (0,0) origin
   matching EA's viewBox convention. Canvas computes min_x/min_y
   from element image_bounds union; translate_x/translate_y
   subtracts min + adds 10px padding.

2. ConnectorRouter: replaces the broken SX/SY/EX/EY delta-based
   routing with position-based orthogonal routing. Computes source
   attachment from EDGE code, target from opposite edge, L-shaped
   bend at intersection. No more zigzagging.

3. ExtensionStyleParser: strips whitespace from keys (EA uses
   leading space in style strings like ' DUID=...').

4. Type name resolution: post-build pass in Xmi::Adapter resolves
   EAID_... xmi:idref pointers to actual classifier names.
   'EAID_0F5E62CD...' → 'gml:CodeType'.

5. Font size: fontsz=0 now defaults to 13px instead of 0px (which
   made text invisible).

6. Dynamic text positions: header/divider/attribute y-positions
   computed from actual font_size + line count, not hardcoded.

Validated: element sizes match EA reference exactly. Connector
routing produces orthogonal L-shaped paths (no zigzagging). 185
diagrams render without errors across the full XMI.
Two changes that improve element alignment with EA reference:

1. Elements now render from logical bounds (element.bounds) not
   image_bounds. EA renders the logical rect; image_bounds were
   adding 25px padding that made our boxes larger than EA's.

2. Canvas PADDING reduced from 10 to 5. EA's per-diagram margin is
   approximately 5px; 10 was overshooting.

After these fixes, x-offsets are 0 for most elements (was 24).
Remaining y-offsets are due to the reference SVGs being generated
from a different version of the source XMI (element heights and
positions have drifted since the SVGs were exported).
Two changes that bring element alignment to near-perfect:

1. PADDING=10: EA uses a consistent 10px margin on all sides.
   Verified across 20 diagrams — all ALIGNED diagrams show dy=0
   with this setting. Waterway and tran:Square achieve dx=0, dy=0
   (pixel-perfect alignment with EA reference).

2. Element-only canvas union: removed connector waypoints from the
   canvas min_x/min_y computation. EA computes the canvas from
   element bounds only; including waypoints pulled the canvas
   origin off, causing spurious x/y offsets.

20-diagram alignment audit:
  Waterway:              dx=0..1 dy=0     PERFECT
  tran:Square:           dx=0..2 dy=0     PERFECT
  bldg:_BoundarySurface: dx=188  dy=0     (element count mismatch)
  tran:Railway:          dx=0..58 dy=-17  (minor)
  Others vary due to source XMI / reference SVG version drift
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant