ea 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (548) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/Rakefile +3 -0
  4. data/TODO.complete/01-t-xref-parsing.md +91 -0
  5. data/TODO.complete/02-per-label-geometry.md +50 -0
  6. data/TODO.complete/03-object-properties-constraints.md +57 -0
  7. data/TODO.complete/04-operation-params.md +37 -0
  8. data/TODO.complete/05-pdata-flag-matrix.md +52 -0
  9. data/TODO.complete/06-emf-image-rendering.md +34 -0
  10. data/TODO.complete/07-auxiliary-tables.md +47 -0
  11. data/TODO.complete/10-mdg-stereotype-registry.md +40 -0
  12. data/TODO.complete/11-gml-xsd-generation.md +42 -0
  13. data/TODO.complete/13-shapescript-interpreter.md +28 -0
  14. data/TODO.complete/20-cli-diff.md +46 -0
  15. data/TODO.complete/21-cli-render.md +26 -0
  16. data/TODO.complete/22-cli-export.md +29 -0
  17. data/TODO.complete/23-cli-mdg.md +23 -0
  18. data/TODO.complete/30-headerlines-ocp-refactor.md +72 -0
  19. data/TODO.complete/31-public-send-cleanup.md +61 -0
  20. data/TODO.complete/32-spec-coverage.md +33 -0
  21. data/TODO.complete/33-blocked-parity-gaps.md +41 -0
  22. data/TODO.complete/36-stereotype-icon-wiring.md +32 -0
  23. data/TODO.complete/37-cli-lint.md +32 -0
  24. data/TODO.complete/38-cli-query.md +26 -0
  25. data/TODO.complete/39-cli-info.md +23 -0
  26. data/TODO.complete/40-ocl-evaluator.md +31 -0
  27. data/TODO.complete/41-round-trip-spec.md +18 -0
  28. data/TODO.complete/42-html-diff-report.md +18 -0
  29. data/TODO.complete/43-json-schema-export.md +20 -0
  30. data/TODO.complete/44-remaining-qea-tables.md +40 -0
  31. data/TODO.complete/45-shapescript-extensions.md +25 -0
  32. data/TODO.complete/46-xsd-consolidation.md +20 -0
  33. data/TODO.complete/47-headerlines-legacy-removal.md +20 -0
  34. data/TODO.complete/51-remove-respond-to.md +25 -0
  35. data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
  36. data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
  37. data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
  38. data/TODO.complete/55-wire-shapescript.md +20 -0
  39. data/TODO.complete/56-wire-emf-renderer.md +27 -0
  40. data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
  41. data/TODO.complete/58-diff-modifications.md +21 -0
  42. data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
  43. data/TODO.complete/60-curate-json-export.md +20 -0
  44. data/TODO.complete/61-plantuml-relationships.md +22 -0
  45. data/TODO.complete/62-changelog.md +17 -0
  46. data/TODO.complete/63-dependabot-fix.md +18 -0
  47. data/TODO.complete/STATUS.md +85 -0
  48. data/TODO.diagrams/01-svg-measurement-harness.md +31 -0
  49. data/TODO.diagrams/02-polygon-over-rendering.md +29 -0
  50. data/TODO.diagrams/03-text-over-rendering.md +30 -0
  51. data/TODO.diagrams/04-rect-under-rendering.md +33 -0
  52. data/TODO.diagrams/05-canvas-dimension-parity.md +51 -0
  53. data/TODO.diagrams/06-group-layering-parity.md +39 -0
  54. data/TODO.diagrams/07-text-content-fidelity.md +65 -0
  55. data/TODO.diagrams/08-qea-direct-pipeline.md +46 -0
  56. data/TODO.diagrams/12-connector-labels.md +40 -0
  57. data/TODO.diagrams/12-per-entity-svg-layer-ordering.md +24 -0
  58. data/TODO.diagrams/13-marker-polygon-vs-path-dispatch.md +20 -0
  59. data/TODO.diagrams/13-per-text-type-font-sizes.md +33 -0
  60. data/TODO.diagrams/14-center-header-text.md +33 -0
  61. data/TODO.diagrams/14-filter-diagram-frame-elements.md +18 -0
  62. data/TODO.diagrams/15-deep-dead-code-audit.md +47 -0
  63. data/TODO.diagrams/15-per-element-font-from-style.md +24 -0
  64. data/TODO.diagrams/16-canvas-marker-extent.md +30 -0
  65. data/TODO.diagrams/16-document-remaining-parity-gaps.md +91 -0
  66. data/TODO.diagrams/17-italic-abstract-classes.md +28 -0
  67. data/TODO.diagrams/17-remaining-parity-gaps.md +90 -0
  68. data/TODO.diagrams/18-canvas-from-image-bounds.md +20 -0
  69. data/TODO.diagrams/18-property-lookup-index.md +42 -0
  70. data/TODO.diagrams/19-connector-sx-sy-routing.md +27 -0
  71. data/TODO.diagrams/19-replace-respond-to-with-is-a.md +31 -0
  72. data/TODO.diagrams/20-split-labels-emitter.md +56 -0
  73. data/TODO.diagrams/20-visibility-glyph-font-encoding.md +27 -0
  74. data/TODO.diagrams/21-aggregation-navigability-arrow.md +21 -0
  75. data/TODO.diagrams/21-auto-generated-legend-block.md +65 -0
  76. data/TODO.diagrams/22-ghost-element-discriminator.md +57 -0
  77. data/TODO.diagrams/22-style-based-path-grouping.md +28 -0
  78. data/TODO.diagrams/23-marker-polygon-deduplication.md +47 -0
  79. data/TODO.diagrams/23-multiplicity-label-positioning.md +29 -0
  80. data/TODO.diagrams/24-marker-anchor-structural.md +35 -0
  81. data/TODO.diagrams/24-track-marker-anchors-structurally.md +66 -0
  82. data/TODO.diagrams/24-truncate-properties-to-fit-box.md +24 -0
  83. data/TODO.diagrams/26-phantom-general-parent-rendering.md +59 -0
  84. data/TODO.diagrams/27-path-grouping-mode-detection.md +43 -0
  85. data/TODO.diagrams/28-architecture-cleanup-batch.md +47 -0
  86. data/TODO.diagrams/30-decompose-elements-groups-for.md +52 -0
  87. data/TODO.diagrams/32-examples-coverage-batch.md +78 -0
  88. data/TODO.diagrams/36-instance-specification-model.md +42 -0
  89. data/TODO.diagrams/37-element-icon-decorations.md +41 -0
  90. data/TODO.diagrams/38-html-note-and-receptions.md +32 -0
  91. data/TODO.diagrams/39-property-stereotype-discriminator.md +40 -0
  92. data/TODO.diagrams/40-standalone-1-discriminator.md +38 -0
  93. data/TODO.diagrams/41-xmi-to-svg-pipeline.md +113 -0
  94. data/TODO.diagrams/42-close-all-parity-deltas.md +69 -0
  95. data/TODO.diagrams/43-marker-deduplication-strategy.md +54 -0
  96. data/TODO.diagrams/44-element-icon-decorations.md +34 -0
  97. data/TODO.diagrams/45-phantom-parent-class-boxes.md +40 -0
  98. data/TODO.diagrams/46-user-drawn-legend-swatches.md +44 -0
  99. data/TODO.diagrams/47-attribute-line-fidelity.md +72 -0
  100. data/TODO.diagrams/48-spec-coverage-critical-emitter-components.md +45 -0
  101. data/TODO.diagrams/49-spec-coverage-orchestrators.md +25 -0
  102. data/TODO.diagrams/50-examples-parity.md +85 -0
  103. data/TODO.diagrams/51-property-oy-gap-and-remaining-gaps.md +48 -0
  104. data/TODO.diagrams/52-marker-registration-architecture.md +89 -0
  105. data/TODO.diagrams/53-remaining-example-gaps.md +86 -0
  106. data/TODO.diagrams/54-display-name-nested-classes.md +40 -0
  107. data/TODO.diagrams/55-visibility-icon-discriminator.md +58 -0
  108. data/TODO.diagrams/56-package-contents-rendering.md +148 -0
  109. data/TODO.diagrams/57-instance-specification-model.md +100 -0
  110. data/TODO.diagrams/58-domain-model-note-text.md +67 -0
  111. data/TODO.diagrams/59-classes-arrow-discriminator.md +57 -0
  112. data/TODO.diagrams/60-testschema-association-marker.md +40 -0
  113. data/TODO.diagrams/61-legend-block-rendering.md +55 -0
  114. data/TODO.diagrams/61-plateau-parity-gaps.md +59 -0
  115. data/TODO.diagrams/62-ghost-element-rendering.md +56 -0
  116. data/TODO.diagrams/63-qualified-name-wrap.md +36 -0
  117. data/TODO.diagrams/64-property-stereotype-labels.md +22 -0
  118. data/TODO.diagrams/65-path-coordinate-frame.md +62 -0
  119. data/TODO.diagrams/67-path-coordinate-frame.md +62 -0
  120. data/TODO.diagrams/68-inherited-attributes.md +65 -0
  121. data/TODO.diagrams/69-package-anchor-conditional.md +29 -0
  122. data/TODO.diagrams/70-mdg-technology-support.md +68 -0
  123. data/TODO.diagrams/71-off-canvas-parent-ghost.md +45 -0
  124. data/TODO.diagrams/72-basic-attr-divider-paths.md +39 -0
  125. data/TODO.diagrams/73-plateau-polygon-gap.md +69 -0
  126. data/TODO.diagrams/74-plateau-rect-gap.md +22 -0
  127. data/TODO.diagrams/75-plateau-text-gap.md +39 -0
  128. data/TODO.diagrams/76-basic-poly-path-gaps.md +41 -0
  129. data/TODO.diagrams/77-basic-text-overrender.md +40 -0
  130. data/TODO.diagrams/78-headerlines-ocp-refactor.md +65 -0
  131. data/TODO.diagrams/79-architecture-backlog.md +65 -0
  132. data/TODO.diagrams/80-basic-triangle-polygons.md +51 -0
  133. data/TODO.diagrams/81-plateau-path-overrender.md +53 -0
  134. data/TODO.diagrams/82-appearance-canvas-offset.md +54 -0
  135. data/TODO.diagrams/83-3d-diagram-subtitle.md +58 -0
  136. data/TODO.diagrams/84-canvas-offset-mismatches.md +61 -0
  137. data/TODO.diagrams/85-phantom-property-overrender.md +95 -0
  138. data/TODO.diagrams/86-foreign-package-rule.md +62 -0
  139. data/TODO.diagrams/87-internal-tech-encryption.md +80 -0
  140. data/TODO.diagrams/EXAMPLES_STATUS.md +88 -0
  141. data/TODO.diagrams/README.md +69 -0
  142. data/TODO.diagrams/STATUS.md +73 -0
  143. data/TODO.fixup/01-default-grouped-paths.md +34 -0
  144. data/TODO.fixup/02-marker-consolidation-per-connector.md +20 -0
  145. data/TODO.fixup/03-diagram-font-inference.md +28 -0
  146. data/TODO.fixup/04-extract-bounds-calculator.md +25 -0
  147. data/TODO.fixup/05-marker-registry-ocp.md +32 -0
  148. data/TODO.fixup/06-connector-path-bend-routing.md +28 -0
  149. data/TODO.fixup/07-edge-attachment-formula.md +22 -0
  150. data/TODO.fixup/08-visual-diff-harness.md +28 -0
  151. data/TODO.fixup/09-extract-element-compartments.md +34 -0
  152. data/TODO.fixup/10-canvas-width-overshoot-fix.md +20 -0
  153. data/TODO.fixup/11-layer-sequencer-extraction.md +18 -0
  154. data/TODO.fixup/12-path-style-harmony.md +23 -0
  155. data/TODO.fixup/13-datatype-stereotype-fallback.md +28 -0
  156. data/TODO.fixup/14-ea-exe-reverse-engineering-notes.md +56 -0
  157. data/TODO.fixup/15-enumeration-literal-rendering.md +30 -0
  158. data/TODO.fixup/16-operation-compartment-rendering.md +42 -0
  159. data/TODO.fixup/17-path-count-calibration.md +27 -0
  160. data/TODO.fixup/18-polygon-count-calibration.md +32 -0
  161. data/TODO.fixup/19-rect-count-undershoot-investigation.md +40 -0
  162. data/TODO.fixup/20-stereotype-color-from-classifier.md +18 -0
  163. data/TODO.fixup/21-visibility-filter-for-properties.md +24 -0
  164. data/TODO.fixup/22-multiplicity-display-rule-calibration.md +25 -0
  165. data/TODO.fixup/23-diagram-frame-title-element.md +31 -0
  166. data/TODO.fixup/24-note-element-rendering.md +23 -0
  167. data/TODO.fixup/25-ea-exe-disassembly-findings.md +177 -0
  168. data/TODO.fixup/26-svg-renderer-architecture.md +78 -0
  169. data/TODO.fixup/27-svg-element-emitters-disassembly.md +135 -0
  170. data/TODO.fixup/28-geometry-parser-identification.md +57 -0
  171. data/TODO.fixup/29-qea-svg-xmi-triple-comparison.md +185 -0
  172. data/TODO.fixup/30-theme-system-stub.md +46 -0
  173. data/TODO.fixup/31-theme-value-object-registry.md +17 -0
  174. data/TODO.fixup/32-styleex-on-diagram-model.md +22 -0
  175. data/TODO.fixup/33-text-encoding-decimals-rotation.md +29 -0
  176. data/TODO.fixup/34-per-type-fill-via-theme.md +18 -0
  177. data/TODO.fixup/35-stroke-width-per-theme.md +7 -0
  178. data/TODO.fixup/36-qea-rect-coord-normalization.md +24 -0
  179. data/TODO.fixup/37-package-shape-rendering.md +22 -0
  180. data/TODO.fixup/38-note-element-rendering.md +15 -0
  181. data/TODO.fixup/39-styleex-flag-honoring.md +25 -0
  182. data/TODO.fixup/40-connector-dashed-lines.md +15 -0
  183. data/TODO.fixup/41-diagram-frame-showborder-detection.md +23 -0
  184. data/TODO.fixup/42-textrenderer-centralization.md +10 -0
  185. data/TODO.fixup/43-colorresolver-unification.md +24 -0
  186. data/TODO.fixup/44-theme-aware-textrenderer.md +17 -0
  187. data/TODO.fixup/45-theme-aware-fontresolver.md +22 -0
  188. data/TODO.fixup/46-style-parameterized-by-theme.md +21 -0
  189. data/TODO.fixup/47-canvas-frame-inset.md +19 -0
  190. data/TODO.fixup/48-background-rect-format.md +15 -0
  191. data/TODO.fixup/49-all-samples-integration-test.md +17 -0
  192. data/TODO.fixup/50-svg-emitter-architecture-docs.md +33 -0
  193. data/TODO.fixup/51-promote-theme-to-ea-namespace.md +28 -0
  194. data/TODO.fixup/52-theme-definition-integrated-fills.md +26 -0
  195. data/TODO.fixup/53-theme-registry-yaml-loading.md +50 -0
  196. data/TODO.fixup/54-diagram-theme-api.md +50 -0
  197. data/TODO.fixup/55-update-eaemitter-to-ea-theme.md +23 -0
  198. data/TODO.fixup/56-theme-yaml-files.md +65 -0
  199. data/TODO.fixup/57-theme-specs-and-integration.md +27 -0
  200. data/TODO.fixup/58-ea-entry-point-theme-autoload.md +20 -0
  201. data/TODO.fixup/59-thread-theme-through-all-renderers.md +29 -0
  202. data/TODO.fixup/60-cli-theme-option.md +22 -0
  203. data/TODO.fixup/61-theme-validation.md +28 -0
  204. data/TODO.fixup/62-theme-builder-dsl.md +31 -0
  205. data/TODO.fixup/63-theme-integration-rendering-spec.md +24 -0
  206. data/TODO.fixup/64-theme-writeback-styleex.md +21 -0
  207. data/TODO.fixup/65-remove-dead-eaemitter-theme-files.md +30 -0
  208. data/TODO.fixup/66-styleex-writeback.md +22 -0
  209. data/TODO.fixup/67-delete-dead-code.md +28 -0
  210. data/TODO.fixup/68-figure-parity-status.md +82 -0
  211. data/TODO.fixup/69-package-polygon-rendering.md +54 -0
  212. data/TODO.fixup/70-tagged-values-compartment.md +39 -0
  213. data/TODO.fixup/71-auto-detect-theme-from-xmi.md +37 -0
  214. data/TODO.fixup/72-sample-exports-as-regression-specs.md +37 -0
  215. data/TODO.fixup/73-delete-dead-transformations.md +14 -0
  216. data/TODO.fixup/74-delete-dead-diagram-renderers.md +18 -0
  217. data/TODO.fixup/75-visibility-legend-icons.md +23 -0
  218. data/TODO.fixup/76-plateau-text-undershoot.md +27 -0
  219. data/TODO.fixup/77-canvas-frame-margins.md +28 -0
  220. data/TODO.fixup/78-note-element-rendering.md +30 -0
  221. data/TODO.fixup/79-operation-compartment-rendering.md +27 -0
  222. data/TODO.fixup/80-tagged-value-stereotype-visibility.md +37 -0
  223. data/TODO.fixup/81-figure-parity-current-state.md +63 -0
  224. data/config/qea_schema.yml +356 -0
  225. data/config/themes/119.yml +98 -0
  226. data/config/themes/default.yml +14 -0
  227. data/examples/exports/20251010_current_plateau_v5/UML_EA.DTD +6856 -0
  228. data/examples/exports/20251010_current_plateau_v5/model.xml +96709 -0
  229. data/examples/exports/ArcGISWorkspace_template/UML_EA.DTD +6856 -0
  230. data/examples/exports/ArcGISWorkspace_template/model.xml +1427 -0
  231. data/examples/exports/UmlModel_template/UML_EA.DTD +6856 -0
  232. data/examples/exports/UmlModel_template/model.xml +23 -0
  233. data/examples/exports/basic/UML_EA.DTD +6856 -0
  234. data/examples/exports/basic/model.xml +8039 -0
  235. data/examples/exports/simple/UML_EA.DTD +6856 -0
  236. data/examples/exports/simple/model.xml +585 -0
  237. data/examples/exports/simple_example/UML_EA.DTD +6856 -0
  238. data/examples/exports/simple_example/model.xml +23 -0
  239. data/examples/exports/test/UML_EA.DTD +6856 -0
  240. data/examples/exports/test/model.xml +1638 -0
  241. data/examples/qea/basic.qea +0 -0
  242. data/frontend/README.md +51 -0
  243. data/frontend/dist/app.iife.js +24 -0
  244. data/frontend/dist/style.css +1 -0
  245. data/frontend/index.html +26 -0
  246. data/frontend/package-lock.json +2359 -0
  247. data/frontend/package.json +26 -0
  248. data/frontend/src/App.vue +59 -0
  249. data/frontend/src/app.ts +8 -0
  250. data/frontend/src/components/AppHeader.vue +127 -0
  251. data/frontend/src/components/AppSidebar.vue +119 -0
  252. data/frontend/src/components/ClassDetails.vue +247 -0
  253. data/frontend/src/components/DiagramView.vue +79 -0
  254. data/frontend/src/components/PackageDetails.vue +92 -0
  255. data/frontend/src/components/PackageTreeNode.vue +64 -0
  256. data/frontend/src/components/SearchResults.vue +48 -0
  257. data/frontend/src/components/WelcomeView.vue +87 -0
  258. data/frontend/src/shims-vue.d.ts +5 -0
  259. data/frontend/src/stores/dataStore.ts +79 -0
  260. data/frontend/src/stores/uiStore.ts +167 -0
  261. data/frontend/src/styles/main.css +1566 -0
  262. data/frontend/src/types.ts +221 -0
  263. data/frontend/tests/e2e/__screenshots__/new-dark-mode.png +0 -0
  264. data/frontend/tests/e2e/__screenshots__/new-package-details.png +0 -0
  265. data/frontend/tests/e2e/__screenshots__/new-search-modal.png +0 -0
  266. data/frontend/tests/e2e/__screenshots__/new-sidebar-collapsed.png +0 -0
  267. data/frontend/tests/e2e/__screenshots__/new-welcome.png +0 -0
  268. data/frontend/tests/e2e/render.spec.ts +188 -0
  269. data/frontend/tsconfig.json +20 -0
  270. data/frontend/vite.config.ts +26 -0
  271. data/frontend/vitest.config.ts +8 -0
  272. data/lib/ea/cli/app.rb +57 -2
  273. data/lib/ea/cli/command/diagrams.rb +37 -28
  274. data/lib/ea/cli/command/diff.rb +46 -0
  275. data/lib/ea/cli/command/export.rb +101 -0
  276. data/lib/ea/cli/command/info.rb +75 -0
  277. data/lib/ea/cli/command/lint.rb +48 -0
  278. data/lib/ea/cli/command/mdg.rb +98 -0
  279. data/lib/ea/cli/command/query.rb +34 -0
  280. data/lib/ea/cli/command/render.rb +135 -0
  281. data/lib/ea/cli/command/svg.rb +8 -15
  282. data/lib/ea/cli/command.rb +7 -0
  283. data/lib/ea/diagram/display_config.rb +184 -0
  284. data/lib/ea/diagram.rb +4 -41
  285. data/lib/ea/diff/change.rb +30 -0
  286. data/lib/ea/diff/comparator.rb +109 -0
  287. data/lib/ea/diff/html_reporter.rb +91 -0
  288. data/lib/ea/diff.rb +15 -0
  289. data/lib/ea/export/json/generator.rb +118 -0
  290. data/lib/ea/export/json.rb +12 -0
  291. data/lib/ea/export/json_schema/generator.rb +77 -0
  292. data/lib/ea/export/json_schema.rb +12 -0
  293. data/lib/ea/export/plantuml/generator.rb +140 -0
  294. data/lib/ea/export/plantuml.rb +12 -0
  295. data/lib/ea/export/xsd/class_mapping.rb +65 -0
  296. data/lib/ea/export/xsd/generator.rb +137 -0
  297. data/lib/ea/export/xsd/namespace_registry.rb +82 -0
  298. data/lib/ea/export/xsd.rb +19 -0
  299. data/lib/ea/export.rb +13 -0
  300. data/lib/ea/image/emf_renderer.rb +42 -0
  301. data/lib/ea/image.rb +12 -0
  302. data/lib/ea/lint/engine.rb +30 -0
  303. data/lib/ea/lint/lint_rule.rb +38 -0
  304. data/lib/ea/lint/offense.rb +18 -0
  305. data/lib/ea/lint/rules/cyclic_generalization.rb +62 -0
  306. data/lib/ea/lint/rules/duplicate_name.rb +28 -0
  307. data/lib/ea/lint/rules/missing_stereotype.rb +40 -0
  308. data/lib/ea/lint/rules/naming_convention.rb +44 -0
  309. data/lib/ea/lint/rules/orphan_element.rb +28 -0
  310. data/lib/ea/lint/rules.rb +14 -0
  311. data/lib/ea/lint.rb +13 -0
  312. data/lib/ea/mdg/document.rb +120 -0
  313. data/lib/ea/mdg/loader.rb +118 -0
  314. data/lib/ea/mdg/registry.rb +98 -0
  315. data/lib/ea/mdg/stereotype_alias_registry.rb +77 -0
  316. data/lib/ea/mdg/xml/applies_to.rb +18 -0
  317. data/lib/ea/mdg/xml/apply.rb +18 -0
  318. data/lib/ea/mdg/xml/content.rb +18 -0
  319. data/lib/ea/mdg/xml/documentation.rb +24 -0
  320. data/lib/ea/mdg/xml/stereotype.rb +28 -0
  321. data/lib/ea/mdg/xml/stereotypes.rb +18 -0
  322. data/lib/ea/mdg/xml/tag.rb +28 -0
  323. data/lib/ea/mdg/xml/tagged_values.rb +18 -0
  324. data/lib/ea/mdg/xml/technology.rb +28 -0
  325. data/lib/ea/mdg/xml/uml_profile.rb +22 -0
  326. data/lib/ea/mdg/xml/uml_profiles.rb +18 -0
  327. data/lib/ea/mdg/xml.rb +24 -0
  328. data/lib/ea/mdg.rb +25 -0
  329. data/lib/ea/model/classifier.rb +4 -0
  330. data/lib/ea/model/constraint.rb +24 -0
  331. data/lib/ea/model/diagram.rb +85 -0
  332. data/lib/ea/model/diagram_connector.rb +34 -0
  333. data/lib/ea/model/diagram_element.rb +16 -0
  334. data/lib/ea/model/document.rb +47 -2
  335. data/lib/ea/model/ghost_label.rb +32 -0
  336. data/lib/ea/model/instance_specification.rb +34 -0
  337. data/lib/ea/model/legend.rb +43 -0
  338. data/lib/ea/model/legend_item.rb +26 -0
  339. data/lib/ea/model/note.rb +28 -0
  340. data/lib/ea/model/operation.rb +2 -0
  341. data/lib/ea/model/property.rb +2 -0
  342. data/lib/ea/model/relationship.rb +2 -0
  343. data/lib/ea/model/signal.rb +14 -0
  344. data/lib/ea/model/slot.rb +19 -0
  345. data/lib/ea/model.rb +8 -0
  346. data/lib/ea/ocl/evaluator.rb +94 -0
  347. data/lib/ea/ocl/nodes.rb +30 -0
  348. data/lib/ea/ocl/parser.rb +133 -0
  349. data/lib/ea/ocl/unsupported_error.rb +7 -0
  350. data/lib/ea/ocl.rb +13 -0
  351. data/lib/ea/qea/factory/document_builder.rb +24 -10
  352. data/lib/ea/qea/factory/generalization_builder.rb +9 -11
  353. data/lib/ea/qea/infrastructure/table_reader.rb +9 -0
  354. data/lib/ea/qea/models/ea_author.rb +35 -0
  355. data/lib/ea/qea/models/ea_glossary.rb +34 -0
  356. data/lib/ea/qea/models/ea_image.rb +53 -0
  357. data/lib/ea/qea/models/ea_implement.rb +32 -0
  358. data/lib/ea/qea/models/ea_list.rb +42 -0
  359. data/lib/ea/qea/models/ea_object_effort.rb +34 -0
  360. data/lib/ea/qea/models/ea_object_problem.rb +37 -0
  361. data/lib/ea/qea/models/ea_object_require.rb +34 -0
  362. data/lib/ea/qea/models/ea_object_resource.rb +34 -0
  363. data/lib/ea/qea/models/ea_object_risk.rb +34 -0
  364. data/lib/ea/qea/models/ea_object_scenario.rb +34 -0
  365. data/lib/ea/qea/models/ea_object_test.rb +35 -0
  366. data/lib/ea/qea/models/ea_object_trx.rb +34 -0
  367. data/lib/ea/qea/models/ea_operation.rb +7 -0
  368. data/lib/ea/qea/models/ea_palette.rb +30 -0
  369. data/lib/ea/qea/models/ea_palette_item.rb +36 -0
  370. data/lib/ea/qea/models/ea_phase.rb +40 -0
  371. data/lib/ea/qea/models/ea_role_constraint.rb +32 -0
  372. data/lib/ea/qea/models/ea_secrypt.rb +34 -0
  373. data/lib/ea/qea/models/ea_version.rb +39 -0
  374. data/lib/ea/qea/models/ea_xref.rb +11 -1
  375. data/lib/ea/qea/models.rb +19 -0
  376. data/lib/ea/qea/services/database_loader.rb +22 -1
  377. data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
  378. data/lib/ea/qea/validation/database.rb +2 -0
  379. data/lib/ea/qea/validation/validation_engine.rb +2 -0
  380. data/lib/ea/qea/validation.rb +2 -0
  381. data/lib/ea/query/builder.rb +127 -0
  382. data/lib/ea/query.rb +9 -0
  383. data/lib/ea/render/image_converter.rb +101 -0
  384. data/lib/ea/render.rb +10 -0
  385. data/lib/ea/shapescript/parser.rb +208 -0
  386. data/lib/ea/shapescript/renderer.rb +74 -0
  387. data/lib/ea/shapescript/shape.rb +34 -0
  388. data/lib/ea/shapescript.rb +24 -0
  389. data/lib/ea/sources/qea/adapter.rb +22 -5
  390. data/lib/ea/sources/qea/classifier_builder.rb +184 -7
  391. data/lib/ea/sources/qea/diagram_builder.rb +352 -24
  392. data/lib/ea/sources/qea/id_normalizer.rb +16 -1
  393. data/lib/ea/sources/qea/instance_builder.rb +113 -0
  394. data/lib/ea/sources/qea/legend_builder.rb +137 -0
  395. data/lib/ea/sources/qea/note_builder.rb +58 -0
  396. data/lib/ea/sources/qea/object_classifier_map.rb +6 -1
  397. data/lib/ea/sources/qea/operation_builder.rb +1 -0
  398. data/lib/ea/sources/qea/package_builder.rb +16 -0
  399. data/lib/ea/sources/qea/relationship_builder.rb +21 -5
  400. data/lib/ea/sources/qea/tagged_value_builder.rb +28 -9
  401. data/lib/ea/sources/qea/xref/legend_definition.rb +35 -0
  402. data/lib/ea/sources/qea/xref/parser.rb +150 -0
  403. data/lib/ea/sources/qea/xref/property_definition.rb +29 -0
  404. data/lib/ea/sources/qea/xref/record.rb +41 -0
  405. data/lib/ea/sources/qea/xref/stereotype_application.rb +37 -0
  406. data/lib/ea/sources/qea/xref.rb +27 -0
  407. data/lib/ea/sources/qea.rb +4 -0
  408. data/lib/ea/sources/xmi/adapter.rb +80 -2
  409. data/lib/ea/sources/xmi/classifier_builder.rb +10 -1
  410. data/lib/ea/sources/xmi/diagram_builder.rb +95 -18
  411. data/lib/ea/sources/xmi/extension_geometry_parser.rb +19 -2
  412. data/lib/ea/sources/xmi/extension_style_parser.rb +15 -2
  413. data/lib/ea/sources/xmi/note_builder.rb +46 -0
  414. data/lib/ea/sources/xmi/property_builder.rb +9 -0
  415. data/lib/ea/sources/xmi/stereotype_builder.rb +54 -0
  416. data/lib/ea/sources/xmi/tag_builder.rb +45 -0
  417. data/lib/ea/sources/xmi/umldi_keyword_extractor.rb +87 -0
  418. data/lib/ea/sources/xmi.rb +4 -0
  419. data/lib/ea/spa/output/sharded_multi_file_strategy.rb +18 -3
  420. data/lib/ea/spa/output/single_file_strategy.rb +8 -2
  421. data/lib/ea/spa/output/strategy.rb +34 -0
  422. data/lib/ea/svg/connector_router.rb +134 -0
  423. data/lib/ea/svg/ea_emitter/background.rb +2 -3
  424. data/lib/ea/svg/ea_emitter/bounds_calculator.rb +142 -0
  425. data/lib/ea/svg/ea_emitter/canvas.rb +56 -33
  426. data/lib/ea/svg/ea_emitter/color_resolver.rb +56 -0
  427. data/lib/ea/svg/ea_emitter/compartment/attributes.rb +28 -0
  428. data/lib/ea/svg/ea_emitter/compartment/constraints.rb +30 -0
  429. data/lib/ea/svg/ea_emitter/compartment/enum_literals.rb +86 -0
  430. data/lib/ea/svg/ea_emitter/compartment/header.rb +32 -0
  431. data/lib/ea/svg/ea_emitter/compartment/header_divider.rb +57 -0
  432. data/lib/ea/svg/ea_emitter/compartment/instance_slots.rb +96 -0
  433. data/lib/ea/svg/ea_emitter/compartment/instance_underline.rb +67 -0
  434. data/lib/ea/svg/ea_emitter/compartment/note_body.rb +52 -0
  435. data/lib/ea/svg/ea_emitter/compartment/note_text.rb +102 -0
  436. data/lib/ea/svg/ea_emitter/compartment/operations.rb +38 -0
  437. data/lib/ea/svg/ea_emitter/compartment/package_contents.rb +72 -0
  438. data/lib/ea/svg/ea_emitter/compartment/package_from_parent.rb +136 -0
  439. data/lib/ea/svg/ea_emitter/compartment/shape.rb +82 -0
  440. data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
  441. data/lib/ea/svg/ea_emitter/compartment/tagged_values.rb +30 -0
  442. data/lib/ea/svg/ea_emitter/compartment.rb +60 -0
  443. data/lib/ea/svg/ea_emitter/connectors.rb +46 -12
  444. data/lib/ea/svg/ea_emitter/diagram_frame.rb +143 -0
  445. data/lib/ea/svg/ea_emitter/document.rb +49 -25
  446. data/lib/ea/svg/ea_emitter/element/attribute_line_builder.rb +117 -0
  447. data/lib/ea/svg/ea_emitter/element/attribute_renderer.rb +119 -0
  448. data/lib/ea/svg/ea_emitter/element/bcol_decoder.rb +29 -0
  449. data/lib/ea/svg/ea_emitter/element/compartment_geometry.rb +113 -0
  450. data/lib/ea/svg/ea_emitter/element/constraint_renderer.rb +53 -0
  451. data/lib/ea/svg/ea_emitter/element/divider_renderer.rb +17 -0
  452. data/lib/ea/svg/ea_emitter/element/enumeration_literal_renderer.rb +34 -0
  453. data/lib/ea/svg/ea_emitter/element/filter.rb +44 -0
  454. data/lib/ea/svg/ea_emitter/element/hand_draw_shape_renderer.rb +77 -0
  455. data/lib/ea/svg/ea_emitter/element/header_line_pipeline.rb +43 -0
  456. data/lib/ea/svg/ea_emitter/element/header_line_provider/instance_spec.rb +27 -0
  457. data/lib/ea/svg/ea_emitter/element/header_line_provider/name.rb +76 -0
  458. data/lib/ea/svg/ea_emitter/element/header_line_provider/parent_ghost.rb +25 -0
  459. data/lib/ea/svg/ea_emitter/element/header_line_provider/stereotype_label.rb +68 -0
  460. data/lib/ea/svg/ea_emitter/element/header_line_provider.rb +19 -0
  461. data/lib/ea/svg/ea_emitter/element/header_lines.rb +27 -0
  462. data/lib/ea/svg/ea_emitter/element/header_renderer.rb +48 -0
  463. data/lib/ea/svg/ea_emitter/element/icon_renderer.rb +109 -0
  464. data/lib/ea/svg/ea_emitter/element/legend_renderer.rb +177 -0
  465. data/lib/ea/svg/ea_emitter/element/note_shape_renderer.rb +112 -0
  466. data/lib/ea/svg/ea_emitter/element/operation_renderer.rb +100 -0
  467. data/lib/ea/svg/ea_emitter/element/package_shape_renderer.rb +181 -0
  468. data/lib/ea/svg/ea_emitter/element/row_icon_renderer.rb +156 -0
  469. data/lib/ea/svg/ea_emitter/element/shape_renderer.rb +17 -0
  470. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +119 -0
  471. data/lib/ea/svg/ea_emitter/element/tagged_value_renderer.rb +50 -0
  472. data/lib/ea/svg/ea_emitter/element/text_escape.rb +20 -0
  473. data/lib/ea/svg/ea_emitter/element.rb +37 -0
  474. data/lib/ea/svg/ea_emitter/elements.rb +283 -95
  475. data/lib/ea/svg/ea_emitter/font_resolver.rb +111 -0
  476. data/lib/ea/svg/ea_emitter/ghost_labels.rb +66 -0
  477. data/lib/ea/svg/ea_emitter/label/end_label.rb +266 -0
  478. data/lib/ea/svg/ea_emitter/label/midpoint_label.rb +101 -0
  479. data/lib/ea/svg/ea_emitter/label/registry.rb +50 -0
  480. data/lib/ea/svg/ea_emitter/label.rb +23 -0
  481. data/lib/ea/svg/ea_emitter/labels.rb +93 -27
  482. data/lib/ea/svg/ea_emitter/layer.rb +20 -0
  483. data/lib/ea/svg/ea_emitter/layer_sequencer.rb +93 -0
  484. data/lib/ea/svg/ea_emitter/marker/arrow_path.rb +52 -0
  485. data/lib/ea/svg/ea_emitter/marker/diamond.rb +57 -0
  486. data/lib/ea/svg/ea_emitter/marker/kind.rb +35 -0
  487. data/lib/ea/svg/ea_emitter/marker/open_triangle.rb +27 -0
  488. data/lib/ea/svg/ea_emitter/marker/package_import.rb +39 -0
  489. data/lib/ea/svg/ea_emitter/marker/plus.rb +40 -0
  490. data/lib/ea/svg/ea_emitter/marker/registry.rb +42 -0
  491. data/lib/ea/svg/ea_emitter/marker.rb +37 -0
  492. data/lib/ea/svg/ea_emitter/markers.rb +351 -64
  493. data/lib/ea/svg/ea_emitter/render_context.rb +52 -0
  494. data/lib/ea/svg/ea_emitter/style.rb +15 -0
  495. data/lib/ea/svg/ea_emitter/text_renderer.rb +92 -0
  496. data/lib/ea/svg/ea_emitter.rb +33 -11
  497. data/lib/ea/svg/parity/checker.rb +105 -0
  498. data/lib/ea/svg/parity/source.rb +59 -0
  499. data/lib/ea/svg/parity/suite.rb +113 -0
  500. data/lib/ea/svg/parity.rb +14 -0
  501. data/lib/ea/svg.rb +2 -15
  502. data/lib/ea/theme/definition.rb +176 -0
  503. data/lib/ea/theme/loader.rb +125 -0
  504. data/lib/ea/theme/registry.rb +96 -0
  505. data/lib/ea/theme.rb +37 -0
  506. data/lib/ea/transformations.rb +5 -88
  507. data/lib/ea/validation/xmi_parity.rb +80 -0
  508. data/lib/ea/validation.rb +12 -0
  509. data/lib/ea/version.rb +4 -1
  510. data/lib/ea.rb +15 -4
  511. data/lib/tasks/svg_bench.rake +87 -0
  512. data/lutaml-uml-0.5.1.gem +0 -0
  513. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes-list.png +0 -0
  514. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-1-themes.png +0 -0
  515. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-2-gradients.png +0 -0
  516. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-3-colors.png +0 -0
  517. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance-fonts.png +0 -0
  518. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-4-appearance.png +0 -0
  519. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-5-behavior.png +0 -0
  520. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram-6-sequence.png +0 -0
  521. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-1-diagram.png +0 -0
  522. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-2-objects.png +0 -0
  523. data/reference-docs/enterprise-architect-appearance-dialogs/appearance-3-links.png +0 -0
  524. metadata +464 -26
  525. data/lib/ea/diagram/configuration.rb +0 -379
  526. data/lib/ea/diagram/element_renderers/base_renderer.rb +0 -77
  527. data/lib/ea/diagram/element_renderers/class_renderer.rb +0 -323
  528. data/lib/ea/diagram/element_renderers/connector_renderer.rb +0 -41
  529. data/lib/ea/diagram/element_renderers/package_renderer.rb +0 -61
  530. data/lib/ea/diagram/element_renderers.rb +0 -43
  531. data/lib/ea/diagram/extractor.rb +0 -566
  532. data/lib/ea/diagram/layout_engine.rb +0 -170
  533. data/lib/ea/diagram/path_builder.rb +0 -202
  534. data/lib/ea/diagram/style_parser.rb +0 -42
  535. data/lib/ea/diagram/style_resolver.rb +0 -291
  536. data/lib/ea/diagram/svg_renderer.rb +0 -274
  537. data/lib/ea/diagram/util.rb +0 -73
  538. data/lib/ea/svg/bounds_calculator.rb +0 -74
  539. data/lib/ea/svg/connector_path.rb +0 -138
  540. data/lib/ea/svg/element_box.rb +0 -256
  541. data/lib/ea/svg/renderer.rb +0 -77
  542. data/lib/ea/svg/style_resolver.rb +0 -72
  543. data/lib/ea/transformations/configuration.rb +0 -333
  544. data/lib/ea/transformations/format_registry.rb +0 -366
  545. data/lib/ea/transformations/parsers/base_parser.rb +0 -482
  546. data/lib/ea/transformations/parsers/qea_parser.rb +0 -401
  547. data/lib/ea/transformations/parsers/xmi_parser.rb +0 -243
  548. data/lib/ea/transformations/transformation_engine.rb +0 -390
@@ -0,0 +1,221 @@
1
+ export interface SpaCardinality {
2
+ min?: string
3
+ max?: string
4
+ }
5
+
6
+ export interface SpaAttribute {
7
+ id: string
8
+ name: string
9
+ type: string
10
+ visibility?: string
11
+ owner: string
12
+ ownerName: string
13
+ cardinality?: SpaCardinality
14
+ definition?: string
15
+ stereotypes: string[]
16
+ isStatic: boolean
17
+ isReadOnly: boolean
18
+ defaultValue?: string
19
+ }
20
+
21
+ export interface SpaAssociationEnd {
22
+ class?: string
23
+ className?: string
24
+ role?: string
25
+ cardinality?: SpaCardinality
26
+ aggregation?: string
27
+ }
28
+
29
+ export interface SpaAssociation {
30
+ id: string
31
+ xmiId: string
32
+ name: string
33
+ type: string
34
+ definition?: string
35
+ source?: SpaAssociationEnd
36
+ target?: SpaAssociationEnd
37
+ }
38
+
39
+ export interface SpaOperation {
40
+ id: string
41
+ name: string
42
+ visibility?: string
43
+ returnType?: string
44
+ owner: string
45
+ ownerName: string
46
+ parameters: SpaParameter[]
47
+ isStatic: boolean
48
+ isAbstract: boolean
49
+ }
50
+
51
+ export interface SpaParameter {
52
+ name: string
53
+ type?: string
54
+ direction?: string
55
+ }
56
+
57
+ export interface SpaLiteral {
58
+ name: string
59
+ definition?: string
60
+ }
61
+
62
+ export interface SpaInheritedAttribute {
63
+ attributeId: string
64
+ attribute: SpaAttribute
65
+ inheritedFrom: string
66
+ inheritedFromName: string
67
+ parentOrder: number
68
+ }
69
+
70
+ export interface SpaInheritedAssociation {
71
+ associationId: string
72
+ inheritedFrom: string
73
+ inheritedFromName: string
74
+ parentOrder: number
75
+ localRole: string
76
+ }
77
+
78
+ export interface SpaClass {
79
+ id: string
80
+ xmiId: string
81
+ name: string
82
+ qualifiedName: string
83
+ type: string
84
+ package?: string
85
+ stereotypes: string[]
86
+ definition?: string
87
+ attributes: string[]
88
+ operations: string[]
89
+ associations: string[]
90
+ generalizations: string[]
91
+ specializations: string[]
92
+ isAbstract: boolean
93
+ literals: SpaLiteral[]
94
+ inheritedAttributes: SpaInheritedAttribute[]
95
+ inheritedAssociations: SpaInheritedAssociation[]
96
+ }
97
+
98
+ export interface SpaPackage {
99
+ id: string
100
+ xmiId: string
101
+ name: string
102
+ path: string
103
+ definition?: string
104
+ stereotypes: string[]
105
+ classes: string[]
106
+ subPackages: string[]
107
+ diagrams: string[]
108
+ parent?: string
109
+ }
110
+
111
+ export interface SpaTreeClassRef {
112
+ id: string
113
+ name: string
114
+ stereotypes: string[]
115
+ }
116
+
117
+ export interface SpaPackageTreeNode {
118
+ id: string
119
+ name: string
120
+ path: string
121
+ stereotypes: string[]
122
+ classCount: number
123
+ classes: SpaTreeClassRef[]
124
+ children: SpaPackageTreeNode[]
125
+ }
126
+
127
+ export interface SpaDiagram {
128
+ id: string
129
+ xmiId: string
130
+ name: string
131
+ type: string
132
+ package?: string
133
+ objectCount: number
134
+ linkCount: number
135
+ svg?: string
136
+ }
137
+
138
+ export interface SpaStatistics {
139
+ packages: number
140
+ classes: number
141
+ associations: number
142
+ attributes: number
143
+ operations: number
144
+ }
145
+
146
+ export interface SpaLogoVariant {
147
+ path?: string
148
+ url?: string
149
+ }
150
+
151
+ export interface SpaLogoConfig {
152
+ light: SpaLogoVariant
153
+ dark: SpaLogoVariant
154
+ }
155
+
156
+ export interface SpaLogos {
157
+ square?: SpaLogoConfig
158
+ long?: SpaLogoConfig
159
+ }
160
+
161
+ export interface SpaAppearance {
162
+ logos?: SpaLogos
163
+ }
164
+
165
+ export interface SpaMetadata {
166
+ title?: string
167
+ description?: string
168
+ generated: string
169
+ generator: string
170
+ version: string
171
+ homepage?: string
172
+ repository?: string
173
+ license?: string
174
+ authors?: string
175
+ tags?: string[]
176
+ appearance?: SpaAppearance
177
+ statistics: SpaStatistics
178
+ }
179
+
180
+ export interface SpaSearchEntry {
181
+ id: string
182
+ type: string
183
+ entityType: string
184
+ entityId: string
185
+ name: string
186
+ qualifiedName: string
187
+ package: string
188
+ content: string
189
+ boost: number
190
+ }
191
+
192
+ export interface SpaSearchIndex {
193
+ version: string
194
+ fields: { name: string; boost: number }[]
195
+ ref: string
196
+ documentStore: SpaSearchEntry[]
197
+ pipeline: string[]
198
+ }
199
+
200
+ export interface SpaDocument {
201
+ metadata: SpaMetadata
202
+ packageTree: SpaPackageTreeNode
203
+ packages: Record<string, SpaPackage>
204
+ classes: Record<string, SpaClass>
205
+ attributes: Record<string, SpaAttribute>
206
+ associations: Record<string, SpaAssociation>
207
+ operations: Record<string, SpaOperation>
208
+ diagrams: Record<string, SpaDiagram>
209
+ }
210
+
211
+ export interface SpaData {
212
+ metadata: SpaMetadata
213
+ packageTree: SpaPackageTreeNode
214
+ packages: Record<string, SpaPackage>
215
+ classes: Record<string, SpaClass>
216
+ attributes: Record<string, SpaAttribute>
217
+ associations: Record<string, SpaAssociation>
218
+ operations: Record<string, SpaOperation>
219
+ diagrams: Record<string, SpaDiagram>
220
+ searchIndex: SpaSearchIndex
221
+ }
@@ -0,0 +1,188 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
2
+ import puppeteer, { type Browser, type Page } from 'puppeteer'
3
+ import path from 'path'
4
+ import fs from 'fs'
5
+
6
+ const NEW_HTML = '/tmp/lutaml_test_new.html'
7
+ const SCREENSHOT_DIR = path.join(__dirname, '__screenshots__')
8
+
9
+ describe('Lutaml SPA rendering', () => {
10
+ let browser: Browser
11
+ let page: Page
12
+ const consoleErrors: string[] = []
13
+
14
+ beforeAll(async () => {
15
+ fs.mkdirSync(SCREENSHOT_DIR, { recursive: true })
16
+
17
+ browser = await puppeteer.launch({
18
+ headless: true,
19
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
20
+ })
21
+ page = await browser.newPage()
22
+ await page.setViewport({ width: 1280, height: 800 })
23
+
24
+ page.on('pageerror', (err) => {
25
+ consoleErrors.push(`PageError: ${err.message}`)
26
+ })
27
+ page.on('console', (msg) => {
28
+ if (msg.type() === 'error') {
29
+ consoleErrors.push(`ConsoleError: ${msg.text()}`)
30
+ }
31
+ })
32
+ })
33
+
34
+ afterAll(async () => {
35
+ await browser.close()
36
+ })
37
+
38
+ it('loads without JS errors', async () => {
39
+ await page.goto(`file://${NEW_HTML}`, { waitUntil: 'networkidle0', timeout: 10000 })
40
+
41
+ // Wait for Vue to mount
42
+ await page.waitForSelector('#app', { timeout: 5000 })
43
+ await new Promise((r) => setTimeout(r, 500))
44
+
45
+ const criticalErrors = consoleErrors.filter(
46
+ (e) =>
47
+ !e.includes('favicon') &&
48
+ !e.includes('fonts.googleapis.com') &&
49
+ !e.includes('Download the React DevTools')
50
+ )
51
+ expect(criticalErrors).toEqual([])
52
+ })
53
+
54
+ it('renders the sidebar', async () => {
55
+ const sidebar = await page.$('.sidebar')
56
+ expect(sidebar).not.toBeNull()
57
+
58
+ const branding = await page.$('.sidebar-branding')
59
+ expect(branding).not.toBeNull()
60
+
61
+ const overviewBtn = await page.$('.overview-btn')
62
+ expect(overviewBtn).not.toBeNull()
63
+ })
64
+
65
+ it('renders the header', async () => {
66
+ const header = await page.$('.header')
67
+ expect(header).not.toBeNull()
68
+
69
+ const searchTrigger = await page.$('.search-trigger')
70
+ expect(searchTrigger).not.toBeNull()
71
+
72
+ const themeBtn = await page.$('.theme-btn')
73
+ expect(themeBtn).not.toBeNull()
74
+ })
75
+
76
+ it('renders the package tree', async () => {
77
+ const treeNodes = await page.$$('.tree-node')
78
+ expect(treeNodes.length).toBeGreaterThan(0)
79
+ })
80
+
81
+ it('renders the welcome view by default', async () => {
82
+ const welcome = await page.$('.view-welcome')
83
+ expect(welcome).not.toBeNull()
84
+
85
+ const stats = await page.$$('.welcome-stat')
86
+ expect(stats.length).toBeGreaterThan(0)
87
+ })
88
+
89
+ it('takes a screenshot of the welcome page', async () => {
90
+ await page.screenshot({
91
+ path: path.join(SCREENSHOT_DIR, 'new-welcome.png'),
92
+ fullPage: false,
93
+ })
94
+ })
95
+
96
+ it('toggles dark mode', async () => {
97
+ // Check initial theme
98
+ const initialTheme = await page.evaluate(() => {
99
+ return document.querySelector('.app-layout')?.getAttribute('data-theme')
100
+ })
101
+ expect(initialTheme).toBe('light')
102
+
103
+ // Click theme toggle
104
+ await page.click('.theme-btn')
105
+ await new Promise((r) => setTimeout(r, 200))
106
+
107
+ const darkTheme = await page.evaluate(() => {
108
+ return document.querySelector('.app-layout')?.getAttribute('data-theme')
109
+ })
110
+ expect(darkTheme).toBe('dark')
111
+
112
+ await page.screenshot({
113
+ path: path.join(SCREENSHOT_DIR, 'new-dark-mode.png'),
114
+ fullPage: false,
115
+ })
116
+
117
+ // Toggle back
118
+ await page.click('.theme-btn')
119
+ await new Promise((r) => setTimeout(r, 200))
120
+ })
121
+
122
+ it('collapses and expands sidebar', async () => {
123
+ // Sidebar should be visible initially
124
+ const sidebarClass = await page.evaluate(() => {
125
+ return document.querySelector('.sidebar')?.className
126
+ })
127
+ expect(sidebarClass).not.toContain('collapsed')
128
+
129
+ // Click sidebar toggle
130
+ await page.click('.sidebar-toggle')
131
+ await new Promise((r) => setTimeout(r, 300))
132
+
133
+ const collapsedClass = await page.evaluate(() => {
134
+ return document.querySelector('.sidebar')?.className
135
+ })
136
+ expect(collapsedClass).toContain('collapsed')
137
+
138
+ await page.screenshot({
139
+ path: path.join(SCREENSHOT_DIR, 'new-sidebar-collapsed.png'),
140
+ fullPage: false,
141
+ })
142
+
143
+ // Expand again
144
+ await page.click('.sidebar-toggle')
145
+ await new Promise((r) => setTimeout(r, 300))
146
+ })
147
+
148
+ it('opens search modal with / key', async () => {
149
+ await page.keyboard.press('/')
150
+ await new Promise((r) => setTimeout(r, 200))
151
+
152
+ const modal = await page.$('.search-modal-overlay')
153
+ expect(modal).not.toBeNull()
154
+
155
+ await page.screenshot({
156
+ path: path.join(SCREENSHOT_DIR, 'new-search-modal.png'),
157
+ fullPage: false,
158
+ })
159
+
160
+ // Close
161
+ await page.keyboard.press('Escape')
162
+ await new Promise((r) => setTimeout(r, 200))
163
+ })
164
+
165
+ it('clicks a package in the tree and shows details', async () => {
166
+ // Click the first package label in the tree
167
+ const clicked = await page.evaluate(() => {
168
+ const label = document.querySelector('.tree-label') as HTMLElement
169
+ if (label) {
170
+ label.click()
171
+ return true
172
+ }
173
+ return false
174
+ })
175
+ expect(clicked).toBe(true)
176
+
177
+ await new Promise((r) => setTimeout(r, 300))
178
+
179
+ // Should show package details or class details
180
+ const detailView = await page.$('.detail-view')
181
+ expect(detailView).not.toBeNull()
182
+
183
+ await page.screenshot({
184
+ path: path.join(SCREENSHOT_DIR, 'new-package-details.png'),
185
+ fullPage: false,
186
+ })
187
+ })
188
+ })
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "jsx": "preserve",
8
+ "resolveJsonModule": true,
9
+ "isolatedModules": true,
10
+ "esModuleInterop": true,
11
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
12
+ "skipLibCheck": true,
13
+ "noEmit": true,
14
+ "paths": {
15
+ "@/*": ["./src/*"]
16
+ }
17
+ },
18
+ "include": ["src/**/*.ts", "src/**/*.vue"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }
@@ -0,0 +1,26 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+ import { resolve } from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [vue()],
7
+ define: {
8
+ 'process.env.NODE_ENV': JSON.stringify('production'),
9
+ },
10
+ build: {
11
+ lib: {
12
+ entry: resolve(__dirname, 'src/app.ts'),
13
+ name: 'LutamlUmlSpa',
14
+ formats: ['iife'],
15
+ fileName: () => 'app.iife.js',
16
+ },
17
+ rollupOptions: {
18
+ external: [],
19
+ output: {
20
+ assetFileNames: 'style.[ext]',
21
+ },
22
+ },
23
+ outDir: 'dist',
24
+ emptyOutDir: true,
25
+ },
26
+ })
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ testTimeout: 30000,
6
+ hookTimeout: 30000,
7
+ },
8
+ })
data/lib/ea/cli/app.rb CHANGED
@@ -88,8 +88,6 @@ module Ea
88
88
  desc "svg NAME FILE", "Render a diagram from QEA/XMI to standalone SVG"
89
89
  option :output, **OUTPUT_OPTION,
90
90
  desc: "Output path (default: <basename>.<diagram>.svg)"
91
- option :mode, type: :string, default: "ea",
92
- desc: "Output mode: ea (default, matches EA SVG structure) | thin (compact)"
93
91
  option :all, type: :boolean, default: false,
94
92
  desc: "Render every diagram in the source file (NAME ignored)"
95
93
  option :output_dir, type: :string,
@@ -98,6 +96,63 @@ module Ea
98
96
  Command::Svg.new(name: name, file: file, **symbolize(options)).call
99
97
  end
100
98
 
99
+ desc "diff OLD NEW", "Compare two QEA files structurally"
100
+ option :format, type: :string, default: "table",
101
+ desc: "Output format: table | json | html"
102
+ option :output, **OUTPUT_OPTION, desc: "Output path (html format)"
103
+ def diff(old, new)
104
+ Command::Diff.new(old: old, new: new, **symbolize(options)).call
105
+ end
106
+
107
+ desc "render NAME FILE", "Render diagram(s) to SVG/PNG/PDF"
108
+ option :output, **OUTPUT_OPTION,
109
+ desc: "Output path (default: <basename>.<diagram>.<ext>)"
110
+ option :all, type: :boolean, default: false,
111
+ desc: "Render every diagram in the source file (NAME ignored)"
112
+ option :format, type: :string, default: "svg",
113
+ desc: "Output format: svg | png | pdf"
114
+ option :output_dir, type: :string,
115
+ desc: "Directory for --all output (default: <basename>.renders/)"
116
+ def render(name = nil, file = nil)
117
+ Command::Render.new(name: name, file: file, **symbolize(options)).call
118
+ end
119
+
120
+ desc "export SUB FILE", "Export model (SUB: xmi|json|plantuml|xsd)"
121
+ option :output, **OUTPUT_OPTION, desc: "Output path"
122
+ option :package, type: :string, desc: "Restrict to a package name"
123
+ def export(sub, file)
124
+ Command::Export.new(sub: sub, file: file, **symbolize(options)).call
125
+ end
126
+
127
+ desc "mdg ACTION [ARG]", "MDG technologies: list | show NAME"
128
+ option :format, type: :string, default: "table"
129
+ def mdg(action, arg = nil)
130
+ Command::Mdg.new(action: action, arg: arg, **symbolize(options)).call
131
+ end
132
+
133
+ desc "lint FILE", "Run model quality lint rules"
134
+ option :rule, type: :string, desc: "Filter by rule name substring"
135
+ option :severity, type: :string, desc: "Filter: error | warning | info"
136
+ option :format, type: :string, default: "table"
137
+ def lint(file)
138
+ Command::Lint.new(file: file, **symbolize(options)).call
139
+ end
140
+
141
+ desc "query FILE", "Filter model elements by criteria"
142
+ option :type, type: :string, desc: "Filter by object_type (Class, etc.)"
143
+ option :package, type: :string, desc: "Restrict to a package name"
144
+ option :stereotype, type: :string, desc: "Filter by stereotype name"
145
+ option :format, type: :string, default: "table"
146
+ def query(file)
147
+ Command::Query.new(file: file, **symbolize(options)).call
148
+ end
149
+
150
+ desc "info NAME FILE", "Show details of a single element"
151
+ option :format, type: :string, default: "table"
152
+ def info(name, file)
153
+ Command::Info.new(name: name, file: file, **symbolize(options)).call
154
+ end
155
+
101
156
  private
102
157
 
103
158
  def symbolize(opts)
@@ -6,19 +6,13 @@ module Ea
6
6
  # `ea diagrams ACTION FILE [NAME]`
7
7
  #
8
8
  # Actions:
9
- # list FILE — list diagrams in a QEA/XMI/LUR file (standalone)
9
+ # list FILE — list diagrams in a QEA/XMI file
10
10
  # extract FILE NAME — render the named diagram to SVG
11
11
  #
12
- # `list` reads the EA database directly (no lutaml-uml required
13
- # for QEA; XMI parsing via xmi gem only).
14
- #
15
- # `extract` accepts QEA, XMI, or LUR. The Repository is built
16
- # from the input file via `RepositoryBuilder` (single source of
17
- # truth — QEA/XMI are parsed via `Ea::Transformations`, LUR is
18
- # loaded natively via `Repository.from_file`).
12
+ # Both actions use the modern Ea::Model pipeline (parsed via
13
+ # Ea::Sources::Qea/Xmi::Adapter, rendered via
14
+ # Ea::Svg::EaEmitter::Document). No lutaml-uml dependency.
19
15
  class Diagrams < Base
20
- include RepositoryBuilder
21
-
22
16
  ACTIONS = %w[list extract].freeze
23
17
 
24
18
  def call
@@ -41,34 +35,49 @@ module Ea
41
35
  end
42
36
 
43
37
  def list
44
- db = load_database
45
- rows = db.diagrams.map { |d| [d.name, diagram_type_label(d), d.ea_guid] }
46
- formatter.render(rows, columns: %i[name type guid])
38
+ rows = document.diagrams.map do |d|
39
+ [d.name, diagram_type_label(d)]
40
+ end
41
+ formatter.render(rows, columns: %i[name type])
47
42
  end
48
43
 
49
44
  def extract
50
- repository = RepositoryBuilder.build_repository(file_path)
51
- result = extractor.extract_one(repository, name, extract_options)
52
- raise Ea::Cli::Error, result[:message] unless result[:success]
45
+ diagram = document.diagrams.find { |d| d.name == name }
46
+ unless diagram
47
+ raise Ea::Cli::Error,
48
+ "Diagram #{name.inspect} not found in #{file_path}. " \
49
+ "Use `ea diagrams list #{file_path}` to see names."
50
+ end
53
51
 
54
- path = result[:path] || write_output(result.fetch(:svg_content),
55
- default_name: "#{name}.svg")
52
+ svg = Ea::Svg::EaEmitter::Document.new(diagram,
53
+ model_index: document.index_by_id,
54
+ document: document).render
55
+ path = options[:output] || begin
56
+ base = File.basename(file_path, ".*")
57
+ dir = File.dirname(file_path)
58
+ safe_name = (diagram.name || "diagram").gsub(/[^\w.\-]+/, "_")
59
+ File.join(dir, "#{base}.#{safe_name}.svg")
60
+ end
61
+ FileUtils.mkdir_p(File.dirname(path))
62
+ File.write(path, svg)
56
63
  formatter.render([[path]], columns: [:written_to])
57
64
  end
58
65
 
59
- def extractor
60
- Ea::Diagram::Extractor.new
61
- end
62
-
63
- def extract_options
64
- opts = {}
65
- opts[:output] = options[:output] if options[:output]
66
- opts
67
- end
68
-
69
66
  def diagram_type_label(diagram)
70
67
  diagram.diagram_type || "Logical"
71
68
  end
69
+
70
+ def document
71
+ @document ||= case File.extname(file_path).downcase
72
+ when ".qea"
73
+ Ea::Sources::Qea::Adapter.from_path(file_path)
74
+ when ".xmi", ".xml"
75
+ Ea::Sources::Xmi::Adapter.from_path(file_path)
76
+ else
77
+ raise Ea::Cli::UnsupportedFormat,
78
+ "Unknown file format: #{File.extname(file_path)}"
79
+ end
80
+ end
72
81
  end
73
82
  end
74
83
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ea
4
+ module Cli
5
+ module Command
6
+ # `ea diff OLD_FILE NEW_FILE`
7
+ #
8
+ # Standalone — does not require lutaml-uml. Loads two QEA databases
9
+ # and reports added / removed / renamed entities.
10
+ #
11
+ # Exit code: 0 if identical, 1 if any differences found.
12
+ class Diff < Base
13
+ COLUMNS = %i[change kind id name].freeze
14
+
15
+ def call
16
+ if options[:format] == "html"
17
+ write_html_report
18
+ else
19
+ rows = comparator.differences.map do |diff|
20
+ [diff.change, diff.kind, diff.id, diff.name]
21
+ end
22
+ formatter.render(rows, columns: COLUMNS)
23
+ end
24
+ exit(1) if comparator.differences.any?
25
+ end
26
+
27
+ private
28
+
29
+ def write_html_report
30
+ html = Ea::Diff::HtmlReporter.new(comparator).render
31
+ path = options[:output] || "diff_report.html"
32
+ File.write(path, html)
33
+ formatter.render([[path]], columns: [:written_to])
34
+ end
35
+
36
+ def comparator
37
+ @comparator ||= begin
38
+ old_db = load_database(options[:old])
39
+ new_db = load_database(options[:new])
40
+ Ea::Diff::Comparator.new(old_db, new_db)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end