@dxos/react-ui-editor 0.3.9-next.4584ca9 → 0.3.9-next.8ad58eb

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 (149) hide show
  1. package/dist/lib/browser/index.mjs +1027 -827
  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 +24 -20
  17. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  18. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +7 -24
  19. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  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/{hyperlinkTooltip.d.ts → hyperlink/hyperlinkTooltip.d.ts} +0 -1
  33. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts.map +1 -0
  34. package/dist/types/src/components/TextEditor/extensions/{hyperlink.d.ts → hyperlink/hyperlinkWidget.d.ts} +3 -3
  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 +3 -1
  39. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  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 +1 -3
  49. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  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 +0 -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 +31 -28
  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 -47
  76. package/src/components/TextEditor/TextEditor.stories.tsx +51 -55
  77. package/src/components/TextEditor/TextEditor.tsx +113 -55
  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/{hyperlinkTooltip.tsx → hyperlink/hyperlinkTooltip.tsx} +2 -17
  85. package/src/components/TextEditor/extensions/{hyperlink.tsx → hyperlink/hyperlinkWidget.tsx} +21 -10
  86. package/src/components/TextEditor/extensions/hyperlink/index.ts +7 -0
  87. package/src/components/TextEditor/extensions/index.ts +3 -1
  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} +23 -12
  91. package/src/components/TextEditor/index.ts +1 -4
  92. package/src/components/TextEditor/yjs.stories.tsx +55 -0
  93. package/src/components/index.ts +0 -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 -2
  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 -29
  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 -20
  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 -20
  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 -93
  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 -27
  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/components/TextEditor/extensions/hyperlink.d.ts.map +0 -1
  124. package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts.map +0 -1
  125. package/dist/types/src/components/TextEditor/theme.d.ts +0 -8
  126. package/dist/types/src/components/TextEditor/theme.d.ts.map +0 -1
  127. package/dist/types/src/model.d.ts.map +0 -1
  128. package/dist/types/src/yjs/cursors.d.ts.map +0 -1
  129. package/dist/types/src/yjs/index.d.ts.map +0 -1
  130. package/dist/types/src/yjs/space-provider.d.ts.map +0 -1
  131. package/dist/types/stories/html/code-mirror.stories.d.ts.map +0 -1
  132. package/src/automerge/demo.ts +0 -106
  133. package/src/components/Editor/Editor.stories.tsx +0 -82
  134. package/src/components/Editor/Editor.tsx +0 -33
  135. package/src/components/Editor/index.ts +0 -5
  136. package/src/components/Markdown/Markdown.stories.tsx +0 -59
  137. package/src/components/Markdown/Markdown.tsx +0 -214
  138. package/src/components/Markdown/index.ts +0 -5
  139. package/src/components/RichText/RichText.stories.tsx +0 -70
  140. package/src/components/RichText/RichText.tsx +0 -181
  141. package/src/components/RichText/index.ts +0 -5
  142. package/src/components/TextEditor/theme.ts +0 -33
  143. /package/dist/types/src/{yjs → hooks/yjs}/cursors.d.ts +0 -0
  144. /package/dist/types/src/{yjs → hooks/yjs}/index.d.ts +0 -0
  145. /package/dist/types/src/{yjs → hooks/yjs}/space-provider.d.ts +0 -0
  146. /package/src/components/{Markdown/markdownTags.ts → TextEditor/extensions/markdown/tags.ts} +0 -0
  147. /package/src/{yjs → hooks/yjs}/cursors.ts +0 -0
  148. /package/src/{yjs → hooks/yjs}/index.ts +0 -0
  149. /package/src/{yjs → hooks/yjs}/space-provider.ts +0 -0
@@ -1,235 +1,25 @@
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";
4
-
5
- // packages/ui/react-ui-editor/src/model.ts
6
- import { useMemo } from "react";
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"
14
- },
15
- {
16
- color: "#6eeb83",
17
- light: "#6eeb8333"
18
- },
19
- {
20
- color: "#ffbc42",
21
- light: "#ffbc4233"
22
- },
23
- {
24
- color: "#ecd444",
25
- light: "#ecd44433"
26
- },
27
- {
28
- color: "#ee6352",
29
- light: "#ee635233"
30
- },
31
- {
32
- color: "#9ac2c9",
33
- light: "#9ac2c933"
34
- },
35
- {
36
- color: "#8acb88",
37
- light: "#8acb8833"
38
- },
39
- {
40
- color: "#1be7ff",
41
- light: "#1be7ff33"
42
- }
43
- ];
44
- var cursorColor = cursorColors[random.uint32() % cursorColors.length];
45
-
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));
68
- }
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));
78
- }
79
- get awareness() {
80
- return this._awareness;
81
- }
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));
99
- }
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));
111
- }
112
- }
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;
132
- }
133
- }
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
- }
144
- };
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";
145
4
 
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;
151
- }
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;
164
- }
165
- 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
173
- };
174
- };
5
+ // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
+ import { tags as tags2 } from "@lezer/highlight";
175
7
 
176
- // packages/ui/react-ui-editor/src/components/Markdown/Markdown.tsx
177
- import { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
178
- 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";
181
- import { languages } from "@codemirror/language-data";
182
- import { lintKeymap } from "@codemirror/lint";
183
- import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
184
- import { EditorState, StateField } from "@codemirror/state";
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";
185
11
  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";
195
-
196
- // packages/ui/react-ui-editor/src/components/Markdown/markdownTags.ts
197
- import { styleTags, Tag } from "@lezer/highlight";
198
- var markdownTags = {
199
- headingMark: Tag.define(),
200
- quoteMark: Tag.define(),
201
- listMark: Tag.define(),
202
- linkMark: Tag.define(),
203
- emphasisMark: Tag.define(),
204
- codeMark: Tag.define(),
205
- codeText: Tag.define(),
206
- inlineCode: Tag.define(),
207
- url: Tag.define(),
208
- linkReference: Tag.define(),
209
- linkLabel: Tag.define()
210
- };
211
- var markdownTagsExtension = {
212
- props: [
213
- styleTags({
214
- HeaderMark: markdownTags.headingMark,
215
- QuoteMark: markdownTags.quoteMark,
216
- ListMark: markdownTags.listMark,
217
- LinkMark: markdownTags.linkMark,
218
- EmphasisMark: markdownTags.emphasisMark,
219
- CodeMark: markdownTags.codeMark,
220
- CodeText: markdownTags.codeText,
221
- InlineCode: markdownTags.inlineCode,
222
- URL: markdownTags.url,
223
- LinkReference: markdownTags.linkReference,
224
- LinkLabel: markdownTags.linkLabel
225
- })
226
- ]
227
- };
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);
228
21
 
229
- // packages/ui/react-ui-editor/src/components/Markdown/markdownTheme.ts
230
- import { markdownLanguage } from "@codemirror/lang-markdown";
231
- import { HighlightStyle } from "@codemirror/language";
232
- import { tags } from "@lezer/highlight";
22
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
233
23
  import get from "lodash.get";
234
24
 
235
25
  // packages/ui/react-ui-editor/src/styles/markdown.ts
@@ -242,13 +32,6 @@ var heading = {
242
32
  5: "mbs-4 mbe-2 text-lg font-extrabold text-inherit no-underline",
243
33
  6: "mbs-4 mbe-2 font-black text-inherit no-underline"
244
34
  };
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
35
  var bold = "font-bold";
253
36
  var code = "font-mono bg-neutral-500/10 rounded pli-0.5 plb-0.5 -mlb-0.5";
254
37
  var codeWithoutMarks = mx(code, "pli-1.5 mli-0.5");
@@ -260,53 +43,357 @@ var mark = "!font-normal !no-underline !text-inherit opacity-40";
260
43
  import { tailwindConfig } from "@dxos/react-ui-theme";
261
44
  var tokens = tailwindConfig({}).theme;
262
45
 
263
- // packages/ui/react-ui-editor/src/components/Markdown/markdownTheme.ts
264
- var malibu = "#61afef";
265
- var cursor = "#ffffff";
266
- var monospace = get(tokens, "fontFamily.mono", [
267
- "monospace"
268
- ]).join(",");
269
- var markdownTheme = {
270
- // TODO(thure): consider whether these commented-out rules from one-dark-theme should be integrated.
271
- // '&': {
272
- // color: ivory,
273
- // backgroundColor: background
274
- // },
275
- // '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
276
- // '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
277
- // backgroundColor: selection
278
- // },
279
- // '.cm-panels': { backgroundColor: darkBackground, color: ivory },
280
- // '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
281
- // '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
282
- // '.cm-searchMatch': {
283
- // backgroundColor: '#72a1ff59',
284
- // outline: '1px solid #457dff'
285
- // },
286
- // '.cm-searchMatch.cm-searchMatch-selected': {
287
- // backgroundColor: '#6199ff2f'
288
- // },
289
- // '.cm-activeLine': { backgroundColor: '#6699ff0b' },
290
- // '.cm-selectionMatch': { backgroundColor: '#aafe661a' },
291
- // '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
292
- // backgroundColor: '#bad0f847'
293
- // },
294
- // '.cm-gutters': {
295
- // backgroundColor: background,
296
- // color: stone,
297
- // border: 'none'
298
- // },
299
- // '.cm-activeLineGutter': {
300
- // backgroundColor: highlightBackground
301
- // },
302
- // '.cm-foldPlaceholder': {
303
- // backgroundColor: 'transparent',
304
- // border: 'none',
305
- // color: '#ddd'
306
- // },
307
- ".cm-tooltip": {
308
- backgroundColor: "transparent",
309
- border: "none"
46
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/basic/theme.ts
47
+ var basicTheme = {
48
+ "&.cm-focused": {
49
+ outline: "none"
50
+ },
51
+ "& .cm-line": {
52
+ paddingInline: 0,
53
+ lineHeight: 1.6,
54
+ minBlockSize: "1.6em"
55
+ },
56
+ "& .cm-line *": {
57
+ lineHeight: 1.6
58
+ },
59
+ "& .cm-scroller": {
60
+ fontFamily: get(tokens, "fontFamily.body", []).join(","),
61
+ overflow: "visible"
62
+ },
63
+ "& .cm-content": {
64
+ padding: 0,
65
+ caretColor: "black"
66
+ },
67
+ ".dark & .cm-content": {
68
+ caretColor: "white"
69
+ },
70
+ "& .cm-tooltip": {
71
+ backgroundColor: "transparent",
72
+ border: "none"
73
+ },
74
+ "& .cm-link": {
75
+ color: get(tokens, "extend.colors.primary.500"),
76
+ textDecoration: "underline"
77
+ }
78
+ };
79
+
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());
87
+ }
88
+ return null;
89
+ }
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;
102
+ }
103
+ eq(other) {
104
+ return this._text === other.text && this._url === other.url;
105
+ }
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
+ };
121
+ }
122
+ return link;
123
+ }
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
+ }
148
+ }
149
+ return true;
150
+ }
151
+ });
152
+ return builder.finish();
153
+ };
154
+
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;
169
+ }
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;
178
+ }
179
+ const [, , url] = match ?? [];
180
+ return {
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
+ }
195
+ };
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
+ ];
290
+
291
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
292
+ import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
293
+ import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
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";
296
+ import { languages } from "@codemirror/language-data";
297
+ import { lintKeymap } from "@codemirror/lint";
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";
302
+
303
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/tags.ts
304
+ import { styleTags, Tag } from "@lezer/highlight";
305
+ var markdownTags = {
306
+ headingMark: Tag.define(),
307
+ quoteMark: Tag.define(),
308
+ listMark: Tag.define(),
309
+ linkMark: Tag.define(),
310
+ emphasisMark: Tag.define(),
311
+ codeMark: Tag.define(),
312
+ codeText: Tag.define(),
313
+ inlineCode: Tag.define(),
314
+ url: Tag.define(),
315
+ linkReference: Tag.define(),
316
+ linkLabel: Tag.define()
317
+ };
318
+ var markdownTagsExtension = {
319
+ props: [
320
+ styleTags({
321
+ HeaderMark: markdownTags.headingMark,
322
+ QuoteMark: markdownTags.quoteMark,
323
+ ListMark: markdownTags.listMark,
324
+ LinkMark: markdownTags.linkMark,
325
+ EmphasisMark: markdownTags.emphasisMark,
326
+ CodeMark: markdownTags.codeMark,
327
+ CodeText: markdownTags.codeText,
328
+ InlineCode: markdownTags.inlineCode,
329
+ URL: markdownTags.url,
330
+ LinkReference: markdownTags.linkReference,
331
+ LinkLabel: markdownTags.linkLabel
332
+ })
333
+ ]
334
+ };
335
+
336
+ // packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/theme.ts
337
+ import { markdownLanguage } from "@codemirror/lang-markdown";
338
+ import { HighlightStyle } from "@codemirror/language";
339
+ import { tags } from "@lezer/highlight";
340
+ import get2 from "lodash.get";
341
+ var chalky = "#e5c07b";
342
+ var coral = "#e06c75";
343
+ var cyan = "#56b6c2";
344
+ var invalid = "#ffffff";
345
+ var ivory = "#abb2bf";
346
+ var stone = "#7d8799";
347
+ var malibu = "#61afef";
348
+ var sage = "#98c379";
349
+ var whiskey = "#d19a66";
350
+ var violet = "#c678dd";
351
+ var highlightBackground = "#2c313a";
352
+ var tooltipBackground = "#353a42";
353
+ var monospace = get2(tokens, "fontFamily.mono", [
354
+ "monospace"
355
+ ]).join(",");
356
+ var markdownTheme = {
357
+ // TODO(thure): Consider whether these commented-out rules from one-dark-theme should be integrated.
358
+ // '&': {
359
+ // color: ivory,
360
+ // backgroundColor: background
361
+ // },
362
+ // '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
363
+ // '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {
364
+ // backgroundColor: selection
365
+ // },
366
+ // '.cm-panels': { backgroundColor: darkBackground, color: ivory },
367
+ // '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
368
+ // '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
369
+ // '.cm-searchMatch': {
370
+ // backgroundColor: '#72a1ff59',
371
+ // outline: '1px solid #457dff'
372
+ // },
373
+ // '.cm-searchMatch.cm-searchMatch-selected': {
374
+ // backgroundColor: '#6199ff2f'
375
+ // },
376
+ // '.cm-activeLine': { backgroundColor: '#6699ff0b' },
377
+ // '.cm-selectionMatch': { backgroundColor: '#aafe661a' },
378
+ // '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
379
+ // backgroundColor: '#bad0f847'
380
+ // },
381
+ // '.cm-gutters': {
382
+ // backgroundColor: background,
383
+ // color: stone,
384
+ // border: 'none'
385
+ // },
386
+ // '.cm-activeLineGutter': {
387
+ // backgroundColor: highlightBackground
388
+ // },
389
+ // '.cm-foldPlaceholder': {
390
+ // backgroundColor: 'transparent',
391
+ // border: 'none',
392
+ // color: '#ddd'
393
+ // },
394
+ "& .cm-tooltip": {
395
+ backgroundColor: "transparent",
396
+ border: "none"
310
397
  },
311
398
  // '.dark & .cm-tooltip': {
312
399
  // border: 'none',
@@ -331,40 +418,42 @@ var markdownTheme = {
331
418
  },
332
419
  "& .cm-line": {
333
420
  paddingInline: 0,
421
+ lineHeight: 1.6,
334
422
  minBlockSize: "1.6em"
335
423
  },
336
424
  "& .cm-line *": {
337
425
  lineHeight: 1.6
338
426
  },
339
427
  "&.cm-focused .cm-selectionBackground, & .cm-selectionBackground": {
340
- background: get(tokens, "extend.colors.primary.150", "#00ffff")
428
+ background: get2(tokens, "extend.colors.primary.150", "#00ffff")
341
429
  },
342
430
  ".dark & .cm-selectionBackground, .dark &.cm-focused .cm-selectionBackground": {
343
- background: get(tokens, "extend.colors.primary.850", "#00ffff")
431
+ background: get2(tokens, "extend.colors.primary.850", "#00ffff")
344
432
  },
345
433
  "& .cm-selectionMatch": {
346
- background: get(tokens, "extend.colors.primary.250", "#00ffff") + "44"
434
+ background: get2(tokens, "extend.colors.primary.250", "#00ffff") + "44"
347
435
  },
348
436
  ".dark & .cm-selectionMatch": {
349
- background: get(tokens, "extend.colors.primary.600", "#00ffff") + "44"
437
+ background: get2(tokens, "extend.colors.primary.600", "#00ffff") + "44"
350
438
  },
351
439
  "& .cm-content": {
440
+ padding: 0,
352
441
  caretColor: "black"
353
442
  },
354
443
  ".dark & .cm-content": {
355
- caretColor: cursor
444
+ caretColor: "white"
356
445
  },
357
446
  "& .cm-cursor": {
358
447
  borderLeftColor: "black"
359
448
  },
360
449
  ".dark & .cm-cursor": {
361
- borderLeftColor: cursor
450
+ borderLeftColor: "white"
362
451
  },
363
452
  ".cm-placeholder": {
364
- fontFamily: get(tokens, "fontFamily.body", []).join(",")
453
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
365
454
  },
366
455
  "& .cm-scroller": {
367
- fontFamily: get(tokens, "fontFamily.mono", []).join(","),
456
+ fontFamily: get2(tokens, "fontFamily.mono", []).join(","),
368
457
  overflow: "visible"
369
458
  },
370
459
  "& .cm-activeLine": {
@@ -374,7 +463,7 @@ var markdownTheme = {
374
463
  backgroundColor: "transparent"
375
464
  },
376
465
  "& .cm-ySelectionInfo": {
377
- fontFamily: get(tokens, "fontFamily.body", []).join(","),
466
+ fontFamily: get2(tokens, "fontFamily.body", []).join(","),
378
467
  padding: "2px 4px",
379
468
  marginBlockStart: "-4px"
380
469
  },
@@ -397,8 +486,16 @@ var markdownTheme = {
397
486
  "& .cm-yLineSelection": {
398
487
  margin: "0"
399
488
  },
400
- ...Object.keys(get(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
401
- 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, [
402
499
  "extend",
403
500
  "fontSize",
404
501
  fontSize,
@@ -417,7 +514,7 @@ var markdownTheme = {
417
514
  return acc;
418
515
  }, {})
419
516
  };
420
- var markdownDarkHighlighting = HighlightStyle.define([
517
+ var markdownHighlightStyle = HighlightStyle.define([
421
518
  {
422
519
  tag: [
423
520
  tags.keyword,
@@ -533,17 +630,570 @@ var markdownDarkHighlighting = HighlightStyle.define([
533
630
  ], {
534
631
  scope: markdownLanguage,
535
632
  all: {
536
- fontFamily: get(tokens, "fontFamily.body", []).join(",")
633
+ fontFamily: get2(tokens, "fontFamily.body", []).join(",")
537
634
  }
538
635
  });
539
636
 
540
- // packages/ui/react-ui-editor/src/components/Markdown/Markdown.tsx
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({})
731
+ });
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
757
+ }
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
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
+ }, []);
968
+ useEffect(() => {
969
+ if (provider && peer) {
970
+ provider.awareness.setLocalStateField("user", {
971
+ name: peer.name ?? generateName(peer.id),
972
+ color: getColorForValue({
973
+ value: peer.id,
974
+ type: "color"
975
+ }),
976
+ colorLight: getColorForValue({
977
+ value: peer.id,
978
+ themeMode,
979
+ type: "highlight"
980
+ })
981
+ });
982
+ }
983
+ }, [
984
+ provider,
985
+ peer,
986
+ themeMode
987
+ ]);
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
541
1192
  var EditorModes = [
542
1193
  "default",
543
1194
  "vim"
544
1195
  ];
545
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode, extensions = [], onChange }, forwardedRef) => {
546
- const { id, content, provider, peer } = model ?? {};
1196
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots, editorMode }, forwardedRef) => {
547
1197
  const { themeMode } = useThemeContext();
548
1198
  const tabsterDOMAttribute = useFocusableGroup({
549
1199
  tabBehavior: "limited"
@@ -555,110 +1205,33 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
555
1205
  editor: parent,
556
1206
  state,
557
1207
  view
558
- }));
559
- const listenChangesExtension = useMemo2(() => StateField.define({
560
- create: () => null,
561
- update: (_value, transaction) => {
562
- if (transaction.docChanged && onChange) {
563
- onChange(transaction.newDoc);
564
- }
565
- return null;
566
- }
567
1208
  }), [
568
- onChange
569
- ]);
570
- useEffect(() => {
571
- if (provider && peer) {
572
- provider.awareness.setLocalStateField("user", {
573
- name: peer.name ?? generateName(peer.id),
574
- color: getColorForValue({
575
- value: peer.id,
576
- type: "color"
577
- }),
578
- colorLight: getColorForValue({
579
- value: peer.id,
580
- themeMode,
581
- type: "highlight"
582
- })
583
- });
584
- }
585
- }, [
586
- provider,
587
- peer,
588
- themeMode
1209
+ view,
1210
+ state,
1211
+ parent
589
1212
  ]);
590
- useEffect(() => {
1213
+ const collaboration = useCollaboration(model, themeMode);
1214
+ useEffect2(() => {
591
1215
  if (!parent) {
592
1216
  return;
593
1217
  }
594
- const state2 = EditorState.create({
595
- 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,
596
1221
  extensions: [
597
- // Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799.
598
- listenChangesExtension,
599
- ...editorMode === "vim" ? [
600
- vim()
601
- ] : [],
602
- // All of https://github.com/codemirror/basic-setup minus line numbers and fold gutter.
603
- highlightActiveLineGutter(),
604
- highlightSpecialChars(),
605
- history(),
606
- drawSelection(),
607
- dropCursor(),
608
- EditorState.allowMultipleSelections.of(true),
609
- indentOnInput(),
610
- syntaxHighlighting(defaultHighlightStyle, {
611
- fallback: true
612
- }),
613
- bracketMatching(),
614
- closeBrackets(),
615
- autocompletion(),
616
- rectangularSelection(),
617
- crosshairCursor(),
618
- highlightActiveLine(),
619
- highlightSelectionMatches(),
620
- placeholder(slots.editor?.placeholder ?? ""),
621
- keymap.of([
622
- ...closeBracketsKeymap,
623
- ...defaultKeymap,
624
- ...searchKeymap,
625
- ...historyKeymap,
626
- ...foldKeymap,
627
- ...completionKeymap,
628
- ...lintKeymap,
629
- indentWithTab
630
- ]),
631
- EditorView.lineWrapping,
632
- // Themes.
633
- markdown({
634
- base: markdownLanguage2,
635
- codeLanguages: languages,
636
- extensions: [
637
- markdownTagsExtension
638
- ]
639
- }),
640
- EditorView.theme({
641
- ...markdownTheme,
642
- ...slots.editor?.markdownTheme
643
- }),
644
- ...themeMode === "dark" ? [
645
- syntaxHighlighting(oneDarkHighlightStyle)
646
- ] : [
647
- syntaxHighlighting(defaultHighlightStyle)
648
- ],
649
- // TODO(thure): All but one rule here apply to both themes; rename or refactor.
650
- syntaxHighlighting(markdownDarkHighlighting),
651
- // Replication and awareness (incl. remote selection).
652
- ...content instanceof YText ? [
653
- yCollab(content, provider?.awareness)
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),
655
1228
  // Custom.
656
1229
  ...extensions
657
- ]
1230
+ ].filter(Boolean)
658
1231
  });
659
1232
  setState(state2);
660
1233
  view?.destroy();
661
- setView(new EditorView({
1234
+ setView(new EditorView5({
662
1235
  state: state2,
663
1236
  parent
664
1237
  }));
@@ -669,8 +1242,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
669
1242
  };
670
1243
  }, [
671
1244
  parent,
672
- content,
673
- provider?.awareness,
1245
+ model.content,
674
1246
  themeMode,
675
1247
  editorMode
676
1248
  ]);
@@ -691,461 +1263,89 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode
691
1263
  editorMode
692
1264
  ]);
693
1265
  return /* @__PURE__ */ React.createElement("div", {
694
- tabIndex: 0,
1266
+ key: model.id,
695
1267
  ref: setParent,
696
- key: id,
697
- ...slots.root,
698
- ...editorMode !== "vim" ? tabsterDOMAttribute : {},
1268
+ tabIndex: 0,
1269
+ ...slots?.root,
1270
+ ...editorMode !== "vim" && tabsterDOMAttribute,
699
1271
  onKeyUp: handleKeyUp
700
1272
  });
701
1273
  });
702
-
703
- // packages/ui/react-ui-editor/src/components/RichText/RichText.tsx
704
- import { mergeAttributes } from "@tiptap/core";
705
- import Collaboration from "@tiptap/extension-collaboration";
706
- import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
707
- import Heading from "@tiptap/extension-heading";
708
- import ListItem from "@tiptap/extension-list-item";
709
- import Placeholder from "@tiptap/extension-placeholder";
710
- import { EditorContent, useEditor as useNaturalEditor } from "@tiptap/react";
711
- import StarterKit from "@tiptap/starter-kit";
712
- import React2, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2, useMemo as useMemo3 } from "react";
713
- import { generateName as generateName2 } from "@dxos/display-name";
714
- import { mx as mx2 } from "@dxos/react-ui-theme";
715
- var useEditor = ({ model, placeholder: placeholder3 = "Enter text\u2026", slots = {} }) => {
716
- const extensions = useMemo3(() => [
717
- StarterKit.configure({
718
- // Extensions
719
- history: false,
720
- // Nodes
721
- blockquote: {
722
- HTMLAttributes: {
723
- class: blockquote
724
- }
725
- },
726
- bulletList: {
727
- HTMLAttributes: {
728
- class: unorderedList
729
- }
730
- },
731
- codeBlock: {
732
- HTMLAttributes: {
733
- class: codeBlock
734
- }
735
- },
736
- heading: false,
737
- horizontalRule: {
738
- HTMLAttributes: {
739
- class: horizontalRule
740
- }
741
- },
742
- listItem: false,
743
- orderedList: {
744
- HTMLAttributes: {
745
- class: orderedList
746
- }
747
- },
748
- paragraph: {
749
- HTMLAttributes: {
750
- class: paragraph
751
- }
752
- },
753
- // Marks
754
- bold: {
755
- HTMLAttributes: {
756
- class: bold
757
- }
758
- },
759
- code: {
760
- HTMLAttributes: {
761
- class: codeWithoutMarks
762
- }
763
- },
764
- italic: {
765
- HTMLAttributes: {
766
- class: italic
767
- }
768
- },
769
- strike: {
770
- HTMLAttributes: {
771
- class: strikethrough
772
- }
773
- }
774
- }),
775
- Heading.extend({
776
- renderHTML({ node, HTMLAttributes }) {
777
- const hasLevel = this.options.levels.includes(node.attrs.level);
778
- const level = hasLevel ? node.attrs.level : this.options.levels[0];
779
- return [
780
- `h${level}`,
781
- mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
782
- class: heading[level]
783
- }),
784
- 0
785
- ];
786
- }
787
- }),
788
- ListItem.extend({
789
- renderHTML: ({ HTMLAttributes }) => [
790
- "li",
791
- mergeAttributes(HTMLAttributes, {
792
- marker: "\u2022 ",
793
- class: listItem
794
- }),
795
- [
796
- "div",
797
- {
798
- role: "none"
799
- },
800
- 0
801
- ]
802
- ]
803
- }),
804
- // https://github.com/ueberdosis/tiptap/tree/main/packages/extension-collaboration
805
- ...model && typeof model.content !== "string" ? [
806
- Collaboration.configure({
807
- fragment: model.content
808
- })
809
- ] : [],
810
- ...model?.provider ? [
811
- CollaborationCursor.configure({
812
- provider: model.provider,
813
- user: model.peer && {
814
- name: model.peer.name ?? generateName2(model.peer.id),
815
- color: cursorColor.color
816
- }
817
- })
818
- ] : [],
819
- Placeholder.configure({
820
- placeholder: placeholder3,
821
- emptyEditorClass: "before:content-[attr(data-placeholder)] before:absolute opacity-50 cursor-text"
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
822
1281
  })
823
- ], [
824
- model?.id
825
- ]);
826
- return useNaturalEditor({
1282
+ ];
1283
+ return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1284
+ ref: forwardedRef,
827
1285
  extensions,
828
- editorProps: {
829
- attributes: {
830
- class: mx2("focus:outline-none focus-visible:outline-none", slots.editor?.className),
831
- spellcheck: slots.editor?.spellCheck === false ? "false" : "true",
832
- tabindex: slots.editor?.tabIndex ? String(slots.editor?.tabIndex) : "0"
1286
+ slots: defaultsDeep({}, slots, {
1287
+ editor: {
1288
+ theme: basicTheme
833
1289
  }
834
- }
835
- }, [
836
- extensions
837
- ]);
838
- };
839
- var RichTextEditor = /* @__PURE__ */ forwardRef2((props, ref) => {
840
- const editor = useEditor(props);
841
- useImperativeHandle2(ref, () => editor, [
842
- editor
843
- ]);
844
- return /* @__PURE__ */ React2.createElement(EditorContent, {
845
- ...props.slots?.root,
846
- editor
847
- });
848
- });
849
-
850
- // packages/ui/react-ui-editor/src/components/Editor/Editor.tsx
851
- var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...params }, forwardedRef) => {
852
- const model = useTextModel(params);
853
- if (model?.content instanceof YXmlFragment) {
854
- return /* @__PURE__ */ React3.createElement(RichTextEditor, {
855
- ref: forwardedRef,
856
- model,
857
- slots
858
- });
859
- } else {
860
- return /* @__PURE__ */ React3.createElement(MarkdownEditor, {
861
- ref: forwardedRef,
862
- model,
863
- slots
864
- });
865
- }
866
- }));
867
-
868
- // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
869
- import { tags as tags2 } from "@lezer/highlight";
870
-
871
- // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
872
- import { closeBrackets as closeBrackets2 } from "@codemirror/autocomplete";
873
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
874
- import { EditorState as EditorState2 } from "@codemirror/state";
875
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
876
- import { EditorView as EditorView2, placeholder as placeholder2 } from "@codemirror/view";
877
- import React4, { forwardRef as forwardRef4, useEffect as useEffect2, useImperativeHandle as useImperativeHandle3, useState as useState2, useCallback as useCallback2 } from "react";
878
- import { yCollab as yCollab2 } from "y-codemirror.next";
879
- import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
880
- import { YText as YText2 } from "@dxos/text-model";
881
-
882
- // packages/ui/react-ui-editor/src/components/TextEditor/theme.ts
883
- import get2 from "lodash.get";
884
- import { tailwindConfig as tailwindConfig2 } from "@dxos/react-ui-theme";
885
- var tokens2 = tailwindConfig2({}).theme;
886
- var defaultStyles = {
887
- "&.cm-focused": {
888
- outline: "none"
889
- },
890
- ".cm-placeholder": {
891
- fontFamily: get2(tokens2, "fontFamily.body", []).join(",")
892
- },
893
- "& .cm-scroller": {
894
- fontFamily: get2(tokens2, "fontFamily.body", []).join(","),
895
- overflow: "visible"
896
- },
897
- ".cm-tooltip": {
898
- backgroundColor: "transparent",
899
- border: "none"
900
- }
901
- };
902
-
903
- // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
904
- var TextEditor = /* @__PURE__ */ forwardRef4(({ model, extensions = [], theme = defaultStyles, slots = {}, onKeyDown, ...props }, forwardedRef) => {
905
- const { id, content } = model ?? {};
906
- const { themeMode } = useThemeContext2();
907
- const [parent, setParent] = useState2(null);
908
- const [state, setState] = useState2();
909
- const [view, setView] = useState2();
910
- useImperativeHandle3(forwardedRef, () => {
911
- return {
912
- editor: parent,
913
- state,
914
- view
915
- };
916
- }, [
917
- view
918
- ]);
919
- useEffect2(() => {
920
- if (!parent) {
921
- return;
922
- }
923
- view?.destroy();
924
- const state2 = EditorState2.create({
925
- doc: content?.toString(),
926
- extensions: [
927
- bracketMatching2(),
928
- closeBrackets2(),
929
- placeholder2(slots.editor?.placeholder ?? ""),
930
- EditorView2.lineWrapping,
931
- // Themes.
932
- EditorView2.theme(theme),
933
- ...themeMode === "dark" ? [
934
- syntaxHighlighting2(oneDarkHighlightStyle2)
935
- ] : [
936
- syntaxHighlighting2(defaultHighlightStyle2)
937
- ],
938
- // Replication.
939
- ...content instanceof YText2 ? [
940
- yCollab2(content, void 0)
941
- ] : [],
942
- // Custom.
943
- ...extensions
944
- ]
945
- });
946
- setState(state2);
947
- setView(new EditorView2({
948
- state: state2,
949
- parent
950
- }));
951
- return () => {
952
- view?.destroy();
953
- setView(void 0);
954
- setState(void 0);
955
- };
956
- }, [
957
- parent,
958
- content,
959
- themeMode
960
- ]);
961
- const handleKeyDown = useCallback2((event) => {
962
- if (view) {
963
- const { head, from, to } = view.state.selection.ranges[0];
964
- const { number } = view.state.doc.lineAt(head);
965
- const after = view.state.sliceDoc(from);
966
- onKeyDown?.(event, {
967
- from,
968
- to,
969
- line: number,
970
- lines: view.state.doc.lines,
971
- after
972
- });
973
- }
974
- }, [
975
- view
976
- ]);
977
- return /* @__PURE__ */ React4.createElement("div", {
978
- key: id,
979
- ref: setParent,
980
- ...slots.root,
981
- onKeyDown: handleKeyDown,
1290
+ }),
982
1291
  ...props
983
1292
  });
984
1293
  });
985
-
986
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlink.tsx
987
- import { Decoration, EditorView as EditorView3, MatchDecorator, ViewPlugin } from "@codemirror/view";
988
- var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
989
- var linkDecorator = () => new MatchDecorator({
990
- regexp: markdownLinkRegexp,
991
- decorate: (add, from, to, match, view) => {
992
- const [_, label, url] = match;
993
- const p0 = {
994
- start: from,
995
- end: from + 1
996
- };
997
- const p1 = {
998
- start: from + 1,
999
- end: from + 1 + label.length
1000
- };
1001
- const p2 = {
1002
- start: p1.end,
1003
- end: p1.end + 1
1004
- };
1005
- const p3 = {
1006
- start: p1.end + 1,
1007
- end: p1.end + 1 + url.length + 2
1008
- };
1009
- add(p0.start, p0.end, Decoration.mark({
1010
- class: "cm-hyperlink-bracket"
1011
- }));
1012
- add(p1.start, p1.end, Decoration.mark({
1013
- class: "cm-hyperlink-label"
1014
- }));
1015
- add(p2.start, p2.end, Decoration.mark({
1016
- class: "cm-hyperlink-bracket"
1017
- }));
1018
- add(p3.start, p3.end, Decoration.mark({
1019
- class: "cm-hyperlink-url",
1020
- __attributes: {
1021
- style: "display: none"
1022
- }
1023
- }));
1024
- }
1025
- });
1026
- function hyperLinkExtension() {
1027
- return ViewPlugin.fromClass(class HyperLinkView {
1028
- constructor(view) {
1029
- this.decorator = linkDecorator();
1030
- this.decorations = this.decorator.createDeco(view);
1031
- }
1032
- update(update) {
1033
- if (update.docChanged || update.viewportChanged) {
1034
- this.decorations = this.decorator.updateDeco(update, this.decorations);
1035
- }
1036
- }
1037
- }, {
1038
- decorations: (view) => view.decorations,
1039
- eventHandlers: {
1040
- // TODO(burdon): CLick to expand link.
1041
- // https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
1042
- mousedown: function(event, view) {
1043
- const pos = view.posAtDOM(event.target);
1044
- if (pos !== 0) {
1045
- console.log(this);
1046
- }
1047
- },
1048
- // TODO(burdon): Hide when cursor moves away.
1049
- keydown: (event, view) => {
1050
- view.update([]);
1294
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions: _extensions, slots, ...props }, forwardedRef) => {
1295
+ const { themeMode } = useThemeContext();
1296
+ const extensions = [
1297
+ ..._extensions ?? [],
1298
+ markdownBundle({
1299
+ themeMode
1300
+ })
1301
+ ];
1302
+ return /* @__PURE__ */ React.createElement(BaseTextEditor, {
1303
+ ref: forwardedRef,
1304
+ extensions,
1305
+ slots: defaultsDeep({}, slots, {
1306
+ editor: {
1307
+ theme: markdownTheme
1051
1308
  }
1052
- }
1309
+ }),
1310
+ ...props
1053
1311
  });
1054
- }
1055
- var hyperLinkStyle = EditorView3.baseTheme({
1056
- ".cm-hyperlink-label": {
1057
- cursor: "pointer",
1058
- textDecoration: "underline"
1059
- },
1060
- ".cm-hyperlink-bracket": {
1061
- opacity: 0.2
1062
- },
1063
- ".cm-hyperlink-url": {
1064
- opacity: 0.5,
1065
- fontFamily: "monospace"
1066
- }
1067
- });
1068
- var hyperlink = [
1069
- hyperLinkExtension(),
1070
- hyperLinkStyle
1071
- ];
1072
-
1073
- // packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkTooltip.tsx
1074
- import { hoverTooltip } from "@codemirror/view";
1075
- import { ArrowCircleUp } from "@phosphor-icons/react";
1076
- import React5, { StrictMode } from "react";
1077
- import { createRoot } from "react-dom/client";
1078
- import { getSize, mx as mx3, tooltipContent } from "@dxos/react-ui-theme";
1079
- var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)/;
1080
- var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp2) => hoverTooltip((view, pos) => {
1081
- const { from, text } = view.state.doc.lineAt(pos);
1082
- const p = pos - from;
1083
- let idx = 0;
1084
- let match;
1085
- do {
1086
- match = text.substring(idx).match(regexp);
1087
- if (!match) {
1088
- match = void 0;
1089
- break;
1090
- }
1091
- idx = text.indexOf(match[0]);
1092
- if (p >= idx && p < idx + match[0].length) {
1093
- break;
1094
- }
1095
- idx += match[0].length;
1096
- } while (true);
1097
- if (!match) {
1098
- return null;
1099
- }
1100
- const [, , url] = match ?? [];
1101
- return {
1102
- pos: idx + from,
1103
- end: idx + from + match[0].length,
1104
- above: true,
1105
- create: () => {
1106
- const el = document.createElement("a");
1107
- el.innerText = "_";
1108
- el.className = tooltipContent({}, "mb-2 p-1");
1109
- el.setAttribute("target", "_blank");
1110
- el.setAttribute("href", url);
1111
- onHover(el, url);
1112
- return {
1113
- dom: el
1114
- };
1115
- }
1116
- };
1117
1312
  });
1118
- var defaultHyperLinkTooltip = createHyperlinkTooltip((el, url) => {
1119
- const web = new URL(url);
1120
- createRoot(el).render(/* @__PURE__ */ React5.createElement(StrictMode, null, /* @__PURE__ */ React5.createElement("div", {
1121
- className: "flex gap-1 items-center"
1122
- }, /* @__PURE__ */ React5.createElement(ArrowCircleUp, {
1123
- className: mx3(getSize(6), "text-blue-500")
1124
- }), /* @__PURE__ */ React5.createElement("p", {
1125
- className: "pr-1"
1126
- }, web.origin))));
1127
- });
1128
-
1129
- // packages/ui/react-ui-editor/src/index.ts
1130
- import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
1131
- import { Doc, YText as YText3, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
1132
1313
  export {
1314
+ BaseTextEditor,
1133
1315
  Doc,
1134
- Editor,
1135
1316
  EditorModes,
1136
1317
  MarkdownEditor,
1137
- RichTextEditor,
1318
+ SpaceAwarenessProvider,
1138
1319
  TextEditor,
1139
1320
  TextKind,
1140
- YText3 as YText,
1141
- YXmlFragment2 as YXmlFragment,
1321
+ YText2 as YText,
1322
+ YXmlFragment,
1323
+ basicBundle,
1324
+ basicTheme,
1325
+ chalky,
1326
+ coral,
1142
1327
  createHyperlinkTooltip,
1143
- defaultHyperLinkTooltip,
1144
- defaultStyles,
1328
+ cursorColor,
1329
+ cyan,
1330
+ highlightBackground,
1145
1331
  hyperLinkExtension,
1146
- hyperLinkStyle,
1147
- hyperlink,
1332
+ hyperlinkDecoration,
1333
+ hyperlinkStyle,
1334
+ hyperlinkWidget,
1335
+ invalid,
1336
+ ivory,
1337
+ malibu,
1338
+ markdownBundle,
1339
+ markdownHighlightStyle,
1340
+ markdownTheme,
1341
+ onChangeExtension,
1342
+ sage,
1343
+ stone,
1148
1344
  tags2 as tags,
1149
- useTextModel
1345
+ tooltipBackground,
1346
+ useCollaboration,
1347
+ useTextModel,
1348
+ violet,
1349
+ whiskey
1150
1350
  };
1151
1351
  //# sourceMappingURL=index.mjs.map