@dxos/react-ui-editor 0.3.10-main.8aaa303 → 0.3.10-main.8fd45da
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 +258 -124
- 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 +14 -0
- package/dist/types/src/components/TextEditor/extensions/comments.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 +1 -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.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 +19 -19
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +37 -6
- package/src/components/TextEditor/TextEditor.tsx +14 -7
- package/src/components/TextEditor/extensions/autocomplete.ts +5 -3
- package/src/components/TextEditor/extensions/comments.ts +173 -0
- package/src/components/TextEditor/extensions/experimental.tsx +1 -0
- package/src/components/TextEditor/extensions/index.ts +1 -0
- package/src/components/TextEditor/extensions/link.ts +10 -2
- package/src/components/TextEditor/extensions/listener.ts +1 -0
- 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,130 @@ 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/comments.ts
|
|
58
|
+
import { Decoration, EditorView as EditorView2, keymap as keymap2, MatchDecorator, ViewPlugin, WidgetType } from "@codemirror/view";
|
|
59
|
+
import { invariant } from "@dxos/invariant";
|
|
60
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts";
|
|
61
|
+
var BookmarkWidget = class extends WidgetType {
|
|
62
|
+
constructor(_pos, _id) {
|
|
63
|
+
super();
|
|
64
|
+
this._pos = _pos;
|
|
65
|
+
this._id = _id;
|
|
66
|
+
invariant(this._id, void 0, {
|
|
67
|
+
F: __dxlog_file,
|
|
68
|
+
L: 40,
|
|
69
|
+
S: this,
|
|
70
|
+
A: [
|
|
71
|
+
"this._id",
|
|
72
|
+
""
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
eq(other) {
|
|
77
|
+
return this._id === other._id;
|
|
78
|
+
}
|
|
79
|
+
// TODO(burdon): Click to select.
|
|
80
|
+
toDOM() {
|
|
81
|
+
const span = document.createElement("span");
|
|
82
|
+
span.className = "cm-bookmark";
|
|
83
|
+
span.textContent = "\u{1F4AC}";
|
|
84
|
+
return span;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var styles = EditorView2.baseTheme({
|
|
88
|
+
"& .cm-bookmark": {
|
|
89
|
+
cursor: "pointer",
|
|
90
|
+
margin: "4px",
|
|
91
|
+
padding: "4px",
|
|
92
|
+
backgroundColor: "yellow"
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
var comments = (options = {}) => {
|
|
96
|
+
const bookmarkMatcher = new MatchDecorator({
|
|
97
|
+
regexp: /\[\^(\w+)\]/g,
|
|
98
|
+
decoration: (match, view, pos) => {
|
|
99
|
+
const id = match[1];
|
|
100
|
+
return Decoration.replace({
|
|
101
|
+
id,
|
|
102
|
+
widget: new BookmarkWidget(pos, id)
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
const bookmarks = ViewPlugin.fromClass(class {
|
|
107
|
+
constructor(view) {
|
|
108
|
+
this.bookmarks = bookmarkMatcher.createDeco(view);
|
|
109
|
+
}
|
|
110
|
+
update(update2) {
|
|
111
|
+
this.bookmarks = bookmarkMatcher.updateDeco(update2, this.bookmarks);
|
|
112
|
+
}
|
|
113
|
+
}, {
|
|
114
|
+
decorations: (instance) => instance.bookmarks,
|
|
115
|
+
provide: (plugin) => EditorView2.atomicRanges.of((view) => {
|
|
116
|
+
return view.plugin(plugin)?.bookmarks || Decoration.none;
|
|
117
|
+
})
|
|
118
|
+
});
|
|
119
|
+
return [
|
|
120
|
+
keymap2.of([
|
|
121
|
+
{
|
|
122
|
+
key: options?.key ?? "alt-meta-c",
|
|
123
|
+
run: (view) => {
|
|
124
|
+
const id = options.onCreate?.();
|
|
125
|
+
if (id) {
|
|
126
|
+
const pos = view.state.selection.main.head;
|
|
127
|
+
const tag = `[^${id}]`;
|
|
128
|
+
view.dispatch({
|
|
129
|
+
changes: {
|
|
130
|
+
from: pos,
|
|
131
|
+
insert: tag
|
|
132
|
+
},
|
|
133
|
+
selection: {
|
|
134
|
+
anchor: pos + tag.length
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
]),
|
|
143
|
+
bookmarks,
|
|
144
|
+
// Monitor cursor movement.
|
|
145
|
+
EditorView2.updateListener.of((update2) => {
|
|
146
|
+
const view = update2.view;
|
|
147
|
+
const pos = view.state.selection.main.head;
|
|
148
|
+
const decorations = [];
|
|
149
|
+
const rangeSet = view.plugin(bookmarks)?.bookmarks;
|
|
150
|
+
rangeSet?.between(pos, pos + 1, (from, to, value) => {
|
|
151
|
+
if (value.spec.widget) {
|
|
152
|
+
decorations.push({
|
|
153
|
+
from,
|
|
154
|
+
to,
|
|
155
|
+
value
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
if (decorations.length) {
|
|
160
|
+
const { from, value: { spec: { id } } } = decorations[0];
|
|
161
|
+
const location = view.coordsAtPos(from);
|
|
162
|
+
if (location) {
|
|
163
|
+
options.onUpdate?.({
|
|
164
|
+
items: decorations.map(({ value: { spec: { id: id2 } } }) => id2),
|
|
165
|
+
active: id,
|
|
166
|
+
pos,
|
|
167
|
+
location
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}),
|
|
172
|
+
styles
|
|
173
|
+
];
|
|
174
|
+
};
|
|
175
|
+
|
|
57
176
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/link.ts
|
|
58
177
|
import { syntaxTree } from "@codemirror/language";
|
|
59
178
|
import { RangeSetBuilder, StateField } from "@codemirror/state";
|
|
60
|
-
import { Decoration, EditorView as
|
|
61
|
-
var LinkButton = class extends
|
|
179
|
+
import { Decoration as Decoration2, EditorView as EditorView3, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
180
|
+
var LinkButton = class extends WidgetType2 {
|
|
62
181
|
constructor(_pos, _url, _onAttach) {
|
|
63
182
|
super();
|
|
64
183
|
this._pos = _pos;
|
|
@@ -74,7 +193,7 @@ var LinkButton = class extends WidgetType {
|
|
|
74
193
|
return el;
|
|
75
194
|
}
|
|
76
195
|
};
|
|
77
|
-
var LinkText = class extends
|
|
196
|
+
var LinkText = class extends WidgetType2 {
|
|
78
197
|
constructor(_pos, _text, _url) {
|
|
79
198
|
super();
|
|
80
199
|
this._pos = _pos;
|
|
@@ -114,18 +233,21 @@ var update = (state, options) => {
|
|
|
114
233
|
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
|
|
115
234
|
const urlNode = node.node.getChild("URL");
|
|
116
235
|
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
236
|
+
if (!url) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
117
239
|
if (cursor < node.from || cursor > node.to) {
|
|
118
|
-
builder.add(node.from, node.to,
|
|
240
|
+
builder.add(node.from, node.to, Decoration2.replace({
|
|
119
241
|
widget: new LinkText(node.from, text, options.link ? url : void 0)
|
|
120
242
|
}));
|
|
121
243
|
}
|
|
122
244
|
if (options.onRender) {
|
|
123
|
-
builder.add(node.to, node.to,
|
|
245
|
+
builder.add(node.to, node.to, Decoration2.replace({
|
|
124
246
|
widget: new LinkButton(node.from, url, options.onRender)
|
|
125
247
|
}));
|
|
126
248
|
}
|
|
249
|
+
return false;
|
|
127
250
|
}
|
|
128
|
-
return true;
|
|
129
251
|
}
|
|
130
252
|
});
|
|
131
253
|
return builder.finish();
|
|
@@ -134,7 +256,7 @@ var link = (options = {}) => {
|
|
|
134
256
|
return StateField.define({
|
|
135
257
|
create: (state) => update(state, options),
|
|
136
258
|
update: (_, tr) => update(tr.state, options),
|
|
137
|
-
provide: (field) =>
|
|
259
|
+
provide: (field) => EditorView3.decorations.from(field)
|
|
138
260
|
});
|
|
139
261
|
};
|
|
140
262
|
|
|
@@ -152,15 +274,14 @@ var listener = (onChange) => StateField2.define({
|
|
|
152
274
|
|
|
153
275
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
154
276
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
155
|
-
import { defaultKeymap, history,
|
|
277
|
+
import { defaultKeymap, history, indentWithTab } from "@codemirror/commands";
|
|
156
278
|
import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
|
|
157
|
-
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2,
|
|
279
|
+
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
158
280
|
import { languages } from "@codemirror/language-data";
|
|
159
|
-
import { lintKeymap } from "@codemirror/lint";
|
|
160
281
|
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
|
|
161
282
|
import { EditorState } from "@codemirror/state";
|
|
162
283
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
163
|
-
import { crosshairCursor, drawSelection, dropCursor, EditorView as
|
|
284
|
+
import { crosshairCursor, drawSelection, dropCursor, EditorView as EditorView4, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap as keymap3, placeholder as placeholder2, rectangularSelection } from "@codemirror/view";
|
|
164
285
|
|
|
165
286
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
|
|
166
287
|
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
@@ -320,10 +441,11 @@ var markdownHighlightStyle = HighlightStyle.define([
|
|
|
320
441
|
],
|
|
321
442
|
class: codeMark
|
|
322
443
|
},
|
|
323
|
-
// The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
444
|
+
// NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
324
445
|
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
325
446
|
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
326
447
|
// the code, but all other CSS properties will be inherited.
|
|
448
|
+
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
327
449
|
{
|
|
328
450
|
tag: [
|
|
329
451
|
markdownTags.CodeText,
|
|
@@ -357,8 +479,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
357
479
|
closeBrackets2(),
|
|
358
480
|
_placeholder && placeholder2(_placeholder),
|
|
359
481
|
EditorState.allowMultipleSelections.of(true),
|
|
360
|
-
|
|
361
|
-
// autocompletion(),
|
|
482
|
+
EditorView4.lineWrapping,
|
|
362
483
|
crosshairCursor(),
|
|
363
484
|
dropCursor(),
|
|
364
485
|
drawSelection(),
|
|
@@ -369,13 +490,13 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
369
490
|
history(),
|
|
370
491
|
indentOnInput(),
|
|
371
492
|
rectangularSelection(),
|
|
372
|
-
|
|
493
|
+
// TODO(burdon): Review.
|
|
494
|
+
keymap3.of([
|
|
373
495
|
...closeBracketsKeymap,
|
|
374
|
-
// ...completionKeymap,
|
|
375
496
|
...defaultKeymap,
|
|
376
|
-
...foldKeymap,
|
|
377
|
-
...historyKeymap,
|
|
378
|
-
...lintKeymap,
|
|
497
|
+
// ...foldKeymap,
|
|
498
|
+
// ...historyKeymap,
|
|
499
|
+
// ...lintKeymap,
|
|
379
500
|
...searchKeymap,
|
|
380
501
|
indentWithTab
|
|
381
502
|
]),
|
|
@@ -405,90 +526,84 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
|
405
526
|
};
|
|
406
527
|
|
|
407
528
|
// 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) {
|
|
529
|
+
import { EditorView as EditorView5, Decoration as Decoration3, WidgetType as WidgetType3, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
530
|
+
var CheckboxWidget = class extends WidgetType3 {
|
|
531
|
+
constructor(_pos, _checked, _indent, _onCheck) {
|
|
413
532
|
super();
|
|
414
533
|
this._pos = _pos;
|
|
415
|
-
this._getCursor = _getCursor;
|
|
416
534
|
this._checked = _checked;
|
|
535
|
+
this._indent = _indent;
|
|
536
|
+
this._onCheck = _onCheck;
|
|
417
537
|
}
|
|
418
538
|
eq(other) {
|
|
419
|
-
return this._pos === other._pos && this._checked === other._checked;
|
|
539
|
+
return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
|
|
420
540
|
}
|
|
421
541
|
toDOM(view) {
|
|
422
542
|
const wrap = document.createElement("span");
|
|
423
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
424
543
|
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
|
-
});
|
|
544
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
545
|
+
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
546
|
+
const input = wrap.appendChild(document.createElement("input"));
|
|
547
|
+
input.type = "checkbox";
|
|
548
|
+
input.checked = this._checked;
|
|
549
|
+
input.onchange = (event) => {
|
|
550
|
+
this._onCheck(event.target.checked);
|
|
441
551
|
return true;
|
|
442
552
|
};
|
|
443
553
|
return wrap;
|
|
444
554
|
}
|
|
445
|
-
// TODO(burdon): Remove listener?
|
|
446
|
-
// override destroy() {
|
|
447
|
-
// console.log('destroy', this._pos);
|
|
448
|
-
// }
|
|
449
555
|
ignoreEvent() {
|
|
450
556
|
return false;
|
|
451
557
|
}
|
|
452
558
|
};
|
|
453
|
-
var
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
559
|
+
var styles2 = EditorView5.baseTheme({
|
|
560
|
+
"& .cm-task-item": {
|
|
561
|
+
paddingLeft: "4px",
|
|
562
|
+
paddingRight: "8px"
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
var tasklist = () => {
|
|
566
|
+
const taskMatcher = new MatchDecorator2({
|
|
567
|
+
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
568
|
+
decoration: (match, view, pos) => {
|
|
569
|
+
const indent = Math.floor(match[1].length / 2);
|
|
570
|
+
const checked = match[2] === "x" || match[2] === "X";
|
|
571
|
+
return Decoration3.replace({
|
|
572
|
+
widget: new CheckboxWidget(pos, checked, indent, (checked2) => {
|
|
573
|
+
const idx = pos + match[0].indexOf("[") + 1;
|
|
574
|
+
view.dispatch({
|
|
575
|
+
changes: {
|
|
576
|
+
from: idx,
|
|
577
|
+
to: idx + 1,
|
|
578
|
+
insert: checked2 ? "x" : " "
|
|
579
|
+
},
|
|
580
|
+
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
581
|
+
selection: {
|
|
582
|
+
anchor: idx + 3
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
})
|
|
586
|
+
});
|
|
587
|
+
}
|
|
460
588
|
});
|
|
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)
|
|
589
|
+
const tasks = ViewPlugin2.fromClass(class {
|
|
590
|
+
constructor(view) {
|
|
591
|
+
this.tasks = taskMatcher.createDeco(view);
|
|
592
|
+
}
|
|
593
|
+
update(update2) {
|
|
594
|
+
this.tasks = taskMatcher.updateDeco(update2, this.tasks);
|
|
595
|
+
}
|
|
596
|
+
}, {
|
|
597
|
+
decorations: (instance) => instance.tasks,
|
|
598
|
+
provide: (plugin) => EditorView5.atomicRanges.of((view) => {
|
|
599
|
+
return view.plugin(plugin)?.tasks || Decoration3.none;
|
|
480
600
|
})
|
|
601
|
+
});
|
|
602
|
+
return [
|
|
603
|
+
tasks,
|
|
604
|
+
styles2
|
|
481
605
|
];
|
|
482
606
|
};
|
|
483
|
-
var styles = EditorView4.baseTheme({
|
|
484
|
-
"& .cm-task-item": {
|
|
485
|
-
padding: "0 4px"
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
var tasklist = () => [
|
|
489
|
-
statefield(),
|
|
490
|
-
styles
|
|
491
|
-
];
|
|
492
607
|
|
|
493
608
|
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/tooltip.tsx
|
|
494
609
|
import { hoverTooltip } from "@codemirror/view";
|
|
@@ -677,17 +792,29 @@ var defaultTheme = {
|
|
|
677
792
|
var textTheme = {
|
|
678
793
|
"& .cm-scroller": {
|
|
679
794
|
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
795
|
+
},
|
|
796
|
+
"& .cm-placeholder": {
|
|
797
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
var markdownTheme = {
|
|
801
|
+
// NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
|
|
802
|
+
"& .cm-placeholder": {
|
|
803
|
+
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
680
804
|
}
|
|
681
805
|
};
|
|
682
806
|
var codeTheme = {
|
|
683
807
|
"& .cm-scroller": {
|
|
684
808
|
fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
|
|
809
|
+
},
|
|
810
|
+
"& .cm-placeholder": {
|
|
811
|
+
fontFamily: get2(tokens, "fontFamily.mono", []).join(",")
|
|
685
812
|
}
|
|
686
813
|
};
|
|
687
814
|
|
|
688
815
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
689
816
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
690
|
-
import { EditorView as
|
|
817
|
+
import { EditorView as EditorView6 } from "@codemirror/view";
|
|
691
818
|
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
692
819
|
import { vim } from "@replit/codemirror-vim";
|
|
693
820
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
@@ -704,6 +831,18 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
704
831
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
705
832
|
tabBehavior: "limited"
|
|
706
833
|
});
|
|
834
|
+
const [root, setRoot] = useState(null);
|
|
835
|
+
const [state, setState] = useState();
|
|
836
|
+
const [view, setView] = useState();
|
|
837
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
838
|
+
root,
|
|
839
|
+
state,
|
|
840
|
+
view
|
|
841
|
+
}), [
|
|
842
|
+
view,
|
|
843
|
+
state,
|
|
844
|
+
root
|
|
845
|
+
]);
|
|
707
846
|
const { awareness, peer } = model;
|
|
708
847
|
useEffect(() => {
|
|
709
848
|
if (awareness && peer) {
|
|
@@ -725,18 +864,6 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
725
864
|
peer,
|
|
726
865
|
themeMode
|
|
727
866
|
]);
|
|
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
867
|
useEffect(() => {
|
|
741
868
|
if (!root) {
|
|
742
869
|
return;
|
|
@@ -747,10 +874,10 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
747
874
|
// TODO(burdon): Factor out VIM mode?
|
|
748
875
|
editorMode === "vim" && vim(),
|
|
749
876
|
// Theme.
|
|
750
|
-
|
|
751
|
-
|
|
877
|
+
EditorView6.baseTheme(defaultTheme),
|
|
878
|
+
EditorView6.theme(slots?.editor?.theme ?? {}),
|
|
752
879
|
// TODO(burdon): themeMode doesn't change in storybooks.
|
|
753
|
-
|
|
880
|
+
EditorView6.darkTheme.of(themeMode === "dark"),
|
|
754
881
|
// Storage and replication.
|
|
755
882
|
model.extension,
|
|
756
883
|
// Custom.
|
|
@@ -758,7 +885,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, extensions = [], slots
|
|
|
758
885
|
].filter(Boolean)
|
|
759
886
|
});
|
|
760
887
|
view?.destroy();
|
|
761
|
-
setView(new
|
|
888
|
+
setView(new EditorView6({
|
|
762
889
|
state: state2,
|
|
763
890
|
parent: root
|
|
764
891
|
}));
|
|
@@ -817,7 +944,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, .
|
|
|
817
944
|
});
|
|
818
945
|
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
819
946
|
const { themeMode } = useThemeContext();
|
|
820
|
-
const slots = defaultsDeep({}, _slots,
|
|
947
|
+
const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
|
|
821
948
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
822
949
|
ref: forwardedRef,
|
|
823
950
|
extensions: [
|
|
@@ -842,17 +969,23 @@ var defaultTextSlots = {
|
|
|
842
969
|
theme: textTheme
|
|
843
970
|
}
|
|
844
971
|
};
|
|
972
|
+
var defaultMarkdownSlots = {
|
|
973
|
+
...defaultSlots,
|
|
974
|
+
editor: {
|
|
975
|
+
theme: markdownTheme
|
|
976
|
+
}
|
|
977
|
+
};
|
|
845
978
|
|
|
846
979
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
847
980
|
import get3 from "lodash.get";
|
|
848
981
|
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
849
982
|
import { yCollab } from "y-codemirror.next";
|
|
850
|
-
import { invariant } from "@dxos/invariant";
|
|
983
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
851
984
|
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
852
985
|
|
|
853
986
|
// packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
|
|
854
|
-
import { Annotation, Facet, StateEffect, StateField as
|
|
855
|
-
import { ViewPlugin } from "@codemirror/view";
|
|
987
|
+
import { Annotation, Facet, StateEffect, StateField as StateField3 } from "@codemirror/state";
|
|
988
|
+
import { ViewPlugin as ViewPlugin3 } from "@codemirror/view";
|
|
856
989
|
import * as automerge2 from "@dxos/automerge/automerge";
|
|
857
990
|
|
|
858
991
|
// packages/ui/react-ui-editor/src/hooks/automerge/PatchSemaphore.ts
|
|
@@ -1040,7 +1173,7 @@ var semaphoreFacet = Facet.define({
|
|
|
1040
1173
|
combine: (values) => values.at(-1)
|
|
1041
1174
|
});
|
|
1042
1175
|
var automergePlugin = (handle, path) => {
|
|
1043
|
-
const stateField =
|
|
1176
|
+
const stateField = StateField3.define({
|
|
1044
1177
|
create: () => ({
|
|
1045
1178
|
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
1046
1179
|
unreconciledTransactions: [],
|
|
@@ -1070,7 +1203,7 @@ var automergePlugin = (handle, path) => {
|
|
|
1070
1203
|
}
|
|
1071
1204
|
});
|
|
1072
1205
|
const semaphore = new PatchSemaphore(stateField);
|
|
1073
|
-
const viewPlugin =
|
|
1206
|
+
const viewPlugin = ViewPlugin3.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
1074
1207
|
constructor(view) {
|
|
1075
1208
|
this._handleChange = () => {
|
|
1076
1209
|
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
@@ -1144,7 +1277,7 @@ import * as encoding from "lib0/encoding";
|
|
|
1144
1277
|
import { Observable } from "lib0/observable";
|
|
1145
1278
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
1146
1279
|
import { log } from "@dxos/log";
|
|
1147
|
-
var
|
|
1280
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
|
|
1148
1281
|
var messageAwareness = 1;
|
|
1149
1282
|
var messageQueryAwareness = 3;
|
|
1150
1283
|
var SpaceAwarenessProvider = class extends Observable {
|
|
@@ -1181,7 +1314,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1181
1314
|
removed,
|
|
1182
1315
|
origin
|
|
1183
1316
|
}, {
|
|
1184
|
-
F:
|
|
1317
|
+
F: __dxlog_file2,
|
|
1185
1318
|
L: 67,
|
|
1186
1319
|
S: this,
|
|
1187
1320
|
C: (f, a) => f(...a)
|
|
@@ -1194,7 +1327,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1194
1327
|
}
|
|
1195
1328
|
_handleSpaceMessage({ payload }) {
|
|
1196
1329
|
log("space message", payload, {
|
|
1197
|
-
F:
|
|
1330
|
+
F: __dxlog_file2,
|
|
1198
1331
|
L: 79,
|
|
1199
1332
|
S: this,
|
|
1200
1333
|
C: (f, a) => f(...a)
|
|
@@ -1239,7 +1372,7 @@ var SpaceAwarenessProvider = class extends Observable {
|
|
|
1239
1372
|
};
|
|
1240
1373
|
|
|
1241
1374
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
1242
|
-
var
|
|
1375
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
1243
1376
|
var useTextModel = ({ identity, space, text }) => {
|
|
1244
1377
|
const [model, setModel] = useState2(() => createModel({
|
|
1245
1378
|
identity,
|
|
@@ -1260,44 +1393,44 @@ var useTextModel = ({ identity, space, text }) => {
|
|
|
1260
1393
|
return model;
|
|
1261
1394
|
};
|
|
1262
1395
|
var createModel = (options) => {
|
|
1263
|
-
const {
|
|
1396
|
+
const { text } = options;
|
|
1264
1397
|
if (isActualAutomergeObject(text)) {
|
|
1265
1398
|
return createAutomergeModel(options);
|
|
1266
1399
|
} else {
|
|
1267
|
-
if (!
|
|
1400
|
+
if (!text?.doc) {
|
|
1268
1401
|
return void 0;
|
|
1269
1402
|
}
|
|
1270
1403
|
return createYjsModel(options);
|
|
1271
1404
|
}
|
|
1272
1405
|
};
|
|
1273
1406
|
var createYjsModel = ({ identity, space, text }) => {
|
|
1274
|
-
|
|
1275
|
-
F:
|
|
1276
|
-
L:
|
|
1407
|
+
invariant2(text?.doc && text?.content, void 0, {
|
|
1408
|
+
F: __dxlog_file3,
|
|
1409
|
+
L: 74,
|
|
1277
1410
|
S: void 0,
|
|
1278
1411
|
A: [
|
|
1279
|
-
"
|
|
1412
|
+
"text?.doc && text?.content",
|
|
1280
1413
|
""
|
|
1281
1414
|
]
|
|
1282
1415
|
});
|
|
1283
|
-
const provider = new SpaceAwarenessProvider({
|
|
1416
|
+
const provider = space ? new SpaceAwarenessProvider({
|
|
1284
1417
|
space,
|
|
1285
1418
|
doc: text.doc,
|
|
1286
1419
|
channel: `yjs.awareness.${text.id}`
|
|
1287
|
-
});
|
|
1420
|
+
}) : void 0;
|
|
1288
1421
|
return {
|
|
1289
1422
|
id: text.doc.guid,
|
|
1290
1423
|
content: text.content,
|
|
1291
1424
|
text: () => text.content.toString(),
|
|
1292
|
-
extension: yCollab(text.content, provider
|
|
1293
|
-
awareness: provider
|
|
1425
|
+
extension: yCollab(text.content, provider?.awareness),
|
|
1426
|
+
awareness: provider?.awareness,
|
|
1294
1427
|
peer: identity ? {
|
|
1295
1428
|
id: identity.identityKey.toHex(),
|
|
1296
1429
|
name: identity.profile?.displayName
|
|
1297
1430
|
} : void 0
|
|
1298
1431
|
};
|
|
1299
1432
|
};
|
|
1300
|
-
var createAutomergeModel = ({ identity,
|
|
1433
|
+
var createAutomergeModel = ({ identity, text }) => {
|
|
1301
1434
|
const obj = text;
|
|
1302
1435
|
const doc = getRawDoc(obj, [
|
|
1303
1436
|
obj.field
|
|
@@ -1326,7 +1459,9 @@ export {
|
|
|
1326
1459
|
autocomplete,
|
|
1327
1460
|
basicBundle,
|
|
1328
1461
|
codeTheme,
|
|
1462
|
+
comments,
|
|
1329
1463
|
cursorColor,
|
|
1464
|
+
defaultMarkdownSlots,
|
|
1330
1465
|
defaultSlots,
|
|
1331
1466
|
defaultTextSlots,
|
|
1332
1467
|
defaultTheme,
|
|
@@ -1336,8 +1471,7 @@ export {
|
|
|
1336
1471
|
markdownHighlightStyle,
|
|
1337
1472
|
markdownTags,
|
|
1338
1473
|
markdownTagsExtension,
|
|
1339
|
-
|
|
1340
|
-
styles,
|
|
1474
|
+
markdownTheme,
|
|
1341
1475
|
tags2 as tags,
|
|
1342
1476
|
tasklist,
|
|
1343
1477
|
textTheme,
|