ranger-compiler 3.5.0 → 3.5.2

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 (338) hide show
  1. package/CHANGELOG.md +1160 -19
  2. package/LICENSE +3 -1
  3. package/README.md +124 -29
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79924 -56922
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,415 @@
1
+ # The EVG inspector
2
+
3
+ Dev tools for a picture a browser cannot read.
4
+
5
+ ```bash
6
+ npm run evg:inspect:demo # the dashboard, WebGL, with the panel
7
+ npm run pptx:html:serve # the slide editor, SVG — add ?inspect=1
8
+ npm run evg:inspect:test # the gates, no browser
9
+ npm run evg:inspect:web # the panel, in a real browser
10
+ npm run evg:inspect:live # a rule on disk → the painter, end to end
11
+ npm run evg:inspect:shots # remake the pictures below
12
+ ```
13
+
14
+ An EVG app in a browser is one `<canvas>`. Open the browser's dev tools on it
15
+ and you get exactly that: one element, no children, no styles, no box model.
16
+ The DOM painter is not much better — a flat pile of `<rect>` and `<path>` in
17
+ paint order, with no names and no nesting. You can see the picture. You cannot
18
+ ask it anything.
19
+
20
+ This is the panel that answers. Add `?inspect=1` to a host that has been wired
21
+ to it and you get the element hierarchy, the box model drawn over the real
22
+ pixels, the computed style, and the draw commands each element emitted.
23
+
24
+ Edit `gallery/ui/demo/*.css` while the demo page is open and the picture
25
+ follows on save. That is not a reload of the page and nothing is patched — see
26
+ [the stylesheet is an input](#the-stylesheet-is-an-input).
27
+
28
+ ![the dashboard, inspected](shots/dashboard.png)
29
+
30
+ ## The fourth channel
31
+
32
+ Three descriptions already come out of an EVG app, and `gallery/ui/demo/main.js`
33
+ names them in its own header:
34
+
35
+ ```
36
+ displayListJson() what to draw
37
+ hitId(x, y) what is under the pointer
38
+ a11yJson() what it MEANS
39
+ ```
40
+
41
+ They share the property this design is built on: **each is derived from the
42
+ same laid-out tree, on the same pass.** `EVGA11yFromTree` says why in its
43
+ header — an app that describes its tree a second time has two descriptions to
44
+ keep in step, and the one nobody can see is the one that rots.
45
+
46
+ `EVGInspect` is the fourth — **what it IS** — and obeys the same rule. Every
47
+ rectangle it reports is the one `EVGLayout` computed and `EVGDisplayList` drew,
48
+ read off `calculatedX` and its neighbours. Nothing here has a second opinion
49
+ about geometry, so the panel cannot disagree with the picture.
50
+
51
+ ```
52
+ EVGElement tree (laid out)
53
+ │
54
+ ┌────────────┬───────┴───────┬──────────────┐
55
+ ▼ ▼ ▼ ▼
56
+ EVGDisplayList EVGHitTest EVGA11yFromTree EVGInspect
57
+ what to draw what is what it means WHAT IT IS
58
+ under here
59
+ ```
60
+
61
+ That is also why it works on both painters and off the browser entirely: it is
62
+ attached to the tree, not to a canvas. The WebGL page and the SVG page are the
63
+ same panel with the same code, and the module compiles to every target
64
+ `EVGDisplayList` does.
65
+
66
+ ## Wiring a host to it
67
+
68
+ The panel knows nothing about any app. It is handed an adapter of small
69
+ functions, and everything past `tree` degrades rather than fails — which is
70
+ what lets one panel serve a slide editor and a dashboard without either
71
+ knowing about the other.
72
+
73
+ ```js
74
+ import { attach } from "…/lib/evg/inspect/evg-inspect.js";
75
+
76
+ attach({
77
+ surface, // the <canvas> or <svg> the app paints into
78
+ app: {
79
+ tree: () => app.inspectJson(gen), // REQUIRED
80
+ node: (path) => app.inspectNodeJson(path),
81
+ hit: (x, y) => app.inspectHitPath(x, y),
82
+ frame: () => app.inspectFrameJson(),
83
+ transform: () => app.inspectTransform(), // {x, y, k}, when the tree is
84
+ viewport: () => [w, h], // drawn inside a larger surface
85
+ css: () => ({ name, text, href, errors }),
86
+ setCss: (text) => app.inspectSetCss(text),
87
+ saveCss: (text) => fetch(href, { method: "PUT", body: text }),
88
+ force: (path, bits) => app.inspectForce(path, bits),
89
+ },
90
+ });
91
+ ```
92
+
93
+ **Every one of these may return a promise.** A host in the same page answers
94
+ synchronously and the panel does not notice; the directions that are not in the
95
+ same page — a DevTools extension over `inspectedWindow.eval`, a native target
96
+ over a socket, a paused page answering out of a snapshot — are all the same
97
+ shape, which is that the answer does not arrive on the same tick. The two
98
+ exceptions are `transform` and `viewport`, read once per refresh and cached,
99
+ because a highlight that arrives a tick after the pointer is a highlight that
100
+ lags.
101
+
102
+ On the Ranger side that is four one-liners, because `EVGInspect` carries the
103
+ statics:
104
+
105
+ ```ranger
106
+ fn inspectJson:string (gen:int) {
107
+ this.displayListJson()
108
+ def r:EVGElement (unwrap root)
109
+ def w:double (this.widthPx())
110
+ def h:double (this.heightPx())
111
+ return (EVGInspect.treeOf(r "Dashboard demo" gen w h))
112
+ }
113
+ fn inspectNodeJson:string (path:string) { … EVGInspect.nodeOf(r path) }
114
+ fn inspectHitPath:string (px:double py:double) { … EVGInspect.hitOf(r px py) }
115
+ fn inspectFrameJson:string () { … EVGInspect.frameOf(r) }
116
+ ```
117
+
118
+ `hitOf` and `nodeOf` should be given the layout that is already there
119
+ (`laidOut()`), not a fresh render. On the dashboard `hitId` costs 11 ms and
120
+ `hitIdCached` costs 0.007 ms, and the whole difference is a page rebuilt to
121
+ answer a question about geometry that had not moved.
122
+
123
+ ## Identity is a path
124
+
125
+ `EVGElement.id` is not an identity: it is optional, it is the app's own test
126
+ id, and most elements have none. So a node is named by where it sits:
127
+
128
+ ```
129
+ "0" the root
130
+ "0/3" its fourth child
131
+ "0/3/k:share" a child with `key` set uses the key instead of the index
132
+ ```
133
+
134
+ The same trick `EVGComponentHost` plays with `enter`/`leave`: a name unique
135
+ among siblings composes into a name unique in the tree, with no registry.
136
+
137
+ It is **structural**, and the cost is honest — insert an unkeyed row above the
138
+ selected one and the path names a different element. Keyed children are immune,
139
+ which is the set a list rebuild actually reorders. When a path stops resolving
140
+ the panel drops the selection and says so in its footer; re-pointing it at
141
+ whatever is now at that index would be a lie.
142
+
143
+ ## Which commands did this element draw
144
+
145
+ ![a slide, inspected](shots/pptx-slide.png)
146
+
147
+ `EVGDrawCmd` carries the element that emitted it, as an index into the inspect
148
+ walk, and writes it into the JSON as `"n"` — **only while attribution is on**.
149
+ Off, the list is byte-identical to the one built before the field existed, and
150
+ that is a gate rather than a hope (`inspect-check.mjs`, gate 5).
151
+
152
+ It is stamped in one place. `EVGDisplayList.walk` sets `curNode` for the
153
+ element it is walking and puts back what was there on the way out; `addCmd` is
154
+ the only thing that reads it, and every one of the twenty append sites in that
155
+ file goes through `addCmd`. A child cannot leave its slot behind for the
156
+ commands its parent emits afterwards.
157
+
158
+ This is worth more than the highlight it was added for. "Which element drew
159
+ these three commands" is the question behind the class of bug
160
+ [`gl/README.md`](../gl/README.md) describes: five painters each deciding again
161
+ what a box means, and border-radius working in PDF and silently not in PNG
162
+ because one painter read `box.borderRadius` and another a stale
163
+ `el.borderRadius`. With attribution the command that is wrong names the element
164
+ that is wrong.
165
+
166
+ In the picture above, the ellipse on slide 4 is selected and the commands pane
167
+ says it became a `RECT` and a `BORDER` at 456,288, 288×192, radius 96 — which
168
+ is how a preset ellipse is drawn.
169
+
170
+ ## Which classes reach this element
171
+
172
+ This cascade selects on **classes and nothing else** — an `EVGStyleRule` carries
173
+ a class, a state, a theme and a media block, and the subset of CSS it implements
174
+ has nothing else in a selector. That is a limitation with a useful side: the set
175
+ of rules that can ever touch a node is exactly the set written against one of
176
+ its classes, so the panel can list them completely rather than guessing, and
177
+ that list is the answer to *where do I go to change this*.
178
+
179
+ Two things are shown with it, and both matter before you edit anything:
180
+
181
+ * **how many elements each class reaches.** `.db-card` is four cards. There is
182
+ no way to reach one of them without giving it a class of its own, so a panel
183
+ that let you edit `.db-card` without saying that would be inviting a surprise.
184
+ * **an element with no class at all is unreachable.** `applyTo` returns
185
+ immediately when `className` is empty, so no rule can ever style it, and
186
+ whatever it looks like was set on it directly. The panel says so instead of
187
+ showing an empty rule list that looks like a bug.
188
+
189
+ The rules themselves come out of the plan, in cascade order, winners and losers
190
+ together:
191
+
192
+ ```
193
+ RULES, STRONGEST LAST
194
+ .db-card 4 elements
195
+ display flex
196
+ border-radius 14px
197
+ background-color ▪ #ffffff
198
+ ```
199
+
200
+ Nothing is scored here and no selector is re-matched. `buildPlan` calls
201
+ `planGroup` in the four passes that **are** the precedence — plain, themed,
202
+ stateful, themed-and-stateful — and the last write wins, so "which declaration
203
+ won" is read off the end of the list. The panel cannot disagree with the engine
204
+ about who won because it is not deciding, it is reading. That is gate 6.
205
+
206
+ The record itself is one int per planned declaration in `EVGStyleSheet.planRules`,
207
+ pushed beside the name and value `planGroup` already pushes. It is **per plan,
208
+ not per element**: a 1600-row table has a handful of plans and a hundred
209
+ thousand applications of them, and the same record made at apply time would be
210
+ paid a hundred thousand times over.
211
+
212
+ A declaration that lost to an inline value is marked separately from one that
213
+ lost to another rule — see below.
214
+
215
+ ## The stylesheet is an input
216
+
217
+ ![the sheet, edited in the panel](shots/css.png)
218
+
219
+ This is why the panel can edit at all, and why it needs no override layer.
220
+
221
+ **The element tree is an app's output.** Edit it and the next rebuild throws
222
+ the edit away — which is exactly why a DevTools style edit dies on a React
223
+ re-render, and why editing the tree would need a table of overrides re-applied
224
+ after every pass, an invalidation story, and a way to detect the app writing
225
+ over you.
226
+
227
+ **The stylesheet is an app's input.** `init(css)` is how every demo here starts.
228
+ Hand back a changed one and the app re-parses and re-cascades exactly as it did
229
+ at startup; the layout, the display list and the hit test follow because they
230
+ always did. Nothing is intercepted, no value is held over the app's head, and
231
+ **the text in the editor is the text that goes in the file** — there is no
232
+ "copy as CSS" step because there is nothing to translate.
233
+
234
+ Two routes into the same operation, and they cannot drift:
235
+
236
+ ```
237
+ the panel's CSS pane ─┬─► app.inspectSetCss(text) ─► EVGStyleSheet.reload
238
+ │ ▲
239
+ a save on disk ───────┼────────┘
240
+ │ │
241
+ └─ serve.mjs watch ─► SSE ─► the page fetches and hands it over
242
+ │
243
+ "save to disk" ───────┴─► PUT ─► serve.mjs writes the file
244
+ …which the watch then sees, so every other
245
+ page open on that sheet re-cascades too
246
+ ```
247
+
248
+ The PUT is deliberately narrow — a `.css` file directly inside
249
+ `gallery/ui/demo`, and nothing else. This server exists to serve a demo on a
250
+ developer's own machine, and a write that could reach further would be a worse
251
+ thing than the convenience is worth. The page that saved keeps the text it
252
+ sent, so its own save coming back round the watch is not applied twice.
253
+
254
+ `npm run evg:inspect:live` gates the whole path and checks the **colour of a
255
+ rectangle in the display list**, not the element and not the panel: a value
256
+ that reached the element and stopped there would be a frame still showing the
257
+ old colour, and that is the failure worth catching.
258
+
259
+ ### `reload`, and the trap it exists for
260
+
261
+ `EVGStyleSheet.reload(css)` is not `new EVGStyleSheet()` at the call site, and
262
+ both reasons are silent when got wrong.
263
+
264
+ `parse` **appends**. It drops the plans and adds rules; it does not remove what
265
+ is already there. Parsing twice into one sheet leaves every rule in it twice,
266
+ which mostly looks like it worked because the duplicates agree — until one of
267
+ them is edited.
268
+
269
+ And `generation` starts at 1, so a fresh sheet is at 2 after its first parse —
270
+ exactly where the sheet it replaced was. `applyTo` skips an element whose
271
+ `styleGen` already equals the generation, so **every retained element would be
272
+ skipped and the new CSS would land on nothing.** Worse, it fails asymmetrically:
273
+ a page that rebuilds its tree hands the cascade fresh elements with `styleGen`
274
+ 0 and works fine. It would pass in the tree-literal demos and fail in an app
275
+ that keeps its tree — which the dashboard does, so gate 7 is run against it
276
+ deliberately.
277
+
278
+ ### What CSS editing cannot reach, and why that is worth showing
279
+
280
+ * **A value the app set on the element itself.** `inlineProps` is the cascade's
281
+ own record of what the authoring layer wrote directly, and an inline value
282
+ outranks every rule. The panel strikes those declarations through and marks
283
+ them as beaten by inline rather than by another rule — so "why did my edit do
284
+ nothing" is answered on the spot instead of being a mystery.
285
+ * **`position` in a rule.** `EVGElement.setAttribute` has no branch for it, so
286
+ the cascade never writes the field; an element is out of flow because it has
287
+ offsets, an overlay flag, or a `<Layer>` tag. The panel therefore reports the
288
+ position the **layout used**, not the field, and gate 6 is what would catch a
289
+ rule that says `position: absolute` with no offsets — the engine ignores it
290
+ silently today.
291
+ * **Geometry that came from data.** A PPTX slide's shapes are placed by the
292
+ file, not by a sheet. CSS editing serves that app's chrome; the slide is a
293
+ different question and the panel does not pretend otherwise.
294
+
295
+ ## Holding a state on
296
+
297
+ ![a hover, held on while the pointer is elsewhere](shots/state.png)
298
+
299
+ A `:hover` rule cannot be read while the only way to make it true is to keep
300
+ the pointer somewhere other than this panel. So the four interaction bits can
301
+ be **held on** for the selected node — the same thing Chrome's `:hov` toggles
302
+ do, and for the same reason.
303
+
304
+ Nothing was taught to the cascade. `EVGPseudo.holds` reads `isHovered`,
305
+ `isFocused`, `isPressed` and `a11yDisabled`, and a held bit is one of those
306
+ fields being true — so the rule that wins is the ordinary `.db-brand:hover`
307
+ rule, matching for the ordinary reason. The panel above shows exactly that: the
308
+ initial value struck under it, the base rule, and the hover rule on top.
309
+
310
+ The table is **keyed by node path, not by element**, and that is the whole
311
+ reason it works here. This page throws its element tree away on every input, so
312
+ a flag written onto an element would last one frame; a path is a fact about the
313
+ tree's shape and names whatever is in that position on the next pass too. It
314
+ lives in `EVGInspectForce`, a singleton, applied by
315
+ `EVGInspect.applyForced(root)` — which a host calls at one specific moment:
316
+
317
+ ```ranger
318
+ this.markStates(r) ; what the app says is hovered
319
+ EVGInspect.applyForced(r) ; plus what the panel is holding
320
+ s.applyTree(r "") ; then ask the sheet what that makes true
321
+ ```
322
+
323
+ That ordering is the only thing a host has to get right, and the cost when
324
+ nothing is held is one array length.
325
+
326
+ On, not off. Forcing a state *off* would be arguing with the app about what is
327
+ happening rather than holding still something that already is, and the browser
328
+ does not offer it either.
329
+
330
+ ## A tree drawn inside a larger surface
331
+
332
+ The dashboard's tree *is* the page: the boxes are in the surface's own
333
+ coordinates and the transform is the identity. A slide is not — the editor
334
+ draws it fitted and centred inside a window with a toolbar, a thumbnail strip
335
+ and a properties panel around it.
336
+
337
+ So the adapter can hand the panel a `transform` (`{x, y, k}`, where the tree's
338
+ coordinates land on the surface and how big they are drawn) and a `viewport`
339
+ (how big the surface is in its own units). The panel applies both to the
340
+ overlay and inverts both for the picker, so an adapter never has to know where
341
+ on the page its picture ended up. `PptxWeb.inspectTransform` derives `k` from
342
+ the two widths rather than assembling it from `ptScale` and the converter's
343
+ scale factor: one number instead of two that have to agree.
344
+
345
+ ## The overlay is DOM, not draw commands
346
+
347
+ The four rings are absolutely positioned `<div>`s over the surface, in the four
348
+ colours a browser uses. They are **not** pushed into the display list, and that
349
+ is deliberate twice over: a screenshot taken while the panel is open is still a
350
+ screenshot of the app, and one implementation then serves both painters,
351
+ because the WebGL canvas and the SVG one are the same rectangle on the page.
352
+
353
+ ## What is gated
354
+
355
+ `npm run evg:inspect:test` runs five differences against a real app in Node —
356
+ a second description of something is only worth having if it is differenced
357
+ against the first, which is the habit the painters already keep:
358
+
359
+ | | |
360
+ | --- | --- |
361
+ | the tree is a tree | one root, every parent present, no path twice |
362
+ | the box model closes | content + padding + borders is the border box, on all 470 nodes |
363
+ | attribution lands inside | a command is inside the box of the element that emitted it — with two exceptions that are real and asserted as such: a text run may overhang by the font's side bearings, a shadow by its blur |
364
+ | the hit test agrees | when it answers, the answer contains the point |
365
+ | off costs nothing | a list built without attribution carries none |
366
+ | the cascade agrees | the winning rule's value is the value on the element, over 2000 properties |
367
+ | a reload lands | a rule added to the sheet reaches a **retained** tree, and the frame |
368
+ | a reload does not double | applying the same text twice leaves the plan the same size |
369
+ | the class counts are the tree's | what the panel says `.db-card` reaches is what the tree has |
370
+ | a held state is an ordinary state | the real `:hover` rule wins, the value follows, it survives a rebuild, and letting go puts it back |
371
+
372
+ The hit-test gate is deliberately one-sided, and the first version of it was
373
+ wrong in a way worth recording: it expected every node to be reachable. The
374
+ dashboard's table is 552px tall inside a 414px scroll box, so half its rows sit
375
+ at coordinates the page does not show, and `EVGHitTest` drops a clipping box's
376
+ whole subtree when the point is outside it — its header says why, and it is
377
+ right: a row scrolled out of view that is still clickable is the worst of both.
378
+ "Nothing is there" is the correct answer, so the gate asserts the other half —
379
+ nothing may be named under a pointer that is not over it — plus that most of
380
+ the page does answer, so it cannot pass by never being asked.
381
+
382
+ `npm run evg:inspect:web` gates the part that only exists in a page: that
383
+ `?inspect=1` attaches, that the tree it read has rows, and that the panes
384
+ filled. It runs against the SVG editor because that needs no GPU.
385
+
386
+ `npm run evg:inspect:live` gates the part that only exists across processes:
387
+ the real dev server, a real save, the real page and the real WebGL painter, in
388
+ both directions — a rule written to the file reaching the painter, and the
389
+ panel's "save to disk" reaching the file. It also checks that the watch fires
390
+ **once** per save: `fs.watch` reports the truncate and the write separately,
391
+ and a sheet reloaded three times per keystroke would relayout three times.
392
+
393
+ ## What is not built yet
394
+
395
+ [`../PLAN_INSPECTOR.md`](../PLAN_INSPECTOR.md) is the whole design. Built:
396
+ the walk and the panel, attribution, the cascade view, and CSS as a live input.
397
+ Not here yet, in the order they are worth doing:
398
+
399
+ * **editing a rule in place** rather than appending to the sheet. The
400
+ provenance names the rule and the panel prints its selector; what is missing
401
+ is finding that block in the text and writing back into it. Until then an
402
+ edit is appended, which always wins and is easy to strip, but does not teach
403
+ you where the value really lives.
404
+ * **a second host with a live sheet.** Only the dashboard is wired to
405
+ `css()`/`setCss()` today. The other seventeen demos each have one, and the
406
+ PPTX editor takes chrome CSS through `setChromeCss` — the wiring is four
407
+ methods and a line in the demo registry.
408
+ * **the transports the async adapter now allows** — a DevTools extension over
409
+ `inspectedWindow.eval`, a native target over a socket, a paused page
410
+ answering out of a snapshot. The panel no longer assumes any of them answer
411
+ on the same tick, which was the part that had to be decided early.
412
+ * **component debug notes** — a component saying *why*, which no channel
413
+ carries today.
414
+ * **the offline bundle** — one frame in a file, openable with no app running,
415
+ and attachable to a failing CI gate.
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+ //
4
+ // The panel, in a real browser, over the PPTX slide editor.
5
+ //
6
+ // npm run evg:inspect:web
7
+ //
8
+ // `inspect-check.mjs` gates the four channels in Node, where the app is an
9
+ // object. This gates the half that only exists in a page: that `?inspect=1`
10
+ // actually attaches something, that the tree it read has rows in it, and that
11
+ // selecting a node fills the panes. The wiring between a host and the panel is
12
+ // three functions long and therefore exactly the kind of thing that breaks
13
+ // without any test noticing.
14
+ //
15
+ // The SVG editor is the target because it needs no GPU: `--dump-dom` in
16
+ // headless Chromium is enough, and the page draws the same picture the WebGL
17
+ // one does.
18
+
19
+ import fs from "node:fs";
20
+ import http from "node:http";
21
+ import path from "node:path";
22
+ import { spawn } from "node:child_process";
23
+ import { fileURLToPath } from "node:url";
24
+
25
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
26
+ const ROOT = path.resolve(HERE, "..", "..", "..");
27
+ const DIST = path.join(ROOT, "gallery/pptx/web/html/dist");
28
+ const PORT = 8117;
29
+ const MIME = {
30
+ ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8",
31
+ ".mjs": "text/javascript; charset=utf-8", ".css": "text/css", ".json": "application/json",
32
+ ".ttf": "font/ttf", ".txt": "text/plain; charset=utf-8", ".svg": "image/svg+xml",
33
+ ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
34
+ ".odp": "application/vnd.oasis.opendocument.presentation", ".png": "image/png",
35
+ };
36
+
37
+ function findChrome() {
38
+ const named = [process.env.CHROME_PATH, "/usr/bin/chromium", "/usr/bin/chromium-browser",
39
+ "/usr/bin/google-chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"].filter(Boolean);
40
+ for (const c of named) { try { if (fs.existsSync(c)) return c; } catch { /* keep looking */ } }
41
+ const pw = process.env.PLAYWRIGHT_BROWSERS_PATH || "/opt/pw-browsers";
42
+ try {
43
+ for (const dir of fs.readdirSync(pw)) {
44
+ const c = path.join(pw, dir, "chrome-linux", "chrome");
45
+ if (fs.existsSync(c)) return c;
46
+ }
47
+ } catch { /* none */ }
48
+ return null;
49
+ }
50
+
51
+ function serve() {
52
+ const server = http.createServer((req, res) => {
53
+ const rel = decodeURIComponent((req.url || "/").split("?")[0]);
54
+ const file = path.join(DIST, rel === "/" ? "index.html" : rel);
55
+ if (!file.startsWith(DIST) || !fs.existsSync(file)) { res.writeHead(404); res.end("no"); return; }
56
+ res.writeHead(200, { "content-type": MIME[path.extname(file)] || "application/octet-stream", "cache-control": "no-store" });
57
+ res.end(fs.readFileSync(file));
58
+ });
59
+ return new Promise((r) => server.listen(PORT, "127.0.0.1", () => r(server)));
60
+ }
61
+
62
+ function runChrome(bin, args) {
63
+ return new Promise((resolve) => {
64
+ const child = spawn(bin, args, {
65
+ env: { ...process.env, HTTP_PROXY: "", HTTPS_PROXY: "", http_proxy: "", https_proxy: "", NO_PROXY: "*", no_proxy: "*" },
66
+ });
67
+ let out = "", err = "";
68
+ const kill = setTimeout(() => child.kill("SIGKILL"), 120000);
69
+ child.stdout.on("data", (d) => (out += d));
70
+ child.stderr.on("data", (d) => (err += d));
71
+ child.on("close", (status) => { clearTimeout(kill); resolve({ stdout: out, stderr: err, status }); });
72
+ child.on("error", (error) => { clearTimeout(kill); resolve({ stdout: out, stderr: err, status: -1, error }); });
73
+ });
74
+ }
75
+
76
+ if (!fs.existsSync(path.join(DIST, "pptx_web.js"))) {
77
+ console.error("no build in " + path.relative(ROOT, DIST) + " — run: npm run pptx:html");
78
+ process.exit(1);
79
+ }
80
+ const chrome = findChrome();
81
+ if (!chrome) { console.error("no Chrome found — set CHROME_PATH to one"); process.exit(1); }
82
+
83
+ const server = await serve();
84
+ let failed = 0;
85
+ const ok = (name, cond, detail) => {
86
+ if (cond) console.log(" ok " + name);
87
+ else { failed++; console.log(" FAIL " + name + (detail ? " — " + detail : "")); }
88
+ };
89
+
90
+ try {
91
+ // The panel opens on the root, so a dumped DOM already carries a selection
92
+ // and a filled pane — no script has to be injected to make it interesting.
93
+ const url = `http://127.0.0.1:${PORT}/index.html?inspect=1`;
94
+ const run = await runChrome(chrome, ["--headless=new", "--no-sandbox", "--no-proxy-server",
95
+ "--proxy-bypass-list=<-loopback>", "--disable-dev-shm-usage",
96
+ "--virtual-time-budget=25000", "--dump-dom", url]);
97
+ const dom = run.stdout || "";
98
+ if (process.env.SMOKE_DEBUG) fs.writeFileSync("/tmp/evg-inspect-dom.html", dom);
99
+
100
+ ok("the panel attached", /class="evgi"/.test(dom));
101
+ const rows = (dom.match(/class="evgi-row/g) || []).length;
102
+ ok("the tree has rows", rows >= 3, rows + " rows");
103
+ const foot = (dom.match(/class="evgi-foot"[^>]*>([^<]*)/) || [])[1] || "";
104
+ ok("the footer counted nodes and commands", /\d+ nodes/.test(foot) && /\d+ commands/.test(foot), JSON.stringify(foot));
105
+ ok("the box-model diagram is there", /evgi-bm/.test(dom));
106
+ ok("the overlay is mounted over the picture", /class="evgi-ov"/.test(dom));
107
+ ok("a node is selected", /evgi-row sel/.test(dom));
108
+ ok("the panes filled", /evgi-kv/.test(dom));
109
+ if (failed && process.env.SMOKE_DEBUG !== "1") console.log("\n re-run with SMOKE_DEBUG=1 to dump the DOM to /tmp");
110
+ } finally {
111
+ server.close();
112
+ }
113
+
114
+ console.log(failed ? `\n${failed} failed\n` : "\nthe inspector attached to the slide editor and read its tree\n");
115
+ process.exit(failed ? 1 : 0);