@dxos/react-ui-editor 0.3.9-main.604ae36 → 0.3.9-main.622f05b

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.
Files changed (144) hide show
  1. package/dist/lib/browser/index.mjs +974 -499
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts +2 -2
  5. package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +1 -1
  6. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts +2 -2
  7. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +1 -1
  8. package/dist/types/src/automerge/automerge-plugin/handle.d.ts +9 -0
  9. package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +1 -0
  10. package/dist/types/src/automerge/automerge-plugin/index.d.ts +2 -2
  11. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -1
  12. package/dist/types/src/automerge/automerge-plugin/plugin.d.ts +6 -5
  13. package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +1 -1
  14. package/dist/types/src/automerge/automerge.stories.d.ts +2 -1
  15. package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -1
  16. package/dist/types/src/components/TextEditor/TextEditor.d.ts +43 -0
  17. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -0
  18. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +15 -0
  19. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -0
  20. package/dist/types/{stories/html/code-mirror.stories.d.ts → src/components/TextEditor/codemirror.stories.d.ts} +2 -2
  21. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +1 -0
  22. package/dist/types/src/components/TextEditor/extensions/basic/bundle.d.ts +8 -0
  23. package/dist/types/src/components/TextEditor/extensions/basic/bundle.d.ts.map +1 -0
  24. package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts +3 -0
  25. package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts.map +1 -0
  26. package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts +10 -0
  27. package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts.map +1 -0
  28. package/dist/types/src/components/TextEditor/extensions/change.d.ts +7 -0
  29. package/dist/types/src/components/TextEditor/extensions/change.d.ts.map +1 -0
  30. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts +11 -0
  31. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts.map +1 -0
  32. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts +8 -0
  33. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts.map +1 -0
  34. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.d.ts +10 -0
  35. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.d.ts.map +1 -0
  36. package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts +4 -0
  37. package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts.map +1 -0
  38. package/dist/types/src/components/TextEditor/extensions/index.d.ts +5 -0
  39. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -0
  40. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +8 -0
  41. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -0
  42. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +3 -0
  43. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +1 -0
  44. package/dist/types/src/components/{Markdown/markdownTags.d.ts → TextEditor/extensions/markdown/tags.d.ts} +1 -1
  45. package/dist/types/src/components/TextEditor/extensions/markdown/tags.d.ts.map +1 -0
  46. package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts +21 -0
  47. package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts.map +1 -0
  48. package/dist/types/src/components/TextEditor/index.d.ts +4 -0
  49. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -0
  50. package/dist/types/src/components/TextEditor/yjs.stories.d.ts +11 -0
  51. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +1 -0
  52. package/dist/types/src/components/index.d.ts +1 -3
  53. package/dist/types/src/components/index.d.ts.map +1 -1
  54. package/dist/types/src/hooks/index.d.ts +4 -0
  55. package/dist/types/src/hooks/index.d.ts.map +1 -0
  56. package/dist/types/src/hooks/useCollaboration.d.ts +9 -0
  57. package/dist/types/src/hooks/useCollaboration.d.ts.map +1 -0
  58. package/dist/types/src/{model.d.ts → hooks/useTextModel.d.ts} +3 -15
  59. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -0
  60. package/dist/types/src/hooks/yjs/cursors.d.ts.map +1 -0
  61. package/dist/types/src/hooks/yjs/index.d.ts.map +1 -0
  62. package/dist/types/src/hooks/yjs/space-provider.d.ts.map +1 -0
  63. package/dist/types/src/index.d.ts +2 -2
  64. package/dist/types/src/index.d.ts.map +1 -1
  65. package/dist/types/src/styles/tokens.d.ts +4 -0
  66. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  67. package/dist/types/src/testing/replicator.d.ts +1 -1
  68. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  69. package/package.json +35 -27
  70. package/src/automerge/automerge-plugin/PatchSemaphore.ts +5 -5
  71. package/src/automerge/automerge-plugin/codeMirrorToAm.ts +10 -7
  72. package/src/automerge/automerge-plugin/handle.ts +14 -0
  73. package/src/automerge/automerge-plugin/index.ts +2 -2
  74. package/src/automerge/automerge-plugin/plugin.ts +39 -10
  75. package/src/automerge/automerge.stories.tsx +32 -48
  76. package/src/components/TextEditor/TextEditor.stories.tsx +79 -0
  77. package/src/components/TextEditor/TextEditor.tsx +188 -0
  78. package/src/components/TextEditor/codemirror.stories.ts +114 -0
  79. package/src/components/TextEditor/extensions/basic/bundle.ts +28 -0
  80. package/src/components/TextEditor/extensions/basic/index.ts +6 -0
  81. package/src/components/TextEditor/extensions/basic/theme.ts +49 -0
  82. package/src/components/TextEditor/extensions/change.ts +22 -0
  83. package/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.ts +99 -0
  84. package/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.tsx +61 -0
  85. package/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.tsx +119 -0
  86. package/src/components/TextEditor/extensions/hyperlink/index.ts +7 -0
  87. package/src/components/TextEditor/extensions/index.ts +8 -0
  88. package/src/components/TextEditor/extensions/markdown/bundle.ts +99 -0
  89. package/src/components/TextEditor/extensions/markdown/index.ts +6 -0
  90. package/src/components/{Markdown/markdownTheme.ts → TextEditor/extensions/markdown/theme.ts} +42 -27
  91. package/src/components/TextEditor/index.ts +8 -0
  92. package/src/components/TextEditor/yjs.stories.tsx +55 -0
  93. package/src/components/index.ts +1 -3
  94. package/src/hooks/index.ts +7 -0
  95. package/src/hooks/useCollaboration.ts +45 -0
  96. package/src/{model.ts → hooks/useTextModel.ts} +42 -17
  97. package/src/index.ts +3 -3
  98. package/src/styles/tokens.ts +6 -0
  99. package/src/testing/replicator.ts +1 -3
  100. package/dist/types/src/automerge/demo.d.ts +0 -25
  101. package/dist/types/src/automerge/demo.d.ts.map +0 -1
  102. package/dist/types/src/components/Editor/Editor.d.ts +0 -15
  103. package/dist/types/src/components/Editor/Editor.d.ts.map +0 -1
  104. package/dist/types/src/components/Editor/Editor.stories.d.ts +0 -28
  105. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +0 -1
  106. package/dist/types/src/components/Editor/index.d.ts +0 -2
  107. package/dist/types/src/components/Editor/index.d.ts.map +0 -1
  108. package/dist/types/src/components/Markdown/Markdown.d.ts +0 -19
  109. package/dist/types/src/components/Markdown/Markdown.d.ts.map +0 -1
  110. package/dist/types/src/components/Markdown/Markdown.stories.d.ts +0 -19
  111. package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +0 -1
  112. package/dist/types/src/components/Markdown/index.d.ts +0 -2
  113. package/dist/types/src/components/Markdown/index.d.ts.map +0 -1
  114. package/dist/types/src/components/Markdown/markdownTags.d.ts.map +0 -1
  115. package/dist/types/src/components/Markdown/markdownTheme.d.ts +0 -107
  116. package/dist/types/src/components/Markdown/markdownTheme.d.ts.map +0 -1
  117. package/dist/types/src/components/RichText/RichText.d.ts +0 -20
  118. package/dist/types/src/components/RichText/RichText.d.ts.map +0 -1
  119. package/dist/types/src/components/RichText/RichText.stories.d.ts +0 -26
  120. package/dist/types/src/components/RichText/RichText.stories.d.ts.map +0 -1
  121. package/dist/types/src/components/RichText/index.d.ts +0 -2
  122. package/dist/types/src/components/RichText/index.d.ts.map +0 -1
  123. package/dist/types/src/model.d.ts.map +0 -1
  124. package/dist/types/src/yjs/cursors.d.ts.map +0 -1
  125. package/dist/types/src/yjs/index.d.ts.map +0 -1
  126. package/dist/types/src/yjs/space-provider.d.ts.map +0 -1
  127. package/dist/types/stories/html/code-mirror.stories.d.ts.map +0 -1
  128. package/src/automerge/demo.ts +0 -106
  129. package/src/components/Editor/Editor.stories.tsx +0 -80
  130. package/src/components/Editor/Editor.tsx +0 -33
  131. package/src/components/Editor/index.ts +0 -5
  132. package/src/components/Markdown/Markdown.stories.tsx +0 -56
  133. package/src/components/Markdown/Markdown.tsx +0 -206
  134. package/src/components/Markdown/index.ts +0 -5
  135. package/src/components/RichText/RichText.stories.tsx +0 -68
  136. package/src/components/RichText/RichText.tsx +0 -181
  137. package/src/components/RichText/index.ts +0 -5
  138. /package/dist/types/src/{yjs → hooks/yjs}/cursors.d.ts +0 -0
  139. /package/dist/types/src/{yjs → hooks/yjs}/index.d.ts +0 -0
  140. /package/dist/types/src/{yjs → hooks/yjs}/space-provider.d.ts +0 -0
  141. /package/src/components/{Markdown/markdownTags.ts → TextEditor/extensions/markdown/tags.ts} +0 -0
  142. /package/src/{yjs → hooks/yjs}/cursors.ts +0 -0
  143. /package/src/{yjs → hooks/yjs}/index.ts +0 -0
  144. /package/src/{yjs → hooks/yjs}/space-provider.ts +0 -0
@@ -1,199 +1,306 @@
1
- // packages/ui/react-ui-editor/src/components/Editor/Editor.tsx
2
- import React3, { forwardRef as forwardRef3, memo } from "react";
3
- import { YXmlFragment } from "@dxos/text-model";
1
+ // packages/ui/react-ui-editor/src/index.ts
2
+ import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
3
+ import { Doc, YText as YText2, YXmlFragment } from "@dxos/text-model";
4
4
 
5
- // packages/ui/react-ui-editor/src/model.ts
6
- import { useMemo } from "react";
5
+ // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
+ import { tags as tags2 } from "@lezer/highlight";
7
7
 
8
- // packages/ui/react-ui-editor/src/yjs/cursors.ts
9
- import * as random from "lib0/random";
10
- var cursorColors = [
11
- {
12
- color: "#30bced",
13
- light: "#30bced33"
8
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/bundle.ts
9
+ import { closeBrackets } from "@codemirror/autocomplete";
10
+ import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
11
+ import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
12
+ import { EditorView, placeholder } from "@codemirror/view";
13
+ var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
14
+ bracketMatching(),
15
+ closeBrackets(),
16
+ _placeholder && placeholder(_placeholder),
17
+ EditorView.lineWrapping,
18
+ // TODO(burdon): Is this required?
19
+ themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
20
+ ].filter(Boolean);
21
+
22
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
23
+ import get from "lodash.get";
24
+
25
+ // packages/ui/react-ui-editor/src/styles/markdown.ts
26
+ import { mx } from "@dxos/react-ui-theme";
27
+ var heading = {
28
+ 1: "mbs-4 mbe-2 text-4xl font-semibold text-inherit no-underline",
29
+ 2: "mbs-4 mbe-2 text-3xl font-bold text-inherit no-underline",
30
+ 3: "mbs-4 mbe-2 text-2xl font-bold text-inherit no-underline",
31
+ 4: "mbs-4 mbe-2 text-xl font-extrabold text-inherit no-underline",
32
+ 5: "mbs-4 mbe-2 text-lg font-extrabold text-inherit no-underline",
33
+ 6: "mbs-4 mbe-2 font-black text-inherit no-underline"
34
+ };
35
+ var bold = "font-bold";
36
+ var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
37
+ var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
38
+ var italic = "italic";
39
+ var strikethrough = "line-through";
40
+ var mark = "!font-normal !no-underline !text-inherit opacity-40";
41
+
42
+ // packages/ui/react-ui-editor/src/styles/tokens.ts
43
+ import { tailwindConfig } from "@dxos/react-ui-theme";
44
+ var tokens = tailwindConfig({}).theme;
45
+
46
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
47
+ var basicTheme = {
48
+ "&.cm-focused": {
49
+ outline: "none"
14
50
  },
15
- {
16
- color: "#6eeb83",
17
- light: "#6eeb8333"
51
+ "& .cm-line": {
52
+ paddingInline: 0,
53
+ lineHeight: 1.6,
54
+ minBlockSize: "1.6em"
18
55
  },
19
- {
20
- color: "#ffbc42",
21
- light: "#ffbc4233"
56
+ "& .cm-line *": {
57
+ lineHeight: 1.6
22
58
  },
23
- {
24
- color: "#ecd444",
25
- light: "#ecd44433"
59
+ "& .cm-scroller": {
60
+ fontFamily: get(tokens, "fontFamily.body", []).join(","),
61
+ overflow: "visible"
26
62
  },
27
- {
28
- color: "#ee6352",
29
- light: "#ee635233"
63
+ "& .cm-content": {
64
+ padding: 0,
65
+ caretColor: "black"
30
66
  },
31
- {
32
- color: "#9ac2c9",
33
- light: "#9ac2c933"
67
+ ".dark & .cm-content": {
68
+ caretColor: "white"
34
69
  },
35
- {
36
- color: "#8acb88",
37
- light: "#8acb8833"
70
+ "& .cm-tooltip": {
71
+ backgroundColor: "transparent",
72
+ border: "none"
38
73
  },
39
- {
40
- color: "#1be7ff",
41
- light: "#1be7ff33"
74
+ "& .cm-link": {
75
+ color: get(tokens, "extend.colors.primary.500"),
76
+ textDecoration: "underline"
42
77
  }
43
- ];
44
- var cursorColor = cursorColors[random.uint32() % cursorColors.length];
78
+ };
45
79
 
46
- // packages/ui/react-ui-editor/src/yjs/space-provider.ts
47
- import * as decoding from "lib0/decoding";
48
- import * as encoding from "lib0/encoding";
49
- import { Observable } from "lib0/observable";
50
- import * as awarenessProtocol from "y-protocols/awareness";
51
- import { log } from "@dxos/log";
52
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/yjs/space-provider.ts";
53
- var messageAwareness = 1;
54
- var messageQueryAwareness = 3;
55
- var SpaceAwarenessProvider = class extends Observable {
56
- constructor({ space, doc, channel, awareness }) {
57
- super();
58
- this._space = space;
59
- this._awareness = awareness ?? new awarenessProtocol.Awareness(doc);
60
- this._channel = channel;
61
- this._clientId = doc.clientID;
62
- this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
63
- this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
64
- if (typeof window !== "undefined") {
65
- window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
66
- } else if (typeof process !== "undefined") {
67
- process.on("exit", this._handleBeforeUnload.bind(this));
80
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/change.ts
81
+ import { StateField } from "@codemirror/state";
82
+ var onChangeExtension = (onChange) => StateField.define({
83
+ create: () => null,
84
+ update: (_value, transaction) => {
85
+ if (transaction.docChanged && onChange) {
86
+ onChange(transaction.newDoc.toString());
68
87
  }
69
- const encoderAwarenessQuery = encoding.createEncoder();
70
- encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
71
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
72
- const encoderAwarenessState = encoding.createEncoder();
73
- encoding.writeVarUint(encoderAwarenessState, messageAwareness);
74
- encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [
75
- this._clientId
76
- ]));
77
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
88
+ return null;
78
89
  }
79
- get awareness() {
80
- return this._awareness;
90
+ });
91
+
92
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.ts
93
+ import { syntaxTree } from "@codemirror/language";
94
+ import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
95
+ import { Decoration, EditorView as EditorView2, WidgetType } from "@codemirror/view";
96
+ var Link = class extends WidgetType {
97
+ constructor(_anchor, _text, _url) {
98
+ super();
99
+ this._anchor = _anchor;
100
+ this._text = _text;
101
+ this._url = _url;
81
102
  }
82
- _handleAwarenessUpdate({ added, updated, removed }, origin) {
83
- log("awareness update", {
84
- added,
85
- updated,
86
- removed,
87
- origin
88
- }, {
89
- F: __dxlog_file,
90
- L: 67,
91
- S: this,
92
- C: (f, a) => f(...a)
93
- });
94
- const changedClients = added.concat(updated).concat(removed);
95
- const encoderAwareness = encoding.createEncoder();
96
- encoding.writeVarUint(encoderAwareness, messageAwareness);
97
- encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
98
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
103
+ eq(other) {
104
+ return this._text === other.text && this._url === other.url;
99
105
  }
100
- _handleSpaceMessage({ payload }) {
101
- log("space message", payload, {
102
- F: __dxlog_file,
103
- L: 79,
104
- S: this,
105
- C: (f, a) => f(...a)
106
- });
107
- const data = new Uint8Array(Array.from(Object.values(payload)));
108
- const encoder = this._readMessage(data);
109
- if (encoder) {
110
- void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
106
+ toDOM(view) {
107
+ const link = document.createElement("a");
108
+ link.setAttribute("class", "cm-link");
109
+ link.textContent = this._text;
110
+ if (this._url) {
111
+ link.setAttribute("target", "_blank");
112
+ link.href = this._url;
113
+ } else {
114
+ link.onclick = () => {
115
+ view.dispatch({
116
+ selection: {
117
+ anchor: this._anchor
118
+ }
119
+ });
120
+ };
111
121
  }
122
+ return link;
112
123
  }
113
- _readMessage(message) {
114
- const decoder = decoding.createDecoder(message);
115
- const encoder = encoding.createEncoder();
116
- const messageType = decoding.readVarUint(decoder);
117
- let sendReply = false;
118
- switch (messageType) {
119
- case messageAwareness: {
120
- awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
121
- break;
122
- }
123
- case messageQueryAwareness: {
124
- encoding.writeVarUint(encoder, messageAwareness);
125
- encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
126
- sendReply = true;
127
- break;
128
- }
129
- default: {
130
- console.error("Invalid message:", messageType);
131
- return encoder;
124
+ };
125
+ var hyperlinkDecoration = ({ link } = {}) => {
126
+ return StateField2.define({
127
+ create: (state) => update(state, link),
128
+ update: (_, tr) => update(tr.state, link),
129
+ provide: (field) => EditorView2.decorations.from(field)
130
+ });
131
+ };
132
+ var update = (state, link) => {
133
+ const builder = new RangeSetBuilder();
134
+ const cursor = state.selection.main.head;
135
+ syntaxTree(state).iterate({
136
+ enter: (node) => {
137
+ if (cursor < node.from || cursor > node.to) {
138
+ if (node.name === "Link") {
139
+ const marks = node.node.getChildren("LinkMark");
140
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
141
+ const urlNode = node.node.getChild("URL");
142
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
143
+ builder.add(node.from, node.to, Decoration.replace({
144
+ widget: new Link(node.from, text, link ? url : void 0)
145
+ }));
146
+ return false;
147
+ }
132
148
  }
149
+ return true;
133
150
  }
134
- if (!sendReply) {
135
- return null;
136
- }
137
- return encoder;
138
- }
139
- _handleBeforeUnload() {
140
- awarenessProtocol.removeAwarenessStates(this._awareness, [
141
- this._clientId
142
- ], "window unload");
143
- }
151
+ });
152
+ return builder.finish();
144
153
  };
145
154
 
146
- // packages/ui/react-ui-editor/src/model.ts
147
- var useTextModel = ({ identity, space, text }) => {
148
- const provider = useMemo(() => {
149
- if (!space || !text?.doc) {
150
- return void 0;
155
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.tsx
156
+ import { hoverTooltip } from "@codemirror/view";
157
+ import { tooltipContent } from "@dxos/react-ui-theme";
158
+ var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
159
+ var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp) => hoverTooltip((view, pos) => {
160
+ const { from, text } = view.state.doc.lineAt(pos);
161
+ const p = pos - from;
162
+ let idx = 0;
163
+ let match;
164
+ do {
165
+ match = text.substring(idx).match(regexp);
166
+ if (!match) {
167
+ match = void 0;
168
+ break;
151
169
  }
152
- return new SpaceAwarenessProvider({
153
- space,
154
- doc: text.doc,
155
- channel: `yjs.awareness.${text.id}`
156
- });
157
- }, [
158
- identity,
159
- space,
160
- text?.doc
161
- ]);
162
- if (!text?.doc || !text?.content) {
163
- return void 0;
170
+ idx = text.indexOf(match[0]);
171
+ if (p >= idx && p < idx + match[0].length) {
172
+ break;
173
+ }
174
+ idx += match[0].length;
175
+ } while (true);
176
+ if (!match) {
177
+ return null;
164
178
  }
179
+ const [, , url] = match ?? [];
165
180
  return {
166
- id: text.doc.guid,
167
- content: text.content,
168
- provider,
169
- peer: identity ? {
170
- id: identity.identityKey.toHex(),
171
- name: identity.profile?.displayName
172
- } : void 0
181
+ pos: idx + from,
182
+ end: idx + from + match[0].length,
183
+ above: true,
184
+ create: () => {
185
+ const el = document.createElement("a");
186
+ el.innerText = "_";
187
+ el.className = tooltipContent({}, "mb-2 p-1");
188
+ el.setAttribute("target", "_blank");
189
+ el.setAttribute("href", url);
190
+ onHover(el, url);
191
+ return {
192
+ dom: el
193
+ };
194
+ }
173
195
  };
174
- };
196
+ });
197
+
198
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.tsx
199
+ import { Decoration as Decoration2, EditorView as EditorView3, MatchDecorator, ViewPlugin } from "@codemirror/view";
200
+ var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
201
+ var linkDecorator = () => new MatchDecorator({
202
+ regexp: markdownLinkRegexp2,
203
+ decorate: (add, from, to, match, view) => {
204
+ const [_, label, url] = match;
205
+ const p0 = {
206
+ start: from,
207
+ end: from + 1
208
+ };
209
+ const p1 = {
210
+ start: from + 1,
211
+ end: from + 1 + label.length
212
+ };
213
+ const p2 = {
214
+ start: p1.end,
215
+ end: p1.end + 1
216
+ };
217
+ const p3 = {
218
+ start: p1.end + 1,
219
+ end: p1.end + 1 + url.length + 2
220
+ };
221
+ add(p0.start, p0.end, Decoration2.mark({
222
+ class: "cm-hyperlink-bracket",
223
+ _attributes: {
224
+ style: "display: none"
225
+ }
226
+ }));
227
+ add(p1.start, p1.end, Decoration2.mark({
228
+ class: "cm-hyperlink-label"
229
+ }));
230
+ add(p2.start, p2.end, Decoration2.mark({
231
+ class: "cm-hyperlink-bracket",
232
+ _attributes: {
233
+ style: "display: none"
234
+ }
235
+ }));
236
+ add(p3.start, p3.end, Decoration2.mark({
237
+ class: "cm-hyperlink-url",
238
+ _attributes: {
239
+ style: "display: none"
240
+ }
241
+ }));
242
+ }
243
+ });
244
+ function hyperLinkExtension() {
245
+ return ViewPlugin.fromClass(class HyperLinkView {
246
+ constructor(view) {
247
+ this.decorator = linkDecorator();
248
+ this.decorations = this.decorator.createDeco(view);
249
+ }
250
+ update(update2) {
251
+ if (update2.docChanged || update2.viewportChanged) {
252
+ this.decorations = this.decorator.updateDeco(update2, this.decorations);
253
+ }
254
+ }
255
+ }, {
256
+ decorations: (view) => view.decorations,
257
+ eventHandlers: {
258
+ // TODO(burdon): CLick to expand link.
259
+ // https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
260
+ mousedown: function(event, view) {
261
+ const pos = view.posAtDOM(event.target);
262
+ if (pos !== 0) {
263
+ console.log(this);
264
+ }
265
+ },
266
+ // TODO(burdon): Hide when cursor moves away.
267
+ keydown: (event, view) => {
268
+ view.update([]);
269
+ }
270
+ }
271
+ });
272
+ }
273
+ var hyperlinkStyle = EditorView3.baseTheme({
274
+ ".cm-hyperlink-label": {
275
+ cursor: "pointer",
276
+ textDecoration: "underline"
277
+ },
278
+ ".cm-hyperlink-bracket": {
279
+ opacity: 0.2
280
+ },
281
+ ".cm-hyperlink-url": {
282
+ opacity: 0.5,
283
+ fontFamily: "monospace"
284
+ }
285
+ });
286
+ var hyperlinkWidget = [
287
+ hyperLinkExtension(),
288
+ hyperlinkStyle
289
+ ];
175
290
 
176
- // packages/ui/react-ui-editor/src/components/Markdown/Markdown.tsx
177
- import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
291
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
292
+ import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
178
293
  import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
179
- import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
180
- import { bracketMatching, defaultHighlightStyle, foldKeymap, indentOnInput, syntaxHighlighting } from "@codemirror/language";
294
+ import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
295
+ import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, foldKeymap, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
181
296
  import { languages } from "@codemirror/language-data";
182
297
  import { lintKeymap } from "@codemirror/lint";
183
- import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
184
- import { EditorState, StateField } from "@codemirror/state";
185
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
186
- import { keymap, crosshairCursor, drawSelection, dropCursor, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, placeholder, rectangularSelection, EditorView } from "@codemirror/view";
187
- import { useFocusableGroup } from "@fluentui/react-tabster";
188
- import { vim } from "@replit/codemirror-vim";
189
- import React, { forwardRef, useEffect, useImperativeHandle, useState, useMemo as useMemo2, useCallback } from "react";
190
- import { yCollab } from "y-codemirror.next";
191
- import { generateName } from "@dxos/display-name";
192
- import { useThemeContext } from "@dxos/react-ui";
193
- import { getColorForValue } from "@dxos/react-ui-theme";
194
- import { YText } from "@dxos/text-model";
298
+ import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
299
+ import { EditorState } from "@codemirror/state";
300
+ import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
301
+ import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView4, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
195
302
 
196
- // packages/ui/react-ui-editor/src/components/Markdown/markdownTags.ts
303
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/tags.ts
197
304
  import { styleTags, Tag } from "@lezer/highlight";
198
305
  var markdownTags = {
199
306
  headingMark: Tag.define(),
@@ -226,51 +333,28 @@ var markdownTagsExtension = {
226
333
  ]
227
334
  };
228
335
 
229
- // packages/ui/react-ui-editor/src/components/Markdown/markdownTheme.ts
336
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/theme.ts
230
337
  import { markdownLanguage } from "@codemirror/lang-markdown";
231
338
  import { HighlightStyle } from "@codemirror/language";
232
339
  import { tags } from "@lezer/highlight";
233
- import get from "lodash.get";
234
-
235
- // packages/ui/react-ui-editor/src/styles/markdown.ts
236
- import { mx } from "@dxos/react-ui-theme";
237
- var heading = {
238
- 1: "mbs-4 mbe-2 text-4xl font-semibold text-inherit no-underline",
239
- 2: "mbs-4 mbe-2 text-3xl font-bold text-inherit no-underline",
240
- 3: "mbs-4 mbe-2 text-2xl font-bold text-inherit no-underline",
241
- 4: "mbs-4 mbe-2 text-xl font-extrabold text-inherit no-underline",
242
- 5: "mbs-4 mbe-2 text-lg font-extrabold text-inherit no-underline",
243
- 6: "mbs-4 mbe-2 font-black text-inherit no-underline"
244
- };
245
- var blockquote = "mlb-2 border-is-4 border-neutral-500/50 pis-5";
246
- var unorderedList = "mlb-2 grid grid-cols-[min-content_1fr] [&>li:before]:content-[attr(marker)] [&>li:before]:mlb-1 [&>li:before]:mie-2";
247
- var orderedList = 'mlb-2 grid grid-cols-[min-content_1fr] [&>li:before]:content-[counters(section,_".")_"._"] [counter-reset:section] [&>li:before]:mlb-1';
248
- var listItem = "contents before:[counter-increment:section]";
249
- var codeBlock = "mlb-2 font-mono bg-neutral-500/10 p-3 rounded";
250
- var horizontalRule = "mlb-4 border-neutral-500/50";
251
- var paragraph = "mlb-1";
252
- var bold = "font-bold";
253
- var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
254
- var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
255
- var italic = "italic";
256
- var strikethrough = "line-through";
257
- var mark = "!font-normal !no-underline !text-inherit opacity-40";
258
-
259
- // packages/ui/react-ui-editor/src/styles/tokens.ts
260
- import { tailwindConfig } from "@dxos/react-ui-theme";
261
- var tokens = tailwindConfig({}).theme;
262
-
263
- // packages/ui/react-ui-editor/src/components/Markdown/markdownTheme.ts
340
+ import get2 from "lodash.get";
341
+ var chalky = "#e5c07b";
342
+ var coral = "#e06c75";
343
+ var cyan = "#56b6c2";
344
+ var invalid = "#ffffff";
264
345
  var ivory = "#abb2bf";
346
+ var stone = "#7d8799";
265
347
  var malibu = "#61afef";
348
+ var sage = "#98c379";
349
+ var whiskey = "#d19a66";
350
+ var violet = "#c678dd";
266
351
  var highlightBackground = "#2c313a";
267
352
  var tooltipBackground = "#353a42";
268
- var cursor = "#ffffff";
269
- var monospace = get(tokens, "fontFamily.mono", [
353
+ var monospace = get2(tokens, "fontFamily.mono", [
270
354
  "monospace"
271
355
  ]).join(",");
272
356
  var markdownTheme = {
273
- // TODO(thure): consider whether these commented-out rules from one-dark-theme should be integrated.
357
+ // TODO(thure): Consider whether these commented-out rules from one-dark-theme should be integrated.
274
358
  // '&': {
275
359
  // color: ivory,
276
360
  // backgroundColor: background
@@ -307,63 +391,69 @@ var markdownTheme = {
307
391
  // border: 'none',
308
392
  // color: '#ddd'
309
393
  // },
310
- ".dark & .cm-tooltip": {
311
- border: "none",
312
- backgroundColor: tooltipBackground
394
+ "& .cm-tooltip": {
395
+ backgroundColor: "transparent",
396
+ border: "none"
313
397
  },
314
- ".dark & .cm-tooltip .cm-tooltip-arrow:before": {
315
- borderTopColor: "transparent",
316
- borderBottomColor: "transparent"
317
- },
318
- ".dark & .cm-tooltip .cm-tooltip-arrow:after": {
319
- borderTopColor: tooltipBackground,
320
- borderBottomColor: tooltipBackground
321
- },
322
- ".dark & .cm-tooltip-autocomplete": {
323
- "& > ul > li[aria-selected]": {
324
- backgroundColor: highlightBackground,
325
- color: ivory
326
- }
327
- },
328
- "&.cm-focused": {
329
- outline: "none"
398
+ // '.dark & .cm-tooltip': {
399
+ // border: 'none',
400
+ // backgroundColor: tooltipBackground,
401
+ // },
402
+ // '.dark & .cm-tooltip .cm-tooltip-arrow:before': {
403
+ // borderTopColor: 'transparent',
404
+ // borderBottomColor: 'transparent',
405
+ // },
406
+ // '.dark & .cm-tooltip .cm-tooltip-arrow:after': {
407
+ // borderTopColor: tooltipBackground,
408
+ // borderBottomColor: tooltipBackground,
409
+ // },
410
+ // '.dark & .cm-tooltip-autocomplete': {
411
+ // '& > ul > li[aria-selected]': {
412
+ // backgroundColor: highlightBackground,
413
+ // color: ivory,
414
+ // },
415
+ // },
416
+ "&.cm-focused": {
417
+ outline: "none"
330
418
  },
331
419
  "& .cm-line": {
332
420
  paddingInline: 0,
421
+ lineHeight: 1.6,
333
422
  minBlockSize: "1.6em"
334
423
  },
335
424
  "& .cm-line *": {
336
425
  lineHeight: 1.6
337
426
  },
338
427
  "&.cm-focused .cm-selectionBackground, & .cm-selectionBackground": {
339
- background: get(tokens, "extend.colors.primary.150", "#00ffff")
428
+ background: get2(tokens, "extend.colors.primary.150", "#00ffff")
340
429
  },
341
430
  ".dark & .cm-selectionBackground, .dark &.cm-focused .cm-selectionBackground": {
342
- background: get(tokens, "extend.colors.primary.850", "#00ffff")
431
+ background: get2(tokens, "extend.colors.primary.850", "#00ffff")
343
432
  },
344
433
  "& .cm-selectionMatch": {
345
- background: get(tokens, "extend.colors.primary.250", "#00ffff") + "44"
434
+ background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
346
435
  },
347
436
  ".dark & .cm-selectionMatch": {
348
- background: get(tokens, "extend.colors.primary.600", "#00ffff") + "44"
437
+ background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
349
438
  },
350
439
  "& .cm-content": {
440
+ padding: 0,
351
441
  caretColor: "black"
352
442
  },
353
443
  ".dark & .cm-content": {
354
- caretColor: cursor
444
+ caretColor: "white"
355
445
  },
356
446
  "& .cm-cursor": {
357
447
  borderLeftColor: "black"
358
448
  },
359
449
  ".dark & .cm-cursor": {
360
- borderLeftColor: cursor
450
+ borderLeftColor: "white"
361
451
  },
362
452
  ".cm-placeholder": {
363
- fontFamily: get(tokens, "fontFamily.body", []).join(",")
453
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
364
454
  },
365
455
  "& .cm-scroller": {
366
- fontFamily: get(tokens, "fontFamily.mono", []).join(","),
456
+ fontFamily: get2(tokens, "fontFamily.mono", []).join(","),
367
457
  overflow: "visible"
368
458
  },
369
459
  "& .cm-activeLine": {
@@ -373,7 +463,7 @@ var markdownTheme = {
373
463
  backgroundColor: "transparent"
374
464
  },
375
465
  "& .cm-ySelectionInfo": {
376
- fontFamily: get(tokens, "fontFamily.body", []).join(","),
466
+ fontFamily: get2(tokens, "fontFamily.body", []).join(","),
377
467
  padding: "2px 4px",
378
468
  marginBlockStart: "-4px"
379
469
  },
@@ -396,8 +486,16 @@ var markdownTheme = {
396
486
  "& .cm-yLineSelection": {
397
487
  margin: "0"
398
488
  },
399
- ...Object.keys(get(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
400
- const height = get(tokens, [
489
+ ".cm-link": {
490
+ color: "rgb(20 89 208)",
491
+ textDecorationLine: "underline",
492
+ textDecorationThickness: "1px",
493
+ textUnderlineOffset: "2px",
494
+ borderRadius: ".125rem"
495
+ },
496
+ // TODO(burdon): Document.
497
+ ...Object.keys(get2(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
498
+ const height = get2(tokens, [
401
499
  "extend",
402
500
  "fontSize",
403
501
  fontSize,
@@ -416,7 +514,7 @@ var markdownTheme = {
416
514
  return acc;
417
515
  }, {})
418
516
  };
419
- var markdownDarkHighlighting = HighlightStyle.define([
517
+ var markdownHighlightStyle = HighlightStyle.define([
420
518
  {
421
519
  tag: [
422
520
  tags.keyword,
@@ -532,40 +630,341 @@ var markdownDarkHighlighting = HighlightStyle.define([
532
630
  ], {
533
631
  scope: markdownLanguage,
534
632
  all: {
535
- fontFamily: get(tokens, "fontFamily.body", []).join(",")
633
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
536
634
  }
537
635
  });
538
636
 
539
- // packages/ui/react-ui-editor/src/components/Markdown/Markdown.tsx
540
- var EditorModes = [
541
- "default",
542
- "vim"
543
- ];
544
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange, editorMode }, forwardedRef) => {
545
- const { id, content, provider, peer } = model ?? {};
546
- const { themeMode } = useThemeContext();
547
- const tabsterDOMAttribute = useFocusableGroup({
548
- tabBehavior: "limited"
637
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
638
+ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
639
+ return [
640
+ bracketMatching2(),
641
+ closeBrackets2(),
642
+ _placeholder && placeholder2(_placeholder),
643
+ EditorState.allowMultipleSelections.of(true),
644
+ EditorView4.lineWrapping,
645
+ // autocompletion(),
646
+ crosshairCursor(),
647
+ dropCursor(),
648
+ drawSelection(),
649
+ highlightActiveLine(),
650
+ highlightActiveLineGutter(),
651
+ highlightSelectionMatches(),
652
+ highlightSpecialChars(),
653
+ history(),
654
+ indentOnInput(),
655
+ rectangularSelection(),
656
+ keymap.of([
657
+ ...closeBracketsKeymap,
658
+ // ...completionKeymap,
659
+ ...defaultKeymap,
660
+ ...foldKeymap,
661
+ ...historyKeymap,
662
+ ...lintKeymap,
663
+ ...searchKeymap,
664
+ indentWithTab
665
+ ]),
666
+ // Main extension.
667
+ // https://github.com/codemirror/lang-markdown
668
+ markdown({
669
+ base: markdownLanguage2,
670
+ codeLanguages: languages,
671
+ extensions: [
672
+ // TODO(burdon): This seems to upgrade the parser.
673
+ // GitHub flavored markdown bundle: Table, TaskList, Strikethrough, and Autolink.
674
+ // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
675
+ // https://github.github.com/gfm
676
+ // GFM,
677
+ // Custom styling.
678
+ markdownTagsExtension
679
+ ]
680
+ }),
681
+ // TODO(thure): All but one rule here apply to both themes; rename or refactor.
682
+ syntaxHighlighting2(markdownHighlightStyle),
683
+ themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2)
684
+ ].filter(Boolean);
685
+ };
686
+
687
+ // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
688
+ import { EditorState as EditorState2 } from "@codemirror/state";
689
+ import { EditorView as EditorView5 } from "@codemirror/view";
690
+ import { useFocusableGroup } from "@fluentui/react-tabster";
691
+ import { vim } from "@replit/codemirror-vim";
692
+ import defaultsDeep from "lodash.defaultsdeep";
693
+ import get3 from "lodash.get";
694
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState } from "react";
695
+ import { isDocAccessor as isDocAccessor2 } from "@dxos/echo-schema";
696
+ import { useThemeContext } from "@dxos/react-ui";
697
+
698
+ // packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
699
+ import { useEffect, useMemo } from "react";
700
+ import { yCollab } from "y-codemirror.next";
701
+ import { generateName } from "@dxos/display-name";
702
+ import { isDocAccessor } from "@dxos/echo-schema";
703
+ import { getColorForValue } from "@dxos/react-ui-theme";
704
+ import { YText } from "@dxos/text-model";
705
+
706
+ // packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
707
+ import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
708
+ import { ViewPlugin as ViewPlugin2 } from "@codemirror/view";
709
+ import * as automerge2 from "@dxos/automerge/automerge";
710
+
711
+ // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
712
+ import { next as automerge } from "@dxos/automerge/automerge";
713
+
714
+ // packages/ui/react-ui-editor/src/automerge/automerge-plugin/amToCodemirror.ts
715
+ import { ChangeSet } from "@codemirror/state";
716
+ var amToCodemirror_default = (view, selection, target, patches) => {
717
+ for (const patch of patches) {
718
+ const changeSpec = handlePatch(patch, target, view.state);
719
+ if (changeSpec != null) {
720
+ const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
721
+ selection = selection.map(changeSet, 1);
722
+ view.dispatch({
723
+ changes: changeSet,
724
+ annotations: reconcileAnnotationType.of({})
725
+ });
726
+ }
727
+ }
728
+ view.dispatch({
729
+ selection,
730
+ annotations: reconcileAnnotationType.of({})
549
731
  });
550
- const [parent, setParent] = useState(null);
551
- const [state, setState] = useState();
552
- const [view, setView] = useState();
553
- useImperativeHandle(forwardedRef, () => ({
554
- editor: parent,
555
- state,
556
- view
557
- }));
558
- const listenChangesExtension = useMemo2(() => StateField.define({
559
- create: () => null,
560
- update: (_value, transaction) => {
561
- if (transaction.docChanged && onChange) {
562
- onChange(transaction.newDoc);
563
- }
564
- return null;
732
+ };
733
+ var handlePatch = (patch, target, state) => {
734
+ if (patch.action === "insert") {
735
+ return handleInsert(target, patch);
736
+ } else if (patch.action === "splice") {
737
+ return handleSplice(target, patch);
738
+ } else if (patch.action === "del") {
739
+ return handleDel(target, patch);
740
+ } else if (patch.action === "put") {
741
+ return handlePut(target, patch, state);
742
+ } else {
743
+ return null;
744
+ }
745
+ };
746
+ var handleInsert = (target, patch) => {
747
+ const index = charPath(target, patch.path);
748
+ if (index == null) {
749
+ return [];
750
+ }
751
+ const text = patch.values.map((v) => v ? v.toString() : "").join("");
752
+ return [
753
+ {
754
+ from: index,
755
+ to: index,
756
+ insert: text
565
757
  }
566
- }), [
567
- onChange
758
+ ];
759
+ };
760
+ var handleSplice = (target, patch) => {
761
+ const index = charPath(target, patch.path);
762
+ if (index == null) {
763
+ return [];
764
+ }
765
+ return [
766
+ {
767
+ from: index,
768
+ insert: patch.value
769
+ }
770
+ ];
771
+ };
772
+ var handleDel = (target, patch) => {
773
+ const index = charPath(target, patch.path);
774
+ if (index == null) {
775
+ return [];
776
+ }
777
+ const length = patch.length || 1;
778
+ return [
779
+ {
780
+ from: index,
781
+ to: index + length
782
+ }
783
+ ];
784
+ };
785
+ var handlePut = (target, patch, state) => {
786
+ const index = charPath(target, [
787
+ ...patch.path,
788
+ 0
568
789
  ]);
790
+ if (index == null) {
791
+ return [];
792
+ }
793
+ const length = state.doc.length;
794
+ if (typeof patch.value !== "string") {
795
+ return [];
796
+ }
797
+ return [
798
+ {
799
+ from: 0,
800
+ to: length,
801
+ insert: patch.value
802
+ }
803
+ ];
804
+ };
805
+ var charPath = (textPath, candidatePath) => {
806
+ if (candidatePath.length !== textPath.length + 1) {
807
+ return null;
808
+ }
809
+ for (let i = 0; i < textPath.length; i++) {
810
+ if (textPath[i] !== candidatePath[i]) {
811
+ return null;
812
+ }
813
+ }
814
+ const index = candidatePath[candidatePath.length - 1];
815
+ if (typeof index === "number") {
816
+ return index;
817
+ }
818
+ return null;
819
+ };
820
+
821
+ // packages/ui/react-ui-editor/src/automerge/automerge-plugin/codeMirrorToAm.ts
822
+ import { next as am } from "@dxos/automerge/automerge";
823
+ var codeMirrorToAm_default = (field, handle, transactions, state) => {
824
+ const { lastHeads, path } = state.field(field);
825
+ let hasChanges = false;
826
+ for (const tr of transactions) {
827
+ tr.changes.iterChanges(() => {
828
+ hasChanges = true;
829
+ });
830
+ }
831
+ if (!hasChanges) {
832
+ return void 0;
833
+ }
834
+ const newHeads = handle.changeAt(lastHeads, (doc) => {
835
+ for (const tr of transactions) {
836
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
837
+ am.splice(doc, path, fromA, toA - fromA, inserted.toString());
838
+ });
839
+ }
840
+ });
841
+ return newHeads ?? void 0;
842
+ };
843
+
844
+ // packages/ui/react-ui-editor/src/automerge/automerge-plugin/PatchSemaphore.ts
845
+ var PatchSemaphore = class {
846
+ constructor(field) {
847
+ this._inReconcile = false;
848
+ this._queue = [];
849
+ this.reconcile = (handle, view) => {
850
+ if (this._inReconcile) {
851
+ return;
852
+ }
853
+ this._inReconcile = true;
854
+ const path = getPath(view.state, this._field);
855
+ const oldHeads = getLastHeads(view.state, this._field);
856
+ let selection = view.state.selection;
857
+ const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
858
+ const toInvert = transactions.slice().reverse();
859
+ for (const tx of toInvert) {
860
+ const inverted = tx.changes.invert(tx.startState.doc);
861
+ selection = selection.map(inverted);
862
+ view.dispatch({
863
+ changes: inverted,
864
+ annotations: reconcileAnnotationType.of(true)
865
+ });
866
+ }
867
+ let newHeads = codeMirrorToAm_default(this._field, handle, transactions, view.state);
868
+ if (newHeads === null || newHeads === void 0) {
869
+ newHeads = automerge.getHeads(handle.docSync());
870
+ }
871
+ const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
872
+ amToCodemirror_default(view, selection, path, diff);
873
+ view.dispatch({
874
+ effects: updateHeads(newHeads),
875
+ annotations: reconcileAnnotationType.of({})
876
+ });
877
+ this._inReconcile = false;
878
+ };
879
+ if (field !== void 0) {
880
+ this._field = field;
881
+ }
882
+ }
883
+ };
884
+
885
+ // packages/ui/react-ui-editor/src/automerge/automerge-plugin/plugin.ts
886
+ var effectType = StateEffect.define({});
887
+ var updateHeads = (newHeads) => effectType.of({
888
+ newHeads
889
+ });
890
+ var getLastHeads = (state, field) => state.field(field).lastHeads;
891
+ var getPath = (state, field) => state.field(field).path;
892
+ var semaphoreFacet = Facet.define({
893
+ combine: (values) => values.at(-1)
894
+ });
895
+ var automergePlugin = (handle, path) => {
896
+ const stateField = StateField3.define({
897
+ create: () => ({
898
+ lastHeads: automerge2.getHeads(handle.docSync()),
899
+ unreconciledTransactions: [],
900
+ path: path.slice()
901
+ }),
902
+ update: (value, tr) => {
903
+ const result = {
904
+ lastHeads: value.lastHeads,
905
+ unreconciledTransactions: value.unreconciledTransactions.slice(),
906
+ path: path.slice()
907
+ };
908
+ let clearUnreconciled = false;
909
+ for (const effect of tr.effects) {
910
+ if (effect.is(effectType)) {
911
+ result.lastHeads = effect.value.newHeads;
912
+ clearUnreconciled = true;
913
+ }
914
+ }
915
+ if (clearUnreconciled) {
916
+ result.unreconciledTransactions = [];
917
+ } else {
918
+ if (!isReconcileTx(tr)) {
919
+ result.unreconciledTransactions.push(tr);
920
+ }
921
+ }
922
+ return result;
923
+ }
924
+ });
925
+ const semaphore = new PatchSemaphore(stateField);
926
+ const viewPlugin = ViewPlugin2.fromClass(class AutomergeCodemirrorViewPlugin {
927
+ constructor(view) {
928
+ this._handleChange = () => {
929
+ this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
930
+ };
931
+ this._view = view;
932
+ handle.addListener("change", this._handleChange);
933
+ }
934
+ update(update2) {
935
+ if (update2.transactions.length > 0 && update2.transactions.some((t) => !isReconcileTx(t))) {
936
+ queueMicrotask(() => {
937
+ this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
938
+ });
939
+ }
940
+ }
941
+ destroy() {
942
+ handle.addListener("change", this._handleChange);
943
+ }
944
+ });
945
+ return {
946
+ extension: [
947
+ stateField,
948
+ semaphoreFacet.of(semaphore),
949
+ viewPlugin
950
+ ]
951
+ };
952
+ };
953
+ var reconcileAnnotationType = Annotation.define();
954
+ var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
955
+
956
+ // packages/ui/react-ui-editor/src/hooks/useCollaboration.ts
957
+ var useCollaboration = (model, themeMode) => {
958
+ const { provider, peer, content } = model;
959
+ const extension = useMemo(() => {
960
+ if (content instanceof YText) {
961
+ return yCollab(content, provider?.awareness);
962
+ } else if (isDocAccessor(content)) {
963
+ return automergePlugin(content.handle, content.path);
964
+ } else {
965
+ return void 0;
966
+ }
967
+ }, []);
569
968
  useEffect(() => {
570
969
  if (provider && peer) {
571
970
  provider.awareness.setLocalStateField("user", {
@@ -586,76 +985,253 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
586
985
  peer,
587
986
  themeMode
588
987
  ]);
589
- useEffect(() => {
988
+ return extension;
989
+ };
990
+
991
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
992
+ import { useMemo as useMemo2 } from "react";
993
+ import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
994
+
995
+ // packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
996
+ import * as random from "lib0/random";
997
+ var cursorColors = [
998
+ {
999
+ color: "#30bced",
1000
+ light: "#30bced33"
1001
+ },
1002
+ {
1003
+ color: "#6eeb83",
1004
+ light: "#6eeb8333"
1005
+ },
1006
+ {
1007
+ color: "#ffbc42",
1008
+ light: "#ffbc4233"
1009
+ },
1010
+ {
1011
+ color: "#ecd444",
1012
+ light: "#ecd44433"
1013
+ },
1014
+ {
1015
+ color: "#ee6352",
1016
+ light: "#ee635233"
1017
+ },
1018
+ {
1019
+ color: "#9ac2c9",
1020
+ light: "#9ac2c933"
1021
+ },
1022
+ {
1023
+ color: "#8acb88",
1024
+ light: "#8acb8833"
1025
+ },
1026
+ {
1027
+ color: "#1be7ff",
1028
+ light: "#1be7ff33"
1029
+ }
1030
+ ];
1031
+ var cursorColor = cursorColors[random.uint32() % cursorColors.length];
1032
+
1033
+ // packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
1034
+ import * as decoding from "lib0/decoding";
1035
+ import * as encoding from "lib0/encoding";
1036
+ import { Observable } from "lib0/observable";
1037
+ import * as awarenessProtocol from "y-protocols/awareness";
1038
+ import { log } from "@dxos/log";
1039
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
1040
+ var messageAwareness = 1;
1041
+ var messageQueryAwareness = 3;
1042
+ var SpaceAwarenessProvider = class extends Observable {
1043
+ constructor({ space, doc, channel, awareness }) {
1044
+ super();
1045
+ this._space = space;
1046
+ this._awareness = awareness ?? new awarenessProtocol.Awareness(doc);
1047
+ this._channel = channel;
1048
+ this._clientId = doc.clientID;
1049
+ this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
1050
+ this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
1051
+ if (typeof window !== "undefined") {
1052
+ window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
1053
+ } else if (typeof process !== "undefined") {
1054
+ process.on("exit", this._handleBeforeUnload.bind(this));
1055
+ }
1056
+ const encoderAwarenessQuery = encoding.createEncoder();
1057
+ encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
1058
+ void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
1059
+ const encoderAwarenessState = encoding.createEncoder();
1060
+ encoding.writeVarUint(encoderAwarenessState, messageAwareness);
1061
+ encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [
1062
+ this._clientId
1063
+ ]));
1064
+ void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
1065
+ }
1066
+ get awareness() {
1067
+ return this._awareness;
1068
+ }
1069
+ _handleAwarenessUpdate({ added, updated, removed }, origin) {
1070
+ log("awareness update", {
1071
+ added,
1072
+ updated,
1073
+ removed,
1074
+ origin
1075
+ }, {
1076
+ F: __dxlog_file,
1077
+ L: 67,
1078
+ S: this,
1079
+ C: (f, a) => f(...a)
1080
+ });
1081
+ const changedClients = added.concat(updated).concat(removed);
1082
+ const encoderAwareness = encoding.createEncoder();
1083
+ encoding.writeVarUint(encoderAwareness, messageAwareness);
1084
+ encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
1085
+ void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
1086
+ }
1087
+ _handleSpaceMessage({ payload }) {
1088
+ log("space message", payload, {
1089
+ F: __dxlog_file,
1090
+ L: 79,
1091
+ S: this,
1092
+ C: (f, a) => f(...a)
1093
+ });
1094
+ const data = new Uint8Array(Array.from(Object.values(payload)));
1095
+ const encoder = this._readMessage(data);
1096
+ if (encoder) {
1097
+ void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
1098
+ }
1099
+ }
1100
+ _readMessage(message) {
1101
+ const decoder = decoding.createDecoder(message);
1102
+ const encoder = encoding.createEncoder();
1103
+ const messageType = decoding.readVarUint(decoder);
1104
+ let sendReply = false;
1105
+ switch (messageType) {
1106
+ case messageAwareness: {
1107
+ awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
1108
+ break;
1109
+ }
1110
+ case messageQueryAwareness: {
1111
+ encoding.writeVarUint(encoder, messageAwareness);
1112
+ encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
1113
+ sendReply = true;
1114
+ break;
1115
+ }
1116
+ default: {
1117
+ console.error("Invalid message:", messageType);
1118
+ return encoder;
1119
+ }
1120
+ }
1121
+ if (!sendReply) {
1122
+ return null;
1123
+ }
1124
+ return encoder;
1125
+ }
1126
+ _handleBeforeUnload() {
1127
+ awarenessProtocol.removeAwarenessStates(this._awareness, [
1128
+ this._clientId
1129
+ ], "window unload");
1130
+ }
1131
+ };
1132
+
1133
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
1134
+ var useTextModel = ({ identity, space, text }) => {
1135
+ const provider = useMemo2(() => {
1136
+ if (isActualAutomergeObject(text)) {
1137
+ return void 0;
1138
+ }
1139
+ if (!space || !text?.doc) {
1140
+ return void 0;
1141
+ }
1142
+ return new SpaceAwarenessProvider({
1143
+ space,
1144
+ doc: text.doc,
1145
+ channel: `yjs.awareness.${text.id}`
1146
+ });
1147
+ }, [
1148
+ identity,
1149
+ space,
1150
+ text,
1151
+ text?.doc
1152
+ ]);
1153
+ const content = useMemo2(() => {
1154
+ if (isActualAutomergeObject(text)) {
1155
+ const obj = text;
1156
+ return getRawDoc(obj, [
1157
+ obj.field
1158
+ ]);
1159
+ } else {
1160
+ return text?.content;
1161
+ }
1162
+ }, [
1163
+ text
1164
+ ]);
1165
+ if (isActualAutomergeObject(text)) {
1166
+ const obj = text;
1167
+ return {
1168
+ id: obj.id,
1169
+ content,
1170
+ provider: void 0,
1171
+ peer: identity ? {
1172
+ id: identity.identityKey.toHex(),
1173
+ name: identity.profile?.displayName
1174
+ } : void 0
1175
+ };
1176
+ }
1177
+ if (!text?.doc || !text?.content) {
1178
+ return void 0;
1179
+ }
1180
+ return {
1181
+ id: text.doc.guid,
1182
+ content: text.content,
1183
+ provider,
1184
+ peer: identity ? {
1185
+ id: identity.identityKey.toHex(),
1186
+ name: identity.profile?.displayName
1187
+ } : void 0
1188
+ };
1189
+ };
1190
+
1191
+ // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
1192
+ var EditorModes = [
1193
+ "default",
1194
+ "vim"
1195
+ ];
1196
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots, editorMode }, forwardedRef) => {
1197
+ const { themeMode } = useThemeContext();
1198
+ const tabsterDOMAttribute = useFocusableGroup({
1199
+ tabBehavior: "limited"
1200
+ });
1201
+ const [parent, setParent] = useState(null);
1202
+ const [state, setState] = useState();
1203
+ const [view, setView] = useState();
1204
+ useImperativeHandle(forwardedRef, () => ({
1205
+ editor: parent,
1206
+ state,
1207
+ view
1208
+ }), [
1209
+ view,
1210
+ state,
1211
+ parent
1212
+ ]);
1213
+ const collaboration = useCollaboration(model, themeMode);
1214
+ useEffect2(() => {
590
1215
  if (!parent) {
591
1216
  return;
592
1217
  }
593
- const state2 = EditorState.create({
594
- doc: content?.toString(),
1218
+ const initialText = isDocAccessor2(model.content) ? get3(model.content.handle.docSync(), model.content.path) : model.content?.toString();
1219
+ const state2 = EditorState2.create({
1220
+ doc: initialText,
595
1221
  extensions: [
596
- // Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799.
597
- listenChangesExtension,
598
- ...editorMode === "vim" ? [
599
- vim()
600
- ] : [],
601
- // All of https://github.com/codemirror/basic-setup minus line numbers and fold gutter.
602
- highlightActiveLineGutter(),
603
- highlightSpecialChars(),
604
- history(),
605
- drawSelection(),
606
- dropCursor(),
607
- EditorState.allowMultipleSelections.of(true),
608
- indentOnInput(),
609
- syntaxHighlighting(defaultHighlightStyle, {
610
- fallback: true
611
- }),
612
- bracketMatching(),
613
- closeBrackets(),
614
- autocompletion(),
615
- rectangularSelection(),
616
- crosshairCursor(),
617
- highlightActiveLine(),
618
- highlightSelectionMatches(),
619
- placeholder(slots.editor?.placeholder ?? ""),
620
- keymap.of([
621
- ...closeBracketsKeymap,
622
- ...defaultKeymap,
623
- ...searchKeymap,
624
- ...historyKeymap,
625
- ...foldKeymap,
626
- ...completionKeymap,
627
- ...lintKeymap,
628
- indentWithTab
629
- ]),
630
- EditorView.lineWrapping,
631
- // Theme
632
- markdown({
633
- base: markdownLanguage2,
634
- codeLanguages: languages,
635
- extensions: [
636
- markdownTagsExtension
637
- ]
638
- }),
639
- EditorView.theme({
640
- ...markdownTheme,
641
- ...slots.editor?.markdownTheme
642
- }),
643
- ...themeMode === "dark" ? [
644
- syntaxHighlighting(oneDarkHighlightStyle)
645
- ] : [
646
- syntaxHighlighting(defaultHighlightStyle)
647
- ],
648
- // TODO(thure): All but one rule here apply to both themes; rename or refactor.
649
- syntaxHighlighting(markdownDarkHighlighting),
650
- // Collaboration
651
- ...content instanceof YText ? [
652
- yCollab(content, provider?.awareness)
653
- ] : []
654
- ]
1222
+ // TODO(burdon): Factor out VIM mode?
1223
+ editorMode === "vim" && vim(),
1224
+ // Replication.
1225
+ collaboration,
1226
+ // Theme.
1227
+ slots?.editor?.theme && EditorView5.theme(slots?.editor?.theme),
1228
+ // Custom.
1229
+ ...extensions
1230
+ ].filter(Boolean)
655
1231
  });
656
1232
  setState(state2);
657
1233
  view?.destroy();
658
- setView(new EditorView({
1234
+ setView(new EditorView5({
659
1235
  state: state2,
660
1236
  parent
661
1237
  }));
@@ -666,211 +1242,110 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
666
1242
  };
667
1243
  }, [
668
1244
  parent,
669
- content,
670
- provider?.awareness,
1245
+ model.content,
671
1246
  themeMode,
672
1247
  editorMode
673
1248
  ]);
674
- const handleKeyUp = useCallback(({ key, altKey, shiftKey, metaKey, ctrlKey }) => {
1249
+ const handleKeyUp = useCallback((event) => {
1250
+ const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
675
1251
  switch (key) {
676
- case "Enter":
1252
+ case "Enter": {
677
1253
  view?.contentDOM.focus();
678
1254
  break;
679
- case "Escape":
1255
+ }
1256
+ case "Escape": {
680
1257
  editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && parent?.focus();
681
1258
  break;
1259
+ }
682
1260
  }
683
1261
  }, [
684
1262
  view,
685
1263
  editorMode
686
1264
  ]);
687
1265
  return /* @__PURE__ */ React.createElement("div", {
1266
+ key: model.id,
1267
+ ref: setParent,
688
1268
  tabIndex: 0,
689
- key: id,
690
- ...slots.root,
691
- onKeyUp: handleKeyUp,
692
- ...editorMode !== "vim" ? tabsterDOMAttribute : {},
693
- ref: setParent
1269
+ ...slots?.root,
1270
+ ...editorMode !== "vim" && tabsterDOMAttribute,
1271
+ onKeyUp: handleKeyUp
694
1272
  });
695
1273
  });
696
-
697
- // packages/ui/react-ui-editor/src/components/RichText/RichText.tsx
698
- import { mergeAttributes } from "@tiptap/core";
699
- import Collaboration from "@tiptap/extension-collaboration";
700
- import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
701
- import Heading from "@tiptap/extension-heading";
702
- import ListItem from "@tiptap/extension-list-item";
703
- import Placeholder from "@tiptap/extension-placeholder";
704
- import { EditorContent, useEditor as useNaturalEditor } from "@tiptap/react";
705
- import StarterKit from "@tiptap/starter-kit";
706
- import React2, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2, useMemo as useMemo3 } from "react";
707
- import { generateName as generateName2 } from "@dxos/display-name";
708
- import { mx as mx2 } from "@dxos/react-ui-theme";
709
- var useEditor = ({ model, placeholder: placeholder2 = "Enter text\u2026", slots = {} }) => {
710
- const extensions = useMemo3(() => [
711
- StarterKit.configure({
712
- // Extensions
713
- history: false,
714
- // Nodes
715
- blockquote: {
716
- HTMLAttributes: {
717
- class: blockquote
718
- }
719
- },
720
- bulletList: {
721
- HTMLAttributes: {
722
- class: unorderedList
723
- }
724
- },
725
- codeBlock: {
726
- HTMLAttributes: {
727
- class: codeBlock
728
- }
729
- },
730
- heading: false,
731
- horizontalRule: {
732
- HTMLAttributes: {
733
- class: horizontalRule
734
- }
735
- },
736
- listItem: false,
737
- orderedList: {
738
- HTMLAttributes: {
739
- class: orderedList
740
- }
741
- },
742
- paragraph: {
743
- HTMLAttributes: {
744
- class: paragraph
745
- }
746
- },
747
- // Marks
748
- bold: {
749
- HTMLAttributes: {
750
- class: bold
751
- }
752
- },
753
- code: {
754
- HTMLAttributes: {
755
- class: codeWithoutMarks
756
- }
757
- },
758
- italic: {
759
- HTMLAttributes: {
760
- class: italic
761
- }
762
- },
763
- strike: {
764
- HTMLAttributes: {
765
- class: strikethrough
766
- }
767
- }
768
- }),
769
- Heading.extend({
770
- renderHTML({ node, HTMLAttributes }) {
771
- const hasLevel = this.options.levels.includes(node.attrs.level);
772
- const level = hasLevel ? node.attrs.level : this.options.levels[0];
773
- return [
774
- `h${level}`,
775
- mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
776
- class: heading[level]
777
- }),
778
- 0
779
- ];
1274
+ var TextEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
1275
+ const { themeMode } = useThemeContext();
1276
+ const extensions = [
1277
+ ..._extensions ?? [],
1278
+ basicBundle({
1279
+ themeMode,
1280
+ placeholder: slots?.editor?.placeholder
1281
+ })
1282
+ ];
1283
+ return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1284
+ ref: forwardedRef,
1285
+ extensions,
1286
+ slots: defaultsDeep({}, slots, {
1287
+ editor: {
1288
+ theme: basicTheme
780
1289
  }
781
1290
  }),
782
- ListItem.extend({
783
- renderHTML: ({ HTMLAttributes }) => [
784
- "li",
785
- mergeAttributes(HTMLAttributes, {
786
- marker: "\u2022 ",
787
- class: listItem
788
- }),
789
- [
790
- "div",
791
- {
792
- role: "none"
793
- },
794
- 0
795
- ]
796
- ]
797
- }),
798
- // https://github.com/ueberdosis/tiptap/tree/main/packages/extension-collaboration
799
- ...model && typeof model.content !== "string" ? [
800
- Collaboration.configure({
801
- fragment: model.content
802
- })
803
- ] : [],
804
- ...model?.provider ? [
805
- CollaborationCursor.configure({
806
- provider: model.provider,
807
- user: model.peer && {
808
- name: model.peer.name ?? generateName2(model.peer.id),
809
- color: cursorColor.color
810
- }
811
- })
812
- ] : [],
813
- Placeholder.configure({
814
- placeholder: placeholder2,
815
- emptyEditorClass: "before:content-[attr(data-placeholder)] before:absolute opacity-50 cursor-text"
1291
+ ...props
1292
+ });
1293
+ });
1294
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
1295
+ const { themeMode } = useThemeContext();
1296
+ const extensions = [
1297
+ ..._extensions ?? [],
1298
+ markdownBundle({
1299
+ themeMode
816
1300
  })
817
- ], [
818
- model?.id
819
- ]);
820
- return useNaturalEditor({
1301
+ ];
1302
+ return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1303
+ ref: forwardedRef,
821
1304
  extensions,
822
- editorProps: {
823
- attributes: {
824
- class: mx2("focus:outline-none focus-visible:outline-none", slots.editor?.className),
825
- spellcheck: slots.editor?.spellCheck === false ? "false" : "true",
826
- tabindex: slots.editor?.tabIndex ? String(slots.editor?.tabIndex) : "0"
1305
+ slots: defaultsDeep({}, slots, {
1306
+ editor: {
1307
+ theme: markdownTheme
827
1308
  }
828
- }
829
- }, [
830
- extensions
831
- ]);
832
- };
833
- var RichTextEditor = /* @__PURE__ */ forwardRef2((props, ref) => {
834
- const editor = useEditor(props);
835
- useImperativeHandle2(ref, () => editor, [
836
- editor
837
- ]);
838
- return /* @__PURE__ */ React2.createElement(EditorContent, {
839
- ...props.slots?.root,
840
- editor
1309
+ }),
1310
+ ...props
841
1311
  });
842
1312
  });
843
-
844
- // packages/ui/react-ui-editor/src/components/Editor/Editor.tsx
845
- var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...options }, forwardedRef) => {
846
- const model = useTextModel(options);
847
- if (model?.content instanceof YXmlFragment) {
848
- return /* @__PURE__ */ React3.createElement(RichTextEditor, {
849
- ref: forwardedRef,
850
- model,
851
- slots
852
- });
853
- } else {
854
- return /* @__PURE__ */ React3.createElement(MarkdownEditor, {
855
- ref: forwardedRef,
856
- model,
857
- slots
858
- });
859
- }
860
- }));
861
-
862
- // packages/ui/react-ui-editor/src/index.ts
863
- import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
864
- import { Doc, YText as YText2, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
865
1313
  export {
1314
+ BaseTextEditor,
866
1315
  Doc,
867
- Editor,
868
1316
  EditorModes,
869
1317
  MarkdownEditor,
870
- RichTextEditor,
1318
+ SpaceAwarenessProvider,
1319
+ TextEditor,
871
1320
  TextKind,
872
1321
  YText2 as YText,
873
- YXmlFragment2 as YXmlFragment,
874
- useTextModel
1322
+ YXmlFragment,
1323
+ basicBundle,
1324
+ basicTheme,
1325
+ chalky,
1326
+ coral,
1327
+ createHyperlinkTooltip,
1328
+ cursorColor,
1329
+ cyan,
1330
+ highlightBackground,
1331
+ hyperLinkExtension,
1332
+ hyperlinkDecoration,
1333
+ hyperlinkStyle,
1334
+ hyperlinkWidget,
1335
+ invalid,
1336
+ ivory,
1337
+ malibu,
1338
+ markdownBundle,
1339
+ markdownHighlightStyle,
1340
+ markdownTheme,
1341
+ onChangeExtension,
1342
+ sage,
1343
+ stone,
1344
+ tags2 as tags,
1345
+ tooltipBackground,
1346
+ useCollaboration,
1347
+ useTextModel,
1348
+ violet,
1349
+ whiskey
875
1350
  };
876
1351
  //# sourceMappingURL=index.mjs.map