@react-x11/components 0.8.0 → 0.10.0
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/README.md +105 -38
- package/dist/charts/node.d.ts +12 -2
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +50 -16
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +12 -3
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +94 -25
- package/dist/charts/render.js.map +1 -1
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/index.js +3 -1
- package/dist/code/index.js.map +1 -1
- package/dist/code-editor/completion.d.ts.map +1 -1
- package/dist/code-editor/completion.js +3 -2
- package/dist/code-editor/completion.js.map +1 -1
- package/dist/code-editor/index.d.ts.map +1 -1
- package/dist/code-editor/index.js +19 -0
- package/dist/code-editor/index.js.map +1 -1
- package/dist/code-editor/node.d.ts +177 -7
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +1329 -164
- package/dist/code-editor/node.js.map +1 -1
- package/dist/code-language/languages/glsl.d.ts.map +1 -1
- package/dist/code-language/languages/glsl.js +10 -2
- package/dist/code-language/languages/glsl.js.map +1 -1
- package/dist/code-language/languages/javascript.d.ts.map +1 -1
- package/dist/code-language/languages/javascript.js +18 -3
- package/dist/code-language/languages/javascript.js.map +1 -1
- package/dist/code-language/languages/json.d.ts.map +1 -1
- package/dist/code-language/languages/json.js +10 -2
- package/dist/code-language/languages/json.js.map +1 -1
- package/dist/code-language/languages/shell.d.ts.map +1 -1
- package/dist/code-language/languages/shell.js +10 -2
- package/dist/code-language/languages/shell.js.map +1 -1
- package/dist/code-language/languages/sql.d.ts.map +1 -1
- package/dist/code-language/languages/sql.js +20 -2
- package/dist/code-language/languages/sql.js.map +1 -1
- package/dist/code-language/stream.d.ts +22 -0
- package/dist/code-language/stream.d.ts.map +1 -1
- package/dist/code-language/stream.js +259 -9
- package/dist/code-language/stream.js.map +1 -1
- package/dist/code-language/types.d.ts +5 -4
- package/dist/code-language/types.d.ts.map +1 -1
- package/dist/codeblock/index.d.ts +13 -2
- package/dist/codeblock/index.d.ts.map +1 -1
- package/dist/codeblock/index.js +20 -3
- package/dist/codeblock/index.js.map +1 -1
- package/dist/embed/client.d.ts +48 -3
- package/dist/embed/client.d.ts.map +1 -1
- package/dist/embed/client.js +79 -5
- package/dist/embed/client.js.map +1 -1
- package/dist/embed/index.d.ts +1 -1
- package/dist/embed/index.d.ts.map +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/embed/index.js.map +1 -1
- package/dist/flow/draw.d.ts +50 -0
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +132 -33
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/gl/color.d.ts +13 -0
- package/dist/flow/gl/color.d.ts.map +1 -0
- package/dist/flow/gl/color.js +82 -0
- package/dist/flow/gl/color.js.map +1 -0
- package/dist/flow/gl/index.d.ts +60 -0
- package/dist/flow/gl/index.d.ts.map +1 -0
- package/dist/flow/gl/index.js +283 -0
- package/dist/flow/gl/index.js.map +1 -0
- package/dist/flow/gl/pack.d.ts +133 -0
- package/dist/flow/gl/pack.d.ts.map +1 -0
- package/dist/flow/gl/pack.js +408 -0
- package/dist/flow/gl/pack.js.map +1 -0
- package/dist/flow/gl/renderer.d.ts +95 -0
- package/dist/flow/gl/renderer.d.ts.map +1 -0
- package/dist/flow/gl/renderer.js +459 -0
- package/dist/flow/gl/renderer.js.map +1 -0
- package/dist/flow/gl/shaders.d.ts +79 -0
- package/dist/flow/gl/shaders.d.ts.map +1 -0
- package/dist/flow/gl/shaders.js +346 -0
- package/dist/flow/gl/shaders.js.map +1 -0
- package/dist/flow/gl/text.d.ts +318 -0
- package/dist/flow/gl/text.d.ts.map +1 -0
- package/dist/flow/gl/text.js +1103 -0
- package/dist/flow/gl/text.js.map +1 -0
- package/dist/flow/index.d.ts +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +619 -64
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/node.d.ts +621 -72
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +2253 -1018
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/paint.d.ts +35 -0
- package/dist/flow/paint.d.ts.map +1 -0
- package/dist/flow/paint.js +393 -0
- package/dist/flow/paint.js.map +1 -0
- package/dist/flow/paths.d.ts +14 -0
- package/dist/flow/paths.d.ts.map +1 -1
- package/dist/flow/paths.js +45 -19
- package/dist/flow/paths.js.map +1 -1
- package/dist/flow/scene.d.ts +584 -0
- package/dist/flow/scene.d.ts.map +1 -0
- package/dist/flow/scene.js +1453 -0
- package/dist/flow/scene.js.map +1 -0
- package/dist/flow/types.d.ts +111 -1
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/html/controls.d.ts +28 -0
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +52 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/calc.d.ts +14 -0
- package/dist/html/css/calc.d.ts.map +1 -0
- package/dist/html/css/calc.js +344 -0
- package/dist/html/css/calc.js.map +1 -0
- package/dist/html/css/cascade.d.ts +240 -6
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +1327 -66
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +23 -0
- package/dist/html/css/color.d.ts.map +1 -0
- package/dist/html/css/color.js +740 -0
- package/dist/html/css/color.js.map +1 -0
- package/dist/html/css/content.d.ts +92 -0
- package/dist/html/css/content.d.ts.map +1 -0
- package/dist/html/css/content.js +388 -0
- package/dist/html/css/content.js.map +1 -0
- package/dist/html/css/counter-styles.d.ts +76 -0
- package/dist/html/css/counter-styles.d.ts.map +1 -0
- package/dist/html/css/counter-styles.js +893 -0
- package/dist/html/css/counter-styles.js.map +1 -0
- package/dist/html/css/decode.d.ts +12 -0
- package/dist/html/css/decode.d.ts.map +1 -0
- package/dist/html/css/decode.js +116 -0
- package/dist/html/css/decode.js.map +1 -0
- package/dist/html/css/parse.d.ts +110 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +1473 -180
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +498 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3698 -206
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +57 -11
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +37 -3
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +122 -48
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +52 -0
- package/dist/html/css/vars.d.ts.map +1 -0
- package/dist/html/css/vars.js +358 -0
- package/dist/html/css/vars.js.map +1 -0
- package/dist/html/dom.d.ts +30 -4
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +278 -17
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +106 -0
- package/dist/html/fonts.d.ts.map +1 -0
- package/dist/html/fonts.js +565 -0
- package/dist/html/fonts.js.map +1 -0
- package/dist/html/gif.d.ts +15 -0
- package/dist/html/gif.d.ts.map +1 -0
- package/dist/html/gif.js +229 -0
- package/dist/html/gif.js.map +1 -0
- package/dist/html/index.d.ts +50 -11
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +115 -9
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +226 -3
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +2937 -197
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +340 -17
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +1714 -182
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +58 -0
- package/dist/html/layout/cache.d.ts.map +1 -0
- package/dist/html/layout/cache.js +224 -0
- package/dist/html/layout/cache.js.map +1 -0
- package/dist/html/layout/collapse.d.ts +45 -0
- package/dist/html/layout/collapse.d.ts.map +1 -0
- package/dist/html/layout/collapse.js +246 -0
- package/dist/html/layout/collapse.js.map +1 -0
- package/dist/html/layout/css-grid.d.ts +7 -0
- package/dist/html/layout/css-grid.d.ts.map +1 -0
- package/dist/html/layout/css-grid.js +900 -0
- package/dist/html/layout/css-grid.js.map +1 -0
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +1025 -89
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +45 -6
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +113 -14
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/grid-lines.d.ts +39 -0
- package/dist/html/layout/grid-lines.d.ts.map +1 -0
- package/dist/html/layout/grid-lines.js +119 -0
- package/dist/html/layout/grid-lines.js.map +1 -0
- package/dist/html/layout/grid.d.ts +53 -0
- package/dist/html/layout/grid.d.ts.map +1 -0
- package/dist/html/layout/grid.js +153 -0
- package/dist/html/layout/grid.js.map +1 -0
- package/dist/html/layout/inline.d.ts +151 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +3375 -174
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/shaping.d.ts +6 -0
- package/dist/html/layout/shaping.d.ts.map +1 -0
- package/dist/html/layout/shaping.js +100 -0
- package/dist/html/layout/shaping.js.map +1 -0
- package/dist/html/layout/table.d.ts +3 -0
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +681 -175
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/layout/tracks.d.ts +44 -0
- package/dist/html/layout/tracks.d.ts.map +1 -0
- package/dist/html/layout/tracks.js +349 -0
- package/dist/html/layout/tracks.js.map +1 -0
- package/dist/html/node.d.ts +172 -5
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +1336 -103
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +92 -3
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +3503 -141
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.d.ts +60 -14
- package/dist/html/resources.d.ts.map +1 -1
- package/dist/html/resources.js +122 -36
- package/dist/html/resources.js.map +1 -1
- package/dist/html/surfaces.d.ts +23 -0
- package/dist/html/surfaces.d.ts.map +1 -0
- package/dist/html/surfaces.js +68 -0
- package/dist/html/surfaces.js.map +1 -0
- package/dist/html/svg.d.ts +80 -0
- package/dist/html/svg.d.ts.map +1 -0
- package/dist/html/svg.js +435 -0
- package/dist/html/svg.js.map +1 -0
- package/dist/html/url.d.ts +23 -0
- package/dist/html/url.d.ts.map +1 -0
- package/dist/html/url.js +80 -0
- package/dist/html/url.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/collate.d.ts +3 -0
- package/dist/internal/collate.d.ts.map +1 -0
- package/dist/internal/collate.js +11 -0
- package/dist/internal/collate.js.map +1 -0
- package/dist/{markdown → internal/markdown}/ast.d.ts +23 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -0
- package/dist/internal/markdown/ast.js +22 -0
- package/dist/internal/markdown/ast.js.map +1 -0
- package/dist/internal/markdown/parse.d.ts.map +1 -0
- package/dist/{markdown → internal/markdown}/parse.js +221 -30
- package/dist/internal/markdown/parse.js.map +1 -0
- package/dist/internal/markdown/stringify.d.ts +4 -0
- package/dist/internal/markdown/stringify.d.ts.map +1 -0
- package/dist/internal/markdown/stringify.js +684 -0
- package/dist/internal/markdown/stringify.js.map +1 -0
- package/dist/internal/markdown/tags.d.ts.map +1 -0
- package/dist/internal/markdown/tags.js.map +1 -0
- package/dist/internal/prewarm.d.ts +18 -0
- package/dist/internal/prewarm.d.ts.map +1 -0
- package/dist/internal/prewarm.js +42 -0
- package/dist/internal/prewarm.js.map +1 -0
- package/dist/internal/scroll.d.ts +3 -2
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +1 -0
- package/dist/internal/scroll.js.map +1 -1
- package/dist/internal/sdf.d.ts +20 -0
- package/dist/internal/sdf.d.ts.map +1 -0
- package/dist/internal/sdf.js +216 -0
- package/dist/internal/sdf.js.map +1 -0
- package/dist/internal/splice.d.ts +12 -0
- package/dist/internal/splice.d.ts.map +1 -0
- package/dist/internal/splice.js +23 -0
- package/dist/internal/splice.js.map +1 -0
- package/dist/internal/window.d.ts +12 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +23 -6
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +103 -0
- package/dist/maps/anchors.d.ts.map +1 -0
- package/dist/maps/anchors.js +790 -0
- package/dist/maps/anchors.js.map +1 -0
- package/dist/maps/controller.d.ts +340 -0
- package/dist/maps/controller.d.ts.map +1 -0
- package/dist/maps/controller.js +822 -0
- package/dist/maps/controller.js.map +1 -0
- package/dist/maps/gl/buckets.d.ts +99 -0
- package/dist/maps/gl/buckets.d.ts.map +1 -0
- package/dist/maps/gl/buckets.js +365 -0
- package/dist/maps/gl/buckets.js.map +1 -0
- package/dist/maps/gl/build-worker.d.ts +15 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -0
- package/dist/maps/gl/build-worker.js +44 -0
- package/dist/maps/gl/build-worker.js.map +1 -0
- package/dist/maps/gl/color.d.ts +8 -0
- package/dist/maps/gl/color.d.ts.map +1 -0
- package/dist/maps/gl/color.js +57 -0
- package/dist/maps/gl/color.js.map +1 -0
- package/dist/maps/gl/cover.d.ts +70 -0
- package/dist/maps/gl/cover.d.ts.map +1 -0
- package/dist/maps/gl/cover.js +137 -0
- package/dist/maps/gl/cover.js.map +1 -0
- package/dist/maps/gl/index.d.ts +3 -0
- package/dist/maps/gl/index.d.ts.map +1 -0
- package/dist/maps/gl/index.js +20 -0
- package/dist/maps/gl/index.js.map +1 -0
- package/dist/maps/gl/markers.d.ts +28 -0
- package/dist/maps/gl/markers.d.ts.map +1 -0
- package/dist/maps/gl/markers.js +60 -0
- package/dist/maps/gl/markers.js.map +1 -0
- package/dist/maps/gl/overlays.d.ts +68 -0
- package/dist/maps/gl/overlays.d.ts.map +1 -0
- package/dist/maps/gl/overlays.js +300 -0
- package/dist/maps/gl/overlays.js.map +1 -0
- package/dist/maps/gl/pane.d.ts +12 -0
- package/dist/maps/gl/pane.d.ts.map +1 -0
- package/dist/maps/gl/pane.js +37 -0
- package/dist/maps/gl/pane.js.map +1 -0
- package/dist/maps/gl/placement.d.ts +124 -0
- package/dist/maps/gl/placement.d.ts.map +1 -0
- package/dist/maps/gl/placement.js +642 -0
- package/dist/maps/gl/placement.js.map +1 -0
- package/dist/maps/gl/renderer.d.ts +384 -0
- package/dist/maps/gl/renderer.d.ts.map +1 -0
- package/dist/maps/gl/renderer.js +1184 -0
- package/dist/maps/gl/renderer.js.map +1 -0
- package/dist/maps/gl/shaders.d.ts +121 -0
- package/dist/maps/gl/shaders.d.ts.map +1 -0
- package/dist/maps/gl/shaders.js +453 -0
- package/dist/maps/gl/shaders.js.map +1 -0
- package/dist/maps/gl/store.d.ts +125 -0
- package/dist/maps/gl/store.d.ts.map +1 -0
- package/dist/maps/gl/store.js +512 -0
- package/dist/maps/gl/store.js.map +1 -0
- package/dist/maps/gl/text.d.ts +283 -0
- package/dist/maps/gl/text.d.ts.map +1 -0
- package/dist/maps/gl/text.js +698 -0
- package/dist/maps/gl/text.js.map +1 -0
- package/dist/maps/gl/view.d.ts +345 -0
- package/dist/maps/gl/view.d.ts.map +1 -0
- package/dist/maps/gl/view.js +1209 -0
- package/dist/maps/gl/view.js.map +1 -0
- package/dist/maps/icons.d.ts +32 -0
- package/dist/maps/icons.d.ts.map +1 -0
- package/dist/maps/icons.js +151 -0
- package/dist/maps/icons.js.map +1 -0
- package/dist/maps/index.d.ts +3 -1
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +218 -52
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +91 -18
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +376 -140
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +179 -56
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +663 -537
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/overlay.d.ts +72 -9
- package/dist/maps/overlay.d.ts.map +1 -1
- package/dist/maps/overlay.js +108 -28
- package/dist/maps/overlay.js.map +1 -1
- package/dist/maps/paint.d.ts +3 -0
- package/dist/maps/paint.d.ts.map +1 -1
- package/dist/maps/paint.js.map +1 -1
- package/dist/maps/proj.d.ts +62 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +101 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/renderer.d.ts +69 -0
- package/dist/maps/renderer.d.ts.map +1 -0
- package/dist/maps/renderer.js +66 -0
- package/dist/maps/renderer.js.map +1 -0
- package/dist/maps/sources.d.ts +30 -6
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +22 -3
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/style.d.ts +35 -0
- package/dist/maps/style.d.ts.map +1 -1
- package/dist/maps/style.js.map +1 -1
- package/dist/maps/styles.d.ts +12 -0
- package/dist/maps/styles.d.ts.map +1 -1
- package/dist/maps/styles.js +120 -6
- package/dist/maps/styles.js.map +1 -1
- package/dist/maps/theme.d.ts +19 -0
- package/dist/maps/theme.d.ts.map +1 -0
- package/dist/maps/theme.js +44 -0
- package/dist/maps/theme.js.map +1 -0
- package/dist/maps/tiles.d.ts +147 -27
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +221 -54
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +213 -21
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/expressions.d.ts +1 -1
- package/dist/markdown/expressions.d.ts.map +1 -1
- package/dist/markdown/expressions.js.map +1 -1
- package/dist/markdown/index.d.ts +7 -6
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +121 -50
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +6 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/media-player/index.d.ts +11 -3
- package/dist/media-player/index.d.ts.map +1 -1
- package/dist/media-player/index.js +13 -2
- package/dist/media-player/index.js.map +1 -1
- package/dist/qml/containers.d.ts.map +1 -1
- package/dist/qml/containers.js +6 -1
- package/dist/qml/containers.js.map +1 -1
- package/dist/qml/interaction.d.ts.map +1 -1
- package/dist/qml/interaction.js +38 -5
- package/dist/qml/interaction.js.map +1 -1
- package/dist/qml/layouts.d.ts.map +1 -1
- package/dist/qml/layouts.js +21 -6
- package/dist/qml/layouts.js.map +1 -1
- package/dist/qml/qtquick.d.ts.map +1 -1
- package/dist/qml/qtquick.js +9 -2
- package/dist/qml/qtquick.js.map +1 -1
- package/dist/qml/react.d.ts.map +1 -1
- package/dist/qml/react.js +9 -7
- package/dist/qml/react.js.map +1 -1
- package/dist/reorder/clock.d.ts +7 -0
- package/dist/reorder/clock.d.ts.map +1 -0
- package/dist/reorder/clock.js +26 -0
- package/dist/reorder/clock.js.map +1 -0
- package/dist/reorder/index.d.ts +25 -3
- package/dist/reorder/index.d.ts.map +1 -1
- package/dist/reorder/index.js +64 -19
- package/dist/reorder/index.js.map +1 -1
- package/dist/rich-text-editor/clipboard.d.ts +23 -0
- package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
- package/dist/rich-text-editor/clipboard.js +307 -0
- package/dist/rich-text-editor/clipboard.js.map +1 -0
- package/dist/rich-text-editor/collab.d.ts +32 -0
- package/dist/rich-text-editor/collab.d.ts.map +1 -0
- package/dist/rich-text-editor/collab.js +55 -0
- package/dist/rich-text-editor/collab.js.map +1 -0
- package/dist/rich-text-editor/commands.d.ts +50 -0
- package/dist/rich-text-editor/commands.d.ts.map +1 -0
- package/dist/rich-text-editor/commands.js +376 -0
- package/dist/rich-text-editor/commands.js.map +1 -0
- package/dist/rich-text-editor/drag.d.ts +68 -0
- package/dist/rich-text-editor/drag.d.ts.map +1 -0
- package/dist/rich-text-editor/drag.js +148 -0
- package/dist/rich-text-editor/drag.js.map +1 -0
- package/dist/rich-text-editor/html.d.ts +145 -0
- package/dist/rich-text-editor/html.d.ts.map +1 -0
- package/dist/rich-text-editor/html.js +516 -0
- package/dist/rich-text-editor/html.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +252 -0
- package/dist/rich-text-editor/index.d.ts.map +1 -0
- package/dist/rich-text-editor/index.js +856 -0
- package/dist/rich-text-editor/index.js.map +1 -0
- package/dist/rich-text-editor/inline.d.ts +94 -0
- package/dist/rich-text-editor/inline.d.ts.map +1 -0
- package/dist/rich-text-editor/inline.js +252 -0
- package/dist/rich-text-editor/inline.js.map +1 -0
- package/dist/rich-text-editor/keymap.d.ts +71 -0
- package/dist/rich-text-editor/keymap.d.ts.map +1 -0
- package/dist/rich-text-editor/keymap.js +394 -0
- package/dist/rich-text-editor/keymap.js.map +1 -0
- package/dist/rich-text-editor/keys.d.ts +40 -0
- package/dist/rich-text-editor/keys.d.ts.map +1 -0
- package/dist/rich-text-editor/keys.js +202 -0
- package/dist/rich-text-editor/keys.js.map +1 -0
- package/dist/rich-text-editor/look.d.ts +71 -0
- package/dist/rich-text-editor/look.d.ts.map +1 -0
- package/dist/rich-text-editor/look.js +342 -0
- package/dist/rich-text-editor/look.js.map +1 -0
- package/dist/rich-text-editor/markdown.d.ts +18 -0
- package/dist/rich-text-editor/markdown.d.ts.map +1 -0
- package/dist/rich-text-editor/markdown.js +496 -0
- package/dist/rich-text-editor/markdown.js.map +1 -0
- package/dist/rich-text-editor/marks.d.ts +9 -0
- package/dist/rich-text-editor/marks.d.ts.map +1 -0
- package/dist/rich-text-editor/marks.js +83 -0
- package/dist/rich-text-editor/marks.js.map +1 -0
- package/dist/rich-text-editor/nodes.d.ts +135 -0
- package/dist/rich-text-editor/nodes.d.ts.map +1 -0
- package/dist/rich-text-editor/nodes.js +397 -0
- package/dist/rich-text-editor/nodes.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts +68 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -0
- package/dist/rich-text-editor/render.js +518 -0
- package/dist/rich-text-editor/render.js.map +1 -0
- package/dist/rich-text-editor/schema.d.ts +16 -0
- package/dist/rich-text-editor/schema.d.ts.map +1 -0
- package/dist/rich-text-editor/schema.js +386 -0
- package/dist/rich-text-editor/schema.js.map +1 -0
- package/dist/rich-text-editor/store.d.ts +11 -0
- package/dist/rich-text-editor/store.d.ts.map +1 -0
- package/dist/rich-text-editor/store.js +43 -0
- package/dist/rich-text-editor/store.js.map +1 -0
- package/dist/rich-text-editor/suggest.d.ts +127 -0
- package/dist/rich-text-editor/suggest.d.ts.map +1 -0
- package/dist/rich-text-editor/suggest.js +0 -0
- package/dist/rich-text-editor/suggest.js.map +1 -0
- package/dist/rich-text-editor/tables.d.ts +41 -0
- package/dist/rich-text-editor/tables.d.ts.map +1 -0
- package/dist/rich-text-editor/tables.js +204 -0
- package/dist/rich-text-editor/tables.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts +58 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
- package/dist/rich-text-editor/toolbar.js +548 -0
- package/dist/rich-text-editor/toolbar.js.map +1 -0
- package/dist/rich-text-editor/view.d.ts +368 -0
- package/dist/rich-text-editor/view.d.ts.map +1 -0
- package/dist/rich-text-editor/view.js +1968 -0
- package/dist/rich-text-editor/view.js.map +1 -0
- package/dist/rich-text-editor/virtual.d.ts +54 -0
- package/dist/rich-text-editor/virtual.d.ts.map +1 -0
- package/dist/rich-text-editor/virtual.js +279 -0
- package/dist/rich-text-editor/virtual.js.map +1 -0
- package/dist/richtext/node.d.ts +71 -4
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +233 -28
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +13 -8
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +18 -8
- package/dist/richtext/runs.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +23 -0
- package/dist/table/index.js.map +1 -1
- package/dist/table/rows.d.ts +3 -2
- package/dist/table/rows.d.ts.map +1 -1
- package/dist/table/rows.js +20 -6
- package/dist/table/rows.js.map +1 -1
- package/dist/terminal/backends.d.ts +4 -1
- package/dist/terminal/backends.d.ts.map +1 -1
- package/dist/terminal/backends.js.map +1 -1
- package/dist/terminal/index.d.ts +18 -7
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +32 -11
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +13 -2
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +18 -3
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/terminal-output/index.d.ts.map +1 -1
- package/dist/terminal-output/index.js +6 -1
- package/dist/terminal-output/index.js.map +1 -1
- package/dist/three/canvas.d.ts.map +1 -1
- package/dist/three/canvas.js +85 -7
- package/dist/three/canvas.js.map +1 -1
- package/dist/three/events.d.ts +22 -4
- package/dist/three/events.d.ts.map +1 -1
- package/dist/three/events.js +34 -4
- package/dist/three/events.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +17 -0
- package/dist/tree/index.js.map +1 -1
- package/package.json +59 -31
- package/src/charts/node.ts +68 -28
- package/src/charts/render.ts +120 -30
- package/src/code/index.ts +3 -1
- package/src/code-editor/completion.ts +3 -2
- package/src/code-editor/index.ts +19 -0
- package/src/code-editor/node.ts +1574 -184
- package/src/code-language/languages/glsl.ts +12 -2
- package/src/code-language/languages/javascript.ts +21 -4
- package/src/code-language/languages/json.ts +12 -2
- package/src/code-language/languages/shell.ts +12 -2
- package/src/code-language/languages/sql.ts +22 -2
- package/src/code-language/stream.ts +257 -17
- package/src/code-language/types.ts +5 -4
- package/src/codeblock/index.ts +20 -3
- package/src/embed/client.ts +102 -8
- package/src/embed/index.ts +2 -0
- package/src/flow/draw.ts +167 -41
- package/src/flow/gl/color.ts +85 -0
- package/src/flow/gl/index.ts +342 -0
- package/src/flow/gl/pack.ts +585 -0
- package/src/flow/gl/renderer.ts +679 -0
- package/src/flow/gl/shaders.ts +371 -0
- package/src/flow/gl/text.ts +1295 -0
- package/src/flow/index.ts +794 -67
- package/src/flow/node.ts +2496 -1256
- package/src/flow/paint.ts +511 -0
- package/src/flow/paths.ts +80 -15
- package/src/flow/scene.ts +2199 -0
- package/src/flow/types.ts +113 -1
- package/src/html/controls.ts +73 -2
- package/src/html/css/calc.ts +385 -0
- package/src/html/css/cascade.ts +1403 -62
- package/src/html/css/color.ts +755 -0
- package/src/html/css/content.ts +417 -0
- package/src/html/css/counter-styles.ts +973 -0
- package/src/html/css/decode.ts +130 -0
- package/src/html/css/parse.ts +1514 -160
- package/src/html/css/style.ts +4102 -222
- package/src/html/css/ua.ts +58 -12
- package/src/html/css/values.ts +150 -50
- package/src/html/css/vars.ts +348 -0
- package/src/html/dom.ts +284 -17
- package/src/html/fonts.ts +641 -0
- package/src/html/gif.ts +233 -0
- package/src/html/index.ts +180 -20
- package/src/html/layout/block.ts +3456 -230
- package/src/html/layout/boxes.ts +2100 -190
- package/src/html/layout/cache.ts +289 -0
- package/src/html/layout/collapse.ts +317 -0
- package/src/html/layout/css-grid.ts +1063 -0
- package/src/html/layout/flex.ts +1115 -106
- package/src/html/layout/floats.ts +128 -14
- package/src/html/layout/grid-lines.ts +141 -0
- package/src/html/layout/grid.ts +181 -0
- package/src/html/layout/inline.ts +3943 -192
- package/src/html/layout/shaping.ts +140 -0
- package/src/html/layout/table.ts +696 -186
- package/src/html/layout/tracks.ts +437 -0
- package/src/html/node.ts +1486 -121
- package/src/html/paint.ts +4293 -169
- package/src/html/resources.ts +161 -50
- package/src/html/surfaces.ts +81 -0
- package/src/html/svg.ts +493 -0
- package/src/html/url.ts +86 -0
- package/src/index.ts +4 -0
- package/src/internal/collate.ts +12 -0
- package/src/{markdown → internal/markdown}/ast.ts +35 -7
- package/src/{markdown → internal/markdown}/parse.ts +288 -35
- package/src/internal/markdown/stringify.ts +772 -0
- package/src/internal/prewarm.ts +45 -0
- package/src/internal/scroll.ts +5 -3
- package/src/internal/sdf.ts +227 -0
- package/src/internal/splice.ts +25 -0
- package/src/internal/window.ts +36 -5
- package/src/maps/anchors.ts +913 -0
- package/src/maps/controller.ts +1084 -0
- package/src/maps/gl/buckets.ts +461 -0
- package/src/maps/gl/build-worker.ts +66 -0
- package/src/maps/gl/color.ts +62 -0
- package/src/maps/gl/cover.ts +205 -0
- package/src/maps/gl/index.ts +23 -0
- package/src/maps/gl/markers.ts +83 -0
- package/src/maps/gl/overlays.ts +379 -0
- package/src/maps/gl/pane.ts +42 -0
- package/src/maps/gl/placement.ts +968 -0
- package/src/maps/gl/renderer.ts +1660 -0
- package/src/maps/gl/shaders.ts +489 -0
- package/src/maps/gl/store.ts +615 -0
- package/src/maps/gl/text.ts +868 -0
- package/src/maps/gl/view.ts +1489 -0
- package/src/maps/icons.ts +183 -0
- package/src/maps/index.ts +274 -81
- package/src/maps/labels.ts +457 -144
- package/src/maps/node.ts +832 -665
- package/src/maps/overlay.ts +148 -43
- package/src/maps/paint.ts +3 -0
- package/src/maps/proj.ts +118 -0
- package/src/maps/renderer.ts +155 -0
- package/src/maps/sources.ts +43 -6
- package/src/maps/style.ts +32 -0
- package/src/maps/styles.ts +215 -4
- package/src/maps/theme.ts +51 -0
- package/src/maps/tiles.ts +253 -62
- package/src/maps/types.ts +213 -21
- package/src/markdown/expressions.ts +5 -1
- package/src/markdown/index.ts +163 -78
- package/src/markdown/spans.ts +6 -1
- package/src/media-player/index.ts +18 -4
- package/src/qml/containers.tsx +6 -1
- package/src/qml/interaction.tsx +42 -5
- package/src/qml/layouts.tsx +21 -6
- package/src/qml/qtquick.tsx +9 -2
- package/src/qml/react.tsx +7 -5
- package/src/reorder/clock.ts +27 -0
- package/src/reorder/index.ts +100 -27
- package/src/rich-text-editor/clipboard.ts +439 -0
- package/src/rich-text-editor/collab.ts +91 -0
- package/src/rich-text-editor/commands.ts +448 -0
- package/src/rich-text-editor/dom-globals.d.ts +23 -0
- package/src/rich-text-editor/drag.ts +211 -0
- package/src/rich-text-editor/html.ts +619 -0
- package/src/rich-text-editor/index.ts +1364 -0
- package/src/rich-text-editor/inline.ts +345 -0
- package/src/rich-text-editor/keymap.ts +510 -0
- package/src/rich-text-editor/keys.ts +228 -0
- package/src/rich-text-editor/look.ts +427 -0
- package/src/rich-text-editor/markdown.ts +653 -0
- package/src/rich-text-editor/marks.ts +101 -0
- package/src/rich-text-editor/nodes.ts +497 -0
- package/src/rich-text-editor/render.ts +837 -0
- package/src/rich-text-editor/schema.ts +431 -0
- package/src/rich-text-editor/store.ts +39 -0
- package/src/rich-text-editor/suggest.ts +597 -0
- package/src/rich-text-editor/tables.ts +272 -0
- package/src/rich-text-editor/toolbar.ts +724 -0
- package/src/rich-text-editor/view.ts +2287 -0
- package/src/rich-text-editor/virtual.ts +372 -0
- package/src/richtext/node.ts +294 -44
- package/src/richtext/runs.ts +23 -12
- package/src/table/index.ts +29 -0
- package/src/table/rows.ts +18 -8
- package/src/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +47 -15
- package/src/terminal/vt/pty.ts +16 -2
- package/src/terminal-output/index.ts +6 -1
- package/src/three/canvas.ts +102 -8
- package/src/three/events.ts +49 -9
- package/src/tree/index.ts +19 -0
- package/dist/markdown/ast.d.ts.map +0 -1
- package/dist/markdown/ast.js +0 -17
- package/dist/markdown/ast.js.map +0 -1
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js.map +0 -1
- package/dist/markdown/tags.d.ts.map +0 -1
- package/dist/markdown/tags.js.map +0 -1
- /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.js +0 -0
- /package/src/{markdown → internal/markdown}/tags.ts +0 -0
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
// Labels, the text half: strings set by the platform's own text engine,
|
|
2
|
+
// read back as coverage, and packed into one GL texture.
|
|
3
|
+
//
|
|
4
|
+
// A `<glarea>` is stacked over every 2D thing in its window, so a label on a
|
|
5
|
+
// GL map has to be *in* the GL frame — there is no drawing text over it
|
|
6
|
+
// afterwards. The glyphs come from where every other string in the app
|
|
7
|
+
// comes from: `app.fonts.layout()` shapes it (CoreText on the Cocoa
|
|
8
|
+
// backend, ntk's own shaper on X11, font fallback and all), and the layout
|
|
9
|
+
// draws into an offscreen {@link Surface} whose pixels are read back. That
|
|
10
|
+
// is one readback per *batch* of labels, not per label, and each label is
|
|
11
|
+
// set once and drawn every frame after.
|
|
12
|
+
//
|
|
13
|
+
// A label is rasterized **whole**, not glyph by glyph: placement sets every
|
|
14
|
+
// line label on a straight stretch, so a label is one rigid piece of type —
|
|
15
|
+
// one textured quad, rotated as a unit — and shaping, kerning and scripts
|
|
16
|
+
// stay the text engine's business.
|
|
17
|
+
//
|
|
18
|
+
// **One raster per string, at every size.** The engine sets a string once,
|
|
19
|
+
// at the atlas's `base` size, as coverage; what is stored is its signed
|
|
20
|
+
// distance field (`src/internal/sdf.ts`), one byte a texel. A name drawn at 11 pixels
|
|
21
|
+
// and the same name at 20 are one raster scaled, so a zoom ramp that grows
|
|
22
|
+
// the type asks for nothing new, and a halo of any width is a threshold on
|
|
23
|
+
// the same distances. Colour and halo are the shader's (see `./shaders.ts`),
|
|
24
|
+
// so one raster serves every palette and a light-to-dark switch
|
|
25
|
+
// re-rasterizes nothing. What it costs is the engine's hinting — the text
|
|
26
|
+
// is its outline, scaled — which at map-label sizes is the right trade:
|
|
27
|
+
// one shaping and one readback per name for the life of the atlas.
|
|
28
|
+
//
|
|
29
|
+
// An icon's two pieces (`../icons.ts`) are fields here too, drawn as
|
|
30
|
+
// coverage from the same paths the retained renderer fills, at the same
|
|
31
|
+
// base size, and coloured by the same shader.
|
|
32
|
+
import { Surface } from 'react-x11/ntk';
|
|
33
|
+
|
|
34
|
+
import { traceGlyph, tracePlate } from '../icons.js';
|
|
35
|
+
import type { IconPathContext, MapIcon } from '../icons.js';
|
|
36
|
+
import { SDF_EDGE, distanceField } from '../../internal/sdf.js';
|
|
37
|
+
|
|
38
|
+
/** A set string's box, in device pixels. */
|
|
39
|
+
export interface TextBox {
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Which of an icon's two single-colour rasters. */
|
|
45
|
+
export type IconPart = 'plate' | 'glyph';
|
|
46
|
+
|
|
47
|
+
/** One string to set, at `size` device pixels — or, with `icon`, one piece
|
|
48
|
+
* of an icon `size` pixels across, and `text` empty. */
|
|
49
|
+
export interface TextItem {
|
|
50
|
+
text: string;
|
|
51
|
+
size: number;
|
|
52
|
+
icon?: { name: MapIcon; part: IconPart };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** A rasterized string, `pad` pixels of clear margin included: RGBA as
|
|
56
|
+
* read back, coverage in alpha — or, with `stride` 1, the text engine's own
|
|
57
|
+
* coverage, one byte a pixel. */
|
|
58
|
+
export interface TextRaster {
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
pixels: Uint8Array;
|
|
62
|
+
/** Bytes a pixel: 4 (the default) for a readback, 1 for coverage. */
|
|
63
|
+
stride?: 1 | 4;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** What the atlas asks of a text engine. */
|
|
67
|
+
export interface TextEngine {
|
|
68
|
+
/** The box `text` sets in, synchronously — placement needs it now. */
|
|
69
|
+
measure(text: string, size: number): TextBox;
|
|
70
|
+
/**
|
|
71
|
+
* Rasterize as many of `items` as fit one batch. Answers per item: a
|
|
72
|
+
* raster; `null` for one that can never be drawn (wider than the engine
|
|
73
|
+
* can set); `undefined` for one left for the next batch.
|
|
74
|
+
*/
|
|
75
|
+
rasterize(
|
|
76
|
+
items: readonly TextItem[],
|
|
77
|
+
pad: number,
|
|
78
|
+
): Promise<(TextRaster | null | undefined)[]>;
|
|
79
|
+
/**
|
|
80
|
+
* One item set now, synchronously, where the engine can: a raster;
|
|
81
|
+
* `null` for one that can never be drawn; `undefined` for one it can only
|
|
82
|
+
* set through {@link rasterize} (an icon's paths, or an engine whose
|
|
83
|
+
* layouts answer no coverage of their own). Optional.
|
|
84
|
+
*/
|
|
85
|
+
rasterizeNow?(item: TextItem, pad: number): TextRaster | null | undefined;
|
|
86
|
+
dispose(): void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** ntk's font cache, structurally — `../labels.ts`' `FontsLike`. */
|
|
90
|
+
interface FontsLike {
|
|
91
|
+
layout(
|
|
92
|
+
content: string,
|
|
93
|
+
style: Record<string, unknown>,
|
|
94
|
+
): {
|
|
95
|
+
width: number;
|
|
96
|
+
height: number;
|
|
97
|
+
draw(ctx: unknown, x: number, y: number): void;
|
|
98
|
+
/** The engine's own coverage of the layout, one byte a pixel, with
|
|
99
|
+
* `pad` round its box — where the engine answers it
|
|
100
|
+
* (sidorares/react-x11#673). */
|
|
101
|
+
coverage?(options?: {
|
|
102
|
+
pad?: number;
|
|
103
|
+
}): { width: number; height: number; data: Uint8Array } | null;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface ImageLike {
|
|
108
|
+
data: Uint8ClampedArray;
|
|
109
|
+
width: number;
|
|
110
|
+
height: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
interface ContextLike extends Partial<IconPathContext> {
|
|
114
|
+
getImageData(x: number, y: number, w: number, h: number): Promise<ImageLike>;
|
|
115
|
+
destroy?(): void;
|
|
116
|
+
fillStyle?: unknown;
|
|
117
|
+
fill?(): void;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Whether a context can fill a path — what an icon is drawn with. */
|
|
121
|
+
function fills(
|
|
122
|
+
ctx: ContextLike,
|
|
123
|
+
): ctx is ContextLike & IconPathContext & { fill(): void } {
|
|
124
|
+
return (
|
|
125
|
+
typeof ctx.beginPath === 'function' &&
|
|
126
|
+
typeof ctx.arc === 'function' &&
|
|
127
|
+
typeof ctx.fill === 'function'
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface SurfaceLike {
|
|
132
|
+
getContext(name: '2d'): ContextLike;
|
|
133
|
+
clear(): void;
|
|
134
|
+
destroy(): void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The offscreen surface a batch is set on, in device pixels. A readback
|
|
138
|
+
* has to stay inside it — X answers a GetImage past a pixmap's edge with
|
|
139
|
+
* BadMatch — so a batch is whatever fits. */
|
|
140
|
+
const STAGING_WIDTH = 1024;
|
|
141
|
+
const STAGING_HEIGHT = 512;
|
|
142
|
+
/** Milliseconds of drawing one batch may take. A batch runs between two
|
|
143
|
+
* frames, and at 75 Hz the gap between them is a few milliseconds. */
|
|
144
|
+
const DRAW_BUDGET_MS = 1.5;
|
|
145
|
+
|
|
146
|
+
/** The text engine over an app's fonts and an offscreen surface. */
|
|
147
|
+
export class SurfaceTextEngine implements TextEngine {
|
|
148
|
+
private readonly _app: unknown;
|
|
149
|
+
private readonly _fonts: FontsLike;
|
|
150
|
+
private readonly _family: string;
|
|
151
|
+
private readonly _layouts = new Map<
|
|
152
|
+
string,
|
|
153
|
+
ReturnType<FontsLike['layout']>
|
|
154
|
+
>();
|
|
155
|
+
private _surface: SurfaceLike | null = null;
|
|
156
|
+
private _ctx: ContextLike | null = null;
|
|
157
|
+
/** Whether the layouts answer their own coverage: unknown until a string
|
|
158
|
+
* asks, and false for good once one does not. */
|
|
159
|
+
private _coverage: boolean | null = null;
|
|
160
|
+
|
|
161
|
+
constructor(app: unknown, fonts: FontsLike, family: string) {
|
|
162
|
+
this._app = app;
|
|
163
|
+
this._fonts = fonts;
|
|
164
|
+
this._family = family;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** The engine for the app a node belongs to, or null where there is
|
|
168
|
+
* nothing to set text with. */
|
|
169
|
+
static forApp(app: unknown, family: string): SurfaceTextEngine | null {
|
|
170
|
+
const fonts = (app as { fonts?: FontsLike } | null | undefined)?.fonts;
|
|
171
|
+
if (!fonts || typeof fonts.layout !== 'function') return null;
|
|
172
|
+
return new SurfaceTextEngine(app, fonts, family);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private _layout(text: string, size: number): ReturnType<FontsLike['layout']> {
|
|
176
|
+
const key = `${size}|${text}`;
|
|
177
|
+
let layout = this._layouts.get(key);
|
|
178
|
+
if (!layout) {
|
|
179
|
+
// Bounded like the retained renderer's shaper: cleared wholesale, and
|
|
180
|
+
// what is on screen is set again once.
|
|
181
|
+
if (this._layouts.size > 8000) this._layouts.clear();
|
|
182
|
+
layout = this._fonts.layout(text, {
|
|
183
|
+
family: this._family,
|
|
184
|
+
size,
|
|
185
|
+
weight: 400,
|
|
186
|
+
style: 'normal',
|
|
187
|
+
color: '#ffffff',
|
|
188
|
+
});
|
|
189
|
+
this._layouts.set(key, layout);
|
|
190
|
+
}
|
|
191
|
+
return layout;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
measure(text: string, size: number): TextBox {
|
|
195
|
+
const layout = this._layout(text, size);
|
|
196
|
+
return { width: Math.ceil(layout.width), height: Math.ceil(layout.height) };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Strings the layouts set themselves, where the engine answers a layout's
|
|
201
|
+
* coverage (react-x11#673): no staging surface and no readback. The rest —
|
|
202
|
+
* icons, which are paths, and every string on an engine that cannot — go
|
|
203
|
+
* through the surface.
|
|
204
|
+
*/
|
|
205
|
+
/**
|
|
206
|
+
* A string the layout sets itself, synchronously, where the engine
|
|
207
|
+
* answers a layout's coverage — no surface, no readback, nothing to wait
|
|
208
|
+
* for. `undefined` for an icon, and for every string once the engine has
|
|
209
|
+
* shown it cannot.
|
|
210
|
+
*/
|
|
211
|
+
rasterizeNow(item: TextItem, pad: number): TextRaster | null | undefined {
|
|
212
|
+
if (item.icon || this._coverage === false) return undefined;
|
|
213
|
+
const coverage =
|
|
214
|
+
this._layout(item.text, item.size).coverage?.({ pad }) ?? null;
|
|
215
|
+
if (!coverage) {
|
|
216
|
+
if (this._coverage === null) this._coverage = false;
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
this._coverage = true;
|
|
220
|
+
return coverage.width > STAGING_WIDTH || coverage.height > STAGING_HEIGHT
|
|
221
|
+
? null // wider than the surface could set: never drawn, as there
|
|
222
|
+
: {
|
|
223
|
+
width: coverage.width,
|
|
224
|
+
height: coverage.height,
|
|
225
|
+
pixels: coverage.data,
|
|
226
|
+
stride: 1,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async rasterize(
|
|
231
|
+
items: readonly TextItem[],
|
|
232
|
+
pad: number,
|
|
233
|
+
): Promise<(TextRaster | null | undefined)[]> {
|
|
234
|
+
if (this._coverage === false) return this._stage(items, pad);
|
|
235
|
+
const out: (TextRaster | null | undefined)[] = new Array(items.length);
|
|
236
|
+
const rest: number[] = [];
|
|
237
|
+
items.forEach((item, i) => {
|
|
238
|
+
const raster = this.rasterizeNow(item, pad);
|
|
239
|
+
if (raster === undefined) {
|
|
240
|
+
rest.push(i);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
out[i] = raster;
|
|
244
|
+
});
|
|
245
|
+
if (rest.length === 0) return out;
|
|
246
|
+
const staged = await this._stage(
|
|
247
|
+
rest.map((i) => items[i]),
|
|
248
|
+
pad,
|
|
249
|
+
);
|
|
250
|
+
rest.forEach((i, k) => (out[i] = staged[k]));
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private async _stage(
|
|
255
|
+
items: readonly TextItem[],
|
|
256
|
+
pad: number,
|
|
257
|
+
): Promise<(TextRaster | null | undefined)[]> {
|
|
258
|
+
const places: (
|
|
259
|
+
{ x: number; y: number; w: number; h: number } | null | undefined
|
|
260
|
+
)[] = [];
|
|
261
|
+
let x = 0;
|
|
262
|
+
let y = 0;
|
|
263
|
+
let row = 0;
|
|
264
|
+
for (const item of items) {
|
|
265
|
+
const box = item.icon
|
|
266
|
+
? { width: Math.ceil(item.size), height: Math.ceil(item.size) }
|
|
267
|
+
: this.measure(item.text, item.size);
|
|
268
|
+
const w = box.width + pad * 2;
|
|
269
|
+
const h = box.height + pad * 2;
|
|
270
|
+
if (w > STAGING_WIDTH || h > STAGING_HEIGHT) {
|
|
271
|
+
places.push(null);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (x + w > STAGING_WIDTH) {
|
|
275
|
+
x = 0;
|
|
276
|
+
y += row;
|
|
277
|
+
row = 0;
|
|
278
|
+
}
|
|
279
|
+
if (y + h > STAGING_HEIGHT) {
|
|
280
|
+
places.push(undefined);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
places.push({ x, y, w, h });
|
|
284
|
+
x += w;
|
|
285
|
+
row = Math.max(row, h);
|
|
286
|
+
}
|
|
287
|
+
if (!places.some((p) => p)) {
|
|
288
|
+
return places.map((p) => (p === null ? null : undefined));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!this._surface) {
|
|
292
|
+
this._surface = new Surface(this._app as never, {
|
|
293
|
+
width: STAGING_WIDTH,
|
|
294
|
+
height: STAGING_HEIGHT,
|
|
295
|
+
}) as unknown as SurfaceLike;
|
|
296
|
+
this._ctx = this._surface.getContext('2d');
|
|
297
|
+
}
|
|
298
|
+
const surface = this._surface;
|
|
299
|
+
const ctx = this._ctx!;
|
|
300
|
+
surface.clear();
|
|
301
|
+
const started = now();
|
|
302
|
+
// The rows drawn into: what is read back, and no more.
|
|
303
|
+
let used = 0;
|
|
304
|
+
let drawn = 0;
|
|
305
|
+
items.forEach((item, i) => {
|
|
306
|
+
const place = places[i];
|
|
307
|
+
if (!place) return;
|
|
308
|
+
// Drawing is this thread's time; past the budget the rest wait for
|
|
309
|
+
// the next batch — all but the first. A pause before it, a collection
|
|
310
|
+
// or a slow runner, deferred every string: nothing drawn, a readback of
|
|
311
|
+
// no rows, and a throw out of the frame; and a machine that slow would
|
|
312
|
+
// never have set one.
|
|
313
|
+
if (drawn > 0 && now() - started > DRAW_BUDGET_MS) {
|
|
314
|
+
places[i] = undefined;
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (item.icon) {
|
|
318
|
+
// White, like the strings: coverage, coloured by the shader.
|
|
319
|
+
if (!fills(ctx)) {
|
|
320
|
+
places[i] = null;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const half = item.size / 2;
|
|
324
|
+
const cx = place.x + pad + half;
|
|
325
|
+
const cy = place.y + pad + half;
|
|
326
|
+
ctx.fillStyle = '#ffffff';
|
|
327
|
+
ctx.beginPath();
|
|
328
|
+
if (item.icon.part === 'plate') tracePlate(ctx, cx, cy, item.size);
|
|
329
|
+
else traceGlyph(ctx, item.icon.name, cx, cy, item.size);
|
|
330
|
+
ctx.fill();
|
|
331
|
+
} else {
|
|
332
|
+
this._layout(item.text, item.size).draw(
|
|
333
|
+
ctx,
|
|
334
|
+
place.x + pad,
|
|
335
|
+
place.y + pad,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
used = Math.max(used, place.y + place.h);
|
|
339
|
+
drawn += 1;
|
|
340
|
+
});
|
|
341
|
+
// nothing drawn — every icon here had nothing to fill with — is nothing
|
|
342
|
+
// to read back
|
|
343
|
+
if (drawn === 0) {
|
|
344
|
+
return places.map((p) => (p === null ? null : undefined));
|
|
345
|
+
}
|
|
346
|
+
const image = await ctx.getImageData(0, 0, STAGING_WIDTH, used);
|
|
347
|
+
return places.map((place) => {
|
|
348
|
+
if (!place) return place;
|
|
349
|
+
// Row by row, as the readback has them: a copy, not a pass over the
|
|
350
|
+
// pixels, and uploaded as it is — the shader reads alpha alone.
|
|
351
|
+
const rowBytes = place.w * 4;
|
|
352
|
+
const pixels = new Uint8Array(rowBytes * place.h);
|
|
353
|
+
for (let r = 0; r < place.h; r++) {
|
|
354
|
+
const src = ((place.y + r) * image.width + place.x) * 4;
|
|
355
|
+
pixels.set(image.data.subarray(src, src + rowBytes), r * rowBytes);
|
|
356
|
+
}
|
|
357
|
+
return { width: place.w, height: place.h, pixels };
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
dispose(): void {
|
|
362
|
+
this._ctx?.destroy?.();
|
|
363
|
+
this._ctx = null;
|
|
364
|
+
this._surface?.destroy();
|
|
365
|
+
this._surface = null;
|
|
366
|
+
this._layouts.clear();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** A rasterized label's place in the atlas: its whole padded raster. */
|
|
371
|
+
export interface AtlasEntry {
|
|
372
|
+
key: string;
|
|
373
|
+
x: number;
|
|
374
|
+
y: number;
|
|
375
|
+
width: number;
|
|
376
|
+
height: number;
|
|
377
|
+
/** The distance field, one byte a texel — kept to upload again, after a
|
|
378
|
+
* compaction moves it or into the texture of a new context. */
|
|
379
|
+
pixels: Uint8Array;
|
|
380
|
+
/** In the texture where it is now; only then is it drawn. Set by the
|
|
381
|
+
* renderer that uploads it. */
|
|
382
|
+
ready: boolean;
|
|
383
|
+
/** The frame it was last asked for — what a compaction keeps. */
|
|
384
|
+
used: number;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** A string's key: the text alone, because one field serves every size. */
|
|
388
|
+
const keyOf = (text: string): string => `|${text}`;
|
|
389
|
+
/** An icon piece's key: `#` where a string's has `|`, so no string can
|
|
390
|
+
* be taken for one. */
|
|
391
|
+
const iconKey = (name: MapIcon, part: IconPart): string => `#${part}:${name}`;
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Texels the drawn quad leaves off each side of a field: bilinear samples
|
|
395
|
+
* at its edge then stay inside the field's own margin, never reaching the
|
|
396
|
+
* shelf's unwritten rows or a neighbour.
|
|
397
|
+
*/
|
|
398
|
+
export const QUAD_INSET = 1;
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* How far past the glyphs' edge a field can see, in its own texels — the
|
|
402
|
+
* widest halo it holds, and the reach of its distances before they
|
|
403
|
+
* saturate — for a field set with `pad` texels of margin and `pad` of
|
|
404
|
+
* spread. Scale by the drawn size over the base for screen pixels.
|
|
405
|
+
*/
|
|
406
|
+
export const fieldReach = (pad: number): number =>
|
|
407
|
+
Math.max(0, Math.min(pad * SDF_EDGE, pad - QUAD_INSET) - 0.5);
|
|
408
|
+
|
|
409
|
+
/** The base size a display scale sets fields at, in device pixels: 16 at
|
|
410
|
+
* 1x, 32 at 2x — the size most map labels are drawn at or just under, so
|
|
411
|
+
* a field is mostly sampled down, where it is sharpest. */
|
|
412
|
+
export const fieldBase = (scale: number): number =>
|
|
413
|
+
Math.round(16 * Math.min(2, Math.max(1, scale)));
|
|
414
|
+
|
|
415
|
+
/** A field's margin and spread at a base size: room for a halo of a
|
|
416
|
+
* quarter of the type's height. */
|
|
417
|
+
export const fieldPad = (base: number): number => Math.ceil(base * 0.375);
|
|
418
|
+
|
|
419
|
+
/** Strings a frame measures however little budget it has left, so a frame
|
|
420
|
+
* that starts late still makes progress. One: on X11 a string's first
|
|
421
|
+
* measurement can cost milliseconds, and a floor of several put a pan
|
|
422
|
+
* frame 27 ms over. */
|
|
423
|
+
const MIN_MEASURES = 1;
|
|
424
|
+
/** Strings per rasterization batch at most — a few rows of the staging
|
|
425
|
+
* surface, so the readback and the copies out of it stay small too. */
|
|
426
|
+
const BATCH = 32;
|
|
427
|
+
/** Frames an unused entry survives a compaction for. */
|
|
428
|
+
const KEEP_FRAMES = 120;
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Every label raster, packed into one texture's worth of shelves, with the
|
|
432
|
+
* uploads the renderer owes it.
|
|
433
|
+
*
|
|
434
|
+
* The atlas is CPU-side and outlives any one GL context: it keeps each
|
|
435
|
+
* entry's raster, so a renderer that is new — the surface was recreated —
|
|
436
|
+
* uploads it again from here ({@link restart}), and asks nothing of the text
|
|
437
|
+
* engine. What it holds is bounded by the texture it packs into: 16 MB for
|
|
438
|
+
* the default 2048², whatever is panned past.
|
|
439
|
+
*
|
|
440
|
+
* **Measuring is budgeted, drawing is asynchronous, uploading is metered.**
|
|
441
|
+
* Placement needs a label's box the frame it is considered, which is a
|
|
442
|
+
* synchronous shaping call — tens of microseconds on the Cocoa backend, but
|
|
443
|
+
* milliseconds each the first time on X11, where fonts load and glyphs
|
|
444
|
+
* rasterize in JS — so a frame measures until its deadline and leaves the
|
|
445
|
+
* rest for the next. A raster is asked for only once a label has won a
|
|
446
|
+
* place, and arrives a batch at a time, a readback later; the renderer
|
|
447
|
+
* takes a few dozen a frame into the texture, and a label fades in once its
|
|
448
|
+
* raster is there.
|
|
449
|
+
*/
|
|
450
|
+
export class LabelAtlas {
|
|
451
|
+
readonly size: number;
|
|
452
|
+
/** The device-pixel size every string and icon is set at: a label drawn
|
|
453
|
+
* at `size` scales its field by `size / base`. */
|
|
454
|
+
readonly base: number;
|
|
455
|
+
/** The margin set around every field, in its texels, which is also the
|
|
456
|
+
* distance its values span — what a halo grows into. */
|
|
457
|
+
readonly pad: number;
|
|
458
|
+
/** A frame's measuring ran out of budget: there is more to place. */
|
|
459
|
+
starved = false;
|
|
460
|
+
onChange: (() => void) | null = null;
|
|
461
|
+
private _engine: TextEngine | null;
|
|
462
|
+
private readonly _entries = new Map<string, AtlasEntry>();
|
|
463
|
+
/** Entries the texture does not have where they are, oldest first. */
|
|
464
|
+
private _uploads: AtlasEntry[] = [];
|
|
465
|
+
/** Drawable entries a compaction moved: all of them go up in the next
|
|
466
|
+
* frame, ahead of any cap, before it draws. */
|
|
467
|
+
private _moved: AtlasEntry[] = [];
|
|
468
|
+
private readonly _wanted = new Map<string, TextItem>();
|
|
469
|
+
private readonly _failed = new Set<string>();
|
|
470
|
+
/** Rasters read back and waiting for their fields, oldest first — asked
|
|
471
|
+
* for as much as anything in `_wanted`, so a frame never asks again. */
|
|
472
|
+
private _fieldQueue: { key: string; raster: TextRaster }[] = [];
|
|
473
|
+
private readonly _queued = new Set<string>();
|
|
474
|
+
private readonly _measured = new Map<string, TextBox>();
|
|
475
|
+
private _busy = false;
|
|
476
|
+
private _shelves: { y: number; height: number; x: number }[] = [];
|
|
477
|
+
private _top = 0;
|
|
478
|
+
private _frame = 0;
|
|
479
|
+
private _deadline = Infinity;
|
|
480
|
+
private _measures = 0;
|
|
481
|
+
|
|
482
|
+
constructor(
|
|
483
|
+
engine: TextEngine | null,
|
|
484
|
+
options: { size?: number; base?: number; pad?: number } = {},
|
|
485
|
+
) {
|
|
486
|
+
this._engine = engine;
|
|
487
|
+
this.size = options.size ?? 2048;
|
|
488
|
+
this.base = options.base ?? fieldBase(1);
|
|
489
|
+
this.pad = options.pad ?? fieldPad(this.base);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/** Strings asked for and not yet set: waiting, being read back, or
|
|
493
|
+
* waiting for their fields. */
|
|
494
|
+
get pending(): boolean {
|
|
495
|
+
return this._busy || this._wanted.size > 0 || this._fieldQueue.length > 0;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/** Rasters waiting for the texture. */
|
|
499
|
+
get uploading(): boolean {
|
|
500
|
+
return this._uploads.length > 0;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
get entries(): number {
|
|
504
|
+
return this._entries.size;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** A new frame, whose measuring stops at `deadline` (after one). */
|
|
508
|
+
beginFrame(deadline: number): void {
|
|
509
|
+
this._frame++;
|
|
510
|
+
this._deadline = deadline;
|
|
511
|
+
this._measures = 0;
|
|
512
|
+
this.starved = false;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** The box a string sets in at `size`, or null when this frame's budget
|
|
516
|
+
* is spent. Measured once, at the base size, and scaled — the same
|
|
517
|
+
* arithmetic that scales its field, so the box placement collides and
|
|
518
|
+
* the quad the frame draws are one size. */
|
|
519
|
+
measure(text: string, size: number): TextBox | null {
|
|
520
|
+
const key = keyOf(text);
|
|
521
|
+
let box = this._measured.get(key);
|
|
522
|
+
if (!box) {
|
|
523
|
+
const engine = this._engine;
|
|
524
|
+
if (!engine) return null;
|
|
525
|
+
if (this._measures >= MIN_MEASURES && now() > this._deadline) {
|
|
526
|
+
this.starved = true;
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
this._measures++;
|
|
530
|
+
box = engine.measure(text, this.base);
|
|
531
|
+
if (this._measured.size > 20000) this._measured.clear();
|
|
532
|
+
this._measured.set(key, box);
|
|
533
|
+
}
|
|
534
|
+
const k = size / this.base;
|
|
535
|
+
return { width: box.width * k, height: box.height * k };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** The string's field, once the texture has it; null until then —
|
|
539
|
+
* asking is what gets it set. One field whatever size it is drawn at. */
|
|
540
|
+
entry(text: string): AtlasEntry | null {
|
|
541
|
+
return this._lookup(keyOf(text), text, null, 'plate');
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/** One piece of an icon, as {@link entry} answers for a string. */
|
|
545
|
+
icon(name: MapIcon, part: IconPart): AtlasEntry | null {
|
|
546
|
+
return this._lookup(iconKey(name, part), '', name, part);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
private _lookup(
|
|
550
|
+
key: string,
|
|
551
|
+
text: string,
|
|
552
|
+
icon: MapIcon | null,
|
|
553
|
+
part: IconPart,
|
|
554
|
+
): AtlasEntry | null {
|
|
555
|
+
const entry = this._entries.get(key);
|
|
556
|
+
if (entry) {
|
|
557
|
+
entry.used = this._frame;
|
|
558
|
+
return entry.ready ? entry : null;
|
|
559
|
+
}
|
|
560
|
+
if (
|
|
561
|
+
!this._failed.has(key) &&
|
|
562
|
+
!this._wanted.has(key) &&
|
|
563
|
+
!this._queued.has(key)
|
|
564
|
+
) {
|
|
565
|
+
const size = this.base;
|
|
566
|
+
this._wanted.set(
|
|
567
|
+
key,
|
|
568
|
+
icon ? { text, size, icon: { name: icon, part } } : { text, size },
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/** Whether an icon can never be drawn — the engine has no path to fill
|
|
575
|
+
* it with. A name set beside it is then set alone. */
|
|
576
|
+
iconFailed(name: MapIcon): boolean {
|
|
577
|
+
return (
|
|
578
|
+
this._failed.has(iconKey(name, 'plate')) ||
|
|
579
|
+
this._failed.has(iconKey(name, 'glyph'))
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** Start rasterizing what has been asked for, unless a batch is out. */
|
|
584
|
+
pump(): void {
|
|
585
|
+
const engine = this._engine;
|
|
586
|
+
if (!engine || this._busy || this._wanted.size === 0) return;
|
|
587
|
+
const keys: string[] = [];
|
|
588
|
+
const batch: TextItem[] = [];
|
|
589
|
+
for (const [key, item] of this._wanted) {
|
|
590
|
+
keys.push(key);
|
|
591
|
+
batch.push(item);
|
|
592
|
+
if (batch.length >= BATCH) break;
|
|
593
|
+
}
|
|
594
|
+
this._busy = true;
|
|
595
|
+
engine.rasterize(batch, this.pad).then(
|
|
596
|
+
(rasters) => {
|
|
597
|
+
this._busy = false;
|
|
598
|
+
rasters.forEach((raster, i) => {
|
|
599
|
+
if (raster === undefined) return;
|
|
600
|
+
const key = keys[i];
|
|
601
|
+
this._wanted.delete(key);
|
|
602
|
+
// A string with no raster never will have one; a raster with no
|
|
603
|
+
// room is a fact about this moment, and the string is asked for
|
|
604
|
+
// again by the next frame that still shows it.
|
|
605
|
+
if (raster === null) {
|
|
606
|
+
this._failed.add(key);
|
|
607
|
+
} else {
|
|
608
|
+
this._fieldQueue.push({ key, raster });
|
|
609
|
+
this._queued.add(key);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
// The staging surface is free again as soon as the pixels are
|
|
613
|
+
// copied out, so the next batch is read back while this one waits
|
|
614
|
+
// for its fields; and a frame is asked for, to make them.
|
|
615
|
+
this.pump();
|
|
616
|
+
this.onChange?.();
|
|
617
|
+
},
|
|
618
|
+
() => {
|
|
619
|
+
// A readback that fails once fails every time (no surface, a
|
|
620
|
+
// backend without one): stop asking, and draw no labels.
|
|
621
|
+
this._busy = false;
|
|
622
|
+
this._engine = null;
|
|
623
|
+
this._wanted.clear();
|
|
624
|
+
this.onChange?.();
|
|
625
|
+
},
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** Rasters for the texture: every one a compaction moved, then up to
|
|
630
|
+
* `max` new ones, oldest first. The renderer marks each `ready` once it
|
|
631
|
+
* is uploaded — a moved one already is, because the frame that takes it
|
|
632
|
+
* draws after taking it. */
|
|
633
|
+
takeUploads(max: number): AtlasEntry[] {
|
|
634
|
+
const moved = this._moved;
|
|
635
|
+
this._moved = [];
|
|
636
|
+
return moved.length > 0
|
|
637
|
+
? moved.concat(this._uploads.splice(0, max))
|
|
638
|
+
: this._uploads.splice(0, max);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** The texture is new, and has none of it: everything goes up again. */
|
|
642
|
+
restart(): void {
|
|
643
|
+
for (const entry of this._entries.values()) entry.ready = false;
|
|
644
|
+
this._uploads = [...this._entries.values()];
|
|
645
|
+
this._moved = [];
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
dispose(): void {
|
|
649
|
+
this._engine?.dispose();
|
|
650
|
+
this._engine = null;
|
|
651
|
+
this._fieldQueue = [];
|
|
652
|
+
this._queued.clear();
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/** Read back and waiting for their fields: another frame is worth
|
|
656
|
+
* drawing, to make them. */
|
|
657
|
+
get fielding(): boolean {
|
|
658
|
+
return this._fieldQueue.length > 0;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Make fields for what has been read back until `deadline` — at least
|
|
663
|
+
* one — in the frame, whose own draw then takes them into the texture.
|
|
664
|
+
*
|
|
665
|
+
* A field is about 0.2 ms. A batch of them made as it landed was 4-9 ms
|
|
666
|
+
* beside the frames, measured as event-loop stalls on a jump to a view
|
|
667
|
+
* whose every name was new; slices on timers of their own each waited
|
|
668
|
+
* behind a frame, and names arrived late. In the frame, on a budget,
|
|
669
|
+
* they are made at a known cost and drawn the frame after.
|
|
670
|
+
*/
|
|
671
|
+
/**
|
|
672
|
+
* Set what placement asked for, in the frame, until `deadline` (at least
|
|
673
|
+
* one): what was read back already, then every string the engine can set
|
|
674
|
+
* synchronously. What is set here is drawable in this frame — it goes
|
|
675
|
+
* into the texture ahead of any cap, before this frame draws (the
|
|
676
|
+
* `labelsWhileMoving` path, `view.ts`). Icons, and an engine with no
|
|
677
|
+
* synchronous path, still go through {@link pump}.
|
|
678
|
+
*/
|
|
679
|
+
setNow(deadline: number): void {
|
|
680
|
+
const engine = this._engine;
|
|
681
|
+
if (!engine) return;
|
|
682
|
+
let made = 0;
|
|
683
|
+
while (this._fieldQueue.length > 0 && (made === 0 || now() < deadline)) {
|
|
684
|
+
const { key, raster } = this._fieldQueue.shift()!;
|
|
685
|
+
this._queued.delete(key);
|
|
686
|
+
this._add(key, raster.width, raster.height, this._field(raster), true);
|
|
687
|
+
made++;
|
|
688
|
+
}
|
|
689
|
+
if (!engine.rasterizeNow) return;
|
|
690
|
+
for (const [key, item] of this._wanted) {
|
|
691
|
+
if (made > 0 && now() >= deadline) {
|
|
692
|
+
this.starved = true;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
const raster = engine.rasterizeNow(item, this.pad);
|
|
696
|
+
if (raster === undefined) continue;
|
|
697
|
+
this._wanted.delete(key);
|
|
698
|
+
if (raster === null) {
|
|
699
|
+
this._failed.add(key);
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
this._add(key, raster.width, raster.height, this._field(raster), true);
|
|
703
|
+
made++;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
makeFields(deadline: number): void {
|
|
708
|
+
if (!this._engine) return;
|
|
709
|
+
let made = 0;
|
|
710
|
+
while (this._fieldQueue.length > 0 && (made === 0 || now() < deadline)) {
|
|
711
|
+
const { key, raster } = this._fieldQueue.shift()!;
|
|
712
|
+
this._queued.delete(key);
|
|
713
|
+
this._add(key, raster.width, raster.height, this._field(raster));
|
|
714
|
+
made++;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/** A raster's coverage as its distance field. The coverage is the
|
|
719
|
+
* readback's alpha; the field spans `pad` texels either side of the
|
|
720
|
+
* edge, which is the margin the engine set it with. */
|
|
721
|
+
private _field(raster: TextRaster): Uint8Array {
|
|
722
|
+
const stride = raster.stride ?? 4;
|
|
723
|
+
return distanceField(
|
|
724
|
+
raster.pixels,
|
|
725
|
+
raster.width,
|
|
726
|
+
raster.height,
|
|
727
|
+
this.pad,
|
|
728
|
+
stride,
|
|
729
|
+
stride === 4 ? 3 : 0,
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
private _add(
|
|
734
|
+
key: string,
|
|
735
|
+
width: number,
|
|
736
|
+
height: number,
|
|
737
|
+
pixels: Uint8Array,
|
|
738
|
+
/** Drawable in this frame: uploaded ahead of any cap before it draws. */
|
|
739
|
+
immediate = false,
|
|
740
|
+
): boolean {
|
|
741
|
+
let place =
|
|
742
|
+
this._allocate(width, height) ?? this._reuseShelf(width, height);
|
|
743
|
+
if (!place && this._compactable()) {
|
|
744
|
+
this._compact();
|
|
745
|
+
place = this._allocate(width, height);
|
|
746
|
+
}
|
|
747
|
+
if (!place) return false;
|
|
748
|
+
const entry: AtlasEntry = {
|
|
749
|
+
key,
|
|
750
|
+
x: place.x,
|
|
751
|
+
y: place.y,
|
|
752
|
+
width,
|
|
753
|
+
height,
|
|
754
|
+
pixels,
|
|
755
|
+
ready: immediate,
|
|
756
|
+
used: this._frame,
|
|
757
|
+
};
|
|
758
|
+
this._entries.set(key, entry);
|
|
759
|
+
if (immediate) this._moved.push(entry);
|
|
760
|
+
else this._uploads.push(entry);
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/** A shelf packer: rows of one height class (rounded up to 8 pixels),
|
|
765
|
+
* filled left to right, a new row below the last when none has room. */
|
|
766
|
+
private _allocate(w: number, h: number): { x: number; y: number } | null {
|
|
767
|
+
if (w > this.size || h > this.size) return null;
|
|
768
|
+
const height = Math.ceil(h / 8) * 8;
|
|
769
|
+
for (const shelf of this._shelves) {
|
|
770
|
+
if (shelf.height === height && shelf.x + w <= this.size) {
|
|
771
|
+
const at = { x: shelf.x, y: shelf.y };
|
|
772
|
+
shelf.x += w;
|
|
773
|
+
return at;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (this._top + height > this.size) return null;
|
|
777
|
+
const shelf = { y: this._top, height, x: w };
|
|
778
|
+
this._shelves.push(shelf);
|
|
779
|
+
this._top += height;
|
|
780
|
+
return { x: 0, y: shelf.y };
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Room made in place: the shelf of this height whose rasters were drawn
|
|
785
|
+
* longest ago, emptied and filled again from its left.
|
|
786
|
+
*
|
|
787
|
+
* **Nothing that stays moves.** A compaction moves every raster it keeps,
|
|
788
|
+
* and a moved raster is not drawn until it is in the texture again — at
|
|
789
|
+
* {@link takeUploads}' pace, so every label on screen went out together
|
|
790
|
+
* and came back over the next few frames, at full opacity. A full atlas
|
|
791
|
+
* is the ordinary state of a session (a thousand strings at 1x, a quarter
|
|
792
|
+
* of that at 2x), so that was a flash of the whole label layer at the end
|
|
793
|
+
* of most settles. A shelf any label of the last two frames stands on is not
|
|
794
|
+
* taken: what is dropped is off screen, and asked for again if it
|
|
795
|
+
* returns.
|
|
796
|
+
*/
|
|
797
|
+
private _reuseShelf(w: number, h: number): { x: number; y: number } | null {
|
|
798
|
+
if (w > this.size) return null;
|
|
799
|
+
const height = Math.ceil(h / 8) * 8;
|
|
800
|
+
const newest = new Map<number, number>();
|
|
801
|
+
for (const entry of this._entries.values()) {
|
|
802
|
+
const seen = newest.get(entry.y);
|
|
803
|
+
if (seen === undefined || entry.used > seen)
|
|
804
|
+
newest.set(entry.y, entry.used);
|
|
805
|
+
}
|
|
806
|
+
let shelf: { y: number; height: number; x: number } | null = null;
|
|
807
|
+
// Not a shelf any label of this frame or the last stands on: fields can
|
|
808
|
+
// be made before a frame's labels are batched (`setNow`), when a label
|
|
809
|
+
// still fading out has not been asked for yet.
|
|
810
|
+
let oldest = this._frame - 1;
|
|
811
|
+
for (const candidate of this._shelves) {
|
|
812
|
+
if (candidate.height !== height) continue;
|
|
813
|
+
const used = newest.get(candidate.y) ?? -Infinity;
|
|
814
|
+
if (used < oldest) {
|
|
815
|
+
oldest = used;
|
|
816
|
+
shelf = candidate;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (!shelf) return null;
|
|
820
|
+
const y = shelf.y;
|
|
821
|
+
for (const [key, entry] of this._entries) {
|
|
822
|
+
if (entry.y === y) this._entries.delete(key);
|
|
823
|
+
}
|
|
824
|
+
this._uploads = this._uploads.filter((entry) => entry.y !== y);
|
|
825
|
+
this._moved = this._moved.filter((entry) => entry.y !== y);
|
|
826
|
+
shelf.x = w;
|
|
827
|
+
return { x: 0, y };
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/** Whether a compaction would free anything: something kept past its
|
|
831
|
+
* last use. One that keeps everything only moves it — every label on
|
|
832
|
+
* screen out of the texture for the frames its upload takes, for no
|
|
833
|
+
* room at all. */
|
|
834
|
+
private _compactable(): boolean {
|
|
835
|
+
for (const entry of this._entries.values()) {
|
|
836
|
+
if (entry.used < this._frame - KEEP_FRAMES) return true;
|
|
837
|
+
}
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/** Drop what has not been drawn for a while and pack the rest again,
|
|
842
|
+
* tallest first — the last resort, when no shelf of the height wanted
|
|
843
|
+
* can be taken in place. What was drawable stays drawable: it is
|
|
844
|
+
* uploaded where it now is, all of it, by the next frame before that
|
|
845
|
+
* frame draws ({@link takeUploads}); what was waiting still waits. */
|
|
846
|
+
private _compact(): void {
|
|
847
|
+
const keep = [...this._entries.values()]
|
|
848
|
+
.filter((e) => e.used >= this._frame - KEEP_FRAMES)
|
|
849
|
+
.sort((a, b) => b.height - a.height);
|
|
850
|
+
this._entries.clear();
|
|
851
|
+
this._shelves = [];
|
|
852
|
+
this._top = 0;
|
|
853
|
+
this._uploads = [];
|
|
854
|
+
this._moved = [];
|
|
855
|
+
for (const entry of keep) {
|
|
856
|
+
const place = this._allocate(entry.width, entry.height);
|
|
857
|
+
if (!place) continue;
|
|
858
|
+
entry.x = place.x;
|
|
859
|
+
entry.y = place.y;
|
|
860
|
+
this._entries.set(entry.key, entry);
|
|
861
|
+
if (entry.ready) this._moved.push(entry);
|
|
862
|
+
else this._uploads.push(entry);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const globals = globalThis as { performance?: { now(): number } };
|
|
868
|
+
const now = (): number => globals.performance?.now() ?? Date.now();
|