@react-x11/components 0.7.1 → 0.9.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 +295 -51
- package/dist/charts/node.d.ts +1 -1
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +6 -2
- package/dist/charts/node.js.map +1 -1
- package/dist/color-picker/ColorPicker.d.ts.map +1 -1
- package/dist/color-picker/ColorPicker.js +43 -33
- package/dist/color-picker/ColorPicker.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/html/node.d.ts +3 -1
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +7 -5
- package/dist/html/node.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{markdown → internal/markdown}/ast.d.ts +12 -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 +112 -22
- 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/scroll.d.ts +3 -2
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +33 -15
- package/dist/internal/scroll.js.map +1 -1
- package/dist/internal/units.d.ts +2 -0
- package/dist/internal/units.d.ts.map +1 -0
- package/dist/internal/units.js +35 -0
- package/dist/internal/units.js.map +1 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +4 -2
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +98 -0
- package/dist/maps/anchors.d.ts.map +1 -0
- package/dist/maps/anchors.js +531 -0
- package/dist/maps/anchors.js.map +1 -0
- package/dist/maps/controller.d.ts +283 -0
- package/dist/maps/controller.d.ts.map +1 -0
- package/dist/maps/controller.js +743 -0
- package/dist/maps/controller.js.map +1 -0
- package/dist/maps/gl/buckets.d.ts +95 -0
- package/dist/maps/gl/buckets.d.ts.map +1 -0
- package/dist/maps/gl/buckets.js +362 -0
- package/dist/maps/gl/buckets.js.map +1 -0
- package/dist/maps/gl/build-worker.d.ts +13 -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 +51 -0
- package/dist/maps/gl/cover.d.ts.map +1 -0
- package/dist/maps/gl/cover.js +124 -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 +111 -0
- package/dist/maps/gl/placement.d.ts.map +1 -0
- package/dist/maps/gl/placement.js +567 -0
- package/dist/maps/gl/placement.js.map +1 -0
- package/dist/maps/gl/renderer.d.ts +352 -0
- package/dist/maps/gl/renderer.d.ts.map +1 -0
- package/dist/maps/gl/renderer.js +1127 -0
- package/dist/maps/gl/renderer.js.map +1 -0
- package/dist/maps/gl/shaders.d.ts +120 -0
- package/dist/maps/gl/shaders.d.ts.map +1 -0
- package/dist/maps/gl/shaders.js +451 -0
- package/dist/maps/gl/shaders.js.map +1 -0
- package/dist/maps/gl/store.d.ts +115 -0
- package/dist/maps/gl/store.d.ts.map +1 -0
- package/dist/maps/gl/store.js +469 -0
- package/dist/maps/gl/store.js.map +1 -0
- package/dist/maps/gl/text.d.ts +154 -0
- package/dist/maps/gl/text.d.ts.map +1 -0
- package/dist/maps/gl/text.js +382 -0
- package/dist/maps/gl/text.js.map +1 -0
- package/dist/maps/gl/view.d.ts +54 -0
- package/dist/maps/gl/view.d.ts.map +1 -0
- package/dist/maps/gl/view.js +974 -0
- package/dist/maps/gl/view.js.map +1 -0
- package/dist/maps/index.d.ts +1 -1
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +216 -52
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +37 -16
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +188 -119
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +162 -56
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +607 -515
- 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 +14 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +28 -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 +25 -3
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +17 -0
- package/dist/maps/sources.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 +117 -17
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +163 -30
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +188 -20
- 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 +9 -7
- 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/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/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 +263 -0
- package/dist/rich-text-editor/index.d.ts.map +1 -0
- package/dist/rich-text-editor/index.js +822 -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 +393 -0
- package/dist/rich-text-editor/keymap.js.map +1 -0
- package/dist/rich-text-editor/keys.d.ts +29 -0
- package/dist/rich-text-editor/keys.d.ts.map +1 -0
- package/dist/rich-text-editor/keys.js +93 -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/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 +500 -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 +262 -0
- package/dist/rich-text-editor/virtual.js.map +1 -0
- package/dist/richtext/node.d.ts +25 -2
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +46 -24
- package/dist/richtext/node.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +14 -5
- package/dist/table/index.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 +28 -11
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/node.d.ts +56 -3
- package/dist/terminal/vt/node.d.ts.map +1 -1
- package/dist/terminal/vt/node.js +106 -26
- package/dist/terminal/vt/node.js.map +1 -1
- package/dist/terminal/vt/renderer.d.ts +4 -0
- package/dist/terminal/vt/renderer.d.ts.map +1 -1
- package/dist/terminal/vt/renderer.js +14 -5
- package/dist/terminal/vt/renderer.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +6 -4
- package/dist/tree/index.js.map +1 -1
- package/package.json +23 -4
- package/src/charts/node.ts +6 -2
- package/src/color-picker/ColorPicker.ts +107 -81
- package/src/embed/client.ts +102 -8
- package/src/embed/index.ts +2 -0
- package/src/html/node.ts +7 -5
- package/src/index.ts +2 -0
- package/src/{markdown → internal/markdown}/ast.ts +24 -7
- package/src/{markdown → internal/markdown}/parse.ts +134 -24
- package/src/internal/markdown/stringify.ts +772 -0
- package/src/internal/scroll.ts +39 -18
- package/src/internal/units.ts +34 -0
- package/src/internal/window.ts +4 -2
- package/src/maps/anchors.ts +607 -0
- package/src/maps/controller.ts +999 -0
- package/src/maps/gl/buckets.ts +457 -0
- package/src/maps/gl/build-worker.ts +55 -0
- package/src/maps/gl/color.ts +62 -0
- package/src/maps/gl/cover.ts +170 -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 +816 -0
- package/src/maps/gl/renderer.ts +1576 -0
- package/src/maps/gl/shaders.ts +486 -0
- package/src/maps/gl/store.ts +566 -0
- package/src/maps/gl/text.ts +495 -0
- package/src/maps/gl/view.ts +1224 -0
- package/src/maps/index.ts +270 -81
- package/src/maps/labels.ts +214 -121
- package/src/maps/node.ts +763 -629
- package/src/maps/overlay.ts +148 -43
- package/src/maps/paint.ts +3 -0
- package/src/maps/proj.ts +31 -0
- package/src/maps/renderer.ts +155 -0
- package/src/maps/sources.ts +38 -3
- package/src/maps/theme.ts +51 -0
- package/src/maps/tiles.ts +191 -33
- package/src/maps/types.ts +188 -20
- package/src/markdown/expressions.ts +5 -1
- package/src/markdown/index.ts +14 -11
- package/src/markdown/spans.ts +1 -1
- package/src/media-player/index.ts +18 -4
- 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 +1331 -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 +119 -0
- package/src/rich-text-editor/look.ts +427 -0
- package/src/rich-text-editor/markdown.ts +653 -0
- package/src/rich-text-editor/nodes.ts +497 -0
- package/src/rich-text-editor/render.ts +800 -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 +347 -0
- package/src/richtext/node.ts +59 -30
- package/src/table/index.ts +14 -5
- package/src/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +43 -15
- package/src/terminal/vt/node.ts +128 -29
- package/src/terminal/vt/renderer.ts +27 -5
- package/src/tree/index.ts +6 -4
- 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,347 @@
|
|
|
1
|
+
// Long documents: only the top-level blocks near the viewport are drawn.
|
|
2
|
+
//
|
|
3
|
+
// The machinery is `<Tree>`'s and `<Table>`'s — the height index
|
|
4
|
+
// (../internal/heights.ts), the window (../internal/window.ts) and the owed
|
|
5
|
+
// reveal (../internal/scroll.ts) — laid over the document's top-level blocks
|
|
6
|
+
// and keyed by their block keys (./keys.ts), so a height measured once
|
|
7
|
+
// survives every edit that keeps its block. Each drawn block is in a box of
|
|
8
|
+
// its own, measured after layout (./render.ts, `renderBlockRange`), and the
|
|
9
|
+
// blocks outside the window are two spacers, so the scrollbar measures the
|
|
10
|
+
// whole document.
|
|
11
|
+
//
|
|
12
|
+
// The view's geometry is laid-out text — a caret, a hit, the line an arrow
|
|
13
|
+
// key moves to — so a block that is not drawn has none. The view asks this
|
|
14
|
+
// window (`BlockWindow`) whether the block a position is in is drawn, and
|
|
15
|
+
// when it is not, because the reader scrolled away from the caret, it has
|
|
16
|
+
// the block revealed first and does what it meant to once the block is laid
|
|
17
|
+
// out (./view.ts: `scrollToSelection` and `move`).
|
|
18
|
+
//
|
|
19
|
+
// A block is measured with the gap after it, so that the index's offsets are
|
|
20
|
+
// where blocks start: the content column keeps its `gap`, and each spacer
|
|
21
|
+
// gives back the one gap it stands beside.
|
|
22
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
23
|
+
import type { Node as PMNode } from 'prosemirror-model';
|
|
24
|
+
import type { DrawnNode, ScrollableNode } from 'react-x11';
|
|
25
|
+
|
|
26
|
+
import { RowHeights } from '../internal/heights.js';
|
|
27
|
+
import type { RowKey } from '../internal/heights.js';
|
|
28
|
+
import { useReveal } from '../internal/scroll.js';
|
|
29
|
+
import {
|
|
30
|
+
afterLayout,
|
|
31
|
+
cancelAfterLayout,
|
|
32
|
+
cancelLater,
|
|
33
|
+
later,
|
|
34
|
+
} from '../internal/timers.js';
|
|
35
|
+
import type { DelayTick, LayoutTick } from '../internal/timers.js';
|
|
36
|
+
import { scaleOf } from '../internal/units.js';
|
|
37
|
+
import {
|
|
38
|
+
BURST_BUDGET,
|
|
39
|
+
DEFAULT_OVERSCAN,
|
|
40
|
+
DEFAULT_PREFETCH,
|
|
41
|
+
SETTLE_BUDGET,
|
|
42
|
+
useVirtualWindow,
|
|
43
|
+
} from '../internal/window.js';
|
|
44
|
+
import type { BlockKeys } from './keys.js';
|
|
45
|
+
|
|
46
|
+
/** Top-level blocks past which `virtual: 'auto'` draws only a window of
|
|
47
|
+
* them — `<Tree>`'s threshold, and far more than a composer ever holds. */
|
|
48
|
+
export const VIRTUAL_THRESHOLD = 200;
|
|
49
|
+
|
|
50
|
+
/** What the view asks of a long document's window. */
|
|
51
|
+
export interface BlockWindow {
|
|
52
|
+
/** Whether top-level block `index` is drawn and laid out. */
|
|
53
|
+
drawn(index: number): boolean;
|
|
54
|
+
/** Scroll top-level block `index` into view, and so draw it; `then` runs
|
|
55
|
+
* once it is laid out — unless the reader scrolls somewhere else first,
|
|
56
|
+
* which drops it. */
|
|
57
|
+
reveal(index: number, then: () => void): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface Row {
|
|
61
|
+
id: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface Drawn {
|
|
65
|
+
node: DrawnNode;
|
|
66
|
+
at: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The document's top-level blocks, by key, in order. */
|
|
70
|
+
function topBlocks(doc: PMNode, keys: BlockKeys): Row[] {
|
|
71
|
+
const rows: Row[] = [];
|
|
72
|
+
doc.forEach((_node, offset) => {
|
|
73
|
+
rows.push({ id: keys.keyAt(offset) ?? `p${offset}` });
|
|
74
|
+
});
|
|
75
|
+
return rows;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface BlockWindowInputs {
|
|
79
|
+
doc: PMNode;
|
|
80
|
+
keys: BlockKeys;
|
|
81
|
+
virtual: boolean | 'auto';
|
|
82
|
+
/** What a block nobody has measured is guessed at, gap included —
|
|
83
|
+
* logical pixels, like every length here. */
|
|
84
|
+
estimate: number;
|
|
85
|
+
/** The content column's gap between blocks. */
|
|
86
|
+
gap: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface BlockWindowState {
|
|
90
|
+
virtualizing: boolean;
|
|
91
|
+
/** The top-level blocks to draw, `last` exclusive. */
|
|
92
|
+
first: number;
|
|
93
|
+
last: number;
|
|
94
|
+
/** The spacers before and after them — 0 for none. */
|
|
95
|
+
above: number;
|
|
96
|
+
below: number;
|
|
97
|
+
/** The scroll pane. */
|
|
98
|
+
box: { current: ScrollableNode | null };
|
|
99
|
+
/** The pane's handlers, while the window is on. */
|
|
100
|
+
onViewport(ev: { width: number; height: number }): void;
|
|
101
|
+
onScroll(ev: { scrollY: number }): void;
|
|
102
|
+
/** A drawn block's box as it mounts, and null as it goes. */
|
|
103
|
+
register(key: string, at: number, node: unknown): void;
|
|
104
|
+
/** What the view asks — see `BlockWindow`. */
|
|
105
|
+
window: BlockWindow;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The window over a document's top-level blocks. Call every render, with
|
|
109
|
+
* the document about to be drawn. */
|
|
110
|
+
export function useBlockWindow(inputs: BlockWindowInputs): BlockWindowState {
|
|
111
|
+
const { doc, keys, virtual, estimate, gap } = inputs;
|
|
112
|
+
const box = useRef<ScrollableNode | null>(null);
|
|
113
|
+
const [, setMeasured] = useState(0);
|
|
114
|
+
const rows = useMemo(() => topBlocks(doc, keys), [doc, keys]);
|
|
115
|
+
const rowsRef = useRef(rows);
|
|
116
|
+
rowsRef.current = rows;
|
|
117
|
+
const gapRef = useRef(gap);
|
|
118
|
+
gapRef.current = gap;
|
|
119
|
+
/** The drawn blocks' boxes, by key, with the index each was drawn at. */
|
|
120
|
+
const nodes = useRef(new Map<RowKey, Drawn>());
|
|
121
|
+
const virtualizing =
|
|
122
|
+
virtual === true || (virtual === 'auto' && rows.length > VIRTUAL_THRESHOLD);
|
|
123
|
+
|
|
124
|
+
// One index per editor, for as long as it is mounted. A new look is a new
|
|
125
|
+
// height for every block, so what was measured under the old one goes.
|
|
126
|
+
const [heights] = useState(() => new RowHeights(estimate));
|
|
127
|
+
const estimated = useRef(estimate);
|
|
128
|
+
if (estimated.current !== estimate) {
|
|
129
|
+
estimated.current = estimate;
|
|
130
|
+
heights.reset();
|
|
131
|
+
}
|
|
132
|
+
heights.sync(rows, estimate);
|
|
133
|
+
|
|
134
|
+
const win = useVirtualWindow({
|
|
135
|
+
box,
|
|
136
|
+
heights,
|
|
137
|
+
rows,
|
|
138
|
+
exact: false,
|
|
139
|
+
virtualizing,
|
|
140
|
+
overscan: DEFAULT_OVERSCAN,
|
|
141
|
+
prefetch: DEFAULT_PREFETCH,
|
|
142
|
+
// a block is built whole or not at all: no skeletons
|
|
143
|
+
threshold: Number.POSITIVE_INFINITY,
|
|
144
|
+
burstBudget: BURST_BUDGET,
|
|
145
|
+
settleBudget: SETTLE_BUDGET,
|
|
146
|
+
});
|
|
147
|
+
const reveal = useReveal({ box, rows: rowsRef, nodes, heights });
|
|
148
|
+
|
|
149
|
+
/** A reveal the view is waiting on, and what it does once the block is
|
|
150
|
+
* laid out. */
|
|
151
|
+
const owed = useRef<{ id: string; then: () => void } | null>(null);
|
|
152
|
+
|
|
153
|
+
const drawnAt = useCallback((index: number): boolean => {
|
|
154
|
+
const row = rowsRef.current[index];
|
|
155
|
+
const drawn = row ? nodes.current.get(row.id) : undefined;
|
|
156
|
+
return !!drawn && drawn.at === index && drawn.node.abs.height > 0;
|
|
157
|
+
}, []);
|
|
158
|
+
|
|
159
|
+
/** What the drawn blocks laid out at, into the index — `<Tree>`'s pass:
|
|
160
|
+
* what grew above the viewport comes back out of the offset, so the text
|
|
161
|
+
* being read does not move. */
|
|
162
|
+
const measure = (): boolean => {
|
|
163
|
+
const pane = box.current;
|
|
164
|
+
const list = rowsRef.current;
|
|
165
|
+
const anchor = pane ? heights.indexAt(pane.scrollY / scaleOf(pane)) : 0;
|
|
166
|
+
let shift = 0;
|
|
167
|
+
let changed = false;
|
|
168
|
+
for (const [id, { node, at }] of nodes.current) {
|
|
169
|
+
if (list[at]?.id !== id || !(node.abs.height > 0)) continue;
|
|
170
|
+
// `abs` is device, the index logical (../internal/units.ts)
|
|
171
|
+
const height = node.abs.height / scaleOf(node) + gapRef.current;
|
|
172
|
+
const was = heights.heightAt(at);
|
|
173
|
+
if (!heights.measure(id, at, height)) continue;
|
|
174
|
+
changed = true;
|
|
175
|
+
if (at < anchor) shift += height - was;
|
|
176
|
+
}
|
|
177
|
+
if (!changed) return false;
|
|
178
|
+
reveal.nudge(shift);
|
|
179
|
+
setMeasured((n) => n + 1);
|
|
180
|
+
return true;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/** The estimate learns the measured mean — idle only, and the screen
|
|
184
|
+
* kept still by the same anchor arithmetic. */
|
|
185
|
+
const adapt = (): boolean => {
|
|
186
|
+
const pane = box.current;
|
|
187
|
+
if (!pane) return false;
|
|
188
|
+
const anchor = heights.indexAt(pane.scrollY / scaleOf(pane));
|
|
189
|
+
const before = heights.offsetAt(anchor);
|
|
190
|
+
if (!heights.adapt()) return false;
|
|
191
|
+
reveal.nudge(heights.offsetAt(anchor) - before);
|
|
192
|
+
setMeasured((n) => n + 1);
|
|
193
|
+
return true;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** Whether a drawn block has no size yet: a commit can land between frame
|
|
197
|
+
* flushes, and a pass over it reads zeros. */
|
|
198
|
+
const unsized = (): boolean => {
|
|
199
|
+
const list = rowsRef.current;
|
|
200
|
+
for (const [id, { node, at }] of nodes.current) {
|
|
201
|
+
if (list[at]?.id === id && !(node.abs.height > 0)) return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/** Hand the view the block it was waiting on, once it is laid out — or
|
|
207
|
+
* drop the wait, when the block has left the document. */
|
|
208
|
+
const settle = (): void => {
|
|
209
|
+
const o = owed.current;
|
|
210
|
+
if (!o) return;
|
|
211
|
+
const drawn = nodes.current.get(o.id);
|
|
212
|
+
if (
|
|
213
|
+
drawn &&
|
|
214
|
+
rowsRef.current[drawn.at]?.id === o.id &&
|
|
215
|
+
drawn.node.abs.height > 0
|
|
216
|
+
) {
|
|
217
|
+
owed.current = null;
|
|
218
|
+
o.then();
|
|
219
|
+
} else if (!rowsRef.current.some((row) => row.id === o.id)) {
|
|
220
|
+
owed.current = null;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// The tick after layout, and everything only known there: what the blocks
|
|
225
|
+
// measured, whether an owed scroll can go further, where the pane really
|
|
226
|
+
// is, and whether the block the view waits on is laid out — in that order,
|
|
227
|
+
// each able to move what the next one reads.
|
|
228
|
+
const ticks = useRef<{ layout: LayoutTick; look: DelayTick; tries: number }>({
|
|
229
|
+
layout: null,
|
|
230
|
+
look: null,
|
|
231
|
+
tries: 0,
|
|
232
|
+
});
|
|
233
|
+
const pass = useRef<() => void>(() => {});
|
|
234
|
+
pass.current = (): void => {
|
|
235
|
+
if (!virtualizing) return;
|
|
236
|
+
const pane = box.current;
|
|
237
|
+
// `onViewport` reports a change, so a window turned on after the pane's
|
|
238
|
+
// first layout has never heard its size: read it here
|
|
239
|
+
if (pane && win.viewRef.current.height === 0 && pane.abs.height > 0) {
|
|
240
|
+
const s = scaleOf(pane);
|
|
241
|
+
win.sized(pane.abs.width / s, pane.abs.height / s);
|
|
242
|
+
}
|
|
243
|
+
const moved = win.fast() ? false : measure();
|
|
244
|
+
const adapted = !win.scrolling() && adapt();
|
|
245
|
+
reveal.retry(moved || adapted);
|
|
246
|
+
win.sync();
|
|
247
|
+
settle();
|
|
248
|
+
// look again, briefly, while a block is unsized or the view still waits
|
|
249
|
+
// — nothing else would come back for them
|
|
250
|
+
const t = ticks.current;
|
|
251
|
+
if ((unsized() || owed.current !== null) && t.tries++ < 8) {
|
|
252
|
+
t.look = later(() => {
|
|
253
|
+
t.look = null;
|
|
254
|
+
pass.current();
|
|
255
|
+
}, 16);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const kick = useCallback((): void => {
|
|
259
|
+
const t = ticks.current;
|
|
260
|
+
cancelAfterLayout(t.layout);
|
|
261
|
+
cancelLater(t.look);
|
|
262
|
+
t.look = null;
|
|
263
|
+
t.tries = 0;
|
|
264
|
+
t.layout = afterLayout(() => {
|
|
265
|
+
t.layout = null;
|
|
266
|
+
pass.current();
|
|
267
|
+
});
|
|
268
|
+
}, []);
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
if (virtualizing) kick();
|
|
271
|
+
});
|
|
272
|
+
useEffect(
|
|
273
|
+
() => () => {
|
|
274
|
+
const t = ticks.current;
|
|
275
|
+
cancelAfterLayout(t.layout);
|
|
276
|
+
cancelLater(t.look);
|
|
277
|
+
},
|
|
278
|
+
[],
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const { sized, scrolled, sync } = win;
|
|
282
|
+
const onViewport = useCallback(
|
|
283
|
+
(ev: { width: number; height: number }): void => {
|
|
284
|
+
sized(ev.width, ev.height);
|
|
285
|
+
// the content changed size: an owed scroll may reach further, and the
|
|
286
|
+
// pane may have re-clamped its offset without a word
|
|
287
|
+
reveal.retry(true);
|
|
288
|
+
sync();
|
|
289
|
+
},
|
|
290
|
+
[sized, reveal, sync],
|
|
291
|
+
);
|
|
292
|
+
const onScroll = useCallback(
|
|
293
|
+
(ev: { scrollY: number }): void => {
|
|
294
|
+
// a scroll this editor did not ask for is the reader's, and what the
|
|
295
|
+
// view was waiting to do gives way to it
|
|
296
|
+
if (!reveal.heard(ev.scrollY)) owed.current = null;
|
|
297
|
+
scrolled(ev.scrollY);
|
|
298
|
+
},
|
|
299
|
+
[reveal, scrolled],
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const register = useCallback(
|
|
303
|
+
(key: string, at: number, node: unknown): void => {
|
|
304
|
+
const map = nodes.current;
|
|
305
|
+
if (node) map.set(key, { node: node as DrawnNode, at });
|
|
306
|
+
else if (map.get(key)?.at === at) map.delete(key);
|
|
307
|
+
},
|
|
308
|
+
[],
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const window = useMemo<BlockWindow>(
|
|
312
|
+
() => ({
|
|
313
|
+
drawn: drawnAt,
|
|
314
|
+
reveal: (index, then) => {
|
|
315
|
+
const row = rowsRef.current[index];
|
|
316
|
+
if (!row) return;
|
|
317
|
+
if (drawnAt(index)) {
|
|
318
|
+
then();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
owed.current = { id: row.id, then };
|
|
322
|
+
reveal.to(row.id);
|
|
323
|
+
// the reveal may need no render of its own — a block already in the
|
|
324
|
+
// window, not yet laid out — so a pass is asked for here
|
|
325
|
+
kick();
|
|
326
|
+
},
|
|
327
|
+
}),
|
|
328
|
+
[drawnAt, reveal, kick],
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
const { first, last } = win.slice;
|
|
332
|
+
return {
|
|
333
|
+
virtualizing,
|
|
334
|
+
first,
|
|
335
|
+
last,
|
|
336
|
+
above: virtualizing && first > 0 ? Math.max(0, win.slice.above - gap) : 0,
|
|
337
|
+
below:
|
|
338
|
+
virtualizing && last < rows.length
|
|
339
|
+
? Math.max(0, win.slice.below - gap)
|
|
340
|
+
: 0,
|
|
341
|
+
box,
|
|
342
|
+
onViewport,
|
|
343
|
+
onScroll,
|
|
344
|
+
register,
|
|
345
|
+
window,
|
|
346
|
+
};
|
|
347
|
+
}
|
package/src/richtext/node.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
paintRunBackgrounds,
|
|
38
38
|
paintRunRules,
|
|
39
39
|
} from './runs.js';
|
|
40
|
+
import type { FillContext } from './runs.js';
|
|
40
41
|
import { codeUnitOffsets } from '../internal/text.js';
|
|
41
42
|
|
|
42
43
|
/** The element name — registration key, `node.kind` and JSX tag alike. */
|
|
@@ -215,8 +216,18 @@ export class RichTextNode extends Node {
|
|
|
215
216
|
private _layouts = new Map<string, TextLayoutLike | null>();
|
|
216
217
|
private _text: string | null = null;
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
|
|
219
|
+
/**
|
|
220
|
+
* `kind` is for a subclass registered under a name of its own — the rich
|
|
221
|
+
* text editor's text blocks are `<richtext>` plus a caret and a selection
|
|
222
|
+
* the editor owns (`src/rich-text-editor/nodes.ts`). A node's `kind` must be
|
|
223
|
+
* the name it was registered under, so the subclass passes its own.
|
|
224
|
+
*/
|
|
225
|
+
constructor(
|
|
226
|
+
props: Record<string, unknown>,
|
|
227
|
+
app: NtkApp,
|
|
228
|
+
kind: string = ELEMENT,
|
|
229
|
+
) {
|
|
230
|
+
super(kind, props, app);
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
/**
|
|
@@ -302,8 +313,10 @@ export class RichTextNode extends Node {
|
|
|
302
313
|
* The layout this node is currently painted with — and, because it is the
|
|
303
314
|
* one thing every accessor below answers from, the reason a caret rect and
|
|
304
315
|
* a glyph cannot disagree (docs/extending.md, "answer from what you draw").
|
|
316
|
+
* Protected rather than private for the editor's subclass, whose line-wise
|
|
317
|
+
* caret motion reads the lines off the same layout the glyphs came from.
|
|
305
318
|
*/
|
|
306
|
-
|
|
319
|
+
protected paintLayout(): TextLayoutLike | null {
|
|
307
320
|
const wrap = (this.props as unknown as RichTextProps).wrap !== false;
|
|
308
321
|
return this._layoutFor(wrap ? this.abs.width : Infinity);
|
|
309
322
|
}
|
|
@@ -347,14 +360,14 @@ export class RichTextNode extends Node {
|
|
|
347
360
|
}
|
|
348
361
|
|
|
349
362
|
override textIndexAt(x: number, y: number): number {
|
|
350
|
-
const layout = this.
|
|
363
|
+
const layout = this.paintLayout();
|
|
351
364
|
if (!layout) return 0;
|
|
352
365
|
const i = layout.indexAt(x - this.abs.x, y - this.abs.y);
|
|
353
366
|
return Math.max(0, Math.min([...this.textContent()].length, i));
|
|
354
367
|
}
|
|
355
368
|
|
|
356
369
|
override textCaretRect(index: number): Rect | null {
|
|
357
|
-
const layout = this.
|
|
370
|
+
const layout = this.paintLayout();
|
|
358
371
|
if (!layout) return null;
|
|
359
372
|
const caret = layout.caretPosition(index);
|
|
360
373
|
return {
|
|
@@ -366,7 +379,7 @@ export class RichTextNode extends Node {
|
|
|
366
379
|
}
|
|
367
380
|
|
|
368
381
|
override textRangeRects(start: number, end: number): Rect[] {
|
|
369
|
-
const layout = this.
|
|
382
|
+
const layout = this.paintLayout();
|
|
370
383
|
if (!layout) return [];
|
|
371
384
|
return rangeBands(layout, this.textContent(), start, end).map((band) => ({
|
|
372
385
|
x: this.abs.x + band.x,
|
|
@@ -381,7 +394,7 @@ export class RichTextNode extends Node {
|
|
|
381
394
|
* selection seam: core deliberately left hover and `cursorAt` out of
|
|
382
395
|
* #291, so following a link stays this package's. */
|
|
383
396
|
hrefAtPoint(x: number, y: number): string | null {
|
|
384
|
-
const layout = this.
|
|
397
|
+
const layout = this.paintLayout();
|
|
385
398
|
if (!layout) return null;
|
|
386
399
|
const s = this._scale;
|
|
387
400
|
const lx = x * s - this.abs.x;
|
|
@@ -401,7 +414,7 @@ export class RichTextNode extends Node {
|
|
|
401
414
|
|
|
402
415
|
override paint(ctx: Context2D): void {
|
|
403
416
|
super.paint(ctx); // background, border, clip to `abs`
|
|
404
|
-
const layout = this.
|
|
417
|
+
const layout = this.paintLayout();
|
|
405
418
|
if (!layout || !canFill(ctx)) return;
|
|
406
419
|
const { x, y } = this.abs;
|
|
407
420
|
const s = this._scale;
|
|
@@ -412,28 +425,8 @@ export class RichTextNode extends Node {
|
|
|
412
425
|
// terminal's cell backgrounds
|
|
413
426
|
for (const line of layout.lines) paintRunBackgrounds(ctx, line, x, y, s);
|
|
414
427
|
|
|
415
|
-
// 2. the band
|
|
416
|
-
|
|
417
|
-
// same reasoning as `<textarea>`'s). The range and the colour both
|
|
418
|
-
// arrive from the surface above; `textRangeRects` is what a custom
|
|
419
|
-
// element and core's own `<text>` both fill, so they cannot drift.
|
|
420
|
-
const range = this.selectionRange;
|
|
421
|
-
// The colour is the condition, not just the range: core types it
|
|
422
|
-
// `string | null` and defines it as what to fill the rectangles with
|
|
423
|
-
// *while a selection is set*, so a null one is "nothing to draw"
|
|
424
|
-
// rather than a band in the default ink.
|
|
425
|
-
const selectionColor = this.selectionColor;
|
|
426
|
-
if (selectionColor && range && range.end > range.start) {
|
|
427
|
-
ctx.fillStyle = selectionColor;
|
|
428
|
-
for (const r of this.textRangeRects(range.start, range.end)) {
|
|
429
|
-
ctx.fillRect(
|
|
430
|
-
Math.round(r.x),
|
|
431
|
-
Math.round(r.y),
|
|
432
|
-
Math.ceil(r.width),
|
|
433
|
-
Math.ceil(r.height),
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
428
|
+
// 2. the band a selection has claimed of this element's text
|
|
429
|
+
this.paintSelection(ctx);
|
|
437
430
|
|
|
438
431
|
// 3. the ink
|
|
439
432
|
layout.draw(ctx, x, y);
|
|
@@ -441,6 +434,42 @@ export class RichTextNode extends Node {
|
|
|
441
434
|
// 4. rules over the ink: link underlines, strikethrough
|
|
442
435
|
for (const line of layout.lines) paintRunRules(ctx, line, x, y, s);
|
|
443
436
|
|
|
437
|
+
// 5. anything that belongs over everything — a subclass's caret
|
|
438
|
+
this.paintOverlay(ctx);
|
|
439
|
+
|
|
444
440
|
ctx.restore();
|
|
445
441
|
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* The selection band, between the run backgrounds and the ink. The default
|
|
445
|
+
* is core's document selection: the band the `selectable` surface above
|
|
446
|
+
* has claimed of this element's text, translucent so the ink keeps its
|
|
447
|
+
* contrast on either palette (the same reasoning as `<textarea>`'s). The
|
|
448
|
+
* range and the colour both arrive from that surface; `textRangeRects` is
|
|
449
|
+
* what a custom element and core's own `<text>` both fill, so they cannot
|
|
450
|
+
* drift. An element with a selection of its own — the editor's text
|
|
451
|
+
* blocks — overrides this and fills that one instead.
|
|
452
|
+
*/
|
|
453
|
+
protected paintSelection(ctx: FillContext): void {
|
|
454
|
+
const range = this.selectionRange;
|
|
455
|
+
// The colour is the condition, not just the range: core types it
|
|
456
|
+
// `string | null` and defines it as what to fill the rectangles with
|
|
457
|
+
// *while a selection is set*, so a null one is "nothing to draw"
|
|
458
|
+
// rather than a band in the default ink.
|
|
459
|
+
const selectionColor = this.selectionColor;
|
|
460
|
+
if (!selectionColor || !range || range.end <= range.start) return;
|
|
461
|
+
ctx.fillStyle = selectionColor;
|
|
462
|
+
for (const r of this.textRangeRects(range.start, range.end)) {
|
|
463
|
+
ctx.fillRect(
|
|
464
|
+
Math.round(r.x),
|
|
465
|
+
Math.round(r.y),
|
|
466
|
+
Math.ceil(r.width),
|
|
467
|
+
Math.ceil(r.height),
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** Drawn last, over the ink and the rules: nothing here, a caret in the
|
|
473
|
+
* editor's subclass. Inside the same save/restore as the rest. */
|
|
474
|
+
protected paintOverlay(_ctx: FillContext): void {}
|
|
446
475
|
}
|
package/src/table/index.ts
CHANGED
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
cancelLater,
|
|
71
71
|
later,
|
|
72
72
|
} from '../internal/timers.js';
|
|
73
|
+
import { scaleOf } from '../internal/units.js';
|
|
73
74
|
import type { DelayTick } from '../internal/timers.js';
|
|
74
75
|
import { useReveal } from '../internal/scroll.js';
|
|
75
76
|
import {
|
|
@@ -953,7 +954,10 @@ export function Table<Row = any>(props: TableProps<Row>): ReactElement {
|
|
|
953
954
|
const syncScroll = useCallback((): void => {
|
|
954
955
|
const box = body.current;
|
|
955
956
|
if (!box) return;
|
|
956
|
-
|
|
957
|
+
// Device on the raw property; the header margin it feeds is a style
|
|
958
|
+
// length, logical — the same division every raw read here makes
|
|
959
|
+
// (../internal/units.ts).
|
|
960
|
+
const x = box.scrollX / scaleOf(box);
|
|
957
961
|
setScrollX((prev) => (prev === x ? prev : x));
|
|
958
962
|
winSync();
|
|
959
963
|
}, [winSync]);
|
|
@@ -992,13 +996,18 @@ export function Table<Row = any>(props: TableProps<Row>): ReactElement {
|
|
|
992
996
|
).total;
|
|
993
997
|
const box = body.current;
|
|
994
998
|
const rows = orderedRef.current;
|
|
995
|
-
const anchor = box ? heights.indexAt(box.scrollY) : 0;
|
|
999
|
+
const anchor = box ? heights.indexAt(box.scrollY / scaleOf(box)) : 0;
|
|
996
1000
|
let shift = 0;
|
|
997
1001
|
let changed = false;
|
|
998
1002
|
for (const [id, { node, at }] of rowNodes.current) {
|
|
999
1003
|
if (rows[at]?.id !== id) continue; // drawn against a list that moved
|
|
1000
|
-
|
|
1001
|
-
|
|
1004
|
+
// `abs` is device; the resolved widths and the height index are
|
|
1005
|
+
// logical. Within a pixel rather than exact: a fractional scale
|
|
1006
|
+
// rounds the device width to the pixel grid, and the stale-width
|
|
1007
|
+
// layouts this gate exists for are whole columns off, not fractions.
|
|
1008
|
+
const s = scaleOf(node);
|
|
1009
|
+
if (Math.abs(node.abs.width / s - expected) >= 1) continue;
|
|
1010
|
+
const height = node.abs.height / s;
|
|
1002
1011
|
const was = heights.heightAt(at);
|
|
1003
1012
|
if (!heights.measure(id, at, height)) continue;
|
|
1004
1013
|
changed = true;
|
|
@@ -1026,7 +1035,7 @@ export function Table<Row = any>(props: TableProps<Row>): ReactElement {
|
|
|
1026
1035
|
if (uniform || !virtualizing) return false;
|
|
1027
1036
|
const box = body.current;
|
|
1028
1037
|
if (!box) return false;
|
|
1029
|
-
const anchor = heights.indexAt(box.scrollY);
|
|
1038
|
+
const anchor = heights.indexAt(box.scrollY / scaleOf(box));
|
|
1030
1039
|
const before = heights.offsetAt(anchor);
|
|
1031
1040
|
if (!heights.adapt()) return false;
|
|
1032
1041
|
reveal.nudge(heights.offsetAt(anchor) - before);
|
package/src/terminal/backends.ts
CHANGED
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
* program installed and is the only backend `write()` can work on.
|
|
22
22
|
*
|
|
23
23
|
* `'auto'` takes the first embeddable emulator that is installed and falls
|
|
24
|
-
* back to `'vt'
|
|
24
|
+
* back to `'vt'` — and on a react-x11 backend that cannot host an embedded
|
|
25
|
+
* window at all (the native macOS one) it takes `'vt'` without looking for
|
|
26
|
+
* the other three, because an emulator found on `PATH` there is one this app
|
|
27
|
+
* cannot show. That order — rather than vt first — is deliberate: an
|
|
25
28
|
* external emulator is battle-tested, and a user who installed one expressed
|
|
26
29
|
* a preference. Moving vt to the front is a later, deliberate decision.
|
|
27
30
|
*/
|