@dxos/react-ui-editor 0.4.8-main.eb5ae94 → 0.4.8-main.fff1521
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 +2725 -2711
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +18 -34
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +80 -5
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +11 -5
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +4 -2
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +2 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts +1 -15
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/index.d.ts +0 -1
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts +2 -1
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/{hooks → extensions/awareness}/awareness-provider.d.ts +1 -1
- package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness/index.d.ts +3 -0
- package/dist/types/src/extensions/awareness/index.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +2 -2
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/cursor-line-margin.d.ts +6 -7
- package/dist/types/src/extensions/cursor-line-margin.d.ts.map +1 -1
- package/dist/types/src/extensions/cursor.d.ts +1 -1
- package/dist/types/src/extensions/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/doc.d.ts +3 -0
- package/dist/types/src/extensions/doc.d.ts.map +1 -0
- package/dist/types/src/extensions/factories.d.ts +49 -0
- package/dist/types/src/extensions/factories.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +4 -1
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +5 -4
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +8 -5
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/image.d.ts +6 -0
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/table.d.ts +2 -2
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
- package/dist/types/src/extensions/modes.d.ts +1 -0
- package/dist/types/src/extensions/modes.d.ts.map +1 -1
- package/dist/types/src/extensions/state.d.ts +20 -0
- package/dist/types/src/extensions/state.d.ts.map +1 -0
- package/dist/types/src/extensions/types.d.ts +9 -0
- package/dist/types/src/extensions/types.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +1 -4
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useDocAccessor.d.ts +7 -0
- package/dist/types/src/hooks/useDocAccessor.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +3 -44
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.stories.d.ts +7 -0
- package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/themes/default.d.ts +0 -3
- package/dist/types/src/themes/default.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/package.json +25 -27
- package/src/components/TextEditor/TextEditor.stories.tsx +520 -23
- package/src/components/TextEditor/TextEditor.tsx +45 -156
- package/src/components/Toolbar/Toolbar.stories.tsx +54 -55
- package/src/components/Toolbar/Toolbar.tsx +96 -35
- package/src/extensions/automerge/automerge.stories.tsx +16 -17
- package/src/extensions/automerge/automerge.ts +2 -1
- package/src/extensions/automerge/cursor.ts +1 -1
- package/src/extensions/automerge/defs.ts +1 -22
- package/src/extensions/automerge/index.ts +0 -1
- package/src/extensions/automerge/sync.ts +2 -9
- package/src/extensions/automerge/update-automerge.ts +2 -1
- package/src/{hooks → extensions/awareness}/awareness-provider.ts +1 -1
- package/src/extensions/{awareness.ts → awareness/awareness.ts} +1 -1
- package/src/extensions/awareness/index.ts +6 -0
- package/src/extensions/command/state.ts +1 -1
- package/src/extensions/comments.ts +9 -5
- package/src/extensions/cursor-line-margin.ts +17 -18
- package/src/extensions/cursor.ts +1 -1
- package/src/extensions/doc.ts +14 -0
- package/src/extensions/factories.ts +167 -0
- package/src/extensions/index.ts +4 -1
- package/src/extensions/markdown/bundle.ts +11 -28
- package/src/extensions/markdown/decorate.ts +2 -4
- package/src/extensions/markdown/formatting.test.ts +13 -13
- package/src/extensions/markdown/formatting.ts +106 -93
- package/src/extensions/markdown/image.ts +7 -0
- package/src/extensions/markdown/table.ts +11 -3
- package/src/extensions/modes.ts +3 -1
- package/src/extensions/state.ts +90 -0
- package/src/extensions/types.ts +16 -0
- package/src/hooks/index.ts +1 -5
- package/src/hooks/useActionHandler.ts +5 -1
- package/src/hooks/useDocAccessor.ts +21 -0
- package/src/hooks/useTextEditor.stories.tsx +40 -35
- package/src/hooks/useTextEditor.ts +14 -120
- package/src/index.ts +1 -1
- package/src/themes/default.ts +1 -28
- package/src/translations.ts +1 -0
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +0 -103
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +0 -1
- package/dist/types/src/extensions/awareness.d.ts.map +0 -1
- package/dist/types/src/extensions/outliner.d.ts +0 -4
- package/dist/types/src/extensions/outliner.d.ts.map +0 -1
- package/dist/types/src/hooks/awareness-provider.d.ts.map +0 -1
- package/dist/types/src/hooks/defs.d.ts +0 -20
- package/dist/types/src/hooks/defs.d.ts.map +0 -1
- package/dist/types/src/hooks/useEditorView.d.ts +0 -20
- package/dist/types/src/hooks/useEditorView.d.ts.map +0 -1
- package/dist/types/src/hooks/useTextModel.d.ts +0 -33
- package/dist/types/src/hooks/useTextModel.d.ts.map +0 -1
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +0 -524
- package/src/extensions/outliner.ts +0 -12
- package/src/hooks/defs.ts +0 -30
- package/src/hooks/useEditorView.ts +0 -32
- package/src/hooks/useTextModel.ts +0 -121
- /package/dist/types/src/extensions/{awareness.d.ts → awareness/awareness.d.ts} +0 -0
|
@@ -7,11 +7,13 @@ import {
|
|
|
7
7
|
ChatText,
|
|
8
8
|
Code,
|
|
9
9
|
CodeBlock,
|
|
10
|
+
Image,
|
|
10
11
|
Link,
|
|
11
12
|
ListBullets,
|
|
12
13
|
ListChecks,
|
|
13
14
|
ListNumbers,
|
|
14
15
|
Paragraph,
|
|
16
|
+
Quotes,
|
|
15
17
|
TextStrikethrough,
|
|
16
18
|
Table,
|
|
17
19
|
TextB,
|
|
@@ -22,19 +24,20 @@ import {
|
|
|
22
24
|
TextHFive,
|
|
23
25
|
TextHSix,
|
|
24
26
|
TextItalic,
|
|
25
|
-
Quotes,
|
|
26
27
|
} from '@phosphor-icons/react';
|
|
27
28
|
import { createContext } from '@radix-ui/react-context';
|
|
28
|
-
import React, { type PropsWithChildren, useRef, useState } from 'react';
|
|
29
|
+
import React, { type PropsWithChildren, useEffect, useRef, useState } from 'react';
|
|
30
|
+
import { useDropzone } from 'react-dropzone';
|
|
29
31
|
|
|
30
32
|
import {
|
|
31
33
|
DensityProvider,
|
|
32
34
|
ElevationProvider,
|
|
33
35
|
Select,
|
|
34
|
-
type ThemedClassName,
|
|
35
36
|
Toolbar as NaturalToolbar,
|
|
36
37
|
Tooltip,
|
|
37
|
-
type
|
|
38
|
+
type ThemedClassName,
|
|
39
|
+
type ToolbarToggleGroupItemProps as NaturalToolbarToggleGroupItemProps,
|
|
40
|
+
type ToolbarButtonProps as NaturalToolbarButtonProps,
|
|
38
41
|
useTranslation,
|
|
39
42
|
} from '@dxos/react-ui';
|
|
40
43
|
import { getSize } from '@dxos/react-ui-theme';
|
|
@@ -80,7 +83,7 @@ export type Action = {
|
|
|
80
83
|
|
|
81
84
|
export type ToolbarProps = ThemedClassName<
|
|
82
85
|
PropsWithChildren<{
|
|
83
|
-
state: Formatting |
|
|
86
|
+
state: Formatting | undefined;
|
|
84
87
|
onAction?: (action: Action) => void;
|
|
85
88
|
}>
|
|
86
89
|
>;
|
|
@@ -111,9 +114,9 @@ type ButtonProps = {
|
|
|
111
114
|
disabled?: (state: Formatting) => boolean;
|
|
112
115
|
};
|
|
113
116
|
|
|
114
|
-
type
|
|
117
|
+
type ToolbarToggleButtonProps = NaturalToolbarToggleGroupItemProps & { Icon: Icon };
|
|
115
118
|
|
|
116
|
-
const
|
|
119
|
+
const ToolbarToggleButton = ({ Icon, children, ...props }: ToolbarToggleButtonProps) => {
|
|
117
120
|
return (
|
|
118
121
|
<Tooltip.Root>
|
|
119
122
|
<Tooltip.Trigger asChild>
|
|
@@ -132,6 +135,27 @@ const ToolbarButton = ({ Icon, children, ...props }: ToolbarButtonProps) => {
|
|
|
132
135
|
);
|
|
133
136
|
};
|
|
134
137
|
|
|
138
|
+
type ToolbarButtonProps = NaturalToolbarButtonProps & { Icon: Icon };
|
|
139
|
+
|
|
140
|
+
const ToolbarButton = ({ Icon, children, ...props }: ToolbarButtonProps) => {
|
|
141
|
+
return (
|
|
142
|
+
<Tooltip.Root>
|
|
143
|
+
<Tooltip.Trigger asChild>
|
|
144
|
+
<NaturalToolbar.Button variant='ghost' {...props} classNames={buttonStyles}>
|
|
145
|
+
<Icon className={iconStyles} />
|
|
146
|
+
<span className='sr-only'>{children}</span>
|
|
147
|
+
</NaturalToolbar.Button>
|
|
148
|
+
</Tooltip.Trigger>
|
|
149
|
+
<Tooltip.Portal>
|
|
150
|
+
<Tooltip.Content {...tooltipProps}>
|
|
151
|
+
{children}
|
|
152
|
+
<Tooltip.Arrow />
|
|
153
|
+
</Tooltip.Content>
|
|
154
|
+
</Tooltip.Portal>
|
|
155
|
+
</Tooltip.Root>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
135
159
|
const ToolbarSeparator = () => <div role='separator' className='grow' />;
|
|
136
160
|
|
|
137
161
|
const MarkdownHeading = () => {
|
|
@@ -227,7 +251,7 @@ const MarkdownStyles = () => {
|
|
|
227
251
|
value={markdownStyles.filter(({ getState }) => state && getState(state)).map(({ type }) => type)}
|
|
228
252
|
>
|
|
229
253
|
{markdownStyles.map(({ type, getState, Icon }) => (
|
|
230
|
-
<
|
|
254
|
+
<ToolbarToggleButton
|
|
231
255
|
key={type}
|
|
232
256
|
value={type}
|
|
233
257
|
Icon={Icon}
|
|
@@ -235,7 +259,7 @@ const MarkdownStyles = () => {
|
|
|
235
259
|
onClick={state ? () => onAction?.({ type, data: !getState(state) }) : undefined}
|
|
236
260
|
>
|
|
237
261
|
{t(`${type} label`)}
|
|
238
|
-
</
|
|
262
|
+
</ToolbarToggleButton>
|
|
239
263
|
))}
|
|
240
264
|
</NaturalToolbar.ToggleGroup>
|
|
241
265
|
);
|
|
@@ -253,14 +277,14 @@ const MarkdownLists = () => {
|
|
|
253
277
|
return (
|
|
254
278
|
<NaturalToolbar.ToggleGroup type='single' value={state?.listStyle ? `list-${state.listStyle}` : ''}>
|
|
255
279
|
{markdownLists.map(({ type, getState, Icon }) => (
|
|
256
|
-
<
|
|
280
|
+
<ToolbarToggleButton
|
|
257
281
|
key={type}
|
|
258
282
|
value={type}
|
|
259
283
|
Icon={Icon}
|
|
260
284
|
onClick={state ? () => onAction?.({ type, data: !getState(state) }) : undefined}
|
|
261
285
|
>
|
|
262
286
|
{t(`${type} label`)}
|
|
263
|
-
</
|
|
287
|
+
</ToolbarToggleButton>
|
|
264
288
|
))}
|
|
265
289
|
</NaturalToolbar.ToggleGroup>
|
|
266
290
|
);
|
|
@@ -292,7 +316,7 @@ const MarkdownBlocks = () => {
|
|
|
292
316
|
return (
|
|
293
317
|
<NaturalToolbar.ToggleGroup type='single' value={value?.type ?? ''}>
|
|
294
318
|
{markdownBlocks.map(({ type, disabled, getState, Icon }) => (
|
|
295
|
-
<
|
|
319
|
+
<ToolbarToggleButton
|
|
296
320
|
key={type}
|
|
297
321
|
value={type}
|
|
298
322
|
Icon={Icon}
|
|
@@ -300,7 +324,7 @@ const MarkdownBlocks = () => {
|
|
|
300
324
|
onClick={state ? () => onAction?.({ type, data: !getState(state) }) : undefined}
|
|
301
325
|
>
|
|
302
326
|
{t(`${type} label`)}
|
|
303
|
-
</
|
|
327
|
+
</ToolbarToggleButton>
|
|
304
328
|
))}
|
|
305
329
|
</NaturalToolbar.ToggleGroup>
|
|
306
330
|
);
|
|
@@ -315,38 +339,75 @@ const MarkdownStandard = () => (
|
|
|
315
339
|
</>
|
|
316
340
|
);
|
|
317
341
|
|
|
318
|
-
|
|
342
|
+
// TODO(burdon): Make extensible.
|
|
343
|
+
export type MarkdownCustomOptions = {
|
|
344
|
+
onUpload?: (file: File) => Promise<{ url?: string }>;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const MarkdownCustom = ({ onUpload }: MarkdownCustomOptions = {}) => {
|
|
319
348
|
const { onAction } = useToolbarContext('MarkdownStyles');
|
|
320
349
|
const { t } = useTranslation(translationKey);
|
|
350
|
+
// https://react-dropzone.js.org/#src
|
|
351
|
+
const { acceptedFiles, getInputProps, open } = useDropzone({
|
|
352
|
+
multiple: false,
|
|
353
|
+
noDrag: true,
|
|
354
|
+
accept: {
|
|
355
|
+
'image/*': ['.jpg', '.jpeg', '.png', '.gif'],
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
useEffect(() => {
|
|
360
|
+
if (onUpload && acceptedFiles.length) {
|
|
361
|
+
setTimeout(async () => {
|
|
362
|
+
// NOTE: Clone file since react-dropzone patches in a non-standard `path` property, which confuses IPFS.
|
|
363
|
+
const f = acceptedFiles[0];
|
|
364
|
+
const file = new File([f], f.name, {
|
|
365
|
+
type: f.type,
|
|
366
|
+
lastModified: f.lastModified,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const { url } = await onUpload(file);
|
|
370
|
+
if (url) {
|
|
371
|
+
onAction?.({ type: 'image', data: url });
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}, [acceptedFiles]);
|
|
376
|
+
|
|
321
377
|
return (
|
|
322
|
-
|
|
323
|
-
<
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
378
|
+
<>
|
|
379
|
+
<input {...getInputProps()} />
|
|
380
|
+
<ToolbarButton value='image' Icon={Image} onClick={() => open()}>
|
|
381
|
+
{t('image label')}
|
|
382
|
+
</ToolbarButton>
|
|
383
|
+
</>
|
|
384
|
+
);
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// TODO(burdon): Make extensible.
|
|
388
|
+
const MarkdownActions = () => {
|
|
389
|
+
const { onAction } = useToolbarContext('MarkdownStyles');
|
|
390
|
+
const { t } = useTranslation(translationKey);
|
|
391
|
+
return (
|
|
392
|
+
<>
|
|
393
|
+
{/* TODO(burdon): Toggle readonly state. */}
|
|
394
|
+
{/* <ToolbarButton value='comment' Icon={BookOpenText} onClick={() => onAction?.({ type: 'comment' })}> */}
|
|
395
|
+
{/* {t('comment label')} */}
|
|
396
|
+
{/* </ToolbarButton> */}
|
|
397
|
+
<ToolbarButton value='comment' Icon={ChatText} onClick={() => onAction?.({ type: 'comment' })}>
|
|
398
|
+
{t('comment label')}
|
|
399
|
+
</ToolbarButton>
|
|
400
|
+
</>
|
|
341
401
|
);
|
|
342
402
|
};
|
|
343
403
|
|
|
344
404
|
export const Toolbar = {
|
|
345
405
|
Root: ToolbarRoot,
|
|
346
|
-
Button:
|
|
406
|
+
Button: ToolbarToggleButton,
|
|
347
407
|
Separator: ToolbarSeparator,
|
|
348
408
|
Markdown: MarkdownStandard,
|
|
349
|
-
|
|
409
|
+
Custom: MarkdownCustom,
|
|
410
|
+
Actions: MarkdownActions,
|
|
350
411
|
};
|
|
351
412
|
|
|
352
413
|
export { useToolbarContext };
|
|
@@ -9,7 +9,7 @@ import '@preact/signals-react';
|
|
|
9
9
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
10
10
|
|
|
11
11
|
import { Repo } from '@dxos/automerge/automerge-repo';
|
|
12
|
-
import { createDocAccessor, Filter } from '@dxos/echo-schema';
|
|
12
|
+
import { createDocAccessor, Filter, DocAccessor } from '@dxos/echo-schema';
|
|
13
13
|
import { type PublicKey } from '@dxos/keys';
|
|
14
14
|
import { Expando, TextObject, useSpace } from '@dxos/react-client/echo';
|
|
15
15
|
import { ClientRepeater } from '@dxos/react-client/testing';
|
|
@@ -17,9 +17,9 @@ import { useThemeContext } from '@dxos/react-ui';
|
|
|
17
17
|
import { withTheme } from '@dxos/storybook-utils';
|
|
18
18
|
|
|
19
19
|
import { automerge } from './automerge';
|
|
20
|
-
import {
|
|
21
|
-
import { createBasicExtensions, createThemeExtensions, useTextEditor } from '../../hooks';
|
|
20
|
+
import { useTextEditor } from '../../hooks';
|
|
22
21
|
import translations from '../../translations';
|
|
22
|
+
import { createBasicExtensions, createThemeExtensions } from '../factories';
|
|
23
23
|
|
|
24
24
|
const initialContent = 'Hello world!';
|
|
25
25
|
|
|
@@ -34,21 +34,19 @@ type EditorProps = {
|
|
|
34
34
|
|
|
35
35
|
const Editor = ({ source, autoFocus }: EditorProps) => {
|
|
36
36
|
const { themeMode } = useThemeContext();
|
|
37
|
-
const
|
|
38
|
-
() =>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
const { parentRef } = useTextEditor(
|
|
38
|
+
() => ({
|
|
39
|
+
doc: DocAccessor.getValue(source),
|
|
40
|
+
extensions: [
|
|
41
|
+
createBasicExtensions({ placeholder: 'Type here...' }),
|
|
42
|
+
createThemeExtensions({ themeMode, slots: { editor: { className: 'p-2 bg-white dark:bg-black' } } }),
|
|
43
|
+
automerge(source),
|
|
44
|
+
],
|
|
45
|
+
autoFocus,
|
|
46
|
+
}),
|
|
47
|
+
[source, themeMode],
|
|
44
48
|
);
|
|
45
49
|
|
|
46
|
-
const { parentRef } = useTextEditor({
|
|
47
|
-
doc: DocAccessor.getValue(source),
|
|
48
|
-
extensions,
|
|
49
|
-
autoFocus,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
50
|
return <div ref={parentRef} />;
|
|
53
51
|
};
|
|
54
52
|
|
|
@@ -114,7 +112,8 @@ const EchoStory = ({ spaceKey }: { spaceKey: PublicKey }) => {
|
|
|
114
112
|
|
|
115
113
|
export const Default = {};
|
|
116
114
|
|
|
117
|
-
// TODO(burdon):
|
|
115
|
+
// TODO(burdon): Error:
|
|
116
|
+
// chunk-6NX3RPDS.mjs:2021 ControlPipeline#5 Error: invariant violation: Feed already added
|
|
118
117
|
export const WithEcho = {
|
|
119
118
|
decorators: [withTheme],
|
|
120
119
|
render: () => {
|
|
@@ -8,9 +8,10 @@ import { StateField, type Extension } from '@codemirror/state';
|
|
|
8
8
|
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
9
9
|
|
|
10
10
|
import { next as A } from '@dxos/automerge/automerge';
|
|
11
|
+
import { type DocAccessor } from '@dxos/echo-schema';
|
|
11
12
|
|
|
12
13
|
import { cursorConverter } from './cursor';
|
|
13
|
-
import { updateHeadsEffect, isReconcile, type State
|
|
14
|
+
import { updateHeadsEffect, isReconcile, type State } from './defs';
|
|
14
15
|
import { Syncer } from './sync';
|
|
15
16
|
import { Cursor } from '../cursor';
|
|
16
17
|
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import get from 'lodash.get';
|
|
6
6
|
|
|
7
7
|
import { next as A } from '@dxos/automerge/automerge';
|
|
8
|
+
import { type DocAccessor } from '@dxos/echo-schema';
|
|
8
9
|
import { log } from '@dxos/log';
|
|
9
10
|
|
|
10
|
-
import { type DocAccessor } from './defs';
|
|
11
11
|
import { type CursorConverter } from '../cursor';
|
|
12
12
|
|
|
13
13
|
export const cursorConverter = ({ handle, path }: DocAccessor): CursorConverter => ({
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
//
|
|
6
6
|
|
|
7
7
|
import { Annotation, StateEffect, type StateField, type EditorState, type Transaction } from '@codemirror/state';
|
|
8
|
-
import get from 'lodash.get';
|
|
9
8
|
|
|
10
|
-
import { type
|
|
9
|
+
import { type Heads, type Prop } from '@dxos/automerge/automerge';
|
|
11
10
|
|
|
12
11
|
export type State = {
|
|
13
12
|
path: Prop[];
|
|
@@ -31,23 +30,3 @@ export const reconcileAnnotation = Annotation.define<boolean>();
|
|
|
31
30
|
export const isReconcile = (tr: Transaction): boolean => {
|
|
32
31
|
return !!tr.annotation(reconcileAnnotation);
|
|
33
32
|
};
|
|
34
|
-
|
|
35
|
-
// TODO(burdon): Reconcile with echo.
|
|
36
|
-
export interface DocAccessor<T = any> {
|
|
37
|
-
handle: IDocHandle<T>;
|
|
38
|
-
get path(): string[]; // TODO(burdon): Getter or prop?
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// TODO(burdon): Reconcile with echo.
|
|
42
|
-
export const DocAccessor = {
|
|
43
|
-
getValue: (accessor: DocAccessor) => get(accessor.handle, accessor.path),
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// TODO(burdon): Reconcile with echo.
|
|
47
|
-
export interface IDocHandle<T = any> {
|
|
48
|
-
docSync(): Doc<T> | undefined;
|
|
49
|
-
change(callback: ChangeFn<T>, options?: ChangeOptions<T>): void;
|
|
50
|
-
changeAt(heads: Heads, callback: ChangeFn<T>, options?: ChangeOptions<T>): string[] | undefined;
|
|
51
|
-
addListener(event: 'change', listener: () => void): void;
|
|
52
|
-
removeListener(event: 'change', listener: () => void): void;
|
|
53
|
-
}
|
|
@@ -8,16 +8,9 @@ import { type StateField } from '@codemirror/state';
|
|
|
8
8
|
import { type EditorView } from '@codemirror/view';
|
|
9
9
|
|
|
10
10
|
import { next as A } from '@dxos/automerge/automerge';
|
|
11
|
+
import { type IDocHandle } from '@dxos/echo-schema';
|
|
11
12
|
|
|
12
|
-
import {
|
|
13
|
-
getLastHeads,
|
|
14
|
-
getPath,
|
|
15
|
-
type IDocHandle,
|
|
16
|
-
type State,
|
|
17
|
-
reconcileAnnotation,
|
|
18
|
-
updateHeads,
|
|
19
|
-
isReconcile,
|
|
20
|
-
} from './defs';
|
|
13
|
+
import { getLastHeads, getPath, type State, reconcileAnnotation, updateHeads, isReconcile } from './defs';
|
|
21
14
|
import { updateAutomerge } from './update-automerge';
|
|
22
15
|
import { updateCodeMirror } from './update-codemirror';
|
|
23
16
|
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
import { type EditorState, type StateField, type Transaction, type Text } from '@codemirror/state';
|
|
8
8
|
|
|
9
9
|
import { next as A, type Heads } from '@dxos/automerge/automerge';
|
|
10
|
+
import { type IDocHandle } from '@dxos/echo-schema';
|
|
10
11
|
|
|
11
|
-
import { type
|
|
12
|
+
import { type State } from './defs';
|
|
12
13
|
|
|
13
14
|
export const updateAutomerge = (
|
|
14
15
|
field: StateField<State>,
|
|
@@ -9,7 +9,7 @@ import { log } from '@dxos/log';
|
|
|
9
9
|
import { type Space } from '@dxos/react-client/echo';
|
|
10
10
|
import { type GossipMessage } from '@dxos/react-client/mesh';
|
|
11
11
|
|
|
12
|
-
import { type AwarenessInfo, type AwarenessPosition, type AwarenessProvider, type AwarenessState } from '
|
|
12
|
+
import { type AwarenessInfo, type AwarenessPosition, type AwarenessProvider, type AwarenessState } from './awareness';
|
|
13
13
|
|
|
14
14
|
type ProtocolMessage =
|
|
15
15
|
| {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { Event } from '@dxos/async';
|
|
17
17
|
import { Context } from '@dxos/context';
|
|
18
18
|
|
|
19
|
-
import { Cursor, type CursorConverter } from '
|
|
19
|
+
import { Cursor, type CursorConverter } from '../cursor';
|
|
20
20
|
|
|
21
21
|
export interface AwarenessProvider {
|
|
22
22
|
remoteStateChange: Event<void>;
|
|
@@ -74,7 +74,7 @@ export const commandState = StateField.define<CommandState>({
|
|
|
74
74
|
|
|
75
75
|
return state;
|
|
76
76
|
},
|
|
77
|
-
provide: (
|
|
77
|
+
provide: (field) => [showTooltip.from(field, (value) => value.tooltip ?? null)],
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
export const openEffect = StateEffect.define<{ pos: number; fullWidth?: boolean }>();
|
|
@@ -13,7 +13,7 @@ import { log } from '@dxos/log';
|
|
|
13
13
|
import { nonNullable } from '@dxos/util';
|
|
14
14
|
|
|
15
15
|
import { Cursor } from './cursor';
|
|
16
|
-
import { type Comment, type Range } from '
|
|
16
|
+
import { type Comment, type Range } from './types';
|
|
17
17
|
import { getToken } from '../styles';
|
|
18
18
|
import { callbackWrapper } from '../util';
|
|
19
19
|
|
|
@@ -508,12 +508,16 @@ export const focusComment = (view: EditorView, id: string, center = true) => {
|
|
|
508
508
|
/**
|
|
509
509
|
* Update comments state field.
|
|
510
510
|
*/
|
|
511
|
-
export const useComments = (view: EditorView | null, id: string, comments
|
|
511
|
+
export const useComments = (view: EditorView | null | undefined, id: string, comments?: Comment[]) => {
|
|
512
512
|
useEffect(() => {
|
|
513
513
|
if (view) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
514
|
+
// Check same document.
|
|
515
|
+
// NOTE: Hook might be called before editor state is updated.
|
|
516
|
+
if (id === view.state.facet(documentId)) {
|
|
517
|
+
view.dispatch({
|
|
518
|
+
effects: setComments.of({ id, comments: comments ?? [] }),
|
|
519
|
+
});
|
|
520
|
+
}
|
|
517
521
|
}
|
|
518
522
|
}, [id, view, comments]);
|
|
519
523
|
};
|
|
@@ -5,26 +5,26 @@
|
|
|
5
5
|
// Based upon @acheronfail’s implementation, fetched 16 Feb 2024
|
|
6
6
|
// https://discuss.codemirror.net/t/cursorscrollmargin-for-v6/7448/5
|
|
7
7
|
|
|
8
|
-
import { type EditorState, type Extension,
|
|
8
|
+
import { type EditorState, type Extension, Transaction } from '@codemirror/state';
|
|
9
9
|
import { EditorView } from '@codemirror/view';
|
|
10
10
|
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Number of lines above/below the cursor to keep visible in the viewport.
|
|
15
|
-
* Defaults to 3.
|
|
16
|
-
*/
|
|
17
|
-
export const cursorLineMarginFacet = Facet.define<number, number>({
|
|
18
|
-
combine: (input) => input[0] ?? 3,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
13
|
const annotation = 'cursorLineMargin';
|
|
22
14
|
const lineAtPos = (s: EditorState, pos: number) => s.doc.lineAt(pos).number;
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
type Options = {
|
|
17
|
+
lines?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// TODO(burdon): Remove. Buggy and creates scrolling problems. Find better (more general) way to deal with HALO notch.
|
|
21
|
+
export const cursorLineMargin = (options: Options = { lines: 3 }): Extension => {
|
|
22
|
+
const { lines } = options;
|
|
23
|
+
if (!lines || lines <= 0) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return EditorView.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
|
|
28
28
|
// make sure we don't trigger an infinite loop and ignore our own changes
|
|
29
29
|
if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
|
|
30
30
|
return;
|
|
@@ -32,7 +32,6 @@ export const cursorLineMargin: Extension = EditorView.updateListener.of(
|
|
|
32
32
|
|
|
33
33
|
const s = state;
|
|
34
34
|
const { main } = s.selection;
|
|
35
|
-
const cursorLineMargin = s.facet(cursorLineMarginFacet);
|
|
36
35
|
|
|
37
36
|
// editor rect
|
|
38
37
|
const rect = view.dom.getBoundingClientRect();
|
|
@@ -59,7 +58,7 @@ export const cursorLineMargin: Extension = EditorView.updateListener.of(
|
|
|
59
58
|
|
|
60
59
|
// const needsScrollTop = mainLine <= visTopLine + cursorLineMargin;
|
|
61
60
|
const needsScrollTop = false;
|
|
62
|
-
const needsScrollBot = mainLine >= visBotLine -
|
|
61
|
+
const needsScrollBot = mainLine >= visBotLine - lines;
|
|
63
62
|
|
|
64
63
|
// the scroll margins are overlapping
|
|
65
64
|
if (needsScrollTop && needsScrollBot) {
|
|
@@ -88,8 +87,8 @@ export const cursorLineMargin: Extension = EditorView.updateListener.of(
|
|
|
88
87
|
annotations: Transaction.userEvent.of(annotation),
|
|
89
88
|
effects: EditorView.scrollIntoView(s.selection.main.head, {
|
|
90
89
|
y: needsScrollTop ? 'start' : 'end',
|
|
91
|
-
yMargin: view.defaultLineHeight *
|
|
90
|
+
yMargin: view.defaultLineHeight * lines,
|
|
92
91
|
}),
|
|
93
92
|
});
|
|
94
|
-
}
|
|
95
|
-
|
|
93
|
+
});
|
|
94
|
+
};
|
package/src/extensions/cursor.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Facet } from '@codemirror/state';
|
|
6
|
+
|
|
7
|
+
import { invariant } from '@dxos/invariant';
|
|
8
|
+
|
|
9
|
+
export const documentId = Facet.define<string, string>({
|
|
10
|
+
combine: (providers) => {
|
|
11
|
+
invariant(providers.length <= 1);
|
|
12
|
+
return providers[0];
|
|
13
|
+
},
|
|
14
|
+
});
|