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,415 @@
|
|
|
1
|
+
# The EVG inspector
|
|
2
|
+
|
|
3
|
+
Dev tools for a picture a browser cannot read.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm run evg:inspect:demo # the dashboard, WebGL, with the panel
|
|
7
|
+
npm run pptx:html:serve # the slide editor, SVG — add ?inspect=1
|
|
8
|
+
npm run evg:inspect:test # the gates, no browser
|
|
9
|
+
npm run evg:inspect:web # the panel, in a real browser
|
|
10
|
+
npm run evg:inspect:live # a rule on disk → the painter, end to end
|
|
11
|
+
npm run evg:inspect:shots # remake the pictures below
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
An EVG app in a browser is one `<canvas>`. Open the browser's dev tools on it
|
|
15
|
+
and you get exactly that: one element, no children, no styles, no box model.
|
|
16
|
+
The DOM painter is not much better — a flat pile of `<rect>` and `<path>` in
|
|
17
|
+
paint order, with no names and no nesting. You can see the picture. You cannot
|
|
18
|
+
ask it anything.
|
|
19
|
+
|
|
20
|
+
This is the panel that answers. Add `?inspect=1` to a host that has been wired
|
|
21
|
+
to it and you get the element hierarchy, the box model drawn over the real
|
|
22
|
+
pixels, the computed style, and the draw commands each element emitted.
|
|
23
|
+
|
|
24
|
+
Edit `gallery/ui/demo/*.css` while the demo page is open and the picture
|
|
25
|
+
follows on save. That is not a reload of the page and nothing is patched — see
|
|
26
|
+
[the stylesheet is an input](#the-stylesheet-is-an-input).
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
## The fourth channel
|
|
31
|
+
|
|
32
|
+
Three descriptions already come out of an EVG app, and `gallery/ui/demo/main.js`
|
|
33
|
+
names them in its own header:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
displayListJson() what to draw
|
|
37
|
+
hitId(x, y) what is under the pointer
|
|
38
|
+
a11yJson() what it MEANS
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
They share the property this design is built on: **each is derived from the
|
|
42
|
+
same laid-out tree, on the same pass.** `EVGA11yFromTree` says why in its
|
|
43
|
+
header — an app that describes its tree a second time has two descriptions to
|
|
44
|
+
keep in step, and the one nobody can see is the one that rots.
|
|
45
|
+
|
|
46
|
+
`EVGInspect` is the fourth — **what it IS** — and obeys the same rule. Every
|
|
47
|
+
rectangle it reports is the one `EVGLayout` computed and `EVGDisplayList` drew,
|
|
48
|
+
read off `calculatedX` and its neighbours. Nothing here has a second opinion
|
|
49
|
+
about geometry, so the panel cannot disagree with the picture.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
EVGElement tree (laid out)
|
|
53
|
+
│
|
|
54
|
+
┌────────────┬───────┴───────┬──────────────┐
|
|
55
|
+
▼ ▼ ▼ ▼
|
|
56
|
+
EVGDisplayList EVGHitTest EVGA11yFromTree EVGInspect
|
|
57
|
+
what to draw what is what it means WHAT IT IS
|
|
58
|
+
under here
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
That is also why it works on both painters and off the browser entirely: it is
|
|
62
|
+
attached to the tree, not to a canvas. The WebGL page and the SVG page are the
|
|
63
|
+
same panel with the same code, and the module compiles to every target
|
|
64
|
+
`EVGDisplayList` does.
|
|
65
|
+
|
|
66
|
+
## Wiring a host to it
|
|
67
|
+
|
|
68
|
+
The panel knows nothing about any app. It is handed an adapter of small
|
|
69
|
+
functions, and everything past `tree` degrades rather than fails — which is
|
|
70
|
+
what lets one panel serve a slide editor and a dashboard without either
|
|
71
|
+
knowing about the other.
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
import { attach } from "…/lib/evg/inspect/evg-inspect.js";
|
|
75
|
+
|
|
76
|
+
attach({
|
|
77
|
+
surface, // the <canvas> or <svg> the app paints into
|
|
78
|
+
app: {
|
|
79
|
+
tree: () => app.inspectJson(gen), // REQUIRED
|
|
80
|
+
node: (path) => app.inspectNodeJson(path),
|
|
81
|
+
hit: (x, y) => app.inspectHitPath(x, y),
|
|
82
|
+
frame: () => app.inspectFrameJson(),
|
|
83
|
+
transform: () => app.inspectTransform(), // {x, y, k}, when the tree is
|
|
84
|
+
viewport: () => [w, h], // drawn inside a larger surface
|
|
85
|
+
css: () => ({ name, text, href, errors }),
|
|
86
|
+
setCss: (text) => app.inspectSetCss(text),
|
|
87
|
+
saveCss: (text) => fetch(href, { method: "PUT", body: text }),
|
|
88
|
+
force: (path, bits) => app.inspectForce(path, bits),
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Every one of these may return a promise.** A host in the same page answers
|
|
94
|
+
synchronously and the panel does not notice; the directions that are not in the
|
|
95
|
+
same page — a DevTools extension over `inspectedWindow.eval`, a native target
|
|
96
|
+
over a socket, a paused page answering out of a snapshot — are all the same
|
|
97
|
+
shape, which is that the answer does not arrive on the same tick. The two
|
|
98
|
+
exceptions are `transform` and `viewport`, read once per refresh and cached,
|
|
99
|
+
because a highlight that arrives a tick after the pointer is a highlight that
|
|
100
|
+
lags.
|
|
101
|
+
|
|
102
|
+
On the Ranger side that is four one-liners, because `EVGInspect` carries the
|
|
103
|
+
statics:
|
|
104
|
+
|
|
105
|
+
```ranger
|
|
106
|
+
fn inspectJson:string (gen:int) {
|
|
107
|
+
this.displayListJson()
|
|
108
|
+
def r:EVGElement (unwrap root)
|
|
109
|
+
def w:double (this.widthPx())
|
|
110
|
+
def h:double (this.heightPx())
|
|
111
|
+
return (EVGInspect.treeOf(r "Dashboard demo" gen w h))
|
|
112
|
+
}
|
|
113
|
+
fn inspectNodeJson:string (path:string) { … EVGInspect.nodeOf(r path) }
|
|
114
|
+
fn inspectHitPath:string (px:double py:double) { … EVGInspect.hitOf(r px py) }
|
|
115
|
+
fn inspectFrameJson:string () { … EVGInspect.frameOf(r) }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`hitOf` and `nodeOf` should be given the layout that is already there
|
|
119
|
+
(`laidOut()`), not a fresh render. On the dashboard `hitId` costs 11 ms and
|
|
120
|
+
`hitIdCached` costs 0.007 ms, and the whole difference is a page rebuilt to
|
|
121
|
+
answer a question about geometry that had not moved.
|
|
122
|
+
|
|
123
|
+
## Identity is a path
|
|
124
|
+
|
|
125
|
+
`EVGElement.id` is not an identity: it is optional, it is the app's own test
|
|
126
|
+
id, and most elements have none. So a node is named by where it sits:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
"0" the root
|
|
130
|
+
"0/3" its fourth child
|
|
131
|
+
"0/3/k:share" a child with `key` set uses the key instead of the index
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The same trick `EVGComponentHost` plays with `enter`/`leave`: a name unique
|
|
135
|
+
among siblings composes into a name unique in the tree, with no registry.
|
|
136
|
+
|
|
137
|
+
It is **structural**, and the cost is honest — insert an unkeyed row above the
|
|
138
|
+
selected one and the path names a different element. Keyed children are immune,
|
|
139
|
+
which is the set a list rebuild actually reorders. When a path stops resolving
|
|
140
|
+
the panel drops the selection and says so in its footer; re-pointing it at
|
|
141
|
+
whatever is now at that index would be a lie.
|
|
142
|
+
|
|
143
|
+
## Which commands did this element draw
|
|
144
|
+
|
|
145
|
+

|
|
146
|
+
|
|
147
|
+
`EVGDrawCmd` carries the element that emitted it, as an index into the inspect
|
|
148
|
+
walk, and writes it into the JSON as `"n"` — **only while attribution is on**.
|
|
149
|
+
Off, the list is byte-identical to the one built before the field existed, and
|
|
150
|
+
that is a gate rather than a hope (`inspect-check.mjs`, gate 5).
|
|
151
|
+
|
|
152
|
+
It is stamped in one place. `EVGDisplayList.walk` sets `curNode` for the
|
|
153
|
+
element it is walking and puts back what was there on the way out; `addCmd` is
|
|
154
|
+
the only thing that reads it, and every one of the twenty append sites in that
|
|
155
|
+
file goes through `addCmd`. A child cannot leave its slot behind for the
|
|
156
|
+
commands its parent emits afterwards.
|
|
157
|
+
|
|
158
|
+
This is worth more than the highlight it was added for. "Which element drew
|
|
159
|
+
these three commands" is the question behind the class of bug
|
|
160
|
+
[`gl/README.md`](../gl/README.md) describes: five painters each deciding again
|
|
161
|
+
what a box means, and border-radius working in PDF and silently not in PNG
|
|
162
|
+
because one painter read `box.borderRadius` and another a stale
|
|
163
|
+
`el.borderRadius`. With attribution the command that is wrong names the element
|
|
164
|
+
that is wrong.
|
|
165
|
+
|
|
166
|
+
In the picture above, the ellipse on slide 4 is selected and the commands pane
|
|
167
|
+
says it became a `RECT` and a `BORDER` at 456,288, 288×192, radius 96 — which
|
|
168
|
+
is how a preset ellipse is drawn.
|
|
169
|
+
|
|
170
|
+
## Which classes reach this element
|
|
171
|
+
|
|
172
|
+
This cascade selects on **classes and nothing else** — an `EVGStyleRule` carries
|
|
173
|
+
a class, a state, a theme and a media block, and the subset of CSS it implements
|
|
174
|
+
has nothing else in a selector. That is a limitation with a useful side: the set
|
|
175
|
+
of rules that can ever touch a node is exactly the set written against one of
|
|
176
|
+
its classes, so the panel can list them completely rather than guessing, and
|
|
177
|
+
that list is the answer to *where do I go to change this*.
|
|
178
|
+
|
|
179
|
+
Two things are shown with it, and both matter before you edit anything:
|
|
180
|
+
|
|
181
|
+
* **how many elements each class reaches.** `.db-card` is four cards. There is
|
|
182
|
+
no way to reach one of them without giving it a class of its own, so a panel
|
|
183
|
+
that let you edit `.db-card` without saying that would be inviting a surprise.
|
|
184
|
+
* **an element with no class at all is unreachable.** `applyTo` returns
|
|
185
|
+
immediately when `className` is empty, so no rule can ever style it, and
|
|
186
|
+
whatever it looks like was set on it directly. The panel says so instead of
|
|
187
|
+
showing an empty rule list that looks like a bug.
|
|
188
|
+
|
|
189
|
+
The rules themselves come out of the plan, in cascade order, winners and losers
|
|
190
|
+
together:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
RULES, STRONGEST LAST
|
|
194
|
+
.db-card 4 elements
|
|
195
|
+
display flex
|
|
196
|
+
border-radius 14px
|
|
197
|
+
background-color ▪ #ffffff
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Nothing is scored here and no selector is re-matched. `buildPlan` calls
|
|
201
|
+
`planGroup` in the four passes that **are** the precedence — plain, themed,
|
|
202
|
+
stateful, themed-and-stateful — and the last write wins, so "which declaration
|
|
203
|
+
won" is read off the end of the list. The panel cannot disagree with the engine
|
|
204
|
+
about who won because it is not deciding, it is reading. That is gate 6.
|
|
205
|
+
|
|
206
|
+
The record itself is one int per planned declaration in `EVGStyleSheet.planRules`,
|
|
207
|
+
pushed beside the name and value `planGroup` already pushes. It is **per plan,
|
|
208
|
+
not per element**: a 1600-row table has a handful of plans and a hundred
|
|
209
|
+
thousand applications of them, and the same record made at apply time would be
|
|
210
|
+
paid a hundred thousand times over.
|
|
211
|
+
|
|
212
|
+
A declaration that lost to an inline value is marked separately from one that
|
|
213
|
+
lost to another rule — see below.
|
|
214
|
+
|
|
215
|
+
## The stylesheet is an input
|
|
216
|
+
|
|
217
|
+

|
|
218
|
+
|
|
219
|
+
This is why the panel can edit at all, and why it needs no override layer.
|
|
220
|
+
|
|
221
|
+
**The element tree is an app's output.** Edit it and the next rebuild throws
|
|
222
|
+
the edit away — which is exactly why a DevTools style edit dies on a React
|
|
223
|
+
re-render, and why editing the tree would need a table of overrides re-applied
|
|
224
|
+
after every pass, an invalidation story, and a way to detect the app writing
|
|
225
|
+
over you.
|
|
226
|
+
|
|
227
|
+
**The stylesheet is an app's input.** `init(css)` is how every demo here starts.
|
|
228
|
+
Hand back a changed one and the app re-parses and re-cascades exactly as it did
|
|
229
|
+
at startup; the layout, the display list and the hit test follow because they
|
|
230
|
+
always did. Nothing is intercepted, no value is held over the app's head, and
|
|
231
|
+
**the text in the editor is the text that goes in the file** — there is no
|
|
232
|
+
"copy as CSS" step because there is nothing to translate.
|
|
233
|
+
|
|
234
|
+
Two routes into the same operation, and they cannot drift:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
the panel's CSS pane ─┬─► app.inspectSetCss(text) ─► EVGStyleSheet.reload
|
|
238
|
+
│ ▲
|
|
239
|
+
a save on disk ───────┼────────┘
|
|
240
|
+
│ │
|
|
241
|
+
└─ serve.mjs watch ─► SSE ─► the page fetches and hands it over
|
|
242
|
+
│
|
|
243
|
+
"save to disk" ───────┴─► PUT ─► serve.mjs writes the file
|
|
244
|
+
…which the watch then sees, so every other
|
|
245
|
+
page open on that sheet re-cascades too
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The PUT is deliberately narrow — a `.css` file directly inside
|
|
249
|
+
`gallery/ui/demo`, and nothing else. This server exists to serve a demo on a
|
|
250
|
+
developer's own machine, and a write that could reach further would be a worse
|
|
251
|
+
thing than the convenience is worth. The page that saved keeps the text it
|
|
252
|
+
sent, so its own save coming back round the watch is not applied twice.
|
|
253
|
+
|
|
254
|
+
`npm run evg:inspect:live` gates the whole path and checks the **colour of a
|
|
255
|
+
rectangle in the display list**, not the element and not the panel: a value
|
|
256
|
+
that reached the element and stopped there would be a frame still showing the
|
|
257
|
+
old colour, and that is the failure worth catching.
|
|
258
|
+
|
|
259
|
+
### `reload`, and the trap it exists for
|
|
260
|
+
|
|
261
|
+
`EVGStyleSheet.reload(css)` is not `new EVGStyleSheet()` at the call site, and
|
|
262
|
+
both reasons are silent when got wrong.
|
|
263
|
+
|
|
264
|
+
`parse` **appends**. It drops the plans and adds rules; it does not remove what
|
|
265
|
+
is already there. Parsing twice into one sheet leaves every rule in it twice,
|
|
266
|
+
which mostly looks like it worked because the duplicates agree — until one of
|
|
267
|
+
them is edited.
|
|
268
|
+
|
|
269
|
+
And `generation` starts at 1, so a fresh sheet is at 2 after its first parse —
|
|
270
|
+
exactly where the sheet it replaced was. `applyTo` skips an element whose
|
|
271
|
+
`styleGen` already equals the generation, so **every retained element would be
|
|
272
|
+
skipped and the new CSS would land on nothing.** Worse, it fails asymmetrically:
|
|
273
|
+
a page that rebuilds its tree hands the cascade fresh elements with `styleGen`
|
|
274
|
+
0 and works fine. It would pass in the tree-literal demos and fail in an app
|
|
275
|
+
that keeps its tree — which the dashboard does, so gate 7 is run against it
|
|
276
|
+
deliberately.
|
|
277
|
+
|
|
278
|
+
### What CSS editing cannot reach, and why that is worth showing
|
|
279
|
+
|
|
280
|
+
* **A value the app set on the element itself.** `inlineProps` is the cascade's
|
|
281
|
+
own record of what the authoring layer wrote directly, and an inline value
|
|
282
|
+
outranks every rule. The panel strikes those declarations through and marks
|
|
283
|
+
them as beaten by inline rather than by another rule — so "why did my edit do
|
|
284
|
+
nothing" is answered on the spot instead of being a mystery.
|
|
285
|
+
* **`position` in a rule.** `EVGElement.setAttribute` has no branch for it, so
|
|
286
|
+
the cascade never writes the field; an element is out of flow because it has
|
|
287
|
+
offsets, an overlay flag, or a `<Layer>` tag. The panel therefore reports the
|
|
288
|
+
position the **layout used**, not the field, and gate 6 is what would catch a
|
|
289
|
+
rule that says `position: absolute` with no offsets — the engine ignores it
|
|
290
|
+
silently today.
|
|
291
|
+
* **Geometry that came from data.** A PPTX slide's shapes are placed by the
|
|
292
|
+
file, not by a sheet. CSS editing serves that app's chrome; the slide is a
|
|
293
|
+
different question and the panel does not pretend otherwise.
|
|
294
|
+
|
|
295
|
+
## Holding a state on
|
|
296
|
+
|
|
297
|
+

|
|
298
|
+
|
|
299
|
+
A `:hover` rule cannot be read while the only way to make it true is to keep
|
|
300
|
+
the pointer somewhere other than this panel. So the four interaction bits can
|
|
301
|
+
be **held on** for the selected node — the same thing Chrome's `:hov` toggles
|
|
302
|
+
do, and for the same reason.
|
|
303
|
+
|
|
304
|
+
Nothing was taught to the cascade. `EVGPseudo.holds` reads `isHovered`,
|
|
305
|
+
`isFocused`, `isPressed` and `a11yDisabled`, and a held bit is one of those
|
|
306
|
+
fields being true — so the rule that wins is the ordinary `.db-brand:hover`
|
|
307
|
+
rule, matching for the ordinary reason. The panel above shows exactly that: the
|
|
308
|
+
initial value struck under it, the base rule, and the hover rule on top.
|
|
309
|
+
|
|
310
|
+
The table is **keyed by node path, not by element**, and that is the whole
|
|
311
|
+
reason it works here. This page throws its element tree away on every input, so
|
|
312
|
+
a flag written onto an element would last one frame; a path is a fact about the
|
|
313
|
+
tree's shape and names whatever is in that position on the next pass too. It
|
|
314
|
+
lives in `EVGInspectForce`, a singleton, applied by
|
|
315
|
+
`EVGInspect.applyForced(root)` — which a host calls at one specific moment:
|
|
316
|
+
|
|
317
|
+
```ranger
|
|
318
|
+
this.markStates(r) ; what the app says is hovered
|
|
319
|
+
EVGInspect.applyForced(r) ; plus what the panel is holding
|
|
320
|
+
s.applyTree(r "") ; then ask the sheet what that makes true
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
That ordering is the only thing a host has to get right, and the cost when
|
|
324
|
+
nothing is held is one array length.
|
|
325
|
+
|
|
326
|
+
On, not off. Forcing a state *off* would be arguing with the app about what is
|
|
327
|
+
happening rather than holding still something that already is, and the browser
|
|
328
|
+
does not offer it either.
|
|
329
|
+
|
|
330
|
+
## A tree drawn inside a larger surface
|
|
331
|
+
|
|
332
|
+
The dashboard's tree *is* the page: the boxes are in the surface's own
|
|
333
|
+
coordinates and the transform is the identity. A slide is not — the editor
|
|
334
|
+
draws it fitted and centred inside a window with a toolbar, a thumbnail strip
|
|
335
|
+
and a properties panel around it.
|
|
336
|
+
|
|
337
|
+
So the adapter can hand the panel a `transform` (`{x, y, k}`, where the tree's
|
|
338
|
+
coordinates land on the surface and how big they are drawn) and a `viewport`
|
|
339
|
+
(how big the surface is in its own units). The panel applies both to the
|
|
340
|
+
overlay and inverts both for the picker, so an adapter never has to know where
|
|
341
|
+
on the page its picture ended up. `PptxWeb.inspectTransform` derives `k` from
|
|
342
|
+
the two widths rather than assembling it from `ptScale` and the converter's
|
|
343
|
+
scale factor: one number instead of two that have to agree.
|
|
344
|
+
|
|
345
|
+
## The overlay is DOM, not draw commands
|
|
346
|
+
|
|
347
|
+
The four rings are absolutely positioned `<div>`s over the surface, in the four
|
|
348
|
+
colours a browser uses. They are **not** pushed into the display list, and that
|
|
349
|
+
is deliberate twice over: a screenshot taken while the panel is open is still a
|
|
350
|
+
screenshot of the app, and one implementation then serves both painters,
|
|
351
|
+
because the WebGL canvas and the SVG one are the same rectangle on the page.
|
|
352
|
+
|
|
353
|
+
## What is gated
|
|
354
|
+
|
|
355
|
+
`npm run evg:inspect:test` runs five differences against a real app in Node —
|
|
356
|
+
a second description of something is only worth having if it is differenced
|
|
357
|
+
against the first, which is the habit the painters already keep:
|
|
358
|
+
|
|
359
|
+
| | |
|
|
360
|
+
| --- | --- |
|
|
361
|
+
| the tree is a tree | one root, every parent present, no path twice |
|
|
362
|
+
| the box model closes | content + padding + borders is the border box, on all 470 nodes |
|
|
363
|
+
| attribution lands inside | a command is inside the box of the element that emitted it — with two exceptions that are real and asserted as such: a text run may overhang by the font's side bearings, a shadow by its blur |
|
|
364
|
+
| the hit test agrees | when it answers, the answer contains the point |
|
|
365
|
+
| off costs nothing | a list built without attribution carries none |
|
|
366
|
+
| the cascade agrees | the winning rule's value is the value on the element, over 2000 properties |
|
|
367
|
+
| a reload lands | a rule added to the sheet reaches a **retained** tree, and the frame |
|
|
368
|
+
| a reload does not double | applying the same text twice leaves the plan the same size |
|
|
369
|
+
| the class counts are the tree's | what the panel says `.db-card` reaches is what the tree has |
|
|
370
|
+
| a held state is an ordinary state | the real `:hover` rule wins, the value follows, it survives a rebuild, and letting go puts it back |
|
|
371
|
+
|
|
372
|
+
The hit-test gate is deliberately one-sided, and the first version of it was
|
|
373
|
+
wrong in a way worth recording: it expected every node to be reachable. The
|
|
374
|
+
dashboard's table is 552px tall inside a 414px scroll box, so half its rows sit
|
|
375
|
+
at coordinates the page does not show, and `EVGHitTest` drops a clipping box's
|
|
376
|
+
whole subtree when the point is outside it — its header says why, and it is
|
|
377
|
+
right: a row scrolled out of view that is still clickable is the worst of both.
|
|
378
|
+
"Nothing is there" is the correct answer, so the gate asserts the other half —
|
|
379
|
+
nothing may be named under a pointer that is not over it — plus that most of
|
|
380
|
+
the page does answer, so it cannot pass by never being asked.
|
|
381
|
+
|
|
382
|
+
`npm run evg:inspect:web` gates the part that only exists in a page: that
|
|
383
|
+
`?inspect=1` attaches, that the tree it read has rows, and that the panes
|
|
384
|
+
filled. It runs against the SVG editor because that needs no GPU.
|
|
385
|
+
|
|
386
|
+
`npm run evg:inspect:live` gates the part that only exists across processes:
|
|
387
|
+
the real dev server, a real save, the real page and the real WebGL painter, in
|
|
388
|
+
both directions — a rule written to the file reaching the painter, and the
|
|
389
|
+
panel's "save to disk" reaching the file. It also checks that the watch fires
|
|
390
|
+
**once** per save: `fs.watch` reports the truncate and the write separately,
|
|
391
|
+
and a sheet reloaded three times per keystroke would relayout three times.
|
|
392
|
+
|
|
393
|
+
## What is not built yet
|
|
394
|
+
|
|
395
|
+
[`../PLAN_INSPECTOR.md`](../PLAN_INSPECTOR.md) is the whole design. Built:
|
|
396
|
+
the walk and the panel, attribution, the cascade view, and CSS as a live input.
|
|
397
|
+
Not here yet, in the order they are worth doing:
|
|
398
|
+
|
|
399
|
+
* **editing a rule in place** rather than appending to the sheet. The
|
|
400
|
+
provenance names the rule and the panel prints its selector; what is missing
|
|
401
|
+
is finding that block in the text and writing back into it. Until then an
|
|
402
|
+
edit is appended, which always wins and is easy to strip, but does not teach
|
|
403
|
+
you where the value really lives.
|
|
404
|
+
* **a second host with a live sheet.** Only the dashboard is wired to
|
|
405
|
+
`css()`/`setCss()` today. The other seventeen demos each have one, and the
|
|
406
|
+
PPTX editor takes chrome CSS through `setChromeCss` — the wiring is four
|
|
407
|
+
methods and a line in the demo registry.
|
|
408
|
+
* **the transports the async adapter now allows** — a DevTools extension over
|
|
409
|
+
`inspectedWindow.eval`, a native target over a socket, a paused page
|
|
410
|
+
answering out of a snapshot. The panel no longer assumes any of them answer
|
|
411
|
+
on the same tick, which was the part that had to be decided early.
|
|
412
|
+
* **component debug notes** — a component saying *why*, which no channel
|
|
413
|
+
carries today.
|
|
414
|
+
* **the offline bundle** — one frame in a file, openable with no app running,
|
|
415
|
+
and attachable to a failing CI gate.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
//
|
|
4
|
+
// The panel, in a real browser, over the PPTX slide editor.
|
|
5
|
+
//
|
|
6
|
+
// npm run evg:inspect:web
|
|
7
|
+
//
|
|
8
|
+
// `inspect-check.mjs` gates the four channels in Node, where the app is an
|
|
9
|
+
// object. This gates the half that only exists in a page: that `?inspect=1`
|
|
10
|
+
// actually attaches something, that the tree it read has rows in it, and that
|
|
11
|
+
// selecting a node fills the panes. The wiring between a host and the panel is
|
|
12
|
+
// three functions long and therefore exactly the kind of thing that breaks
|
|
13
|
+
// without any test noticing.
|
|
14
|
+
//
|
|
15
|
+
// The SVG editor is the target because it needs no GPU: `--dump-dom` in
|
|
16
|
+
// headless Chromium is enough, and the page draws the same picture the WebGL
|
|
17
|
+
// one does.
|
|
18
|
+
|
|
19
|
+
import fs from "node:fs";
|
|
20
|
+
import http from "node:http";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { spawn } from "node:child_process";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
|
|
25
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const ROOT = path.resolve(HERE, "..", "..", "..");
|
|
27
|
+
const DIST = path.join(ROOT, "gallery/pptx/web/html/dist");
|
|
28
|
+
const PORT = 8117;
|
|
29
|
+
const MIME = {
|
|
30
|
+
".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8",
|
|
31
|
+
".mjs": "text/javascript; charset=utf-8", ".css": "text/css", ".json": "application/json",
|
|
32
|
+
".ttf": "font/ttf", ".txt": "text/plain; charset=utf-8", ".svg": "image/svg+xml",
|
|
33
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
34
|
+
".odp": "application/vnd.oasis.opendocument.presentation", ".png": "image/png",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function findChrome() {
|
|
38
|
+
const named = [process.env.CHROME_PATH, "/usr/bin/chromium", "/usr/bin/chromium-browser",
|
|
39
|
+
"/usr/bin/google-chrome", "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"].filter(Boolean);
|
|
40
|
+
for (const c of named) { try { if (fs.existsSync(c)) return c; } catch { /* keep looking */ } }
|
|
41
|
+
const pw = process.env.PLAYWRIGHT_BROWSERS_PATH || "/opt/pw-browsers";
|
|
42
|
+
try {
|
|
43
|
+
for (const dir of fs.readdirSync(pw)) {
|
|
44
|
+
const c = path.join(pw, dir, "chrome-linux", "chrome");
|
|
45
|
+
if (fs.existsSync(c)) return c;
|
|
46
|
+
}
|
|
47
|
+
} catch { /* none */ }
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function serve() {
|
|
52
|
+
const server = http.createServer((req, res) => {
|
|
53
|
+
const rel = decodeURIComponent((req.url || "/").split("?")[0]);
|
|
54
|
+
const file = path.join(DIST, rel === "/" ? "index.html" : rel);
|
|
55
|
+
if (!file.startsWith(DIST) || !fs.existsSync(file)) { res.writeHead(404); res.end("no"); return; }
|
|
56
|
+
res.writeHead(200, { "content-type": MIME[path.extname(file)] || "application/octet-stream", "cache-control": "no-store" });
|
|
57
|
+
res.end(fs.readFileSync(file));
|
|
58
|
+
});
|
|
59
|
+
return new Promise((r) => server.listen(PORT, "127.0.0.1", () => r(server)));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function runChrome(bin, args) {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
const child = spawn(bin, args, {
|
|
65
|
+
env: { ...process.env, HTTP_PROXY: "", HTTPS_PROXY: "", http_proxy: "", https_proxy: "", NO_PROXY: "*", no_proxy: "*" },
|
|
66
|
+
});
|
|
67
|
+
let out = "", err = "";
|
|
68
|
+
const kill = setTimeout(() => child.kill("SIGKILL"), 120000);
|
|
69
|
+
child.stdout.on("data", (d) => (out += d));
|
|
70
|
+
child.stderr.on("data", (d) => (err += d));
|
|
71
|
+
child.on("close", (status) => { clearTimeout(kill); resolve({ stdout: out, stderr: err, status }); });
|
|
72
|
+
child.on("error", (error) => { clearTimeout(kill); resolve({ stdout: out, stderr: err, status: -1, error }); });
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!fs.existsSync(path.join(DIST, "pptx_web.js"))) {
|
|
77
|
+
console.error("no build in " + path.relative(ROOT, DIST) + " — run: npm run pptx:html");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
const chrome = findChrome();
|
|
81
|
+
if (!chrome) { console.error("no Chrome found — set CHROME_PATH to one"); process.exit(1); }
|
|
82
|
+
|
|
83
|
+
const server = await serve();
|
|
84
|
+
let failed = 0;
|
|
85
|
+
const ok = (name, cond, detail) => {
|
|
86
|
+
if (cond) console.log(" ok " + name);
|
|
87
|
+
else { failed++; console.log(" FAIL " + name + (detail ? " — " + detail : "")); }
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// The panel opens on the root, so a dumped DOM already carries a selection
|
|
92
|
+
// and a filled pane — no script has to be injected to make it interesting.
|
|
93
|
+
const url = `http://127.0.0.1:${PORT}/index.html?inspect=1`;
|
|
94
|
+
const run = await runChrome(chrome, ["--headless=new", "--no-sandbox", "--no-proxy-server",
|
|
95
|
+
"--proxy-bypass-list=<-loopback>", "--disable-dev-shm-usage",
|
|
96
|
+
"--virtual-time-budget=25000", "--dump-dom", url]);
|
|
97
|
+
const dom = run.stdout || "";
|
|
98
|
+
if (process.env.SMOKE_DEBUG) fs.writeFileSync("/tmp/evg-inspect-dom.html", dom);
|
|
99
|
+
|
|
100
|
+
ok("the panel attached", /class="evgi"/.test(dom));
|
|
101
|
+
const rows = (dom.match(/class="evgi-row/g) || []).length;
|
|
102
|
+
ok("the tree has rows", rows >= 3, rows + " rows");
|
|
103
|
+
const foot = (dom.match(/class="evgi-foot"[^>]*>([^<]*)/) || [])[1] || "";
|
|
104
|
+
ok("the footer counted nodes and commands", /\d+ nodes/.test(foot) && /\d+ commands/.test(foot), JSON.stringify(foot));
|
|
105
|
+
ok("the box-model diagram is there", /evgi-bm/.test(dom));
|
|
106
|
+
ok("the overlay is mounted over the picture", /class="evgi-ov"/.test(dom));
|
|
107
|
+
ok("a node is selected", /evgi-row sel/.test(dom));
|
|
108
|
+
ok("the panes filled", /evgi-kv/.test(dom));
|
|
109
|
+
if (failed && process.env.SMOKE_DEBUG !== "1") console.log("\n re-run with SMOKE_DEBUG=1 to dump the DOM to /tmp");
|
|
110
|
+
} finally {
|
|
111
|
+
server.close();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log(failed ? `\n${failed} failed\n` : "\nthe inspector attached to the slide editor and read its tree\n");
|
|
115
|
+
process.exit(failed ? 1 : 0);
|