@dxos/react-ui-editor 0.3.11-main.a6c4a82 → 0.3.11-main.b4b98ab

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 (109) hide show
  1. package/dist/lib/browser/index.mjs +140 -124
  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 +0 -3
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +2 -2
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  9. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  10. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
  11. package/dist/types/src/extensions/basic.d.ts.map +1 -0
  12. package/dist/types/src/extensions/blast.d.ts.map +1 -0
  13. package/dist/types/src/extensions/code.d.ts.map +1 -0
  14. package/dist/types/src/{components/TextEditor/extensions → extensions}/comments.d.ts +1 -1
  15. package/dist/types/src/extensions/comments.d.ts.map +1 -0
  16. package/dist/types/src/extensions/hr.d.ts.map +1 -0
  17. package/dist/types/src/extensions/image.d.ts.map +1 -0
  18. package/dist/types/src/{components/TextEditor/extensions → extensions}/index.d.ts +0 -1
  19. package/dist/types/src/extensions/index.d.ts.map +1 -0
  20. package/dist/types/src/{components/TextEditor/extensions → extensions}/link.d.ts +1 -0
  21. package/dist/types/src/extensions/link.d.ts.map +1 -0
  22. package/dist/types/src/extensions/listener.d.ts +10 -0
  23. package/dist/types/src/extensions/listener.d.ts.map +1 -0
  24. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
  25. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
  26. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
  27. package/dist/types/src/extensions/mention.d.ts.map +1 -0
  28. package/dist/types/src/extensions/mermaid.d.ts.map +1 -0
  29. package/dist/types/src/extensions/table.d.ts.map +1 -0
  30. package/dist/types/src/extensions/tasklist.d.ts.map +1 -0
  31. package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
  32. package/dist/types/src/extensions/util.d.ts.map +1 -0
  33. package/dist/types/src/hooks/useTextModel.d.ts +1 -1
  34. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  35. package/dist/types/src/index.d.ts +2 -0
  36. package/dist/types/src/index.d.ts.map +1 -1
  37. package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +1 -1
  38. package/dist/types/src/themes/default.d.ts.map +1 -0
  39. package/dist/types/src/themes/index.d.ts.map +1 -0
  40. package/package.json +21 -21
  41. package/src/components/TextEditor/MarkdownEditor.stories.tsx +11 -13
  42. package/src/components/TextEditor/TextEditor.stories.tsx +1 -1
  43. package/src/components/TextEditor/TextEditor.tsx +6 -6
  44. package/src/components/TextEditor/index.ts +0 -3
  45. package/src/{components/TextEditor/extensions → extensions}/code.ts +1 -1
  46. package/src/{components/TextEditor/extensions → extensions}/comments.ts +10 -7
  47. package/src/{components/TextEditor/extensions → extensions}/hr.ts +1 -1
  48. package/src/{components/TextEditor/extensions → extensions}/index.ts +0 -1
  49. package/src/{components/TextEditor/extensions → extensions}/link.ts +66 -10
  50. package/src/extensions/listener.ts +35 -0
  51. package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +1 -1
  52. package/src/{components/TextEditor/extensions → extensions}/typewriter.ts +1 -1
  53. package/src/hooks/useTextModel.ts +5 -4
  54. package/src/index.ts +2 -0
  55. package/src/{components/TextEditor/themes → themes}/default.ts +1 -1
  56. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
  57. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
  58. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
  59. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
  60. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
  61. package/dist/types/src/components/TextEditor/extensions/hr.d.ts.map +0 -1
  62. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
  63. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
  64. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
  65. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -9
  66. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
  67. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
  68. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
  69. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
  70. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
  71. package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts.map +0 -1
  72. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
  73. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
  74. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
  75. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
  76. package/dist/types/src/components/TextEditor/extensions/typewriter.d.ts.map +0 -1
  77. package/dist/types/src/components/TextEditor/extensions/util.d.ts.map +0 -1
  78. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
  79. package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
  80. package/src/components/TextEditor/extensions/listener.ts +0 -23
  81. package/src/components/TextEditor/extensions/tooltip.ts +0 -61
  82. /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
  83. /package/dist/types/src/{components/TextEditor/extensions → extensions}/basic.d.ts +0 -0
  84. /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
  85. /package/dist/types/src/{components/TextEditor/extensions → extensions}/code.d.ts +0 -0
  86. /package/dist/types/src/{components/TextEditor/extensions → extensions}/hr.d.ts +0 -0
  87. /package/dist/types/src/{components/TextEditor/extensions → extensions}/image.d.ts +0 -0
  88. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/bundle.d.ts +0 -0
  89. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -0
  90. /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/index.d.ts +0 -0
  91. /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
  92. /package/dist/types/src/{components/TextEditor/extensions → extensions}/mermaid.d.ts +0 -0
  93. /package/dist/types/src/{components/TextEditor/extensions → extensions}/table.d.ts +0 -0
  94. /package/dist/types/src/{components/TextEditor/extensions → extensions}/tasklist.d.ts +0 -0
  95. /package/dist/types/src/{components/TextEditor/extensions → extensions}/typewriter.d.ts +0 -0
  96. /package/dist/types/src/{components/TextEditor/extensions → extensions}/util.d.ts +0 -0
  97. /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
  98. /package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +0 -0
  99. /package/src/{components/TextEditor/extensions → extensions}/basic.ts +0 -0
  100. /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
  101. /package/src/{components/TextEditor/extensions → extensions}/image.ts +0 -0
  102. /package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +0 -0
  103. /package/src/{components/TextEditor/extensions → extensions}/markdown/index.ts +0 -0
  104. /package/src/{components/TextEditor/extensions → extensions}/mention.ts +0 -0
  105. /package/src/{components/TextEditor/extensions → extensions}/mermaid.ts +0 -0
  106. /package/src/{components/TextEditor/extensions → extensions}/table.ts +0 -0
  107. /package/src/{components/TextEditor/extensions → extensions}/tasklist.ts +0 -0
  108. /package/src/{components/TextEditor/extensions → extensions}/util.ts +0 -0
  109. /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
@@ -21,10 +21,10 @@ import { generateName } from '@dxos/display-name';
21
21
  import { useThemeContext } from '@dxos/react-ui';
22
22
  import { getColorForValue, inputSurface, mx } from '@dxos/react-ui-theme';
23
23
 
24
- import { basicBundle, markdownBundle, setCommentRange } from './extensions';
25
- import { defaultTheme, markdownTheme, textTheme } from './themes';
24
+ import { basicBundle, markdownBundle, setCommentRange } from '../../extensions';
26
25
  import { type CommentRange, type EditorModel } from '../../hooks';
27
26
  import { type ThemeStyles } from '../../styles';
27
+ import { defaultTheme, markdownTheme, textTheme } from '../../themes';
28
28
 
29
29
  export const EditorModes = ['default', 'vim'] as const;
30
30
  export type EditorMode = (typeof EditorModes)[number];
@@ -56,10 +56,10 @@ export type TextEditorSlots = {
56
56
 
57
57
  export type TextEditorProps = {
58
58
  model: EditorModel;
59
- comments?: CommentRange[];
60
- readonly?: boolean;
61
- editorMode?: EditorMode;
59
+ readonly?: boolean; // TODO(burdon): Move into model.
60
+ comments?: CommentRange[]; // TODO(burdon): Move into extension.
62
61
  extensions?: Extension[];
62
+ editorMode?: EditorMode;
63
63
  slots?: TextEditorSlots;
64
64
  };
65
65
 
@@ -68,7 +68,7 @@ export type TextEditorProps = {
68
68
  * NOTE: Rather than adding properties, try to create extensions that can be reused.
69
69
  */
70
70
  export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
71
- ({ model, comments, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
71
+ ({ model, readonly, comments, extensions = [], editorMode, slots = defaultSlots }, forwardedRef) => {
72
72
  const { themeMode } = useThemeContext();
73
73
  const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
74
74
  const [root, setRoot] = useState<HTMLDivElement | null>(null);
@@ -5,7 +5,4 @@
5
5
  export { tags } from '@lezer/highlight';
6
6
  export { type Extension } from '@codemirror/state';
7
7
 
8
- export * from './extensions';
9
- export * from './themes';
10
-
11
8
  export * from './TextEditor';
@@ -15,7 +15,7 @@ import get from 'lodash.get';
15
15
 
16
16
  import { mx } from '@dxos/react-ui-theme';
17
17
 
18
- import { tokens } from '../../../styles';
18
+ import { tokens } from '../styles';
19
19
 
20
20
  const CODE_REGEX = /```[\s\S]*?```/gs;
21
21
 
@@ -24,8 +24,9 @@ import { log } from '@dxos/log';
24
24
  import { nonNullable } from '@dxos/util';
25
25
 
26
26
  import { callbackWrapper } from './util';
27
- import { type CommentRange, type EditorModel, modelState, type Range } from '../../../hooks';
27
+ import { type CommentRange, type EditorModel, modelState, type Range } from '../hooks';
28
28
 
29
+ // TODO(burdon): Handle scroll to.
29
30
  // TODO(burdon): Handle delete, cut, copy, and paste (separately) text that includes comment range.
30
31
  // TODO(burdon): Consider breaking into separate plugin (since not standalone)? Like mermaid?
31
32
 
@@ -93,10 +94,12 @@ const commentsStateField = StateField.define<CommentsState>({
93
94
  // Update range from store.
94
95
  if (effect.is(setCommentRange)) {
95
96
  const { model, comments } = effect.value;
96
- const ranges: ExtendedCommentRange[] = comments.map((comment) => {
97
- const range = model.getRangeFromCursor!(comment.cursor)!;
98
- return { ...comment, ...range };
99
- });
97
+ const ranges: ExtendedCommentRange[] = comments
98
+ .map((comment) => {
99
+ const range = model.getRangeFromCursor!(comment.cursor);
100
+ return range && { ...comment, ...range };
101
+ })
102
+ .filter(nonNullable);
100
103
 
101
104
  return { ...value, ranges };
102
105
  }
@@ -284,7 +287,7 @@ export const comments = (options: CommentsOptions = {}): Extension => {
284
287
  ? keymap.of([
285
288
  {
286
289
  key: options?.key ?? "meta-'",
287
- run: createCommentThread,
290
+ run: callbackWrapper(createCommentThread),
288
291
  },
289
292
  ])
290
293
  : [],
@@ -334,7 +337,7 @@ export const comments = (options: CommentsOptions = {}): Extension => {
334
337
  ranges.forEach((range) => {
335
338
  if (from2 === to2) {
336
339
  const newRange = model.getRangeFromCursor!(range.cursor);
337
- if (newRange.to - newRange.from === 0) {
340
+ if (!newRange || newRange.to - newRange.from === 0) {
338
341
  // TODO(burdon): Delete range if empty.
339
342
  log.info('deleted comment', { thread: range.id });
340
343
  }
@@ -13,7 +13,7 @@ import {
13
13
  } from '@codemirror/view';
14
14
  import get from 'lodash.get';
15
15
 
16
- import { tokens } from '../../../styles';
16
+ import { tokens } from '../styles';
17
17
 
18
18
  // TODO(burdon): Reconcile with theme.
19
19
  const styles = EditorView.baseTheme({
@@ -16,5 +16,4 @@ export * from './mention';
16
16
  export * from './mermaid';
17
17
  export * from './table';
18
18
  export * from './tasklist';
19
- export * from './tooltip';
20
19
  export * from './typewriter';
@@ -12,7 +12,11 @@ import {
12
12
  StateField,
13
13
  type Transaction,
14
14
  } from '@codemirror/state';
15
- import { Decoration, EditorView, WidgetType } from '@codemirror/view';
15
+ import { Decoration, EditorView, WidgetType, hoverTooltip } from '@codemirror/view';
16
+
17
+ import { tooltipContent } from '@dxos/react-ui-theme';
18
+
19
+ const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
16
20
 
17
21
  // Adapted from:
18
22
  // https://codemirror.net/try/?c=aW1wb3J0IHsgYmFzaWNTZXR1cCwgRWRpdG9yVmlldyB9IGZyb20gImNvZGVtaXJyb3IiCmltcG9ydCB7IG1hcmtkb3duIH0gZnJvbSAiQGNvZGVtaXJyb3IvbGFuZy1tYXJrZG93biIKaW1wb3J0IHsgc3ludGF4VHJlZSB9IGZyb20gIkBjb2RlbWlycm9yL2xhbmd1YWdlIjsKaW1wb3J0IHsgRWRpdG9yU3RhdGUsIFJhbmdlU2V0QnVpbGRlciwgU3RhdGVGaWVsZCB9IGZyb20gIkBjb2RlbWlycm9yL3N0YXRlIjsKaW1wb3J0IHsgRGVjb3JhdGlvbiwgV2lkZ2V0VHlwZSB9IGZyb20gIkBjb2RlbWlycm9yL3ZpZXciOwoKY2xhc3MgTGluayBleHRlbmRzIFdpZGdldFR5cGUgewogIGNvbnN0cnVjdG9yKHRleHQsIHVybCkgewogICAgc3VwZXIoKQogICAgdGhpcy50ZXh0ID0gdGV4dAogICAgdGhpcy51cmwgPSB1cmwKICB9CiAgZXEob3RoZXIpIHsgcmV0dXJuIHRoaXMudGV4dCA9PSBvdGhlci50ZXh0ICYmIHRoaXMudXJsID09IG90aGVyLnVybCB9CiAgdG9ET00oKSB7CiAgICBsZXQgbGluayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImEiKQogICAgbGluay50ZXh0Q29udGVudCA9IHRoaXMudGV4dAogICAgbGluay5ocmVmID0gdGhpcy51cmwKICAgIHJldHVybiBsaW5rOwogIH0KfQoKbGV0IGRlY29yYXRpb25zRmllbGQgPSBTdGF0ZUZpZWxkLmRlZmluZSh7CiAgY3JlYXRlKCkgewogICAgcmV0dXJuIERlY29yYXRpb24ubm9uZTsKICB9LAogIHVwZGF0ZShfLCB0cikgewogICAgY29uc3QgYnVpbGRlciA9IG5ldyBSYW5nZVNldEJ1aWxkZXIoKTsKICAgIGxldCBjdXJzb3IgPSB0ci5zdGF0ZS5zZWxlY3Rpb24ubWFpbi5oZWFkOwogICAgc3ludGF4VHJlZSh0ci5zdGF0ZSkuaXRlcmF0ZSh7CiAgICAgIGVudGVyOiAobm9kZSkgPT4gewogICAgICAgIGlmICgoY3Vyc29yIDwgbm9kZS5mcm9tIHx8IGN1cnNvciA+IG5vZGUudG8pICYmIG5vZGUubmFtZSA9PSAiTGluayIpIHsKICAgICAgICAgIGxldCBtYXJrcyA9IG5vZGUubm9kZS5nZXRDaGlsZHJlbigiTGlua01hcmsiKTsKICAgICAgICAgIGxldCB0ZXh0ID0gbWFya3MubGVuZ3RoID49IDIgPyB0ci5zdGF0ZS5zbGljZURvYyhtYXJrc1swXS50bywgbWFya3NbMV0uZnJvbSkgOiAiIgoKICAgICAgICAgIGxldCB1cmxOb2RlID0gbm9kZS5ub2RlLmdldENoaWxkKCJVUkwiKTsKICAgICAgICAgIGxldCB1cmwgPSB1cmxOb2RlID8gdHIuc3RhdGUuc2xpY2VEb2ModXJsTm9kZS5mcm9tLCB1cmxOb2RlLnRvKSA6ICIiCiAgICAgICAgICBidWlsZGVyLmFkZChub2RlLmZyb20sIG5vZGUudG8sIERlY29yYXRpb24ucmVwbGFjZSh7CiAgICAgICAgICAgIHdpZGdldDogbmV3IExpbmsodGV4dCwgdXJsKSwKICAgICAgICAgIH0pLAogICAgICAgICAgICAgICAgICAgICApOwogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSwKICAgIH0pOwogICAgcmV0dXJuIGJ1aWxkZXIuZmluaXNoKCk7CiAgfSwKICBwcm92aWRlOiAoZikgPT4gRWRpdG9yVmlldy5kZWNvcmF0aW9ucy5mcm9tKGYpLAp9KTsKCmxldCB2aWV3ID0gbmV3IEVkaXRvclZpZXcoewogIGRvYzogIiMgSGVsbG8gV29ybGRcblxuW1Rlc3RdKGh0dHBzOi8vZXhhbXBsZS5jb20pXG5cblxuIiwKICBleHRlbnNpb25zOiBbZGVjb3JhdGlvbnNGaWVsZCwgbWFya2Rvd24oKV0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5LAp9KTsKCnZpZXcuZm9jdXMoKTs=
@@ -20,6 +24,7 @@ import { Decoration, EditorView, WidgetType } from '@codemirror/view';
20
24
 
21
25
  export type LinkOptions = {
22
26
  link?: boolean;
27
+ onHover?: (el: Element, url: string) => void;
23
28
  onRender?: (el: Element, url: string) => void;
24
29
  };
25
30
 
@@ -28,11 +33,62 @@ export type LinkOptions = {
28
33
  * https://codemirror.net/docs/ref/#state.StateField
29
34
  */
30
35
  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
+ const extensions: Extension[] = [
37
+ StateField.define<RangeSet<any>>({
38
+ create: (state) => update(state, options),
39
+ update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
40
+ provide: (field) => EditorView.decorations.from(field),
41
+ }),
42
+ ];
43
+
44
+ if (options.onHover) {
45
+ extensions.push(
46
+ // https://codemirror.net/examples/tooltip
47
+ // https://codemirror.net/docs/ref/#view.hoverTooltip
48
+ // https://github.com/codemirror/view/blob/main/src/tooltip.ts
49
+ hoverTooltip((view, pos) => {
50
+ const { from, text } = view.state.doc.lineAt(pos);
51
+ const p = pos - from;
52
+
53
+ let idx = 0;
54
+ let match;
55
+ do {
56
+ match = text.substring(idx).match(markdownLinkRegexp);
57
+ if (!match) {
58
+ match = undefined;
59
+ break;
60
+ }
61
+
62
+ idx = text.indexOf(match[0]);
63
+ if (p >= idx && p < idx + match[0].length) {
64
+ break;
65
+ }
66
+
67
+ idx += match[0].length;
68
+ } while (true);
69
+
70
+ if (!match) {
71
+ return null;
72
+ }
73
+
74
+ const [, , url] = match ?? [];
75
+
76
+ return {
77
+ pos: idx + from,
78
+ end: idx + from + match[0].length,
79
+ above: true,
80
+ create: () => {
81
+ const el = document.createElement('div');
82
+ el.className = tooltipContent({}, 'pli-2 plb-1');
83
+ options.onHover!(el, url);
84
+ return { dom: el, offset: { x: 0, y: 4 } };
85
+ },
86
+ };
87
+ }),
88
+ );
89
+ }
90
+
91
+ return extensions;
36
92
  };
37
93
 
38
94
  class LinkButton extends WidgetType {
@@ -56,12 +112,12 @@ class LinkButton extends WidgetType {
56
112
  }
57
113
 
58
114
  class LinkText extends WidgetType {
59
- constructor(private readonly _pos: number, private readonly _text: string, private readonly _url?: string) {
115
+ constructor(private readonly _text: string, private readonly _url?: string) {
60
116
  super();
61
117
  }
62
118
 
63
119
  override eq(other: LinkText) {
64
- return this._pos === other._pos && this._url === other._url;
120
+ return this._url === other._url;
65
121
  }
66
122
 
67
123
  toDOM(view: EditorView) {
@@ -76,7 +132,7 @@ class LinkText extends WidgetType {
76
132
  link.onclick = () => {
77
133
  view.dispatch({
78
134
  selection: {
79
- anchor: this._pos,
135
+ anchor: view.posAtDOM(link),
80
136
  },
81
137
  });
82
138
  };
@@ -112,7 +168,7 @@ const update = (state: EditorState, options: LinkOptions) => {
112
168
  node.from,
113
169
  node.to,
114
170
  Decoration.replace({
115
- widget: new LinkText(node.from, text, options.link ? url : undefined),
171
+ widget: new LinkText(text, options.link ? url : undefined),
116
172
  }),
117
173
  );
118
174
  }
@@ -0,0 +1,35 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Extension } from '@codemirror/state';
6
+ import { EditorView } from '@codemirror/view';
7
+
8
+ export type ListenerOptions = {
9
+ onFocus?: (focused: boolean) => void;
10
+ onChange?: (text: string) => void;
11
+ };
12
+
13
+ /**
14
+ * Event listener.
15
+ */
16
+ export const listener = ({ onFocus, onChange }: ListenerOptions): Extension => {
17
+ const extensions: Extension[] = [];
18
+
19
+ onFocus &&
20
+ extensions.push(
21
+ EditorView.focusChangeEffect.of((_, focused) => {
22
+ onFocus(focused);
23
+ return null;
24
+ }),
25
+ );
26
+
27
+ onChange &&
28
+ extensions.push(
29
+ EditorView.updateListener.of((update) => {
30
+ onChange(update.state.doc.toString());
31
+ }),
32
+ );
33
+
34
+ return extensions;
35
+ };
@@ -19,7 +19,7 @@ import {
19
19
  mark,
20
20
  strikethrough,
21
21
  tokens,
22
- } from '../../../../styles';
22
+ } from '../../styles';
23
23
 
24
24
  /**
25
25
  * Custom tags defined and processed by the GFM lezer extension.
@@ -19,7 +19,7 @@ const defaultItems = ['hello world!', 'this is a test.', 'this is [DXOS](https:/
19
19
  */
20
20
  export const typewriter = ({ delay = 75, items = defaultItems }: DemoOptions = {}): Extension => {
21
21
  let t: any;
22
- let idx = 0;
22
+ let idx = 0; // TODO(burdon): Make global.
23
23
 
24
24
  return [
25
25
  keymap.of([
@@ -55,7 +55,7 @@ export type EditorModel = {
55
55
  content: string | YText | YXmlFragment | DocAccessor;
56
56
  text: () => string;
57
57
  getCursorFromRange?: (value: Range) => string;
58
- getRangeFromCursor?: (cursor: string) => Range;
58
+ getRangeFromCursor?: (cursor: string) => Range | undefined;
59
59
  extension?: Extension;
60
60
  awareness?: Awareness;
61
61
  peer?: {
@@ -110,16 +110,17 @@ const createYjsModel = ({ identity, space, text }: UseTextModelProps): EditorMod
110
110
  return [arrayToString(from), arrayToString(to)].join(':');
111
111
  },
112
112
  getRangeFromCursor: (cursor: string) => {
113
+ invariant(text.doc);
113
114
  const parts = cursor.split(':');
114
115
  const from = Y.createAbsolutePositionFromRelativePosition(
115
116
  Y.decodeRelativePosition(stringToArray(parts[0])),
116
- text.doc!,
117
+ text.doc,
117
118
  );
118
119
  const to = Y.createAbsolutePositionFromRelativePosition(
119
120
  Y.decodeRelativePosition(stringToArray(parts[1])),
120
- text.doc!,
121
+ text.doc,
121
122
  );
122
- return { from: from!.index, to: to!.index };
123
+ return from && to ? { from: from.index, to: to.index } : undefined;
123
124
  },
124
125
  extension: [yCollab(text.content as YText, provider?.awareness), modelState.init(() => model)],
125
126
  awareness: provider?.awareness,
package/src/index.ts CHANGED
@@ -7,4 +7,6 @@ export { Doc, YText, YXmlFragment } from '@dxos/text-model';
7
7
  export { type Extension } from '@codemirror/state';
8
8
 
9
9
  export * from './components';
10
+ export * from './extensions';
10
11
  export * from './hooks';
12
+ export * from './themes';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import get from 'lodash.get';
6
6
 
7
- import { type ThemeStyles, tokens } from '../../../styles';
7
+ import { type ThemeStyles, tokens } from '../styles';
8
8
 
9
9
  // TODO(burdon): Can we erase All of the default CM styles?
10
10
  // TODO(burdon): Theme styles seem to be added multiple times?
@@ -1 +0,0 @@
1
- {"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/autocomplete.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAE5C,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,EAAE,CAAC;CAC1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,iBAAkB,mBAAmB,4CAgC7D,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"basic.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/basic.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,WAAW,uDAAwD,kBAAkB,KAAG,SAAS,EAa5E,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"blast.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/blast.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAMnD,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,gBAAgB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,qBAAqB,EAAE;QAAE,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IACpE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,YAU5B,CAAC;AAEF,eAAO,MAAM,KAAK,aAAa,QAAQ,YAAY,CAAC,KAAoB,SAmEvE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/code.ts"],"names":[],"mappings":"AAuDA,eAAO,MAAM,IAAI,+CA2ChB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/comments.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAA2B,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAWL,KAAK,IAAI,EACV,MAAM,kBAAkB,CAAC;AAS1B,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAc,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AA8B7F,KAAK,eAAe,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAGF,KAAK,oBAAoB,GAAG,KAAK,GAC/B,YAAY,GAAG;IAEb,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB,CAAC;AAEJ,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG;IAC5C,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC,CAAC;AAEF,eAAO,MAAM,eAAe;WAA+B,WAAW;cAAY,YAAY,EAAE;EAAK,CAAC;AA+FtG,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACjD;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,QAAQ,aAAa,eAAe,KAAQ,SA+LxD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"hr.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/hr.ts"],"names":[],"mappings":"AAmDA,eAAO,MAAM,EAAE,+CAsBd,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/image.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,SAAS,EAKf,MAAM,mBAAmB,CAAC;AAG3B,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC;AAE9B,eAAO,MAAM,KAAK,aAAa,YAAY,KAAQ,SAMlD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/index.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,MAAM,CAAC;AACrB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/link.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,SAAS,EAKf,MAAM,mBAAmB,CAAC;AAO3B,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,IAAI,aAAa,WAAW,KAAQ,SAMhD,CAAC"}
@@ -1,9 +0,0 @@
1
- import { StateField } from '@codemirror/state';
2
- export type ListenerOptions = {
3
- onChange: (text: string) => void;
4
- };
5
- /**
6
- * Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799
7
- */
8
- export declare const listener: ({ onChange }: ListenerOptions) => StateField<null>;
9
- //# sourceMappingURL=listener.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/listener.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,CAAC;AAEnE;;GAEG;AACH,eAAO,MAAM,QAAQ,iBAAkB,eAAe,qBAWrD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/TextEditor/extensions/markdown/bundle.ts"],"names":[],"mappings":"AAUA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAYhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;GAOG;AAEH,eAAO,MAAM,cAAc,wDAIxB,qBAAqB,KAAG,SAAS,EA6CnC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/TextEditor/extensions/markdown/highlight.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAmB,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,KAAK,cAAc,EAAS,MAAM,iBAAiB,CAAC;AAgB7D;;;;GAIG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;CAgBxB,CAAC;AAaF,eAAO,MAAM,sBAAsB,EAAE,cAAc,EAKlD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,cAAe,OAAO,mBAgIxD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/components/TextEditor/extensions/markdown/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/mention.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,OAAO,iBAAkB,cAAc,KAAG,SAmBtD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/mermaid.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,eAAO,MAAM,OAAO,QAAO,SAE1B,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/table.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoB,KAAK,QAAQ,EAAmB,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AASnH,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC;AAE9B;;;GAGG;AACH,eAAO,MAAM,KAAK,aAAa,YAAY,8BAM1C,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tasklist.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/tasklist.ts"],"names":[],"mappings":"AA+DA,MAAM,MAAM,eAAe,GAAG,EAAE,CAAC;AAEjC,eAAO,MAAM,QAAQ,aAAa,eAAe,4CAuDhD,CAAC"}
@@ -1,12 +0,0 @@
1
- import { type Extension } from '@codemirror/state';
2
- export type TooltipOptions = {
3
- onHover: (el: Element, url: string) => void;
4
- regexp?: RegExp;
5
- };
6
- /**
7
- * https://codemirror.net/examples/tooltip
8
- * https://codemirror.net/docs/ref/#view.hoverTooltip
9
- * https://github.com/codemirror/view/blob/main/src/tooltip.ts
10
- */
11
- export declare const tooltip: ({ onHover, regexp }: TooltipOptions) => Extension;
12
- //# sourceMappingURL=tooltip.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/tooltip.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAOnD,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,wBAA8C,cAAc,KAAG,SAuC/E,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"typewriter.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/typewriter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAKnD,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,UAAU,sBAA0C,WAAW,KAAQ,SAgDnF,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/extensions/util.ts"],"names":[],"mappings":"AAMA;;GAEG;AAEH,eAAO,MAAM,eAAe,kCAOR,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,iBAAiB,CAAC;AAK3D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,WA8P1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAK3B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,WAOvB,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/components/TextEditor/themes/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC"}
@@ -1,23 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { StateField } from '@codemirror/state';
6
-
7
- export type ListenerOptions = { onChange: (text: string) => void };
8
-
9
- /**
10
- * Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799
11
- */
12
- export const listener = ({ onChange }: ListenerOptions) => {
13
- return StateField.define({
14
- create: () => null,
15
- update: (_value, transaction) => {
16
- if (transaction.docChanged && onChange) {
17
- onChange(transaction.newDoc.toString());
18
- }
19
-
20
- return null;
21
- },
22
- });
23
- };
@@ -1,61 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { type Extension } from '@codemirror/state';
6
- import { hoverTooltip } from '@codemirror/view';
7
-
8
- import { tooltipContent } from '@dxos/react-ui-theme';
9
-
10
- const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
11
-
12
- export type TooltipOptions = {
13
- onHover: (el: Element, url: string) => void;
14
- regexp?: RegExp;
15
- };
16
-
17
- /**
18
- * https://codemirror.net/examples/tooltip
19
- * https://codemirror.net/docs/ref/#view.hoverTooltip
20
- * https://github.com/codemirror/view/blob/main/src/tooltip.ts
21
- */
22
- export const tooltip = ({ onHover, regexp = markdownLinkRegexp }: TooltipOptions): Extension =>
23
- hoverTooltip((view, pos) => {
24
- const { from, text } = view.state.doc.lineAt(pos);
25
- const p = pos - from;
26
-
27
- let idx = 0;
28
- let match;
29
- do {
30
- match = text.substring(idx).match(regexp);
31
- if (!match) {
32
- match = undefined;
33
- break;
34
- }
35
-
36
- idx = text.indexOf(match[0]);
37
- if (p >= idx && p < idx + match[0].length) {
38
- break;
39
- }
40
-
41
- idx += match[0].length;
42
- } while (true);
43
-
44
- if (!match) {
45
- return null;
46
- }
47
-
48
- const [, , url] = match ?? [];
49
-
50
- return {
51
- pos: idx + from,
52
- end: idx + from + match[0].length,
53
- above: true,
54
- create: () => {
55
- const el = document.createElement('div');
56
- el.className = tooltipContent({}, 'pli-2 plb-1');
57
- onHover(el, url);
58
- return { dom: el, offset: { x: 0, y: 4 } };
59
- },
60
- };
61
- });