@dxos/react-ui-editor 0.8.2-main.f081794 → 0.8.2-main.fbd8ed0

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 (131) hide show
  1. package/dist/lib/browser/index.mjs +1664 -1359
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs.map +2 -2
  5. package/dist/lib/node/index.cjs +2122 -1819
  6. package/dist/lib/node/index.cjs.map +4 -4
  7. package/dist/lib/node/meta.json +1 -1
  8. package/dist/lib/node/testing/index.cjs.map +2 -2
  9. package/dist/lib/node-esm/index.mjs +1664 -1359
  10. package/dist/lib/node-esm/index.mjs.map +4 -4
  11. package/dist/lib/node-esm/meta.json +1 -1
  12. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  13. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
  14. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  15. package/dist/types/src/{stories/InputMode.stories.d.ts → components/EditorToolbar/EditorToolbar.stories.d.ts} +3 -7
  16. package/dist/types/src/components/EditorToolbar/EditorToolbar.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -3
  18. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  19. package/dist/types/src/components/EditorToolbar/comment.d.ts +4 -3
  20. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +1 -1
  21. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -3
  22. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -3
  24. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  25. package/dist/types/src/components/EditorToolbar/image.d.ts +16 -0
  26. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -0
  27. package/dist/types/src/components/EditorToolbar/lists.d.ts +4 -3
  28. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  29. package/dist/types/src/components/EditorToolbar/search.d.ts +17 -0
  30. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -0
  31. package/dist/types/src/components/EditorToolbar/util.d.ts +11 -17
  32. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  33. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +4 -3
  34. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  35. package/dist/types/src/defaults.d.ts.map +1 -1
  36. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  37. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  38. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  39. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  40. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  41. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  42. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  43. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  44. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  45. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  46. package/dist/types/src/extensions/automerge/update-codemirror.d.ts +1 -1
  47. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  48. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  49. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  50. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  51. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  52. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  53. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  54. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  55. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  56. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  57. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  58. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  59. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  60. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  61. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  62. package/dist/types/src/extensions/markdown/decorate.d.ts +1 -0
  63. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  64. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  65. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  66. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  67. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  68. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  69. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  70. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  71. package/dist/types/src/extensions/markdown/outliner.d.ts +12 -0
  72. package/dist/types/src/extensions/markdown/outliner.d.ts.map +1 -0
  73. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  74. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  75. package/dist/types/src/extensions/modes.d.ts +5 -5
  76. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  77. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  78. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  79. package/dist/types/src/extensions/typewriter.d.ts.map +1 -1
  80. package/dist/types/src/hooks/index.d.ts +0 -1
  81. package/dist/types/src/hooks/index.d.ts.map +1 -1
  82. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  83. package/dist/types/src/stories/TextEditorBasic.stories.d.ts +3 -0
  84. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +1 -1
  85. package/dist/types/src/stories/story-utils.d.ts.map +1 -1
  86. package/dist/types/src/styles/theme.d.ts.map +1 -1
  87. package/dist/types/src/testing/RefPopover.d.ts.map +1 -1
  88. package/dist/types/src/util/cursor.d.ts.map +1 -1
  89. package/dist/types/src/util/debug.d.ts.map +1 -1
  90. package/dist/types/src/util/dom.d.ts.map +1 -1
  91. package/dist/types/src/util/facet.d.ts.map +1 -1
  92. package/dist/types/src/util/react.d.ts.map +1 -1
  93. package/dist/types/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +32 -28
  95. package/src/components/EditorToolbar/EditorToolbar.stories.tsx +90 -0
  96. package/src/components/EditorToolbar/EditorToolbar.tsx +31 -32
  97. package/src/components/EditorToolbar/blocks.ts +27 -6
  98. package/src/components/EditorToolbar/comment.ts +11 -4
  99. package/src/components/EditorToolbar/formatting.ts +34 -7
  100. package/src/components/EditorToolbar/headings.ts +9 -8
  101. package/src/components/EditorToolbar/image.ts +16 -0
  102. package/src/components/EditorToolbar/lists.ts +26 -7
  103. package/src/components/EditorToolbar/search.ts +19 -0
  104. package/src/components/EditorToolbar/util.ts +14 -14
  105. package/src/components/EditorToolbar/view-mode.ts +9 -8
  106. package/src/defaults.ts +1 -1
  107. package/src/extensions/automerge/automerge.stories.tsx +9 -7
  108. package/src/extensions/automerge/automerge.test.tsx +4 -4
  109. package/src/extensions/automerge/automerge.ts +2 -2
  110. package/src/extensions/automerge/defs.ts +1 -2
  111. package/src/extensions/automerge/sync.ts +4 -4
  112. package/src/extensions/automerge/update-automerge.ts +1 -1
  113. package/src/extensions/automerge/update-codemirror.ts +3 -4
  114. package/src/extensions/markdown/changes.ts +3 -2
  115. package/src/extensions/markdown/decorate.ts +8 -7
  116. package/src/extensions/markdown/formatting.ts +4 -4
  117. package/src/extensions/markdown/index.ts +1 -0
  118. package/src/extensions/markdown/outliner.ts +235 -0
  119. package/src/extensions/markdown/styles.ts +2 -2
  120. package/src/extensions/modes.ts +5 -6
  121. package/src/extensions/preview/preview.ts +1 -1
  122. package/src/hooks/index.ts +0 -1
  123. package/src/stories/TextEditorBasic.stories.tsx +44 -0
  124. package/src/stories/story-utils.tsx +7 -9
  125. package/src/styles/theme.ts +3 -0
  126. package/src/testing/RefPopover.tsx +4 -4
  127. package/dist/types/src/hooks/useActionHandler.d.ts +0 -4
  128. package/dist/types/src/hooks/useActionHandler.d.ts.map +0 -1
  129. package/dist/types/src/stories/InputMode.stories.d.ts.map +0 -1
  130. package/src/hooks/useActionHandler.ts +0 -12
  131. package/src/stories/InputMode.stories.tsx +0 -124
@@ -2,11 +2,13 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { type EditorView } from '@codemirror/view';
6
+
5
7
  import { type NodeArg } from '@dxos/app-graph';
6
8
  import { type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
7
9
 
8
10
  import { createEditorAction, createEditorActionGroup, type EditorToolbarState } from './util';
9
- import { type PayloadType } from '../../extensions';
11
+ import { addList, List, removeList } from '../../extensions';
10
12
 
11
13
  const listStyles = {
12
14
  bullet: 'ph--list-bullets--regular',
@@ -21,15 +23,32 @@ const createListGroupAction = (value: string) =>
21
23
  value,
22
24
  } as ToolbarMenuActionGroupProperties);
23
25
 
24
- const createListActions = (value: string) =>
25
- Object.entries(listStyles).map(([listStyle, icon]) =>
26
- createEditorAction({ type: `list-${listStyle}` as PayloadType, checked: value === listStyle }, icon),
27
- );
26
+ const createListActions = (value: string, getView: () => EditorView) =>
27
+ Object.entries(listStyles).map(([listStyle, icon]) => {
28
+ const checked = value === listStyle;
29
+ return createEditorAction(
30
+ `list-${listStyle}`,
31
+ () => {
32
+ const view = getView();
33
+ if (!view) {
34
+ return;
35
+ }
36
+
37
+ const listType = listStyle === 'ordered' ? List.Ordered : listStyle === 'bullet' ? List.Bullet : List.Task;
38
+ if (checked) {
39
+ removeList(listType)(view);
40
+ } else {
41
+ addList(listType)(view);
42
+ }
43
+ },
44
+ { checked, icon },
45
+ );
46
+ });
28
47
 
29
- export const createLists = (state: EditorToolbarState) => {
48
+ export const createLists = (state: EditorToolbarState, getView: () => EditorView) => {
30
49
  const value = state.listStyle ?? '';
31
50
  const listGroupAction = createListGroupAction(value);
32
- const listActionsMap = createListActions(value);
51
+ const listActionsMap = createListActions(value, getView);
33
52
  return {
34
53
  nodes: [listGroupAction as NodeArg<any>, ...listActionsMap],
35
54
  edges: [
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { openSearchPanel } from '@codemirror/search';
6
+ import { type EditorView } from '@codemirror/view';
7
+
8
+ import { createEditorAction } from './util';
9
+
10
+ const createSearchAction = (getView: () => EditorView) =>
11
+ createEditorAction('search', () => openSearchPanel(getView()), {
12
+ testId: 'editor.toolbar.search',
13
+ icon: 'ph--magnifying-glass--regular',
14
+ });
15
+
16
+ export const createSearch = (getView: () => EditorView) => ({
17
+ nodes: [createSearchAction(getView)],
18
+ edges: [{ source: 'root', target: 'search' }],
19
+ });
@@ -2,21 +2,23 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { type EditorView } from '@codemirror/view';
5
6
  import { useMemo } from 'react';
6
7
 
8
+ import { type Action } from '@dxos/app-graph';
7
9
  import { live, type Live } from '@dxos/live-object';
8
- import { type Label, type ThemedClassName } from '@dxos/react-ui';
10
+ import { type ThemedClassName } from '@dxos/react-ui';
9
11
  import {
10
12
  type MenuSeparator,
11
13
  type MenuItemGroup,
12
14
  type ToolbarMenuActionGroupProperties,
13
- type MenuActionProperties,
14
15
  createMenuAction,
15
16
  createMenuItemGroup,
16
17
  type ActionGraphProps,
18
+ type MenuActionProperties,
17
19
  } from '@dxos/react-ui-menu';
18
20
 
19
- import type { EditorAction, EditorActionPayload, EditorViewMode, Formatting } from '../../extensions';
21
+ import type { EditorAction, EditorViewMode, Formatting } from '../../extensions';
20
22
  import { translationKey } from '../../translations';
21
23
 
22
24
  export type EditorToolbarState = Formatting &
@@ -31,16 +33,18 @@ export type EditorToolbarFeatureFlags = Partial<{
31
33
  formatting: boolean;
32
34
  lists: boolean;
33
35
  blocks: boolean;
34
- comment: boolean;
35
36
  search: boolean;
36
- viewMode: boolean;
37
+ // TODO(wittjosiah): Factor out. Depend on plugin-level capabilities.
38
+ comment: boolean;
39
+ image: () => void;
40
+ viewMode: (mode: EditorViewMode) => void;
37
41
  }>;
38
42
 
39
43
  export type EditorToolbarActionGraphProps = {
40
44
  state: Live<EditorToolbarState>;
45
+ getView: () => EditorView;
41
46
  // TODO(wittjosiah): Control positioning.
42
47
  customActions?: () => ActionGraphProps;
43
- onAction: (action: EditorAction) => void;
44
48
  };
45
49
 
46
50
  export type EditorToolbarProps = ThemedClassName<
@@ -49,17 +53,13 @@ export type EditorToolbarProps = ThemedClassName<
49
53
 
50
54
  export type EditorToolbarItem = EditorAction | MenuItemGroup | MenuSeparator;
51
55
 
52
- export const createEditorAction = (
53
- payload: EditorActionPayload & Partial<MenuActionProperties>,
54
- icon: string,
55
- label: Label = [`${payload.type} label`, { ns: translationKey }],
56
- id: string = payload.type,
57
- ) => createMenuAction(id, { icon, label, ...payload }) as EditorAction;
56
+ export const createEditorAction = (id: string, invoke: () => void, properties: Partial<MenuActionProperties>) => {
57
+ const { label = [`${id} label`, { ns: translationKey }], ...rest } = properties;
58
+ return createMenuAction(id, invoke, { label, ...rest }) as Action<MenuActionProperties>;
59
+ };
58
60
 
59
61
  export const createEditorActionGroup = (
60
62
  id: string,
61
63
  props: Omit<ToolbarMenuActionGroupProperties, 'icon'>,
62
64
  icon?: string,
63
65
  ) => createMenuItemGroup(id, { icon, iconOnly: true, ...props });
64
-
65
- export const editorToolbarSearch = createEditorAction({ type: 'search' }, 'ph--magnifying-glass--regular');
@@ -6,6 +6,7 @@ import { type NodeArg } from '@dxos/app-graph';
6
6
  import { type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
7
7
 
8
8
  import { createEditorAction, createEditorActionGroup, type EditorToolbarState } from './util';
9
+ import { type EditorViewMode } from '../../extensions';
9
10
  import { translationKey } from '../../translations';
10
11
 
11
12
  const createViewModeGroupAction = (value: string) =>
@@ -20,24 +21,24 @@ const createViewModeGroupAction = (value: string) =>
20
21
  'ph--eye--regular',
21
22
  );
22
23
 
23
- const createViewModeActions = (value: string) =>
24
+ const createViewModeActions = (value: string, onViewModeChange: (mode: EditorViewMode) => void) =>
24
25
  Object.entries({
25
26
  preview: 'ph--eye--regular',
26
27
  source: 'ph--pencil-simple--regular',
27
28
  readonly: 'ph--pencil-slash--regular',
28
29
  }).map(([viewMode, icon]) => {
29
- return createEditorAction(
30
- { type: 'view-mode', data: viewMode, checked: viewMode === value },
30
+ const checked = viewMode === value;
31
+ return createEditorAction(`view-mode--${viewMode}`, () => onViewModeChange(viewMode as EditorViewMode), {
32
+ label: [`${viewMode} mode label`, { ns: translationKey }],
33
+ checked,
31
34
  icon,
32
- [`${viewMode} mode label`, { ns: translationKey }],
33
- `view-mode--${viewMode}`,
34
- );
35
+ });
35
36
  });
36
37
 
37
- export const createViewMode = (state: EditorToolbarState) => {
38
+ export const createViewMode = (state: EditorToolbarState, onViewModeChange: (mode: EditorViewMode) => void) => {
38
39
  const value = state.viewMode ?? 'source';
39
40
  const viewModeGroupAction = createViewModeGroupAction(value);
40
- const viewModeActions = createViewModeActions(value);
41
+ const viewModeActions = createViewModeActions(value, onViewModeChange);
41
42
  return {
42
43
  nodes: [viewModeGroupAction as NodeArg<any>, ...viewModeActions],
43
44
  edges: [
package/src/defaults.ts CHANGED
@@ -52,6 +52,6 @@ export const stackItemContentEditorClassNames = (role?: string) =>
52
52
 
53
53
  export const stackItemContentToolbarClassNames = (role?: string) =>
54
54
  mx(
55
- 'attention-surface is-full border-be !border-separator relative z-[1]',
55
+ 'relative z-[1] flex is-full bg-toolbarSurface border-be border-separator',
56
56
  role === 'section' && 'sticky block-start-0 -mbe-px min-is-0',
57
57
  );
@@ -5,12 +5,13 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import '@preact/signals-react';
8
+
9
+ import { Repo } from '@automerge/automerge-repo';
10
+ import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
8
11
  import React, { useEffect, useState } from 'react';
9
12
 
10
- import { Repo } from '@dxos/automerge/automerge-repo';
11
- import { BroadcastChannelNetworkAdapter } from '@dxos/automerge/automerge-repo-network-broadcastchannel';
12
13
  import { Expando } from '@dxos/echo-schema';
13
- import { DocAccessor, Filter, live, createDocAccessor, useQuery, useSpace, type Space } from '@dxos/react-client/echo';
14
+ import { DocAccessor, live, createDocAccessor, useQuery, useSpace, type Space, Query } from '@dxos/react-client/echo';
14
15
  import { useIdentity, type Identity } from '@dxos/react-client/halo';
15
16
  import { ClientRepeater, type ClientRepeatedComponentProps } from '@dxos/react-client/testing';
16
17
  import { useThemeContext } from '@dxos/react-ui';
@@ -71,11 +72,12 @@ const Story = () => {
71
72
  doc.text = initialContent;
72
73
  });
73
74
 
74
- const object2 = repo2.find<TestObject>(object1.url);
75
+ const object2 = await repo2.find<TestObject>(object1.url);
75
76
  await object2.whenReady();
76
77
 
77
- setObject1({ handle: object1, path: ['text'] });
78
- setObject2({ handle: object2, path: ['text'] });
78
+ // TODO(mykola): Fix types.
79
+ setObject1({ handle: object1 as any, path: ['text'] });
80
+ setObject2({ handle: object2 as any, path: ['text'] });
79
81
  });
80
82
  }, []);
81
83
 
@@ -103,7 +105,7 @@ const EchoStory = ({ spaceKey }: ClientRepeatedComponentProps) => {
103
105
  const identity = useIdentity();
104
106
  const space = useSpace(spaceKey);
105
107
  const [source, setSource] = useState<DocAccessor>();
106
- const objects = useQuery<Expando>(space, Filter.from({ type: 'test' }));
108
+ const objects = useQuery(space, Query.type(Expando, { type: 'test' }));
107
109
 
108
110
  useEffect(() => {
109
111
  if (!source && objects.length) {
@@ -2,6 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { type DocHandle, Repo } from '@automerge/automerge-repo';
5
6
  import { EditorState } from '@codemirror/state';
6
7
  import { EditorView } from '@codemirror/view';
7
8
  import { render, screen } from '@testing-library/react';
@@ -12,8 +13,6 @@ import get from 'lodash.get';
12
13
  import React, { type FC, useEffect, useRef, useState } from 'react';
13
14
  import { describe, test } from 'vitest';
14
15
 
15
- import { type DocHandle, Repo } from '@dxos/automerge/automerge-repo';
16
-
17
16
  import { automerge } from './automerge';
18
17
 
19
18
  type TestObject = {
@@ -36,7 +35,8 @@ const Test: FC<{ handle: DocHandle<TestObject>; generator: Generator }> = ({ han
36
35
  const [view, setView] = useState<EditorView>();
37
36
  useEffect(() => {
38
37
  const extensions = [
39
- automerge({ handle, path }),
38
+ // TODO(mykola): Fix types.
39
+ automerge({ handle: handle as any, path }),
40
40
  EditorView.updateListener.of((update) => {
41
41
  if (view.state.doc.toString() === 'hello!') {
42
42
  // Update editor.
@@ -46,7 +46,7 @@ const Test: FC<{ handle: DocHandle<TestObject>; generator: Generator }> = ({ han
46
46
  ];
47
47
 
48
48
  const view = new EditorView({
49
- state: EditorState.create({ doc: get(handle.docSync()!, path), extensions }),
49
+ state: EditorState.create({ doc: get(handle.doc()!, path), extensions }),
50
50
  parent: ref.current!,
51
51
  });
52
52
 
@@ -4,10 +4,10 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { next as A } from '@automerge/automerge';
7
8
  import { StateField, type Extension } from '@codemirror/state';
8
9
  import { EditorView, ViewPlugin } from '@codemirror/view';
9
10
 
10
- import { next as A } from '@dxos/automerge/automerge';
11
11
  import { type DocAccessor } from '@dxos/react-client/echo';
12
12
 
13
13
  import { cursorConverter } from './cursor';
@@ -19,7 +19,7 @@ export const automerge = (accessor: DocAccessor): Extension => {
19
19
  const syncState = StateField.define<State>({
20
20
  create: () => ({
21
21
  path: accessor.path.slice(),
22
- lastHeads: A.getHeads(accessor.handle.docSync()!),
22
+ lastHeads: A.getHeads(accessor.handle.doc()!),
23
23
  unreconciledTransactions: [],
24
24
  }),
25
25
 
@@ -4,10 +4,9 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { type Heads, type Prop } from '@automerge/automerge';
7
8
  import { Annotation, StateEffect, type StateField, type EditorState, type Transaction } from '@codemirror/state';
8
9
 
9
- import { type Heads, type Prop } from '@dxos/automerge/automerge';
10
-
11
10
  export type State = {
12
11
  path: Prop[];
13
12
  lastHeads: Heads;
@@ -4,10 +4,10 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { next as A } from '@automerge/automerge';
7
8
  import { type StateField } from '@codemirror/state';
8
9
  import { type EditorView } from '@codemirror/view';
9
10
 
10
- import { next as A } from '@dxos/automerge/automerge';
11
11
  import { type IDocHandle } from '@dxos/react-client/echo';
12
12
 
13
13
  import { getLastHeads, getPath, isReconcile, reconcileAnnotation, type State, updateHeads } from './defs';
@@ -57,15 +57,15 @@ export class Syncer {
57
57
  onAutomergeChange(view: EditorView) {
58
58
  // Get the diff between the updated state of the document and the heads and apply that to the codemirror doc.
59
59
  const oldHeads = getLastHeads(view.state, this._state);
60
- const newHeads = A.getHeads(this._handle.docSync()!);
61
- const diff = A.equals(oldHeads, newHeads) ? [] : A.diff(this._handle.docSync()!, oldHeads, newHeads);
60
+ const newHeads = A.getHeads(this._handle.doc()!);
61
+ const diff = A.equals(oldHeads, newHeads) ? [] : A.diff(this._handle.doc()!, oldHeads, newHeads);
62
62
 
63
63
  const selection = view.state.selection;
64
64
  const path = getPath(view.state, this._state);
65
65
  updateCodeMirror(view, selection, path, diff);
66
66
 
67
67
  // TODO(burdon): Test conflicts?
68
- // A.getConflicts(this._handle.docSync()!, path[0]);
68
+ // A.getConflicts(this._handle.doc()!, path[0]);
69
69
 
70
70
  view.dispatch({
71
71
  effects: updateHeads(newHeads),
@@ -4,9 +4,9 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
+ import { next as A, type Heads } from '@automerge/automerge';
7
8
  import { type EditorState, type StateField, type Transaction, type Text } from '@codemirror/state';
8
9
 
9
- import { next as A, type Heads } from '@dxos/automerge/automerge';
10
10
  import { type IDocHandle } from '@dxos/react-client/echo';
11
11
 
12
12
  import { type State } from './defs';
@@ -4,9 +4,6 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
- import { ChangeSet, type ChangeSpec, type EditorSelection, type EditorState } from '@codemirror/state';
8
- import { type EditorView } from '@codemirror/view';
9
-
10
7
  import {
11
8
  type DelPatch,
12
9
  type InsertPatch,
@@ -14,7 +11,9 @@ import {
14
11
  type Prop,
15
12
  type PutPatch,
16
13
  type SpliceTextPatch,
17
- } from '@dxos/automerge/automerge';
14
+ } from '@automerge/automerge';
15
+ import { ChangeSet, type ChangeSpec, type EditorSelection, type EditorState } from '@codemirror/state';
16
+ import { type EditorView } from '@codemirror/view';
18
17
 
19
18
  import { reconcileAnnotation } from './defs';
20
19
 
@@ -56,8 +56,9 @@ export const adjustChanges = () => {
56
56
  // Check for URL.
57
57
  const url = getValidUrl(update.view.state.sliceDoc(fromB, toB));
58
58
  if (url) {
59
+ // Check if pasting inside existing link.
59
60
  const node = tree.resolveInner(fromA, -1);
60
- const invalidPositions = new Set(['Link', 'LinkMark', 'Code', 'CodeText', 'FencedCode', 'URL']);
61
+ const invalidPositions = new Set(['Code', 'CodeText', 'FencedCode', 'Link', 'LinkMark', 'URL']);
61
62
  if (!invalidPositions.has(node?.name)) {
62
63
  const replacedText = tr.startState.sliceDoc(fromA, toA);
63
64
  adjustments.push({ from: fromA, to: toB, insert: createLink(url, replacedText) });
@@ -84,7 +85,7 @@ export const adjustChanges = () => {
84
85
  }
85
86
  }
86
87
 
87
- // TODO(burdon): Is this the right way to augment changes?
88
+ // TODO(burdon): Is this the right way to augment changes? Alt: EditorState.transactionFilter
88
89
  if (adjustments.length) {
89
90
  setTimeout(() => {
90
91
  update.view.dispatch(
@@ -129,10 +129,10 @@ class TextWidget extends WidgetType {
129
129
  }
130
130
 
131
131
  const hide = Decoration.replace({});
132
- const blockQuote = Decoration.line({ class: mx('cm-blockquote') });
133
- const fencedCodeLine = Decoration.line({ class: mx('cm-code cm-codeblock-line') });
134
- const fencedCodeLineFirst = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-first') });
135
- const fencedCodeLineLast = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-last') });
132
+ const blockQuote = Decoration.line({ class: 'cm-blockquote' });
133
+ const fencedCodeLine = Decoration.line({ class: 'cm-code cm-codeblock-line' });
134
+ const fencedCodeLineFirst = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-start') });
135
+ const fencedCodeLineLast = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-end') });
136
136
  const commentBlockLine = fencedCodeLine;
137
137
  const commentBlockLineFirst = fencedCodeLineFirst;
138
138
  const commentBlockLineLast = fencedCodeLineLast;
@@ -277,7 +277,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
277
277
  // Set indentation.
278
278
  const list = getCurrentListLevel();
279
279
  const width = list.type === 'OrderedList' ? orderedListIndentationWidth : bulletListIndentationWidth;
280
- const offset = ((list.level ?? 0) + 1) * width;
280
+ const offset = (options?.listPaddingLeft ?? 0) + ((list.level ?? 0) + 1) * width;
281
281
  if (node.from === line.to - 1) {
282
282
  // Abort if only the hyphen is typed.
283
283
  return false;
@@ -285,7 +285,6 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
285
285
 
286
286
  // Add line decoration for the continuation indent.
287
287
  // TODO(burdon): Bug if indentation is more than one indentation unit (e.g., 4 spaces) from the previous line.
288
-
289
288
  deco.add(
290
289
  line.from,
291
290
  line.from,
@@ -406,7 +405,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
406
405
  }
407
406
 
408
407
  const first = block.from <= node.from;
409
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
408
+ const last = block.to >= node.to && /```$/.test(state.doc.sliceString(block.from, block.to));
410
409
  deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
411
410
 
412
411
  const editing = editingRange(state, node, focus);
@@ -521,6 +520,8 @@ export interface DecorateOptions {
521
520
  selectionChangeDelay?: number;
522
521
  numberedHeadings?: { from: number; to?: number };
523
522
  renderLinkButton?: RenderCallback<{ url: string }>;
523
+ // TODO(burdon): Additional padding for each line.
524
+ listPaddingLeft?: number;
524
525
  }
525
526
 
526
527
  export const decorateMarkdown = (options: DecorateOptions = {}) => {
@@ -5,13 +5,13 @@
5
5
  import { snippet } from '@codemirror/autocomplete';
6
6
  import { syntaxTree } from '@codemirror/language';
7
7
  import {
8
- type Extension,
9
- type StateCommand,
10
- type EditorState,
11
8
  type ChangeSpec,
12
- type Text,
13
9
  EditorSelection,
10
+ type Extension,
11
+ type EditorState,
14
12
  type Line,
13
+ type StateCommand,
14
+ type Text,
15
15
  } from '@codemirror/state';
16
16
  import { EditorView, keymap } from '@codemirror/view';
17
17
  import { type SyntaxNodeRef, type SyntaxNode } from '@lezer/common';
@@ -10,4 +10,5 @@ export * from './formatting';
10
10
  export * from './highlight';
11
11
  export * from './image';
12
12
  export * from './link';
13
+ export * from './outliner';
13
14
  export * from './table';