@dxos/react-ui-editor 0.3.11-main.927e4ba → 0.3.11-main.9b81c36

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 (121) hide show
  1. package/dist/lib/browser/index.mjs +1267 -662
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +1 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
  8. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/TextEditor/{comments.stories.d.ts → hooks.stories.d.ts} +9 -12
  10. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts +25 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -0
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  15. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  16. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  17. package/dist/types/src/components/index.d.ts +1 -0
  18. package/dist/types/src/components/index.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  21. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  22. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  23. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  24. package/dist/types/src/extensions/automerge/defs.d.ts +5 -5
  25. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
  27. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  28. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  29. package/dist/types/src/extensions/awareness.d.ts +1 -1
  30. package/dist/types/src/extensions/basic.d.ts +1 -1
  31. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  32. package/dist/types/src/extensions/comments.d.ts +6 -2
  33. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  34. package/dist/types/src/extensions/index.d.ts +0 -6
  35. package/dist/types/src/extensions/index.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  37. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  38. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  39. package/dist/types/src/extensions/markdown/formatting.d.ts +13 -0
  40. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  41. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  42. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  43. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  45. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  46. package/dist/types/src/extensions/markdown/index.d.ts +8 -0
  47. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  49. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  50. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  51. package/dist/types/src/hooks/index.d.ts +2 -0
  52. package/dist/types/src/hooks/index.d.ts.map +1 -1
  53. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  54. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  55. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  56. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  57. package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
  58. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  59. package/dist/types/src/hooks/useTextModel.d.ts +12 -0
  60. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  61. package/dist/types/src/index.d.ts +1 -1
  62. package/dist/types/src/index.d.ts.map +1 -1
  63. package/dist/types/src/styles/markdown.d.ts +2 -2
  64. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  65. package/dist/types/src/themes/default.d.ts.map +1 -1
  66. package/package.json +39 -26
  67. package/src/components/TextEditor/MarkdownEditor.stories.tsx +14 -18
  68. package/src/components/TextEditor/TextEditor.tsx +27 -16
  69. package/src/components/TextEditor/automerge.stories.tsx +1 -2
  70. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  71. package/src/components/Toolbar/Toolbar.stories.tsx +90 -0
  72. package/src/components/Toolbar/Toolbar.tsx +166 -0
  73. package/src/components/Toolbar/index.ts +5 -0
  74. package/src/components/index.ts +1 -0
  75. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  76. package/src/extensions/automerge/automerge.test.ts +13 -0
  77. package/src/extensions/automerge/automerge.ts +26 -11
  78. package/src/extensions/automerge/cursor.ts +3 -3
  79. package/src/extensions/automerge/defs.ts +9 -9
  80. package/src/extensions/automerge/semaphore.ts +17 -19
  81. package/src/extensions/automerge/update-automerge.ts +12 -6
  82. package/src/extensions/automerge/update-codemirror.ts +5 -5
  83. package/src/extensions/awareness.ts +7 -4
  84. package/src/extensions/basic.ts +15 -13
  85. package/src/extensions/blast.ts +4 -1
  86. package/src/extensions/comments.ts +113 -65
  87. package/src/extensions/index.ts +0 -6
  88. package/src/extensions/markdown/bundle.ts +9 -8
  89. package/src/extensions/markdown/code.ts +150 -0
  90. package/src/extensions/markdown/formatting.ts +188 -0
  91. package/src/extensions/markdown/heading.ts +59 -0
  92. package/src/extensions/markdown/highlight.ts +9 -15
  93. package/src/extensions/{hr.ts → markdown/hr.ts} +3 -4
  94. package/src/extensions/markdown/index.ts +8 -0
  95. package/src/extensions/{link.ts → markdown/link.ts} +13 -4
  96. package/src/extensions/{tasklist.ts → markdown/tasklist.ts} +2 -3
  97. package/src/hooks/index.ts +2 -0
  98. package/src/hooks/useActionHandler.ts +62 -0
  99. package/src/hooks/useEditorView.ts +29 -0
  100. package/src/hooks/useTextEditor.ts +143 -19
  101. package/src/hooks/useTextModel.ts +28 -9
  102. package/src/index.ts +1 -1
  103. package/src/styles/markdown.ts +9 -10
  104. package/src/themes/default.ts +9 -6
  105. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
  106. package/dist/types/src/extensions/code.d.ts.map +0 -1
  107. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  108. package/dist/types/src/extensions/image.d.ts.map +0 -1
  109. package/dist/types/src/extensions/link.d.ts.map +0 -1
  110. package/dist/types/src/extensions/table.d.ts.map +0 -1
  111. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  112. package/src/components/TextEditor/comments.stories.tsx +0 -357
  113. package/src/extensions/code.ts +0 -104
  114. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  115. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  116. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  117. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  118. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  119. /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
  120. /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
  121. /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"comments.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/TextEditor/comments.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,YAAY,CAAC;AAEpB,OAAO,EAAE,KAAK,UAAU,EAAU,MAAM,kBAAkB,CAAC;AAG3D,OAAO,KAAwD,MAAM,OAAO,CAAC;AA6M7E,KAAK,UAAU,GAAG;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;;;;;mBAoHe,UAAU;;AAJ3B,wBAKE;AAWF,eAAO,MAAM,OAAO;;;;CAInB,CAAC;AAEF,eAAO,MAAM,OAAO;;;;;CAKnB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../src/extensions/code.ts"],"names":[],"mappings":"AAsDA,eAAO,MAAM,IAAI,+CAiDhB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"hr.d.ts","sourceRoot":"","sources":["../../../../src/extensions/hr.ts"],"names":[],"mappings":"AAmDA,eAAO,MAAM,EAAE,+CAoBd,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../../src/extensions/image.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,SAAS,EAA4C,MAAM,mBAAmB,CAAC;AAG/G,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC;AAE9B,eAAO,MAAM,KAAK,aAAa,YAAY,KAAQ,SA8BlD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../../src/extensions/link.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAkBpE,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,IAAI,aAAa,WAAW,KAAQ,SAsDhD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../../src/extensions/table.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,QAAQ,EAAmB,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AASnH,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC;AAE9B;;;GAGG;AACH,eAAO,MAAM,KAAK,aAAa,YAAY,8BAM1C,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tasklist.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasklist.ts"],"names":[],"mappings":"AAsFA,MAAM,MAAM,eAAe,GAAG,EAAE,CAAC;AAEjC,eAAO,MAAM,QAAQ,aAAa,eAAe,4CAsBhD,CAAC"}
@@ -1,357 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxosTheme';
6
-
7
- import { type EditorView, keymap } from '@codemirror/view';
8
- import { faker } from '@faker-js/faker';
9
- import { Check, Trash } from '@phosphor-icons/react';
10
- import React, { type FC, useEffect, useMemo, useRef, useState } from 'react';
11
-
12
- import { getTextContent, TextObject } from '@dxos/echo-schema';
13
- import { PublicKey } from '@dxos/keys';
14
- import { log } from '@dxos/log';
15
- import { Button, DensityProvider } from '@dxos/react-ui';
16
- import { fixedInsetFlexLayout, mx } from '@dxos/react-ui-theme';
17
- import { withTheme } from '@dxos/storybook-utils';
18
-
19
- import { MarkdownEditor, TextEditor } from './TextEditor';
20
- import { comments, type CommentsOptions, setFocus, useComments } from '../../extensions';
21
- import { type Comment, type Range, useTextModel } from '../../hooks';
22
-
23
- faker.seed(101);
24
-
25
- //
26
- // Editor
27
- //
28
-
29
- const Editor: FC<{
30
- item: { text: TextObject };
31
- comments: Comment[];
32
- selected?: string;
33
- onCreateComment: CommentsOptions['onCreate'];
34
- onDeleteComment: CommentsOptions['onDelete'];
35
- onUpdateComment: CommentsOptions['onUpdate'];
36
- onSelectComment: CommentsOptions['onSelect'];
37
- }> = ({
38
- item,
39
- selected: selectedValue,
40
- comments: commentRanges,
41
- onCreateComment,
42
- onDeleteComment,
43
- onUpdateComment,
44
- onSelectComment,
45
- }) => {
46
- const model = useTextModel({ text: item.text });
47
- const view = useRef<EditorView>(null);
48
- const [selected, setSelected] = useState<string>();
49
- useEffect(() => {
50
- if (!view.current?.hasFocus && selectedValue !== selected) {
51
- setSelected(selectedValue);
52
- if (selectedValue) {
53
- setFocus(view.current!, selectedValue);
54
- }
55
- }
56
- }, [selected, commentRanges, selectedValue]);
57
-
58
- useComments(view.current, commentRanges);
59
-
60
- const extensions = useMemo(() => {
61
- return [
62
- comments({
63
- onCreate: onCreateComment,
64
- onDelete: onDeleteComment,
65
- onUpdate: onUpdateComment,
66
- onSelect: onSelectComment,
67
- }),
68
- ];
69
- }, []);
70
-
71
- if (!model) {
72
- return null;
73
- }
74
-
75
- // TODO(burdon): Highlight currently selected comment.
76
- return (
77
- <div className='flex grow overflow-y-scroll'>
78
- <MarkdownEditor ref={view} model={model} extensions={extensions} />
79
- </div>
80
- );
81
- };
82
-
83
- //
84
- // Comments thread
85
- //
86
-
87
- type CommentThread = {
88
- id: string;
89
- cursor?: string;
90
- range?: Range;
91
- yPos?: number;
92
- messages: TextObject[];
93
- };
94
-
95
- const Thread: FC<{
96
- thread: CommentThread;
97
- selected: boolean;
98
- onSelect: () => void;
99
- onResolve: () => void;
100
- }> = ({ thread, selected, onSelect, onResolve }) => {
101
- const [focus, setFocus] = useState(false);
102
- const [item, setItem] = useState({ text: new TextObject() });
103
- const model = useTextModel({ text: item.text });
104
- const editorRef = useRef<EditorView>(null);
105
-
106
- const containerRef = useRef<HTMLDivElement>(null);
107
- useEffect(() => {
108
- if (selected) {
109
- containerRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' });
110
- if (thread.messages.length === 0) {
111
- setFocus(true);
112
- }
113
- }
114
- }, [selected]);
115
-
116
- const handleCreateMessage = () => {
117
- const text = model?.text().trim();
118
- if (text?.length) {
119
- thread.messages.push(item.text);
120
- setItem({ text: new TextObject() });
121
- setFocus(true);
122
- }
123
- };
124
-
125
- if (!model) {
126
- return null;
127
- }
128
-
129
- return (
130
- <div
131
- className={mx(
132
- 'flex flex-col m-1 rounded shadow divide-y bg-white',
133
- selected && 'ring',
134
- !thread.cursor && 'opacity-50',
135
- )}
136
- >
137
- <div className='flex p-2 gap-2 items-center text-xs font-mono text-neutral-500 font-thin'>
138
- <span>id:{thread.id.slice(0, 4)}</span>
139
- <span>from:{thread.range?.from}</span>
140
- <span>to:{thread.range?.to}</span>
141
- <span>y:{thread.yPos}</span>
142
- <span className='grow' />
143
- {!thread.cursor && <Trash />}
144
- </div>
145
-
146
- {thread.messages.map((message, i) => (
147
- // TODO(burdon): Fix default editor padding so content doesn't jump on creating message.
148
- <div key={i} className='p-2' onClick={() => onSelect()}>
149
- {getTextContent(message)}
150
- </div>
151
- ))}
152
-
153
- <div ref={containerRef} onClick={() => onSelect()} className='flex'>
154
- <TextEditor
155
- ref={editorRef}
156
- autoFocus={focus}
157
- model={model}
158
- placeholder={'Enter comment...'}
159
- slots={{ root: { className: 'grow rounded-b' } }}
160
- extensions={[
161
- keymap.of([
162
- {
163
- key: 'Enter',
164
- run: () => {
165
- handleCreateMessage();
166
- return true;
167
- },
168
- },
169
- ]),
170
- ]}
171
- />
172
- <Button variant='ghost' classNames='px-1' title='Resolve' onClick={onResolve}>
173
- <Check />
174
- </Button>
175
- </div>
176
- </div>
177
- );
178
- };
179
-
180
- const Sidebar: FC<{
181
- threads: CommentThread[];
182
- selected?: string;
183
- onSelect: (thread: string) => void;
184
- onResolve: (thread: string) => void;
185
- }> = ({ threads, selected, onSelect, onResolve }) => {
186
- // Sort by y-position.
187
- const sortedThreads = useMemo(() => {
188
- const sorted = [...threads];
189
- return sorted.sort(({ yPos: a = Infinity }, { yPos: b = Infinity }) => {
190
- return a < b ? -1 : a > b ? 1 : 0;
191
- });
192
- }, [threads]);
193
-
194
- return (
195
- <DensityProvider density='fine'>
196
- <div className='flex flex-col grow overflow-y-scroll py-4 gap-4'>
197
- {sortedThreads.map((thread) => (
198
- <Thread
199
- key={thread.id}
200
- thread={thread}
201
- selected={thread.id === selected}
202
- onSelect={() => onSelect(thread.id)}
203
- onResolve={() => onResolve(thread.id)}
204
- />
205
- ))}
206
- </div>
207
- </DensityProvider>
208
- );
209
- };
210
-
211
- //
212
- // Story container.
213
- //
214
-
215
- type StoryProps = {
216
- text?: string;
217
- autoCreate?: boolean;
218
- };
219
-
220
- const Story = ({ text, autoCreate }: StoryProps) => {
221
- const [item] = useState({ text: new TextObject(text) });
222
- const [threads, setThreads] = useState<CommentThread[]>([]);
223
- const [selected, setSelected] = useState<string>();
224
-
225
- const comments = useMemo<Comment[]>(() => threads.map(({ id, cursor }) => ({ id, cursor })), [threads]);
226
-
227
- // Filter by visibility.
228
- const visibleThreads = useMemo(() => threads.filter((thread) => thread.yPos !== undefined), [threads]);
229
-
230
- const handleCreateComment: CommentsOptions['onCreate'] = (cursor, location) => {
231
- const id = PublicKey.random().toHex();
232
- log.info('create', { id: id.slice(0, 4), cursor });
233
- setThreads((threads) => [
234
- ...threads,
235
- {
236
- id,
237
- cursor,
238
- yPos: location ? Math.floor(location.top) : undefined,
239
- messages: autoCreate
240
- ? faker.helpers.multiple(() => new TextObject(faker.lorem.sentence()), { count: { min: 1, max: 3 } })
241
- : [],
242
- },
243
- ]);
244
- setSelected(id); // TODO(burdon): Not required.
245
- return id;
246
- };
247
-
248
- const handleDeleteComment: CommentsOptions['onDelete'] = (id) => {
249
- log.info('delete', { id: id.slice(0, 4) });
250
- setThreads((threads) =>
251
- threads.map((thread) => {
252
- if (thread.id === id) {
253
- thread.cursor = undefined;
254
- }
255
-
256
- return thread;
257
- }),
258
- );
259
-
260
- setSelected((selected) => (selected === id ? undefined : selected));
261
- };
262
-
263
- const handleUpdateComment: CommentsOptions['onUpdate'] = (id, cursor) => {
264
- log.info('update', { id: id.slice(0, 4), cursor });
265
- setThreads((threads) =>
266
- threads.map((thread) => {
267
- if (thread.id === id) {
268
- thread.cursor = cursor;
269
- }
270
-
271
- return thread;
272
- }),
273
- );
274
- };
275
-
276
- const handleSelectComment: CommentsOptions['onSelect'] = ({ comments, selection: { current, closest } }) => {
277
- log.info('select', { active: current?.slice(0, 4), closest: closest?.slice(0, 4) });
278
- setThreads((threads) =>
279
- threads.map((thread) => {
280
- const comment = comments.find(({ comment }) => comment.id === thread.id);
281
- if (comment) {
282
- thread.yPos = comment.location ? Math.floor(comment.location.top) : undefined;
283
- thread.range = { from: comment.range.from, to: comment.range.to };
284
- }
285
-
286
- return thread;
287
- }),
288
- );
289
-
290
- setSelected(current ?? closest);
291
- };
292
-
293
- const handleSelectThread = (id: string) => {
294
- setSelected(id);
295
- };
296
-
297
- const handleResolveThread = (id: string) => {
298
- setThreads((threads) => [...threads.filter((thread) => thread.id !== id)]);
299
- setSelected(undefined);
300
- };
301
-
302
- return (
303
- <div className={mx(fixedInsetFlexLayout, 'bg-neutral-100')}>
304
- <div className='flex justify-center h-full gap-8'>
305
- <div className='flex flex-col h-full w-[600px]'>
306
- <Editor
307
- item={item}
308
- selected={selected}
309
- comments={comments}
310
- onCreateComment={handleCreateComment}
311
- onDeleteComment={handleDeleteComment}
312
- onUpdateComment={handleUpdateComment}
313
- onSelectComment={handleSelectComment}
314
- />
315
- </div>
316
-
317
- <div className='flex flex-col h-full w-[300px]'>
318
- <Sidebar
319
- threads={visibleThreads}
320
- selected={selected}
321
- onSelect={handleSelectThread}
322
- onResolve={handleResolveThread}
323
- />
324
- </div>
325
- </div>
326
- </div>
327
- );
328
- };
329
-
330
- export default {
331
- title: 'react-ui-editor/comments',
332
- component: MarkdownEditor,
333
- decorators: [withTheme],
334
- render: (args: StoryProps) => <Story {...args} />,
335
- };
336
-
337
- const str = (...lines: string[]) => lines.join('\n');
338
-
339
- const document = str(
340
- '# Comments',
341
- '',
342
- str(...faker.helpers.multiple(() => [faker.lorem.paragraph({ min: 5, max: 10 }), ''], { count: 20 }).flat()),
343
- '',
344
- );
345
-
346
- export const Default = {
347
- args: {
348
- text: document,
349
- },
350
- };
351
-
352
- export const Testing = {
353
- args: {
354
- text: document,
355
- autoCreate: true,
356
- },
357
- };
@@ -1,104 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { type Range } from '@codemirror/state';
6
- import {
7
- ViewPlugin,
8
- type DecorationSet,
9
- EditorView,
10
- type ViewUpdate,
11
- Decoration,
12
- type BlockInfo,
13
- } from '@codemirror/view';
14
-
15
- import { mx } from '@dxos/react-ui-theme';
16
-
17
- import { getToken } from '../styles';
18
-
19
- const CODE_REGEX = /```[\s\S]*?```/gs;
20
-
21
- // TODO(burdon): Reconcile with theme.
22
- const styles = EditorView.baseTheme({
23
- '& .cm-codeblock': {
24
- fontFamily: getToken('fontFamily.mono', []).join(','),
25
- },
26
- '&light [aria-readonly="true"] .cm-codeblock': {
27
- background: getToken('extend.colors.neutral.50'),
28
- },
29
- '&dark [aria-readonly="true"] .cm-codeblock': {
30
- background: getToken('extend.colors.neutral.850'),
31
- },
32
- '& [aria-readonly="true"] .cm-codeblock': {
33
- display: 'block',
34
- paddingInline: '4px !important',
35
- },
36
- '& [aria-readonly="true"] .cm-codeblock-first': {
37
- paddingTop: '4px !important',
38
- borderTopLeftRadius: '4px',
39
- borderTopRightRadius: '4px',
40
- },
41
- '& [aria-readonly="true"] .cm-codeblock-last': {
42
- paddingBottom: '4px !important',
43
- borderBottomLeftRadius: '4px',
44
- borderBottomRightRadius: '4px',
45
- },
46
- });
47
-
48
- // TODO(burdon): Start from EditorView.lineBlockAt(pos).
49
- const getLineRange = (lines: BlockInfo[], from: number, to: number) => {
50
- const start = lines.findIndex((line) => line.from >= from);
51
- const end = lines.findIndex((line) => line.to >= to);
52
- return [start, end];
53
- };
54
-
55
- export const code = () => {
56
- const buildDecorations = (view: EditorView) => {
57
- const decorations: Range<Decoration>[] = [];
58
- const text = view.state.doc.sliceString(0);
59
- const matches = text.matchAll(CODE_REGEX);
60
- const blocks = view.viewportLineBlocks;
61
- for (const match of matches) {
62
- const range = getLineRange(blocks, match.index!, match.index! + match[0].length);
63
- for (let i = range[0]; i <= range[1]; i++) {
64
- const block = blocks[i];
65
- decorations.push(
66
- Decoration.line({
67
- class: mx('cm-codeblock', i === range[0] && 'cm-codeblock-first', i === range[1] && 'cm-codeblock-last'),
68
- }).range(block.from),
69
- );
70
- }
71
- }
72
-
73
- return Decoration.set(decorations);
74
- };
75
-
76
- return [
77
- ViewPlugin.fromClass(
78
- class {
79
- hasFocus = false;
80
- decorations: DecorationSet;
81
- constructor(view: EditorView) {
82
- this.decorations = buildDecorations(view);
83
- }
84
-
85
- update(update: ViewUpdate) {
86
- if (
87
- // TODO(burdon): Generalize for other extensions.
88
- this.hasFocus !== update.view.hasFocus ||
89
- update.startState.readOnly !== update.view.state.readOnly ||
90
- update.docChanged ||
91
- update.viewportChanged
92
- ) {
93
- this.hasFocus = update.view.hasFocus;
94
- this.decorations = buildDecorations(update.view);
95
- }
96
- }
97
- },
98
- {
99
- decorations: (value) => value.decorations,
100
- },
101
- ),
102
- styles,
103
- ];
104
- };
File without changes
File without changes