@contraagent/elements 0.0.0-stage → 0.1.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 +72 -2
- package/custom-elements.json +4260 -0
- package/dist/charts/bar-chart.d.ts +32 -0
- package/dist/charts/bar-chart.d.ts.map +1 -0
- package/dist/charts/bar-chart.js +70 -0
- package/dist/charts/bar-chart.js.map +1 -0
- package/dist/charts/base.d.ts +48 -0
- package/dist/charts/base.d.ts.map +1 -0
- package/dist/charts/base.js +203 -0
- package/dist/charts/base.js.map +1 -0
- package/dist/charts/column-chart.d.ts +42 -0
- package/dist/charts/column-chart.d.ts.map +1 -0
- package/dist/charts/column-chart.js +146 -0
- package/dist/charts/column-chart.js.map +1 -0
- package/dist/charts/heatmap.d.ts +42 -0
- package/dist/charts/heatmap.d.ts.map +1 -0
- package/dist/charts/heatmap.js +124 -0
- package/dist/charts/heatmap.js.map +1 -0
- package/dist/charts/index.d.ts +9 -0
- package/dist/charts/index.d.ts.map +1 -0
- package/dist/charts/index.js +11 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/charts/line-chart.d.ts +36 -0
- package/dist/charts/line-chart.d.ts.map +1 -0
- package/dist/charts/line-chart.js +118 -0
- package/dist/charts/line-chart.js.map +1 -0
- package/dist/charts/scale.d.ts +32 -0
- package/dist/charts/scale.d.ts.map +1 -0
- package/dist/charts/scale.js +50 -0
- package/dist/charts/scale.js.map +1 -0
- package/dist/charts/state-grid.d.ts +64 -0
- package/dist/charts/state-grid.d.ts.map +1 -0
- package/dist/charts/state-grid.js +223 -0
- package/dist/charts/state-grid.js.map +1 -0
- package/dist/charts/types.d.ts +34 -0
- package/dist/charts/types.d.ts.map +1 -0
- package/dist/charts/types.js +11 -0
- package/dist/charts/types.js.map +1 -0
- package/dist/checks/check-table.d.ts +32 -0
- package/dist/checks/check-table.d.ts.map +1 -0
- package/dist/checks/check-table.js +94 -0
- package/dist/checks/check-table.js.map +1 -0
- package/dist/checks/index.d.ts +4 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +6 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/styles.d.ts +5 -0
- package/dist/checks/styles.d.ts.map +1 -0
- package/dist/checks/styles.js +30 -0
- package/dist/checks/styles.js.map +1 -0
- package/dist/checks/types.d.ts +35 -0
- package/dist/checks/types.d.ts.map +1 -0
- package/dist/checks/types.js +12 -0
- package/dist/checks/types.js.map +1 -0
- package/dist/core/clock.d.ts +20 -0
- package/dist/core/clock.d.ts.map +1 -0
- package/dist/core/clock.js +45 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/define.d.ts +3 -0
- package/dist/core/define.d.ts.map +1 -0
- package/dist/core/define.js +8 -0
- package/dist/core/define.js.map +1 -0
- package/dist/core/events.d.ts +78 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +8 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/format.d.ts +8 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +28 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/frame.d.ts +4 -0
- package/dist/core/frame.d.ts.map +1 -0
- package/dist/core/frame.js +5 -0
- package/dist/core/frame.js.map +1 -0
- package/dist/core/strings.d.ts +3 -0
- package/dist/core/strings.d.ts.map +1 -0
- package/dist/core/strings.js +14 -0
- package/dist/core/strings.js.map +1 -0
- package/dist/core/tone.d.ts +8 -0
- package/dist/core/tone.d.ts.map +1 -0
- package/dist/core/tone.js +9 -0
- package/dist/core/tone.js.map +1 -0
- package/dist/count/count.d.ts +22 -0
- package/dist/count/count.d.ts.map +1 -0
- package/dist/count/count.js +60 -0
- package/dist/count/count.js.map +1 -0
- package/dist/count/elapsed.d.ts +18 -0
- package/dist/count/elapsed.d.ts.map +1 -0
- package/dist/count/elapsed.js +33 -0
- package/dist/count/elapsed.js.map +1 -0
- package/dist/count/index.d.ts +3 -0
- package/dist/count/index.d.ts.map +1 -0
- package/dist/count/index.js +5 -0
- package/dist/count/index.js.map +1 -0
- package/dist/define/all.d.ts +13 -0
- package/dist/define/all.d.ts.map +1 -0
- package/dist/define/all.js +14 -0
- package/dist/define/all.js.map +1 -0
- package/dist/define/charts.d.ts +8 -0
- package/dist/define/charts.d.ts.map +1 -0
- package/dist/define/charts.js +15 -0
- package/dist/define/charts.js.map +1 -0
- package/dist/define/check-table.d.ts +4 -0
- package/dist/define/check-table.d.ts.map +1 -0
- package/dist/define/check-table.js +7 -0
- package/dist/define/check-table.js.map +1 -0
- package/dist/define/count.d.ts +5 -0
- package/dist/define/count.d.ts.map +1 -0
- package/dist/define/count.js +9 -0
- package/dist/define/count.js.map +1 -0
- package/dist/define/data-table.d.ts +4 -0
- package/dist/define/data-table.d.ts.map +1 -0
- package/dist/define/data-table.js +7 -0
- package/dist/define/data-table.js.map +1 -0
- package/dist/define/graph.d.ts +6 -0
- package/dist/define/graph.d.ts.map +1 -0
- package/dist/define/graph.js +9 -0
- package/dist/define/graph.js.map +1 -0
- package/dist/define/json-tree.d.ts +4 -0
- package/dist/define/json-tree.d.ts.map +1 -0
- package/dist/define/json-tree.js +7 -0
- package/dist/define/json-tree.js.map +1 -0
- package/dist/define/lanes.d.ts +4 -0
- package/dist/define/lanes.d.ts.map +1 -0
- package/dist/define/lanes.js +7 -0
- package/dist/define/lanes.js.map +1 -0
- package/dist/define/markdown.d.ts +4 -0
- package/dist/define/markdown.d.ts.map +1 -0
- package/dist/define/markdown.js +7 -0
- package/dist/define/markdown.js.map +1 -0
- package/dist/define/matrix.d.ts +4 -0
- package/dist/define/matrix.d.ts.map +1 -0
- package/dist/define/matrix.js +7 -0
- package/dist/define/matrix.js.map +1 -0
- package/dist/define/segmented.d.ts +4 -0
- package/dist/define/segmented.d.ts.map +1 -0
- package/dist/define/segmented.js +7 -0
- package/dist/define/segmented.js.map +1 -0
- package/dist/define/tiles.d.ts +5 -0
- package/dist/define/tiles.d.ts.map +1 -0
- package/dist/define/tiles.js +9 -0
- package/dist/define/tiles.js.map +1 -0
- package/dist/define/transcript.d.ts +4 -0
- package/dist/define/transcript.d.ts.map +1 -0
- package/dist/define/transcript.js +7 -0
- package/dist/define/transcript.js.map +1 -0
- package/dist/graph/arrange.d.ts +43 -0
- package/dist/graph/arrange.d.ts.map +1 -0
- package/dist/graph/arrange.js +390 -0
- package/dist/graph/arrange.js.map +1 -0
- package/dist/graph/card.d.ts +71 -0
- package/dist/graph/card.d.ts.map +1 -0
- package/dist/graph/card.js +185 -0
- package/dist/graph/card.js.map +1 -0
- package/dist/graph/host.d.ts +82 -0
- package/dist/graph/host.d.ts.map +1 -0
- package/dist/graph/host.js +21 -0
- package/dist/graph/host.js.map +1 -0
- package/dist/graph/icons.d.ts +7 -0
- package/dist/graph/icons.d.ts.map +1 -0
- package/dist/graph/icons.js +18 -0
- package/dist/graph/icons.js.map +1 -0
- package/dist/graph/index.d.ts +15 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +17 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/layered-graph.d.ts +226 -0
- package/dist/graph/layered-graph.d.ts.map +1 -0
- package/dist/graph/layered-graph.js +396 -0
- package/dist/graph/layered-graph.js.map +1 -0
- package/dist/graph/layout.d.ts +80 -0
- package/dist/graph/layout.d.ts.map +1 -0
- package/dist/graph/layout.js +264 -0
- package/dist/graph/layout.js.map +1 -0
- package/dist/graph/marks.d.ts +24 -0
- package/dist/graph/marks.d.ts.map +1 -0
- package/dist/graph/marks.js +38 -0
- package/dist/graph/marks.js.map +1 -0
- package/dist/graph/nav.d.ts +14 -0
- package/dist/graph/nav.d.ts.map +1 -0
- package/dist/graph/nav.js +14 -0
- package/dist/graph/nav.js.map +1 -0
- package/dist/graph/pan-zoom.d.ts +80 -0
- package/dist/graph/pan-zoom.d.ts.map +1 -0
- package/dist/graph/pan-zoom.js +191 -0
- package/dist/graph/pan-zoom.js.map +1 -0
- package/dist/graph/paths.d.ts +9 -0
- package/dist/graph/paths.d.ts.map +1 -0
- package/dist/graph/paths.js +44 -0
- package/dist/graph/paths.js.map +1 -0
- package/dist/graph/place-card.d.ts +36 -0
- package/dist/graph/place-card.d.ts.map +1 -0
- package/dist/graph/place-card.js +68 -0
- package/dist/graph/place-card.js.map +1 -0
- package/dist/graph/strings.d.ts +50 -0
- package/dist/graph/strings.d.ts.map +1 -0
- package/dist/graph/strings.js +37 -0
- package/dist/graph/strings.js.map +1 -0
- package/dist/graph/styles.d.ts +2 -0
- package/dist/graph/styles.d.ts.map +1 -0
- package/dist/graph/styles.js +203 -0
- package/dist/graph/styles.js.map +1 -0
- package/dist/graph/types.d.ts +184 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +2 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/graph/view.d.ts +17 -0
- package/dist/graph/view.d.ts.map +1 -0
- package/dist/graph/view.js +21 -0
- package/dist/graph/view.js.map +1 -0
- package/dist/graph/views/dag.d.ts +10 -0
- package/dist/graph/views/dag.d.ts.map +1 -0
- package/dist/graph/views/dag.js +292 -0
- package/dist/graph/views/dag.js.map +1 -0
- package/dist/graph/views/foot.d.ts +8 -0
- package/dist/graph/views/foot.d.ts.map +1 -0
- package/dist/graph/views/foot.js +103 -0
- package/dist/graph/views/foot.js.map +1 -0
- package/dist/graph/views/node-table.d.ts +17 -0
- package/dist/graph/views/node-table.d.ts.map +1 -0
- package/dist/graph/views/node-table.js +80 -0
- package/dist/graph/views/node-table.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/json/index.d.ts +2 -0
- package/dist/json/index.d.ts.map +1 -0
- package/dist/json/index.js +3 -0
- package/dist/json/index.js.map +1 -0
- package/dist/json/json-tree.d.ts +21 -0
- package/dist/json/json-tree.d.ts.map +1 -0
- package/dist/json/json-tree.js +51 -0
- package/dist/json/json-tree.js.map +1 -0
- package/dist/lanes/index.d.ts +5 -0
- package/dist/lanes/index.d.ts.map +1 -0
- package/dist/lanes/index.js +7 -0
- package/dist/lanes/index.js.map +1 -0
- package/dist/lanes/lanes.d.ts +56 -0
- package/dist/lanes/lanes.d.ts.map +1 -0
- package/dist/lanes/lanes.js +160 -0
- package/dist/lanes/lanes.js.map +1 -0
- package/dist/lanes/stack.d.ts +26 -0
- package/dist/lanes/stack.d.ts.map +1 -0
- package/dist/lanes/stack.js +43 -0
- package/dist/lanes/stack.js.map +1 -0
- package/dist/lanes/styles.d.ts +2 -0
- package/dist/lanes/styles.d.ts.map +1 -0
- package/dist/lanes/styles.js +30 -0
- package/dist/lanes/styles.js.map +1 -0
- package/dist/lanes/types.d.ts +41 -0
- package/dist/lanes/types.d.ts.map +1 -0
- package/dist/lanes/types.js +6 -0
- package/dist/lanes/types.js.map +1 -0
- package/dist/markdown/cae-markdown.d.ts +37 -0
- package/dist/markdown/cae-markdown.d.ts.map +1 -0
- package/dist/markdown/cae-markdown.js +68 -0
- package/dist/markdown/cae-markdown.js.map +1 -0
- package/dist/markdown/highlight.d.ts +5 -0
- package/dist/markdown/highlight.d.ts.map +1 -0
- package/dist/markdown/highlight.js +86 -0
- package/dist/markdown/highlight.js.map +1 -0
- package/dist/markdown/index.d.ts +4 -0
- package/dist/markdown/index.d.ts.map +1 -0
- package/dist/markdown/index.js +6 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markdown/markdown.d.ts +4 -0
- package/dist/markdown/markdown.d.ts.map +1 -0
- package/dist/markdown/markdown.js +105 -0
- package/dist/markdown/markdown.js.map +1 -0
- package/dist/matrix/grid-step.d.ts +8 -0
- package/dist/matrix/grid-step.d.ts.map +1 -0
- package/dist/matrix/grid-step.js +69 -0
- package/dist/matrix/grid-step.js.map +1 -0
- package/dist/matrix/index.d.ts +5 -0
- package/dist/matrix/index.d.ts.map +1 -0
- package/dist/matrix/index.js +7 -0
- package/dist/matrix/index.js.map +1 -0
- package/dist/matrix/mark-matrix.d.ts +97 -0
- package/dist/matrix/mark-matrix.d.ts.map +1 -0
- package/dist/matrix/mark-matrix.js +272 -0
- package/dist/matrix/mark-matrix.js.map +1 -0
- package/dist/matrix/styles.d.ts +4 -0
- package/dist/matrix/styles.d.ts.map +1 -0
- package/dist/matrix/styles.js +47 -0
- package/dist/matrix/styles.js.map +1 -0
- package/dist/matrix/types.d.ts +79 -0
- package/dist/matrix/types.d.ts.map +1 -0
- package/dist/matrix/types.js +5 -0
- package/dist/matrix/types.js.map +1 -0
- package/dist/primitives/fact-line.d.ts +14 -0
- package/dist/primitives/fact-line.d.ts.map +1 -0
- package/dist/primitives/fact-line.js +18 -0
- package/dist/primitives/fact-line.js.map +1 -0
- package/dist/primitives/icon.d.ts +14 -0
- package/dist/primitives/icon.d.ts.map +1 -0
- package/dist/primitives/icon.js +19 -0
- package/dist/primitives/icon.js.map +1 -0
- package/dist/primitives/index.d.ts +10 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +13 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/primitives/legend.d.ts +28 -0
- package/dist/primitives/legend.d.ts.map +1 -0
- package/dist/primitives/legend.js +28 -0
- package/dist/primitives/legend.js.map +1 -0
- package/dist/primitives/pill.d.ts +23 -0
- package/dist/primitives/pill.d.ts.map +1 -0
- package/dist/primitives/pill.js +40 -0
- package/dist/primitives/pill.js.map +1 -0
- package/dist/primitives/roving-focus.d.ts +47 -0
- package/dist/primitives/roving-focus.d.ts.map +1 -0
- package/dist/primitives/roving-focus.js +111 -0
- package/dist/primitives/roving-focus.js.map +1 -0
- package/dist/primitives/segmented.d.ts +41 -0
- package/dist/primitives/segmented.d.ts.map +1 -0
- package/dist/primitives/segmented.js +88 -0
- package/dist/primitives/segmented.js.map +1 -0
- package/dist/primitives/share-bar.d.ts +37 -0
- package/dist/primitives/share-bar.d.ts.map +1 -0
- package/dist/primitives/share-bar.js +69 -0
- package/dist/primitives/share-bar.js.map +1 -0
- package/dist/primitives/states.d.ts +19 -0
- package/dist/primitives/states.d.ts.map +1 -0
- package/dist/primitives/states.js +11 -0
- package/dist/primitives/states.js.map +1 -0
- package/dist/primitives/window.d.ts +24 -0
- package/dist/primitives/window.d.ts.map +1 -0
- package/dist/primitives/window.js +56 -0
- package/dist/primitives/window.js.map +1 -0
- package/dist/styles/chips.d.ts +2 -0
- package/dist/styles/chips.d.ts.map +1 -0
- package/dist/styles/chips.js +18 -0
- package/dist/styles/chips.js.map +1 -0
- package/dist/styles/facts.d.ts +2 -0
- package/dist/styles/facts.d.ts.map +1 -0
- package/dist/styles/facts.js +7 -0
- package/dist/styles/facts.js.map +1 -0
- package/dist/styles/index.d.ts +16 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/index.js +15 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/styles/layout.d.ts +2 -0
- package/dist/styles/layout.d.ts.map +1 -0
- package/dist/styles/layout.js +33 -0
- package/dist/styles/layout.js.map +1 -0
- package/dist/styles/marks.d.ts +2 -0
- package/dist/styles/marks.d.ts.map +1 -0
- package/dist/styles/marks.js +30 -0
- package/dist/styles/marks.js.map +1 -0
- package/dist/styles/meter.d.ts +3 -0
- package/dist/styles/meter.d.ts.map +1 -0
- package/dist/styles/meter.js +9 -0
- package/dist/styles/meter.js.map +1 -0
- package/dist/styles/motion.d.ts +2 -0
- package/dist/styles/motion.d.ts.map +1 -0
- package/dist/styles/motion.js +27 -0
- package/dist/styles/motion.js.map +1 -0
- package/dist/styles/prose.d.ts +3 -0
- package/dist/styles/prose.d.ts.map +1 -0
- package/dist/styles/prose.js +59 -0
- package/dist/styles/prose.js.map +1 -0
- package/dist/styles/reset.d.ts +2 -0
- package/dist/styles/reset.d.ts.map +1 -0
- package/dist/styles/reset.js +13 -0
- package/dist/styles/reset.js.map +1 -0
- package/dist/styles/status.d.ts +2 -0
- package/dist/styles/status.d.ts.map +1 -0
- package/dist/styles/status.js +52 -0
- package/dist/styles/status.js.map +1 -0
- package/dist/styles/text.d.ts +2 -0
- package/dist/styles/text.d.ts.map +1 -0
- package/dist/styles/text.js +21 -0
- package/dist/styles/text.js.map +1 -0
- package/dist/styles/tokens.d.ts +8 -0
- package/dist/styles/tokens.d.ts.map +1 -0
- package/dist/styles/tokens.js +32 -0
- package/dist/styles/tokens.js.map +1 -0
- package/dist/table/data-table.d.ts +106 -0
- package/dist/table/data-table.d.ts.map +1 -0
- package/dist/table/data-table.js +408 -0
- package/dist/table/data-table.js.map +1 -0
- package/dist/table/index.d.ts +4 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/index.js +6 -0
- package/dist/table/index.js.map +1 -0
- package/dist/table/styles.d.ts +2 -0
- package/dist/table/styles.d.ts.map +1 -0
- package/dist/table/styles.js +32 -0
- package/dist/table/styles.js.map +1 -0
- package/dist/table/types.d.ts +48 -0
- package/dist/table/types.d.ts.map +1 -0
- package/dist/table/types.js +30 -0
- package/dist/table/types.js.map +1 -0
- package/dist/themes/boot.d.ts +7 -0
- package/dist/themes/boot.d.ts.map +1 -0
- package/dist/themes/boot.js +14 -0
- package/dist/themes/boot.js.map +1 -0
- package/dist/themes/choice.d.ts +16 -0
- package/dist/themes/choice.d.ts.map +1 -0
- package/dist/themes/choice.js +45 -0
- package/dist/themes/choice.js.map +1 -0
- package/dist/themes/data.d.ts +43 -0
- package/dist/themes/data.d.ts.map +1 -0
- package/dist/themes/data.js +408 -0
- package/dist/themes/data.js.map +1 -0
- package/dist/themes/index.d.ts +7 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +11 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/themes/sheet.d.ts +16 -0
- package/dist/themes/sheet.d.ts.map +1 -0
- package/dist/themes/sheet.js +28 -0
- package/dist/themes/sheet.js.map +1 -0
- package/dist/themes/token-meanings.d.ts +3 -0
- package/dist/themes/token-meanings.d.ts.map +1 -0
- package/dist/themes/token-meanings.js +67 -0
- package/dist/themes/token-meanings.js.map +1 -0
- package/dist/themes/tokens-list.d.ts +14 -0
- package/dist/themes/tokens-list.d.ts.map +1 -0
- package/dist/themes/tokens-list.js +69 -0
- package/dist/themes/tokens-list.js.map +1 -0
- package/dist/tiles/index.d.ts +4 -0
- package/dist/tiles/index.d.ts.map +1 -0
- package/dist/tiles/index.js +6 -0
- package/dist/tiles/index.js.map +1 -0
- package/dist/tiles/stat-tiles.d.ts +40 -0
- package/dist/tiles/stat-tiles.d.ts.map +1 -0
- package/dist/tiles/stat-tiles.js +99 -0
- package/dist/tiles/stat-tiles.js.map +1 -0
- package/dist/tiles/styles.d.ts +2 -0
- package/dist/tiles/styles.d.ts.map +1 -0
- package/dist/tiles/styles.js +23 -0
- package/dist/tiles/styles.js.map +1 -0
- package/dist/tiles/types.d.ts +22 -0
- package/dist/tiles/types.d.ts.map +1 -0
- package/dist/tiles/types.js +2 -0
- package/dist/tiles/types.js.map +1 -0
- package/dist/transcript/follow-scroll.d.ts +21 -0
- package/dist/transcript/follow-scroll.d.ts.map +1 -0
- package/dist/transcript/follow-scroll.js +76 -0
- package/dist/transcript/follow-scroll.js.map +1 -0
- package/dist/transcript/index.d.ts +5 -0
- package/dist/transcript/index.d.ts.map +1 -0
- package/dist/transcript/index.js +7 -0
- package/dist/transcript/index.js.map +1 -0
- package/dist/transcript/styles.d.ts +2 -0
- package/dist/transcript/styles.d.ts.map +1 -0
- package/dist/transcript/styles.js +114 -0
- package/dist/transcript/styles.js.map +1 -0
- package/dist/transcript/tags.d.ts +6 -0
- package/dist/transcript/tags.d.ts.map +1 -0
- package/dist/transcript/tags.js +13 -0
- package/dist/transcript/tags.js.map +1 -0
- package/dist/transcript/tool-card.d.ts +16 -0
- package/dist/transcript/tool-card.d.ts.map +1 -0
- package/dist/transcript/tool-card.js +87 -0
- package/dist/transcript/tool-card.js.map +1 -0
- package/dist/transcript/transcript.d.ts +103 -0
- package/dist/transcript/transcript.d.ts.map +1 -0
- package/dist/transcript/transcript.js +397 -0
- package/dist/transcript/transcript.js.map +1 -0
- package/dist/transcript/types.d.ts +161 -0
- package/dist/transcript/types.d.ts.map +1 -0
- package/dist/transcript/types.js +25 -0
- package/dist/transcript/types.js.map +1 -0
- package/docs/charts.md +58 -0
- package/docs/check-table.md +40 -0
- package/docs/data-table.md +57 -0
- package/docs/examples/charts.ts +61 -0
- package/docs/examples/check-table.ts +19 -0
- package/docs/examples/ci-pipeline.ts +271 -0
- package/docs/examples/data-table.ts +39 -0
- package/docs/examples/graph.ts +57 -0
- package/docs/examples/lanes.ts +40 -0
- package/docs/examples/matrix.ts +56 -0
- package/docs/examples/page.css +34 -0
- package/docs/examples/segmented.ts +18 -0
- package/docs/examples/status.ts +30 -0
- package/docs/examples/theme.css +54 -0
- package/docs/examples/tiles.ts +27 -0
- package/docs/examples/transcript.ts +59 -0
- package/docs/graph.md +112 -0
- package/docs/lanes.md +48 -0
- package/docs/matrix.md +34 -0
- package/docs/page.md +87 -0
- package/docs/primitives.md +57 -0
- package/docs/strings.md +16 -0
- package/docs/theming.md +147 -0
- package/docs/tiles.md +40 -0
- package/docs/transcript.md +73 -0
- package/docs/tsconfig.json +20 -0
- package/package.json +161 -4
- package/src/charts/bar-chart.ts +74 -0
- package/src/charts/base.ts +188 -0
- package/src/charts/column-chart.ts +153 -0
- package/src/charts/heatmap.ts +129 -0
- package/src/charts/index.ts +10 -0
- package/src/charts/line-chart.ts +128 -0
- package/src/charts/scale.ts +61 -0
- package/src/charts/state-grid.ts +245 -0
- package/src/charts/types.ts +48 -0
- package/src/checks/check-table.ts +85 -0
- package/src/checks/index.ts +5 -0
- package/src/checks/styles.ts +31 -0
- package/src/checks/types.ts +48 -0
- package/src/core/clock.ts +46 -0
- package/src/core/define.ts +7 -0
- package/src/core/events.ts +89 -0
- package/src/core/format.ts +25 -0
- package/src/core/frame.ts +5 -0
- package/src/core/strings.ts +11 -0
- package/src/core/tone.ts +14 -0
- package/src/count/count.ts +54 -0
- package/src/count/elapsed.ts +33 -0
- package/src/count/index.ts +4 -0
- package/src/define/all.ts +13 -0
- package/src/define/charts.ts +17 -0
- package/src/define/check-table.ts +9 -0
- package/src/define/count.ts +11 -0
- package/src/define/data-table.ts +9 -0
- package/src/define/graph.ts +11 -0
- package/src/define/json-tree.ts +9 -0
- package/src/define/lanes.ts +9 -0
- package/src/define/markdown.ts +9 -0
- package/src/define/matrix.ts +9 -0
- package/src/define/segmented.ts +9 -0
- package/src/define/tiles.ts +11 -0
- package/src/define/transcript.ts +9 -0
- package/src/graph/arrange.ts +399 -0
- package/src/graph/card.ts +209 -0
- package/src/graph/host.ts +100 -0
- package/src/graph/icons.ts +23 -0
- package/src/graph/index.ts +16 -0
- package/src/graph/layered-graph.ts +417 -0
- package/src/graph/layout.ts +331 -0
- package/src/graph/marks.ts +42 -0
- package/src/graph/nav.ts +19 -0
- package/src/graph/pan-zoom.ts +219 -0
- package/src/graph/paths.ts +41 -0
- package/src/graph/place-card.ts +93 -0
- package/src/graph/strings.ts +86 -0
- package/src/graph/styles.ts +204 -0
- package/src/graph/types.ts +202 -0
- package/src/graph/view.ts +28 -0
- package/src/graph/views/dag.ts +294 -0
- package/src/graph/views/foot.ts +109 -0
- package/src/graph/views/node-table.ts +90 -0
- package/src/index.ts +25 -0
- package/src/json/index.ts +2 -0
- package/src/json/json-tree.ts +49 -0
- package/src/lanes/index.ts +6 -0
- package/src/lanes/lanes.ts +143 -0
- package/src/lanes/stack.ts +51 -0
- package/src/lanes/styles.ts +30 -0
- package/src/lanes/types.ts +51 -0
- package/src/markdown/cae-markdown.ts +64 -0
- package/src/markdown/highlight.ts +94 -0
- package/src/markdown/index.ts +5 -0
- package/src/markdown/markdown.ts +110 -0
- package/src/matrix/grid-step.ts +58 -0
- package/src/matrix/index.ts +6 -0
- package/src/matrix/mark-matrix.ts +285 -0
- package/src/matrix/styles.ts +48 -0
- package/src/matrix/types.ts +83 -0
- package/src/primitives/fact-line.ts +26 -0
- package/src/primitives/icon.ts +26 -0
- package/src/primitives/index.ts +12 -0
- package/src/primitives/legend.ts +56 -0
- package/src/primitives/pill.ts +58 -0
- package/src/primitives/roving-focus.ts +138 -0
- package/src/primitives/segmented.ts +92 -0
- package/src/primitives/share-bar.ts +94 -0
- package/src/primitives/states.ts +28 -0
- package/src/primitives/window.ts +65 -0
- package/src/styles/chips.ts +18 -0
- package/src/styles/facts.ts +7 -0
- package/src/styles/index.ts +19 -0
- package/src/styles/layout.ts +33 -0
- package/src/styles/marks.ts +30 -0
- package/src/styles/meter.ts +9 -0
- package/src/styles/motion.ts +27 -0
- package/src/styles/prose.ts +59 -0
- package/src/styles/reset.ts +13 -0
- package/src/styles/status.ts +52 -0
- package/src/styles/text.ts +21 -0
- package/src/styles/tokens.ts +35 -0
- package/src/table/data-table.ts +367 -0
- package/src/table/index.ts +5 -0
- package/src/table/styles.ts +32 -0
- package/src/table/types.ts +73 -0
- package/src/themes/boot.ts +14 -0
- package/src/themes/choice.ts +47 -0
- package/src/themes/data.ts +433 -0
- package/src/themes/index.ts +10 -0
- package/src/themes/sheet.ts +29 -0
- package/src/themes/token-meanings.ts +67 -0
- package/src/themes/tokens-list.ts +82 -0
- package/src/tiles/index.ts +5 -0
- package/src/tiles/stat-tiles.ts +92 -0
- package/src/tiles/styles.ts +23 -0
- package/src/tiles/types.ts +20 -0
- package/src/transcript/follow-scroll.ts +72 -0
- package/src/transcript/index.ts +6 -0
- package/src/transcript/styles.ts +114 -0
- package/src/transcript/tags.ts +18 -0
- package/src/transcript/tool-card.ts +103 -0
- package/src/transcript/transcript.ts +358 -0
- package/src/transcript/types.ts +167 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// The drawing of a layered graph: coordinates from the measured sizes, and each
|
|
2
|
+
// edge as straight runs that bend only between layers.
|
|
3
|
+
import { isPoint } from './arrange.js';
|
|
4
|
+
import type { Arrangement, EdgeStyle, EdgePathStep } from './types.js';
|
|
5
|
+
|
|
6
|
+
export interface Size {
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** A rectangle in the drawing. */
|
|
12
|
+
export interface Rect {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
w: number;
|
|
16
|
+
h: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** A straight stretch of an edge along the flow, from `a` to `b`, `at` across it; an edge bends between two. */
|
|
20
|
+
export interface Stretch {
|
|
21
|
+
a: number;
|
|
22
|
+
b: number;
|
|
23
|
+
at: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface GraphLayout {
|
|
27
|
+
/** The drawing's size, its margin included. */
|
|
28
|
+
w: number;
|
|
29
|
+
h: number;
|
|
30
|
+
/** Every node, stub and cluster, by id. */
|
|
31
|
+
at: Record<string, Rect>;
|
|
32
|
+
/** Every edge path, with its runs. */
|
|
33
|
+
edges: { from: string; to: string; style: EdgeStyle; runs: Stretch[] }[];
|
|
34
|
+
/** Each cycling cluster's return to its next round. */
|
|
35
|
+
returns: LoopReturn[];
|
|
36
|
+
down: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A cluster's return to its next round, along the flow (`u`) and across it (`v`): from
|
|
41
|
+
* the far side of its last layer's outermost item (`from`, at `v0`) out to a band
|
|
42
|
+
* past its content (`band`), back along the band, and in to its first layer's
|
|
43
|
+
* outermost item (`to`, at `v1`). Left to right the band is under the nodes; top to
|
|
44
|
+
* bottom, on their right.
|
|
45
|
+
*/
|
|
46
|
+
export interface LoopReturn {
|
|
47
|
+
box: string;
|
|
48
|
+
from: number;
|
|
49
|
+
v0: number;
|
|
50
|
+
to: number;
|
|
51
|
+
v1: number;
|
|
52
|
+
band: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The spaces of a drawing; left to right unless `down`. */
|
|
56
|
+
export interface LayoutSpace {
|
|
57
|
+
/** Between two layers, at the top level and in a cluster. */
|
|
58
|
+
gap: number;
|
|
59
|
+
boxGap: number;
|
|
60
|
+
/** Between two items of a layer, and next to a point an edge passes. */
|
|
61
|
+
sep: number;
|
|
62
|
+
pointSep: number;
|
|
63
|
+
/** A cluster's padding, and the space under its label. */
|
|
64
|
+
pad: number;
|
|
65
|
+
/** The space after a box's content, across the flow: the band its return to the next round runs in. */
|
|
66
|
+
padEnd: number;
|
|
67
|
+
margin: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The band past a cluster's content that its return to the next round runs in, and the return's corner radius. */
|
|
71
|
+
export const RETURN_BAND = 24;
|
|
72
|
+
export const RETURN_R = 6;
|
|
73
|
+
|
|
74
|
+
export const SPACE: Record<'across' | 'down', LayoutSpace> = {
|
|
75
|
+
across: { gap: 56, boxGap: 40, sep: 16, pointSep: 12, pad: 12, padEnd: RETURN_BAND, margin: 16 },
|
|
76
|
+
down: { gap: 40, boxGap: 32, sep: 16, pointSep: 12, pad: 10, padEnd: RETURN_BAND, margin: 16 },
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** Tops (across the flow) nearest to `want` that keep the order and each gap: weighted isotonic regression (pool adjacent violators). */
|
|
80
|
+
function placeRow(want: readonly number[], gaps: readonly number[], weight: readonly number[]): number[] {
|
|
81
|
+
const off: number[] = [0];
|
|
82
|
+
for (let i = 1; i < want.length; i++) off.push((off[i - 1] as number) + (gaps[i - 1] as number));
|
|
83
|
+
const blocks: { sum: number; w: number; n: number }[] = [];
|
|
84
|
+
want.forEach((v, i) => {
|
|
85
|
+
const w = weight[i] ?? 1;
|
|
86
|
+
blocks.push({ sum: w * (v - (off[i] as number)), w, n: 1 });
|
|
87
|
+
while (blocks.length > 1) {
|
|
88
|
+
const b = blocks[blocks.length - 1] as { sum: number; w: number; n: number };
|
|
89
|
+
const a = blocks[blocks.length - 2] as { sum: number; w: number; n: number };
|
|
90
|
+
if (a.sum / a.w <= b.sum / b.w) break;
|
|
91
|
+
blocks.splice(-2, 2, { sum: a.sum + b.sum, w: a.w + b.w, n: a.n + b.n });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const out: number[] = [];
|
|
95
|
+
for (const b of blocks) for (let k = 0; k < b.n; k++) out.push(b.sum / b.w + (off[out.length] as number));
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Where everything of a layered graph is drawn, from the sizes of its nodes, its stubs
|
|
101
|
+
* and its clusters' labels (`lbl:<box id>`): left to right, or top to bottom when
|
|
102
|
+
* `down`. Each container is laid out inside out, a box being one item of its parent:
|
|
103
|
+
* its layers side by side, an item at the start of its layer; across the flow, each
|
|
104
|
+
* item as near as its order allows to the items it is joined to (sweeps of weighted
|
|
105
|
+
* isotonic regression), so chains run straight. An edge runs along its points and
|
|
106
|
+
* bends only in the gap between two layers; it enters and leaves a box where its
|
|
107
|
+
* point inside is, and never passes a node or a box's label.
|
|
108
|
+
*/
|
|
109
|
+
export function layoutGraph(g: Pick<Arrangement, 'clusters' | 'slots' | 'edgePaths'>, sizes: ReadonlyMap<string, Size>, down = false): GraphLayout {
|
|
110
|
+
const S = SPACE[down ? 'down' : 'across'];
|
|
111
|
+
const len = (s: Size) => (down ? s.h : s.w);
|
|
112
|
+
const brd = (s: Size) => (down ? s.w : s.h);
|
|
113
|
+
const boxIds = new Set(g.clusters.map((b) => b.id));
|
|
114
|
+
const parentOf = new Map(g.clusters.map((b) => [b.id, b.parent]));
|
|
115
|
+
const label = (box: string): Size => sizes.get(`lbl:${box}`) ?? { w: 0, h: 0 };
|
|
116
|
+
// A box's padding: along the flow, and across it. Its label is in a band at its top: across
|
|
117
|
+
// the flow left to right; along it top to bottom, where a gap under the label lets edges in.
|
|
118
|
+
const padOf = (box: string) =>
|
|
119
|
+
down
|
|
120
|
+
? { u0: label(box).h + S.pad + S.gap / 2, u1: S.pad, v0: S.pad, v1: S.padEnd, minU: 0, minV: label(box).w + 2 * S.pad }
|
|
121
|
+
: { u0: S.pad, u1: S.pad, v0: label(box).h + S.pad, v1: S.padEnd, minU: label(box).w + 2 * S.pad, minV: 0 };
|
|
122
|
+
const extent = new Map<string, { u: number; v: number }>(); // a box's size, along and across
|
|
123
|
+
const slotSize = (id: string): { u: number; v: number } => {
|
|
124
|
+
if (boxIds.has(id)) return extent.get(id) ?? { u: 0, v: 0 };
|
|
125
|
+
if (isPoint(id)) return { u: 0, v: 0 };
|
|
126
|
+
const s = sizes.get(id) ?? { w: 0, h: 0 };
|
|
127
|
+
return { u: len(s), v: brd(s) };
|
|
128
|
+
};
|
|
129
|
+
interface Local {
|
|
130
|
+
rows: string[][];
|
|
131
|
+
start: number[];
|
|
132
|
+
width: number[];
|
|
133
|
+
top: Map<string, number>;
|
|
134
|
+
u: number;
|
|
135
|
+
v: number;
|
|
136
|
+
}
|
|
137
|
+
const local = new Map<string, Local>();
|
|
138
|
+
const layerIn = new Map<string, Map<string, number>>();
|
|
139
|
+
const depth = (c: string): number => (c === '' ? 0 : 1 + depth(parentOf.get(c) ?? ''));
|
|
140
|
+
const containers = ['', ...g.clusters.map((b) => b.id)].sort((a, b) => depth(b) - depth(a));
|
|
141
|
+
// Where an edge meets a slot, across the flow from the slot's top: a node's middle,
|
|
142
|
+
// a point, or (a box) where the edge's step inside it is.
|
|
143
|
+
const attach = (ri: number, si: number): number => {
|
|
144
|
+
const steps = g.edgePaths[ri]?.steps ?? [];
|
|
145
|
+
const s = steps[si] as EdgePathStep;
|
|
146
|
+
if (isPoint(s.id)) return 0;
|
|
147
|
+
if (!boxIds.has(s.id)) return slotSize(s.id).v / 2;
|
|
148
|
+
const inner = steps[si - 1]?.in === s.id ? si - 1 : si + 1;
|
|
149
|
+
const step = steps[inner] as EdgePathStep;
|
|
150
|
+
return padOf(s.id).v0 + (local.get(s.id)?.top.get(step.id) ?? 0) + attach(ri, inner);
|
|
151
|
+
};
|
|
152
|
+
for (const c of containers) {
|
|
153
|
+
const rows = (g.slots[c] ?? []).map((r) => [...r]);
|
|
154
|
+
layerIn.set(c, new Map(rows.flatMap((r, i) => r.map((id) => [id, i] as const))));
|
|
155
|
+
const width = rows.map((r) => Math.max(0, ...r.map((id) => slotSize(id).u)));
|
|
156
|
+
const gap = c === '' ? S.gap : S.boxGap;
|
|
157
|
+
const start: number[] = [];
|
|
158
|
+
width.forEach((_, i) => start.push(i ? (start[i - 1] as number) + (width[i - 1] as number) + gap : 0));
|
|
159
|
+
// The joins inside this container: slot a of layer l to slot b of layer l + 1.
|
|
160
|
+
const links: { a: string; b: string; oa: number; ob: number }[] = [];
|
|
161
|
+
g.edgePaths.forEach((r, ri) =>
|
|
162
|
+
r.steps.forEach((s, si) => {
|
|
163
|
+
const p = r.steps[si - 1];
|
|
164
|
+
if (si && p && p.in === c && s.in === c && layerIn.get(c)?.get(p.id) !== layerIn.get(c)?.get(s.id)) links.push({ a: p.id, b: s.id, oa: attach(ri, si - 1), ob: attach(ri, si) });
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
167
|
+
// Points sit close to each other and to a node, further from a box (its next-pass mark is on its edge).
|
|
168
|
+
const sepOf = (a: string, b: string | undefined) =>
|
|
169
|
+
b === undefined ? 0 : boxIds.has(a) || boxIds.has(b) || !(isPoint(a) || isPoint(b)) ? S.sep : S.pointSep;
|
|
170
|
+
const top = new Map<string, number>();
|
|
171
|
+
for (const r of rows) {
|
|
172
|
+
let v = 0;
|
|
173
|
+
r.forEach((id, i) => {
|
|
174
|
+
top.set(id, v);
|
|
175
|
+
v += slotSize(id).v + sepOf(id, r[i + 1]);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const place = (r: string[], want: (id: string) => number[]) => {
|
|
179
|
+
const desired = r.map((id) => {
|
|
180
|
+
const xs = want(id);
|
|
181
|
+
return xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : (top.get(id) ?? 0);
|
|
182
|
+
});
|
|
183
|
+
const gaps = r.map((id, i) => slotSize(id).v + sepOf(id, r[i + 1]));
|
|
184
|
+
// The nodes first, as if the points between them were fixed in size; then the points, around the nodes.
|
|
185
|
+
const real = r.map((id, i) => (isPoint(id) ? -1 : i)).filter((i) => i >= 0);
|
|
186
|
+
const span = (i: number, j: number) => gaps.slice(i, j).reduce((a, b) => a + b, 0);
|
|
187
|
+
const at = placeRow(
|
|
188
|
+
real.map((i) => desired[i] as number),
|
|
189
|
+
real.map((i, k) => (k + 1 < real.length ? span(i, real[k + 1] as number) : 0)),
|
|
190
|
+
real.map(() => 1),
|
|
191
|
+
);
|
|
192
|
+
real.forEach((i, k) => (desired[i] = at[k] as number));
|
|
193
|
+
placeRow(desired, gaps, r.map((id) => (isPoint(id) ? 1 : 1e6))).forEach((v, i) => top.set(r[i] as string, Math.round(v * 100) / 100));
|
|
194
|
+
};
|
|
195
|
+
// A node lines up with the nodes it is joined to, when it has any on that side, else with the points;
|
|
196
|
+
// a point lines up with whatever it is joined to, so a chain runs straight and a long edge goes round it.
|
|
197
|
+
const near = (id: string, ls: { other: string; at: number }[]) => {
|
|
198
|
+
const real = isPoint(id) ? ls : ls.filter((x) => !isPoint(x.other));
|
|
199
|
+
return (real.length ? real : ls).map((x) => x.at);
|
|
200
|
+
};
|
|
201
|
+
const fromPrev = (id: string) => near(id, links.filter((l) => l.b === id).map((l) => ({ other: l.a, at: (top.get(l.a) ?? 0) + l.oa - l.ob })));
|
|
202
|
+
const fromNext = (id: string) => near(id, links.filter((l) => l.a === id).map((l) => ({ other: l.b, at: (top.get(l.b) ?? 0) + l.ob - l.oa })));
|
|
203
|
+
for (let sweep = 0; sweep < 8; sweep++) {
|
|
204
|
+
const order = rows.map((_, i) => i);
|
|
205
|
+
if (sweep % 2) order.reverse();
|
|
206
|
+
for (const i of order) place(rows[i] as string[], sweep % 2 ? fromNext : fromPrev);
|
|
207
|
+
}
|
|
208
|
+
for (const r of rows) place(r, (id) => [...fromPrev(id), ...fromNext(id)]);
|
|
209
|
+
const lo = top.size ? Math.min(...top.values()) : 0;
|
|
210
|
+
for (const [id, v] of top) top.set(id, v - lo);
|
|
211
|
+
const u = (start[start.length - 1] ?? 0) + (width[width.length - 1] ?? 0);
|
|
212
|
+
const v = Math.max(0, ...rows.flat().map((id) => (top.get(id) ?? 0) + slotSize(id).v));
|
|
213
|
+
local.set(c, { rows, start, width, top, u, v });
|
|
214
|
+
if (c !== '') {
|
|
215
|
+
const p = padOf(c);
|
|
216
|
+
extent.set(c, { u: Math.max(p.u0 + u + p.u1, p.minU), v: Math.max(p.v0 + v + p.v1, p.minV) });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Inside out, then outside in: each container's place in the drawing.
|
|
221
|
+
const at: Record<string, Rect> = {};
|
|
222
|
+
const origin = new Map<string, { u: number; v: number }>();
|
|
223
|
+
const rect = (u: number, v: number, s: { u: number; v: number }): Rect => (down ? { x: v, y: u, w: s.v, h: s.u } : { x: u, y: v, w: s.u, h: s.v });
|
|
224
|
+
const put = (c: string, ou: number, ov: number) => {
|
|
225
|
+
origin.set(c, { u: ou, v: ov });
|
|
226
|
+
const l = local.get(c) as Local;
|
|
227
|
+
l.rows.forEach((r, i) =>
|
|
228
|
+
r.forEach((id) => {
|
|
229
|
+
if (isPoint(id)) return;
|
|
230
|
+
const u = ou + (l.start[i] as number);
|
|
231
|
+
const v = ov + (l.top.get(id) ?? 0);
|
|
232
|
+
at[id] = rect(u, v, slotSize(id));
|
|
233
|
+
if (boxIds.has(id)) {
|
|
234
|
+
const p = padOf(id);
|
|
235
|
+
put(id, u + p.u0, v + p.v0);
|
|
236
|
+
}
|
|
237
|
+
}),
|
|
238
|
+
);
|
|
239
|
+
};
|
|
240
|
+
put('', S.margin, S.margin);
|
|
241
|
+
const whole = local.get('') as Local;
|
|
242
|
+
const U = whole.u + 2 * S.margin;
|
|
243
|
+
const V = whole.v + 2 * S.margin;
|
|
244
|
+
|
|
245
|
+
// A container's layer l: where it starts and ends along the flow, in the drawing.
|
|
246
|
+
const col = (c: string, l: number): [number, number] => {
|
|
247
|
+
const lc = local.get(c) as Local;
|
|
248
|
+
const o = origin.get(c) ?? { u: 0, v: 0 };
|
|
249
|
+
const c0 = o.u + (lc.start[l] ?? 0);
|
|
250
|
+
return [c0, c0 + (lc.width[l] ?? 0)];
|
|
251
|
+
};
|
|
252
|
+
const layer = (c: string, id: string) => layerIn.get(c)?.get(id) ?? 0;
|
|
253
|
+
const byOf = (s: EdgePathStep | undefined): string => (s && isPoint(s.id) ? (/\|by:([^|]*)\|/.exec(s.id)?.[1] ?? '') : '');
|
|
254
|
+
const edges = g.edgePaths.map((r, ri) => {
|
|
255
|
+
const vOf = (si: number): number => {
|
|
256
|
+
const s = r.steps[si] as EdgePathStep;
|
|
257
|
+
const o = origin.get(s.in) ?? { u: 0, v: 0 };
|
|
258
|
+
return o.v + (local.get(s.in)?.top.get(s.id) ?? 0) + attach(ri, si);
|
|
259
|
+
};
|
|
260
|
+
const runs: Stretch[] = r.steps.map((s, si): Stretch => {
|
|
261
|
+
const prev = r.steps[si - 1];
|
|
262
|
+
const next = r.steps[si + 1];
|
|
263
|
+
const [c0, c1] = col(s.in, layer(s.in, s.id));
|
|
264
|
+
const v = vOf(si);
|
|
265
|
+
if (si === 0) return { a: c0 + slotSize(s.id).u, b: c1, at: v };
|
|
266
|
+
if (si === r.steps.length - 1) return { a: c0, b: c0, at: v };
|
|
267
|
+
if (isPoint(s.id)) {
|
|
268
|
+
const box = byOf(s);
|
|
269
|
+
// Beside a box: from where it left the box's side, or up to where it goes in.
|
|
270
|
+
if (box && prev?.id === box) return { a: col(box, layer(box, r.steps[si - 2]?.id ?? '') + 1)[0], b: c1, at: v };
|
|
271
|
+
if (box && next?.id === box) {
|
|
272
|
+
const ln = layer(box, r.steps[si + 2]?.id ?? '');
|
|
273
|
+
const gap = ln > 0 ? col(box, ln - 1)[1] : (at[box] ? (down ? at[box].y : at[box].x) : c0) + label(box).h + S.pad;
|
|
274
|
+
return { a: c0, b: gap, at: v };
|
|
275
|
+
}
|
|
276
|
+
return { a: c0, b: c1, at: v };
|
|
277
|
+
}
|
|
278
|
+
const p = padOf(s.id);
|
|
279
|
+
const inner = local.get(s.id) as Local;
|
|
280
|
+
// Out of a box: at its far side, or through its side after the layer it leaves.
|
|
281
|
+
if (prev?.in === s.id) {
|
|
282
|
+
if (byOf(next) === s.id) {
|
|
283
|
+
const u = col(s.id, layer(s.id, prev.id) + 1)[0];
|
|
284
|
+
return { a: u, b: u, at: vOf(si + 1) };
|
|
285
|
+
}
|
|
286
|
+
return { a: c0 + p.u0 + inner.u, b: c1, at: v };
|
|
287
|
+
}
|
|
288
|
+
// Into a box: at its near side, or through its side before the layer it goes to.
|
|
289
|
+
if (byOf(prev) === s.id) {
|
|
290
|
+
const u = col(s.id, layer(s.id, next?.id ?? ''))[0];
|
|
291
|
+
return { a: u, b: u, at: vOf(si + 1) };
|
|
292
|
+
}
|
|
293
|
+
return { a: c0, b: c0 + p.u0, at: v };
|
|
294
|
+
});
|
|
295
|
+
return { from: r.from, to: r.to, style: r.style, runs };
|
|
296
|
+
});
|
|
297
|
+
// Each box's return to its next round: from its last layer's item that reaches furthest
|
|
298
|
+
// across the flow, along the band past the content, to its first layer's.
|
|
299
|
+
const returns = g.clusters.flatMap((b): LoopReturn[] => {
|
|
300
|
+
if (!b.cycle) return [];
|
|
301
|
+
const l = local.get(b.id);
|
|
302
|
+
const o = origin.get(b.id);
|
|
303
|
+
if (!l || !o) return [];
|
|
304
|
+
const outer = (row: readonly string[] | undefined) => {
|
|
305
|
+
const ids = (row ?? []).filter((id) => !isPoint(id));
|
|
306
|
+
let best: { u: number; v: number } | null = null;
|
|
307
|
+
for (const id of ids) {
|
|
308
|
+
const s = slotSize(id);
|
|
309
|
+
const v = (l.top.get(id) ?? 0) + s.v;
|
|
310
|
+
if (!best || v > best.v) best = { u: s.u, v };
|
|
311
|
+
}
|
|
312
|
+
return best;
|
|
313
|
+
};
|
|
314
|
+
const real = l.rows.map((r, i) => (r.some((id) => !isPoint(id)) ? i : -1)).filter((i) => i >= 0);
|
|
315
|
+
const fi = real[0] ?? 0;
|
|
316
|
+
const li = real[real.length - 1] ?? 0;
|
|
317
|
+
const last = outer(l.rows[li]);
|
|
318
|
+
const first = outer(l.rows[fi]);
|
|
319
|
+
if (!last || !first) return [];
|
|
320
|
+
// An item's middle along the flow, as wide as its layer; one layer: either side of its middle.
|
|
321
|
+
let from = o.u + (l.start[li] ?? 0) + (l.width[li] ?? 0) / 2;
|
|
322
|
+
let to = o.u + (l.start[fi] ?? 0) + (l.width[fi] ?? 0) / 2;
|
|
323
|
+
if (from - to < 4 * RETURN_R) {
|
|
324
|
+
const d = Math.max(2 * RETURN_R, Math.min(28, Math.min(last.u, first.u) / 4));
|
|
325
|
+
from = (from + to) / 2 + d;
|
|
326
|
+
to = from - 2 * d;
|
|
327
|
+
}
|
|
328
|
+
return [{ box: b.id, from, v0: o.v + last.v, to, v1: o.v + first.v, band: o.v + l.v + RETURN_BAND / 2 }];
|
|
329
|
+
});
|
|
330
|
+
return { w: down ? V : U, h: down ? U : V, at, edges, returns, down };
|
|
331
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// How a node draws its marks: a grid, or a bar of counts when it has many; and
|
|
2
|
+
// what a node without `counts` or `progress` shows.
|
|
3
|
+
import type { StateStyles } from '../primitives/states.js';
|
|
4
|
+
import type { GraphNode, GraphNodeInput } from './types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A node with its defaults: no marks, its marks by state as its counts, and as its
|
|
8
|
+
* progress its marks whose state has the tone `done`, of all its marks, in the words
|
|
9
|
+
* of `words`.
|
|
10
|
+
*/
|
|
11
|
+
export function fillNode(n: GraphNodeInput, states: StateStyles, words: (done: number, total: number) => string): GraphNode {
|
|
12
|
+
const { marks = [], loose = [] } = n;
|
|
13
|
+
const counts: Record<string, number> = {};
|
|
14
|
+
let done = 0;
|
|
15
|
+
let total = 0;
|
|
16
|
+
for (const m of [...marks, ...loose]) {
|
|
17
|
+
if (!m) continue;
|
|
18
|
+
counts[m.state] = (counts[m.state] ?? 0) + 1;
|
|
19
|
+
total++;
|
|
20
|
+
if (states[m.state]?.tone === 'done') done++;
|
|
21
|
+
}
|
|
22
|
+
return { ...n, marks, loose, counts: n.counts ?? counts, progress: n.progress ?? { done, total, label: words(done, total) } };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** How a node lays out its series marks: as many columns as series up to 12, then 10, then 20; smaller marks as they grow. */
|
|
26
|
+
export function markGrid(series: number): { cols: number; size: number } {
|
|
27
|
+
if (series <= 12) return { cols: Math.max(1, series), size: 12 };
|
|
28
|
+
if (series <= 100) return { cols: 10, size: 10 };
|
|
29
|
+
return { cols: 20, size: 6 };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Above this many marks, a node shows a bar of its counts and lists its failed marks. */
|
|
33
|
+
export const BAR_MARKS = 200;
|
|
34
|
+
|
|
35
|
+
/** A node shows its marks as a bar of counts: it has more than `at` marks. */
|
|
36
|
+
export const asBar = (n: Pick<GraphNode, 'marks' | 'loose'>, at = BAR_MARKS): boolean => n.marks.filter((m) => m !== null).length + n.loose.length > at;
|
|
37
|
+
|
|
38
|
+
/** A node's bar: each state's share of its items, in `order`, the states it has only. */
|
|
39
|
+
export function barParts(counts: Readonly<Record<string, number>>, order: readonly string[]): { state: string; n: number; pct: number }[] {
|
|
40
|
+
const total = order.reduce((a, s) => a + (counts[s] ?? 0), 0);
|
|
41
|
+
return order.filter((s) => (counts[s] ?? 0) > 0).map((s) => ({ state: s, n: counts[s] ?? 0, pct: (100 * (counts[s] ?? 0)) / Math.max(1, total) }));
|
|
42
|
+
}
|
package/src/graph/nav.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Keyboard moves along a drawn edge.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The node joined to `from` along a drawn edge: a later one (`later`, that waits for
|
|
5
|
+
* it or was created after it) or an earlier one; of several, the one nearest across
|
|
6
|
+
* the flow (`across`), then the first in drawing order. Null when there is none.
|
|
7
|
+
*/
|
|
8
|
+
export function alongEdge(
|
|
9
|
+
g: { drawn: readonly string[]; edges: readonly { from: string; to: string }[]; back: readonly boolean[] },
|
|
10
|
+
from: string,
|
|
11
|
+
dir: 'later' | 'earlier',
|
|
12
|
+
across: (id: string) => number,
|
|
13
|
+
): string | null {
|
|
14
|
+
const rank = new Map(g.drawn.map((id, i) => [id, i]));
|
|
15
|
+
const ids = [...new Set(g.edges.filter((e, i) => !g.back[i] && (dir === 'later' ? e.from === from : e.to === from)).map((e) => (dir === 'later' ? e.to : e.from)))];
|
|
16
|
+
const at = across(from);
|
|
17
|
+
ids.sort((a, b) => Math.abs(across(a) - at) - Math.abs(across(b) - at) || (rank.get(a) ?? 0) - (rank.get(b) ?? 0));
|
|
18
|
+
return ids[0] ?? null;
|
|
19
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// The graph's frame: fit the drawing in it, zoom, and pan. The view is written on
|
|
2
|
+
// the stage directly, so panning and zooming do not render the element again.
|
|
3
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
4
|
+
import { FRAME_MIN, FRAME_SHARE } from '../core/frame.js';
|
|
5
|
+
import { fitView, zoomView, type View } from './view.js';
|
|
6
|
+
|
|
7
|
+
/** How far the view zooms: from half of the whole-drawing view (or of 1) up to this. */
|
|
8
|
+
export const ZOOM_MAX = 2.5;
|
|
9
|
+
/** A pointer moved this far is a pan, not a click. */
|
|
10
|
+
export const DRAG = 4;
|
|
11
|
+
|
|
12
|
+
/** The transform that shows `v`. */
|
|
13
|
+
export const transformOf = (v: View): string => `translate(${v.x}px,${v.y}px) scale(${v.k})`;
|
|
14
|
+
|
|
15
|
+
/** What there is to fit: the drawing's size, whether it runs top to bottom, and the least frame height (the selection's card). */
|
|
16
|
+
export interface Drawing {
|
|
17
|
+
w: number;
|
|
18
|
+
h: number;
|
|
19
|
+
down: boolean;
|
|
20
|
+
room: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PanZoomOptions {
|
|
24
|
+
/** The frame, and the stage in it that the view moves. */
|
|
25
|
+
frame: () => HTMLElement | null;
|
|
26
|
+
stage: () => HTMLElement | null;
|
|
27
|
+
/** What to fit, or null while there is nothing to fit (a view without the drawing). */
|
|
28
|
+
drawing: () => Drawing | null;
|
|
29
|
+
/** Ctrl (or ⌘) and the wheel zoom: the drawing is shown. */
|
|
30
|
+
wheel: () => boolean;
|
|
31
|
+
/** The drawing runs top to bottom: a touch scrolls the page, a mouse still pans. */
|
|
32
|
+
down: () => boolean;
|
|
33
|
+
/** After the view moved. */
|
|
34
|
+
onMove: () => void;
|
|
35
|
+
/** After the host's size changed. */
|
|
36
|
+
onResize: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The zoom and pan of a drawing in its frame, and the frame's height. Until the
|
|
41
|
+
* reader zooms or pans (`moved`), the view shows the whole drawing. The host
|
|
42
|
+
* renders again only when what it draws changes: the frame's height, and whether
|
|
43
|
+
* "Show all" does anything (`moved`, `fitK`).
|
|
44
|
+
*/
|
|
45
|
+
export class PanZoomController implements ReactiveController {
|
|
46
|
+
/** The zoom and pan, written on the stage. */
|
|
47
|
+
view: View = { k: 1, x: 0, y: 0 };
|
|
48
|
+
/** The frame's height as the drawing needs it; a height set on the host makes the frame taller or shorter. */
|
|
49
|
+
frameH = 0;
|
|
50
|
+
/** The scale that shows the whole drawing. */
|
|
51
|
+
fitK = 1;
|
|
52
|
+
/** The reader has zoomed or panned away from the whole drawing. */
|
|
53
|
+
moved = false;
|
|
54
|
+
/** A pointer is down on the frame. */
|
|
55
|
+
pressed = false;
|
|
56
|
+
/** A pan in progress, and whether it went far enough to swallow the click that ends it. */
|
|
57
|
+
private pan: { id: number; px: number; py: number; x: number; y: number; far: boolean } | null = null;
|
|
58
|
+
private swallowClick = false;
|
|
59
|
+
private resize?: ResizeObserver;
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
private readonly host: ReactiveControllerHost & HTMLElement,
|
|
63
|
+
private readonly o: PanZoomOptions,
|
|
64
|
+
) {
|
|
65
|
+
host.addController(this);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
hostConnected(): void {
|
|
69
|
+
// Ctrl (or ⌘) and the wheel zoom; the wheel alone still scrolls the page.
|
|
70
|
+
this.host.addEventListener('wheel', this.onWheel, { passive: false });
|
|
71
|
+
this.host.addEventListener('click', this.onClickCapture, { capture: true });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
hostDisconnected(): void {
|
|
75
|
+
this.host.removeEventListener('wheel', this.onWheel);
|
|
76
|
+
this.host.removeEventListener('click', this.onClickCapture, { capture: true });
|
|
77
|
+
this.resize?.disconnect();
|
|
78
|
+
this.resize = undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
hostUpdated(): void {
|
|
82
|
+
if (this.resize) return;
|
|
83
|
+
this.resize = new ResizeObserver(() => this.o.onResize());
|
|
84
|
+
this.resize.observe(this.host);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The stage's style: the view, as the host renders it. */
|
|
88
|
+
get transform(): string {
|
|
89
|
+
return transformOf(this.view);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Back to the whole drawing, on a new layer. */
|
|
93
|
+
reset(): void {
|
|
94
|
+
this.moved = false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The frame's height and, until the reader zooms or pans, the view of the whole drawing. */
|
|
98
|
+
fit(): void {
|
|
99
|
+
const d = this.o.drawing();
|
|
100
|
+
if (!d) return;
|
|
101
|
+
const frame = this.o.frame();
|
|
102
|
+
// The frame is beside the inspector, or above it on a narrow screen.
|
|
103
|
+
const width = frame?.clientWidth || this.host.clientWidth;
|
|
104
|
+
// Top to bottom, the drawing is as tall as it needs at the frame's width.
|
|
105
|
+
const own = fitView(d.w, d.h, width, d.down ? Infinity : Math.max(FRAME_MIN, FRAME_SHARE * globalThis.innerHeight));
|
|
106
|
+
// A height set on the host: the frame is not as tall as it was drawn, and the drawing fits the frame as it is.
|
|
107
|
+
const fh = frame?.clientHeight ?? 0;
|
|
108
|
+
const whole = `${fh}px` !== (frame?.style.height ?? '0px') ? fitView(d.w, d.h, width, fh, fh) : own;
|
|
109
|
+
if (whole.k !== this.fitK) {
|
|
110
|
+
this.fitK = whole.k;
|
|
111
|
+
this.host.requestUpdate();
|
|
112
|
+
}
|
|
113
|
+
// A short drawing's frame grows down to hold the selection's card; the drawing stays where it is.
|
|
114
|
+
const h = d.down ? own.h : Math.max(own.h, d.room);
|
|
115
|
+
if (!this.moved) {
|
|
116
|
+
this.setHeight(h);
|
|
117
|
+
if (whole.k !== this.view.k || whole.x !== this.view.x || whole.y !== this.view.y) this.setView({ k: whole.k, x: whole.x, y: whole.y });
|
|
118
|
+
} else if (h > this.frameH) this.setHeight(h);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Back to the whole drawing. */
|
|
122
|
+
showAll(): void {
|
|
123
|
+
this.moved = false;
|
|
124
|
+
this.fit();
|
|
125
|
+
this.host.requestUpdate();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
zoomBy(factor: number, cx = (this.o.frame()?.clientWidth ?? this.host.clientWidth) / 2, cy = (this.o.frame()?.clientHeight ?? this.frameH) / 2): void {
|
|
129
|
+
this.setMoved();
|
|
130
|
+
this.setView(zoomView(this.view, factor, cx, cy, Math.min(1, this.fitK) / 2, ZOOM_MAX));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Bring `el` into the frame when zoom or pan left it outside. */
|
|
134
|
+
reveal(el: HTMLElement): void {
|
|
135
|
+
const frame = this.o.frame();
|
|
136
|
+
if (!frame) return;
|
|
137
|
+
const f = frame.getBoundingClientRect();
|
|
138
|
+
const r = el.getBoundingClientRect();
|
|
139
|
+
const pad = 24;
|
|
140
|
+
let dx = 0;
|
|
141
|
+
let dy = 0;
|
|
142
|
+
if (r.left < f.left + pad) dx = f.left + pad - r.left;
|
|
143
|
+
else if (r.right > f.right - pad) dx = f.right - pad - r.right;
|
|
144
|
+
if (r.top < f.top + pad) dy = f.top + pad - r.top;
|
|
145
|
+
else if (r.bottom > f.bottom - pad) dy = f.bottom - pad - r.bottom;
|
|
146
|
+
if (dx || dy) {
|
|
147
|
+
this.setMoved();
|
|
148
|
+
this.setView({ ...this.view, x: this.view.x + dx, y: this.view.y + dy });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
readonly onPointerDown = (e: PointerEvent): void => {
|
|
153
|
+
this.pressed = true;
|
|
154
|
+
globalThis.addEventListener('pointerup', () => (this.pressed = false), { once: true });
|
|
155
|
+
// On a narrow screen a touch scrolls the page; a mouse still pans.
|
|
156
|
+
if (e.button !== 0 || (e.target as HTMLElement).closest('.tools') || (this.o.down() && e.pointerType !== 'mouse')) return;
|
|
157
|
+
this.pan = { id: e.pointerId, px: e.clientX, py: e.clientY, x: this.view.x, y: this.view.y, far: false };
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
readonly onPointerMove = (e: PointerEvent): void => {
|
|
161
|
+
const p = this.pan;
|
|
162
|
+
if (!p || p.id !== e.pointerId) return;
|
|
163
|
+
const dx = e.clientX - p.px;
|
|
164
|
+
const dy = e.clientY - p.py;
|
|
165
|
+
if (!p.far && Math.hypot(dx, dy) < DRAG) return;
|
|
166
|
+
if (!p.far) {
|
|
167
|
+
p.far = true;
|
|
168
|
+
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
|
|
169
|
+
(e.currentTarget as HTMLElement).classList.add('drag');
|
|
170
|
+
}
|
|
171
|
+
this.setMoved();
|
|
172
|
+
this.setView({ ...this.view, x: p.x + dx, y: p.y + dy });
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
readonly onPointerUp = (e: PointerEvent): void => {
|
|
176
|
+
const p = this.pan;
|
|
177
|
+
if (!p || p.id !== e.pointerId) return;
|
|
178
|
+
this.pan = null;
|
|
179
|
+
(e.currentTarget as HTMLElement).classList.remove('drag');
|
|
180
|
+
if (p.far) this.swallowClick = true;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
private readonly onWheel = (e: WheelEvent): void => {
|
|
184
|
+
if (!this.o.wheel() || !(e.ctrlKey || e.metaKey)) return;
|
|
185
|
+
const frame = this.o.frame();
|
|
186
|
+
if (!frame) return;
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
const r = frame.getBoundingClientRect();
|
|
189
|
+
this.zoomBy(Math.exp(-e.deltaY * 0.0015), e.clientX - r.left, e.clientY - r.top);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/** The click that ends a pan opens nothing. */
|
|
193
|
+
private readonly onClickCapture = (e: Event): void => {
|
|
194
|
+
if (!this.swallowClick) return;
|
|
195
|
+
this.swallowClick = false;
|
|
196
|
+
e.stopPropagation();
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
private setMoved(): void {
|
|
201
|
+
if (this.moved) return;
|
|
202
|
+
this.moved = true;
|
|
203
|
+
this.host.requestUpdate();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private setHeight(h: number): void {
|
|
207
|
+
if (h === this.frameH) return;
|
|
208
|
+
this.frameH = h;
|
|
209
|
+
this.host.requestUpdate();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Write the view on the stage; the host's next render writes the same. */
|
|
213
|
+
private setView(v: View): void {
|
|
214
|
+
this.view = v;
|
|
215
|
+
const stage = this.o.stage();
|
|
216
|
+
if (stage) stage.style.transform = this.transform;
|
|
217
|
+
this.o.onMove();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Edges and loop returns as SVG paths.
|
|
2
|
+
import { RETURN_R, type LoopReturn, type Stretch } from './layout.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A loop box's return as an SVG path: out from its last layer to the band, back along
|
|
6
|
+
* it with rounded corners, and in to its first layer, stopping `gap` short of each item.
|
|
7
|
+
*/
|
|
8
|
+
export function returnPath(r: LoopReturn, down = false, gap = 3): string {
|
|
9
|
+
const r2 = (n: number) => Math.round(n * 2) / 2;
|
|
10
|
+
const pt = (u: number, v: number) => (down ? `${r2(v)},${r2(u)}` : `${r2(u)},${r2(v)}`);
|
|
11
|
+
const k = Math.max(0, Math.min(RETURN_R, (r.from - r.to) / 2, r.band - r.v0 - gap, r.band - r.v1 - gap));
|
|
12
|
+
return [
|
|
13
|
+
`M${pt(r.from, r.v0 + gap)}`,
|
|
14
|
+
`L${pt(r.from, r.band - k)}`,
|
|
15
|
+
`Q${pt(r.from, r.band)} ${pt(r.from - k, r.band)}`,
|
|
16
|
+
`L${pt(r.to + k, r.band)}`,
|
|
17
|
+
`Q${pt(r.to, r.band)} ${pt(r.to, r.band - k)}`,
|
|
18
|
+
`L${pt(r.to, r.v1 + gap)}`,
|
|
19
|
+
].join(' ');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** An edge's line as an SVG path: its runs, joined by a curve in each gap where it bends. */
|
|
23
|
+
export function edgePath(runs: readonly Stretch[], down = false): string {
|
|
24
|
+
const r2 = (n: number) => Math.round(n * 2) / 2;
|
|
25
|
+
const pt = (u: number, v: number) => (down ? `${r2(v)},${r2(u)}` : `${r2(u)},${r2(v)}`);
|
|
26
|
+
const first = runs[0];
|
|
27
|
+
if (!first) return '';
|
|
28
|
+
let d = `M${pt(first.a, first.at)}`;
|
|
29
|
+
runs.forEach((r, i) => {
|
|
30
|
+
if (r.b !== r.a) d += ` L${pt(r.b, r.at)}`;
|
|
31
|
+
const next = runs[i + 1];
|
|
32
|
+
if (!next) return;
|
|
33
|
+
if (Math.abs(next.at - r.at) < 0.5) {
|
|
34
|
+
if (next.a !== r.b) d += ` L${pt(next.a, next.at)}`;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const m = (r.b + next.a) / 2;
|
|
38
|
+
d += ` C${pt(m, r.at)} ${pt(m, next.at)} ${pt(next.a, next.at)}`;
|
|
39
|
+
});
|
|
40
|
+
return d;
|
|
41
|
+
}
|