@dxos/react-ui-editor 0.3.11-main.8be6c8a → 0.3.11-main.8ef1cc6

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 (81) hide show
  1. package/dist/lib/browser/index.mjs +306 -402
  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 +3 -0
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +4 -9
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +4 -4
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
  11. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  12. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  13. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  14. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/defs.d.ts +4 -4
  16. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/index.d.ts +0 -1
  18. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/semaphore.d.ts +4 -9
  20. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  21. package/dist/types/src/extensions/code.d.ts.map +1 -1
  22. package/dist/types/src/extensions/comments.d.ts +14 -5
  23. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  24. package/dist/types/src/{util → extensions}/cursor.d.ts +9 -1
  25. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  26. package/dist/types/src/extensions/hr.d.ts.map +1 -1
  27. package/dist/types/src/extensions/index.d.ts +2 -0
  28. package/dist/types/src/extensions/index.d.ts.map +1 -1
  29. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  30. package/dist/types/src/extensions/outliner.d.ts +4 -0
  31. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  32. package/dist/types/src/extensions/tasklist.d.ts +5 -1
  33. package/dist/types/src/extensions/tasklist.d.ts.map +1 -1
  34. package/dist/types/src/extensions/yjs/cursor.d.ts +1 -1
  35. package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -1
  36. package/dist/types/src/hooks/index.d.ts +1 -1
  37. package/dist/types/src/hooks/index.d.ts.map +1 -1
  38. package/dist/types/src/hooks/useTextModel.d.ts +0 -5
  39. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  40. package/dist/types/src/hooks/yjs.d.ts.map +1 -1
  41. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  42. package/dist/types/src/themes/default.d.ts.map +1 -1
  43. package/dist/types/src/util.d.ts.map +1 -0
  44. package/package.json +25 -24
  45. package/src/components/TextEditor/MarkdownEditor.stories.tsx +28 -26
  46. package/src/components/TextEditor/TextEditor.stories.tsx +10 -43
  47. package/src/components/TextEditor/TextEditor.tsx +31 -41
  48. package/src/components/TextEditor/comments.stories.tsx +355 -0
  49. package/src/extensions/automerge/automerge.ts +12 -14
  50. package/src/extensions/automerge/cursor.ts +2 -1
  51. package/src/extensions/automerge/defs.ts +13 -9
  52. package/src/extensions/automerge/index.ts +0 -1
  53. package/src/extensions/automerge/semaphore.ts +10 -12
  54. package/src/extensions/awareness.ts +27 -33
  55. package/src/extensions/code.ts +24 -18
  56. package/src/extensions/comments.ts +128 -140
  57. package/src/extensions/cursor.ts +46 -0
  58. package/src/extensions/hr.ts +8 -9
  59. package/src/extensions/image.ts +2 -2
  60. package/src/extensions/index.ts +2 -0
  61. package/src/extensions/link.ts +7 -7
  62. package/src/extensions/markdown/highlight.ts +0 -8
  63. package/src/extensions/outliner.ts +12 -0
  64. package/src/extensions/table.ts +2 -2
  65. package/src/extensions/tasklist.ts +54 -77
  66. package/src/extensions/yjs/cursor.ts +2 -1
  67. package/src/extensions/yjs/yjs.test.ts +1 -1
  68. package/src/extensions/yjs/yjs.ts +2 -2
  69. package/src/hooks/index.ts +2 -1
  70. package/src/hooks/useTextModel.ts +3 -6
  71. package/src/hooks/yjs.ts +1 -23
  72. package/src/styles/markdown.ts +0 -2
  73. package/src/themes/default.ts +17 -50
  74. package/dist/types/src/util/cursor.d.ts.map +0 -1
  75. package/dist/types/src/util/index.d.ts +0 -3
  76. package/dist/types/src/util/index.d.ts.map +0 -1
  77. package/dist/types/src/util/util.d.ts.map +0 -1
  78. package/src/util/cursor.ts +0 -29
  79. package/src/util/index.ts +0 -6
  80. /package/dist/types/src/{util/util.d.ts → util.d.ts} +0 -0
  81. /package/src/{util/util.ts → util.ts} +0 -0
@@ -28,20 +28,24 @@ class HorizontalRuleWidget extends WidgetType {
28
28
  super();
29
29
  }
30
30
 
31
- override eq(other: WidgetType) {
31
+ override eq(other: this) {
32
32
  return this._pos === (other as any as HorizontalRuleWidget)._pos;
33
33
  }
34
34
 
35
- toDOM(view: EditorView) {
36
- // TODO(burdon): Create <hr> element? Does this clash with markdown parser?
35
+ override toDOM(view: EditorView) {
37
36
  const el = document.createElement('div');
38
37
  el.className = 'cm-hr';
39
38
  return el;
40
39
  }
41
40
  }
42
41
 
42
+ // TODO(burdon): Like Tasklist, allow cursor to move into range.
43
+
44
+ // NOTE: Without a blank line before this markup will treat the previous line as a heading.
45
+ // https://www.markdownguide.org/basic-syntax/#horizontal-rules
43
46
  const placeholderMatcher = new MatchDecorator({
44
- regexp: /^---$/g,
47
+ // regexp: /(?<=\n\n)---/gs,
48
+ regexp: /^---$/gs,
45
49
  decoration: (match, view, pos) =>
46
50
  Decoration.replace({
47
51
  widget: new HorizontalRuleWidget(pos),
@@ -63,11 +67,6 @@ export const hr = () => [
63
67
  },
64
68
  {
65
69
  decorations: (instance) => instance.rules,
66
- // TODO(burdon): Is this really atomic (cursor can move into ---).
67
- provide: (plugin) =>
68
- EditorView.atomicRanges.of((view) => {
69
- return view.plugin(plugin)?.rules || Decoration.none;
70
- }),
71
70
  },
72
71
  ),
73
72
  ];
@@ -72,11 +72,11 @@ class ImageWidget extends WidgetType {
72
72
  super();
73
73
  }
74
74
 
75
- override eq(other: WidgetType) {
75
+ override eq(other: this) {
76
76
  return this._url === (other as any as ImageWidget)._url;
77
77
  }
78
78
 
79
- toDOM(view: EditorView) {
79
+ override toDOM(view: EditorView) {
80
80
  const img = document.createElement('img');
81
81
  img.setAttribute('src', this._url);
82
82
  img.setAttribute('class', 'cm-image');
@@ -9,12 +9,14 @@ export * from './basic';
9
9
  export * from './blast';
10
10
  export * from './code';
11
11
  export * from './comments';
12
+ export * from './cursor';
12
13
  export * from './hr';
13
14
  export * from './image';
14
15
  export * from './link';
15
16
  export * from './listener';
16
17
  export * from './markdown';
17
18
  export * from './mention';
19
+ export * from './outliner';
18
20
  export * from './table';
19
21
  export * from './tasklist';
20
22
  export * from './typewriter';
@@ -6,13 +6,13 @@
6
6
  import { syntaxTree } from '@codemirror/language';
7
7
  import { type Extension, RangeSetBuilder } from '@codemirror/state';
8
8
  import {
9
+ hoverTooltip,
9
10
  Decoration,
11
+ type DecorationSet,
10
12
  type EditorView,
11
- WidgetType,
12
- hoverTooltip,
13
13
  ViewPlugin,
14
14
  type ViewUpdate,
15
- type DecorationSet,
15
+ WidgetType,
16
16
  } from '@codemirror/view';
17
17
  import { type SyntaxNode } from '@lezer/common';
18
18
 
@@ -93,11 +93,11 @@ class LinkButton extends WidgetType {
93
93
  super();
94
94
  }
95
95
 
96
- override eq(other: LinkButton) {
96
+ override eq(other: this) {
97
97
  return this._url === other._url;
98
98
  }
99
99
 
100
- toDOM(view: EditorView) {
100
+ override toDOM(view: EditorView) {
101
101
  const el = document.createElement('span');
102
102
  this._onAttach!(el, this._url);
103
103
  return el;
@@ -109,11 +109,11 @@ class LinkText extends WidgetType {
109
109
  super();
110
110
  }
111
111
 
112
- override eq(other: LinkText) {
112
+ override eq(other: this) {
113
113
  return this._url === other._url;
114
114
  }
115
115
 
116
- toDOM(view: EditorView) {
116
+ override toDOM(view: EditorView) {
117
117
  const link = document.createElement('a');
118
118
  link.setAttribute('class', 'cm-link');
119
119
  link.textContent = this._text;
@@ -31,7 +31,6 @@ export const markdownTags = {
31
31
  CodeText: Tag.define(),
32
32
  EmphasisMark: Tag.define(),
33
33
  HeaderMark: Tag.define(),
34
- // HorizontalRule: Tag.define(),
35
34
  InlineCode: Tag.define(),
36
35
  LinkLabel: Tag.define(),
37
36
  LinkReference: Tag.define(),
@@ -177,13 +176,6 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
177
176
  class: blockquote,
178
177
  },
179
178
 
180
- // TODO(burdon): Replace with extension.
181
- // {
182
- // tag: [markdownTags.HorizontalRule],
183
- // class: mx(horizontalRule, readonly && '-indent-[100rem]'),
184
- // },
185
-
186
- // TODO(burdon): Only if being edited.
187
179
  {
188
180
  tag: [markdownTags.TableCell],
189
181
  class: 'font-mono',
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Extension } from '@codemirror/state';
6
+
7
+ export type OutlinerOptions = {};
8
+
9
+ // TODO(burdon): Experimental component: multiple hierarchical draggable editor sections.
10
+ export const outliner = (options: OutlinerOptions = {}): Extension => {
11
+ return [];
12
+ };
@@ -97,14 +97,14 @@ class TableWidget extends WidgetType {
97
97
  super();
98
98
  }
99
99
 
100
- override eq(other: TableWidget) {
100
+ override eq(other: this) {
101
101
  return (
102
102
  this._table.header?.join() === other._table.header?.join() &&
103
103
  this._table.rows?.join() === other._table.rows?.join()
104
104
  );
105
105
  }
106
106
 
107
- toDOM(view: EditorView) {
107
+ override toDOM(view: EditorView) {
108
108
  const table = document.createElement('table');
109
109
 
110
110
  {
@@ -2,57 +2,39 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import {
6
- EditorView,
7
- Decoration,
8
- WidgetType,
9
- MatchDecorator,
10
- ViewPlugin,
11
- type DecorationSet,
12
- type ViewUpdate,
13
- } from '@codemirror/view';
14
-
15
- // TODO(burdon): Reconcile with theme.
16
- const styles = EditorView.baseTheme({
17
- '& .cm-task-item': {
18
- paddingLeft: '4px',
19
- paddingRight: '8px',
20
- },
21
- });
5
+ import { syntaxTree } from '@codemirror/language';
6
+ import { RangeSetBuilder } from '@codemirror/state';
7
+ import { EditorView, Decoration, WidgetType, ViewPlugin, type DecorationSet, type ViewUpdate } from '@codemirror/view';
22
8
 
23
9
  class CheckboxWidget extends WidgetType {
24
- constructor(
25
- private _checked: boolean,
26
- private readonly _indent: number,
27
- private readonly _onCheck?: (check: boolean) => void,
28
- ) {
10
+ constructor(private _checked: boolean) {
29
11
  super();
30
12
  }
31
13
 
32
- override eq(other: CheckboxWidget) {
33
- return this._checked === other._checked && this._indent === other._indent;
14
+ override eq(other: this) {
15
+ return this._checked === other._checked;
34
16
  }
35
17
 
36
- toDOM(view: EditorView) {
37
- const wrap = document.createElement('span');
38
- wrap.className = 'cm-task-item';
39
- wrap.setAttribute('aria-hidden', 'true');
40
- wrap.style.setProperty('margin-left', this._indent * 24 + 'px');
41
-
42
- const input = wrap.appendChild(document.createElement('input'));
18
+ override toDOM(view: EditorView) {
19
+ const input = document.createElement('input');
43
20
  input.type = 'checkbox';
44
21
  input.checked = this._checked;
45
- if (!this._onCheck) {
22
+ if (view.state.readOnly) {
46
23
  input.setAttribute('disabled', 'true');
47
- }
48
- if (this._onCheck) {
49
- input.onchange = (event: Event) => {
50
- this._onCheck?.((event.target as any).checked);
51
- return true;
24
+ } else {
25
+ input.onmousedown = (event: Event) => {
26
+ const pos = view.posAtDOM(input);
27
+ const text = view.state.sliceDoc(pos, pos + 3);
28
+ if (text === (this._checked ? '[x]' : '[ ]')) {
29
+ view.dispatch({
30
+ changes: { from: pos + 1, to: pos + 2, insert: this._checked ? ' ' : 'x' },
31
+ });
32
+ event.preventDefault();
33
+ }
52
34
  };
53
35
  }
54
36
 
55
- return wrap;
37
+ return input;
56
38
  }
57
39
 
58
40
  override ignoreEvent() {
@@ -60,60 +42,55 @@ class CheckboxWidget extends WidgetType {
60
42
  }
61
43
  }
62
44
 
45
+ const checkedDeco = Decoration.replace({ widget: new CheckboxWidget(true) });
46
+ const uncheckedDeco = Decoration.replace({ widget: new CheckboxWidget(false) });
47
+
63
48
  export type TasklistOptions = {};
64
49
 
65
50
  export const tasklist = (options: TasklistOptions = {}) => {
66
- // TODO(burdon): Matcher isn't as precise as syntax tree.
67
- // Allows for indents to be greater than AST would allow.
68
- const taskMatcher = new MatchDecorator({
69
- regexp: /^(\s*)- \[([ xX])\]\s/g,
70
- decoration: (match, view, pos) => {
71
- const indent = Math.floor(match[1].length / 2);
72
- const checked = match[2] === 'x' || match[2] === 'X';
73
- return Decoration.replace({
74
- widget: new CheckboxWidget(
75
- checked,
76
- indent,
77
- view.state.readOnly
78
- ? undefined
79
- : (checked) => {
80
- const idx = pos + match[0].indexOf('[') + 1;
81
- view.dispatch({
82
- changes: {
83
- from: idx,
84
- to: idx + 1,
85
- insert: checked ? 'x' : ' ',
86
- },
87
- // TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
88
- selection: {
89
- anchor: idx + 3,
90
- },
91
- });
92
- },
93
- ),
94
- });
95
- },
96
- });
97
-
98
- const tasks = ViewPlugin.fromClass(
51
+ return ViewPlugin.fromClass(
99
52
  class {
100
- tasks: DecorationSet;
53
+ decorations: DecorationSet;
54
+
101
55
  constructor(view: EditorView) {
102
- this.tasks = taskMatcher.createDeco(view);
56
+ this.decorations = buildDecorations(view);
103
57
  }
104
58
 
105
59
  update(update: ViewUpdate) {
106
- this.tasks = taskMatcher.updateDeco(update, this.tasks);
60
+ if (update.docChanged || update.viewportChanged || update.selectionSet) {
61
+ this.decorations = buildDecorations(update.view);
62
+ }
107
63
  }
108
64
  },
109
65
  {
110
- decorations: (value) => value.tasks,
66
+ decorations: (v) => v.decorations,
67
+ // TODO(burdon): Is this still required?
111
68
  provide: (plugin) =>
112
69
  EditorView.atomicRanges.of((view) => {
113
- return view.plugin(plugin)?.tasks || Decoration.none;
70
+ return view.plugin(plugin)?.decorations || Decoration.none;
114
71
  }),
115
72
  },
116
73
  );
74
+ };
75
+
76
+ const buildDecorations = (view: EditorView): DecorationSet => {
77
+ const builder = new RangeSetBuilder<Decoration>();
78
+ const { state } = view;
79
+ const cursor = state.selection.main.head;
80
+
81
+ for (const { from, to } of view.visibleRanges) {
82
+ syntaxTree(state).iterate({
83
+ enter: (node) => {
84
+ // Check if cursor is inside text.
85
+ if (node.name === 'TaskMarker' && (cursor < node.from || cursor > node.to)) {
86
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === 'x';
87
+ builder.add(node.from, node.to, checked ? checkedDeco : uncheckedDeco);
88
+ }
89
+ },
90
+ from,
91
+ to,
92
+ });
93
+ }
117
94
 
118
- return [tasks, styles];
95
+ return builder.finish();
119
96
  };
@@ -6,12 +6,13 @@ import * as Y from 'yjs';
6
6
 
7
7
  import { arrayToString, stringToArray } from '@dxos/util';
8
8
 
9
- import { type CursorConverter } from '../../util';
9
+ import { type CursorConverter } from '../cursor';
10
10
 
11
11
  export const cursorConverter = (text: Y.Text): CursorConverter => ({
12
12
  toCursor: (index, assoc) => {
13
13
  return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
14
14
  },
15
+
15
16
  fromCursor: (cursor) => {
16
17
  return (
17
18
  Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc!)?.index ??
@@ -12,7 +12,7 @@ import { type YText } from '@dxos/text-model';
12
12
  describe('YJS', () => {
13
13
  // https://docs.yjs.dev/api/shared-types/y.text
14
14
  // https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
15
- test('absolute position', () => {
15
+ test.skip('absolute position', () => {
16
16
  const obj = new TextObject('hello world');
17
17
  const index = obj.text.indexOf('world');
18
18
  const relPos = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(obj.content as YText, index));
@@ -8,9 +8,9 @@ import type * as YP from 'y-protocols/awareness';
8
8
  import type { YText } from '@dxos/text-model';
9
9
 
10
10
  import { cursorConverter } from './cursor';
11
- import { CursorConverter } from '../../util';
11
+ import { Cursor } from '../cursor';
12
12
 
13
13
  export const yjs = (content: YText, awareness?: YP.Awareness) => [
14
- CursorConverter.of(cursorConverter(content)),
14
+ Cursor.converter.of(cursorConverter(content)),
15
15
  yCollab(content, awareness),
16
16
  ];
@@ -4,5 +4,6 @@
4
4
 
5
5
  export * from './automerge';
6
6
  export * from './awareness-provider';
7
- export * from './useTextModel';
8
7
  export * from './yjs';
8
+
9
+ export * from './useTextModel';
@@ -30,7 +30,7 @@ export type Range = {
30
30
 
31
31
  export type CommentRange = {
32
32
  id: string;
33
- cursor: string;
33
+ cursor: string; // TODO(burdon): Rename range.
34
34
  };
35
35
 
36
36
  // TODO(wittjosiah): Factor out to common package? @dxos/react-client?
@@ -41,13 +41,10 @@ export type EditorModel = {
41
41
 
42
42
  // TODO(burdon): Remove.
43
43
  content: string | YText | YXmlFragment | DocAccessor;
44
- /**
45
- * @deprecated Use CursorConverter.
46
- */
47
- getCursorFromRange?: (value: Range) => string;
48
- getRangeFromCursor?: (cursor: string) => Range | undefined;
44
+
49
45
  // TODO(burdon): Move into extension.
50
46
  awareness?: YP.Awareness;
47
+
51
48
  // TODO(burdon): Remove.
52
49
  peer?: {
53
50
  id: string;
package/src/hooks/yjs.ts CHANGED
@@ -6,7 +6,6 @@ import * as decoding from 'lib0/decoding';
6
6
  import * as encoding from 'lib0/encoding';
7
7
  import { Observable } from 'lib0/observable';
8
8
  import * as YP from 'y-protocols/awareness';
9
- import * as Y from 'yjs';
10
9
  import { type Doc } from 'yjs';
11
10
 
12
11
  import { invariant } from '@dxos/invariant';
@@ -14,9 +13,8 @@ import { log } from '@dxos/log';
14
13
  import { type Space } from '@dxos/react-client/echo';
15
14
  import { type GossipMessage } from '@dxos/react-client/mesh';
16
15
  import type { YText } from '@dxos/text-model';
17
- import { arrayToString, stringToArray } from '@dxos/util';
18
16
 
19
- import { type EditorModel, modelState, type Range, type UseTextModelProps } from './useTextModel';
17
+ import { type EditorModel, modelState, type UseTextModelProps } from './useTextModel';
20
18
  import { yjs } from '../extensions';
21
19
 
22
20
  export const createYjsModel = ({ identity, space, text }: UseTextModelProps): EditorModel => {
@@ -29,26 +27,6 @@ export const createYjsModel = ({ identity, space, text }: UseTextModelProps): Ed
29
27
  id: text.doc.guid,
30
28
  content: text.content,
31
29
  text: () => text.content!.toString(),
32
- // https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
33
- // TODO(dmaretskyi): Refactor as cursor-converter.
34
- getCursorFromRange: (range: Range) => {
35
- const from = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content as YText, range.from));
36
- const to = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content as YText, range.to, -1));
37
- return [arrayToString(from), arrayToString(to)].join(':');
38
- },
39
- getRangeFromCursor: (cursor: string) => {
40
- invariant(text.doc);
41
- const parts = cursor.split(':');
42
- const from = Y.createAbsolutePositionFromRelativePosition(
43
- Y.decodeRelativePosition(stringToArray(parts[0])),
44
- text.doc,
45
- );
46
- const to = Y.createAbsolutePositionFromRelativePosition(
47
- Y.decodeRelativePosition(stringToArray(parts[1])),
48
- text.doc,
49
- );
50
- return from && to ? { from: from.index, to: to.index } : undefined;
51
- },
52
30
  extension: [modelState.init(() => model), yjs(text.content as YText, provider?.awareness)],
53
31
  awareness: provider?.awareness,
54
32
  peer: identity
@@ -39,10 +39,8 @@ export const codeBlock = 'mlb-2 font-mono bg-neutral-500/10 p-3 rounded';
39
39
 
40
40
  export const inlineUrl = mx(code, 'px-1');
41
41
 
42
- // TODO(burdon): Replace with widget.
43
42
  export const blockquote = mx('pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30', light);
44
43
 
45
- // TODO(burdon): Replace with widget.
46
44
  export const horizontalRule =
47
45
  'flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800';
48
46
 
@@ -6,8 +6,8 @@ import get from 'lodash.get';
6
6
 
7
7
  import { type ThemeStyles, tokens } from '../styles';
8
8
 
9
- // TODO(burdon): Can we erase All of the default CM styles?
10
- // TODO(burdon): Theme styles seem to be added multiple times?
9
+ // TODO(burdon): Can we use @apply and import css file?
10
+ // https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply?
11
11
 
12
12
  /**
13
13
  * Minimal styles.
@@ -86,6 +86,8 @@ export const defaultTheme: ThemeStyles = {
86
86
  //
87
87
  '.cm-line': {
88
88
  paddingInline: 0,
89
+ // TODO(burdon): Other values leave gaps between lines when highlighted.
90
+ // lineHeight: 'normal',
89
91
  },
90
92
  '.cm-activeLine': {
91
93
  background: 'inherit',
@@ -119,36 +121,6 @@ export const defaultTheme: ThemeStyles = {
119
121
  backgroundColor: get(tokens, 'extend.colors.yellow.700'),
120
122
  },
121
123
 
122
- //
123
- // collaboration
124
- // TODO(burdon): Review classnames (YJS dependent?) Note: e2e tests depend on this class.
125
- //
126
-
127
- '&light .cm-ySelection, &light .cm-yLineSelection': {
128
- mixBlendMode: 'multiply',
129
- },
130
- '&dark .cm-ySelection, &dark .cm-yLineSelection': {
131
- mixBlendMode: 'screen',
132
- },
133
-
134
- '.cm-ySelectionInfo': {
135
- padding: '2px 4px',
136
- marginBlockStart: '-4px',
137
- },
138
- '.cm-ySelection, & .cm-selectionMatch': {
139
- paddingBlockStart: '.15em',
140
- paddingBlockEnd: '.15em',
141
- },
142
- '.cm-ySelectionCaret': {
143
- display: 'inline-block',
144
- insetBlockStart: '.1em',
145
- blockSize: '1.4em',
146
- verticalAlign: 'top',
147
- },
148
- '.cm-yLineSelection': {
149
- margin: 0,
150
- },
151
-
152
124
  //
153
125
  // link
154
126
  //
@@ -188,14 +160,6 @@ export const defaultTheme: ThemeStyles = {
188
160
  textDecoration: 'none',
189
161
  },
190
162
 
191
- //
192
- // widgets
193
- //
194
- '.cm-widgetBuffer': {
195
- display: 'none',
196
- height: 0,
197
- },
198
-
199
163
  //
200
164
  // table
201
165
  //
@@ -229,16 +193,17 @@ export const defaultTheme: ThemeStyles = {
229
193
 
230
194
  //
231
195
  // font size
232
- // TODO(thure): This appears to be the best or only way to set selection caret heights, but it's far more verbose than it needs to be.
196
+ // TODO(thure): This appears to be the best or only way to set selection caret heights,
197
+ // but it's far more verbose than it needs to be.
233
198
  //
234
- ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
235
- const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
236
-
237
- acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
238
- acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
239
- acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
240
- return acc;
241
- }, {}),
199
+ // ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
200
+ // const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
201
+ //
202
+ // acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
203
+ // acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
204
+ // acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
205
+ // return acc;
206
+ // }, {}),
242
207
 
243
208
  /**
244
209
  * Panels
@@ -292,7 +257,9 @@ export const textTheme: ThemeStyles = {
292
257
  };
293
258
 
294
259
  export const markdownTheme: ThemeStyles = {
295
- // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
260
+ '.cm-scroller': {
261
+ fontFamily: get(tokens, 'fontFamily.body', []).join(','),
262
+ },
296
263
  '.cm-placeholder': {
297
264
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
298
265
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../../../src/util/cursor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC;IAC/D,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CACpC;AAOD,eAAO,MAAM,eAAe,yCAE1B,CAAC"}
@@ -1,3 +0,0 @@
1
- export * from './cursor';
2
- export * from './util';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/util/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../../src/util/util.ts"],"names":[],"mappings":"AAMA;;GAEG;AAEH,eAAO,MAAM,eAAe,kCAOR,CAAC"}
@@ -1,29 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { Facet } from '@codemirror/state';
6
-
7
- /**
8
- * Converts indexes into the text document into stable peer-independent cursors.
9
- *
10
- * See:
11
- * - https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
12
- * - https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
13
- *
14
- * @param {assoc} number Negative values will associate the cursor with the previous character
15
- * while positive - with the next one.
16
- */
17
- export interface CursorConverter {
18
- toCursor(position: number, assoc?: -1 | 1 | undefined): string;
19
- fromCursor(cursor: string): number;
20
- }
21
-
22
- const DEFAULT_CURSOR_CONVERTER: CursorConverter = {
23
- toCursor: (position) => position.toString(),
24
- fromCursor: (cursor) => parseInt(cursor),
25
- };
26
-
27
- export const CursorConverter = Facet.define<CursorConverter, CursorConverter>({
28
- combine: (providers) => providers[0] ?? DEFAULT_CURSOR_CONVERTER,
29
- });
package/src/util/index.ts DELETED
@@ -1,6 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- export * from './cursor';
6
- export * from './util';
File without changes
File without changes