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,899 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; ==============================================================================
4
+ ; evg_agent — the tool surface, before there is a protocol around it
5
+ ; ==============================================================================
6
+ ;
7
+ ; Four verbs over an EVG document. An agent with a shell and a way to read a
8
+ ; file already has everything it needs to edit one; MCP, a widget and an editor
9
+ ; panel are transports over exactly these, added when something needs them.
10
+ ;
11
+ ; npm run agent -- outline <doc.evg.json> [--depth=N] [--at=PATH]
12
+ ; npm run agent -- query <doc.evg.json> <.class | #id | tag | path>
13
+ ; npm run agent -- patch <doc.evg.json> <ops.json> [--out=FILE]
14
+ ; npm run agent -- measure <doc.evg.json> [--width=N] [--height=N] [--boxes]
15
+ ;
16
+ ; --- WHY OUTLINE IS NOT JSON --------------------------------------------------
17
+ ;
18
+ ; `outline` prints an indented text tree and everything else prints JSON, which
19
+ ; looks inconsistent until you count tokens. An outline exists to be READ by a
20
+ ; model deciding what to change, and one line per node — path, tag, class, the
21
+ ; few properties it actually sets — costs a fraction of the same tree as JSON
22
+ ; while carrying the addresses the other verbs take. The others are parsed, not
23
+ ; read, so they are JSON.
24
+ ;
25
+ ; --- WHY MEASURE IS THE INTERESTING ONE ---------------------------------------
26
+ ;
27
+ ; The rest of this is bookkeeping. `measure` is the verb that does something a
28
+ ; model cannot do for itself: it lays the document out and answers, in numbers,
29
+ ; whether the result is wrong — text past its box, siblings on top of each
30
+ ; other, a node off the page. A screenshot asks the model to SEE that, badly
31
+ ; and expensively. This reads it off the boxes layout already computed.
32
+ ; ==============================================================================
33
+
34
+ Import "../EVGElement.rgr"
35
+ Import "../EVGInspect.rgr"
36
+ Import "../EVGLayout.rgr"
37
+ Import "../EVGMeasure.rgr"
38
+ Import "../EVGHoles.rgr"
39
+ Import "../EVGHitTest.rgr"
40
+ Import "../EVGPatch.rgr"
41
+ Import "../EVGUnit.rgr"
42
+ Import "../EVGTreeJson.rgr"
43
+
44
+ class EvgAgent {
45
+ def insp:EVGInspect
46
+
47
+ Constructor () {
48
+ insp = (new EVGInspect())
49
+ }
50
+
51
+ sfn main:void () {
52
+ def a (new EvgAgent())
53
+ a.run()
54
+ }
55
+
56
+ fn run:void () {
57
+ def argc:int (shell_arg_cnt)
58
+ if (argc < 2) {
59
+ this.usage()
60
+ return
61
+ }
62
+ def verb:string (shell_arg 0)
63
+ def docPath:string (shell_arg 1)
64
+ def text@(optional):string (EvgAgent.slurp(docPath))
65
+ if (null? text) {
66
+ print (("{\"error\":\"cannot read \\\"" + docPath) + "\\\"\"}")
67
+ return
68
+ }
69
+ def root@(optional):EVGElement (EVGTreeJson.fromText((unwrap text)))
70
+ if (null? root) {
71
+ print (("{\"error\":\"not an EVG document: \\\"" + docPath) + "\\\"\"}")
72
+ return
73
+ }
74
+ def tree:EVGElement (unwrap root)
75
+
76
+ if (verb == "outline") {
77
+ this.outline(tree)
78
+ return
79
+ }
80
+ if (verb == "query") {
81
+ this.query(tree)
82
+ return
83
+ }
84
+ if (verb == "patch") {
85
+ this.patch(tree docPath)
86
+ return
87
+ }
88
+ if (verb == "measure") {
89
+ this.measure(tree)
90
+ return
91
+ }
92
+ if (verb == "pick") {
93
+ this.pick(tree)
94
+ return
95
+ }
96
+ this.usage()
97
+ }
98
+
99
+ fn usage:void () {
100
+ print "evg_agent — read, edit and check an EVG document"
101
+ print ""
102
+ print " outline <doc> [--depth=N] [--at=PATH] the tree, one line per node"
103
+ print " query <doc> <.class|#id|tag|path> the nodes that match"
104
+ print " patch <doc> <ops.json> [--out=FILE] apply ops, print the inverse"
105
+ print " measure <doc> [--width=N] [--height=N] [--boxes] [--at=PATH]"
106
+ print " lay it out and say what is wrong"
107
+ print " pick <doc> X Y [--width=N] [--height=N]"
108
+ print " what is under that point, root first"
109
+ }
110
+
111
+ ; --- pick -----------------------------------------------------------------
112
+ ; The editor's question, which `query` cannot answer: somebody pointed at
113
+ ; the picture — what is that?
114
+ ;
115
+ ; It answers with the whole CHAIN from the root down to the deepest thing
116
+ ; under the point, not with one node, because the node under a pointer is
117
+ ; almost never the one somebody means. A click on a switch lands on its
118
+ ; thumb, and the thing to select is two levels up. Handing back the chain
119
+ ; lets the caller show the levels and let a person choose, and it costs one
120
+ ; walk rather than a round trip per level.
121
+ ;
122
+ ; The tree is dressed first, for the same reason `measure` dresses it: the
123
+ ; point was aimed at the picture on the screen, so the boxes have to be the
124
+ ; boxes that were drawn.
125
+ fn pick:void (root:EVGElement) {
126
+ def argc:int (shell_arg_cnt)
127
+ if (argc < 4) {
128
+ print "{\"error\":\"pick needs X and Y\"}"
129
+ return
130
+ }
131
+ EVGDress.dress(root)
132
+ def w:int (EvgAgent.intFlag("--width" (EVGMeasure.sizeOf(root true 1200))))
133
+ def h:int (EvgAgent.intFlag("--height" (EVGMeasure.sizeOf(root false 900))))
134
+ def ax:int (EvgAgent.numAt(2))
135
+ def ay:int (EvgAgent.numAt(3))
136
+ def px:double (to_double ax)
137
+ def py:double (to_double ay)
138
+ def l (new EVGLayout())
139
+ l.setPageSize((to_double w) (to_double h))
140
+ l.layout(root)
141
+ ; Every box where it was drawn. A `transform` is carried as a shift
142
+ ; that a reader settles before looking, and a picker is a reader.
143
+ ; Note this settles a subtree, not the shifted element's own box —
144
+ ; the same thing `EVGHitTest` does, so a thumb moved by a transform
145
+ ; answers as its track here exactly as it does under a press.
146
+ root.settleAll()
147
+ insp.build(root)
148
+
149
+ ; The deepest element holding the point. Depth decides, and among
150
+ ; equals the later one wins, because that is the one painted on top.
151
+ def best:string ""
152
+ def bestDepth:int (0 - 1)
153
+ def i:int 0
154
+ while (i < (insp.count())) {
155
+ def path:string (insp.pathAt(i))
156
+ def hit@(optional):EVGElement (insp.elementAt(path))
157
+ if ((null? hit) == false) {
158
+ def el:EVGElement (unwrap hit)
159
+ if ((el.isHidden()) == false) {
160
+ if (EVGHitTest.containsPoint(el px py)) {
161
+ def d:int (EvgAgent.depthOf(path))
162
+ if (d >= bestDepth) {
163
+ bestDepth = d
164
+ best = path
165
+ }
166
+ }
167
+ }
168
+ }
169
+ i = (i + 1)
170
+ }
171
+ if ((strlen best) == 0) {
172
+ print "{\"chain\":[],\"count\":0}"
173
+ return
174
+ }
175
+ ; Up to the root, then printed the other way round, so the first entry
176
+ ; is the page and the last is what the pointer was actually over.
177
+ def up:[string]
178
+ def at:string best
179
+ def guard:int 0
180
+ while ((guard < 64) && ((strlen at) > 0)) {
181
+ push up at
182
+ at = (insp.parentOf(at))
183
+ guard = (guard + 1)
184
+ }
185
+ def out:string "{\"chain\":["
186
+ def n:int 0
187
+ def k:int ((array_length up) - 1)
188
+ while (k >= 0) {
189
+ def p:string (itemAt up k)
190
+ def got@(optional):EVGElement (insp.elementAt(p))
191
+ if ((null? got) == false) {
192
+ def el:EVGElement (unwrap got)
193
+ if (n > 0) {
194
+ out = (out + ",")
195
+ }
196
+ out = (out + "\n ")
197
+ out = (out + (EvgAgent.pickOne(p el)))
198
+ n = (n + 1)
199
+ }
200
+ k = (k - 1)
201
+ }
202
+ if (n > 0) {
203
+ out = (out + "\n")
204
+ }
205
+ out = ((out + "],\"count\":") + (to_string n))
206
+ out = ((out + ",\"at\":") + (EVGTreeJson.str(best)))
207
+ print (out + "}")
208
+ }
209
+
210
+ ; One link of the chain: enough to name it in a sidebar and draw a box
211
+ ; round it, and not the whole node.
212
+ sfn pickOne:string (path:string el:EVGElement) {
213
+ def out:string "{\"at\":"
214
+ out = (out + (EVGTreeJson.str(path)))
215
+ out = ((out + ",\"tag\":") + (EVGTreeJson.str(el.tagName)))
216
+ if ((strlen el.id) > 0) {
217
+ out = ((out + ",\"id\":") + (EVGTreeJson.str(el.id)))
218
+ }
219
+ if ((strlen el.className) > 0) {
220
+ out = ((out + ",\"class\":") + (EVGTreeJson.str(el.className)))
221
+ }
222
+ if ((strlen el.role) > 0) {
223
+ out = ((out + ",\"role\":") + (EVGTreeJson.str(el.role)))
224
+ }
225
+ def t:string (trim el.textContent)
226
+ if ((strlen t) > 0) {
227
+ out = ((out + ",\"text\":") + (EVGTreeJson.str(t)))
228
+ }
229
+ out = ((out + ",\"x\":") + (EVGMeasure.num(el.calculatedX)))
230
+ out = ((out + ",\"y\":") + (EVGMeasure.num(el.calculatedY)))
231
+ out = ((out + ",\"w\":") + (EVGMeasure.num(el.calculatedWidth)))
232
+ out = ((out + ",\"h\":") + (EVGMeasure.num(el.calculatedHeight)))
233
+ out = ((out + ",\"children\":") + (to_string (array_length el.children)))
234
+ return (out + "}")
235
+ }
236
+
237
+ ; A positional number argument. `to_int` on a string is optional-typed and
238
+ ; answers differently per target, so a digit loop is the same everywhere —
239
+ ; the same reason `intFlag` has one.
240
+ sfn numAt:int (slot:int) {
241
+ def v:string (shell_arg slot)
242
+ def out@(mutable):int 0
243
+ def k@(mutable):int 0
244
+ def neg:boolean ((indexOf v "-") == 0)
245
+ if neg {
246
+ k = 1
247
+ }
248
+ while (k < (strlen v)) {
249
+ def d:int (indexOf "0123456789" (substring v k (k + 1)))
250
+ if (d < 0) {
251
+ k = (strlen v)
252
+ } {
253
+ out = ((out * 10) + d)
254
+ k = (k + 1)
255
+ }
256
+ }
257
+ if neg {
258
+ return (0 - out)
259
+ }
260
+ return out
261
+ }
262
+
263
+ ; --- outline --------------------------------------------------------------
264
+
265
+ fn outline:void (root:EVGElement) {
266
+ def depth:int (EvgAgent.intFlag("--depth" 99))
267
+ def from:string (EvgAgent.textFlag("--at" "0"))
268
+ insp.build(root)
269
+ def shown:int 0
270
+ def hidden:int 0
271
+ def i:int 0
272
+ while (i < (insp.count())) {
273
+ def path:string (insp.pathAt(i))
274
+ if (EvgAgent.under(path from)) {
275
+ def d:int (EvgAgent.depthOf(path))
276
+ def base:int (EvgAgent.depthOf(from))
277
+ if ((d - base) <= depth) {
278
+ def hit@(optional):EVGElement (insp.elementAt(path))
279
+ if ((null? hit) == false) {
280
+ print (EvgAgent.line(path (unwrap hit) (d - base)))
281
+ shown = (shown + 1)
282
+ }
283
+ } {
284
+ hidden = (hidden + 1)
285
+ }
286
+ }
287
+ i = (i + 1)
288
+ }
289
+ ; Never truncate in silence — a model that cannot see there is more
290
+ ; will confidently describe a document it has only the top of.
291
+ if (hidden > 0) {
292
+ print ""
293
+ print ((("… " + (to_string hidden)) + " deeper nodes not shown — raise --depth or pass --at to go into one"))
294
+ }
295
+ }
296
+
297
+ ; One node: its address, what it is, and only what it actually says.
298
+ sfn line:string (path:string el:EVGElement indent:int) {
299
+ def out:string (EvgAgent.pad(path 22))
300
+ def i:int 0
301
+ while (i < indent) {
302
+ out = (out + " ")
303
+ i = (i + 1)
304
+ }
305
+ out = (out + el.tagName)
306
+ if ((strlen el.id) > 0) {
307
+ out = ((out + " #") + el.id)
308
+ }
309
+ if ((strlen el.className) > 0) {
310
+ out = ((out + " .") + el.className)
311
+ }
312
+ if ((strlen el.textContent) > 0) {
313
+ out = ((out + " \"") + (EvgAgent.clip(el.textContent 40)))
314
+ out = (out + "\"")
315
+ }
316
+ def names:[string] (EVGPatch.patchableNames())
317
+ def fresh@(optional):EVGElement (EVGPatch.create(el.tagName))
318
+ if (null? fresh) {
319
+ return out
320
+ }
321
+ def blank:EVGElement (unwrap fresh)
322
+ def k:int 0
323
+ while (k < (array_length names)) {
324
+ def name:string (itemAt names k)
325
+ if (name != "class-name") {
326
+ def mine:string (EVGPatch.readProp(el name))
327
+ def theirs:string (EVGPatch.readProp(blank name))
328
+ if (mine != theirs) {
329
+ out = ((((out + " ") + name) + "=") + mine)
330
+ }
331
+ }
332
+ k = (k + 1)
333
+ }
334
+ ; `evg-fx-*` is per element rather than per format, so it is not in the
335
+ ; static list above. Without this an outline shows that a node has a
336
+ ; starfield and not how it is tuned.
337
+ def fx:[string] (EVGPatch.fxNamesOf(el))
338
+ def f:int 0
339
+ while (f < (array_length fx)) {
340
+ def fxName:string (itemAt fx f)
341
+ out = ((((out + " ") + fxName) + "=") + (EVGPatch.readProp(el fxName)))
342
+ f = (f + 1)
343
+ }
344
+ return out
345
+ }
346
+
347
+ ; --- query ----------------------------------------------------------------
348
+
349
+ fn query:void (root:EVGElement) {
350
+ def argc:int (shell_arg_cnt)
351
+ if (argc < 3) {
352
+ print "{\"error\":\"query needs something to match\"}"
353
+ return
354
+ }
355
+ def want:string (shell_arg 2)
356
+ insp.build(root)
357
+ def out:string "{\"matches\":["
358
+ def n:int 0
359
+ def i:int 0
360
+ while (i < (insp.count())) {
361
+ def path:string (insp.pathAt(i))
362
+ def hit@(optional):EVGElement (insp.elementAt(path))
363
+ if ((null? hit) == false) {
364
+ def el:EVGElement (unwrap hit)
365
+ if (EvgAgent.matches(el path want)) {
366
+ if (n > 0) {
367
+ out = (out + ",")
368
+ }
369
+ out = (out + "\n ")
370
+ out = (out + (EVGTreeJson.brief(path el)))
371
+ n = (n + 1)
372
+ }
373
+ }
374
+ i = (i + 1)
375
+ }
376
+ if (n > 0) {
377
+ out = (out + "\n")
378
+ }
379
+ out = ((out + "],\"count\":") + (to_string n))
380
+ out = (out + "}")
381
+ print out
382
+ }
383
+
384
+ ; `.class`, `#id`, a bare tag, or a path — which is every way an address is
385
+ ; written in the rest of this toolchain. Not a selector engine: a compound
386
+ ; selector belongs to `EVGStyleSheet`, and pretending otherwise here would
387
+ ; be a second, weaker implementation of one.
388
+ sfn matches:boolean (el:EVGElement path:string want:string) {
389
+ if ((strlen want) == 0) {
390
+ return false
391
+ }
392
+ def c:int (charAt want 0)
393
+ if (c == 46) {
394
+ return (el.className == (substring want 1 (strlen want)))
395
+ }
396
+ if (c == 35) {
397
+ return (el.id == (substring want 1 (strlen want)))
398
+ }
399
+ if ((c >= 48) && (c <= 57)) {
400
+ return (path == want)
401
+ }
402
+ return (el.tagName == want)
403
+ }
404
+
405
+ ; --- patch ----------------------------------------------------------------
406
+
407
+ fn patch:void (root:EVGElement docPath:string) {
408
+ def argc:int (shell_arg_cnt)
409
+ if (argc < 3) {
410
+ print "{\"error\":\"patch needs an ops file\"}"
411
+ return
412
+ }
413
+ def opsPath:string (shell_arg 2)
414
+ def opsText@(optional):string (EvgAgent.slurp(opsPath))
415
+ if (null? opsText) {
416
+ print (("{\"error\":\"cannot read \\\"" + opsPath) + "\\\"\"}")
417
+ return
418
+ }
419
+ def ops:[EVGPatchOp] (EvgAgent.readOps((unwrap opsText)))
420
+ if ((array_length ops) == 0) {
421
+ print "{\"error\":\"no ops in that file\"}"
422
+ return
423
+ }
424
+ def p (new EVGPatch())
425
+ def r:EVGPatchResult (p.apply(root ops))
426
+ if (r.ok == false) {
427
+ print (("{\"ok\":false,\"applied\":0,\"rejected\":" + (EvgAgent.strArray(r.rejected))) + "}")
428
+ return
429
+ }
430
+ def outPath:string (EvgAgent.textFlag("--out" docPath))
431
+ def text:string (EVGTreeJson.toText(root))
432
+ EvgAgent.spit(outPath text)
433
+ def out:string "{\"ok\":true,\"applied\":"
434
+ out = (out + (to_string r.applied))
435
+ out = ((out + ",\"wrote\":\"") + outPath)
436
+ out = (out + "\"")
437
+ ; Read the properties back BEFORE laying the document out: layout
438
+ ; resolves units into the elements it measures, so a height asked for
439
+ ; in the ops file no longer reads the way it was written afterwards.
440
+ def dflt:string (this.atDefaults(root ops))
441
+ out = (out + ",\"layout\":")
442
+ out = (out + (this.layoutOf(root)))
443
+ if ((strlen dflt) > 0) {
444
+ out = ((out + ",\"atDefault\":[") + dflt)
445
+ out = (out + "],\"note\":\"applied — but those values are the tag default, and the format writes only what differs from a fresh element, so they will not appear in the file or in outline\"")
446
+ }
447
+ out = (out + ",\"inverse\":[")
448
+ def i:int 0
449
+ while (i < (array_length r.inverse)) {
450
+ if (i > 0) {
451
+ out = (out + ",")
452
+ }
453
+ out = (out + "\n ")
454
+ out = (out + (EvgAgent.opJson((itemAt r.inverse i))))
455
+ i = (i + 1)
456
+ }
457
+ if ((array_length r.inverse) > 0) {
458
+ out = (out + "\n")
459
+ }
460
+ out = (out + "]}")
461
+ print out
462
+ }
463
+
464
+ ; What the edit did to the layout, without being asked for it.
465
+ ;
466
+ ; An agent that patches and then stops has changed a picture it cannot see.
467
+ ; The numbers that decide whether the change was good — what overlaps, what
468
+ ; left the page, how much room is left — cost one layout pass here and a
469
+ ; round trip it would usually skip if it had to ask.
470
+ fn layoutOf:string (root:EVGElement) {
471
+ def m (new EVGMeasure())
472
+ def w:int (EVGMeasure.sizeOf(root true 1200))
473
+ def h:int (EVGMeasure.sizeOf(root false 900))
474
+ m.look(root w h "")
475
+ return (m.summary(5))
476
+ }
477
+
478
+ ; --- WHY AN APPLIED OP CAN LEAVE NO TRACE ---------------------------------
479
+ ;
480
+ ; The format writes a property only where it differs from a fresh element of
481
+ ; the same tag, and EVG's defaults are not CSS's: a div is `column`, not
482
+ ; `row`. So `set-prop flex-direction=column` on a row really does turn the
483
+ ; node, and then the file, the outline and a query all show nothing, because
484
+ ; there is nothing left to say. An agent that re-reads its own edit concludes
485
+ ; the tool dropped it and writes a worse document around the phantom bug.
486
+ ; Naming those ops in the result is the whole fix: the answer arrives with
487
+ ; the edit instead of after a wrong theory about it.
488
+ ;
489
+ ; A batch that inserts, removes or moves is skipped — those shift the paths
490
+ ; the ops were written against, so a lookup afterwards could name the wrong
491
+ ; node, and a confident wrong note is worse than none.
492
+ fn atDefaults:string (root:EVGElement ops:[EVGPatchOp]) {
493
+ def n:int (array_length ops)
494
+ def i:int 0
495
+ while (i < n) {
496
+ def op:EVGPatchOp (itemAt ops i)
497
+ if ((((op.op != "set-prop") && (op.op != "set-text")) && (op.op != "set-id")) && (op.op != "set-css")) {
498
+ return ""
499
+ }
500
+ i = (i + 1)
501
+ }
502
+ insp.build(root)
503
+ def out:string ""
504
+ def wrote:int 0
505
+ def k:int 0
506
+ while (k < n) {
507
+ def op:EVGPatchOp (itemAt ops k)
508
+ if (op.op == "set-prop") {
509
+ def one:string (this.defaultNote(op))
510
+ if ((strlen one) > 0) {
511
+ if (wrote > 0) {
512
+ out = (out + ",")
513
+ }
514
+ out = (out + "\n ")
515
+ out = (out + one)
516
+ wrote = (wrote + 1)
517
+ }
518
+ }
519
+ k = (k + 1)
520
+ }
521
+ if (wrote > 0) {
522
+ out = (out + "\n")
523
+ }
524
+ return out
525
+ }
526
+
527
+ ; One op, if what it wrote now reads the same as a fresh element of that tag.
528
+ fn defaultNote:string (op:EVGPatchOp) {
529
+ ; An empty value IS the request to go back to the default — the inverse
530
+ ; of a property that had never been set writes one. Saying it landed on
531
+ ; the default would be reporting the caller's own instruction back.
532
+ if ((strlen op.value) == 0) {
533
+ return ""
534
+ }
535
+ def hit@(optional):EVGElement (insp.elementAt(op.at))
536
+ if (null? hit) {
537
+ return ""
538
+ }
539
+ def el:EVGElement (unwrap hit)
540
+ def fresh@(optional):EVGElement (EVGPatch.create(el.tagName))
541
+ if (null? fresh) {
542
+ return ""
543
+ }
544
+ def blank:EVGElement (unwrap fresh)
545
+ def mine:string (EVGPatch.readProp(el op.prop))
546
+ def theirs:string (EVGPatch.readProp(blank op.prop))
547
+ if (mine != theirs) {
548
+ return ""
549
+ }
550
+ def out:string "{\"at\":"
551
+ out = (out + (EVGTreeJson.str(op.at)))
552
+ out = ((out + ",\"prop\":") + (EVGTreeJson.str(op.prop)))
553
+ out = ((out + ",\"value\":") + (EVGTreeJson.str(mine)))
554
+ out = ((out + ",\"tag\":") + (EVGTreeJson.str(el.tagName)))
555
+ return (out + "}")
556
+ }
557
+
558
+ ; An inverse op, in the shape `patch` takes back. Writing the file the
559
+ ; caller would have to write is the difference between "here is how to undo
560
+ ; it" and "here is a description of the undo".
561
+ sfn opJson:string (op:EVGPatchOp) {
562
+ def out:string "{\"op\":"
563
+ out = (out + (EVGTreeJson.str(op.op)))
564
+ out = ((out + ",\"at\":") + (EVGTreeJson.str(op.at)))
565
+ if ((strlen op.prop) > 0) {
566
+ out = ((out + ",\"prop\":") + (EVGTreeJson.str(op.prop)))
567
+ }
568
+ if (op.op == "set-prop") {
569
+ out = ((out + ",\"value\":") + (EVGTreeJson.str(op.value)))
570
+ }
571
+ if (op.op == "set-text") {
572
+ out = ((out + ",\"value\":") + (EVGTreeJson.str(op.value)))
573
+ }
574
+ if (op.op == "set-id") {
575
+ out = ((out + ",\"value\":") + (EVGTreeJson.str(op.value)))
576
+ }
577
+ if (op.op == "set-css") {
578
+ out = ((out + ",\"value\":") + (EVGTreeJson.str(op.value)))
579
+ }
580
+ if ((strlen op.toPath) > 0) {
581
+ out = ((out + ",\"to\":") + (EVGTreeJson.str(op.toPath)))
582
+ }
583
+ if ((strlen op.tag) > 0) {
584
+ out = ((out + ",\"tag\":") + (EVGTreeJson.str(op.tag)))
585
+ }
586
+ if (((op.op == "insert") || (op.op == "move")) || (op.op == "restore")) {
587
+ out = ((out + ",\"index\":") + (to_string op.index))
588
+ }
589
+ ; `restore` names a subtree this process is holding, so an inverse
590
+ ; carrying one cannot be replayed from a file. Say so in the file
591
+ ; rather than let it look replayable.
592
+ if (op.op == "restore") {
593
+ out = (out + ",\"note\":\"in-process only — a removed subtree lives in the patch that removed it\"")
594
+ }
595
+ return (out + "}")
596
+ }
597
+
598
+ ; Is `path` at or below `from`? String prefix is not enough: "0/1" is not
599
+ ; under "0/12", and both start the same way.
600
+ sfn under:boolean (path:string from:string) {
601
+ if (path == from) {
602
+ return true
603
+ }
604
+ def head:string (from + "/")
605
+ def n:int (strlen head)
606
+ if ((strlen path) <= n) {
607
+ return false
608
+ }
609
+ return ((substring path 0 n) == head)
610
+ }
611
+
612
+ sfn depthOf:int (path:string) {
613
+ def n:int 0
614
+ def i:int 0
615
+ while (i < (strlen path)) {
616
+ if ((charAt path i) == 47) {
617
+ n = (n + 1)
618
+ }
619
+ i = (i + 1)
620
+ }
621
+ return n
622
+ }
623
+
624
+ ; --- measure --------------------------------------------------------------
625
+
626
+ ; The layout, in numbers. `EVGMeasure` is shared with the live-build server
627
+ ; so the agent and the page cannot disagree about whether a screen is
628
+ ; right; this verb is the flags around it.
629
+ fn measure:void (root:EVGElement) {
630
+ def m (new EVGMeasure())
631
+ ; Fill the design's own state holes first, so the numbers describe the
632
+ ; control as it is drawn rather than a `ui-switch-state-{wifi}` that
633
+ ; matches no rule and lays out to nothing. `measure` never writes the
634
+ ; document back — `patch` is a different verb and a different tree —
635
+ ; so dressing in place here cannot reach the file.
636
+ EVGDress.dress(root)
637
+ def w:int (EvgAgent.intFlag("--width" (EVGMeasure.sizeOf(root true 1200))))
638
+ def h:int (EvgAgent.intFlag("--height" (EVGMeasure.sizeOf(root false 900))))
639
+ def at:string ""
640
+ if (EvgAgent.hasFlag("--boxes")) {
641
+ at = (EvgAgent.textFlag("--at" "0"))
642
+ } {
643
+ at = (EvgAgent.textFlag("--at" ""))
644
+ }
645
+ m.look(root w h at)
646
+ print (m.json(true))
647
+ }
648
+
649
+ ; --- the ops file ---------------------------------------------------------
650
+
651
+ sfn readOps:[EVGPatchOp] (text:string) {
652
+ def out:[EVGPatchOp]
653
+ def c (new EVGJsonCursor())
654
+ c.src = text
655
+ c.at = 0
656
+ c.skipWs()
657
+ if ((c.take()) != 123) {
658
+ return out
659
+ }
660
+ while (c.ok) {
661
+ c.skipWs()
662
+ def ch:int (c.peek())
663
+ if (ch == 125) {
664
+ return out
665
+ }
666
+ if (ch == 0) {
667
+ return out
668
+ }
669
+ if (ch == 44) {
670
+ def comma:int (c.take())
671
+ } {
672
+ def key:string (c.readString())
673
+ c.skipWs()
674
+ def colon:int (c.take())
675
+ if (key == "ops") {
676
+ EvgAgent.readOpList(c out)
677
+ } {
678
+ c.skipValue()
679
+ }
680
+ }
681
+ }
682
+ return out
683
+ }
684
+
685
+ sfn readOpList:void (c:EVGJsonCursor out:[EVGPatchOp]) {
686
+ c.skipWs()
687
+ if ((c.take()) != 91) {
688
+ c.ok = false
689
+ return
690
+ }
691
+ while (c.ok) {
692
+ c.skipWs()
693
+ def ch:int (c.peek())
694
+ if (ch == 93) {
695
+ def closing:int (c.take())
696
+ return
697
+ }
698
+ if (ch == 0) {
699
+ c.ok = false
700
+ return
701
+ }
702
+ if (ch == 44) {
703
+ def comma:int (c.take())
704
+ } {
705
+ def op:EVGPatchOp (EvgAgent.readOp(c))
706
+ push out op
707
+ }
708
+ }
709
+ }
710
+
711
+ sfn readOp:EVGPatchOp (c:EVGJsonCursor) {
712
+ def o (new EVGPatchOp())
713
+ c.skipWs()
714
+ if ((c.take()) != 123) {
715
+ c.ok = false
716
+ return o
717
+ }
718
+ while (c.ok) {
719
+ c.skipWs()
720
+ def ch:int (c.peek())
721
+ if (ch == 125) {
722
+ def closing:int (c.take())
723
+ return o
724
+ }
725
+ if (ch == 0) {
726
+ c.ok = false
727
+ return o
728
+ }
729
+ if (ch == 44) {
730
+ def comma:int (c.take())
731
+ } {
732
+ def key:string (c.readString())
733
+ c.skipWs()
734
+ def colon:int (c.take())
735
+ if (key == "op") {
736
+ o.op = (c.readString())
737
+ } {
738
+ if (key == "at") {
739
+ o.at = (c.readString())
740
+ } {
741
+ if (key == "prop") {
742
+ o.prop = (c.readString())
743
+ } {
744
+ if (key == "value") {
745
+ o.value = (c.readString())
746
+ } {
747
+ if (key == "to") {
748
+ o.toPath = (c.readString())
749
+ } {
750
+ if (key == "tag") {
751
+ o.tag = (c.readString())
752
+ } {
753
+ if (key == "node") {
754
+ ; A whole subtree, in the same shape a
755
+ ; document is written in, read by the
756
+ ; same reader. `insert` then puts in a
757
+ ; control rather than an empty box.
758
+ def sub@(optional):EVGElement (c.readNode())
759
+ if sub {
760
+ def kid:EVGElement (unwrap sub)
761
+ o.node = sub
762
+ if ((strlen o.tag) == 0) {
763
+ o.tag = kid.tagName
764
+ }
765
+ }
766
+ } {
767
+ if (key == "index") {
768
+ o.index = (c.readNumber())
769
+ } {
770
+ c.skipValue()
771
+ }
772
+ }
773
+ }
774
+ }
775
+ }
776
+ }
777
+ }
778
+ }
779
+ }
780
+ }
781
+ return o
782
+ }
783
+
784
+ ; --- flags, files and text ------------------------------------------------
785
+
786
+ sfn textFlag:string (name:string fallback:string) {
787
+ def argc:int (shell_arg_cnt)
788
+ def head:string (name + "=")
789
+ def n:int (strlen head)
790
+ def i:int 0
791
+ while (i < argc) {
792
+ def a:string (shell_arg i)
793
+ if ((strlen a) > n) {
794
+ if ((substring a 0 n) == head) {
795
+ return (substring a n (strlen a))
796
+ }
797
+ }
798
+ i = (i + 1)
799
+ }
800
+ return fallback
801
+ }
802
+
803
+ ; A flag with no value: `--boxes`, not `--boxes=1`.
804
+ sfn hasFlag:boolean (name:string) {
805
+ def argc:int (shell_arg_cnt)
806
+ def i:int 0
807
+ while (i < argc) {
808
+ if ((shell_arg i) == name) {
809
+ return true
810
+ }
811
+ i = (i + 1)
812
+ }
813
+ return false
814
+ }
815
+
816
+ sfn intFlag:int (name:string fallback:int) {
817
+ def v:string (EvgAgent.textFlag(name ""))
818
+ if ((strlen v) == 0) {
819
+ return fallback
820
+ }
821
+ def parsed@(optional):int (to_int v)
822
+ if (null? parsed) {
823
+ return fallback
824
+ }
825
+ return (unwrap parsed)
826
+ }
827
+
828
+ sfn slurp@(optional):string (path:string) {
829
+ def cut:int (EvgAgent.lastSlash(path))
830
+ def dir:string "."
831
+ def name:string path
832
+ if (cut >= 0) {
833
+ dir = (substring path 0 cut)
834
+ name = (substring path (cut + 1) (strlen path))
835
+ }
836
+ def text@(optional):string (read_file dir name)
837
+ return text
838
+ }
839
+
840
+ sfn spit:void (path:string data:string) {
841
+ def cut:int (EvgAgent.lastSlash(path))
842
+ def dir:string "."
843
+ def name:string path
844
+ if (cut >= 0) {
845
+ dir = (substring path 0 cut)
846
+ name = (substring path (cut + 1) (strlen path))
847
+ }
848
+ write_file dir name data
849
+ }
850
+
851
+ sfn lastSlash:int (path:string) {
852
+ def at:int (0 - 1)
853
+ def i:int 0
854
+ while (i < (strlen path)) {
855
+ if ((charAt path i) == 47) {
856
+ at = i
857
+ }
858
+ i = (i + 1)
859
+ }
860
+ return at
861
+ }
862
+
863
+ sfn strArray:string (items:[string]) {
864
+ def out:string "["
865
+ def i:int 0
866
+ while (i < (array_length items)) {
867
+ if (i > 0) {
868
+ out = (out + ",")
869
+ }
870
+ out = (out + "\n ")
871
+ out = (out + (EVGTreeJson.str((itemAt items i))))
872
+ i = (i + 1)
873
+ }
874
+ if ((array_length items) > 0) {
875
+ out = (out + "\n")
876
+ }
877
+ return (out + "]")
878
+ }
879
+
880
+ sfn pad:string (s:string width:int) {
881
+ def out:string s
882
+ while ((strlen out) < width) {
883
+ out = (out + " ")
884
+ }
885
+ return out
886
+ }
887
+
888
+ sfn clip:string (s:string width:int) {
889
+ if ((strlen s) <= width) {
890
+ return s
891
+ }
892
+ return ((substring s 0 width) + "…")
893
+ }
894
+
895
+ sfn num:string (v:double) {
896
+ def whole:int (to_int v)
897
+ return (to_string whole)
898
+ }
899
+ }