@dxos/react-ui-editor 0.3.10-main.3645708 → 0.3.10-main.396fa89

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 (118) hide show
  1. package/dist/lib/browser/index.mjs +1495 -489
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +26 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +3 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +3 -0
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts +3 -2
  11. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +1 -1
  12. package/dist/types/src/components/TextEditor/extensions/basic.d.ts +2 -1
  13. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -1
  14. package/dist/types/src/components/TextEditor/extensions/blast.d.ts +25 -0
  15. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +1 -0
  16. package/dist/types/src/components/TextEditor/extensions/code.d.ts +2 -0
  17. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +1 -0
  18. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +18 -0
  19. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -0
  20. package/dist/types/src/components/TextEditor/extensions/demo.d.ts +11 -0
  21. package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -0
  22. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts +1 -1
  23. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +1 -1
  24. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +24 -0
  25. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +1 -0
  26. package/dist/types/src/components/TextEditor/extensions/image.d.ts +4 -0
  27. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +1 -0
  28. package/dist/types/src/components/TextEditor/extensions/index.d.ts +8 -0
  29. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  30. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -1
  31. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +4 -2
  32. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -1
  33. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +10 -1
  34. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
  35. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +9 -3
  36. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -1
  37. package/dist/types/src/components/TextEditor/extensions/mention.d.ts +6 -0
  38. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +1 -0
  39. package/dist/types/src/components/TextEditor/extensions/table.d.ts +8 -0
  40. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -0
  41. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +2 -4
  42. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -1
  43. package/dist/types/src/components/TextEditor/themes/default.d.ts +8 -0
  44. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -1
  45. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.d.ts +3 -0
  46. package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +1 -0
  47. package/dist/types/src/hooks/automerge/automerge.stories.d.ts +16 -0
  48. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -0
  49. package/dist/types/src/hooks/automerge/handle.d.ts.map +1 -0
  50. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/index.d.ts +1 -1
  51. package/dist/types/src/hooks/automerge/index.d.ts.map +1 -0
  52. package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -0
  53. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +5 -0
  54. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +1 -0
  55. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +6 -0
  56. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +1 -0
  57. package/dist/types/src/hooks/automerge/translation/index.d.ts +3 -0
  58. package/dist/types/src/hooks/automerge/translation/index.d.ts.map +1 -0
  59. package/dist/types/src/hooks/index.d.ts +0 -1
  60. package/dist/types/src/hooks/index.d.ts.map +1 -1
  61. package/dist/types/src/hooks/useTextModel.d.ts +8 -3
  62. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  63. package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +2 -2
  64. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
  65. package/dist/types/src/styles/markdown.d.ts +3 -2
  66. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  67. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  68. package/package.json +22 -19
  69. package/src/components/TextEditor/MarkdownEditor.stories.tsx +161 -36
  70. package/src/components/TextEditor/TextEditor.stories.tsx +12 -1
  71. package/src/components/TextEditor/TextEditor.tsx +49 -24
  72. package/src/components/TextEditor/extensions/autocomplete.ts +9 -22
  73. package/src/components/TextEditor/extensions/basic.ts +6 -4
  74. package/src/components/TextEditor/extensions/blast.ts +373 -0
  75. package/src/components/TextEditor/extensions/code.ts +99 -0
  76. package/src/components/TextEditor/extensions/comments.ts +173 -0
  77. package/src/components/TextEditor/extensions/demo.ts +69 -0
  78. package/src/components/TextEditor/extensions/experimental.tsx +15 -35
  79. package/src/components/TextEditor/extensions/highlight.ts +128 -0
  80. package/src/components/TextEditor/extensions/image.ts +67 -0
  81. package/src/components/TextEditor/extensions/index.ts +8 -0
  82. package/src/components/TextEditor/extensions/link.ts +27 -27
  83. package/src/components/TextEditor/extensions/listener.ts +4 -3
  84. package/src/components/TextEditor/extensions/markdown/bundle.ts +46 -43
  85. package/src/components/TextEditor/extensions/markdown/highlight.ts +148 -107
  86. package/src/components/TextEditor/extensions/mention.ts +31 -0
  87. package/src/components/TextEditor/extensions/table.ts +119 -0
  88. package/src/components/TextEditor/extensions/tasklist.ts +89 -81
  89. package/src/components/TextEditor/themes/default.ts +112 -4
  90. package/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.ts +12 -9
  91. package/src/{automerge → hooks/automerge}/automerge.stories.tsx +6 -3
  92. package/src/{automerge/automerge-plugin → hooks/automerge}/plugin.ts +1 -0
  93. package/src/{automerge/automerge-plugin/amToCodemirror.ts → hooks/automerge/translation/automerge-to-codemirror.ts} +10 -2
  94. package/src/{automerge/automerge-plugin/codeMirrorToAm.ts → hooks/automerge/translation/codemirror-to-automerge.ts} +7 -4
  95. package/src/hooks/automerge/translation/index.ts +6 -0
  96. package/src/hooks/index.ts +0 -1
  97. package/src/hooks/useTextModel.ts +58 -41
  98. package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +2 -2
  99. package/src/styles/markdown.ts +15 -8
  100. package/src/testing/replicator.ts +7 -7
  101. package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +0 -1
  102. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts +0 -6
  103. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts.map +0 -1
  104. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts +0 -7
  105. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +0 -1
  106. package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +0 -1
  107. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +0 -1
  108. package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +0 -1
  109. package/dist/types/src/automerge/automerge.stories.d.ts +0 -9
  110. package/dist/types/src/automerge/automerge.stories.d.ts.map +0 -1
  111. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  112. package/dist/types/src/hooks/useCollaboration.d.ts +0 -9
  113. package/dist/types/src/hooks/useCollaboration.d.ts.map +0 -1
  114. package/src/hooks/useCollaboration.ts +0 -45
  115. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/handle.d.ts +0 -0
  116. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/plugin.d.ts +0 -0
  117. package/src/{automerge/automerge-plugin → hooks/automerge}/handle.ts +0 -0
  118. package/src/{automerge/automerge-plugin → hooks/automerge}/index.ts +1 -1
@@ -18,13 +18,22 @@ import { Decoration, EditorView, WidgetType } from '@codemirror/view';
18
18
  // https://codemirror.net/try/?c=aW1wb3J0IHsgYmFzaWNTZXR1cCwgRWRpdG9yVmlldyB9IGZyb20gImNvZGVtaXJyb3IiCmltcG9ydCB7IG1hcmtkb3duIH0gZnJvbSAiQGNvZGVtaXJyb3IvbGFuZy1tYXJrZG93biIKaW1wb3J0IHsgc3ludGF4VHJlZSB9IGZyb20gIkBjb2RlbWlycm9yL2xhbmd1YWdlIjsKaW1wb3J0IHsgRWRpdG9yU3RhdGUsIFJhbmdlU2V0QnVpbGRlciwgU3RhdGVGaWVsZCB9IGZyb20gIkBjb2RlbWlycm9yL3N0YXRlIjsKaW1wb3J0IHsgRGVjb3JhdGlvbiwgV2lkZ2V0VHlwZSB9IGZyb20gIkBjb2RlbWlycm9yL3ZpZXciOwoKY2xhc3MgTGluayBleHRlbmRzIFdpZGdldFR5cGUgewogIGNvbnN0cnVjdG9yKHRleHQsIHVybCkgewogICAgc3VwZXIoKQogICAgdGhpcy50ZXh0ID0gdGV4dAogICAgdGhpcy51cmwgPSB1cmwKICB9CiAgZXEob3RoZXIpIHsgcmV0dXJuIHRoaXMudGV4dCA9PSBvdGhlci50ZXh0ICYmIHRoaXMudXJsID09IG90aGVyLnVybCB9CiAgdG9ET00oKSB7CiAgICBsZXQgbGluayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImEiKQogICAgbGluay50ZXh0Q29udGVudCA9IHRoaXMudGV4dAogICAgbGluay5ocmVmID0gdGhpcy51cmwKICAgIHJldHVybiBsaW5rOwogIH0KfQoKbGV0IGRlY29yYXRpb25zRmllbGQgPSBTdGF0ZUZpZWxkLmRlZmluZSh7CiAgY3JlYXRlKCkgewogICAgcmV0dXJuIERlY29yYXRpb24ubm9uZTsKICB9LAogIHVwZGF0ZShfLCB0cikgewogICAgY29uc3QgYnVpbGRlciA9IG5ldyBSYW5nZVNldEJ1aWxkZXIoKTsKICAgIGxldCBjdXJzb3IgPSB0ci5zdGF0ZS5zZWxlY3Rpb24ubWFpbi5oZWFkOwogICAgc3ludGF4VHJlZSh0ci5zdGF0ZSkuaXRlcmF0ZSh7CiAgICAgIGVudGVyOiAobm9kZSkgPT4gewogICAgICAgIGlmICgoY3Vyc29yIDwgbm9kZS5mcm9tIHx8IGN1cnNvciA+IG5vZGUudG8pICYmIG5vZGUubmFtZSA9PSAiTGluayIpIHsKICAgICAgICAgIGxldCBtYXJrcyA9IG5vZGUubm9kZS5nZXRDaGlsZHJlbigiTGlua01hcmsiKTsKICAgICAgICAgIGxldCB0ZXh0ID0gbWFya3MubGVuZ3RoID49IDIgPyB0ci5zdGF0ZS5zbGljZURvYyhtYXJrc1swXS50bywgbWFya3NbMV0uZnJvbSkgOiAiIgoKICAgICAgICAgIGxldCB1cmxOb2RlID0gbm9kZS5ub2RlLmdldENoaWxkKCJVUkwiKTsKICAgICAgICAgIGxldCB1cmwgPSB1cmxOb2RlID8gdHIuc3RhdGUuc2xpY2VEb2ModXJsTm9kZS5mcm9tLCB1cmxOb2RlLnRvKSA6ICIiCiAgICAgICAgICBidWlsZGVyLmFkZChub2RlLmZyb20sIG5vZGUudG8sIERlY29yYXRpb24ucmVwbGFjZSh7CiAgICAgICAgICAgIHdpZGdldDogbmV3IExpbmsodGV4dCwgdXJsKSwKICAgICAgICAgIH0pLAogICAgICAgICAgICAgICAgICAgICApOwogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSwKICAgIH0pOwogICAgcmV0dXJuIGJ1aWxkZXIuZmluaXNoKCk7CiAgfSwKICBwcm92aWRlOiAoZikgPT4gRWRpdG9yVmlldy5kZWNvcmF0aW9ucy5mcm9tKGYpLAp9KTsKCmxldCB2aWV3ID0gbmV3IEVkaXRvclZpZXcoewogIGRvYzogIiMgSGVsbG8gV29ybGRcblxuW1Rlc3RdKGh0dHBzOi8vZXhhbXBsZS5jb20pXG5cblxuIiwKICBleHRlbnNpb25zOiBbZGVjb3JhdGlvbnNGaWVsZCwgbWFya2Rvd24oKV0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5LAp9KTsKCnZpZXcuZm9jdXMoKTs=
19
19
  // https://discuss.codemirror.net/t/link-widget-is-clicked-only-if-editor-is-out-of-focus/7433
20
20
 
21
- // TODO(burdon): Adapt to make all markdown editable?
21
+ export type LinkOptions = {
22
+ link?: boolean;
23
+ onRender?: (el: Element, url: string) => void;
24
+ };
22
25
 
23
- // TODO(burdon): Research overlays.
24
- // https://stackoverflow.com/questions/26933344/codemirror-detect-and-create-links-inside-editor
25
- // https://codemirror.net/5/doc/manual.html#addOverlay
26
- // https://codemirror.net/5/demo/simplemode.html
27
- // https://codemirror.net/5/demo/search.html (Search dialog).
26
+ /**
27
+ * Creates a state field to replace AST elements with a hyperlink widget.
28
+ * https://codemirror.net/docs/ref/#state.StateField
29
+ */
30
+ export const link = (options: LinkOptions = {}): Extension => {
31
+ return StateField.define<RangeSet<any>>({
32
+ create: (state) => update(state, options),
33
+ update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
34
+ provide: (field) => EditorView.decorations.from(field),
35
+ });
36
+ };
28
37
 
29
38
  class LinkButton extends WidgetType {
30
39
  constructor(
@@ -77,20 +86,28 @@ class LinkText extends WidgetType {
77
86
  }
78
87
  }
79
88
 
89
+ /**
90
+ * Range sets provide a data structure that can hold a collection of tagged,
91
+ * possibly overlapping ranges in such a way that they can efficiently be mapped though document changes.
92
+ * https://codemirror.net/docs/ref/#state
93
+ */
80
94
  const update = (state: EditorState, options: LinkOptions) => {
81
95
  const builder = new RangeSetBuilder();
82
96
  const cursor = state.selection.main.head;
97
+
83
98
  syntaxTree(state).iterate({
84
99
  enter: (node) => {
85
100
  // Check if cursor is inside text.
86
101
  if (node.name === 'Link') {
87
102
  const marks = node.node.getChildren('LinkMark');
88
103
  const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : '';
89
-
90
104
  const urlNode = node.node.getChild('URL');
91
105
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : '';
106
+ if (!url) {
107
+ return false;
108
+ }
92
109
 
93
- if (cursor < node.from || cursor > node.to) {
110
+ if (state.readOnly || cursor < node.from || cursor > node.to) {
94
111
  builder.add(
95
112
  node.from,
96
113
  node.to,
@@ -109,28 +126,11 @@ const update = (state: EditorState, options: LinkOptions) => {
109
126
  }),
110
127
  );
111
128
  }
112
- }
113
129
 
114
- return true;
130
+ return false;
131
+ }
115
132
  },
116
133
  });
117
134
 
118
135
  return builder.finish();
119
136
  };
120
-
121
- export type LinkOptions = {
122
- link?: boolean;
123
- onRender?: (el: Element, url: string) => void;
124
- };
125
-
126
- /**
127
- * Creates a state field to replace AST elements with a hyperlink widget.
128
- * https://codemirror.net/docs/ref/#state.StateField
129
- */
130
- export const link = (options: LinkOptions = {}): Extension => {
131
- return StateField.define<RangeSet<any>>({
132
- create: (state) => update(state, options),
133
- update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
134
- provide: (field) => EditorView.decorations.from(field),
135
- });
136
- };
@@ -4,13 +4,13 @@
4
4
 
5
5
  import { StateField } from '@codemirror/state';
6
6
 
7
- export type TextListener = (text: string) => void;
7
+ export type ListenerOptions = { onChange: (text: string) => void };
8
8
 
9
9
  /**
10
10
  * Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799
11
11
  */
12
- export const listener = (onChange: TextListener) =>
13
- StateField.define({
12
+ export const listener = ({ onChange }: ListenerOptions) => {
13
+ return StateField.define({
14
14
  create: () => null,
15
15
  update: (_value, transaction) => {
16
16
  if (transaction.docChanged && onChange) {
@@ -20,3 +20,4 @@ export const listener = (onChange: TextListener) =>
20
20
  return null;
21
21
  },
22
22
  });
23
+ };
@@ -3,18 +3,11 @@
3
3
  //
4
4
 
5
5
  import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
6
- import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';
6
+ import { history, historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';
7
7
  import { markdownLanguage, markdown } from '@codemirror/lang-markdown';
8
- import {
9
- bracketMatching,
10
- defaultHighlightStyle,
11
- foldKeymap,
12
- indentOnInput,
13
- syntaxHighlighting,
14
- } from '@codemirror/language';
8
+ import { bracketMatching, defaultHighlightStyle, indentOnInput, syntaxHighlighting } from '@codemirror/language';
15
9
  import { languages } from '@codemirror/language-data';
16
- import { lintKeymap } from '@codemirror/lint';
17
- import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
10
+ import { searchKeymap } from '@codemirror/search';
18
11
  import { EditorState, type Extension } from '@codemirror/state';
19
12
  import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
20
13
  import {
@@ -23,55 +16,50 @@ import {
23
16
  dropCursor,
24
17
  EditorView,
25
18
  highlightActiveLine,
26
- highlightActiveLineGutter,
27
- highlightSpecialChars,
28
19
  keymap,
29
20
  placeholder,
30
- rectangularSelection,
31
21
  } from '@codemirror/view';
32
22
 
33
23
  import type { ThemeMode } from '@dxos/react-ui';
34
24
 
35
- import { markdownHighlightStyle, markdownTagsExtension } from './highlight';
25
+ import { markdownHighlightStyle, markdownTagsExtensions } from './highlight';
36
26
 
37
27
  export type MarkdownBundleOptions = {
28
+ readonly?: boolean;
38
29
  themeMode?: ThemeMode;
39
30
  placeholder?: string;
40
31
  };
41
32
 
42
- export const markdownBundle = ({ themeMode, placeholder: _placeholder }: MarkdownBundleOptions): Extension[] => {
43
- // All of https://github.com/codemirror/basic-setup minus line numbers and fold gutter.
44
- // https://codemirror.net/docs/ref/#codemirror.basicSetup
33
+ /**
34
+ * Markdown bundle.
35
+ * Refs:
36
+ * https://github.com/codemirror/basic-setup
37
+ * https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts#L50
38
+ * https://codemirror.net/docs/community
39
+ * https://codemirror.net/docs/ref/#codemirror.basicSetup
40
+ */
41
+ // TODO(burdon): Add Composer here: https://codemirror.net/docs/community
42
+ export const markdownBundle = ({
43
+ readonly,
44
+ themeMode,
45
+ placeholder: _placeholder,
46
+ }: MarkdownBundleOptions): Extension[] => {
45
47
  return [
46
- bracketMatching(),
47
- closeBrackets(),
48
- _placeholder && placeholder(_placeholder),
49
-
50
48
  EditorState.allowMultipleSelections.of(true),
49
+ EditorState.tabSize.of(2),
51
50
  EditorView.lineWrapping,
52
51
 
53
- // autocompletion(),
52
+ customKeymap(),
53
+
54
+ bracketMatching(),
55
+ closeBrackets(),
54
56
  crosshairCursor(),
55
57
  dropCursor(),
56
58
  drawSelection(),
57
59
  highlightActiveLine(),
58
- highlightActiveLineGutter(),
59
- highlightSelectionMatches(),
60
- highlightSpecialChars(),
61
60
  history(),
62
61
  indentOnInput(),
63
- rectangularSelection(),
64
-
65
- keymap.of([
66
- ...closeBracketsKeymap,
67
- // ...completionKeymap,
68
- ...defaultKeymap,
69
- ...foldKeymap,
70
- ...historyKeymap,
71
- ...lintKeymap,
72
- ...searchKeymap,
73
- indentWithTab,
74
- ]),
62
+ _placeholder && placeholder(_placeholder),
75
63
 
76
64
  // Main extension.
77
65
  // https://github.com/codemirror/lang-markdown
@@ -89,15 +77,30 @@ export const markdownBundle = ({ themeMode, placeholder: _placeholder }: Markdow
89
77
 
90
78
  // Parser extensions.
91
79
  extensions: [
92
- // GFM, // TODO(burdon): Provided by default?
93
- markdownTagsExtension,
80
+ // GFM provided by default.
81
+ markdownTagsExtensions,
94
82
  ],
95
83
  }),
96
84
 
97
- // Custom styles.
98
- syntaxHighlighting(markdownHighlightStyle),
99
-
100
- // TODO(thure): All but one rule here apply to both themes; rename or refactor.
85
+ // https://github.com/codemirror/theme-one-dark
101
86
  themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
87
+
88
+ // Custom styles.
89
+ syntaxHighlighting(markdownHighlightStyle(readonly)),
102
90
  ].filter(Boolean) as Extension[];
103
91
  };
92
+
93
+ // https://codemirror.net/docs/ref/#view.keymap
94
+ const customKeymap = () =>
95
+ keymap.of([
96
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
97
+ indentWithTab,
98
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
99
+ ...standardKeymap,
100
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
101
+ ...historyKeymap,
102
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
103
+ ...closeBracketsKeymap,
104
+ // https://codemirror.net/docs/ref/#search.searchKeymap
105
+ ...searchKeymap,
106
+ ]);
@@ -5,9 +5,11 @@
5
5
  import { markdownLanguage } from '@codemirror/lang-markdown';
6
6
  import { HighlightStyle } from '@codemirror/language';
7
7
  import { tags, styleTags, Tag } from '@lezer/highlight';
8
- import { type MarkdownConfig } from '@lezer/markdown';
8
+ import { type MarkdownConfig, Table } from '@lezer/markdown';
9
9
  import get from 'lodash.get';
10
10
 
11
+ import { mx } from '@dxos/react-ui-theme';
12
+
11
13
  import {
12
14
  blockquote,
13
15
  bold,
@@ -40,127 +42,166 @@ export const markdownTags = {
40
42
  ListMark: Tag.define(),
41
43
  QuoteMark: Tag.define(),
42
44
  URL: Tag.define(),
43
- };
44
45
 
45
- export const markdownTagsExtension: MarkdownConfig = {
46
- props: [styleTags(markdownTags)],
46
+ // Custom.
47
+ TableCell: Tag.define(),
47
48
  };
48
49
 
50
+ // TODO(burdon): Customize table parser (make all content monospace).
51
+ // https://github.com/lezer-parser/markdown/blob/main/src/extension.ts
52
+ Table.defineNodes?.forEach((node: any) => {
53
+ switch (node?.name) {
54
+ case 'TableCell': {
55
+ node.style = markdownTags.TableCell;
56
+ break;
57
+ }
58
+ }
59
+ });
60
+
61
+ export const markdownTagsExtensions: MarkdownConfig[] = [
62
+ Table,
63
+ {
64
+ props: [styleTags(markdownTags)],
65
+ },
66
+ ];
67
+
49
68
  /**
50
69
  * Styling based on `lezer` parser tags.
51
70
  * https://codemirror.net/examples/styling
52
71
  * https://github.com/lezer-parser/highlight
53
- * https://lezer.codemirror.net/docs/ref/#highlight (list of tags)
72
+ * https://github.com/lezer-parser/highlight/blob/main/src/highlight.ts#L427
73
+ * https://lezer.codemirror.net/docs/ref/#highlight.tags (list of tags)
74
+ *
75
+ * Examples:
76
+ * - https://github.com/codemirror/language/blob/main/src/highlight.ts#L194
77
+ * - https://github.com/codemirror/theme-one-dark/blob/main/src/one-dark.ts#L115
54
78
  */
55
- export const markdownHighlightStyle = HighlightStyle.define(
56
- [
57
- {
58
- tag: [
59
- tags.keyword,
60
- tags.name,
61
- tags.deleted,
62
- tags.character,
63
- tags.propertyName,
64
- tags.macroName,
65
- tags.color,
66
- tags.constant(tags.name),
67
- tags.standard(tags.name),
68
- tags.definition(tags.name),
69
- tags.separator,
70
- tags.typeName,
71
- tags.className,
72
- tags.number,
73
- tags.changed,
74
- tags.annotation,
75
- tags.modifier,
76
- tags.self,
77
- tags.namespace,
78
- tags.operator,
79
- tags.operatorKeyword,
80
- tags.escape,
81
- tags.regexp,
82
- tags.special(tags.string),
83
- tags.meta,
84
- tags.comment,
85
- tags.atom,
86
- tags.bool,
87
- tags.special(tags.variableName),
88
- tags.processingInstruction,
89
- tags.string,
90
- tags.inserted,
91
- tags.invalid,
92
- ],
93
- color: 'inherit !important',
94
- },
79
+ export const markdownHighlightStyle = (readonly?: boolean) => {
80
+ return HighlightStyle.define(
81
+ [
82
+ {
83
+ tag: [
84
+ tags.keyword,
85
+ tags.name,
86
+ tags.deleted,
87
+ tags.character,
88
+ tags.propertyName,
89
+ tags.macroName,
90
+ tags.color,
91
+ tags.constant(tags.name),
92
+ tags.standard(tags.name),
93
+ tags.definition(tags.name),
94
+ tags.separator,
95
+ tags.typeName,
96
+ tags.className,
97
+ tags.number,
98
+ tags.changed,
99
+ tags.annotation,
100
+ tags.modifier,
101
+ tags.self,
102
+ tags.namespace,
103
+ tags.operator,
104
+ tags.operatorKeyword,
105
+ tags.escape,
106
+ tags.regexp,
107
+ tags.special(tags.string),
108
+ tags.meta,
109
+ tags.comment,
110
+ tags.atom,
111
+ tags.bool,
112
+ tags.special(tags.variableName),
113
+ tags.processingInstruction,
114
+ tags.string,
115
+ tags.inserted,
116
+ tags.invalid,
117
+ ],
118
+ color: 'inherit !important',
119
+ },
95
120
 
96
- // Markdown marks.
97
- {
98
- tag: [
99
- tags.meta,
100
- tags.processingInstruction,
101
- markdownTags.CodeMark,
102
- markdownTags.EmphasisMark,
103
- markdownTags.HeaderMark,
104
- markdownTags.LinkLabel,
105
- markdownTags.LinkReference,
106
- markdownTags.ListMark,
107
- markdownTags.QuoteMark,
108
- ],
109
- class: mark,
110
- },
121
+ // Markdown marks.
122
+ {
123
+ tag: [
124
+ tags.meta,
125
+ tags.processingInstruction,
126
+ markdownTags.LinkLabel,
127
+ markdownTags.LinkReference,
128
+ markdownTags.ListMark,
129
+ ],
130
+ class: mark,
131
+ },
111
132
 
112
- // Headings.
113
- { tag: tags.heading1, class: heading[1] },
114
- { tag: tags.heading2, class: heading[2] },
115
- { tag: tags.heading3, class: heading[3] },
116
- { tag: tags.heading4, class: heading[4] },
117
- { tag: tags.heading5, class: heading[5] },
118
- { tag: tags.heading6, class: heading[6] },
133
+ // Markdown marks.
134
+ {
135
+ tag: [markdownTags.CodeMark, markdownTags.HeaderMark, markdownTags.QuoteMark, markdownTags.EmphasisMark],
136
+ class: readonly ? 'hidden' : mark,
137
+ },
119
138
 
120
- // Emphasis.
121
- { tag: tags.emphasis, class: italic },
122
- { tag: tags.strong, class: bold },
123
- { tag: tags.strikethrough, class: strikethrough },
139
+ // E.g., code block language (after ```).
140
+ {
141
+ tag: [tags.function(tags.variableName), tags.labelName],
142
+ class: readonly ? 'hidden' : codeMark,
143
+ },
124
144
 
125
- // Naked URLs.
126
- {
127
- tag: [markdownTags.URL],
128
- class: inlineUrl,
129
- },
145
+ {
146
+ tag: [tags.monospace],
147
+ class: 'font-mono',
148
+ },
130
149
 
131
- // E.g., code block language (after ```).
132
- {
133
- tag: [tags.function(tags.variableName), tags.labelName],
134
- class: codeMark,
135
- },
150
+ // Headings.
151
+ { tag: tags.heading1, class: heading(1, readonly) },
152
+ { tag: tags.heading2, class: heading(2, readonly) },
153
+ { tag: tags.heading3, class: heading(3, readonly) },
154
+ { tag: tags.heading4, class: heading(4, readonly) },
155
+ { tag: tags.heading5, class: heading(5, readonly) },
156
+ { tag: tags.heading6, class: heading(6, readonly) },
136
157
 
137
- // The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
138
- // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
139
- // when a language is specified. In this case, the syntax highlighting extensions will colorize
140
- // the code, but all other CSS properties will be inherited.
141
- {
142
- tag: [markdownTags.CodeText, markdownTags.InlineCode],
143
- class: code,
144
- },
158
+ // Emphasis.
159
+ { tag: tags.emphasis, class: italic },
160
+ { tag: tags.strong, class: bold },
161
+ { tag: tags.strikethrough, class: strikethrough },
145
162
 
146
- {
147
- tag: [markdownTags.QuoteMark],
148
- class: blockquote,
149
- },
163
+ // Naked URLs.
164
+ {
165
+ tag: [markdownTags.URL],
166
+ class: inlineUrl,
167
+ },
168
+
169
+ // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
170
+ // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
171
+ // when a language is specified. In this case, the syntax highlighting extensions will colorize
172
+ // the code, but all other CSS properties will be inherited.
173
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font.
174
+ {
175
+ tag: [markdownTags.CodeText, markdownTags.InlineCode],
176
+ class: code,
177
+ },
178
+
179
+ {
180
+ tag: [markdownTags.QuoteMark],
181
+ class: blockquote,
182
+ },
150
183
 
184
+ // TODO(burdon): Replace with extension.
185
+ {
186
+ tag: [markdownTags.HorizontalRule],
187
+ class: mx(horizontalRule, readonly && '-indent-[100rem]'),
188
+ },
189
+
190
+ // TODO(burdon): Only if being edited.
191
+ {
192
+ tag: [markdownTags.TableCell],
193
+ class: 'font-mono',
194
+ },
195
+
196
+ // Main content, paragraphs, etc.
197
+ // {
198
+ // tag: [tags.content],
199
+ // fontFamily: get(tokens, 'fontFamily.body', []).join(','),
200
+ // },
201
+ ],
151
202
  {
152
- tag: [markdownTags.HorizontalRule],
153
- class: horizontalRule,
203
+ scope: markdownLanguage,
204
+ all: { fontFamily: get(tokens, 'fontFamily.body', []).join(',') },
154
205
  },
155
-
156
- // Main content, paragraphs, etc.
157
- // {
158
- // tag: [tags.content],
159
- // fontFamily: get(tokens, 'fontFamily.body', []).join(','),
160
- // },
161
- ],
162
- {
163
- scope: markdownLanguage,
164
- all: { fontFamily: get(tokens, 'fontFamily.body', []).join(',') },
165
- },
166
- );
206
+ );
207
+ };
@@ -0,0 +1,31 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { autocompletion, type CompletionContext, type CompletionResult } from '@codemirror/autocomplete';
6
+ import type { Extension } from '@codemirror/state';
7
+
8
+ export type MentionOptions = {
9
+ onSearch: (text: string) => string[];
10
+ };
11
+
12
+ export const mention = ({ onSearch }: MentionOptions): Extension => {
13
+ return autocompletion({
14
+ closeOnBlur: false, // For debugging.
15
+ override: [
16
+ (context: CompletionContext): CompletionResult | null => {
17
+ const match = context.matchBefore(/@(\w+)?/);
18
+ if (!match || (match.from === match.to && !context.explicit)) {
19
+ return null;
20
+ }
21
+
22
+ console.log(match);
23
+
24
+ return {
25
+ from: match.from,
26
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({ label: `@${value}` })),
27
+ };
28
+ },
29
+ ],
30
+ });
31
+ };