@react-x11/components 0.8.0 → 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 +95 -34
- 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/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 +1 -0
- package/dist/internal/scroll.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/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/package.json +23 -4
- package/src/charts/node.ts +6 -2
- 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 +5 -3
- 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/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +43 -15
- 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/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
|
*/
|
package/src/terminal/index.ts
CHANGED
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
// rendered as a native element rather than as somebody else's X window. It is
|
|
24
24
|
// what makes `write()` real — the pty is ours — and what works on a machine
|
|
25
25
|
// with no emulator installed, which is why `'auto'` ends there rather than at
|
|
26
|
-
// the `fallback`.
|
|
26
|
+
// the `fallback`. It is also the only half that works on a react-x11 backend
|
|
27
|
+
// with no XEmbed at all — the native macOS one — so `'auto'` goes straight
|
|
28
|
+
// there when the app cannot host an emulator's window, whatever `PATH` holds.
|
|
27
29
|
//
|
|
28
30
|
// **`write()` still returns `false` on the XEmbed backends**, and that is not
|
|
29
31
|
// an oversight: the pty belongs to xterm, so "type this" would have to be
|
|
@@ -40,12 +42,13 @@ import React, {
|
|
|
40
42
|
useState,
|
|
41
43
|
} from 'react';
|
|
42
44
|
import type { ReactElement, ReactNode, Ref } from 'react';
|
|
43
|
-
import { useTheme } from 'react-x11';
|
|
45
|
+
import { useApp, useTheme } from 'react-x11';
|
|
44
46
|
import type { ForeignProps } from 'react-x11';
|
|
45
47
|
import type { Style } from 'react-x11/style';
|
|
46
48
|
|
|
47
49
|
import {
|
|
48
50
|
BackendUnavailableError,
|
|
51
|
+
canHostXEmbed,
|
|
49
52
|
resolveBackend,
|
|
50
53
|
useEmbeddedClient,
|
|
51
54
|
} from '../embed/index.js';
|
|
@@ -153,7 +156,13 @@ export interface TerminalProps {
|
|
|
153
156
|
/**
|
|
154
157
|
* Which terminal. Default `'auto'`: the first of xterm, urxvt, alacritty
|
|
155
158
|
* that is installed, and `'vt'` — this package's own emulator, which needs
|
|
156
|
-
* nothing installed — when none of them is
|
|
159
|
+
* nothing installed — when none of them is, or when this react-x11 backend
|
|
160
|
+
* cannot embed another program's window at all (the native macOS one), in
|
|
161
|
+
* which case the three are not even looked for.
|
|
162
|
+
*
|
|
163
|
+
* Name one of the three on such a backend and it is taken at its word,
|
|
164
|
+
* not quietly swapped for vt: `status` is `'unavailable'`, `fallback`
|
|
165
|
+
* renders and `onError` gets an `EmbedUnsupportedError`.
|
|
157
166
|
*/
|
|
158
167
|
backend?: TerminalBackendName;
|
|
159
168
|
fontFamily?: string;
|
|
@@ -179,11 +188,14 @@ export interface TerminalProps {
|
|
|
179
188
|
onExit?: (info: ExitInfo) => void;
|
|
180
189
|
/** The emulator's `_NET_WM_NAME` changed: what the shell says is running. */
|
|
181
190
|
onTitleChange?: (title: string) => void;
|
|
182
|
-
/** Spawn failures,
|
|
183
|
-
* emulator installed
|
|
184
|
-
*
|
|
191
|
+
/** Spawn failures, the `BackendUnavailableError` for a machine with no
|
|
192
|
+
* emulator installed, and the `EmbedUnsupportedError` for an emulator
|
|
193
|
+
* named on a react-x11 backend that cannot embed one. Without a handler
|
|
194
|
+
* none of it is reported anywhere — `status` and `fallback` are the
|
|
195
|
+
* visible half. */
|
|
185
196
|
onError?: (err: Error) => void;
|
|
186
|
-
/** Rendered instead of the surface when no backend is
|
|
197
|
+
/** Rendered instead of the surface when no backend is available — none
|
|
198
|
+
* installed, or the one named cannot be embedded here. */
|
|
187
199
|
fallback?: ReactNode;
|
|
188
200
|
/** False holds off spawning — a pane in a tab that is not open yet. */
|
|
189
201
|
enabled?: boolean;
|
|
@@ -290,8 +302,10 @@ function warnVtOnly(props: TerminalProps): void {
|
|
|
290
302
|
* while the pointer is *over* an embedded emulator, keys go straight to it.
|
|
291
303
|
*
|
|
292
304
|
* **The vt backend is the floor.** With `backend="auto"` (the default), a
|
|
293
|
-
* machine with no emulator installed gets this package's own
|
|
294
|
-
*
|
|
305
|
+
* machine with no emulator installed gets this package's own, and so does an
|
|
306
|
+
* app on a react-x11 backend with no XEmbed — the native macOS one — whatever
|
|
307
|
+
* is installed. `fallback` renders only when the pty module or
|
|
308
|
+
* `@xterm/headless` is missing too.
|
|
295
309
|
*/
|
|
296
310
|
export function Terminal(props: TerminalProps): ReactElement {
|
|
297
311
|
const {
|
|
@@ -302,6 +316,9 @@ export function Terminal(props: TerminalProps): ReactElement {
|
|
|
302
316
|
stopSignal,
|
|
303
317
|
} = props;
|
|
304
318
|
const theme = useTheme() as unknown as Record<string, unknown>;
|
|
319
|
+
// Whether an emulator's window can be hosted here at all. It decides the
|
|
320
|
+
// ladder below before `PATH` is asked anything.
|
|
321
|
+
const embeddable = canHostXEmbed(useApp());
|
|
305
322
|
|
|
306
323
|
// Everything that goes into the command line, as one string.
|
|
307
324
|
//
|
|
@@ -386,12 +403,15 @@ export function Terminal(props: TerminalProps): ReactElement {
|
|
|
386
403
|
// --- which half of the component is running ------------------------------
|
|
387
404
|
//
|
|
388
405
|
// `'vt'` asks for it outright; `'auto'` arrives here only after the PATH
|
|
389
|
-
// probe found no emulator
|
|
390
|
-
//
|
|
391
|
-
//
|
|
392
|
-
//
|
|
406
|
+
// probe found no emulator — or without probing at all on an app that could
|
|
407
|
+
// not host one it found, which on a Mac with XQuartz is exactly what `PATH`
|
|
408
|
+
// offers. That is what makes vt the floor rather than a parallel option.
|
|
409
|
+
// Both halves' hooks run unconditionally — the one that is not in use is
|
|
410
|
+
// simply disabled — because that is what hooks require and because a
|
|
411
|
+
// `<Terminal>` that flips backends must not remount the other one.
|
|
393
412
|
const [vtFallback, setVtFallback] = useState(false);
|
|
394
|
-
const vt =
|
|
413
|
+
const vt =
|
|
414
|
+
backend === 'vt' || (backend === 'auto' && (vtFallback || !embeddable));
|
|
395
415
|
useEffect(() => {
|
|
396
416
|
if (backend !== 'auto') setVtFallback(false);
|
|
397
417
|
}, [backend]);
|
|
@@ -549,11 +569,19 @@ export function Terminal(props: TerminalProps): ReactElement {
|
|
|
549
569
|
|
|
550
570
|
// No backend installed is an ordinary state of a perfectly healthy machine,
|
|
551
571
|
// not an error to report — the same call `useDesktopCalendarEvents` makes
|
|
552
|
-
// about a desktop with no Evolution Data Server.
|
|
572
|
+
// about a desktop with no Evolution Data Server. So is an emulator named on
|
|
573
|
+
// an app that cannot host one.
|
|
553
574
|
if (client.status === 'unavailable' && props.fallback !== undefined) {
|
|
554
575
|
return h(React.Fragment, null, props.fallback);
|
|
555
576
|
}
|
|
556
577
|
|
|
578
|
+
// …and without a `fallback` that app still gets no `<foreign>`, which has
|
|
579
|
+
// no socket to be there: on the Cocoa backend it hands `onReady` a window id
|
|
580
|
+
// of `undefined`, and on core's headless mock mounting one throws from the
|
|
581
|
+
// commit. A box in the terminal's own background holds the layout, as it
|
|
582
|
+
// does while the vt module loads.
|
|
583
|
+
if (!embeddable) return hx('box', { style: styles });
|
|
584
|
+
|
|
557
585
|
const foreign: ForeignElementProps = {
|
|
558
586
|
onReady: client.handleReady,
|
|
559
587
|
onEmbedded: title.onEmbedded,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/markdown/ast.ts"],"names":[],"mappings":"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"}
|
package/dist/markdown/ast.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// The markdown AST `parse.ts` produces and `index.ts` renders. Deliberately
|
|
2
|
-
// small: it is the GFM constructs this component draws, not a general
|
|
3
|
-
// mdast — but it is shaped so that growing it stays additive. In particular
|
|
4
|
-
// `component` is the MDX direction: `ComponentBlock` is parsed and rendered
|
|
5
|
-
// (docs/prd-mdx.md, M1), and `ComponentInline` is still reserved — see the
|
|
6
|
-
// note on it for why the inline half needs machinery `<richtext>` does not
|
|
7
|
-
// have yet.
|
|
8
|
-
/**
|
|
9
|
-
* A spread (`{...props}`) is an attribute with no name, so it is keyed
|
|
10
|
-
* `...0`, `...1`, … in the order it appeared. An attribute name cannot start
|
|
11
|
-
* with `.`, so these cannot collide with one; a renderer walking
|
|
12
|
-
* `Object.entries` in insertion order applies each spread where it was
|
|
13
|
-
* written, which is what decides whether it overrides `height` or `height`
|
|
14
|
-
* overrides it.
|
|
15
|
-
*/
|
|
16
|
-
export const SPREAD_PREFIX = '...';
|
|
17
|
-
//# sourceMappingURL=ast.js.map
|
package/dist/markdown/ast.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/markdown/ast.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,sEAAsE;AACtE,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,YAAY;AAqEZ;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/markdown/parse.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAGV,QAAQ,EACR,UAAU,EAGV,YAAY,EAEb,MAAM,UAAU,CAAC;AA4IlB,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,QAAQ,CAkB1E;AA+kBD;;;GAGG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,WAAW,UAAQ,GAClB,UAAU,EAAE,CA8Pd"}
|