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.
- package/CHANGELOG.md +1064 -20
- package/LICENSE +3 -1
- package/README.md +123 -28
- package/dist/Lang.rgr +1188 -290
- package/dist/api.d.ts +2343 -879
- package/dist/api.js +79923 -56921
- package/dist/lib/JSON.rgr +102 -91
- package/dist/lib/Shell.rgr +4 -4
- package/dist/lib/apple/AppleToolchain.rgr +4 -4
- package/dist/lib/apple/README.md +5 -4
- package/dist/lib/apple/apple_test.rgr +1 -1
- package/dist/lib/core/README.md +1 -1
- package/dist/lib/evg/EVG.rgr +12 -0
- package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
- package/dist/lib/evg/EVGA11yTree.rgr +894 -0
- package/dist/lib/evg/EVGBox.rgr +267 -0
- package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
- package/dist/lib/evg/EVGCodepoint.rgr +316 -0
- package/dist/lib/evg/EVGColor.rgr +700 -0
- package/dist/lib/evg/EVGCommands.rgr +177 -0
- package/dist/lib/evg/EVGComponent.rgr +331 -0
- package/dist/lib/evg/EVGComponentTest.rgr +387 -0
- package/dist/lib/evg/EVGConnector.rgr +541 -0
- package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
- package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
- package/dist/lib/evg/EVGEasing.rgr +370 -0
- package/dist/lib/evg/EVGEffectTest.rgr +253 -0
- package/dist/lib/evg/EVGElement.rgr +4914 -0
- package/dist/lib/evg/EVGFixedTest.rgr +289 -0
- package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
- package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
- package/dist/lib/evg/EVGFling.rgr +244 -0
- package/dist/lib/evg/EVGFocus.rgr +400 -0
- package/dist/lib/evg/EVGFocusTest.rgr +399 -0
- package/dist/lib/evg/EVGGradient.rgr +322 -0
- package/dist/lib/evg/EVGGrapheme.rgr +190 -0
- package/dist/lib/evg/EVGGrid.rgr +975 -0
- package/dist/lib/evg/EVGHitTest.rgr +208 -0
- package/dist/lib/evg/EVGHoles.rgr +294 -0
- package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
- package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
- package/dist/lib/evg/EVGHostTree.rgr +834 -0
- package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
- package/dist/lib/evg/EVGImageDecode.rgr +117 -0
- package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
- package/dist/lib/evg/EVGInspect.rgr +883 -0
- package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
- package/dist/lib/evg/EVGJsonTest.rgr +535 -0
- package/dist/lib/evg/EVGLayout.rgr +4220 -0
- package/dist/lib/evg/EVGMeasure.rgr +1267 -0
- package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
- package/dist/lib/evg/EVGPatch.rgr +1721 -0
- package/dist/lib/evg/EVGPatchTest.rgr +692 -0
- package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
- package/dist/lib/evg/EVGReconcile.rgr +226 -0
- package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
- package/dist/lib/evg/EVGReject.rgr +126 -0
- package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
- package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
- package/dist/lib/evg/EVGRuler.rgr +232 -0
- package/dist/lib/evg/EVGRulerTest.rgr +172 -0
- package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
- package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
- package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
- package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
- package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
- package/dist/lib/evg/EVGText.rgr +105 -0
- package/dist/lib/evg/EVGTextEngine.rgr +536 -0
- package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
- package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
- package/dist/lib/evg/EVGToolbar.rgr +1369 -0
- package/dist/lib/evg/EVGTransition.rgr +728 -0
- package/dist/lib/evg/EVGTreeJson.rgr +721 -0
- package/dist/lib/evg/EVGUnit.rgr +554 -0
- package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
- package/dist/lib/evg/EvgApp.rgr +189 -0
- package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
- package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
- package/dist/lib/evg/EvgHost.rgr +451 -0
- package/dist/lib/evg/EvgTest.rgr +91 -0
- package/dist/lib/evg/EvgTraceColor.rgr +236 -0
- package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
- package/dist/lib/evg/EvgTraceFit.rgr +767 -0
- package/dist/lib/evg/EvgTracePath.rgr +435 -0
- package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
- package/dist/lib/evg/EvgViewport.rgr +313 -0
- package/dist/lib/evg/FxDemoDoc.rgr +185 -0
- package/dist/lib/evg/HOSTS.md +227 -0
- package/dist/lib/evg/ISSUES.md +835 -0
- package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
- package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
- package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
- package/dist/lib/evg/PLAN_EVG.md +518 -0
- package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
- package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
- package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
- package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
- package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
- package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
- package/dist/lib/evg/PathBuilder.rgr +333 -0
- package/dist/lib/evg/README.md +1236 -0
- package/dist/lib/evg/SPEC.md +1252 -0
- package/dist/lib/evg/SVGPathParser.rgr +1199 -0
- package/dist/lib/evg/SvgParser.rgr +1736 -0
- package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
- package/dist/lib/evg/VectorShapes.rgr +331 -0
- package/dist/lib/evg/VectorStroke.rgr +107 -0
- package/dist/lib/evg/VectorViewBox.rgr +380 -0
- package/dist/lib/evg/agent/README.md +290 -0
- package/dist/lib/evg/agent/evg_agent.rgr +899 -0
- package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
- package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
- package/dist/lib/evg/agent/fixtures/connector.css +63 -0
- package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
- package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
- package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
- package/dist/lib/evg/agent/fixtures/popover.css +99 -0
- package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
- package/dist/lib/evg/agent/roundtrip.sh +74 -0
- package/dist/lib/evg/agent/smoke.sh +230 -0
- package/dist/lib/evg/android/README.md +97 -0
- package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
- package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
- package/dist/lib/evg/android/androidstubs/App.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
- package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
- package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
- package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
- package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
- package/dist/lib/evg/android/androidstubs/View.kt +113 -0
- package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
- package/dist/lib/evg/apple/README.md +128 -0
- package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
- package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
- package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
- package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
- package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
- package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
- package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
- package/dist/lib/evg/bench/README.md +64 -0
- package/dist/lib/evg/bench/layout-bench.mjs +316 -0
- package/dist/lib/evg/bench/layout-cases.mjs +510 -0
- package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
- package/dist/lib/evg/bin/.gitignore +17 -0
- package/dist/lib/evg/evg_test.rgr +313 -0
- package/dist/lib/evg/gl/README.md +198 -0
- package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
- package/dist/lib/evg/gl/blur-check.mjs +399 -0
- package/dist/lib/evg/gl/boxmodel.json +1 -0
- package/dist/lib/evg/gl/demo.html +46 -0
- package/dist/lib/evg/gl/effect-presets.css +285 -0
- package/dist/lib/evg/gl/effect-presets.js +63 -0
- package/dist/lib/evg/gl/effect-shots.mjs +135 -0
- package/dist/lib/evg/gl/evg-a11y.js +566 -0
- package/dist/lib/evg/gl/evg-binary.js +160 -0
- package/dist/lib/evg/gl/evg-engine.js +296 -0
- package/dist/lib/evg/gl/evg-fx.js +237 -0
- package/dist/lib/evg/gl/evg-gestures.js +209 -0
- package/dist/lib/evg/gl/evg-list.js +167 -0
- package/dist/lib/evg/gl/evg-measure.js +186 -0
- package/dist/lib/evg/gl/evg-textinput.js +303 -0
- package/dist/lib/evg/gl/evg-view.js +144 -0
- package/dist/lib/evg/gl/evg-webgl.js +3942 -0
- package/dist/lib/evg/gl/fx-check.mjs +998 -0
- package/dist/lib/evg/gl/fx-demo.html +166 -0
- package/dist/lib/evg/gl/fx-demo.js +2 -0
- package/dist/lib/evg/gl/fx-serve.mjs +47 -0
- package/dist/lib/evg/gl/gestures-check.mjs +189 -0
- package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
- package/dist/lib/evg/gl/measure-check.mjs +135 -0
- package/dist/lib/evg/gl/rotation-check.mjs +207 -0
- package/dist/lib/evg/gl/shift-check.mjs +113 -0
- package/dist/lib/evg/gl/stroke-check.mjs +168 -0
- package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
- package/dist/lib/evg/gl/view-check.mjs +343 -0
- package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
- package/dist/lib/evg/html/evg-dom.js +318 -0
- package/dist/lib/evg/html/evg-html.js +600 -0
- package/dist/lib/evg/inspect/README.md +415 -0
- package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
- package/dist/lib/evg/inspect/evg-inspect.js +947 -0
- package/dist/lib/evg/inspect/shots/css.png +0 -0
- package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
- package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
- package/dist/lib/evg/inspect/shots/state.png +0 -0
- package/dist/lib/evg/inspect/shots.mjs +226 -0
- package/dist/lib/evg/oracle/css-blur.json +576 -0
- package/dist/lib/evg/oracle/css-box.json +157 -0
- package/dist/lib/evg/oracle/css-timing.json +709 -0
- package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
- package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
- package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
- package/dist/lib/evg/original/EVGColor.clj +310 -0
- package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
- package/dist/lib/evg/original/SVGPath.rgr +627 -0
- package/dist/lib/evg/original/Vec2.crgr +103 -0
- package/dist/lib/evg/ranger.json +9 -0
- package/dist/lib/evg/showcase/README.md +246 -0
- package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
- package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
- package/dist/lib/evg/showcase/build.mjs +651 -0
- package/dist/lib/evg/showcase/pages/album.tsx +30 -0
- package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
- package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
- package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
- package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
- package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
- package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
- package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
- package/dist/lib/evg/showcase/pages/more.tsx +332 -0
- package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
- package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
- package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
- package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
- package/dist/lib/evg/showcase/pages/units.tsx +35 -0
- package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
- package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
- package/dist/lib/evg/showcase/pages/views.tsx +223 -0
- package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
- package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
- package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
- package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
- package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
- package/dist/lib/evg/showcase/themes/more-default.css +128 -0
- package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
- package/dist/lib/evg/showcase/themes/showcase.css +508 -0
- package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
- package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
- package/dist/lib/evg/showcase/themes/views-default.css +15 -0
- package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
- package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
- package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
- package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
- package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
- package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
- package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
- package/dist/lib/evg/web/responsive/README.md +97 -0
- package/dist/lib/evg/web/responsive/build.mjs +90 -0
- package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
- package/dist/lib/evg/web/responsive/index.html +177 -0
- package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
- package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
- package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
- package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
- package/dist/lib/evg/web/tools/minify.mjs +60 -0
- package/dist/lib/evg/web/tracer/build.mjs +87 -0
- package/dist/lib/evg/web/tracer/index.html +2805 -0
- package/dist/lib/evg/web/tracer/sample.png +0 -0
- package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
- package/dist/lib/evgr/Cargo.lock +21 -0
- package/dist/lib/evgr/Cargo.toml +19 -0
- package/dist/lib/evgr/README.md +140 -0
- package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
- package/dist/lib/evgr/bench/compare.mjs +252 -0
- package/dist/lib/evgr/bench/speed.mjs +277 -0
- package/dist/lib/evgr/bin/.gitignore +3 -0
- package/dist/lib/evgr/src/bin/bench.rs +106 -0
- package/dist/lib/evgr/src/bin/smoke.rs +13 -0
- package/dist/lib/evgr/src/grid.rs +730 -0
- package/dist/lib/evgr/src/lib.rs +1003 -0
- package/dist/lib/evgr/src/style.rs +468 -0
- package/dist/lib/evgr/src/text.rs +83 -0
- package/dist/lib/image/BitReader.rgr +171 -0
- package/dist/lib/image/Buffer.rgr +173 -0
- package/dist/lib/image/DCT.rgr +283 -0
- package/dist/lib/image/Deflate.rgr +339 -0
- package/dist/lib/image/HuffmanDecoder.rgr +181 -0
- package/dist/lib/image/ImageBuffer.rgr +706 -0
- package/dist/lib/image/JPEGDecoder.rgr +808 -0
- package/dist/lib/image/PNGDecoder.rgr +544 -0
- package/dist/lib/image/PNGEncoder.rgr +388 -0
- package/dist/lib/image/PPMImage.rgr +224 -0
- package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
- package/dist/lib/image/README.md +33 -0
- package/dist/lib/image/RasterBuffer.rgr +294 -0
- package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
- package/dist/lib/image/WebPDecoder.rgr +433 -0
- package/dist/lib/image/WebPLossless.rgr +1035 -0
- package/dist/lib/image/WebPLossy.rgr +1929 -0
- package/dist/lib/image/ranger.json +9 -0
- package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
- package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
- package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
- package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
- package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
- package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
- package/dist/lib/image/tests/run_webp_tests.sh +39 -0
- package/dist/lib/rust/RsJson.rgr +468 -0
- package/dist/lib/rust/RsPrelude.rgr +1663 -0
- package/dist/lib/shell_test.rgr +7 -7
- package/dist/lib/stdlib.rgr +21 -5
- package/dist/lib/zip/ranger.json +6 -0
- package/dist/rgrc.js +87318 -67610
- package/package.json +1010 -1040
- package/dist/README.md +0 -117
- package/dist/package.json +0 -47
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; ==============================================================================
|
|
4
|
+
; VectorViewBox.rgr — the one place that decides what vector coordinates mean
|
|
5
|
+
; ==============================================================================
|
|
6
|
+
; PLAN_VECTOR_IR.md §2.1 and §5 Stage 0.
|
|
7
|
+
;
|
|
8
|
+
; Before this file, each renderer answered "where on the page does user unit
|
|
9
|
+
; (10, 20) land?" for itself, and they answered differently:
|
|
10
|
+
;
|
|
11
|
+
; PDF fitted the path's BOUNDING BOX to the element box with independent X
|
|
12
|
+
; and Y scale factors, and ignored viewBox entirely — so a non-square
|
|
13
|
+
; icon was stretched, and deliberate padding inside the viewBox was
|
|
14
|
+
; cropped away.
|
|
15
|
+
; HTML handed viewBox to the browser, which applied real SVG semantics:
|
|
16
|
+
; uniform scale, centred, letterboxed.
|
|
17
|
+
;
|
|
18
|
+
; Same document, two geometries. The fix is not to patch either renderer but to
|
|
19
|
+
; have exactly one implementation of the SVG rule, which every renderer calls.
|
|
20
|
+
;
|
|
21
|
+
; The rule implemented here is SVG 1.1 viewBox / preserveAspectRatio: derive a
|
|
22
|
+
; scale from viewport/viewBox on each axis, collapse the two to one (min for
|
|
23
|
+
; `meet`, max for `slice`) unless alignment is `none`, then translate so the
|
|
24
|
+
; requested alignment point of the scaled viewBox meets the same point of the
|
|
25
|
+
; viewport.
|
|
26
|
+
;
|
|
27
|
+
; Nothing here renders. It returns a Matrix2D, so PDF can emit it as a `cm`
|
|
28
|
+
; operand, raster can transform points before flattening, and a test can assert
|
|
29
|
+
; the numbers directly against the spec — no image comparison needed.
|
|
30
|
+
; ==============================================================================
|
|
31
|
+
|
|
32
|
+
; ------------------------------------------------------------------------------
|
|
33
|
+
; Matrix2D — a 2D affine transform in SVG/PDF operand order.
|
|
34
|
+
;
|
|
35
|
+
; | a c e | x' = a*x + c*y + e
|
|
36
|
+
; | b d f | y' = b*x + d*y + f
|
|
37
|
+
; | 0 0 1 |
|
|
38
|
+
;
|
|
39
|
+
; Deliberately the same six-number layout as an SVG `matrix(...)` and a PDF `cm`
|
|
40
|
+
; operator, so handing it to either renderer needs no reordering.
|
|
41
|
+
; ------------------------------------------------------------------------------
|
|
42
|
+
class Matrix2D {
|
|
43
|
+
def a:double 1.0
|
|
44
|
+
def b:double 0.0
|
|
45
|
+
def c:double 0.0
|
|
46
|
+
def d:double 1.0
|
|
47
|
+
def e:double 0.0
|
|
48
|
+
def f:double 0.0
|
|
49
|
+
|
|
50
|
+
sfn identity:Matrix2D () {
|
|
51
|
+
def m (new Matrix2D())
|
|
52
|
+
return m
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
sfn create:Matrix2D (ma:double mb:double mc:double md:double me:double mf:double) {
|
|
56
|
+
def m (new Matrix2D())
|
|
57
|
+
m.a = ma
|
|
58
|
+
m.b = mb
|
|
59
|
+
m.c = mc
|
|
60
|
+
m.d = md
|
|
61
|
+
m.e = me
|
|
62
|
+
m.f = mf
|
|
63
|
+
return m
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
sfn translate:Matrix2D (tx:double ty:double) {
|
|
67
|
+
return (Matrix2D.create(1.0 0.0 0.0 1.0 tx ty))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
sfn scale:Matrix2D (sx:double sy:double) {
|
|
71
|
+
return (Matrix2D.create(sx 0.0 0.0 sy 0.0 0.0))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn applyX:double (x:double y:double) {
|
|
75
|
+
def v:double (((a * x) + (c * y)) + e)
|
|
76
|
+
return v
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fn applyY:double (x:double y:double) {
|
|
80
|
+
def v:double (((b * x) + (d * y)) + f)
|
|
81
|
+
return v
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
; this ∘ other — apply `other` first, then this.
|
|
85
|
+
fn multiply:Matrix2D (o:Matrix2D) {
|
|
86
|
+
def na:double ((a * o.a) + (c * o.b))
|
|
87
|
+
def nb:double ((b * o.a) + (d * o.b))
|
|
88
|
+
def nc:double ((a * o.c) + (c * o.d))
|
|
89
|
+
def nd:double ((b * o.c) + (d * o.d))
|
|
90
|
+
def ne:double (((a * o.e) + (c * o.f)) + e)
|
|
91
|
+
def nf:double (((b * o.e) + (d * o.f)) + f)
|
|
92
|
+
return (Matrix2D.create(na nb nc nd ne nf))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fn isIdentity:boolean () {
|
|
96
|
+
def flat:boolean true
|
|
97
|
+
if ((a == 1.0) == false) { flat = false }
|
|
98
|
+
if ((b == 0.0) == false) { flat = false }
|
|
99
|
+
if ((c == 0.0) == false) { flat = false }
|
|
100
|
+
if ((d == 1.0) == false) { flat = false }
|
|
101
|
+
if ((e == 0.0) == false) { flat = false }
|
|
102
|
+
if ((f == 0.0) == false) { flat = false }
|
|
103
|
+
return flat
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
; ------------------------------------------------------------------------------
|
|
108
|
+
; ViewBoxRect — a parsed `viewBox="minX minY width height"`.
|
|
109
|
+
;
|
|
110
|
+
; `isSet` is false for a missing or malformed viewBox. Callers must check it:
|
|
111
|
+
; per spec a negative or zero width/height disables rendering rather than
|
|
112
|
+
; silently meaning something else, so guessing here would hide a document bug.
|
|
113
|
+
; ------------------------------------------------------------------------------
|
|
114
|
+
class ViewBoxRect {
|
|
115
|
+
def minX:double 0.0
|
|
116
|
+
def minY:double 0.0
|
|
117
|
+
def width:double 0.0
|
|
118
|
+
def height:double 0.0
|
|
119
|
+
def isSet:boolean false
|
|
120
|
+
|
|
121
|
+
sfn create:ViewBoxRect (x:double y:double w:double h:double) {
|
|
122
|
+
def r (new ViewBoxRect())
|
|
123
|
+
r.minX = x
|
|
124
|
+
r.minY = y
|
|
125
|
+
r.width = w
|
|
126
|
+
r.height = h
|
|
127
|
+
r.isSet = true
|
|
128
|
+
return r
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
; Back to the attribute form, for renderers that emit SVG rather than
|
|
132
|
+
; consume it. Round-trips through parseViewBox.
|
|
133
|
+
fn asAttribute:string () {
|
|
134
|
+
def s:string ((to_string minX) + " " + (to_string minY))
|
|
135
|
+
s = (s + " " + (to_string width) + " " + (to_string height))
|
|
136
|
+
return s
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
; ------------------------------------------------------------------------------
|
|
141
|
+
; VectorViewBox — viewBox + preserveAspectRatio → Matrix2D.
|
|
142
|
+
; ------------------------------------------------------------------------------
|
|
143
|
+
class VectorViewBox {
|
|
144
|
+
|
|
145
|
+
; Alignment codes. X and Y are independent in SVG, so they are kept apart
|
|
146
|
+
; rather than enumerating the nine combined keywords.
|
|
147
|
+
; 0 = Min (left / top), 1 = Mid (centre), 2 = Max (right / bottom)
|
|
148
|
+
; `none` is signalled separately, because it is the one case where the two
|
|
149
|
+
; axes keep independent scales.
|
|
150
|
+
|
|
151
|
+
sfn splitTokens:[string] (s:string) {
|
|
152
|
+
def parts:[string]
|
|
153
|
+
def current:string ""
|
|
154
|
+
def i:int 0
|
|
155
|
+
def n:int (strlen s)
|
|
156
|
+
while (i < n) {
|
|
157
|
+
def ch:int (charAt s i)
|
|
158
|
+
def isSep:boolean false
|
|
159
|
+
if (ch == 32) { isSep = true }
|
|
160
|
+
if (ch == 9) { isSep = true }
|
|
161
|
+
if (ch == 10) { isSep = true }
|
|
162
|
+
if (ch == 13) { isSep = true }
|
|
163
|
+
if (ch == 44) { isSep = true }
|
|
164
|
+
if isSep {
|
|
165
|
+
if ((strlen current) > 0) {
|
|
166
|
+
push parts current
|
|
167
|
+
current = ""
|
|
168
|
+
}
|
|
169
|
+
} {
|
|
170
|
+
current = (current + (strfromcode ch))
|
|
171
|
+
}
|
|
172
|
+
i = (i + 1)
|
|
173
|
+
}
|
|
174
|
+
if ((strlen current) > 0) {
|
|
175
|
+
push parts current
|
|
176
|
+
}
|
|
177
|
+
return parts
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
; "minX minY width height", separated by whitespace and/or commas.
|
|
181
|
+
;
|
|
182
|
+
; Returns an unset rect when the list is not four numbers, or when width or
|
|
183
|
+
; height is not positive. SVG 1.1: a negative value is an error and zero
|
|
184
|
+
; disables rendering of the element — either way there is no transform to
|
|
185
|
+
; compute, and the caller decides what to do about it.
|
|
186
|
+
sfn parseViewBox:ViewBoxRect (s:string) {
|
|
187
|
+
def out (new ViewBoxRect())
|
|
188
|
+
def parts:[string] (VectorViewBox.splitTokens(s))
|
|
189
|
+
if ((array_length parts) != 4) {
|
|
190
|
+
return out
|
|
191
|
+
}
|
|
192
|
+
def vals:[double]
|
|
193
|
+
def i:int 0
|
|
194
|
+
while (i < 4) {
|
|
195
|
+
def tok:string (itemAt parts i)
|
|
196
|
+
def num@(optional):double (to_double tok)
|
|
197
|
+
if num {
|
|
198
|
+
push vals (unwrap num)
|
|
199
|
+
} {
|
|
200
|
+
return out
|
|
201
|
+
}
|
|
202
|
+
i = (i + 1)
|
|
203
|
+
}
|
|
204
|
+
def w:double (itemAt vals 2)
|
|
205
|
+
def h:double (itemAt vals 3)
|
|
206
|
+
if (w <= 0.0) {
|
|
207
|
+
return out
|
|
208
|
+
}
|
|
209
|
+
if (h <= 0.0) {
|
|
210
|
+
return out
|
|
211
|
+
}
|
|
212
|
+
out.minX = (itemAt vals 0)
|
|
213
|
+
out.minY = (itemAt vals 1)
|
|
214
|
+
out.width = w
|
|
215
|
+
out.height = h
|
|
216
|
+
out.isSet = true
|
|
217
|
+
return out
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
; preserveAspectRatio → the three things the transform needs.
|
|
221
|
+
;
|
|
222
|
+
; Grammar: [defer] <align> [meet | slice], where align is "none" or one of
|
|
223
|
+
; the nine xM..YM.. keywords. `defer` only applies to <image> referencing
|
|
224
|
+
; another SVG, so it is accepted and ignored. An unrecognised value falls
|
|
225
|
+
; back to the spec default rather than failing: an odd string should not
|
|
226
|
+
; make an icon vanish.
|
|
227
|
+
sfn alignX:int (par:string) {
|
|
228
|
+
def parts:[string] (VectorViewBox.splitTokens(par))
|
|
229
|
+
def i:int 0
|
|
230
|
+
while (i < (array_length parts)) {
|
|
231
|
+
def tok:string (itemAt parts i)
|
|
232
|
+
if ((indexOf tok "xMin") == 0) { return 0 }
|
|
233
|
+
if ((indexOf tok "xMid") == 0) { return 1 }
|
|
234
|
+
if ((indexOf tok "xMax") == 0) { return 2 }
|
|
235
|
+
i = (i + 1)
|
|
236
|
+
}
|
|
237
|
+
return 1
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
sfn alignY:int (par:string) {
|
|
241
|
+
def parts:[string] (VectorViewBox.splitTokens(par))
|
|
242
|
+
def i:int 0
|
|
243
|
+
while (i < (array_length parts)) {
|
|
244
|
+
def tok:string (itemAt parts i)
|
|
245
|
+
if ((indexOf tok "YMin") > 0) { return 0 }
|
|
246
|
+
if ((indexOf tok "YMid") > 0) { return 1 }
|
|
247
|
+
if ((indexOf tok "YMax") > 0) { return 2 }
|
|
248
|
+
i = (i + 1)
|
|
249
|
+
}
|
|
250
|
+
return 1
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
sfn isNone:boolean (par:string) {
|
|
254
|
+
def parts:[string] (VectorViewBox.splitTokens(par))
|
|
255
|
+
def i:int 0
|
|
256
|
+
while (i < (array_length parts)) {
|
|
257
|
+
def tok:string (itemAt parts i)
|
|
258
|
+
if (tok == "none") { return true }
|
|
259
|
+
i = (i + 1)
|
|
260
|
+
}
|
|
261
|
+
return false
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
sfn isSlice:boolean (par:string) {
|
|
265
|
+
def parts:[string] (VectorViewBox.splitTokens(par))
|
|
266
|
+
def i:int 0
|
|
267
|
+
while (i < (array_length parts)) {
|
|
268
|
+
def tok:string (itemAt parts i)
|
|
269
|
+
if (tok == "slice") { return true }
|
|
270
|
+
i = (i + 1)
|
|
271
|
+
}
|
|
272
|
+
return false
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
; The transform mapping viewBox user units onto a viewport of viewW x viewH
|
|
276
|
+
; whose origin is (0, 0). Callers translate that origin into place
|
|
277
|
+
; themselves — for PDF that means composing with the page flip, which is why
|
|
278
|
+
; this stays viewport-local.
|
|
279
|
+
;
|
|
280
|
+
; SVG 1.1, in order:
|
|
281
|
+
; 1. scale-x = viewW / vb.width, scale-y = viewH / vb.height
|
|
282
|
+
; 2. unless align is `none`, collapse to one scale: min for meet (the
|
|
283
|
+
; whole viewBox fits, with letterboxing), max for slice (the viewport
|
|
284
|
+
; is covered, with overflow)
|
|
285
|
+
; 3. translate by -minX/-minY scaled, then by the alignment offset
|
|
286
|
+
sfn resolve:Matrix2D (vb:ViewBoxRect viewW:double viewH:double par:string) {
|
|
287
|
+
if (vb.isSet == false) {
|
|
288
|
+
return (Matrix2D.identity())
|
|
289
|
+
}
|
|
290
|
+
if (viewW <= 0.0) {
|
|
291
|
+
return (Matrix2D.identity())
|
|
292
|
+
}
|
|
293
|
+
if (viewH <= 0.0) {
|
|
294
|
+
return (Matrix2D.identity())
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
def scaleX:double (viewW / vb.width)
|
|
298
|
+
def scaleY:double (viewH / vb.height)
|
|
299
|
+
|
|
300
|
+
if ((VectorViewBox.isNone(par)) == false) {
|
|
301
|
+
def uniform:double scaleX
|
|
302
|
+
if (VectorViewBox.isSlice(par)) {
|
|
303
|
+
if (scaleY > uniform) {
|
|
304
|
+
uniform = scaleY
|
|
305
|
+
}
|
|
306
|
+
} {
|
|
307
|
+
if (scaleY < uniform) {
|
|
308
|
+
uniform = scaleY
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
scaleX = uniform
|
|
312
|
+
scaleY = uniform
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
def tx:double (0.0 - (vb.minX * scaleX))
|
|
316
|
+
def ty:double (0.0 - (vb.minY * scaleY))
|
|
317
|
+
|
|
318
|
+
def slackX:double (viewW - (vb.width * scaleX))
|
|
319
|
+
def slackY:double (viewH - (vb.height * scaleY))
|
|
320
|
+
|
|
321
|
+
def ax:int (VectorViewBox.alignX(par))
|
|
322
|
+
def ay:int (VectorViewBox.alignY(par))
|
|
323
|
+
|
|
324
|
+
if (ax == 1) {
|
|
325
|
+
tx = (tx + (slackX / 2.0))
|
|
326
|
+
}
|
|
327
|
+
if (ax == 2) {
|
|
328
|
+
tx = (tx + slackX)
|
|
329
|
+
}
|
|
330
|
+
if (ay == 1) {
|
|
331
|
+
ty = (ty + (slackY / 2.0))
|
|
332
|
+
}
|
|
333
|
+
if (ay == 2) {
|
|
334
|
+
ty = (ty + slackY)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return (Matrix2D.create(scaleX 0.0 0.0 scaleY tx ty))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
; Which viewBox actually governs an element, given what the document
|
|
341
|
+
; declared and where the path's ink turned out to be.
|
|
342
|
+
;
|
|
343
|
+
; An explicit viewBox always wins — that is the author saying what the
|
|
344
|
+
; coordinate system is. When there is none, SVG proper would draw at 1:1 and
|
|
345
|
+
; clip, but EVG's <Path> has always been sized by its `width`/`height` and
|
|
346
|
+
; every existing document relies on the art being fitted to that box. So the
|
|
347
|
+
; fallback synthesises a viewBox from the path bounds, which keeps those
|
|
348
|
+
; documents working AND — the point of doing it here rather than in one
|
|
349
|
+
; renderer — gives every renderer the same fallback. Previously PDF fitted
|
|
350
|
+
; the bounds while HTML drew at 1:1, so the two disagreed on exactly the
|
|
351
|
+
; documents that never set a viewBox, which is all of them.
|
|
352
|
+
;
|
|
353
|
+
; Degenerate ink (a horizontal line has zero height) yields an unset rect,
|
|
354
|
+
; and the caller falls back to identity rather than dividing by zero.
|
|
355
|
+
sfn effectiveViewBox:ViewBoxRect (declared:string boundsX:double boundsY:double boundsW:double boundsH:double) {
|
|
356
|
+
def explicit:ViewBoxRect (VectorViewBox.parseViewBox(declared))
|
|
357
|
+
if explicit.isSet {
|
|
358
|
+
return explicit
|
|
359
|
+
}
|
|
360
|
+
def synth (new ViewBoxRect())
|
|
361
|
+
if (boundsW <= 0.0) {
|
|
362
|
+
return synth
|
|
363
|
+
}
|
|
364
|
+
if (boundsH <= 0.0) {
|
|
365
|
+
return synth
|
|
366
|
+
}
|
|
367
|
+
return (ViewBoxRect.create(boundsX boundsY boundsW boundsH))
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
; Convenience: parse and resolve in one step, with the SVG default
|
|
371
|
+
; preserveAspectRatio when none was given on the element.
|
|
372
|
+
sfn resolveString:Matrix2D (viewBox:string viewW:double viewH:double par:string) {
|
|
373
|
+
def effective:string par
|
|
374
|
+
if ((strlen effective) == 0) {
|
|
375
|
+
effective = "xMidYMid meet"
|
|
376
|
+
}
|
|
377
|
+
def vb:ViewBoxRect (VectorViewBox.parseViewBox(viewBox))
|
|
378
|
+
return (VectorViewBox.resolve(vb viewW viewH effective))
|
|
379
|
+
}
|
|
380
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# `evg_agent` — the tool surface
|
|
2
|
+
|
|
3
|
+
Four verbs over an EVG document. An agent with a shell already has everything
|
|
4
|
+
it needs to edit one; MCP, a widget and an editor panel are transports over
|
|
5
|
+
exactly these, added when something needs them. The plan they belong to is
|
|
6
|
+
[`PLAN_AI_BRIDGE.md`](../../../docs/plans/PLAN_AI_BRIDGE.md).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm run agent -- outline <doc.evg.json> [--depth=N] [--at=PATH]
|
|
10
|
+
npm run agent -- query <doc.evg.json> <.class | #id | tag | path>
|
|
11
|
+
npm run agent -- patch <doc.evg.json> <ops.json> [--out=FILE]
|
|
12
|
+
npm run agent -- measure <doc.evg.json> [--width=N] [--height=N]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## outline — the addresses
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
$ npm run agent -- outline lib/evg/agent/fixtures/card.evg.json --depth=1
|
|
19
|
+
0 div .page width=600px height=400px background-color=rgb(246,247,249) …
|
|
20
|
+
0/0 div .card width=320px height=120px background-color=rgb(255,255,255) …
|
|
21
|
+
0/1 div .card width=320px height=120px background-color=rgb(255,255,255) …
|
|
22
|
+
|
|
23
|
+
… 3 deeper nodes not shown — raise --depth or pass --at to go into one
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The only verb that does not print JSON, because it is the only one meant to be
|
|
27
|
+
**read** rather than parsed. One line per node — its address, what it is, and
|
|
28
|
+
just the properties it actually sets — costs a fraction of the same tree as
|
|
29
|
+
JSON while carrying the addresses every other verb takes.
|
|
30
|
+
|
|
31
|
+
A truncated outline says so. A model that cannot see there is more will
|
|
32
|
+
confidently describe a document it has only the top of.
|
|
33
|
+
|
|
34
|
+
Addresses are `EVGInspect` paths: `0` is the root, `0/3` its fourth child,
|
|
35
|
+
`0/3/k:share` a child with a `key`. Keyed children survive a sibling being
|
|
36
|
+
inserted above them; unkeyed ones do not, which is the reason to key anything
|
|
37
|
+
an edit will come back to.
|
|
38
|
+
|
|
39
|
+
## query — finding one
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
$ npm run agent -- query lib/evg/agent/fixtures/card.evg.json .card
|
|
43
|
+
{"matches":[
|
|
44
|
+
{"at":"0/0","tag":"div","props":{"class-name":"card","width":"320px", …},"children":2},
|
|
45
|
+
{"at":"0/1","tag":"div","props":{"class-name":"card","width":"320px", …},"children":1}
|
|
46
|
+
],"count":2}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`.class`, `#id`, a bare tag, or a path. Not a selector engine — a compound
|
|
50
|
+
selector belongs to `EVGStyleSheet`, and a second weaker implementation of one
|
|
51
|
+
here would be worth less than the honest limit.
|
|
52
|
+
|
|
53
|
+
## patch — the edit
|
|
54
|
+
|
|
55
|
+
Ops are the only way to change a document. Each is addressed by a path,
|
|
56
|
+
validated against the engine's real property set, and carries its own inverse.
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{"ops":[
|
|
60
|
+
{"op":"set-text","at":"0/0/k:title","value":"Invoices"},
|
|
61
|
+
{"op":"set-prop","at":"0/0","prop":"background-color","value":"rgb(255,251,235)"},
|
|
62
|
+
{"op":"insert","at":"0/0","index":2,"tag":"span"},
|
|
63
|
+
{"op":"remove","at":"0/1"},
|
|
64
|
+
{"op":"move","at":"0/0/k:sub","to":"0/1","index":0}
|
|
65
|
+
]}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
$ npm run agent -- patch doc.evg.json ops.json
|
|
70
|
+
{"ok":true,"applied":4,"wrote":"doc.evg.json","inverse":[
|
|
71
|
+
{"op":"set-text","at":"0/0/k:title","value":"Orders"},
|
|
72
|
+
{"op":"set-prop","at":"0/0","prop":"background-color","value":"rgb(255,255,255)"},
|
|
73
|
+
{"op":"remove","at":"0/0/2"}
|
|
74
|
+
]}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`patch` answers with `layout` too — the same summary, without being asked:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
{"ok":true,"applied":1,"wrote":"doc.evg.json",
|
|
81
|
+
"layout":{"nodes":30,"count":1,"bottomFree":-13,
|
|
82
|
+
"findings":["0/8: bottom edge 856 is past the page height 844"]},
|
|
83
|
+
"inverse":[…]}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
An agent that patches and stops has changed a picture it cannot see. One
|
|
87
|
+
layout pass here costs less than the round trip it would skip.
|
|
88
|
+
|
|
89
|
+
The `inverse` list is a **runnable ops file**: save it, run `patch` with the
|
|
90
|
+
ops in reverse order, and the document is what it was. That is the difference
|
|
91
|
+
between "here is how to undo it" and "here is a description of the undo".
|
|
92
|
+
|
|
93
|
+
Four rules worth knowing before writing ops:
|
|
94
|
+
|
|
95
|
+
- **A rejected op fails the whole batch.** Nothing is applied and the document
|
|
96
|
+
is untouched. "3 of 5 applied" hands you a document nobody designed.
|
|
97
|
+
- **An op can apply and leave no trace.** A file carries only what differs from
|
|
98
|
+
a fresh element of that tag, and EVG's defaults are not CSS's — a div is
|
|
99
|
+
`flex-direction: column`. Setting a property to its default therefore removes
|
|
100
|
+
a line rather than adding one, and the node changed anyway. `patch` names
|
|
101
|
+
those ops in `atDefault` so that re-reading the file is not read as the edit
|
|
102
|
+
having been dropped:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
{"ok":true,"applied":1,"wrote":"doc.evg.json","atDefault":[
|
|
106
|
+
{"at":"0/0","prop":"flex-direction","value":"column","tag":"div"}
|
|
107
|
+
],"note":"applied — but those values are the tag default …","inverse":[…]}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- **A property that cannot be read back cannot be patched.** The patchable set
|
|
111
|
+
is `EVGPatch.patchableNames()` — which is also exactly what a document file
|
|
112
|
+
can carry, so nothing can be written down that an edit would silently drop.
|
|
113
|
+
- **`remove`'s inverse is in-process only.** It names a subtree the patch that
|
|
114
|
+
removed it is holding. A file cannot replay it; keep the pre-patch document
|
|
115
|
+
if you want that undo across two runs.
|
|
116
|
+
|
|
117
|
+
## measure — the part a model cannot do for itself
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
$ npm run agent -- measure lib/evg/agent/fixtures/broken.evg.json --width=600 --height=300
|
|
121
|
+
{"width":600,"height":300,"nodes":5,"findings":[
|
|
122
|
+
"0/0 and 0/1 overlap by 100×40",
|
|
123
|
+
"0/0/0 overflows its parent to the right by 200",
|
|
124
|
+
"0/2: right edge 820 is past the page width 600",
|
|
125
|
+
"0/2: bottom edge 320 is past the page height 300"
|
|
126
|
+
],"count":4,"bottomFree":200}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
It lays the document out and answers, in numbers, whether the result is wrong.
|
|
130
|
+
A screenshot asks a model to *see* that — badly, and for a thousand times the
|
|
131
|
+
tokens. This reads it off the boxes layout already computed.
|
|
132
|
+
|
|
133
|
+
What it checks today: a node past the page, a child spilling out of a parent
|
|
134
|
+
that clips, and two in-flow siblings on top of each other — with the overlap
|
|
135
|
+
in pixels, because "they overlap" and "they overlap by 1" are different bugs.
|
|
136
|
+
`overflow: visible` is not a finding — the author meant it to spill. Absolutely
|
|
137
|
+
positioned nodes are not checked for overlap; overlapping is what they are for.
|
|
138
|
+
|
|
139
|
+
Two numbers come back on a sound document as well, because "nothing is wrong"
|
|
140
|
+
is not the same as "this is what it looks like":
|
|
141
|
+
|
|
142
|
+
- **`bottomFree`** — the room left under the content, absolutes excluded. A
|
|
143
|
+
screen that grew by a card and reports the same free space did not put the
|
|
144
|
+
card where you think.
|
|
145
|
+
- **`tight`** — neighbours less than 4px apart. Not a defect; the measurement
|
|
146
|
+
of one, for a caller who can decide whether 2px between two cards was meant.
|
|
147
|
+
- **`align`** — what does not line up. Children that share a left edge are
|
|
148
|
+
aligned, children whose centres agree are centred, children that agree on
|
|
149
|
+
neither were aligned to nothing, and the spread says by how much. This is
|
|
150
|
+
the defect every other check misses: a screen where each row starts at a
|
|
151
|
+
different x has no overlap, no overflow, and is plainly wrong to look at.
|
|
152
|
+
An absolutely positioned child is compared against the flow it floats over,
|
|
153
|
+
because it is exempt from every other check and still has to line up; when
|
|
154
|
+
it is off by exactly the parent's padding, the report says so, since `left`
|
|
155
|
+
on an absolute child is resolved from inside that padding and asking for it
|
|
156
|
+
again adds it twice.
|
|
157
|
+
|
|
158
|
+
### Distances, when spacing is the question
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
$ npm run agent -- measure doc.evg.json --boxes --at=0/2
|
|
162
|
+
{…,"boxes":[
|
|
163
|
+
{"at":"0/2","tag":"div","x":16,"y":113,"w":358,"h":64,"gapNext":8},
|
|
164
|
+
{"at":"0/2/0","tag":"span","x":32,"y":122,"w":65,"h":29,"gapNext":4,"text":"1,284"}
|
|
165
|
+
]}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Where each node really is, and how far the next sibling starts from it.
|
|
169
|
+
`gapNext` is the layout's answer, not the markup's: it is what `gap`, the
|
|
170
|
+
margins and `flex` actually produced, which is the thing a model cannot work
|
|
171
|
+
out from the document it just wrote. The listing is the only part of `measure`
|
|
172
|
+
that grows with the document, so it costs nothing until `--boxes` asks for it,
|
|
173
|
+
and `--at` narrows it to one subtree.
|
|
174
|
+
|
|
175
|
+
The same checks run inside the live-build server (`EVGMeasure`), so the numbers
|
|
176
|
+
the agent reads and the numbers the page shows cannot drift apart.
|
|
177
|
+
|
|
178
|
+
A `path` has no box — its geometry is the `d` string — so its page bounds are
|
|
179
|
+
read from the coordinates in `d`, control points included. That bound is looser
|
|
180
|
+
than the shape, which is the right way round: a shape reported as on the page
|
|
181
|
+
is on it. A path using `A` is left unchecked, because arc flags among the
|
|
182
|
+
coordinates would invent a bound rather than widen one.
|
|
183
|
+
|
|
184
|
+
**What it does NOT check, on a diagram.** A document exported from RangerFlow
|
|
185
|
+
is entirely absolute positions and paths, so the overlap and parent-overflow
|
|
186
|
+
checks have nothing to work with: a label that no longer fits the shape drawn
|
|
187
|
+
behind it is not a finding, because nothing in the document says the two belong
|
|
188
|
+
together. Off-page is caught; fit is not. Look at a diagram before believing a
|
|
189
|
+
count of zero.
|
|
190
|
+
|
|
191
|
+
## A bitmap in — `evg_image_tool`
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
$ npm run agent:image # once, to build it
|
|
195
|
+
$ node lib/evg/bin/evg_image_tool.js photo.png --out=photo --width=180
|
|
196
|
+
{"width":320,"height":221,"layers":8,"rings":126,"colors":[
|
|
197
|
+
{"hex":"#E3C8A6","share":0.223},
|
|
198
|
+
{"hex":"#F0E9D4","share":0.214},
|
|
199
|
+
{"hex":"#0E184D","share":0.108}
|
|
200
|
+
],"wrote":{"svg":"photo.svg","ops":"photo.ops.json"},
|
|
201
|
+
"insertsAt":"0/0","placed":"180x124"}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
A model handed a photograph can describe it and cannot put it on a screen. The
|
|
205
|
+
bridge — `EvgBitmapTracer`, the same one the live tracer page uses —
|
|
206
|
+
has been here all along with nothing pointing an agent at it. This turns an
|
|
207
|
+
image into the two things an agent can act on:
|
|
208
|
+
|
|
209
|
+
- **the picture**, traced to flat colour layers and written out as an ops file,
|
|
210
|
+
so `npm run agent -- patch doc.evg.json photo.ops.json` puts it in the
|
|
211
|
+
document. It goes in as an `svg` node: vector, painted by the browser, the
|
|
212
|
+
rasteriser and the PDF backend alike, with no image loading anywhere.
|
|
213
|
+
- **the colours**, each with its share of the pixels — counted over the image,
|
|
214
|
+
not inferred from how many paths a layer produced, because one huge
|
|
215
|
+
background region and four hundred specks of the same colour say opposite
|
|
216
|
+
things about which colour the picture *is*.
|
|
217
|
+
|
|
218
|
+
The ops file is the point. A traced photograph is tens of kilobytes of
|
|
219
|
+
coordinates, and printing them would put every one through a model's context on
|
|
220
|
+
the way back into a patch. `--at` and `--index` say where the picture is
|
|
221
|
+
inserted, `--width` what it is placed at, and `--preset` picks how it is traced
|
|
222
|
+
(lineart, poster, photo, broken, print — see `EvgTraceTypes.rgr`).
|
|
223
|
+
|
|
224
|
+
## Getting a real document in, and a picture out
|
|
225
|
+
|
|
226
|
+
The four verbs read `.evg.json`. Two neighbours in `gallery/pdf_writer/src/tools`
|
|
227
|
+
connect that to everything else — and both of them read either format, so
|
|
228
|
+
nothing below is a special path for agents:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# a .tsx document (with its stylesheet resolved in) -> the editable format
|
|
232
|
+
npm run agent:import -- page.tsx page.evg.json -css themes/showcase.css -theme editorial
|
|
233
|
+
|
|
234
|
+
# the editable format -> a picture to look at
|
|
235
|
+
npm run agent:render -- page.evg.json page.png
|
|
236
|
+
npm run agent:html -- page.evg.json page.html
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
These build the tool if it is not there and then run it — the compiled
|
|
240
|
+
JavaScript under `gallery/pdf_writer/bin/` is not in the repository, because a
|
|
241
|
+
regenerated bundle is 40 000 lines of diff nobody can review. Running the tool
|
|
242
|
+
directly with `node` works too, once something has built it.
|
|
243
|
+
|
|
244
|
+
There is no `render` verb here, and that is deliberate: the painters already
|
|
245
|
+
exist and adding a second entry point to them would be two things to keep
|
|
246
|
+
working instead of one.
|
|
247
|
+
|
|
248
|
+
**The conversion is checked, not asserted.** `evg_json_tool` lays the original
|
|
249
|
+
out, reads its own output back, lays that out too, and compares — the boxes,
|
|
250
|
+
and then the draw commands the two produce. It also names anything it had to
|
|
251
|
+
drop. `-strict` turns that report into a refusal, which is what a build step
|
|
252
|
+
wants.
|
|
253
|
+
|
|
254
|
+
**Stylesheets are resolved in.** Whatever `-css` decides becomes inline props
|
|
255
|
+
in the output, so the agent edits one document rather than a tree plus a
|
|
256
|
+
cascade it cannot see. The cost is that theming is baked: re-theming means
|
|
257
|
+
converting again from the source.
|
|
258
|
+
|
|
259
|
+
**Asset paths move with the document.** A `src` is resolved against the file it
|
|
260
|
+
is written in, so the converter re-anchors every one for wherever the output is
|
|
261
|
+
going. When the two places have no expressible relation — one absolute, one not
|
|
262
|
+
— it says so instead of writing a path that resolves somewhere else.
|
|
263
|
+
|
|
264
|
+
## The document format
|
|
265
|
+
|
|
266
|
+
[`EVGTreeJson`](../EVGTreeJson.rgr). A node says its tag, identity, text, and
|
|
267
|
+
`props` — and `props` is exactly the patchable set. Only what differs from a
|
|
268
|
+
fresh element **of the same tag** is written, so a file stays short and a span
|
|
269
|
+
and a div can disagree about their defaults.
|
|
270
|
+
|
|
271
|
+
It is not a lossless container for an arbitrary EVG tree: a `path` element's
|
|
272
|
+
`d`, an image's `alt`, a `viewBox` — the engine has them and this format does
|
|
273
|
+
not. A converter into this format from JSX or a `.fig` has to say what it drops
|
|
274
|
+
rather than round-trip through here and call it identity.
|
|
275
|
+
|
|
276
|
+
## Running the tests
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
npm run evg:patch:test # EVGPatch and EVGTreeJson — 81 assertions
|
|
280
|
+
npm run agent:smoke # the four verbs, against the fixtures
|
|
281
|
+
npm run agent:roundtrip # every showcase page, converted and re-rendered
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
`agent:roundtrip` is the one that matters most and the one that found every
|
|
285
|
+
loss so far. It converts all seventeen showcase pages and requires the PNG
|
|
286
|
+
rendered from the conversion to be **byte-identical** to the PNG rendered from
|
|
287
|
+
the original. It asks nothing and compares everything, which is why it caught
|
|
288
|
+
three properties the format was missing — `grid-row`, an imported SVG's source,
|
|
289
|
+
and the `emoji-color` tint — each of which the converter's own audit had called
|
|
290
|
+
clean.
|