@dxos/react-ui-editor 0.8.4-main.3f58842 → 0.8.4-main.406dc2a
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/{chunk-22UMM3QJ.mjs → chunk-HL3YF6WC.mjs} +2 -2
- package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +4239 -3098
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +71 -1
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-YXYQPV6R.mjs → chunk-YJZGD3LY.mjs} +2 -2
- package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +4239 -3098
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +71 -1
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/components/Editor/Editor.d.ts +24 -9
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +30 -0
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -0
- package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/util.d.ts +3 -3
- package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts +1 -1
- package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete/autocomplete.d.ts +26 -0
- package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/index.d.ts +5 -0
- package/dist/types/src/extensions/autocomplete/index.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/match.d.ts +13 -0
- package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/placeholder.d.ts +20 -0
- package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete/typeahead.d.ts +10 -0
- package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -0
- 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 +10 -19
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts +2 -2
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/autoscroll.d.ts +10 -0
- package/dist/types/src/extensions/autoscroll.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/dnd.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +8 -8
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/focus.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- 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/json.d.ts +1 -1
- package/dist/types/src/extensions/json.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +8 -2
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/changes.d.ts +1 -1
- package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts +9 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
- package/dist/types/src/extensions/modes.d.ts +1 -1
- package/dist/types/src/extensions/modes.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/menu.d.ts +8 -0
- package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner/outliner.d.ts +1 -1
- package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/tree.d.ts +2 -2
- package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
- package/dist/types/src/extensions/popover/PopoverMenuProvider.d.ts +36 -0
- package/dist/types/src/extensions/popover/PopoverMenuProvider.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/index.d.ts +8 -0
- package/dist/types/src/extensions/popover/index.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/menu-presets.d.ts +4 -0
- package/dist/types/src/extensions/popover/menu-presets.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/menu.d.ts +24 -0
- package/dist/types/src/extensions/popover/menu.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/modal.d.ts +7 -0
- package/dist/types/src/extensions/popover/modal.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/popover.d.ts +47 -0
- package/dist/types/src/extensions/popover/popover.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/usePopoverMenu.d.ts +34 -0
- package/dist/types/src/extensions/popover/usePopoverMenu.d.ts.map +1 -0
- package/dist/types/src/extensions/popover/util.d.ts +8 -0
- package/dist/types/src/extensions/popover/util.d.ts.map +1 -0
- package/dist/types/src/extensions/preview/preview.d.ts +2 -6
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
- package/dist/types/src/extensions/state.d.ts +2 -0
- package/dist/types/src/extensions/state.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/extended-markdown.d.ts +10 -0
- package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/extended-markdown.test.d.ts +2 -0
- package/dist/types/src/extensions/tags/extended-markdown.test.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/index.d.ts +4 -0
- package/dist/types/src/extensions/tags/index.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/streamer.d.ts +12 -0
- package/dist/types/src/extensions/tags/streamer.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts +72 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/xml-util.d.ts +10 -0
- package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +4 -8
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/CommandDialog.stories.d.ts +14 -0
- package/dist/types/src/stories/CommandDialog.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Comments.stories.d.ts +21 -10
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/EditorToolbar.stories.d.ts +39 -3
- package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Experimental.stories.d.ts +22 -13
- package/dist/types/src/stories/Experimental.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Markdown.stories.d.ts +32 -43
- package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Outliner.stories.d.ts +15 -21
- package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Popover.stories.d.ts +20 -0
- package/dist/types/src/stories/Popover.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Preview.stories.d.ts +21 -7
- package/dist/types/src/stories/Preview.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Tags.stories.d.ts +16 -0
- package/dist/types/src/stories/Tags.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TextEditor.stories.d.ts +37 -52
- package/dist/types/src/stories/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/stories/components/EditorStory.d.ts +6 -9
- package/dist/types/src/stories/components/EditorStory.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/testing/PreviewPopover.d.ts +20 -0
- package/dist/types/src/testing/PreviewPopover.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +1 -0
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -1
- package/dist/types/src/types/types.d.ts +2 -2
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/src/util/cursor.d.ts.map +1 -1
- package/dist/types/src/util/debug.d.ts +1 -1
- package/dist/types/src/util/debug.d.ts.map +1 -1
- package/dist/types/src/util/decorations.d.ts +4 -0
- package/dist/types/src/util/decorations.d.ts.map +1 -0
- package/dist/types/src/util/dom.d.ts +2 -12
- package/dist/types/src/util/dom.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/react.d.ts +1 -1
- package/dist/types/src/util/react.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +69 -61
- package/src/components/Editor/Editor.stories.tsx +72 -0
- package/src/components/Editor/Editor.tsx +58 -15
- package/src/components/EditorToolbar/EditorToolbar.tsx +41 -30
- package/src/components/EditorToolbar/blocks.ts +21 -24
- package/src/components/EditorToolbar/formatting.ts +22 -25
- package/src/components/EditorToolbar/headings.ts +10 -5
- package/src/components/EditorToolbar/image.ts +8 -4
- package/src/components/EditorToolbar/lists.ts +16 -19
- package/src/components/EditorToolbar/search.ts +8 -4
- package/src/components/EditorToolbar/util.ts +16 -5
- package/src/components/EditorToolbar/view-mode.ts +11 -6
- package/src/components/index.ts +0 -1
- package/src/defaults.ts +5 -2
- package/src/extensions/autocomplete/autocomplete.ts +220 -0
- package/src/extensions/autocomplete/index.ts +8 -0
- package/src/extensions/autocomplete/match.ts +46 -0
- package/src/extensions/{command → autocomplete}/placeholder.ts +22 -18
- package/src/extensions/{command → autocomplete}/typeahead.ts +8 -50
- package/src/extensions/automerge/automerge.stories.tsx +31 -24
- package/src/extensions/automerge/automerge.ts +31 -11
- package/src/extensions/automerge/defs.ts +1 -1
- package/src/extensions/automerge/sync.ts +8 -4
- package/src/extensions/automerge/update-automerge.ts +1 -1
- package/src/extensions/autoscroll.ts +157 -0
- package/src/extensions/awareness/awareness.ts +2 -2
- package/src/extensions/comments.ts +18 -13
- package/src/extensions/dnd.ts +1 -1
- package/src/extensions/factories.ts +48 -31
- package/src/extensions/focus.ts +5 -4
- package/src/extensions/folding.tsx +4 -6
- package/src/extensions/hashtag.tsx +2 -2
- package/src/extensions/index.ts +4 -1
- package/src/extensions/json.ts +1 -1
- package/src/extensions/markdown/action.ts +2 -1
- package/src/extensions/markdown/bundle.ts +29 -5
- package/src/extensions/markdown/changes.ts +1 -1
- package/src/extensions/markdown/decorate.ts +24 -14
- package/src/extensions/markdown/formatting.test.ts +6 -6
- package/src/extensions/markdown/formatting.ts +3 -3
- package/src/extensions/markdown/highlight.ts +1 -1
- package/src/extensions/markdown/image.ts +3 -4
- package/src/extensions/markdown/link.ts +3 -0
- package/src/extensions/markdown/table.ts +7 -1
- package/src/extensions/mention.ts +1 -1
- package/src/extensions/modes.ts +2 -2
- package/src/extensions/{command/floating-menu.ts → outliner/menu.ts} +16 -21
- package/src/extensions/outliner/outliner.test.ts +3 -2
- package/src/extensions/outliner/outliner.ts +7 -6
- package/src/extensions/outliner/selection.ts +1 -1
- package/src/extensions/outliner/tree.test.ts +2 -1
- package/src/extensions/outliner/tree.ts +2 -2
- package/src/extensions/popover/PopoverMenuProvider.tsx +221 -0
- package/src/extensions/popover/index.ts +12 -0
- package/src/extensions/popover/menu-presets.ts +124 -0
- package/src/extensions/popover/menu.ts +67 -0
- package/src/extensions/popover/modal.ts +24 -0
- package/src/extensions/popover/popover.ts +293 -0
- package/src/extensions/popover/usePopoverMenu.ts +173 -0
- package/src/extensions/popover/util.ts +29 -0
- package/src/extensions/preview/index.ts +1 -1
- package/src/extensions/preview/preview.ts +57 -62
- package/src/extensions/selection.ts +2 -2
- package/src/extensions/state.ts +7 -0
- package/src/extensions/tags/extended-markdown.test.ts +261 -0
- package/src/extensions/tags/extended-markdown.ts +78 -0
- package/src/extensions/tags/index.ts +7 -0
- package/src/extensions/tags/streamer.ts +243 -0
- package/src/extensions/tags/xml-tags.ts +393 -0
- package/src/extensions/tags/xml-util.ts +94 -0
- package/src/hooks/useTextEditor.ts +27 -39
- package/src/stories/CommandDialog.stories.tsx +78 -0
- package/src/stories/Comments.stories.tsx +14 -10
- package/src/stories/EditorToolbar.stories.tsx +13 -12
- package/src/stories/Experimental.stories.tsx +17 -13
- package/src/stories/Markdown.stories.tsx +25 -21
- package/src/stories/Outliner.stories.tsx +54 -35
- package/src/stories/Popover.stories.tsx +163 -0
- package/src/stories/Preview.stories.tsx +34 -33
- package/src/stories/Tags.stories.tsx +81 -0
- package/src/stories/TextEditor.stories.tsx +39 -58
- package/src/stories/components/EditorStory.tsx +16 -15
- package/src/styles/theme.ts +16 -12
- package/src/testing/PreviewPopover.tsx +80 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/util.ts +2 -0
- package/src/translations.ts +1 -1
- package/src/types/types.ts +1 -1
- package/src/util/cursor.ts +2 -1
- package/src/util/debug.ts +2 -2
- package/src/util/decorations.ts +21 -0
- package/src/util/dom.ts +5 -27
- package/src/util/index.ts +1 -0
- package/src/util/react.tsx +1 -1
- package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-YXYQPV6R.mjs.map +0 -7
- package/dist/types/src/components/Popover/CommandMenu.d.ts +0 -34
- package/dist/types/src/components/Popover/CommandMenu.d.ts.map +0 -1
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts +0 -21
- package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +0 -1
- package/dist/types/src/components/Popover/RefPopover.d.ts +0 -34
- package/dist/types/src/components/Popover/RefPopover.d.ts.map +0 -1
- package/dist/types/src/components/Popover/index.d.ts +0 -4
- package/dist/types/src/components/Popover/index.d.ts.map +0 -1
- package/dist/types/src/extensions/autocomplete.d.ts +0 -13
- package/dist/types/src/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/extensions/command/action.d.ts +0 -17
- package/dist/types/src/extensions/command/action.d.ts.map +0 -1
- package/dist/types/src/extensions/command/command-menu.d.ts +0 -20
- package/dist/types/src/extensions/command/command-menu.d.ts.map +0 -1
- package/dist/types/src/extensions/command/command.d.ts +0 -6
- package/dist/types/src/extensions/command/command.d.ts.map +0 -1
- package/dist/types/src/extensions/command/floating-menu.d.ts +0 -7
- package/dist/types/src/extensions/command/floating-menu.d.ts.map +0 -1
- package/dist/types/src/extensions/command/hint.d.ts +0 -24
- package/dist/types/src/extensions/command/hint.d.ts.map +0 -1
- package/dist/types/src/extensions/command/index.d.ts +0 -7
- package/dist/types/src/extensions/command/index.d.ts.map +0 -1
- package/dist/types/src/extensions/command/placeholder.d.ts +0 -10
- package/dist/types/src/extensions/command/placeholder.d.ts.map +0 -1
- package/dist/types/src/extensions/command/state.d.ts +0 -16
- package/dist/types/src/extensions/command/state.d.ts.map +0 -1
- package/dist/types/src/extensions/command/typeahead.d.ts +0 -22
- package/dist/types/src/extensions/command/typeahead.d.ts.map +0 -1
- package/dist/types/src/extensions/command/useCommandMenu.d.ts +0 -26
- package/dist/types/src/extensions/command/useCommandMenu.d.ts.map +0 -1
- package/dist/types/src/stories/Command.stories.d.ts +0 -7
- package/dist/types/src/stories/Command.stories.d.ts.map +0 -1
- package/dist/types/src/stories/CommandMenu.stories.d.ts +0 -13
- package/dist/types/src/stories/CommandMenu.stories.d.ts.map +0 -1
- package/src/components/Popover/CommandMenu.tsx +0 -279
- package/src/components/Popover/RefDropdownMenu.tsx +0 -85
- package/src/components/Popover/RefPopover.tsx +0 -99
- package/src/components/Popover/index.ts +0 -7
- package/src/extensions/autocomplete.ts +0 -69
- package/src/extensions/command/action.ts +0 -56
- package/src/extensions/command/command-menu.ts +0 -210
- package/src/extensions/command/command.ts +0 -34
- package/src/extensions/command/hint.ts +0 -102
- package/src/extensions/command/index.ts +0 -10
- package/src/extensions/command/state.ts +0 -89
- package/src/extensions/command/useCommandMenu.ts +0 -118
- package/src/stories/Command.stories.tsx +0 -97
- package/src/stories/CommandMenu.stories.tsx +0 -159
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import '@preact/signals-react';
|
|
8
6
|
|
|
9
7
|
import { Repo } from '@automerge/automerge-repo';
|
|
10
8
|
import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
|
|
9
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
11
10
|
import React, { useEffect, useState } from 'react';
|
|
12
11
|
|
|
13
12
|
import { Obj, Ref, Type } from '@dxos/echo';
|
|
14
|
-
import { DocAccessor,
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
13
|
+
import { DocAccessor, Query, type Space, createDocAccessor, useQuery, useSpace } from '@dxos/react-client/echo';
|
|
14
|
+
import { type Identity, useIdentity } from '@dxos/react-client/halo';
|
|
15
|
+
import { type ClientRepeatedComponentProps, ClientRepeater } from '@dxos/react-client/testing';
|
|
17
16
|
import { useThemeContext } from '@dxos/react-ui';
|
|
18
|
-
import {
|
|
17
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
18
|
+
import { render } from '@dxos/storybook-utils';
|
|
19
19
|
|
|
20
20
|
import { editorSlots } from '../../defaults';
|
|
21
21
|
import { useTextEditor } from '../../hooks';
|
|
@@ -41,7 +41,7 @@ const Editor = ({ source, autoFocus, space, identity }: EditorProps) => {
|
|
|
41
41
|
() => ({
|
|
42
42
|
initialValue: DocAccessor.getValue(source),
|
|
43
43
|
extensions: [
|
|
44
|
-
createBasicExtensions({ placeholder: 'Type here...' }),
|
|
44
|
+
createBasicExtensions({ placeholder: 'Type here...', search: true }),
|
|
45
45
|
createThemeExtensions({ themeMode, slots: editorSlots }),
|
|
46
46
|
createDataExtensions({ id: 'test', text: source, space, identity }),
|
|
47
47
|
],
|
|
@@ -53,7 +53,7 @@ const Editor = ({ source, autoFocus, space, identity }: EditorProps) => {
|
|
|
53
53
|
return <div ref={parentRef} className='flex w-full' />;
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
const
|
|
56
|
+
const DefaultStory = () => {
|
|
57
57
|
const [object1, setObject1] = useState<DocAccessor<TestObject>>();
|
|
58
58
|
const [object2, setObject2] = useState<DocAccessor<TestObject>>();
|
|
59
59
|
|
|
@@ -88,16 +88,6 @@ const Story = () => {
|
|
|
88
88
|
);
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
export default {
|
|
92
|
-
title: 'ui/react-ui-editor/Automerge',
|
|
93
|
-
component: Editor,
|
|
94
|
-
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
95
|
-
render: () => <Story />,
|
|
96
|
-
parameters: {
|
|
97
|
-
translations,
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
|
|
101
91
|
const EchoStory = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
102
92
|
const identity = useIdentity();
|
|
103
93
|
const space = useSpace(spaceKey);
|
|
@@ -105,8 +95,9 @@ const EchoStory = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
105
95
|
const objects = useQuery(space, Query.type(Type.Expando, { type: 'test' }));
|
|
106
96
|
|
|
107
97
|
useEffect(() => {
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
const content = objects[0]?.content.target;
|
|
99
|
+
if (!source && content) {
|
|
100
|
+
const source = createDocAccessor(content, ['content']);
|
|
110
101
|
setSource(source);
|
|
111
102
|
}
|
|
112
103
|
}, [objects, source]);
|
|
@@ -118,17 +109,33 @@ const EchoStory = ({ spaceKey }: ClientRepeatedComponentProps) => {
|
|
|
118
109
|
return <Editor source={source} space={space} identity={identity ?? undefined} />;
|
|
119
110
|
};
|
|
120
111
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
const meta = {
|
|
113
|
+
title: 'ui/react-ui-editor/Automerge',
|
|
114
|
+
component: Editor as any,
|
|
115
|
+
render: render(DefaultStory),
|
|
124
116
|
decorators: [withTheme],
|
|
117
|
+
parameters: {
|
|
118
|
+
layout: 'fullscreen',
|
|
119
|
+
translations,
|
|
120
|
+
},
|
|
121
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
122
|
+
|
|
123
|
+
export default meta;
|
|
124
|
+
|
|
125
|
+
type Story = StoryObj<typeof meta>;
|
|
126
|
+
|
|
127
|
+
export const Default: Story = {
|
|
128
|
+
args: {},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const WithEcho: Story = {
|
|
125
132
|
render: () => {
|
|
126
133
|
return (
|
|
127
134
|
<ClientRepeater
|
|
128
135
|
count={2}
|
|
129
136
|
component={EchoStory}
|
|
130
137
|
createSpace
|
|
131
|
-
|
|
138
|
+
onCreateSpace={async ({ space }) => {
|
|
132
139
|
space.db.add(
|
|
133
140
|
Obj.make(Type.Expando, {
|
|
134
141
|
type: 'test',
|
|
@@ -5,23 +5,27 @@
|
|
|
5
5
|
//
|
|
6
6
|
|
|
7
7
|
import { next as A } from '@automerge/automerge';
|
|
8
|
-
import { StateField,
|
|
8
|
+
import { type Extension, StateField, Transaction } from '@codemirror/state';
|
|
9
9
|
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { DocAccessor } from '@dxos/react-client/echo';
|
|
12
|
+
|
|
13
|
+
import { Cursor } from '../../util';
|
|
14
|
+
import { initialSync } from '../state';
|
|
12
15
|
|
|
13
16
|
import { cursorConverter } from './cursor';
|
|
14
|
-
import {
|
|
17
|
+
import { type State, isReconcile, updateHeadsEffect } from './defs';
|
|
15
18
|
import { Syncer } from './sync';
|
|
16
|
-
import { Cursor } from '../../util';
|
|
17
19
|
|
|
18
20
|
export const automerge = (accessor: DocAccessor): Extension => {
|
|
19
21
|
const syncState = StateField.define<State>({
|
|
20
|
-
create: () =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
create: () => {
|
|
23
|
+
return {
|
|
24
|
+
path: accessor.path.slice(),
|
|
25
|
+
lastHeads: A.getHeads(accessor.handle.doc()!),
|
|
26
|
+
unreconciledTransactions: [],
|
|
27
|
+
};
|
|
28
|
+
},
|
|
25
29
|
|
|
26
30
|
update: (value, tr) => {
|
|
27
31
|
const result: State = {
|
|
@@ -63,6 +67,18 @@ export const automerge = (accessor: DocAccessor): Extension => {
|
|
|
63
67
|
class {
|
|
64
68
|
constructor(private readonly _view: EditorView) {
|
|
65
69
|
accessor.handle.addListener('change', this._handleChange);
|
|
70
|
+
|
|
71
|
+
requestAnimationFrame(() => {
|
|
72
|
+
const value = DocAccessor.getValue<string>(accessor);
|
|
73
|
+
const current = this._view.state.doc.toString();
|
|
74
|
+
if (value !== current) {
|
|
75
|
+
// TODO(burdon): This attempts to set the initial state, but creates problems.
|
|
76
|
+
// this._view.dispatch({
|
|
77
|
+
// changes: { from: 0, to: this._view.state.doc.length, insert: value },
|
|
78
|
+
// annotations: initialSync,
|
|
79
|
+
// });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
destroy() {
|
|
@@ -76,9 +92,13 @@ export const automerge = (accessor: DocAccessor): Extension => {
|
|
|
76
92
|
),
|
|
77
93
|
|
|
78
94
|
// Reconcile local updates.
|
|
79
|
-
EditorView.updateListener.of(({ view, changes }) => {
|
|
95
|
+
EditorView.updateListener.of(({ view, changes, transactions }) => {
|
|
80
96
|
if (!changes.empty) {
|
|
81
|
-
|
|
97
|
+
// Only reconcile if it's not an initial sync (to avoid loops)
|
|
98
|
+
const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);
|
|
99
|
+
if (!isInitialSync) {
|
|
100
|
+
syncer.reconcile(view, true);
|
|
101
|
+
}
|
|
82
102
|
}
|
|
83
103
|
}),
|
|
84
104
|
];
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
|
|
7
7
|
import { type Heads, type Prop } from '@automerge/automerge';
|
|
8
|
-
import { Annotation, StateEffect, type StateField, type
|
|
8
|
+
import { Annotation, type EditorState, StateEffect, type StateField, type Transaction } from '@codemirror/state';
|
|
9
9
|
|
|
10
10
|
export type State = {
|
|
11
11
|
path: Prop[];
|
|
@@ -8,9 +8,10 @@ import { next as A } from '@automerge/automerge';
|
|
|
8
8
|
import { type StateField } from '@codemirror/state';
|
|
9
9
|
import { type EditorView } from '@codemirror/view';
|
|
10
10
|
|
|
11
|
+
import { log } from '@dxos/log';
|
|
11
12
|
import { type IDocHandle } from '@dxos/react-client/echo';
|
|
12
13
|
|
|
13
|
-
import { getLastHeads, getPath, isReconcile, reconcileAnnotation,
|
|
14
|
+
import { type State, getLastHeads, getPath, isReconcile, reconcileAnnotation, updateHeads } from './defs';
|
|
14
15
|
import { updateAutomerge } from './update-automerge';
|
|
15
16
|
import { updateCodeMirror } from './update-codemirror';
|
|
16
17
|
|
|
@@ -27,7 +28,6 @@ export class Syncer {
|
|
|
27
28
|
) {}
|
|
28
29
|
|
|
29
30
|
reconcile(view: EditorView, editor: boolean): void {
|
|
30
|
-
// TODO(burdon): Better way to do mutex?
|
|
31
31
|
if (this._pending) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
@@ -41,7 +41,9 @@ export class Syncer {
|
|
|
41
41
|
this._pending = false;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
onEditorChange(view: EditorView): void {
|
|
44
|
+
private onEditorChange(view: EditorView): void {
|
|
45
|
+
log('onEditorChange');
|
|
46
|
+
|
|
45
47
|
// Apply the unreconciled transactions to the document.
|
|
46
48
|
const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
|
|
47
49
|
const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
|
|
@@ -54,7 +56,9 @@ export class Syncer {
|
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
onAutomergeChange(view: EditorView): void {
|
|
59
|
+
private onAutomergeChange(view: EditorView): void {
|
|
60
|
+
log('onAutomergeChange');
|
|
61
|
+
|
|
58
62
|
// Get the diff between the updated state of the document and the heads and apply that to the codemirror doc.
|
|
59
63
|
const oldHeads = getLastHeads(view.state, this._state);
|
|
60
64
|
const newHeads = A.getHeads(this._handle.doc()!);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
|
|
7
7
|
import { next as A, type Heads } from '@automerge/automerge';
|
|
8
|
-
import { type EditorState, type StateField, type
|
|
8
|
+
import { type EditorState, type StateField, type Text, type Transaction } from '@codemirror/state';
|
|
9
9
|
|
|
10
10
|
import { type IDocHandle } from '@dxos/react-client/echo';
|
|
11
11
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { StateEffect } from '@codemirror/state';
|
|
6
|
+
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
import { Domino } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
const lineHeight = 24;
|
|
11
|
+
|
|
12
|
+
export const scrollToBottomEffect = StateEffect.define<any>();
|
|
13
|
+
|
|
14
|
+
export type AutoScrollOptions = {
|
|
15
|
+
overscroll?: number;
|
|
16
|
+
throttle?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
|
|
21
|
+
*/
|
|
22
|
+
// TODO(burdon): Reconcile with transcript-extension.
|
|
23
|
+
export const autoScroll = ({ overscroll = 4 * lineHeight, throttle = 2_000 }: Partial<AutoScrollOptions> = {}) => {
|
|
24
|
+
let isThrottled = false;
|
|
25
|
+
let isPinned = true;
|
|
26
|
+
let timeout: NodeJS.Timeout | undefined;
|
|
27
|
+
let buttonContainer: HTMLDivElement | undefined;
|
|
28
|
+
let lastScrollTop = 0;
|
|
29
|
+
let scrollCounter = 0;
|
|
30
|
+
|
|
31
|
+
const hideScrollbar = (view: EditorView) => {
|
|
32
|
+
view.scrollDOM.classList.add('cm-hide-scrollbar');
|
|
33
|
+
clearTimeout(timeout);
|
|
34
|
+
timeout = setTimeout(() => {
|
|
35
|
+
view.scrollDOM.classList.remove('cm-hide-scrollbar');
|
|
36
|
+
}, 1_000);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const scrollToBottom = (view: EditorView) => {
|
|
40
|
+
isPinned = true;
|
|
41
|
+
scrollCounter = 0;
|
|
42
|
+
buttonContainer?.classList.add('opacity-0');
|
|
43
|
+
requestAnimationFrame(() => {
|
|
44
|
+
hideScrollbar(view);
|
|
45
|
+
view.scrollDOM.scrollTo({
|
|
46
|
+
top: view.scrollDOM.scrollHeight,
|
|
47
|
+
behavior: 'smooth',
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return [
|
|
53
|
+
// Update listener for logging when scrolling is needed.
|
|
54
|
+
EditorView.updateListener.of((update) => {
|
|
55
|
+
// Listen for effects.
|
|
56
|
+
update.transactions.forEach((transaction) => {
|
|
57
|
+
for (const effect of transaction.effects) {
|
|
58
|
+
if (effect.is(scrollToBottomEffect)) {
|
|
59
|
+
scrollToBottom(update.view);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Maybe scroll if doc changed and pinned.
|
|
65
|
+
if (update.docChanged && isPinned && !isThrottled) {
|
|
66
|
+
const distanceFromBottom = calcDistance(update.view.scrollDOM);
|
|
67
|
+
if (distanceFromBottom >= overscroll) {
|
|
68
|
+
isThrottled = true;
|
|
69
|
+
requestAnimationFrame(() => {
|
|
70
|
+
scrollToBottom(update.view);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Reset throttle.
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
isThrottled = false;
|
|
76
|
+
scrollToBottom(update.view);
|
|
77
|
+
}, throttle);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}),
|
|
81
|
+
|
|
82
|
+
// Detect user scroll.
|
|
83
|
+
// NOTE: Multiple scroll events are triggered during programmatic smooth scrolling.
|
|
84
|
+
EditorView.domEventHandlers({
|
|
85
|
+
scroll: (event, view) => {
|
|
86
|
+
const scroller = view.scrollDOM;
|
|
87
|
+
// Suspect delta goes positive when rendering widgets, so count positive deltas.
|
|
88
|
+
// TODO(burdon): Detect user scroll directly (wheel, touch, keys, etc.)
|
|
89
|
+
if (lastScrollTop > scroller.scrollTop) {
|
|
90
|
+
scrollCounter++;
|
|
91
|
+
}
|
|
92
|
+
lastScrollTop = scroller.scrollTop;
|
|
93
|
+
const distanceFromBottom = calcDistance(scroller);
|
|
94
|
+
if (distanceFromBottom === 0) {
|
|
95
|
+
// Pin to bottom.
|
|
96
|
+
isPinned = true;
|
|
97
|
+
buttonContainer?.classList.add('opacity-0');
|
|
98
|
+
scrollCounter = 0;
|
|
99
|
+
} else if (scrollCounter > 3) {
|
|
100
|
+
// Break pin if user scrolls up.
|
|
101
|
+
isPinned = false;
|
|
102
|
+
buttonContainer?.classList.remove('opacity-0');
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
}),
|
|
106
|
+
|
|
107
|
+
// Scroll button.
|
|
108
|
+
ViewPlugin.fromClass(
|
|
109
|
+
class {
|
|
110
|
+
constructor(view: EditorView) {
|
|
111
|
+
const scroller = view.scrollDOM.parentElement;
|
|
112
|
+
buttonContainer = Domino.of('div')
|
|
113
|
+
.classNames(true && 'cm-scroll-button transition-opacity duration-300 opacity-0')
|
|
114
|
+
.children(
|
|
115
|
+
Domino.of('button')
|
|
116
|
+
.classNames('dx-button bg-accentSurface')
|
|
117
|
+
.data('density', 'fine')
|
|
118
|
+
.children(Domino.of<any>('dx-icon').attributes({ icon: 'ph--arrow-down--regular' }))
|
|
119
|
+
.on('click', () => {
|
|
120
|
+
scrollToBottom(view);
|
|
121
|
+
}),
|
|
122
|
+
)
|
|
123
|
+
.build();
|
|
124
|
+
scroller?.appendChild(buttonContainer);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
|
|
129
|
+
// Styles.
|
|
130
|
+
EditorView.theme({
|
|
131
|
+
'.cm-scroller': {
|
|
132
|
+
paddingBottom: `${overscroll}px`,
|
|
133
|
+
scrollbarWidth: 'thin',
|
|
134
|
+
},
|
|
135
|
+
'.cm-scroller.cm-hide-scrollbar': {
|
|
136
|
+
scrollbarWidth: 'none',
|
|
137
|
+
},
|
|
138
|
+
'.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {
|
|
139
|
+
display: 'none',
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
'.cm-scroll-button': {
|
|
143
|
+
position: 'absolute',
|
|
144
|
+
bottom: '0.5rem',
|
|
145
|
+
right: '1rem',
|
|
146
|
+
},
|
|
147
|
+
}),
|
|
148
|
+
];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const calcDistance = (scroller: HTMLElement) => {
|
|
152
|
+
const scrollTop = scroller.scrollTop;
|
|
153
|
+
const scrollHeight = scroller.scrollHeight;
|
|
154
|
+
const clientHeight = scroller.clientHeight;
|
|
155
|
+
const distanceFromBottom = scrollHeight - (scrollTop + clientHeight);
|
|
156
|
+
return distanceFromBottom;
|
|
157
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Annotation, type Extension,
|
|
5
|
+
import { Annotation, type Extension, type Range, RangeSet } from '@codemirror/state';
|
|
6
6
|
import {
|
|
7
7
|
Decoration,
|
|
8
8
|
type DecorationSet,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { Event } from '@dxos/async';
|
|
17
17
|
import { Context } from '@dxos/context';
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { Cursor, type CursorConverter, singleValueFacet } from '../../util';
|
|
20
20
|
|
|
21
21
|
export interface AwarenessProvider {
|
|
22
22
|
remoteStateChange: Event<void>;
|
|
@@ -5,25 +5,26 @@
|
|
|
5
5
|
import { invertedEffects } from '@codemirror/commands';
|
|
6
6
|
import { type ChangeDesc, type Extension, StateEffect, StateField, type Text } from '@codemirror/state';
|
|
7
7
|
import {
|
|
8
|
-
hoverTooltip,
|
|
9
|
-
keymap,
|
|
10
8
|
type Command,
|
|
11
9
|
Decoration,
|
|
12
10
|
EditorView,
|
|
13
|
-
type Rect,
|
|
14
11
|
type PluginValue,
|
|
12
|
+
type Rect,
|
|
15
13
|
ViewPlugin,
|
|
14
|
+
hoverTooltip,
|
|
15
|
+
keymap,
|
|
16
16
|
} from '@codemirror/view';
|
|
17
17
|
import sortBy from 'lodash.sortby';
|
|
18
18
|
import { useEffect } from 'react';
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { type CleanupFn, debounce } from '@dxos/async';
|
|
21
21
|
import { log } from '@dxos/log';
|
|
22
22
|
import { isNonNullable } from '@dxos/util';
|
|
23
23
|
|
|
24
|
+
import { type Comment, type Range, type RenderCallback } from '../types';
|
|
25
|
+
import { Cursor, callbackWrapper, singleValueFacet } from '../util';
|
|
26
|
+
|
|
24
27
|
import { documentId } from './selection';
|
|
25
|
-
import { type RenderCallback, type Comment, type Range } from '../types';
|
|
26
|
-
import { Cursor, singleValueFacet, callbackWrapper } from '../util';
|
|
27
28
|
|
|
28
29
|
//
|
|
29
30
|
// State management.
|
|
@@ -57,7 +58,11 @@ const setCommentState = StateEffect.define<CommentsState>();
|
|
|
57
58
|
* The ranges are tracked as Automerge cursors from which the absolute indexed ranges can be computed.
|
|
58
59
|
*/
|
|
59
60
|
export const commentsState = StateField.define<CommentsState>({
|
|
60
|
-
create: (state) => ({
|
|
61
|
+
create: (state) => ({
|
|
62
|
+
id: state.facet(documentId),
|
|
63
|
+
comments: [],
|
|
64
|
+
selection: {},
|
|
65
|
+
}),
|
|
61
66
|
update: (value, tr) => {
|
|
62
67
|
for (const effect of tr.effects) {
|
|
63
68
|
// Update selection.
|
|
@@ -98,16 +103,16 @@ export const commentsState = StateField.define<CommentsState>({
|
|
|
98
103
|
*/
|
|
99
104
|
const styles = EditorView.theme({
|
|
100
105
|
'.cm-comment, .cm-comment-current': {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
padding: '3px 0',
|
|
107
|
+
backgroundColor: 'var(--dx-cmCommentSurface)',
|
|
108
|
+
},
|
|
109
|
+
'.cm-comment > span, .cm-comment-current > span': {
|
|
110
|
+
boxDecorationBreak: 'clone',
|
|
111
|
+
boxShadow: '0 0 1px 3px var(--dx-cmCommentSurface)',
|
|
104
112
|
backgroundColor: 'var(--dx-cmCommentSurface)',
|
|
105
113
|
color: 'var(--dx-cmComment)',
|
|
106
114
|
cursor: 'pointer',
|
|
107
115
|
},
|
|
108
|
-
'.cm-comment:hover, .cm-comment-current': {
|
|
109
|
-
textDecoration: 'underline',
|
|
110
|
-
},
|
|
111
116
|
});
|
|
112
117
|
|
|
113
118
|
const createCommentMark = (id: string, isCurrent: boolean) =>
|
package/src/extensions/dnd.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import type { Extension } from '@codemirror/state';
|
|
6
|
-
import {
|
|
6
|
+
import { EditorView, dropCursor } from '@codemirror/view';
|
|
7
7
|
|
|
8
8
|
export type DNDOptions = { onDrop?: (view: EditorView, event: { files: FileList }) => void };
|
|
9
9
|
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
6
6
|
import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';
|
|
7
|
-
import {
|
|
7
|
+
import { HighlightStyle, bracketMatching, syntaxHighlighting } from '@codemirror/language';
|
|
8
8
|
import { searchKeymap } from '@codemirror/search';
|
|
9
|
-
import { EditorState, type Extension } from '@codemirror/state';
|
|
10
|
-
import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
|
|
9
|
+
import { type ChangeSpec, EditorState, type Extension, type TransactionSpec } from '@codemirror/state';
|
|
11
10
|
import {
|
|
12
11
|
EditorView,
|
|
13
12
|
type KeyBinding,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
placeholder,
|
|
21
20
|
scrollPastEnd,
|
|
22
21
|
} from '@codemirror/view';
|
|
22
|
+
import { vscodeDarkStyle, vscodeLightStyle } from '@uiw/codemirror-theme-vscode';
|
|
23
23
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
24
24
|
import merge from 'lodash.merge';
|
|
25
25
|
|
|
@@ -29,24 +29,48 @@ import { type DocAccessor, type Space } from '@dxos/react-client/echo';
|
|
|
29
29
|
import { type Identity } from '@dxos/react-client/halo';
|
|
30
30
|
import { type ThemeMode } from '@dxos/react-ui';
|
|
31
31
|
import { type HuePalette } from '@dxos/react-ui-theme';
|
|
32
|
-
import { hexToHue,
|
|
32
|
+
import { hexToHue, isTruthy } from '@dxos/util';
|
|
33
|
+
|
|
34
|
+
import { editorGutter, editorMonospace } from '../defaults';
|
|
35
|
+
import { type ThemeStyles, defaultTheme } from '../styles';
|
|
33
36
|
|
|
34
37
|
import { automerge } from './automerge';
|
|
35
38
|
import { SpaceAwarenessProvider, awareness } from './awareness';
|
|
36
39
|
import { focus } from './focus';
|
|
37
|
-
import { editorGutter, editorMonospace } from '../defaults';
|
|
38
|
-
import { type ThemeStyles, defaultTheme } from '../styles';
|
|
39
40
|
|
|
40
41
|
//
|
|
41
42
|
// Basic
|
|
42
43
|
//
|
|
43
44
|
|
|
44
|
-
export const
|
|
45
|
+
export const filterChars = (chars: RegExp) => {
|
|
46
|
+
return EditorState.transactionFilter.of((transaction) => {
|
|
47
|
+
if (!transaction.docChanged) return transaction;
|
|
48
|
+
|
|
49
|
+
const changes: ChangeSpec[] = [];
|
|
50
|
+
transaction.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
51
|
+
const inserted = text.toString();
|
|
52
|
+
const filtered = inserted.replace(chars, '');
|
|
53
|
+
if (inserted !== filtered) {
|
|
54
|
+
changes.push({
|
|
55
|
+
from: fromB,
|
|
56
|
+
to: toB,
|
|
57
|
+
insert: filtered,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (changes.length) {
|
|
63
|
+
return [transaction, { changes, sequential: true } as TransactionSpec];
|
|
64
|
+
}
|
|
65
|
+
return transaction;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
45
68
|
|
|
46
69
|
/**
|
|
47
70
|
* https://codemirror.net/docs/extensions
|
|
48
71
|
* https://github.com/codemirror/basic-setup
|
|
49
72
|
* https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
|
|
73
|
+
* https://github.com/codemirror/theme-one-dark
|
|
50
74
|
*/
|
|
51
75
|
export type BasicExtensionsOptions = {
|
|
52
76
|
allowMultipleSelections?: boolean;
|
|
@@ -68,6 +92,7 @@ export type BasicExtensionsOptions = {
|
|
|
68
92
|
/** If true user cannot edit the text, but they can still select and copy it. */
|
|
69
93
|
readOnly?: boolean;
|
|
70
94
|
search?: boolean;
|
|
95
|
+
/** NOTE: Do not use with stack sections. */
|
|
71
96
|
scrollPastEnd?: boolean;
|
|
72
97
|
standardKeymap?: boolean;
|
|
73
98
|
tabSize?: number;
|
|
@@ -82,7 +107,7 @@ const defaultBasicOptions: BasicExtensionsOptions = {
|
|
|
82
107
|
history: true,
|
|
83
108
|
keymap: 'standard',
|
|
84
109
|
lineWrapping: true,
|
|
85
|
-
search:
|
|
110
|
+
search: false,
|
|
86
111
|
} as const;
|
|
87
112
|
|
|
88
113
|
const keymaps: { [key: string]: readonly KeyBinding[] } = {
|
|
@@ -137,9 +162,9 @@ export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extensio
|
|
|
137
162
|
preventDefault: true,
|
|
138
163
|
run: () => true,
|
|
139
164
|
},
|
|
140
|
-
].filter(
|
|
165
|
+
].filter(isTruthy),
|
|
141
166
|
),
|
|
142
|
-
].filter(
|
|
167
|
+
].filter(isTruthy);
|
|
143
168
|
};
|
|
144
169
|
|
|
145
170
|
//
|
|
@@ -157,9 +182,6 @@ export type ThemeExtensionsOptions = {
|
|
|
157
182
|
scroll?: {
|
|
158
183
|
className?: string;
|
|
159
184
|
};
|
|
160
|
-
scroller?: {
|
|
161
|
-
className?: string;
|
|
162
|
-
};
|
|
163
185
|
content?: {
|
|
164
186
|
className?: string;
|
|
165
187
|
};
|
|
@@ -180,41 +202,37 @@ export const fullWidth: ThemeExtensionsOptions['slots'] = {
|
|
|
180
202
|
|
|
181
203
|
export const defaultThemeSlots = grow;
|
|
182
204
|
|
|
205
|
+
export const defaultStyles = {
|
|
206
|
+
dark: vscodeDarkStyle,
|
|
207
|
+
light: vscodeLightStyle,
|
|
208
|
+
};
|
|
209
|
+
|
|
183
210
|
/**
|
|
184
211
|
* https://codemirror.net/examples/styling
|
|
185
212
|
*/
|
|
186
213
|
export const createThemeExtensions = ({
|
|
187
214
|
themeMode,
|
|
188
215
|
styles,
|
|
189
|
-
syntaxHighlighting:
|
|
190
|
-
slots:
|
|
216
|
+
syntaxHighlighting: syntaxHighlightingProp,
|
|
217
|
+
slots: slotsParam,
|
|
191
218
|
}: ThemeExtensionsOptions = {}): Extension => {
|
|
192
|
-
const slots = defaultsDeep({},
|
|
219
|
+
const slots = defaultsDeep({}, slotsParam, defaultThemeSlots);
|
|
193
220
|
return [
|
|
194
221
|
EditorView.darkTheme.of(themeMode === 'dark'),
|
|
195
222
|
EditorView.baseTheme(styles ? merge({}, defaultTheme, styles) : defaultTheme),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
(themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)),
|
|
223
|
+
syntaxHighlightingProp &&
|
|
224
|
+
syntaxHighlighting(HighlightStyle.define(themeMode === 'dark' ? defaultStyles.dark : defaultStyles.light)),
|
|
199
225
|
slots.editor?.className && EditorView.editorAttributes.of({ class: slots.editor.className }),
|
|
200
226
|
slots.content?.className && EditorView.contentAttributes.of({ class: slots.content.className }),
|
|
201
227
|
slots.scroll?.className &&
|
|
202
228
|
ViewPlugin.fromClass(
|
|
203
229
|
class {
|
|
204
230
|
constructor(view: EditorView) {
|
|
205
|
-
view.scrollDOM.classList.add(slots.scroll.className);
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
),
|
|
209
|
-
slots.scroller?.className &&
|
|
210
|
-
ViewPlugin.fromClass(
|
|
211
|
-
class {
|
|
212
|
-
constructor(view: EditorView) {
|
|
213
|
-
view.dom.querySelector('.cm-scroller')?.classList.add(...slots.scroller.className.split(' '));
|
|
231
|
+
view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
|
|
214
232
|
}
|
|
215
233
|
},
|
|
216
234
|
),
|
|
217
|
-
].filter(
|
|
235
|
+
].filter(isTruthy);
|
|
218
236
|
};
|
|
219
237
|
|
|
220
238
|
//
|
|
@@ -238,7 +256,6 @@ export const createDataExtensions = <T>({ id, text, space, identity }: DataExten
|
|
|
238
256
|
if (space && identity) {
|
|
239
257
|
const peerId = identity?.identityKey.toHex();
|
|
240
258
|
const hue = (identity?.profile?.data?.hue as HuePalette | undefined) ?? hexToHue(peerId ?? '0');
|
|
241
|
-
|
|
242
259
|
extensions.push(
|
|
243
260
|
awareness(
|
|
244
261
|
new SpaceAwarenessProvider({
|
|
@@ -246,9 +263,9 @@ export const createDataExtensions = <T>({ id, text, space, identity }: DataExten
|
|
|
246
263
|
channel: `awareness.${id}`,
|
|
247
264
|
peerId: identity.identityKey.toHex(),
|
|
248
265
|
info: {
|
|
249
|
-
displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
|
250
266
|
darkColor: `var(--dx-${hue}Cursor)`,
|
|
251
267
|
lightColor: `var(--dx-${hue}Cursor)`,
|
|
268
|
+
displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
|
252
269
|
},
|
|
253
270
|
}),
|
|
254
271
|
),
|