@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,439 @@
|
|
|
1
|
+
// Copy and paste, in the formats every other application speaks.
|
|
2
|
+
//
|
|
3
|
+
// A copy offers three things: `text/html` (the schema's `toDOM`, with
|
|
4
|
+
// ProseMirror's `data-pm-slice` marker so the open sides survive), plain text
|
|
5
|
+
// (the `clipboardTextSerializer` prop, or blocks a blank line apart — the
|
|
6
|
+
// markdown editor's default hands markdown here), and, in this process only,
|
|
7
|
+
// the slice itself. The last is not an optimisation: react-x11's Cocoa
|
|
8
|
+
// clipboard carries one flavour, text (react-x11 docs/clipboard.md,
|
|
9
|
+
// "Limits"), so without it a copy and paste inside one editor would flatten
|
|
10
|
+
// every heading and list on a Mac. A paste whose text is exactly what this
|
|
11
|
+
// process last put there takes the slice back instead of re-reading it.
|
|
12
|
+
//
|
|
13
|
+
// Reading is prosemirror-view's `parseFromClipboard`, ported: the same
|
|
14
|
+
// props, in the same order, over the DOM shim in `./html.ts` instead of a
|
|
15
|
+
// browser's — so a paste from a web page is read by the schema's own
|
|
16
|
+
// `parseDOM` rules, and a plugin's `transformPasted` sees what it would see
|
|
17
|
+
// in a browser. The helpers below `parseFromClipboard` are prosemirror-view's
|
|
18
|
+
// own (MIT), which does not export them; they are pure functions over the
|
|
19
|
+
// model and came across unchanged.
|
|
20
|
+
import { DOMParser, DOMSerializer, Fragment, Slice } from 'prosemirror-model';
|
|
21
|
+
import type {
|
|
22
|
+
Node as PMNode,
|
|
23
|
+
NodeType,
|
|
24
|
+
ParseOptions,
|
|
25
|
+
ResolvedPos,
|
|
26
|
+
} from 'prosemirror-model';
|
|
27
|
+
import type { EditorProps, EditorView } from 'prosemirror-view';
|
|
28
|
+
|
|
29
|
+
import { clipboardHTML, readHTML, sliceFromElement } from './html.js';
|
|
30
|
+
import type { ShimElement } from './html.js';
|
|
31
|
+
import type { EditorState } from 'prosemirror-state';
|
|
32
|
+
|
|
33
|
+
/** What the clipboard code needs of the view — `someProp` over the props a
|
|
34
|
+
* plugin can supply, and the state. */
|
|
35
|
+
export interface ClipboardView {
|
|
36
|
+
readonly state: EditorState;
|
|
37
|
+
someProp<K extends keyof EditorProps, R>(
|
|
38
|
+
name: K,
|
|
39
|
+
f: (value: NonNullable<EditorProps[K]>) => R,
|
|
40
|
+
): R | undefined;
|
|
41
|
+
/** The view as a plugin's callback is handed it. */
|
|
42
|
+
readonly asEditorView: EditorView;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** What a copy puts on the clipboard. */
|
|
46
|
+
export interface ClipboardContent {
|
|
47
|
+
html: string;
|
|
48
|
+
text: string;
|
|
49
|
+
slice: Slice;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function serializeForClipboard(
|
|
53
|
+
view: ClipboardView,
|
|
54
|
+
slice: Slice,
|
|
55
|
+
): ClipboardContent {
|
|
56
|
+
view.someProp('transformCopied', (f) => {
|
|
57
|
+
slice = f(slice, view.asEditorView);
|
|
58
|
+
});
|
|
59
|
+
const context: unknown[] = [];
|
|
60
|
+
let { content, openStart, openEnd } = slice;
|
|
61
|
+
while (
|
|
62
|
+
openStart > 1 &&
|
|
63
|
+
openEnd > 1 &&
|
|
64
|
+
content.childCount === 1 &&
|
|
65
|
+
content.firstChild!.childCount === 1
|
|
66
|
+
) {
|
|
67
|
+
openStart--;
|
|
68
|
+
openEnd--;
|
|
69
|
+
const node = content.firstChild!;
|
|
70
|
+
// a node made with no attributes shares its type's default object —
|
|
71
|
+
// ProseMirror's own test, through a member its declarations leave out
|
|
72
|
+
const defaults = (node.type as unknown as { defaultAttrs?: unknown })
|
|
73
|
+
.defaultAttrs;
|
|
74
|
+
context.push(node.type.name, node.attrs !== defaults ? node.attrs : null);
|
|
75
|
+
content = node.content;
|
|
76
|
+
}
|
|
77
|
+
const serializer =
|
|
78
|
+
view.someProp('clipboardSerializer', (s) => s) ??
|
|
79
|
+
DOMSerializer.fromSchema(view.state.schema);
|
|
80
|
+
const html = clipboardHTML(serializer, content, openStart, openEnd, context);
|
|
81
|
+
const text =
|
|
82
|
+
view.someProp('clipboardTextSerializer', (f) =>
|
|
83
|
+
f(slice, view.asEditorView),
|
|
84
|
+
) || slice.content.textBetween(0, slice.content.size, '\n\n');
|
|
85
|
+
return { html, text, slice };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const INLINE_PARENTS =
|
|
89
|
+
/^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;
|
|
90
|
+
|
|
91
|
+
/** A paste — or a drop — read into a slice. `plainText` is a paste that
|
|
92
|
+
* asked for no formatting (Shift held). */
|
|
93
|
+
export function parseFromClipboard(
|
|
94
|
+
view: ClipboardView,
|
|
95
|
+
text: string | null,
|
|
96
|
+
html: string | null,
|
|
97
|
+
plainText: boolean,
|
|
98
|
+
$context: ResolvedPos,
|
|
99
|
+
): Slice | null {
|
|
100
|
+
const { schema } = view.state;
|
|
101
|
+
const inCode = !!$context.parent.type.spec.code;
|
|
102
|
+
let dom: ShimElement | null = null;
|
|
103
|
+
let slice: Slice | null = null;
|
|
104
|
+
if (!html && !text) return null;
|
|
105
|
+
const asText = !!text && (plainText || inCode || !html);
|
|
106
|
+
if (asText) {
|
|
107
|
+
let pasted = text!;
|
|
108
|
+
view.someProp('transformPastedText', (f) => {
|
|
109
|
+
pasted = f(pasted, inCode || plainText, view.asEditorView);
|
|
110
|
+
});
|
|
111
|
+
if (inCode) {
|
|
112
|
+
let codeSlice = pasted
|
|
113
|
+
? new Slice(
|
|
114
|
+
Fragment.from(schema.text(pasted.replace(/\r\n?/g, '\n'))),
|
|
115
|
+
0,
|
|
116
|
+
0,
|
|
117
|
+
)
|
|
118
|
+
: Slice.empty;
|
|
119
|
+
view.someProp('transformPasted', (f) => {
|
|
120
|
+
codeSlice = f(codeSlice, view.asEditorView, true);
|
|
121
|
+
});
|
|
122
|
+
return codeSlice;
|
|
123
|
+
}
|
|
124
|
+
const parsed = view.someProp('clipboardTextParser', (f) =>
|
|
125
|
+
f(pasted, $context, plainText, view.asEditorView),
|
|
126
|
+
);
|
|
127
|
+
if (parsed) {
|
|
128
|
+
slice = parsed;
|
|
129
|
+
} else {
|
|
130
|
+
// One paragraph per line, carrying the marks at the caret — what
|
|
131
|
+
// ProseMirror builds as `<p>`s and parses, built directly.
|
|
132
|
+
const marks = $context.marks();
|
|
133
|
+
const paragraph = defaultTextblock($context);
|
|
134
|
+
if (paragraph) {
|
|
135
|
+
const blocks = pasted
|
|
136
|
+
.split(/(?:\r\n?|\n)+/)
|
|
137
|
+
.map((line) =>
|
|
138
|
+
paragraph.create(null, line ? schema.text(line, marks) : null),
|
|
139
|
+
);
|
|
140
|
+
slice = Slice.maxOpen(Fragment.from(blocks));
|
|
141
|
+
} else {
|
|
142
|
+
slice = new Slice(Fragment.from(schema.text(pasted, marks)), 0, 0);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
let source = html!;
|
|
147
|
+
view.someProp('transformPastedHTML', (f) => {
|
|
148
|
+
source = f(source, view.asEditorView);
|
|
149
|
+
});
|
|
150
|
+
dom = readHTML(source);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const contextNode = dom
|
|
154
|
+
? dom.matches('[data-pm-slice]')
|
|
155
|
+
? dom
|
|
156
|
+
: dom.querySelector('[data-pm-slice]')
|
|
157
|
+
: null;
|
|
158
|
+
const sliceData =
|
|
159
|
+
contextNode &&
|
|
160
|
+
/^(\d+) (\d+)(?: -(\d+))? (.*)/.exec(
|
|
161
|
+
contextNode.getAttribute('data-pm-slice') || '',
|
|
162
|
+
);
|
|
163
|
+
if (dom && sliceData && sliceData[3]) {
|
|
164
|
+
for (let i = +sliceData[3]; i > 0; i--) {
|
|
165
|
+
let child = dom.firstChild;
|
|
166
|
+
while (child && child.nodeType !== 1) child = child.nextSibling;
|
|
167
|
+
if (!child) break;
|
|
168
|
+
dom = child as ShimElement;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!slice && dom) {
|
|
173
|
+
const parser =
|
|
174
|
+
view.someProp('clipboardParser', (p) => p) ??
|
|
175
|
+
view.someProp('domParser', (p) => p) ??
|
|
176
|
+
DOMParser.fromSchema(schema);
|
|
177
|
+
// `ruleFromNode` is a parse option prosemirror-view passes and
|
|
178
|
+
// prosemirror-model's declarations leave out: a trailing `<br>` in a
|
|
179
|
+
// block is the browser's placeholder, not a line break
|
|
180
|
+
const options = {
|
|
181
|
+
preserveWhitespace: !!(asText || sliceData),
|
|
182
|
+
context: $context,
|
|
183
|
+
ruleFromNode(node: unknown) {
|
|
184
|
+
const el = node as ShimElement;
|
|
185
|
+
if (
|
|
186
|
+
el.nodeName === 'BR' &&
|
|
187
|
+
!el.nextSibling &&
|
|
188
|
+
el.parentNode &&
|
|
189
|
+
!INLINE_PARENTS.test(el.parentNode.nodeName)
|
|
190
|
+
) {
|
|
191
|
+
return { ignore: true };
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
slice = sliceFromElement(parser, dom, options as ParseOptions);
|
|
197
|
+
}
|
|
198
|
+
if (!slice) return null;
|
|
199
|
+
|
|
200
|
+
if (sliceData) {
|
|
201
|
+
slice = addContext(
|
|
202
|
+
closeSlice(slice, +sliceData[1], +sliceData[2]),
|
|
203
|
+
sliceData[4],
|
|
204
|
+
);
|
|
205
|
+
} else {
|
|
206
|
+
// HTML that did not come from ProseMirror: make the top-level siblings
|
|
207
|
+
// coherent, so `replace` can place them at all
|
|
208
|
+
slice = Slice.maxOpen(normalizeSiblings(slice.content, $context), true);
|
|
209
|
+
if (slice.openStart || slice.openEnd) {
|
|
210
|
+
let openStart = 0;
|
|
211
|
+
let openEnd = 0;
|
|
212
|
+
for (
|
|
213
|
+
let node = slice.content.firstChild;
|
|
214
|
+
openStart < slice.openStart && node && !node.type.spec.isolating;
|
|
215
|
+
openStart++, node = node.firstChild
|
|
216
|
+
) {
|
|
217
|
+
// counting
|
|
218
|
+
}
|
|
219
|
+
for (
|
|
220
|
+
let node = slice.content.lastChild;
|
|
221
|
+
openEnd < slice.openEnd && node && !node.type.spec.isolating;
|
|
222
|
+
openEnd++, node = node.lastChild
|
|
223
|
+
) {
|
|
224
|
+
// counting
|
|
225
|
+
}
|
|
226
|
+
slice = closeSlice(slice, openStart, openEnd);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const result = slice;
|
|
230
|
+
let transformed = result;
|
|
231
|
+
view.someProp('transformPasted', (f) => {
|
|
232
|
+
transformed = f(transformed, view.asEditorView, asText);
|
|
233
|
+
});
|
|
234
|
+
return transformed;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** The textblock a line of pasted text becomes: the first one the context
|
|
238
|
+
* can hold, which in every schema anyone writes is the paragraph. */
|
|
239
|
+
function defaultTextblock($context: ResolvedPos): NodeType | null {
|
|
240
|
+
for (let d = $context.depth; d >= 0; d--) {
|
|
241
|
+
const type = $context.node(d).contentMatchAt($context.index(d)).defaultType;
|
|
242
|
+
if (type?.isTextblock) return type;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// --- prosemirror-view's helpers (MIT, Marijn Haverbeke) ----------------------
|
|
248
|
+
|
|
249
|
+
// Takes a slice parsed with parseSlice, which means there hasn't been any
|
|
250
|
+
// content-expression checking done on the top nodes, tries to find a parent
|
|
251
|
+
// node in the current context that might fit the nodes, and if successful,
|
|
252
|
+
// rebuilds the slice so that it fits into that parent.
|
|
253
|
+
function normalizeSiblings(
|
|
254
|
+
fragment: Fragment,
|
|
255
|
+
$context: ResolvedPos,
|
|
256
|
+
): Fragment {
|
|
257
|
+
if (fragment.childCount < 2) return fragment;
|
|
258
|
+
for (let d = $context.depth; d >= 0; d--) {
|
|
259
|
+
const parent = $context.node(d);
|
|
260
|
+
let match = parent.contentMatchAt($context.index(d));
|
|
261
|
+
let lastWrap: readonly NodeType[] = [];
|
|
262
|
+
let result: PMNode[] | null = [];
|
|
263
|
+
fragment.forEach((node) => {
|
|
264
|
+
if (!result) return;
|
|
265
|
+
const wrap = match.findWrapping(node.type);
|
|
266
|
+
if (!wrap) {
|
|
267
|
+
result = null;
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const inLast =
|
|
271
|
+
result.length &&
|
|
272
|
+
lastWrap.length &&
|
|
273
|
+
addToSibling(wrap, lastWrap, node, result[result.length - 1], 0);
|
|
274
|
+
if (inLast) {
|
|
275
|
+
result[result.length - 1] = inLast;
|
|
276
|
+
} else {
|
|
277
|
+
if (result.length) {
|
|
278
|
+
result[result.length - 1] = closeRight(
|
|
279
|
+
result[result.length - 1],
|
|
280
|
+
lastWrap.length,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const wrapped = withWrappers(node, wrap);
|
|
284
|
+
result.push(wrapped);
|
|
285
|
+
match = match.matchType(wrapped.type)!;
|
|
286
|
+
lastWrap = wrap;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
if (result) return Fragment.from(result);
|
|
290
|
+
}
|
|
291
|
+
return fragment;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function withWrappers(
|
|
295
|
+
node: PMNode,
|
|
296
|
+
wrap: readonly NodeType[],
|
|
297
|
+
from = 0,
|
|
298
|
+
): PMNode {
|
|
299
|
+
for (let i = wrap.length - 1; i >= from; i--) {
|
|
300
|
+
node = wrap[i].create(null, Fragment.from(node));
|
|
301
|
+
}
|
|
302
|
+
return node;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function addToSibling(
|
|
306
|
+
wrap: readonly NodeType[],
|
|
307
|
+
lastWrap: readonly NodeType[],
|
|
308
|
+
node: PMNode,
|
|
309
|
+
sibling: PMNode,
|
|
310
|
+
depth: number,
|
|
311
|
+
): PMNode | undefined {
|
|
312
|
+
if (
|
|
313
|
+
depth < wrap.length &&
|
|
314
|
+
depth < lastWrap.length &&
|
|
315
|
+
wrap[depth] === lastWrap[depth]
|
|
316
|
+
) {
|
|
317
|
+
const inner = addToSibling(
|
|
318
|
+
wrap,
|
|
319
|
+
lastWrap,
|
|
320
|
+
node,
|
|
321
|
+
sibling.lastChild!,
|
|
322
|
+
depth + 1,
|
|
323
|
+
);
|
|
324
|
+
if (inner) {
|
|
325
|
+
return sibling.copy(
|
|
326
|
+
sibling.content.replaceChild(sibling.childCount - 1, inner),
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
const match = sibling.contentMatchAt(sibling.childCount);
|
|
330
|
+
if (
|
|
331
|
+
match.matchType(depth === wrap.length - 1 ? node.type : wrap[depth + 1])
|
|
332
|
+
) {
|
|
333
|
+
return sibling.copy(
|
|
334
|
+
sibling.content.append(
|
|
335
|
+
Fragment.from(withWrappers(node, wrap, depth + 1)),
|
|
336
|
+
),
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function closeRight(node: PMNode, depth: number): PMNode {
|
|
344
|
+
if (depth === 0) return node;
|
|
345
|
+
const fragment = node.content.replaceChild(
|
|
346
|
+
node.childCount - 1,
|
|
347
|
+
closeRight(node.lastChild!, depth - 1),
|
|
348
|
+
);
|
|
349
|
+
const fill = node
|
|
350
|
+
.contentMatchAt(node.childCount)
|
|
351
|
+
.fillBefore(Fragment.empty, true)!;
|
|
352
|
+
return node.copy(fragment.append(fill));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function closeRange(
|
|
356
|
+
fragment: Fragment,
|
|
357
|
+
side: number,
|
|
358
|
+
from: number,
|
|
359
|
+
to: number,
|
|
360
|
+
depth: number,
|
|
361
|
+
openEnd: number,
|
|
362
|
+
): Fragment {
|
|
363
|
+
const node = side < 0 ? fragment.firstChild! : fragment.lastChild!;
|
|
364
|
+
let inner = node.content;
|
|
365
|
+
if (fragment.childCount > 1) openEnd = 0;
|
|
366
|
+
if (depth < to - 1)
|
|
367
|
+
inner = closeRange(inner, side, from, to, depth + 1, openEnd);
|
|
368
|
+
if (depth >= from) {
|
|
369
|
+
inner =
|
|
370
|
+
side < 0
|
|
371
|
+
? node
|
|
372
|
+
.contentMatchAt(0)
|
|
373
|
+
.fillBefore(inner, openEnd <= depth)!
|
|
374
|
+
.append(inner)
|
|
375
|
+
: inner.append(
|
|
376
|
+
node
|
|
377
|
+
.contentMatchAt(node.childCount)
|
|
378
|
+
.fillBefore(Fragment.empty, true)!,
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return fragment.replaceChild(
|
|
382
|
+
side < 0 ? 0 : fragment.childCount - 1,
|
|
383
|
+
node.copy(inner),
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function closeSlice(slice: Slice, openStart: number, openEnd: number): Slice {
|
|
388
|
+
if (openStart < slice.openStart) {
|
|
389
|
+
slice = new Slice(
|
|
390
|
+
closeRange(
|
|
391
|
+
slice.content,
|
|
392
|
+
-1,
|
|
393
|
+
openStart,
|
|
394
|
+
slice.openStart,
|
|
395
|
+
0,
|
|
396
|
+
slice.openEnd,
|
|
397
|
+
),
|
|
398
|
+
openStart,
|
|
399
|
+
slice.openEnd,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
if (openEnd < slice.openEnd) {
|
|
403
|
+
slice = new Slice(
|
|
404
|
+
closeRange(slice.content, 1, openEnd, slice.openEnd, 0, 0),
|
|
405
|
+
slice.openStart,
|
|
406
|
+
openEnd,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
return slice;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function addContext(slice: Slice, context: string): Slice {
|
|
413
|
+
if (!slice.size) return slice;
|
|
414
|
+
const schema = slice.content.firstChild!.type.schema;
|
|
415
|
+
let array: unknown[];
|
|
416
|
+
try {
|
|
417
|
+
array = JSON.parse(context) as unknown[];
|
|
418
|
+
} catch {
|
|
419
|
+
return slice;
|
|
420
|
+
}
|
|
421
|
+
let { content, openStart, openEnd } = slice;
|
|
422
|
+
for (let i = array.length - 2; i >= 0; i -= 2) {
|
|
423
|
+
const type = schema.nodes[array[i] as string];
|
|
424
|
+
if (!type || type.hasRequiredAttrs()) break;
|
|
425
|
+
try {
|
|
426
|
+
(type as unknown as { checkAttrs(attrs: unknown): void }).checkAttrs(
|
|
427
|
+
array[i + 1],
|
|
428
|
+
);
|
|
429
|
+
} catch {
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
content = Fragment.from(
|
|
433
|
+
type.create(array[i + 1] as Record<string, unknown>, content),
|
|
434
|
+
);
|
|
435
|
+
openStart++;
|
|
436
|
+
openEnd++;
|
|
437
|
+
}
|
|
438
|
+
return new Slice(content, openStart, openEnd);
|
|
439
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Collaboration: what the editor draws for a collaborator's caret.
|
|
2
|
+
//
|
|
3
|
+
// y-prosemirror's cursor plugin — the one a Yjs-backed ProseMirror editor
|
|
4
|
+
// runs — shows another user's caret as a *widget decoration* whose `toDOM`
|
|
5
|
+
// builds an element: its `cursorBuilder` option, which by default makes a
|
|
6
|
+
// `<span>` with the user's name in it. A widget is DOM, and this view has no
|
|
7
|
+
// DOM to draw it with (./view.ts, "what a DOM view would draw that this one
|
|
8
|
+
// cannot"). So the editor supplies a builder of its own, `remoteCaret`, that
|
|
9
|
+
// returns not an element but a description of one — whose caret it is, and
|
|
10
|
+
// in what colour — and the view, meeting a widget with no `text` to draw,
|
|
11
|
+
// asks its `toDOM` once and draws what comes back as a caret beside the
|
|
12
|
+
// editor's own: a bar and a flag, set on the textblock's element the way the
|
|
13
|
+
// local caret is, re-rendering nothing.
|
|
14
|
+
//
|
|
15
|
+
// The other half of y-prosemirror's cursor plugin, the collaborator's
|
|
16
|
+
// selection, is an inline decoration with a `style` — a background colour —
|
|
17
|
+
// and the view draws that already.
|
|
18
|
+
import type { Decoration, EditorView } from 'prosemirror-view';
|
|
19
|
+
|
|
20
|
+
// a registered symbol, so two copies of this package in one app agree
|
|
21
|
+
const CARET = Symbol.for('@react-x11/components/rich-text-editor:remote-caret');
|
|
22
|
+
|
|
23
|
+
/** A collaborator's caret, as `remoteCaret` describes it. */
|
|
24
|
+
export interface RemoteCaret {
|
|
25
|
+
/** The bar's and the flag's colour — the awareness state's `user.color`. */
|
|
26
|
+
readonly color: string;
|
|
27
|
+
/** Whose caret it is — carried along; the caret is drawn without it. */
|
|
28
|
+
readonly name: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* y-prosemirror's `cursorBuilder`, for this editor:
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* yCursorPlugin(awareness, { cursorBuilder: remoteCaret })
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* `user` is the awareness state's `user` field, `{ name, color }`, with the
|
|
39
|
+
* colour a `#rrggbb` (y-prosemirror's own requirement). Typed as the
|
|
40
|
+
* `HTMLElement` y-prosemirror asks a builder for; what it returns is a
|
|
41
|
+
* description only this editor's view reads.
|
|
42
|
+
*/
|
|
43
|
+
export function remoteCaret(
|
|
44
|
+
user?: { name?: unknown; color?: unknown } | null,
|
|
45
|
+
): HTMLElement {
|
|
46
|
+
const caret = {
|
|
47
|
+
[CARET]: true,
|
|
48
|
+
color: typeof user?.color === 'string' ? user.color : '#ffa500',
|
|
49
|
+
name: typeof user?.name === 'string' ? user.name : '',
|
|
50
|
+
};
|
|
51
|
+
return caret as unknown as HTMLElement;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isRemoteCaret(value: unknown): value is RemoteCaret {
|
|
55
|
+
return (
|
|
56
|
+
typeof value === 'object' &&
|
|
57
|
+
value !== null &&
|
|
58
|
+
(value as Record<symbol, unknown>)[CARET] === true
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** What a widget's `toDOM` made, by the widget's type — which a mapped
|
|
63
|
+
* decoration keeps, so a caret that only moved is not asked again. */
|
|
64
|
+
const made = new WeakMap<object, RemoteCaret | null>();
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The caret a widget decoration stands for, when its `toDOM` is
|
|
68
|
+
* `remoteCaret`'s — or null for any other widget, which has nothing here to
|
|
69
|
+
* draw it with. A `toDOM` that reaches for `document` throws under this view:
|
|
70
|
+
* that is a widget written for a browser, skipped like the rest.
|
|
71
|
+
*/
|
|
72
|
+
export function caretOf(
|
|
73
|
+
deco: Decoration,
|
|
74
|
+
view: EditorView,
|
|
75
|
+
): RemoteCaret | null {
|
|
76
|
+
const type = (deco as unknown as { type?: { toDOM?: unknown } }).type;
|
|
77
|
+
if (!type || typeof type.toDOM !== 'function') return null;
|
|
78
|
+
const known = made.get(type);
|
|
79
|
+
if (known !== undefined) return known;
|
|
80
|
+
let caret: RemoteCaret | null = null;
|
|
81
|
+
try {
|
|
82
|
+
const out = (
|
|
83
|
+
type.toDOM as (view: EditorView, getPos: () => number) => unknown
|
|
84
|
+
)(view, () => deco.from);
|
|
85
|
+
caret = isRemoteCaret(out) ? out : null;
|
|
86
|
+
} catch {
|
|
87
|
+
caret = null;
|
|
88
|
+
}
|
|
89
|
+
made.set(type, caret);
|
|
90
|
+
return caret;
|
|
91
|
+
}
|