ranger-compiler 3.5.1 → 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 +1064 -20
  2. package/LICENSE +3 -1
  3. package/README.md +123 -28
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79923 -56921
  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,166 @@
1
+ <!doctype html>
2
+ <!--
3
+ Element-scoped surface effects, live.
4
+
5
+ npm run evg:fx:doc writes lib/evg/gl/fx-demo.js from the Ranger document
6
+ npm run evg:fx:demo → http://localhost:8099/fx-demo.html
7
+
8
+ WHAT THIS PAGE DOES NOT DO is as much the point as what it does. It does not
9
+ say where the sky is, how fast it drifts, which boxes ripple, what starts a
10
+ ripple, or how strong one is. All of that is in lib/evg/FxDemoDoc.rgr, in a
11
+ stylesheet, on elements with names — and it arrives here as
12
+ `window.EVG_FX_DEMO`, the display list that document lays out to.
13
+
14
+ The page is three things: a canvas, a driver that turns pointer events into
15
+ effect events, and a loop that draws while anything is moving.
16
+ -->
17
+ <meta charset="utf-8">
18
+ <title>EVG surface effects</title>
19
+ <style>
20
+ body {
21
+ margin: 0;
22
+ background: #0a0b14;
23
+ color: #aeb6d8;
24
+ font: 14px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ gap: 18px;
29
+ padding: 28px 16px 48px;
30
+ }
31
+ canvas { border-radius: 16px; box-shadow: 0 24px 60px rgba(0, 0, 0, .55); touch-action: none; max-width: 100%; }
32
+ .wrap { max-width: 900px; width: 100%; }
33
+ pre {
34
+ background: #11131f;
35
+ border: 1px solid #1e2133;
36
+ border-radius: 12px;
37
+ padding: 14px 16px;
38
+ overflow: auto;
39
+ color: #c6cdf0;
40
+ font-size: 12.5px;
41
+ }
42
+ b { color: #e6ebff; font-weight: 600; }
43
+ .status { font-variant-numeric: tabular-nums; color: #6f78a0; }
44
+ .switches { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
45
+ .switches button {
46
+ font: inherit;
47
+ color: #c6cdf0;
48
+ background: #171a29;
49
+ border: 1px solid #2a2f47;
50
+ border-radius: 999px;
51
+ padding: 7px 15px 7px 12px;
52
+ cursor: pointer;
53
+ display: inline-flex;
54
+ align-items: center;
55
+ gap: 9px;
56
+ }
57
+ .switches button:hover { border-color: #3c4467; }
58
+ .switches button::before {
59
+ content: "";
60
+ width: 9px;
61
+ height: 9px;
62
+ border-radius: 50%;
63
+ background: #39405c;
64
+ box-shadow: none;
65
+ }
66
+ .switches button[aria-pressed="true"] { color: #eaf0ff; border-color: #46507e; }
67
+ .switches button[aria-pressed="true"]::before {
68
+ background: #8ea2ff;
69
+ box-shadow: 0 0 10px 2px rgba(142, 162, 255, .55);
70
+ }
71
+ .switches span { color: #6f78a0; }
72
+ </style>
73
+ <div class="wrap">
74
+ <canvas id="c" width="900" height="560"></canvas>
75
+ <div class="switches" id="switches"><span>effects:</span></div>
76
+ <p>Press the two dark cards. Press the sky. The page below is the document that decides all of it.</p>
77
+ <p class="status">To <em>edit</em> that stylesheet and watch the effects change as you type, the gallery's
78
+ copy of this demo has the compiled engine in its bundle and an editor under the canvas:
79
+ <code>npm run ui:demo:page</code>, then <code>?demo=effects</code>. This page carries only the display
80
+ list that document laid out to, so there is nothing here to re-run a stylesheet through.</p>
81
+ <pre id="sheet"></pre>
82
+ <p class="status" id="status"></p>
83
+ </div>
84
+ <script src="./fx-demo.js"></script>
85
+ <script type="module">
86
+ import { prepareDisplayList, surfaceEffectNames } from "./evg-webgl.js";
87
+ import { createEffectDriver, attachEffectPointer } from "./evg-fx.js";
88
+
89
+ const doc = window.EVG_FX_DEMO;
90
+ const canvas = document.getElementById("c");
91
+ const dpr = Math.min(2, window.devicePixelRatio || 1);
92
+ canvas.width = doc.width * dpr;
93
+ canvas.height = doc.height * dpr;
94
+ canvas.style.width = doc.width + "px";
95
+ canvas.style.height = doc.height + "px";
96
+ const gl = canvas.getContext("webgl2", { antialias: true, alpha: false });
97
+
98
+ // The effects this document asks for, with what the sheet said about each —
99
+ // printed rather than hard-coded, because nothing on this page knows them.
100
+ document.getElementById("sheet").textContent = (doc.list.effects || [])
101
+ .map((e) => `#${e.id} { evg-surface-effect: ${e.kind}; evg-effect-on: ${e.on || "(the app)"} }\n` +
102
+ ` box from the layout: ${e.box.map(Math.round).join(", ")}\n` +
103
+ ` ${Object.entries(e.p).map(([k, v]) => `evg-fx-${k}: ${v}`).join("; ")}`)
104
+ .join("\n\n");
105
+
106
+ const fx = createEffectDriver();
107
+ // The frame is built once and drawn many times: an effect moving changes no
108
+ // box, no glyph and no buffer — only the numbers the plugin reads.
109
+ const frame = prepareDisplayList(gl, doc, { dpr });
110
+
111
+ // A SWITCH PER EFFECT, built from the list rather than from a list of names
112
+ // written here: the page still knows nothing about what a starfield is, only
113
+ // that the document declared three of something and what each one is called.
114
+ //
115
+ // Turning one off sets `off` on the instance, which the painter and the
116
+ // driver both read — so the pass is skipped, the shader is never run, and a
117
+ // press on a sleeping pool goes nowhere. The frame is NOT rebuilt: not a
118
+ // single buffer is re-uploaded to stop drawing one shader.
119
+ //
120
+ // The choice is remembered per browser, because a page that forgets which
121
+ // effects you turned off is a page you turn them off on again every reload.
122
+ const REMEMBER = "evg-fx-demo-off";
123
+ let off = new Set();
124
+ try { off = new Set(JSON.parse(localStorage.getItem(REMEMBER) || "[]")); } catch (e) { /* private window */ }
125
+ const remember = () => {
126
+ try { localStorage.setItem(REMEMBER, JSON.stringify([...off])); } catch (e) { /* private window */ }
127
+ };
128
+
129
+ const switches = document.getElementById("switches");
130
+ for (const inst of doc.list.effects || []) {
131
+ inst.off = off.has(inst.id);
132
+ const b = document.createElement("button");
133
+ b.type = "button";
134
+ b.textContent = `${inst.id} · ${inst.kind}`;
135
+ b.setAttribute("aria-pressed", String(!inst.off));
136
+ b.addEventListener("click", () => {
137
+ inst.off = !inst.off;
138
+ if (inst.off) off.add(inst.id); else off.delete(inst.id);
139
+ b.setAttribute("aria-pressed", String(!inst.off));
140
+ remember();
141
+ // One frame at least, so switching something OFF repaints the page
142
+ // without it even though nothing is moving any more.
143
+ schedule();
144
+ });
145
+ switches.appendChild(b);
146
+ }
147
+
148
+ let raf = 0;
149
+ let last = 0;
150
+ const draw = (now) => {
151
+ raf = 0;
152
+ const dt = last ? now - last : 16;
153
+ last = now;
154
+ const busy = fx.tick(dt, doc.list);
155
+ frame.draw();
156
+ document.getElementById("status").textContent =
157
+ `${surfaceEffectNames().join(", ")} registered · ` +
158
+ (doc.list.effects || []).map((e) =>
159
+ e.off ? `${e.id}: off` : `${e.id}: ${(e.events || []).length} in flight`).join(" · ");
160
+ if (busy) schedule();
161
+ };
162
+ const schedule = () => { if (!raf) raf = requestAnimationFrame(draw); };
163
+
164
+ attachEffectPointer(canvas, fx, schedule);
165
+ schedule();
166
+ </script>
@@ -0,0 +1,2 @@
1
+ // Written by `npm run evg:fx:doc` from lib/evg/gl/FxDemoDoc.rgr — do not edit.
2
+ window.EVG_FX_DEMO = { width: 900, height: 560, list: {"effect":{"kind":"starfield","drops":[],"speed":220.00,"width":28.00,"strength":7.00,"decay":1.80,"highlight":0.08,"rings":3.00,"stagger":0.09,"falloff":0.62,"shine":0.45,"gloss":120.00,"bump":70.00,"light":[-0.45,-0.65,0.62]},"effects":[{"id":"sky","kind":"starfield","on":"always press","box":[0.00,0.00,900.00,320.00],"p":{"density":1.60,"nebula":0.80,"speed":7.00,"angle":200.00,"hue":228.00,"hue2":305.00,"seed":5.00}},{"id":"glass","kind":"liquid-glass","on":"always","box":[520.00,48.00,320.00,210.00],"r":30.00,"p":{"thickness":26.00,"strength":36.00,"disperse":0.08,"shine":0.55,"sweep":0.70,"sweep-width":0.05,"sweep-speed":0.42,"sweep-duty":0.30,"sweep-rim":0.60,"sweep-edge":2.60}},{"id":"pool","kind":"ripple","on":"press drag","box":[48.00,368.00,250.00,150.00],"r":18.00,"p":{"speed":240.00,"width":28.00,"strength":9.00,"decay":1.50,"highlight":0.08,"rings":3.00,"stagger":0.09,"falloff":0.62,"shine":0.45,"gloss":120.00,"bump":70.00,"lightX":-0.45,"lightY":-0.65,"lightZ":0.62}},{"id":"pool2","kind":"ripple","on":"press drag","box":[325.00,368.00,250.00,150.00],"r":18.00,"p":{"speed":240.00,"width":28.00,"strength":9.00,"decay":1.50,"highlight":0.08,"rings":3.00,"stagger":0.09,"falloff":0.62,"shine":0.45,"gloss":120.00,"bump":70.00,"lightX":-0.45,"lightY":-0.65,"lightZ":0.62}}],"cmds":[{"k":0,"x":0.00,"y":0.00,"w":900.00,"h":560.00,"c":[8,9,20,1.00]},{"k":0,"x":0.00,"y":0.00,"w":900.00,"h":320.00,"c":[6,7,20,1.00],"efx":"sky"},{"k":3,"x":48.00,"y":42.00,"w":487.59,"h":39.10,"c":[240,242,255,1.00],"text":"A sky, declared in the stylesheet","font":"Noto Sans","size":34.00},{"k":3,"x":48.00,"y":92.00,"w":382.69,"h":17.25,"c":[168,176,214,1.00],"text":"evg-surface-effect: starfield, and this line is painted over it","font":"Noto Sans","size":15.00},{"k":0,"x":520.00,"y":48.00,"w":320.00,"h":210.00,"r":30.00,"c":[255,255,255,0.07],"efx":"glass","bb":9.00},{"k":3,"x":552.00,"y":82.00,"w":100.34,"h":21.85,"c":[255,255,255,1.00],"text":"Liquid glass","font":"Noto Sans","size":19.00},{"k":3,"x":552.00,"y":118.00,"w":176.30,"h":14.95,"c":[214,220,248,1.00],"text":"evg-surface-effect: liquid-glass","font":"Noto Sans","size":13.00},{"k":3,"x":552.00,"y":142.00,"w":242.80,"h":14.95,"c":[214,220,248,1.00],"text":"the sky bends at the rim; this line does not","font":"Noto Sans","size":13.00},{"k":0,"x":48.00,"y":368.00,"w":250.00,"h":150.00,"r":18.00,"c":[22,26,52,1.00],"efx":"pool"},{"k":0,"x":325.00,"y":368.00,"w":250.00,"h":150.00,"r":18.00,"c":[22,26,52,1.00],"efx":"pool2"},{"k":0,"x":602.00,"y":368.00,"w":250.00,"h":150.00,"r":18.00,"c":[22,26,52,1.00]},{"k":3,"x":72.00,"y":400.00,"w":63.35,"h":17.25,"c":[226,230,248,1.00],"text":"Press me","font":"Noto Sans","size":15.00},{"k":3,"x":72.00,"y":428.00,"w":144.52,"h":14.95,"c":[150,158,196,1.00],"text":"evg-effect-on: press drag","font":"Noto Sans","size":13.00},{"k":3,"x":349.00,"y":400.00,"w":129.24,"h":17.25,"c":[226,230,248,1.00],"text":"And me, separately","font":"Noto Sans","size":15.00},{"k":3,"x":349.00,"y":428.00,"w":184.30,"h":14.95,"c":[150,158,196,1.00],"text":"one effect per box, not per page","font":"Noto Sans","size":13.00},{"k":3,"x":626.00,"y":400.00,"w":48.35,"h":17.25,"c":[226,230,248,1.00],"text":"Not me","font":"Noto Sans","size":15.00},{"k":3,"x":626.00,"y":428.00,"w":103.35,"h":14.95,"c":[150,158,196,1.00],"text":"no effect declared","font":"Noto Sans","size":13.00}],"seq":0,"shifts":[]} };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The effects demo, served.
3
+ *
4
+ * npm run evg:fx:demo then open http://localhost:8099/fx-demo.html
5
+ *
6
+ * A static server over this one directory, because `fx-demo.html` imports
7
+ * `evg-webgl.js` and `evg-fx.js` as ES modules and a browser refuses a module
8
+ * over `file://`. Nothing else about the page needs a server: the document it
9
+ * draws is a script tag, not a fetch.
10
+ */
11
+
12
+ import fs from "node:fs";
13
+ import path from "node:path";
14
+ import http from "node:http";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
18
+ const PORT = Number(process.env.PORT || 8099);
19
+ const TYPES = {
20
+ ".html": "text/html; charset=utf-8",
21
+ ".js": "text/javascript; charset=utf-8",
22
+ ".mjs": "text/javascript; charset=utf-8",
23
+ ".json": "application/json; charset=utf-8",
24
+ ".css": "text/css; charset=utf-8",
25
+ ".png": "image/png",
26
+ };
27
+
28
+ if (!fs.existsSync(path.join(HERE, "fx-demo.js"))) {
29
+ console.error("fx-demo.js is missing — run `npm run evg:fx:doc` first");
30
+ process.exit(3);
31
+ }
32
+
33
+ http.createServer((req, res) => {
34
+ const name = decodeURIComponent((req.url || "/").split("?")[0]).replace(/^\/+/, "") || "fx-demo.html";
35
+ // One directory, and only this one: a demo server that can be asked for
36
+ // `../../../etc/passwd` is a demo server nobody should run.
37
+ const file = path.join(HERE, path.basename(name));
38
+ if (!fs.existsSync(file) || !fs.statSync(file).isFile()) {
39
+ res.writeHead(404, { "content-type": "text/plain" });
40
+ res.end("not here: " + name);
41
+ return;
42
+ }
43
+ res.writeHead(200, { "content-type": TYPES[path.extname(file)] || "application/octet-stream" });
44
+ res.end(fs.readFileSync(file));
45
+ }).listen(PORT, () => {
46
+ console.log(`EVG effects demo: http://localhost:${PORT}/fx-demo.html`);
47
+ });
@@ -0,0 +1,189 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // `evg-gestures.js` against a canvas that is not one: the module wants an
4
+ // element it can listen on and measure, and nothing else, so the gestures can
5
+ // be checked without a browser — including the two-finger pinch, which is the
6
+ // one no headless driver will send for you.
7
+ //
8
+ // node lib/evg/gl/gestures-check.mjs
9
+
10
+ import { attachViewGestures } from "./evg-gestures.js";
11
+
12
+ let passed = 0;
13
+ let failed = 0;
14
+ const ok = (name, cond, detail) => {
15
+ if (cond) {
16
+ passed += 1;
17
+ console.log(" PASS " + name + (detail ? " (" + detail + ")" : ""));
18
+ } else {
19
+ failed += 1;
20
+ console.log(" FAIL " + name + (detail ? " (" + detail + ")" : ""));
21
+ }
22
+ };
23
+ const near = (a, b, eps = 0.001) => Math.abs(a - b) <= eps;
24
+
25
+ /** A canvas at the page origin, 1000 x 800, that records its listeners. */
26
+ function fakeCanvas() {
27
+ const handlers = new Map();
28
+ return {
29
+ style: {},
30
+ addEventListener: (t, f) => handlers.set(t, [...(handlers.get(t) || []), f]),
31
+ removeEventListener: (t, f) => handlers.set(t, (handlers.get(t) || []).filter((g) => g !== f)),
32
+ getBoundingClientRect: () => ({ left: 0, top: 0, width: 1000, height: 800 }),
33
+ setPointerCapture: () => {},
34
+ hasPointerCapture: () => true,
35
+ releasePointerCapture: () => {},
36
+ fire(type, ev) {
37
+ for (const f of handlers.get(type) || []) f({ preventDefault() {}, ...ev });
38
+ },
39
+ };
40
+ }
41
+
42
+ function rig(start = { x: 0, y: 0, sc: 1 }) {
43
+ const el = fakeCanvas();
44
+ const state = { ...start };
45
+ const taps = [];
46
+ const cursors = [];
47
+ attachViewGestures(el, {
48
+ view: () => ({ ...state }),
49
+ setView: (x, y, sc) => { state.x = x; state.y = y; state.sc = sc; },
50
+ minZoom: 0.05,
51
+ maxZoom: 16,
52
+ onTap: (x, y) => taps.push([x, y]),
53
+ onCursor: (c) => cursors.push(c),
54
+ });
55
+ return { el, state, taps, cursors };
56
+ }
57
+
58
+ const down = (el, id, x, y) => el.fire("pointerdown", { pointerId: id, clientX: x, clientY: y, button: 0 });
59
+ const move = (el, id, x, y) => el.fire("pointermove", { pointerId: id, clientX: x, clientY: y });
60
+ const up = (el, id, x, y) => el.fire("pointerup", { pointerId: id, clientX: x, clientY: y, button: 0 });
61
+ /** Where a point on the canvas is in the scene, which is what a zoom holds. */
62
+ const world = (v, px, py) => ({ x: (px - v.x) / v.sc, y: (py - v.y) / v.sc });
63
+
64
+ console.log("=== evg-gestures ===");
65
+
66
+ // --- drag pans by exactly what the pointer travelled ------------------------
67
+ {
68
+ const { el, state, taps } = rig({ x: 10, y: 20, sc: 0.5 });
69
+ down(el, 1, 100, 100);
70
+ move(el, 1, 160, 130);
71
+ up(el, 1, 160, 130);
72
+ ok("a drag pans by the travel", near(state.x, 70) && near(state.y, 50), `${state.x},${state.y}`);
73
+ ok("and the scale is untouched", near(state.sc, 0.5));
74
+ ok("a drag is not a tap", taps.length === 0);
75
+ }
76
+
77
+ // --- a press that does not travel is a tap ---------------------------------
78
+ {
79
+ const { el, state, taps } = rig();
80
+ down(el, 1, 300, 400);
81
+ move(el, 1, 301, 401);
82
+ up(el, 1, 301, 401);
83
+ ok("a press that does not travel is a tap", taps.length === 1, JSON.stringify(taps[0]));
84
+ ok("and it did not move the view", near(state.x, 0) && near(state.y, 0));
85
+ }
86
+
87
+ // --- the wheel zooms about the pointer -------------------------------------
88
+ {
89
+ const { el, state } = rig({ x: -200, y: -100, sc: 1 });
90
+ const before = { ...state };
91
+ const w0 = world(before, 400, 300);
92
+ el.fire("wheel", { deltaY: -300, deltaMode: 0, ctrlKey: false, clientX: 400, clientY: 300 });
93
+ ok("a wheel up zooms in", state.sc > before.sc, `${before.sc} → ${state.sc.toFixed(3)}`);
94
+ const w1 = world(state, 400, 300);
95
+ ok("and holds the point under the pointer", near(w0.x, w1.x, 0.02) && near(w0.y, w1.y, 0.02),
96
+ `${w0.x.toFixed(2)},${w0.y.toFixed(2)} vs ${w1.x.toFixed(2)},${w1.y.toFixed(2)}`);
97
+ el.fire("wheel", { deltaY: 300, deltaMode: 0, ctrlKey: false, clientX: 400, clientY: 300 });
98
+ ok("and a wheel down puts it back", near(state.sc, before.sc, 0.0001), state.sc.toFixed(4));
99
+ }
100
+
101
+ // --- a trackpad pinch is a wheel with ctrl, and moves faster per unit -------
102
+ {
103
+ const plain = rig();
104
+ plain.el.fire("wheel", { deltaY: -40, deltaMode: 0, ctrlKey: false, clientX: 500, clientY: 400 });
105
+ const pinch = rig();
106
+ pinch.el.fire("wheel", { deltaY: -40, deltaMode: 0, ctrlKey: true, clientX: 500, clientY: 400 });
107
+ ok("ctrl+wheel zooms further than the same wheel", pinch.state.sc > plain.state.sc * 1.2,
108
+ `${plain.state.sc.toFixed(3)} vs ${pinch.state.sc.toFixed(3)}`);
109
+ }
110
+
111
+ // --- a line-mode wheel is a notch, not half a one --------------------------
112
+ {
113
+ const px = rig();
114
+ px.el.fire("wheel", { deltaY: -100, deltaMode: 0, clientX: 500, clientY: 400 });
115
+ const lines = rig();
116
+ lines.el.fire("wheel", { deltaY: -3, deltaMode: 1, clientX: 500, clientY: 400 });
117
+ ok("a notch is a notch in lines and in pixels", near(px.state.sc, lines.state.sc, 0.02),
118
+ `${px.state.sc.toFixed(3)} vs ${lines.state.sc.toFixed(3)}`);
119
+ }
120
+
121
+ // --- two fingers pinch about their midpoint --------------------------------
122
+ {
123
+ const { el, state } = rig({ x: 40, y: 60, sc: 0.5 });
124
+ const before = { ...state };
125
+ const w0 = world(before, 450, 300);
126
+ down(el, 1, 400, 300);
127
+ down(el, 2, 500, 300); // 100 apart, midpoint 450,300
128
+ move(el, 1, 350, 300);
129
+ move(el, 2, 550, 300); // 200 apart, same midpoint
130
+ ok("spreading to twice the gap doubles the scale", near(state.sc, before.sc * 2, 0.0001),
131
+ `${before.sc} → ${state.sc}`);
132
+ const w1 = world(state, 450, 300);
133
+ ok("and the midpoint holds its place in the scene", near(w0.x, w1.x, 0.01) && near(w0.y, w1.y, 0.01),
134
+ `${w0.x.toFixed(2)},${w0.y.toFixed(2)} vs ${w1.x.toFixed(2)},${w1.y.toFixed(2)}`);
135
+ up(el, 1, 350, 300);
136
+ up(el, 2, 550, 300);
137
+ }
138
+
139
+ // --- fingers that move together pan ----------------------------------------
140
+ {
141
+ const { el, state } = rig({ x: 0, y: 0, sc: 1 });
142
+ down(el, 1, 400, 300);
143
+ down(el, 2, 500, 300);
144
+ move(el, 1, 430, 340);
145
+ move(el, 2, 530, 340); // same gap, midpoint moved +30,+40
146
+ ok("two fingers moving together pan", near(state.x, 30) && near(state.y, 40), `${state.x},${state.y}`);
147
+ ok("and do not change the scale", near(state.sc, 1));
148
+ up(el, 1, 430, 340);
149
+ up(el, 2, 530, 340);
150
+ }
151
+
152
+ // --- lifting one finger keeps the other panning ----------------------------
153
+ {
154
+ const { el, state, taps } = rig({ x: 0, y: 0, sc: 1 });
155
+ down(el, 1, 400, 300);
156
+ down(el, 2, 500, 300);
157
+ move(el, 1, 400, 300);
158
+ up(el, 2, 500, 300);
159
+ move(el, 1, 440, 320);
160
+ ok("the finger left on the glass goes on panning", near(state.x, 40) && near(state.y, 20), `${state.x},${state.y}`);
161
+ up(el, 1, 440, 320);
162
+ ok("and the end of a pinch is not a tap", taps.length === 0);
163
+ }
164
+
165
+ // --- the clamps hold -------------------------------------------------------
166
+ {
167
+ const { el, state } = rig({ x: 0, y: 0, sc: 1 });
168
+ for (let i = 0; i < 40; i += 1) el.fire("wheel", { deltaY: -240, deltaMode: 0, clientX: 500, clientY: 400 });
169
+ ok("zoom stops at the ceiling", near(state.sc, 16), state.sc.toFixed(3));
170
+ for (let i = 0; i < 80; i += 1) el.fire("wheel", { deltaY: 240, deltaMode: 0, clientX: 500, clientY: 400 });
171
+ ok("and at the floor", near(state.sc, 0.05), state.sc.toFixed(3));
172
+ }
173
+
174
+ // --- Safari's own pinch ----------------------------------------------------
175
+ {
176
+ const { el, state } = rig();
177
+ el.fire("gesturestart", { scale: 1, clientX: 500, clientY: 400 });
178
+ el.fire("gesturechange", { scale: 1.5, clientX: 500, clientY: 400 });
179
+ ok("a gesture event zooms by its scale", near(state.sc, 1.5, 0.001), state.sc.toFixed(3));
180
+ el.fire("gestureend", { scale: 1.5, clientX: 500, clientY: 400 });
181
+ }
182
+
183
+ console.log("");
184
+ console.log(`passed=${passed} failed=${failed}`);
185
+ if (failed > 0) {
186
+ console.log("SOME FAILED");
187
+ process.exit(1);
188
+ }
189
+ console.log("ALL PASS");
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+ //
4
+ // Does the list say the same thing after crossing a thread?
5
+ //
6
+ // npm run evg:binary:check
7
+ //
8
+ // A host that shares a heap with the engine reads the display list off the
9
+ // Ranger object (`evg-list.js`). A host whose engine is in a Worker gets
10
+ // `toBinary()` — a positional record per command — and reads it with
11
+ // `evg-binary.js`. Both are meant to hand the painter the SAME command
12
+ // objects, and this holds them against each other on real frames: every
13
+ // scene of the RealTrainer app, at a phone's size, including a frame with a
14
+ // scroll layer, four-cornered radii, gradients, rotated runs and paths.
15
+ //
16
+ // A positional format's failure mode is plausible garbage, not a crash
17
+ // (ISSUES #4), so the comparison is key for key and value for value, on
18
+ // every command — not a count, not a spot check.
19
+
20
+ import fs from "node:fs";
21
+ import path from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+ import { createRequire } from "node:module";
24
+ import { cmdsOf, shiftsOf } from "./evg-list.js";
25
+ import { cmdsOfBinary, binaryStride, FIELDS_READ } from "./evg-binary.js";
26
+
27
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
28
+ const ROOT = path.resolve(HERE, "..", "..", "..");
29
+ const RT = path.join(ROOT, "gallery", "realtrainer");
30
+ const BIN = path.join(RT, "bin", "RealTrainerDemo.cjs");
31
+ if (!fs.existsSync(BIN)) {
32
+ console.error("compiled app missing — run `npm run rt:build` first");
33
+ process.exit(3);
34
+ }
35
+ const require_ = createRequire(import.meta.url);
36
+ const { RealTrainerDemo } = require_(BIN);
37
+ const read = (...p) => fs.readFileSync(path.join(RT, ...p), "utf8");
38
+
39
+ const app = new RealTrainerDemo();
40
+ app.init(read("web", "realtrainer.css"), read("fixtures", "session.compact"));
41
+ app.loadPlanMachine(read("fixtures", "machines", "planDialog.machine.json"));
42
+ app.loadChatMachine(read("fixtures", "machines", "chat.machine.json"));
43
+ app.loadReference(read("fixtures", "reference", "seed.json"));
44
+ app.setPageSize(390, 844);
45
+
46
+ let passed = 0, failed = 0;
47
+ const ok = (what, cond, detail = "") => {
48
+ if (cond) passed += 1; else failed += 1;
49
+ console.log(` ${cond ? "PASS" : "FAIL"} ${what}${cond || !detail ? "" : " (" + detail + ")"}`);
50
+ };
51
+
52
+ // The object reader gives the layout's own doubles; the binary gives
53
+ // hundredths. `toJson` writes hundredths too, so the binary is exactly the
54
+ // JSON's numbers, and the object path is held to the same rounding here.
55
+ const r2 = (v) => Math.round(v * 100) / 100;
56
+ function normalise(o) {
57
+ const out = {};
58
+ for (const k of Object.keys(o).sort()) {
59
+ const v = o[k];
60
+ if (typeof v === "number") out[k] = r2(v);
61
+ else if (Array.isArray(v)) out[k] = v.map((x) => (typeof x === "number" ? r2(x) : x));
62
+ else if (v && typeof v === "object") out[k] = normalise(v);
63
+ else out[k] = v;
64
+ }
65
+ return out;
66
+ }
67
+
68
+ function compare(label, dl) {
69
+ const a = cmdsOf(dl).map(normalise);
70
+ const bin = dl.toBinary();
71
+ const b = cmdsOfBinary(bin).map(normalise);
72
+ // At LEAST as wide as this reader wants, not exactly: the format grows by
73
+ // adding slots, and a reader takes the ones it knows (ISSUES #4). Pinning
74
+ // it to the number turned every addition into a failure of this check
75
+ // rather than of anything that draws — and the point of the check is the
76
+ // comparison below, which is what tells a new slot from a shifted one.
77
+ ok(`${label}: the record is at least ${FIELDS_READ} wide`, binaryStride(bin) >= FIELDS_READ, `stride=${binaryStride(bin)}`);
78
+ ok(`${label}: ${a.length} commands both ways`, a.length === b.length, `object=${a.length} binary=${b.length}`);
79
+ let firstDiff = "";
80
+ const n = Math.min(a.length, b.length);
81
+ for (let i = 0; i < n && !firstDiff; i += 1) {
82
+ const ja = JSON.stringify(a[i]), jb = JSON.stringify(b[i]);
83
+ if (ja !== jb) firstDiff = `command ${i}:\n object ${ja}\n binary ${jb}`;
84
+ }
85
+ ok(`${label}: every command reads the same`, firstDiff === "", firstDiff);
86
+ const kinds = new Set(a.map((c) => c.k));
87
+ return { cmds: a, kinds, layers: a.some((c) => c.layer > 0), rc: a.some((c) => c.rc), grad: a.some((c) => c.gd !== undefined), rot: a.some((c) => c.rot), sh: b.some((c) => c.sh) };
88
+ }
89
+
90
+ const seen = { kinds: new Set(), layers: false, rc: false, grad: false, rot: false, sh: false };
91
+ for (const scene of ["loading", "signin", "dashboard", "session", "document"]) {
92
+ app.setScene(scene);
93
+ app.rebuild();
94
+ const r = compare(scene, app.display());
95
+ for (const k of r.kinds) seen.kinds.add(k);
96
+ seen.layers ||= r.layers; seen.rc ||= r.rc; seen.grad ||= r.grad; seen.rot ||= r.rot; seen.sh ||= r.sh;
97
+ }
98
+ // A scrolled, kept list: the commands moved in place, the shifts beside them.
99
+ app.setScene("document");
100
+ app.rebuild();
101
+ app.scrollDocument(400);
102
+ const dl = app.display();
103
+ compare("document, scrolled", dl);
104
+ ok("the scrolled frame has a layer", cmdsOf(dl).some((c) => c.layer > 0));
105
+ ok("and its shifts are readable beside the record", Array.isArray(shiftsOf(dl)));
106
+
107
+ // What the app never draws, built by hand from the same module: a box with
108
+ // three corners rounded and a fourth square, under a drop shadow.
109
+ {
110
+ const M = require_(BIN);
111
+ const root = M.EVGElement.createDiv();
112
+ root.setAttribute("width", "200px");
113
+ root.setAttribute("height", "120px");
114
+ const box = M.EVGElement.createDiv();
115
+ box.setAttribute("width", "100px");
116
+ box.setAttribute("height", "40px");
117
+ box.setAttribute("background-color", "#336699");
118
+ box.setAttribute("border-radius", "8px 8px 0px 8px");
119
+ // The tree walk reads the shadow off these four, which is what the Figma
120
+ // reader writes; `box-shadow` as one CSS shorthand is not parsed.
121
+ box.setAttribute("shadow-offset-x", "2px");
122
+ box.setAttribute("shadow-offset-y", "3px");
123
+ box.setAttribute("shadow-radius", "6px");
124
+ box.setAttribute("shadow-color", "#00000055");
125
+ root.addChild(box);
126
+ const lay = new M.EVGLayout();
127
+ lay.setPageSize(200, 120);
128
+ lay.layout(root);
129
+ const dl = new M.EVGDisplayList();
130
+ dl.setTextEngine(lay.getTextEngine());
131
+ dl.build(root);
132
+ // The walked box above carries a shadow of its own now. This second one is
133
+ // built by hand, the way the office exporters build theirs, so both paths
134
+ // into `hasShadow` are held against the bridge.
135
+ const sh = new M.EVGDrawCmd();
136
+ sh.kind = 0; sh.x = 10; sh.y = 60; sh.w = 80; sh.h = 30; sh.r = 20; sh.g = 30; sh.b = 40;
137
+ sh.hasShadow = true; sh.shadowX = 2; sh.shadowY = 3; sh.shadowBlur = 6;
138
+ sh.shadowR = 1; sh.shadowG = 2; sh.shadowB = 3; sh.shadowA = 0.35;
139
+ dl.addCmd(sh);
140
+ const r = compare("a shadowed, four-cornered box", dl);
141
+ seen.rc ||= r.rc; seen.sh ||= r.sh;
142
+ }
143
+
144
+ console.log(` (kinds seen: ${[...seen.kinds].sort().join(" ")}; layers=${seen.layers} corners=${seen.rc} gradient=${seen.grad} rotation=${seen.rot} shadow=${seen.sh})`);
145
+ ok("the frames exercised a scroll layer", seen.layers);
146
+ ok("and four-cornered radii", seen.rc);
147
+ ok("and a rotation", seen.rot);
148
+ ok("and a shadow, which both readers now carry", seen.sh);
149
+
150
+ console.log(`\npassed = ${passed} failed = ${failed}`);
151
+ if (failed > 0) { console.log("FAILURES"); process.exit(1); }
152
+ console.log("ALL PASS");