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,651 @@
1
+ /**
2
+ * Build the EVG showcase: every page in every theme, rendered to PDF, PNG and
3
+ * HTML, plus an index that puts them side by side.
4
+ *
5
+ * node lib/evg/showcase/build.mjs # -> showcase/dist
6
+ * node lib/evg/showcase/build.mjs --out "$SITE/evg" # for Pages
7
+ *
8
+ * The point of the gallery is that the pages carry no visual attributes at
9
+ * all — they say what is on the page, and themes/showcase.css says how it
10
+ * looks. Rendering the same tree under two themes and three targets is the
11
+ * demonstration.
12
+ *
13
+ * Requires the Ranger compiler to be built (npm run compile).
14
+ */
15
+ import { execFileSync } from "node:child_process";
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+
20
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
21
+ const ROOT = path.resolve(HERE, "../../..");
22
+
23
+ const argv = process.argv.slice(2);
24
+ const outFlag = argv.indexOf("--out");
25
+ const OUT = outFlag >= 0 ? path.resolve(argv[outFlag + 1]) : path.join(HERE, "dist");
26
+ const TOOLS = path.join(OUT, ".tools");
27
+
28
+ /** Pages, in the order they appear in the gallery. */
29
+ const PAGES = [
30
+ {
31
+ id: "album",
32
+ title: "Album spread",
33
+ blurb:
34
+ "grid-template-areas draws the spread as a picture of names, so a theme can rearrange it without touching the tree.",
35
+ shows: ["grid-template-areas", "spans", "object-fit: cover", "gap"],
36
+ },
37
+ {
38
+ id: "cards",
39
+ title: "Cards on shared rows",
40
+ blurb:
41
+ "Three cards, three caption lengths, one baseline. Each card spans the deck's rows and declares grid-template-rows: subgrid, so the photo band and the caption band are shared.",
42
+ shows: ["subgrid (rows)", "grid spans", "text wrapping"],
43
+ },
44
+ {
45
+ id: "typography",
46
+ title: "Type specimens",
47
+ blurb:
48
+ "Measured from the same TTF the page is painted with, kerned from the font's own GPOS pairs, and encoded so ä, — and “ ” survive the trip to PDF.",
49
+ shows: ["real TTF metrics", "GPOS kerning", "align-items: baseline", "UTF-8 + WinAnsi"],
50
+ },
51
+ {
52
+ id: "units",
53
+ title: "Length units",
54
+ blurb:
55
+ "Five bars declared in five units, all the same length, because CSS pins them to the reference pixel. em follows the local font-size; rem stays with the root.",
56
+ shows: ["px % em rem", "pt pc in mm cm"],
57
+ },
58
+ {
59
+ id: "vector",
60
+ title: "Vector paths",
61
+ blurb:
62
+ "One path parser, one viewBox transform, three targets: real vector operators in the PDF, an inline <svg> in the HTML, and the same anti-aliased scanline fill that paints this page's glyphs in the PNG.",
63
+ shows: ["viewBox scaling", "fill-rule holes", "cubic + quadratic", "smooth S/T", "elliptical arcs"],
64
+ },
65
+ {
66
+ id: "svg",
67
+ title: "Imported SVG",
68
+ blurb:
69
+ "Two SVG files read into the same vector layer the rest of the gallery draws with: groups resolved, transforms baked into the geometry, <use> expanded where it is referenced. The browser is handed the resolved shapes rather than the original markup, so the preview cannot draw anything the printer will not.",
70
+ shows: ["<use> and <defs>", "baked transforms", "inherited paint", "themed from CSS"],
71
+ },
72
+ {
73
+ id: "charts",
74
+ title: "Charts",
75
+ blurb:
76
+ "Unmodified Vega specifications, compiled from Vega-Lite examples and run by the Vela runtime in this repository: every gridline, bar, wedge, key and label is geometry Ranger computed and the EVG vector layer drew. The page is generated, and its colours are not in it — the series are numbered and the stylesheet paints them, so the same charts come out in three palettes, legends included.",
77
+ shows: ["generated path data", "series colours from CSS", "one path per paint", "arcs, symbols and legends"],
78
+ themes: ["editorial", "studio", "autumn"],
79
+ },
80
+ {
81
+ id: "chart_api",
82
+ title: "Charts, called",
83
+ blurb:
84
+ "The only page here whose charts came from a program rather than from a specification: each one was built by calling Vela's chart API (gallery/vela/src/VlChart.rgr), and the lines printed above it are the calls that built it — read out of the generating tool's own source, so the code on the page and the code that drew it cannot drift apart. A view's encoding is inherited by its marks, and no channel says what it is: a column of ISO dates is an instant, a column of numbers is a quantity, read off the data.",
85
+ shows: ["marks and channels, called", "inherited view encoding", "types read off the data", "the calls that drew it"],
86
+ themes: ["editorial", "studio", "autumn"],
87
+ },
88
+ {
89
+ id: "plots",
90
+ title: "Chart types",
91
+ blurb:
92
+ "The rest of the runtime's chart types, and the features only some charts have: a size legend whose rows are all different heights, a stroke legend, a log axis that labels only some of the ticks it draws, two marks sharing one plot, and text as a mark. Generated from the same specifications the parity harness compares against official Vega.",
93
+ shows: ["size and colour legends", "log scale", "layered marks", "text marks", "grouped bands"],
94
+ themes: ["editorial", "studio", "autumn"],
95
+ },
96
+ {
97
+ id: "more",
98
+ title: "More chart types",
99
+ blurb:
100
+ "The chart types the runtime learned most recently, which are also the ones that exercise the most of it: a continuous colour ramp with the gradient key it earns, a series that is a faceted group of its own, a stack centred on a common line, a calendar on an axis, and a box plot whose quartiles are computed rather than approximated.",
101
+ shows: ["colour ramps", "gradient legends", "faceted series", "time scales", "computed quartiles"],
102
+ themes: ["editorial", "studio", "autumn"],
103
+ },
104
+ {
105
+ id: "variants",
106
+ title: "Variants",
107
+ blurb:
108
+ "The same marks a chart already on this showcase uses, drawn a different way — bars that go down as well as up, bars that lie on their side, a bar between two values rather than from a baseline, a line that steps instead of sloping, a line that shows its own vertices, a single row of ticks, a shape legend, a mean drawn across the plot, and a pie whose wedges differ in length as well as angle. It is the variants that break a runtime rather than the types.",
109
+ shows: ["negative bars", "horizontal bars", "step interpolation", "shape scales", "rules across a plot", "radial scales"],
110
+ themes: ["editorial", "studio", "autumn"],
111
+ },
112
+ {
113
+ id: "tables",
114
+ title: "Tables",
115
+ blurb:
116
+ "The plots where the cell is the datum: both axes are categories, or both are bins, and the value is carried by the cell's colour, its area, or the number printed in it. The two-dimensional histogram is the one that exercises the most — it bins on both axes, counts what lands in each cell, and ticks its axes on the bin edges rather than wherever a tick algorithm would have put them.",
117
+ shows: ["binning on both axes", "counts as colour", "point scales", "layered labels"],
118
+ themes: ["editorial", "studio", "autumn"],
119
+ },
120
+ {
121
+ id: "drawing",
122
+ title: "What a renderer has to get right",
123
+ blurb:
124
+ "Four specifications that exist to be drawn rather than to say anything: every symbol shape the grammar offers, every interpolation that joins a line's points, the paint channels that change the ink without moving it — a dash pattern, a rounded corner, a square cap, a fill more translucent than its own outline — and a label against each alignment, baseline, nudge and angle. A scenegraph says \"diamond\" and stops; these are the specs that make a renderer say what a diamond looks like, and each of them is compared against the picture official Vega's own renderer produces.",
125
+ shows: ["twelve symbol shapes", "nine interpolations", "dashes and corner radii", "separate fill and stroke opacity", "text anchoring"],
126
+ themes: ["editorial", "studio", "autumn"],
127
+ },
128
+ {
129
+ id: "views",
130
+ title: "More than one chart",
131
+ blurb:
132
+ "The four ways a chart can be more than one chart, all of them laid out by their own bounds rather than by a size anyone declared: a trellis by column, by row, and wrapped onto a grid whose shape is computed from the data, plus two plots concatenated with only the dimension across the join in common. The grid axes stay inside each panel; the labelled axes are drawn once, in the footer and the row header.",
133
+ shows: ["column and row facets", "wrapped grids", "concatenation", "bounds-based layout", "shared axes"],
134
+ themes: ["editorial", "studio", "autumn"],
135
+ },
136
+ {
137
+ id: "flex",
138
+ title: "Flex",
139
+ blurb:
140
+ "Grow, shrink and basis; wrapping; and what a row does with what is left over. Every arrangement is a class.",
141
+ shows: ["flex shorthand", "flex-shrink: 0", "justify-content", "flex-wrap"],
142
+ },
143
+ {
144
+ id: "emoji",
145
+ title: "Emoji",
146
+ blurb:
147
+ "Each specimen is several codepoints and one glyph, and only becomes that glyph if the whole cluster reaches the shaper together. The tinted rows use emoji-color, which exists because there are no inline spans to hang a second colour on.",
148
+ shows: ["grapheme clusters", "GSUB ligatures", "Type0 / Identity-H", "emoji-color"],
149
+ },
150
+ {
151
+ id: "boxmodel",
152
+ title: "Box model",
153
+ blurb:
154
+ "Padding, per-side padding, borders, nesting and margins — the same geometry the browser-parity gate checks against Chromium.",
155
+ shows: ["padding", "border", "margins", "nesting"],
156
+ },
157
+ ];
158
+
159
+ const THEMES = [
160
+ { id: "editorial", label: "Editorial", note: "warm paper, serif display" },
161
+ { id: "studio", label: "Studio", note: "dark, geometric sans" },
162
+ ];
163
+
164
+ /** Themes beyond the two every page gets. A page names them in `themes`. */
165
+ const EXTRA_THEMES = {
166
+ autumn: { id: "autumn", label: "Autumn", note: "muted ochre, rust and olive" },
167
+ };
168
+
169
+ /** The themes one page is rendered under. */
170
+ function themesFor(page) {
171
+ if (!page.themes) return THEMES;
172
+ return page.themes.map((id) => THEMES.find((t) => t.id === id) || EXTRA_THEMES[id]);
173
+ }
174
+
175
+ const CSS = path.join(HERE, "themes/showcase.css");
176
+ /** Stylesheets a page needs beyond the gallery's own, in cascade order. */
177
+ const PAGE_CSS = {
178
+ // The colours the chart specifications asked for. Unscoped, so a theme's
179
+ // scoped chart rules override them and a build with no chart theme still
180
+ // draws the chart the spec described.
181
+ charts: [path.join(HERE, "themes/charts-default.css")],
182
+ chart_api: [path.join(HERE, "themes/chart_api-default.css")],
183
+ plots: [path.join(HERE, "themes/plots-default.css")],
184
+ more: [path.join(HERE, "themes/more-default.css")],
185
+ views: [path.join(HERE, "themes/views-default.css")],
186
+ variants: [path.join(HERE, "themes/variants-default.css")],
187
+ tables: [path.join(HERE, "themes/tables-default.css")],
188
+ drawing: [path.join(HERE, "themes/drawing-default.css")],
189
+ };
190
+
191
+ function sh(cmd, args, opts = {}) {
192
+ return execFileSync(cmd, args, {
193
+ cwd: ROOT,
194
+ encoding: "utf8",
195
+ maxBuffer: 1 << 28,
196
+ ...opts,
197
+ });
198
+ }
199
+
200
+ /** Compile a Ranger tool. The compiler prints failures but still exits 0. */
201
+ function compile(src, outFile) {
202
+ const log = sh("node", [
203
+ "dist/rgrc.js",
204
+ "-es6",
205
+ src,
206
+ `-d=${path.relative(ROOT, TOOLS)}`,
207
+ `-o=${outFile}`,
208
+ "-nodecli",
209
+ ], { env: { ...process.env, RANGER_LIB: "./compiler/Lang.rgr" } });
210
+ if (log.includes("Compilation FAILED")) {
211
+ process.stderr.write(log.split("\n").slice(-30).join("\n") + "\n");
212
+ throw new Error(`compile failed: ${src}`);
213
+ }
214
+ }
215
+
216
+ function render(tool, page, theme, outFile, extra = []) {
217
+ const sheets = [...(PAGE_CSS[page] || []), CSS];
218
+ const args = [
219
+ path.join(TOOLS, tool),
220
+ path.join(HERE, "pages", `${page}.tsx`),
221
+ outFile,
222
+ ...sheets.flatMap((sheet) => ["-css", sheet]),
223
+ "-theme",
224
+ theme,
225
+ ...extra,
226
+ ];
227
+ const log = sh("node", args);
228
+ const warnings = log
229
+ .split("\n")
230
+ .filter((l) => /warning:/i.test(l))
231
+ .map((l) => l.trim());
232
+ return warnings;
233
+ }
234
+
235
+ /** The WebGL viewer page. One canvas, one display list, the real faces. */
236
+ function viewerHtml(faceCss) {
237
+ return `<!doctype html>
238
+ <html lang="en">
239
+ <head>
240
+ <meta charset="utf-8">
241
+ <meta name="viewport" content="width=device-width, initial-scale=1">
242
+ <title>EVG on WebGL</title>
243
+ <style>
244
+ ${faceCss}
245
+ :root { color-scheme: light dark; }
246
+ body { margin:0; background:#8b8f94; display:flex; flex-direction:column;
247
+ align-items:center; gap:14px; padding:24px;
248
+ font:14px/1.5 ui-sans-serif, system-ui, sans-serif; color:#fff }
249
+ canvas { background:#fff; box-shadow:0 3px 22px rgba(0,0,0,.4); max-width:100%; height:auto }
250
+ .bar { display:flex; gap:14px; align-items:baseline; flex-wrap:wrap; justify-content:center }
251
+ a { color:#cfe4ff }
252
+ code { background:rgba(0,0,0,.25); padding:1px 5px; border-radius:4px }
253
+ #err { color:#ffd0d0; font:12px/1.4 monospace; white-space:pre-wrap; max-width:70ch }
254
+ </style>
255
+ </head>
256
+ <body>
257
+ <div class="bar">
258
+ <strong>EVG on WebGL</strong>
259
+ <span id="stats" class="muted">loading…</span>
260
+ <a href="../index.html">back to the gallery</a>
261
+ </div>
262
+ <canvas id="c"></canvas>
263
+ <p class="bar">Same layout as the PDF — flex, grid, TTF metrics and kerning — drawn as GPU quads.
264
+ Rounded corners come from a distance field in the fragment shader.</p>
265
+ <p id="err"></p>
266
+ <script type="module">
267
+ import { renderDisplayList, loadImages } from "./evg-webgl.js";
268
+ const list = new URLSearchParams(location.search).get("list") || "album-editorial.json";
269
+ const err = document.getElementById("err");
270
+ try {
271
+ const doc = await (await fetch(list)).json();
272
+ const dpr = Math.min(2, window.devicePixelRatio || 1);
273
+ const c = document.getElementById("c");
274
+ c.style.width = doc.width + "px";
275
+ c.width = Math.round(doc.width * dpr);
276
+ c.height = Math.round(doc.height * dpr);
277
+ const gl = c.getContext("webgl2", { antialias: true, premultipliedAlpha: false, stencil: true });
278
+ if (!gl) throw new Error("This browser has no WebGL 2.");
279
+ await document.fonts.ready;
280
+ // A face nothing has used yet is not fetched until asked for by size.
281
+ await Promise.all(doc.list.cmds.filter(x => x.text)
282
+ .map(x => document.fonts.load(\`\${x.size}px "\${x.font}"\`).catch(() => {})));
283
+ // Photos have to be in the GPU before the first draw, or the page paints
284
+ // once with holes where they go.
285
+ const images = await loadImages(doc);
286
+ const s = renderDisplayList(gl, doc, { dpr, images });
287
+ document.getElementById("stats").textContent =
288
+ \`\${s.drawn} quads · \${s.paths || 0} paths · \${s.textRuns} text runs · \${s.images} images\`
289
+ + (s.missingImages ? \` · \${s.missingImages} could not be loaded\` : "")
290
+ + (s.skippedFills ? \` · \${s.skippedFills} fills need a stencil buffer\` : "");
291
+ window.__evgStats = s;
292
+ } catch (e) {
293
+ err.textContent = String((e && e.stack) || e);
294
+ window.__evgStats = { error: String(e) };
295
+ }
296
+ </script>
297
+ </body>
298
+ </html>
299
+ `;
300
+ }
301
+
302
+ const esc = (s) =>
303
+ String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
304
+
305
+ function indexHtml(entries, warnings) {
306
+ const cards = PAGES.map((p) => {
307
+ const shots = themesFor(p).map((t) => {
308
+ const e = entries.find((x) => x.page === p.id && x.theme === t.id);
309
+ return `
310
+ <figure class="shot">
311
+ <a href="${e.png}"><img src="${e.png}" alt="${esc(p.title)} — ${esc(t.label)}" loading="lazy"></a>
312
+ <figcaption>
313
+ <strong>${esc(t.label)}</strong> <span class="muted">${esc(t.note)}</span>
314
+ <a class="dl" href="${e.pdf}">PDF</a>
315
+ <a class="dl" href="${e.html}">HTML</a>
316
+ <a class="dl" href="${e.gl}">WebGL</a>
317
+ </figcaption>
318
+ </figure>`;
319
+ }).join("");
320
+ // The first theme's rendered page, which is what "open this page" means.
321
+ const lead = entries.find((x) => x.page === p.id && x.theme === themesFor(p)[0].id);
322
+ return `
323
+ <section class="page" id="${p.id}">
324
+ <header>
325
+ <h2><a href="${lead.html}">${esc(p.title)}</a> <a class="open" href="${lead.html}">open the page &rsaquo;</a></h2>
326
+ <p>${esc(p.blurb)}</p>
327
+ <ul class="tags">${p.shows.map((s) => `<li>${esc(s)}</li>`).join("")}</ul>
328
+ <p class="src"><a href="pages/${p.id}.tsx.txt">${p.id}.tsx</a> — no visual attributes; every rule is in <a href="showcase.css.txt">showcase.css</a></p>
329
+ </header>
330
+ <div class="shots">${shots}</div>
331
+ </section>`;
332
+ }).join("");
333
+
334
+ // A gallery of eleven pages needs a way in that is not scrolling. Each entry
335
+ // opens the page itself; the small link beside it jumps to that page's
336
+ // section, where its other themes and targets are.
337
+ const contents = PAGES.map((p) => {
338
+ const lead = entries.find((x) => x.page === p.id && x.theme === themesFor(p)[0].id);
339
+ return `<li><a class="go" href="${lead.html}">${esc(p.title)}</a> <a class="jump" href="#${p.id}" title="details, themes and targets">details</a></li>`;
340
+ }).join("");
341
+
342
+ const warnBlock = warnings.length
343
+ ? `<section class="warnings"><h2>Engine warnings from this build</h2><pre>${esc(warnings.join("\n"))}</pre></section>`
344
+ : `<section class="warnings"><h2>Engine warnings from this build</h2><p class="muted">None. Every declaration on every page was honoured.</p></section>`;
345
+
346
+ return `<!doctype html>
347
+ <html lang="en">
348
+ <head>
349
+ <meta charset="utf-8">
350
+ <meta name="viewport" content="width=device-width, initial-scale=1">
351
+ <title>EVG showcase</title>
352
+ <style>
353
+ :root {
354
+ --bg: #ffffff; --fg: #1a1d21; --muted: #5c6672; --line: #e3e7ec;
355
+ --accent: #2f6fd0; --card: #f7f9fb;
356
+ }
357
+ @media (prefers-color-scheme: dark) {
358
+ :root { --bg: #12141a; --fg: #e8ecf1; --muted: #939eab; --line: #262b33;
359
+ --accent: #6aa8ff; --card: #171b22; }
360
+ }
361
+ * { box-sizing: border-box; }
362
+ body { margin: 0; background: var(--bg); color: var(--fg);
363
+ font: 16px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
364
+ .wrap { max-width: 1100px; margin: 0 auto; padding: 48px 24px 96px; }
365
+ h1 { font-size: 2rem; margin: 0 0 .3em; letter-spacing: -.02em; }
366
+ .lede { color: var(--muted); max-width: 62ch; margin: 0 0 8px; }
367
+ .meta { color: var(--muted); font-size: .85rem; margin: 24px 0 0; }
368
+ .page { border-top: 1px solid var(--line); padding-top: 32px; margin-top: 48px; }
369
+ .page h2 { font-size: 1.25rem; margin: 0 0 .35em; }
370
+ .page header p { color: var(--muted); max-width: 68ch; margin: 0 0 10px; }
371
+ .tags { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; padding: 0; margin: 0 0 10px; }
372
+ .tags li { font-size: .72rem; letter-spacing: .02em; text-transform: uppercase;
373
+ color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px; }
374
+ .src { font-size: .85rem; }
375
+ a { color: var(--accent); }
376
+ .shots { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-top: 18px; }
377
+ .shot { margin: 0; background: var(--card); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
378
+ .shot img { display: block; width: 100%; height: auto; }
379
+ figcaption { padding: 10px 12px; font-size: .85rem; border-top: 1px solid var(--line);
380
+ display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
381
+ .muted { color: var(--muted); }
382
+ .dl { margin-left: auto; font-size: .78rem; border: 1px solid var(--line);
383
+ border-radius: 6px; padding: 2px 8px; text-decoration: none; }
384
+ .dl + .dl { margin-left: 0; }
385
+ .toc { border-top: 1px solid var(--line); margin-top: 32px; padding-top: 24px; }
386
+ .toc h2 { font-size: .8rem; letter-spacing: .08em; text-transform: uppercase;
387
+ color: var(--muted); margin: 0 0 12px; font-weight: 600; }
388
+ .toc ul { list-style: none; padding: 0; margin: 0;
389
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 8px 20px; }
390
+ .toc li { display: flex; align-items: baseline; gap: 8px; }
391
+ .toc .go { font-weight: 600; text-decoration: none; }
392
+ .toc .go:hover { text-decoration: underline; }
393
+ .toc .jump { font-size: .72rem; color: var(--muted); text-decoration: none;
394
+ border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px; }
395
+ .page h2 a { color: inherit; text-decoration: none; }
396
+ .page h2 a:hover { text-decoration: underline; }
397
+ .open { font-size: .72rem; font-weight: 400; letter-spacing: .02em; color: var(--accent);
398
+ border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px;
399
+ vertical-align: middle; margin-left: 6px; }
400
+ .warnings { border-top: 1px solid var(--line); padding-top: 32px; margin-top: 48px; }
401
+ .warnings pre { background: var(--card); border: 1px solid var(--line); border-radius: 8px;
402
+ padding: 12px; overflow-x: auto; font-size: .8rem; }
403
+ .note { background: var(--card); border: 1px solid var(--line); border-left: 3px solid var(--accent);
404
+ border-radius: 8px; padding: 12px 14px; margin: 24px 0 0; font-size: .9rem; color: var(--muted); }
405
+ </style>
406
+ </head>
407
+ <body>
408
+ <div class="wrap">
409
+ <h1>EVG showcase</h1>
410
+ <p class="lede">
411
+ EVG is the layout engine behind Ranger's PDF and image writers: a print-safe
412
+ CSS subset — flex, grid, a stylesheet layer and real TrueType metrics —
413
+ with no browser anywhere in the pipeline.
414
+ </p>
415
+ <p class="lede">
416
+ Every page below is the same tree rendered under two themes and three
417
+ targets. The <code>.tsx</code> files carry no colours, sizes or spacing:
418
+ they say what is on the page, and one stylesheet says how it looks.
419
+ </p>
420
+ <p class="meta">Built from <code>lib/evg/showcase</code>. PDF is the print target; PNG is the raster preview; HTML is the debug view.</p>
421
+
422
+ <div class="note">
423
+ <strong>The chart API, running.</strong> Every page below is a rendered
424
+ file. <a href="chart-api/">Charts, called &mdash; live</a> is not: it loads
425
+ the compiled <code>VlChart</code> API into the browser and dispatches the
426
+ lines you type to it, method by method, drawing the answer as you edit.
427
+ </div>
428
+ <div class="note">
429
+ <strong>Responsive layout, live.</strong>
430
+ <a href="responsive/">One page, laid out on every frame</a> at the width of
431
+ your window. The <code>@media</code> blocks, <code>vw</code> lengths and the
432
+ card grid are resolved by the compiled engine in the browser, and the
433
+ browser is handed finished pixels &mdash; drag the window edge and watch the
434
+ whole pipeline run again. <a href="responsive/?painter=dom">The same page as
435
+ retained DOM</a>: one node per element, kept across frames and patched from
436
+ the engine's own diff (<code>EVGHostTree</code>) &mdash; open the dev tools
437
+ and the picture is a tree you can read.
438
+ </div>
439
+ <div class="note">
440
+ <strong>Bitmap tracer, live.</strong>
441
+ <a href="tracer/">Upload a JPEG/PNG</a>, tweak Potrace-style parameters
442
+ (<code>turdsize</code>, <code>alphamax</code>, color posterize) and
443
+ vectorize in the browser with the compiled <code>EvgBitmapTracer</code>.
444
+ Nothing is uploaded — the image stays on the page.
445
+ </div>
446
+
447
+ <nav class="toc" aria-label="Pages">
448
+ <h2>Pages</h2>
449
+ <ul>${contents}</ul>
450
+ </nav>
451
+
452
+ ${cards}
453
+
454
+ ${warnBlock}
455
+
456
+ <div class="note">
457
+ <strong>Known limits, visible here.</strong> The raster target decodes JPEGs
458
+ with the repository's own decoder, which shows block artefacts — the PDF
459
+ path embeds the original file untouched, so print output is unaffected.
460
+ </div>
461
+ </div>
462
+ </body>
463
+ </html>
464
+ `;
465
+ }
466
+
467
+ // ---------------------------------------------------------------------------
468
+
469
+ fs.rmSync(OUT, { recursive: true, force: true });
470
+ fs.mkdirSync(path.join(OUT, "pages"), { recursive: true });
471
+ fs.mkdirSync(path.join(OUT, "gl", "fonts"), { recursive: true });
472
+ fs.mkdirSync(TOOLS, { recursive: true });
473
+
474
+ process.stdout.write("Compiling EVG tools...\n");
475
+ compile("./gallery/pdf_writer/src/tools/evg_pdf_tool.rgr", "evg_pdf_tool.js");
476
+ compile("./gallery/pdf_writer/src/tools/evg_png_tool.rgr", "evg_png_tool.js");
477
+ compile("./gallery/pdf_writer/src/tools/evg_html_tool.rgr", "evg_html_tool.js");
478
+ compile("./gallery/pdf_writer/src/tools/evg_displaylist_tool.rgr", "evg_displaylist_tool.js");
479
+
480
+
481
+ /**
482
+ * Copy the photos a display list refers to next to the viewer, and rewrite the
483
+ * list to point at the copies.
484
+ *
485
+ * The `src` in a display list is the path the AUTHOR wrote, relative to the
486
+ * page source — `../../../pdf_writer/assets/images/…`. That resolves in the
487
+ * repository and nowhere else, so the published viewer fetched four 404s and
488
+ * drew a page with holes in it. The PDF and PNG targets never hit this: they
489
+ * read the file at render time and embed the pixels.
490
+ */
491
+ function publishImages(jsonPath) {
492
+ const doc = JSON.parse(fs.readFileSync(jsonPath, "utf8"));
493
+ const dir = path.join(OUT, "gl", "images");
494
+ let changed = false;
495
+ for (const cmd of doc.list.cmds) {
496
+ if (cmd.k !== 2 || !cmd.src) continue;
497
+ const abs = path.resolve(HERE, "pages", cmd.src);
498
+ if (!fs.existsSync(abs)) {
499
+ // Left pointing at the original, so the viewer reports it as missing
500
+ // rather than silently drawing nothing.
501
+ continue;
502
+ }
503
+ fs.mkdirSync(dir, { recursive: true });
504
+ // Keyed by full path, so two different photos with the same basename do
505
+ // not overwrite each other.
506
+ let name = imageNames.get(abs);
507
+ if (!name) {
508
+ const base = path.basename(abs);
509
+ name = imageNames.has(base) ? `${imageNames.size}-${base}` : base;
510
+ imageNames.set(abs, name);
511
+ imageNames.set(base, true);
512
+ fs.copyFileSync(abs, path.join(dir, name));
513
+ }
514
+ cmd.src = `images/${name}`;
515
+ changed = true;
516
+ }
517
+ if (changed) fs.writeFileSync(jsonPath, JSON.stringify(doc), "utf8");
518
+ }
519
+ const imageNames = new Map();
520
+
521
+ const entries = [];
522
+ const allWarnings = [];
523
+
524
+ for (const page of PAGES) {
525
+ for (const theme of themesFor(page)) {
526
+ const stem = `${page.id}-${theme.id}`;
527
+ process.stdout.write(` ${stem}\n`);
528
+ const png = `${stem}.png`;
529
+ const json = `${stem}.json`;
530
+ const pdf = `${stem}.pdf`;
531
+ const html = `${stem}.html`;
532
+ allWarnings.push(...render("evg_png_tool.js", page.id, theme.id, path.join(OUT, png)));
533
+ allWarnings.push(...render("evg_pdf_tool.js", page.id, theme.id, path.join(OUT, pdf)));
534
+ // The same page as draw commands, for the WebGL viewer.
535
+ allWarnings.push(...render("evg_displaylist_tool.js", page.id, theme.id, path.join(OUT, "gl", json)));
536
+ publishImages(path.join(OUT, "gl", json));
537
+ // -embed inlines the TTFs as data URIs. Without it the page references
538
+ // font files by path, which resolves on the build machine and nowhere
539
+ // else — the browser then falls back to a system face, wraps text where
540
+ // EVG did not, and the absolutely-positioned boxes land on the headings.
541
+ allWarnings.push(...render("evg_html_tool.js", page.id, theme.id, path.join(OUT, html), ["-embed"]));
542
+ entries.push({ page: page.id, theme: theme.id, png, pdf, html, gl: `gl/view.html?list=${json}` });
543
+ }
544
+ // Source, served as text so the gallery can link to what produced the page.
545
+ fs.copyFileSync(
546
+ path.join(HERE, "pages", `${page.id}.tsx`),
547
+ path.join(OUT, "pages", `${page.id}.tsx.txt`)
548
+ );
549
+ }
550
+ fs.copyFileSync(CSS, path.join(OUT, "showcase.css.txt"));
551
+
552
+ // ---- WebGL viewer ---------------------------------------------------------
553
+ // The renderer itself, the faces it needs, and a page to put them together.
554
+ // Fonts are copied rather than linked: the published site has no access to the
555
+ // repository tree, and the whole point is that these are the same TTFs the
556
+ // layout was measured with.
557
+ fs.copyFileSync(path.join(ROOT, "lib/evg/gl/evg-webgl.js"), path.join(OUT, "gl", "evg-webgl.js"));
558
+ const FONTS = path.join(ROOT, "gallery/pdf_writer/assets/fonts");
559
+ const FACES = [
560
+ ["Cinzel", "Cinzel/Cinzel-Regular.ttf"],
561
+ ["Cinzel-Bold", "Cinzel/Cinzel-Bold.ttf"],
562
+ ["Noto Sans", "Noto_Sans/NotoSans-Regular.ttf"],
563
+ ["Noto Sans-Bold", "Noto_Sans/NotoSans-Bold.ttf"],
564
+ ["Josefin Sans", "Josefin_Sans/JosefinSans-Regular.ttf"],
565
+ ["Josefin Sans-Bold", "Josefin_Sans/JosefinSans-Bold.ttf"],
566
+ ];
567
+ const faceCss = [];
568
+ for (const [family, rel] of FACES) {
569
+ const src = path.join(FONTS, rel);
570
+ if (!fs.existsSync(src)) continue;
571
+ const base = "fonts/" + path.basename(rel);
572
+ fs.copyFileSync(src, path.join(OUT, "gl", base));
573
+ faceCss.push(`@font-face{font-family:"${family}";src:url("${base}")}`);
574
+ }
575
+ fs.writeFileSync(path.join(OUT, "gl", "view.html"), viewerHtml(faceCss.join("\n ")), "utf8");
576
+
577
+ // ---- the live page --------------------------------------------------------
578
+ // The rest of this gallery is rendered ahead of time; this one runs. The chart
579
+ // API is compiled to a browser bundle and the page dispatches what a reader
580
+ // types to the real methods — which is the one thing a picture of a chart
581
+ // cannot demonstrate.
582
+ {
583
+ const LIVE = path.join(OUT, "chart-api");
584
+ fs.mkdirSync(LIVE, { recursive: true });
585
+ compile(path.join(ROOT, "gallery/vela/tools/vela_chart_web.rgr"), "vela_chart_api.js");
586
+ const bundlePath = path.join(TOOLS, "vela_chart_api.js");
587
+ // The shebang is for a shell, and a `<script>` is not one: left in, the
588
+ // browser stops at the `#` and the page loads nothing.
589
+ const bundle = fs.readFileSync(bundlePath, "utf8").replace(/^#![^\n]*\n/, "");
590
+ // A browser has no `require`, and a file-system call compiles fine and fails
591
+ // only when somebody opens the page. Loading it here with `require`
592
+ // undefined is a stricter check than grepping for one.
593
+ {
594
+ const previous = globalThis.require;
595
+ globalThis.require = undefined;
596
+ const found = (0, eval)(bundle + "; typeof VelaChartApi");
597
+ globalThis.require = previous;
598
+ if (found !== "function") {
599
+ throw new Error("vela_chart_api.js does not define VelaChartApi without require()");
600
+ }
601
+ }
602
+ // The API classes go out with it, not just the entry point: the page's
603
+ // JavaScript mode calls `chart.bar().x("region")` on the compiled
604
+ // `VlChart` itself, so what a reader types reaches the same methods the
605
+ // Ranger interpreter beside it dispatches to.
606
+ fs.writeFileSync(path.join(LIVE, "vela_chart_api.js"),
607
+ "// GENERATED from gallery/vela/tools/vela_chart_web.rgr — do not edit.\n"
608
+ + "(function () {\n" + bundle + "\n;globalThis.VelaChartApi = VelaChartApi;"
609
+ + "\n;globalThis.Vela = { VelaChartApi: VelaChartApi, VlChart: VlChart,"
610
+ + " VlChartMark: VlChartMark, VlDataset: VlDataset, VlDataRow: VlDataRow, VlJson: VlJson };\n})();\n");
611
+ fs.copyFileSync(path.join(ROOT, "gallery/vela/web/chart_api.html"), path.join(LIVE, "index.html"));
612
+ process.stdout.write(" chart-api/ (live)\n");
613
+ }
614
+
615
+ // ---- live bitmap tracer ---------------------------------------------------
616
+ // Same idea as chart-api/: a page that runs the compiled library in the
617
+ // browser. Build logic lives in lib/evg/web/tracer/build.mjs so it can
618
+ // also be invoked alone during development.
619
+ {
620
+ execFileSync(process.execPath, [
621
+ path.join(ROOT, "lib/evg/web/tracer/build.mjs"),
622
+ "--out",
623
+ path.join(OUT, "tracer"),
624
+ ], { cwd: ROOT, stdio: "inherit" });
625
+ process.stdout.write(" tracer/ (live)\n");
626
+ }
627
+
628
+ // ---- live responsive page --------------------------------------------------
629
+ // The third page that runs rather than being rendered, and the only one whose
630
+ // LAYOUT is the thing running: the compiled engine lays the page out again at
631
+ // whatever width the reader's window is.
632
+ {
633
+ execFileSync(process.execPath, [
634
+ path.join(ROOT, "lib/evg/web/responsive/build.mjs"),
635
+ "--out",
636
+ path.join(OUT, "responsive"),
637
+ ], { cwd: ROOT, stdio: "inherit" });
638
+ process.stdout.write(" responsive/ (live)\n");
639
+ }
640
+
641
+ const unique = [...new Set(allWarnings)];
642
+ fs.writeFileSync(path.join(OUT, "index.html"), indexHtml(entries, unique), "utf8");
643
+ fs.rmSync(TOOLS, { recursive: true, force: true });
644
+
645
+ process.stdout.write(
646
+ `\nWrote ${entries.length * 3} files + index to ${path.relative(ROOT, OUT) || OUT}\n`
647
+ );
648
+ if (unique.length) {
649
+ process.stdout.write(`Engine warnings (${unique.length}):\n`);
650
+ for (const w of unique) process.stdout.write(` ${w}\n`);
651
+ }