@dxos/react-ui-editor 0.3.10-main.ee543cf → 0.3.10-main.f916973
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +345 -125
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +3 -0
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts +3 -2
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts +18 -0
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts +10 -0
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +2 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts +4 -2
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +1 -4
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts +3 -0
- package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/package.json +20 -19
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +41 -8
- package/src/components/TextEditor/TextEditor.tsx +32 -11
- package/src/components/TextEditor/extensions/autocomplete.ts +5 -3
- package/src/components/TextEditor/extensions/comments.ts +190 -0
- package/src/components/TextEditor/extensions/demo.ts +66 -0
- package/src/components/TextEditor/extensions/experimental.tsx +1 -0
- package/src/components/TextEditor/extensions/index.ts +2 -0
- package/src/components/TextEditor/extensions/link.ts +10 -2
- package/src/components/TextEditor/extensions/listener.ts +3 -2
- package/src/components/TextEditor/extensions/markdown/bundle.ts +6 -14
- package/src/components/TextEditor/extensions/markdown/highlight.ts +2 -1
- package/src/components/TextEditor/extensions/tasklist.ts +73 -79
- package/src/components/TextEditor/themes/default.ts +15 -0
- package/src/hooks/useTextModel.ts +11 -8
- package/src/testing/replicator.ts +5 -5
|
@@ -8,7 +8,7 @@ import { tags as tags2 } from "@lezer/highlight";
|
|
|
8
8
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/autocomplete.ts
|
|
9
9
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
10
10
|
import { keymap } from "@codemirror/view";
|
|
11
|
-
var autocomplete = ({
|
|
11
|
+
var autocomplete = ({ onSearch }) => {
|
|
12
12
|
return [
|
|
13
13
|
// https://codemirror.net/docs/ref/#view.keymap
|
|
14
14
|
// https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
|
|
@@ -30,7 +30,7 @@ var autocomplete = ({ getOptions }) => {
|
|
|
30
30
|
}
|
|
31
31
|
return {
|
|
32
32
|
from: word.from,
|
|
33
|
-
options:
|
|
33
|
+
options: onSearch(word.text.toLowerCase())
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
]
|
|
@@ -54,11 +54,204 @@ var basicBundle = ({ themeMode, placeholder: _placeholder }) => [
|
|
|
54
54
|
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
55
55
|
].filter(Boolean);
|
|
56
56
|
|
|
57
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/demo.ts
|
|
58
|
+
import { keymap as keymap2 } from "@codemirror/view";
|
|
59
|
+
var defaultItems = [
|
|
60
|
+
"hello world!",
|
|
61
|
+
"this is a test.",
|
|
62
|
+
"this is [DXOS](https://dxos.org)"
|
|
63
|
+
];
|
|
64
|
+
var demo = ({ delay = 75, items = defaultItems } = {}) => {
|
|
65
|
+
let t;
|
|
66
|
+
let idx = 0;
|
|
67
|
+
return [
|
|
68
|
+
keymap2.of([
|
|
69
|
+
{
|
|
70
|
+
// Reset.
|
|
71
|
+
key: "alt-meta-'",
|
|
72
|
+
run: (view) => {
|
|
73
|
+
clearTimeout(t);
|
|
74
|
+
idx = 0;
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
// Next prompt.
|
|
80
|
+
// TODO(burdon): Press 1-9 to select prompt?
|
|
81
|
+
key: "shift-meta-'",
|
|
82
|
+
run: (view) => {
|
|
83
|
+
clearTimeout(t);
|
|
84
|
+
const text = items[idx++];
|
|
85
|
+
if (idx === items?.length) {
|
|
86
|
+
idx = 0;
|
|
87
|
+
}
|
|
88
|
+
let i = 0;
|
|
89
|
+
const insert = (d = 0) => {
|
|
90
|
+
t = setTimeout(() => {
|
|
91
|
+
const pos = view.state.selection.main.head;
|
|
92
|
+
view.dispatch({
|
|
93
|
+
changes: {
|
|
94
|
+
from: pos,
|
|
95
|
+
insert: text[i++]
|
|
96
|
+
},
|
|
97
|
+
selection: {
|
|
98
|
+
anchor: pos + 1
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
if (i < text.length) {
|
|
102
|
+
insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
|
|
103
|
+
}
|
|
104
|
+
}, d);
|
|
105
|
+
};
|
|
106
|
+
insert();
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
])
|
|
111
|
+
];
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts
|
|
115
|
+
import { keymap as keymap3, Decoration, EditorView as EditorView2, MatchDecorator, ViewPlugin, WidgetType } from "@codemirror/view";
|
|
116
|
+
import { debounce } from "@dxos/async";
|
|
117
|
+
import { invariant } from "@dxos/invariant";
|
|
118
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
|
|
119
|
+
var BookmarkWidget = class extends WidgetType {
|
|
120
|
+
constructor(_pos, _id) {
|
|
121
|
+
super();
|
|
122
|
+
this._pos = _pos;
|
|
123
|
+
this._id = _id;
|
|
124
|
+
invariant(this._id, void 0, {
|
|
125
|
+
F: __dxlog_file,
|
|
126
|
+
L: 41,
|
|
127
|
+
S: this,
|
|
128
|
+
A: [
|
|
129
|
+
"this._id",
|
|
130
|
+
""
|
|
131
|
+
]
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
eq(other) {
|
|
135
|
+
return this._id === other._id;
|
|
136
|
+
}
|
|
137
|
+
toDOM() {
|
|
138
|
+
const span = document.createElement("span");
|
|
139
|
+
span.className = "cm-bookmark";
|
|
140
|
+
span.textContent = "\u{1F4AC}";
|
|
141
|
+
return span;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var styles = EditorView2.baseTheme({
|
|
145
|
+
"& .cm-bookmark": {
|
|
146
|
+
cursor: "pointer",
|
|
147
|
+
margin: "4px",
|
|
148
|
+
padding: "4px",
|
|
149
|
+
backgroundColor: "yellow"
|
|
150
|
+
},
|
|
151
|
+
"& .cm-bookmark-selected": {
|
|
152
|
+
backgroundColor: "orange"
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
var comments = (options = {}) => {
|
|
156
|
+
let active;
|
|
157
|
+
const bookmarkMatcher = new MatchDecorator({
|
|
158
|
+
regexp: /\[\^(\w+)\]/g,
|
|
159
|
+
decoration: (match, view, pos) => {
|
|
160
|
+
const id = match[1];
|
|
161
|
+
return Decoration.replace({
|
|
162
|
+
id,
|
|
163
|
+
widget: new BookmarkWidget(pos, id)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
const bookmarks = ViewPlugin.fromClass(class {
|
|
168
|
+
constructor(view) {
|
|
169
|
+
this.bookmarks = bookmarkMatcher.createDeco(view);
|
|
170
|
+
}
|
|
171
|
+
update(update2) {
|
|
172
|
+
this.bookmarks = bookmarkMatcher.updateDeco(update2, this.bookmarks);
|
|
173
|
+
}
|
|
174
|
+
}, {
|
|
175
|
+
decorations: (instance) => instance.bookmarks,
|
|
176
|
+
provide: (plugin) => EditorView2.atomicRanges.of((view) => {
|
|
177
|
+
return view.plugin(plugin)?.bookmarks || Decoration.none;
|
|
178
|
+
})
|
|
179
|
+
});
|
|
180
|
+
const doUpdate = debounce((data) => {
|
|
181
|
+
options.onUpdate?.(data);
|
|
182
|
+
}, 200);
|
|
183
|
+
return [
|
|
184
|
+
keymap3.of([
|
|
185
|
+
{
|
|
186
|
+
key: options?.key ?? "shift-meta-c",
|
|
187
|
+
run: (view) => {
|
|
188
|
+
const id = options.onCreate?.();
|
|
189
|
+
if (id) {
|
|
190
|
+
const pos = view.state.selection.main.head;
|
|
191
|
+
const tag = `[^${id}]`;
|
|
192
|
+
view.dispatch({
|
|
193
|
+
changes: {
|
|
194
|
+
from: pos,
|
|
195
|
+
insert: tag
|
|
196
|
+
},
|
|
197
|
+
selection: {
|
|
198
|
+
anchor: pos + tag.length
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
]),
|
|
207
|
+
bookmarks,
|
|
208
|
+
// Monitor cursor movement.
|
|
209
|
+
EditorView2.updateListener.of((update2) => {
|
|
210
|
+
active = void 0;
|
|
211
|
+
if (!options.onUpdate) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const view = update2.view;
|
|
215
|
+
const pos = view.state.selection.main.head;
|
|
216
|
+
const decorations = [];
|
|
217
|
+
const rangeSet = view.plugin(bookmarks)?.bookmarks;
|
|
218
|
+
let closest = Infinity;
|
|
219
|
+
const { from, to } = view.visibleRanges?.[0] ?? {
|
|
220
|
+
from: 0,
|
|
221
|
+
to: view.state.doc.length
|
|
222
|
+
};
|
|
223
|
+
rangeSet?.between(from, to, (from2, to2, value) => {
|
|
224
|
+
decorations.push({
|
|
225
|
+
from: from2,
|
|
226
|
+
to: to2,
|
|
227
|
+
value
|
|
228
|
+
});
|
|
229
|
+
const d = Math.min(Math.abs(pos - from2), Math.abs(pos - to2));
|
|
230
|
+
if (d < closest) {
|
|
231
|
+
active = value.spec.id;
|
|
232
|
+
closest = d;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
if (decorations.length) {
|
|
236
|
+
doUpdate({
|
|
237
|
+
active,
|
|
238
|
+
items: decorations.map(({ from: from2, value: { spec: { id } } }) => ({
|
|
239
|
+
id,
|
|
240
|
+
pos: from2,
|
|
241
|
+
location: view.coordsAtPos(from2)
|
|
242
|
+
}))
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}),
|
|
246
|
+
styles
|
|
247
|
+
];
|
|
248
|
+
};
|
|
249
|
+
|
|
57
250
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
|
|
58
251
|
import { syntaxTree } from "@codemirror/language";
|
|
59
252
|
import { RangeSetBuilder, StateField } from "@codemirror/state";
|
|
60
|
-
import { Decoration, EditorView as
|
|
61
|
-
var LinkButton = class extends
|
|
253
|
+
import { Decoration as Decoration2, EditorView as EditorView3, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
254
|
+
var LinkButton = class extends WidgetType2 {
|
|
62
255
|
constructor(_pos, _url, _onAttach) {
|
|
63
256
|
super();
|
|
64
257
|
this._pos = _pos;
|
|
@@ -74,7 +267,7 @@ var LinkButton = class extends WidgetType {
|
|
|
74
267
|
return el;
|
|
75
268
|
}
|
|
76
269
|
};
|
|
77
|
-
var LinkText = class extends
|
|
270
|
+
var LinkText = class extends WidgetType2 {
|
|
78
271
|
constructor(_pos, _text, _url) {
|
|
79
272
|
super();
|
|
80
273
|
this._pos = _pos;
|
|
@@ -114,18 +307,21 @@ var update = (state, options) => {
|
|
|
114
307
|
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
|
|
115
308
|
const urlNode = node.node.getChild("URL");
|
|
116
309
|
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
310
|
+
if (!url) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
117
313
|
if (cursor < node.from || cursor > node.to) {
|
|
118
|
-
builder.add(node.from, node.to,
|
|
314
|
+
builder.add(node.from, node.to, Decoration2.replace({
|
|
119
315
|
widget: new LinkText(node.from, text, options.link ? url : void 0)
|
|
120
316
|
}));
|
|
121
317
|
}
|
|
122
318
|
if (options.onRender) {
|
|
123
|
-
builder.add(node.to, node.to,
|
|
319
|
+
builder.add(node.to, node.to, Decoration2.replace({
|
|
124
320
|
widget: new LinkButton(node.from, url, options.onRender)
|
|
125
321
|
}));
|
|
126
322
|
}
|
|
323
|
+
return false;
|
|
127
324
|
}
|
|
128
|
-
return true;
|
|
129
325
|
}
|
|
130
326
|
});
|
|
131
327
|
return builder.finish();
|
|
@@ -134,13 +330,13 @@ var link = (options = {}) => {
|
|
|
134
330
|
return StateField.define({
|
|
135
331
|
create: (state) => update(state, options),
|
|
136
332
|
update: (_, tr) => update(tr.state, options),
|
|
137
|
-
provide: (field) =>
|
|
333
|
+
provide: (field) => EditorView3.decorations.from(field)
|
|
138
334
|
});
|
|
139
335
|
};
|
|
140
336
|
|
|
141
337
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/listener.ts
|
|
142
338
|
import { StateField as StateField2 } from "@codemirror/state";
|
|
143
|
-
var listener = (onChange) => StateField2.define({
|
|
339
|
+
var listener = ({ onChange }) => StateField2.define({
|
|
144
340
|
create: () => null,
|
|
145
341
|
update: (_value, transaction) => {
|
|
146
342
|
if (transaction.docChanged && onChange) {
|
|
@@ -152,15 +348,14 @@ var listener = (onChange) => StateField2.define({
|
|
|
152
348
|
|
|
153
349
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
154
350
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
155
|
-
import { defaultKeymap, history,
|
|
351
|
+
import { defaultKeymap, history, indentWithTab } from "@codemirror/commands";
|
|
156
352
|
import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
|
|
157
|
-
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2,
|
|
353
|
+
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
158
354
|
import { languages } from "@codemirror/language-data";
|
|
159
|
-
import { lintKeymap } from "@codemirror/lint";
|
|
160
355
|
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
|
|
161
356
|
import { EditorState } from "@codemirror/state";
|
|
162
357
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
163
|
-
import { crosshairCursor, drawSelection, dropCursor, EditorView as
|
|
358
|
+
import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView4, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap4, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
|
|
164
359
|
|
|
165
360
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
|
|
166
361
|
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
@@ -320,10 +515,11 @@ var markdownHighlightStyle = HighlightStyle.define([
|
|
|
320
515
|
],
|
|
321
516
|
class: codeMark
|
|
322
517
|
},
|
|
323
|
-
// The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
518
|
+
// NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
324
519
|
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
325
520
|
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
326
521
|
// the code, but all other CSS properties will be inherited.
|
|
522
|
+
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
327
523
|
{
|
|
328
524
|
tag: [
|
|
329
525
|
markdownTags.CodeText,
|
|
@@ -357,8 +553,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
357
553
|
closeBrackets2(),
|
|
358
554
|
_placeholder && placeholder2(_placeholder),
|
|
359
555
|
EditorState.allowMultipleSelections.of(true),
|
|
360
|
-
|
|
361
|
-
// autocompletion(),
|
|
556
|
+
EditorView4.lineWrapping,
|
|
362
557
|
crosshairCursor(),
|
|
363
558
|
dropCursor(),
|
|
364
559
|
drawSelection(),
|
|
@@ -369,13 +564,13 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
369
564
|
history(),
|
|
370
565
|
indentOnInput(),
|
|
371
566
|
rectangularSelection(),
|
|
372
|
-
|
|
567
|
+
// TODO(burdon): Review.
|
|
568
|
+
keymap4.of([
|
|
373
569
|
...closeBracketsKeymap,
|
|
374
|
-
// ...completionKeymap,
|
|
375
570
|
...defaultKeymap,
|
|
376
|
-
...foldKeymap,
|
|
377
|
-
...historyKeymap,
|
|
378
|
-
...lintKeymap,
|
|
571
|
+
// ...foldKeymap,
|
|
572
|
+
// ...historyKeymap,
|
|
573
|
+
// ...lintKeymap,
|
|
379
574
|
...searchKeymap,
|
|
380
575
|
indentWithTab
|
|
381
576
|
]),
|
|
@@ -405,90 +600,84 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
405
600
|
};
|
|
406
601
|
|
|
407
602
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/tasklist.ts
|
|
408
|
-
import {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
var CheckboxWidget = class extends WidgetType2 {
|
|
412
|
-
constructor(_pos, _getCursor, _checked) {
|
|
603
|
+
import { EditorView as EditorView5, Decoration as Decoration3, WidgetType as WidgetType3, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
604
|
+
var CheckboxWidget = class extends WidgetType3 {
|
|
605
|
+
constructor(_pos, _checked, _indent, _onCheck) {
|
|
413
606
|
super();
|
|
414
607
|
this._pos = _pos;
|
|
415
|
-
this._getCursor = _getCursor;
|
|
416
608
|
this._checked = _checked;
|
|
609
|
+
this._indent = _indent;
|
|
610
|
+
this._onCheck = _onCheck;
|
|
417
611
|
}
|
|
418
612
|
eq(other) {
|
|
419
|
-
return this._pos === other._pos && this._checked === other._checked;
|
|
613
|
+
return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
|
|
420
614
|
}
|
|
421
615
|
toDOM(view) {
|
|
422
616
|
const wrap = document.createElement("span");
|
|
423
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
424
617
|
wrap.className = "cm-task-item";
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
from: this._pos + 1,
|
|
433
|
-
to: this._pos + 2,
|
|
434
|
-
insert: this._checked ? "x" : " "
|
|
435
|
-
},
|
|
436
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
437
|
-
selection: {
|
|
438
|
-
anchor: this._pos + 4
|
|
439
|
-
}
|
|
440
|
-
});
|
|
618
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
619
|
+
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
620
|
+
const input = wrap.appendChild(document.createElement("input"));
|
|
621
|
+
input.type = "checkbox";
|
|
622
|
+
input.checked = this._checked;
|
|
623
|
+
input.onchange = (event) => {
|
|
624
|
+
this._onCheck(event.target.checked);
|
|
441
625
|
return true;
|
|
442
626
|
};
|
|
443
627
|
return wrap;
|
|
444
628
|
}
|
|
445
|
-
// TODO(burdon): Remove listener?
|
|
446
|
-
// override destroy() {
|
|
447
|
-
// console.log('destroy', this._pos);
|
|
448
|
-
// }
|
|
449
629
|
ignoreEvent() {
|
|
450
630
|
return false;
|
|
451
631
|
}
|
|
452
632
|
};
|
|
453
|
-
var
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
633
|
+
var styles2 = EditorView5.baseTheme({
|
|
634
|
+
"& .cm-task-item": {
|
|
635
|
+
paddingLeft: "4px",
|
|
636
|
+
paddingRight: "8px"
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
var tasklist = () => {
|
|
640
|
+
const taskMatcher = new MatchDecorator2({
|
|
641
|
+
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
642
|
+
decoration: (match, view, pos) => {
|
|
643
|
+
const indent = Math.floor(match[1].length / 2);
|
|
644
|
+
const checked = match[2] === "x" || match[2] === "X";
|
|
645
|
+
return Decoration3.replace({
|
|
646
|
+
widget: new CheckboxWidget(pos, checked, indent, (checked2) => {
|
|
647
|
+
const idx = pos + match[0].indexOf("[") + 1;
|
|
648
|
+
view.dispatch({
|
|
649
|
+
changes: {
|
|
650
|
+
from: idx,
|
|
651
|
+
to: idx + 1,
|
|
652
|
+
insert: checked2 ? "x" : " "
|
|
653
|
+
},
|
|
654
|
+
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
655
|
+
selection: {
|
|
656
|
+
anchor: idx + 3
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
})
|
|
660
|
+
});
|
|
661
|
+
}
|
|
460
662
|
});
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
return builder.finish();
|
|
473
|
-
};
|
|
474
|
-
return [
|
|
475
|
-
listener2,
|
|
476
|
-
StateField3.define({
|
|
477
|
-
create: (state) => checkbox(state),
|
|
478
|
-
update: (_, tr) => checkbox(tr.state),
|
|
479
|
-
provide: (field) => EditorView4.decorations.from(field)
|
|
663
|
+
const tasks = ViewPlugin2.fromClass(class {
|
|
664
|
+
constructor(view) {
|
|
665
|
+
this.tasks = taskMatcher.createDeco(view);
|
|
666
|
+
}
|
|
667
|
+
update(update2) {
|
|
668
|
+
this.tasks = taskMatcher.updateDeco(update2, this.tasks);
|
|
669
|
+
}
|
|
670
|
+
}, {
|
|
671
|
+
decorations: (instance) => instance.tasks,
|
|
672
|
+
provide: (plugin) => EditorView5.atomicRanges.of((view) => {
|
|
673
|
+
return view.plugin(plugin)?.tasks || Decoration3.none;
|
|
480
674
|
})
|
|
675
|
+
});
|
|
676
|
+
return [
|
|
677
|
+
tasks,
|
|
678
|
+
styles2
|
|
481
679
|
];
|
|
482
680
|
};
|
|
483
|
-
var styles = EditorView4.baseTheme({
|
|
484
|
-
"& .cm-task-item": {
|
|
485
|
-
padding: "0 4px"
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
var tasklist = () => [
|
|
489
|
-
statefield(),
|
|
490
|
-
styles
|
|
491
|
-
];
|
|
492
681
|
|
|
493
682
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/tooltip.tsx
|
|
494
683
|
import { hoverTooltip } from "@codemirror/view";
|
|
@@ -677,17 +866,29 @@ var defaultTheme = {
|
|
|
677
866
|
var textTheme = {
|
|
678
867
|
"& .cm-scroller": {
|
|
679
868
|
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
869
|
+
},
|
|
870
|
+
"& .cm-placeholder": {
|
|
871
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
var markdownTheme = {
|
|
875
|
+
// NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
|
|
876
|
+
"& .cm-placeholder": {
|
|
877
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
680
878
|
}
|
|
681
879
|
};
|
|
682
880
|
var codeTheme = {
|
|
683
881
|
"& .cm-scroller": {
|
|
684
882
|
fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
|
|
883
|
+
},
|
|
884
|
+
"& .cm-placeholder": {
|
|
885
|
+
fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
|
|
685
886
|
}
|
|
686
887
|
};
|
|
687
888
|
|
|
688
889
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
689
890
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
690
|
-
import { EditorView as
|
|
891
|
+
import { EditorView as EditorView6 } from "@codemirror/view";
|
|
691
892
|
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
692
893
|
import { vim } from "@replit/codemirror-vim";
|
|
693
894
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
@@ -704,6 +905,18 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
704
905
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
705
906
|
tabBehavior: "limited"
|
|
706
907
|
});
|
|
908
|
+
const [root, setRoot] = useState(null);
|
|
909
|
+
const [state, setState] = useState();
|
|
910
|
+
const [view, setView] = useState();
|
|
911
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
912
|
+
root,
|
|
913
|
+
state,
|
|
914
|
+
view
|
|
915
|
+
}), [
|
|
916
|
+
view,
|
|
917
|
+
state,
|
|
918
|
+
root
|
|
919
|
+
]);
|
|
707
920
|
const { awareness, peer } = model;
|
|
708
921
|
useEffect(() => {
|
|
709
922
|
if (awareness && peer) {
|
|
@@ -725,18 +938,6 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
725
938
|
peer,
|
|
726
939
|
themeMode
|
|
727
940
|
]);
|
|
728
|
-
const [root, setRoot] = useState(null);
|
|
729
|
-
const [state, setState] = useState();
|
|
730
|
-
const [view, setView] = useState();
|
|
731
|
-
useImperativeHandle(forwardedRef, () => ({
|
|
732
|
-
root,
|
|
733
|
-
state,
|
|
734
|
-
view
|
|
735
|
-
}), [
|
|
736
|
-
view,
|
|
737
|
-
state,
|
|
738
|
-
root
|
|
739
|
-
]);
|
|
740
941
|
useEffect(() => {
|
|
741
942
|
if (!root) {
|
|
742
943
|
return;
|
|
@@ -747,10 +948,10 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
747
948
|
// TODO(burdon): Factor out VIM mode?
|
|
748
949
|
editorMode === "vim" && vim(),
|
|
749
950
|
// Theme.
|
|
750
|
-
|
|
751
|
-
|
|
951
|
+
EditorView6.baseTheme(defaultTheme),
|
|
952
|
+
EditorView6.theme(slots?.editor?.theme ?? {}),
|
|
752
953
|
// TODO(burdon): themeMode doesn't change in storybooks.
|
|
753
|
-
|
|
954
|
+
EditorView6.darkTheme.of(themeMode === "dark"),
|
|
754
955
|
// Storage and replication.
|
|
755
956
|
model.extension,
|
|
756
957
|
// Custom.
|
|
@@ -758,7 +959,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
758
959
|
].filter(Boolean)
|
|
759
960
|
});
|
|
760
961
|
view?.destroy();
|
|
761
|
-
setView(new
|
|
962
|
+
setView(new EditorView6({
|
|
762
963
|
state: state2,
|
|
763
964
|
parent: root
|
|
764
965
|
}));
|
|
@@ -799,6 +1000,15 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
799
1000
|
onKeyUp: handleKeyUp
|
|
800
1001
|
});
|
|
801
1002
|
});
|
|
1003
|
+
var maybeDebug = () => {
|
|
1004
|
+
const items = localStorage.getItem("dxos.composer.demo");
|
|
1005
|
+
if (items) {
|
|
1006
|
+
return demo({
|
|
1007
|
+
items: items.split(",")
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
return [];
|
|
1011
|
+
};
|
|
802
1012
|
var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
803
1013
|
const { themeMode } = useThemeContext();
|
|
804
1014
|
const slots = defaultsDeep({}, _slots, defaultTextSlots);
|
|
@@ -809,6 +1019,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, .
|
|
|
809
1019
|
themeMode,
|
|
810
1020
|
placeholder: slots?.editor?.placeholder
|
|
811
1021
|
}),
|
|
1022
|
+
maybeDebug(),
|
|
812
1023
|
...extensions
|
|
813
1024
|
],
|
|
814
1025
|
slots,
|
|
@@ -817,7 +1028,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, .
|
|
|
817
1028
|
});
|
|
818
1029
|
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
819
1030
|
const { themeMode } = useThemeContext();
|
|
820
|
-
const slots = defaultsDeep({}, _slots,
|
|
1031
|
+
const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
|
|
821
1032
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
822
1033
|
ref: forwardedRef,
|
|
823
1034
|
extensions: [
|
|
@@ -825,6 +1036,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slot
|
|
|
825
1036
|
themeMode,
|
|
826
1037
|
placeholder: slots?.editor?.placeholder
|
|
827
1038
|
}),
|
|
1039
|
+
maybeDebug(),
|
|
828
1040
|
...extensions
|
|
829
1041
|
],
|
|
830
1042
|
slots,
|
|
@@ -842,17 +1054,23 @@ var defaultTextSlots = {
|
|
|
842
1054
|
theme: textTheme
|
|
843
1055
|
}
|
|
844
1056
|
};
|
|
1057
|
+
var defaultMarkdownSlots = {
|
|
1058
|
+
...defaultSlots,
|
|
1059
|
+
editor: {
|
|
1060
|
+
theme: markdownTheme
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
845
1063
|
|
|
846
1064
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
847
1065
|
import get3 from "lodash.get";
|
|
848
1066
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
849
1067
|
import { yCollab } from "y-codemirror.next";
|
|
850
|
-
import { invariant } from "@dxos/invariant";
|
|
1068
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
851
1069
|
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
852
1070
|
|
|
853
1071
|
// packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
|
|
854
|
-
import { Annotation, Facet, StateEffect, StateField as
|
|
855
|
-
import { ViewPlugin } from "@codemirror/view";
|
|
1072
|
+
import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
|
|
1073
|
+
import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
|
|
856
1074
|
import * as automerge2 from "@dxos/automerge/automerge";
|
|
857
1075
|
|
|
858
1076
|
// packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
|
|
@@ -1040,7 +1258,7 @@ var semaphoreFacet = Facet.define({
|
|
|
1040
1258
|
combine: (values) => values.at(-1)
|
|
1041
1259
|
});
|
|
1042
1260
|
var automergePlugin = (handle, path) => {
|
|
1043
|
-
const stateField =
|
|
1261
|
+
const stateField = StateField3.define({
|
|
1044
1262
|
create: () => ({
|
|
1045
1263
|
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
1046
1264
|
unreconciledTransactions: [],
|
|
@@ -1070,7 +1288,7 @@ var automergePlugin = (handle, path) => {
|
|
|
1070
1288
|
}
|
|
1071
1289
|
});
|
|
1072
1290
|
const semaphore = new PatchSemaphore(stateField);
|
|
1073
|
-
const viewPlugin =
|
|
1291
|
+
const viewPlugin = ViewPlugin3.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
1074
1292
|
constructor(view) {
|
|
1075
1293
|
this._handleChange = () => {
|
|
1076
1294
|
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
@@ -1144,7 +1362,7 @@ import * as encoding from "lib0/encoding";
|
|
|
1144
1362
|
import { Observable } from "lib0/observable";
|
|
1145
1363
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
1146
1364
|
import { log } from "@dxos/log";
|
|
1147
|
-
var
|
|
1365
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
|
|
1148
1366
|
var messageAwareness = 1;
|
|
1149
1367
|
var messageQueryAwareness = 3;
|
|
1150
1368
|
var SpaceAwarenessProvider = class extends Observable {
|
|
@@ -1181,7 +1399,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1181
1399
|
removed,
|
|
1182
1400
|
origin
|
|
1183
1401
|
}, {
|
|
1184
|
-
F:
|
|
1402
|
+
F: __dxlog_file2,
|
|
1185
1403
|
L: 67,
|
|
1186
1404
|
S: this,
|
|
1187
1405
|
C: (f, a) => f(...a)
|
|
@@ -1194,7 +1412,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1194
1412
|
}
|
|
1195
1413
|
_handleSpaceMessage({ payload }) {
|
|
1196
1414
|
log("space message", payload, {
|
|
1197
|
-
F:
|
|
1415
|
+
F: __dxlog_file2,
|
|
1198
1416
|
L: 79,
|
|
1199
1417
|
S: this,
|
|
1200
1418
|
C: (f, a) => f(...a)
|
|
@@ -1239,7 +1457,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1239
1457
|
};
|
|
1240
1458
|
|
|
1241
1459
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
1242
|
-
var
|
|
1460
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
1243
1461
|
var useTextModel = ({ identity, space, text }) => {
|
|
1244
1462
|
const [model, setModel] = useState2(() => createModel({
|
|
1245
1463
|
identity,
|
|
@@ -1260,44 +1478,44 @@ var useTextModel = ({ identity, space, text }) => {
|
|
|
1260
1478
|
return model;
|
|
1261
1479
|
};
|
|
1262
1480
|
var createModel = (options) => {
|
|
1263
|
-
const {
|
|
1481
|
+
const { text } = options;
|
|
1264
1482
|
if (isActualAutomergeObject(text)) {
|
|
1265
1483
|
return createAutomergeModel(options);
|
|
1266
1484
|
} else {
|
|
1267
|
-
if (!
|
|
1485
|
+
if (!text?.doc) {
|
|
1268
1486
|
return void 0;
|
|
1269
1487
|
}
|
|
1270
1488
|
return createYjsModel(options);
|
|
1271
1489
|
}
|
|
1272
1490
|
};
|
|
1273
1491
|
var createYjsModel = ({ identity, space, text }) => {
|
|
1274
|
-
|
|
1275
|
-
F:
|
|
1276
|
-
L:
|
|
1492
|
+
invariant2(text?.doc && text?.content, void 0, {
|
|
1493
|
+
F: __dxlog_file3,
|
|
1494
|
+
L: 74,
|
|
1277
1495
|
S: void 0,
|
|
1278
1496
|
A: [
|
|
1279
|
-
"
|
|
1497
|
+
"text?.doc && text?.content",
|
|
1280
1498
|
""
|
|
1281
1499
|
]
|
|
1282
1500
|
});
|
|
1283
|
-
const provider = new SpaceAwarenessProvider({
|
|
1501
|
+
const provider = space ? new SpaceAwarenessProvider({
|
|
1284
1502
|
space,
|
|
1285
1503
|
doc: text.doc,
|
|
1286
1504
|
channel: `yjs.awareness.${text.id}`
|
|
1287
|
-
});
|
|
1505
|
+
}) : void 0;
|
|
1288
1506
|
return {
|
|
1289
1507
|
id: text.doc.guid,
|
|
1290
1508
|
content: text.content,
|
|
1291
1509
|
text: () => text.content.toString(),
|
|
1292
|
-
extension: yCollab(text.content, provider
|
|
1293
|
-
awareness: provider
|
|
1510
|
+
extension: yCollab(text.content, provider?.awareness),
|
|
1511
|
+
awareness: provider?.awareness,
|
|
1294
1512
|
peer: identity ? {
|
|
1295
1513
|
id: identity.identityKey.toHex(),
|
|
1296
1514
|
name: identity.profile?.displayName
|
|
1297
1515
|
} : void 0
|
|
1298
1516
|
};
|
|
1299
1517
|
};
|
|
1300
|
-
var createAutomergeModel = ({ identity,
|
|
1518
|
+
var createAutomergeModel = ({ identity, text }) => {
|
|
1301
1519
|
const obj = text;
|
|
1302
1520
|
const doc = getRawDoc(obj, [
|
|
1303
1521
|
obj.field
|
|
@@ -1326,18 +1544,20 @@ export {
|
|
|
1326
1544
|
autocomplete,
|
|
1327
1545
|
basicBundle,
|
|
1328
1546
|
codeTheme,
|
|
1547
|
+
comments,
|
|
1329
1548
|
cursorColor,
|
|
1549
|
+
defaultMarkdownSlots,
|
|
1330
1550
|
defaultSlots,
|
|
1331
1551
|
defaultTextSlots,
|
|
1332
1552
|
defaultTheme,
|
|
1553
|
+
demo,
|
|
1333
1554
|
link,
|
|
1334
1555
|
listener,
|
|
1335
1556
|
markdownBundle,
|
|
1336
1557
|
markdownHighlightStyle,
|
|
1337
1558
|
markdownTags,
|
|
1338
1559
|
markdownTagsExtension,
|
|
1339
|
-
|
|
1340
|
-
styles,
|
|
1560
|
+
markdownTheme,
|
|
1341
1561
|
tags2 as tags,
|
|
1342
1562
|
tasklist,
|
|
1343
1563
|
textTheme,
|