@dxos/react-ui-editor 0.3.11-main.1c19c60 → 0.3.11-main.279d9e6

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 (36) hide show
  1. package/dist/lib/browser/index.mjs +232 -210
  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 -3
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/extensions/code.d.ts.map +1 -1
  7. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  8. package/dist/types/src/extensions/hr.d.ts.map +1 -1
  9. package/dist/types/src/extensions/image.d.ts.map +1 -1
  10. package/dist/types/src/extensions/index.d.ts +0 -1
  11. package/dist/types/src/extensions/index.d.ts.map +1 -1
  12. package/dist/types/src/extensions/link.d.ts.map +1 -1
  13. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  14. package/dist/types/src/extensions/tasklist.d.ts.map +1 -1
  15. package/dist/types/src/index.d.ts +1 -0
  16. package/dist/types/src/index.d.ts.map +1 -1
  17. package/dist/types/src/styles/tokens.d.ts +1 -0
  18. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  19. package/dist/types/src/themes/default.d.ts.map +1 -1
  20. package/package.json +22 -22
  21. package/src/components/TextEditor/MarkdownEditor.stories.tsx +6 -7
  22. package/src/extensions/code.ts +4 -5
  23. package/src/extensions/comments.ts +4 -5
  24. package/src/extensions/hr.ts +2 -3
  25. package/src/extensions/image.ts +38 -20
  26. package/src/extensions/index.ts +0 -1
  27. package/src/extensions/link.ts +82 -84
  28. package/src/extensions/markdown/highlight.ts +3 -4
  29. package/src/extensions/table.ts +5 -2
  30. package/src/extensions/tasklist.ts +1 -3
  31. package/src/index.ts +1 -0
  32. package/src/styles/tokens.ts +3 -0
  33. package/src/themes/default.ts +39 -33
  34. package/dist/types/src/extensions/mermaid.d.ts +0 -3
  35. package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
  36. package/src/extensions/mermaid.ts +0 -11
@@ -3,28 +3,45 @@
3
3
  //
4
4
 
5
5
  import { syntaxTree } from '@codemirror/language';
6
- import {
7
- type EditorState,
8
- type Extension,
9
- type RangeSet,
10
- RangeSetBuilder,
11
- StateField,
12
- type Transaction,
13
- } from '@codemirror/state';
14
- import { Decoration, EditorView, WidgetType } from '@codemirror/view';
6
+ import { type EditorState, type Extension, StateField, type Transaction, type Range } from '@codemirror/state';
7
+ import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
15
8
 
16
9
  export type ImageOptions = {};
17
10
 
18
11
  export const image = (options: ImageOptions = {}): Extension => {
19
- return StateField.define<RangeSet<any>>({
20
- create: (state) => update(state, options),
21
- update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
12
+ return StateField.define<DecorationSet>({
13
+ create: (state) => {
14
+ return Decoration.set(buildDecorations(0, state.doc.length, state));
15
+ },
16
+ update: (value: DecorationSet, tr: Transaction) => {
17
+ if (!tr.docChanged && !tr.selection) {
18
+ return value;
19
+ }
20
+ // Find range of changes and cursor changes.
21
+ const cursor = tr.state.selection.main.head;
22
+ const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
23
+ let from = Math.min(cursor, oldCursor);
24
+ let to = Math.max(cursor, oldCursor);
25
+ tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
26
+ from = Math.min(from, fromB);
27
+ to = Math.max(to, toB);
28
+ });
29
+ // Expand to cover lines
30
+ from = tr.state.doc.lineAt(from).from;
31
+ to = tr.state.doc.lineAt(to).to;
32
+ return value.map(tr.changes).update({
33
+ filterFrom: from,
34
+ filterTo: to,
35
+ filter: () => false,
36
+ add: buildDecorations(from, to, tr.state),
37
+ });
38
+ },
22
39
  provide: (field) => EditorView.decorations.from(field),
23
40
  });
24
41
  };
25
42
 
26
- const update = (state: EditorState, options: ImageOptions) => {
27
- const builder = new RangeSetBuilder();
43
+ const buildDecorations = (from: number, to: number, state: EditorState) => {
44
+ const decorations: Range<Decoration>[] = [];
28
45
  const cursor = state.selection.main.head;
29
46
 
30
47
  syntaxTree(state).iterate({
@@ -34,20 +51,20 @@ const update = (state: EditorState, options: ImageOptions) => {
34
51
  if (urlNode) {
35
52
  const hide = state.readOnly || cursor < node.from || cursor > node.to;
36
53
  const url = state.sliceDoc(urlNode.from, urlNode.to);
37
- builder.add(
38
- hide ? node.from : node.to,
39
- node.to,
54
+ decorations.push(
40
55
  Decoration.replace({
41
56
  block: true, // Prevent cursor from entering.
42
57
  widget: new ImageWidget(url),
43
- }),
58
+ }).range(hide ? node.from : node.to, node.to),
44
59
  );
45
60
  }
46
61
  }
47
62
  },
63
+ from,
64
+ to,
48
65
  });
49
66
 
50
- return builder.finish();
67
+ return decorations;
51
68
  };
52
69
 
53
70
  class ImageWidget extends WidgetType {
@@ -55,7 +72,6 @@ class ImageWidget extends WidgetType {
55
72
  super();
56
73
  }
57
74
 
58
- // TODO(burdon): Does this need to be unique for each position?
59
75
  override eq(other: WidgetType) {
60
76
  return this._url === (other as any as ImageWidget)._url;
61
77
  }
@@ -64,6 +80,8 @@ class ImageWidget extends WidgetType {
64
80
  const img = document.createElement('img');
65
81
  img.setAttribute('src', this._url);
66
82
  img.setAttribute('class', 'cm-image');
83
+ // Images are hidden until successfully loaded to avoid flickering effects.
84
+ img.onload = () => img.classList.add('cm-loaded-image');
67
85
  return img;
68
86
  }
69
87
  }
@@ -15,7 +15,6 @@ export * from './link';
15
15
  export * from './listener';
16
16
  export * from './markdown';
17
17
  export * from './mention';
18
- export * from './mermaid';
19
18
  export * from './table';
20
19
  export * from './tasklist';
21
20
  export * from './typewriter';
@@ -4,20 +4,20 @@
4
4
  //
5
5
 
6
6
  import { syntaxTree } from '@codemirror/language';
7
+ import { type Extension, RangeSetBuilder } from '@codemirror/state';
7
8
  import {
8
- type EditorState,
9
- type Extension,
10
- type RangeSet,
11
- RangeSetBuilder,
12
- StateField,
13
- type Transaction,
14
- } from '@codemirror/state';
15
- import { Decoration, EditorView, WidgetType, hoverTooltip } from '@codemirror/view';
9
+ Decoration,
10
+ type EditorView,
11
+ WidgetType,
12
+ hoverTooltip,
13
+ ViewPlugin,
14
+ type ViewUpdate,
15
+ type DecorationSet,
16
+ } from '@codemirror/view';
17
+ import { type SyntaxNode } from '@lezer/common';
16
18
 
17
19
  import { tooltipContent } from '@dxos/react-ui-theme';
18
20
 
19
- const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
20
-
21
21
  // Adapted from:
22
22
  // https://codemirror.net/try/?c=aW1wb3J0IHsgYmFzaWNTZXR1cCwgRWRpdG9yVmlldyB9IGZyb20gImNvZGVtaXJyb3IiCmltcG9ydCB7IG1hcmtkb3duIH0gZnJvbSAiQGNvZGVtaXJyb3IvbGFuZy1tYXJrZG93biIKaW1wb3J0IHsgc3ludGF4VHJlZSB9IGZyb20gIkBjb2RlbWlycm9yL2xhbmd1YWdlIjsKaW1wb3J0IHsgRWRpdG9yU3RhdGUsIFJhbmdlU2V0QnVpbGRlciwgU3RhdGVGaWVsZCB9IGZyb20gIkBjb2RlbWlycm9yL3N0YXRlIjsKaW1wb3J0IHsgRGVjb3JhdGlvbiwgV2lkZ2V0VHlwZSB9IGZyb20gIkBjb2RlbWlycm9yL3ZpZXciOwoKY2xhc3MgTGluayBleHRlbmRzIFdpZGdldFR5cGUgewogIGNvbnN0cnVjdG9yKHRleHQsIHVybCkgewogICAgc3VwZXIoKQogICAgdGhpcy50ZXh0ID0gdGV4dAogICAgdGhpcy51cmwgPSB1cmwKICB9CiAgZXEob3RoZXIpIHsgcmV0dXJuIHRoaXMudGV4dCA9PSBvdGhlci50ZXh0ICYmIHRoaXMudXJsID09IG90aGVyLnVybCB9CiAgdG9ET00oKSB7CiAgICBsZXQgbGluayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImEiKQogICAgbGluay50ZXh0Q29udGVudCA9IHRoaXMudGV4dAogICAgbGluay5ocmVmID0gdGhpcy51cmwKICAgIHJldHVybiBsaW5rOwogIH0KfQoKbGV0IGRlY29yYXRpb25zRmllbGQgPSBTdGF0ZUZpZWxkLmRlZmluZSh7CiAgY3JlYXRlKCkgewogICAgcmV0dXJuIERlY29yYXRpb24ubm9uZTsKICB9LAogIHVwZGF0ZShfLCB0cikgewogICAgY29uc3QgYnVpbGRlciA9IG5ldyBSYW5nZVNldEJ1aWxkZXIoKTsKICAgIGxldCBjdXJzb3IgPSB0ci5zdGF0ZS5zZWxlY3Rpb24ubWFpbi5oZWFkOwogICAgc3ludGF4VHJlZSh0ci5zdGF0ZSkuaXRlcmF0ZSh7CiAgICAgIGVudGVyOiAobm9kZSkgPT4gewogICAgICAgIGlmICgoY3Vyc29yIDwgbm9kZS5mcm9tIHx8IGN1cnNvciA+IG5vZGUudG8pICYmIG5vZGUubmFtZSA9PSAiTGluayIpIHsKICAgICAgICAgIGxldCBtYXJrcyA9IG5vZGUubm9kZS5nZXRDaGlsZHJlbigiTGlua01hcmsiKTsKICAgICAgICAgIGxldCB0ZXh0ID0gbWFya3MubGVuZ3RoID49IDIgPyB0ci5zdGF0ZS5zbGljZURvYyhtYXJrc1swXS50bywgbWFya3NbMV0uZnJvbSkgOiAiIgoKICAgICAgICAgIGxldCB1cmxOb2RlID0gbm9kZS5ub2RlLmdldENoaWxkKCJVUkwiKTsKICAgICAgICAgIGxldCB1cmwgPSB1cmxOb2RlID8gdHIuc3RhdGUuc2xpY2VEb2ModXJsTm9kZS5mcm9tLCB1cmxOb2RlLnRvKSA6ICIiCiAgICAgICAgICBidWlsZGVyLmFkZChub2RlLmZyb20sIG5vZGUudG8sIERlY29yYXRpb24ucmVwbGFjZSh7CiAgICAgICAgICAgIHdpZGdldDogbmV3IExpbmsodGV4dCwgdXJsKSwKICAgICAgICAgIH0pLAogICAgICAgICAgICAgICAgICAgICApOwogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSwKICAgIH0pOwogICAgcmV0dXJuIGJ1aWxkZXIuZmluaXNoKCk7CiAgfSwKICBwcm92aWRlOiAoZikgPT4gRWRpdG9yVmlldy5kZWNvcmF0aW9ucy5mcm9tKGYpLAp9KTsKCmxldCB2aWV3ID0gbmV3IEVkaXRvclZpZXcoewogIGRvYzogIiMgSGVsbG8gV29ybGRcblxuW1Rlc3RdKGh0dHBzOi8vZXhhbXBsZS5jb20pXG5cblxuIiwKICBleHRlbnNpb25zOiBbZGVjb3JhdGlvbnNGaWVsZCwgbWFya2Rvd24oKV0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5LAp9KTsKCnZpZXcuZm9jdXMoKTs=
23
23
  // https://discuss.codemirror.net/t/link-widget-is-clicked-only-if-editor-is-out-of-focus/7433
@@ -34,11 +34,24 @@ export type LinkOptions = {
34
34
  */
35
35
  export const link = (options: LinkOptions = {}): Extension => {
36
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
- }),
37
+ ViewPlugin.fromClass(
38
+ class {
39
+ decorations: DecorationSet;
40
+
41
+ constructor(view: EditorView) {
42
+ this.decorations = buildDecorations(view, options);
43
+ }
44
+
45
+ update(update: ViewUpdate) {
46
+ if (update.docChanged || update.viewportChanged || update.selectionSet) {
47
+ this.decorations = buildDecorations(update.view, options);
48
+ }
49
+ }
50
+ },
51
+ {
52
+ decorations: (v) => v.decorations,
53
+ },
54
+ ),
42
55
  ];
43
56
 
44
57
  if (options.onHover) {
@@ -46,41 +59,25 @@ export const link = (options: LinkOptions = {}): Extension => {
46
59
  // https://codemirror.net/examples/tooltip
47
60
  // https://codemirror.net/docs/ref/#view.hoverTooltip
48
61
  // 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) {
62
+ hoverTooltip((view, pos, side) => {
63
+ const syntax = syntaxTree(view.state).resolveInner(pos, side);
64
+ let link = null;
65
+ for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
66
+ link = node.name === 'Link' ? node : null;
67
+ }
68
+ const url = link && link.getChild('URL');
69
+ if (!url || !link) {
71
70
  return null;
72
71
  }
73
-
74
- const [, , url] = match ?? [];
75
-
72
+ const urlText = view.state.sliceDoc(url.from, url.to);
76
73
  return {
77
- pos: idx + from,
78
- end: idx + from + match[0].length,
74
+ pos: link.from,
75
+ end: link.to,
79
76
  above: true,
80
77
  create: () => {
81
78
  const el = document.createElement('div');
82
79
  el.className = tooltipContent({}, 'pli-2 plb-1');
83
- options.onHover!(el, url);
80
+ options.onHover!(el, urlText);
84
81
  return { dom: el, offset: { x: 0, y: 4 } };
85
82
  },
86
83
  };
@@ -92,16 +89,12 @@ export const link = (options: LinkOptions = {}): Extension => {
92
89
  };
93
90
 
94
91
  class LinkButton extends WidgetType {
95
- constructor(
96
- private readonly _pos: number,
97
- private readonly _url: string,
98
- private readonly _onAttach: LinkOptions['onRender'],
99
- ) {
92
+ constructor(private readonly _url: string, private readonly _onAttach: LinkOptions['onRender']) {
100
93
  super();
101
94
  }
102
95
 
103
96
  override eq(other: LinkButton) {
104
- return this._pos === other._pos && this._url === other._url;
97
+ return this._url === other._url;
105
98
  }
106
99
 
107
100
  toDOM(view: EditorView) {
@@ -147,46 +140,51 @@ class LinkText extends WidgetType {
147
140
  * possibly overlapping ranges in such a way that they can efficiently be mapped though document changes.
148
141
  * https://codemirror.net/docs/ref/#state
149
142
  */
150
- const update = (state: EditorState, options: LinkOptions) => {
151
- const builder = new RangeSetBuilder();
143
+ const buildDecorations = (view: EditorView, options: LinkOptions): DecorationSet => {
144
+ const builder = new RangeSetBuilder<Decoration>();
145
+ const { state } = view;
152
146
  const cursor = state.selection.main.head;
153
147
 
154
- syntaxTree(state).iterate({
155
- enter: (node) => {
156
- // Check if cursor is inside text.
157
- if (node.name === 'Link') {
158
- const marks = node.node.getChildren('LinkMark');
159
- const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : '';
160
- const urlNode = node.node.getChild('URL');
161
- const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : '';
162
- if (!url) {
163
- return false;
164
- }
148
+ for (const { from, to } of view.visibleRanges) {
149
+ syntaxTree(state).iterate({
150
+ enter: (node) => {
151
+ // Check if cursor is inside text.
152
+ if (node.name === 'Link') {
153
+ const marks = node.node.getChildren('LinkMark');
154
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : '';
155
+ const urlNode = node.node.getChild('URL');
156
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : '';
157
+ if (!url) {
158
+ return false;
159
+ }
165
160
 
166
- if (state.readOnly || cursor < node.from || cursor > node.to) {
167
- builder.add(
168
- node.from,
169
- node.to,
170
- Decoration.replace({
171
- widget: new LinkText(text, options.link ? url : undefined),
172
- }),
173
- );
174
- }
161
+ if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
162
+ builder.add(
163
+ node.from,
164
+ node.to,
165
+ Decoration.replace({
166
+ widget: new LinkText(text, options.link ? url : undefined),
167
+ }),
168
+ );
169
+ }
175
170
 
176
- if (options.onRender) {
177
- builder.add(
178
- node.to,
179
- node.to,
180
- Decoration.replace({
181
- widget: new LinkButton(node.from, url, options.onRender),
182
- }),
183
- );
184
- }
171
+ if (options.onRender) {
172
+ builder.add(
173
+ node.to,
174
+ node.to,
175
+ Decoration.replace({
176
+ widget: new LinkButton(url, options.onRender),
177
+ }),
178
+ );
179
+ }
185
180
 
186
- return false;
187
- }
188
- },
189
- });
181
+ return false;
182
+ }
183
+ },
184
+ from,
185
+ to,
186
+ });
187
+ }
190
188
 
191
189
  return builder.finish();
192
190
  };
@@ -6,19 +6,18 @@ import { markdownLanguage } from '@codemirror/lang-markdown';
6
6
  import { HighlightStyle } from '@codemirror/language';
7
7
  import { tags, styleTags, Tag } from '@lezer/highlight';
8
8
  import { type MarkdownConfig, Table } from '@lezer/markdown';
9
- import get from 'lodash.get';
10
9
 
11
10
  import {
12
11
  blockquote,
13
12
  bold,
14
13
  code,
15
14
  codeMark,
15
+ getToken,
16
16
  heading,
17
17
  inlineUrl,
18
18
  italic,
19
19
  mark,
20
20
  strikethrough,
21
- tokens,
22
21
  } from '../../styles';
23
22
 
24
23
  /**
@@ -193,12 +192,12 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
193
192
  // Main content, paragraphs, etc.
194
193
  // {
195
194
  // tag: [tags.content],
196
- // fontFamily: get(tokens, 'fontFamily.body', []).join(','),
195
+ // fontFamily: getToken('fontFamily.body', []).join(','),
197
196
  // },
198
197
  ],
199
198
  {
200
199
  scope: markdownLanguage,
201
- all: { fontFamily: get(tokens, 'fontFamily.body', []).join(',') },
200
+ all: { fontFamily: getToken('fontFamily.body', []).join(',') },
202
201
  },
203
202
  );
204
203
  };
@@ -98,8 +98,11 @@ class TableWidget extends WidgetType {
98
98
  super();
99
99
  }
100
100
 
101
- override eq(other: WidgetType) {
102
- return this._table.from === (other as any as TableWidget)?._table?.from;
101
+ override eq(other: TableWidget) {
102
+ return (
103
+ this._table.header?.join() === other._table.header?.join() &&
104
+ this._table.rows?.join() === other._table.rows?.join()
105
+ );
103
106
  }
104
107
 
105
108
  toDOM(view: EditorView) {
@@ -22,7 +22,6 @@ const styles = EditorView.baseTheme({
22
22
 
23
23
  class CheckboxWidget extends WidgetType {
24
24
  constructor(
25
- private readonly _pos: number,
26
25
  private _checked: boolean,
27
26
  private readonly _indent: number,
28
27
  private readonly _onCheck?: (check: boolean) => void,
@@ -31,7 +30,7 @@ class CheckboxWidget extends WidgetType {
31
30
  }
32
31
 
33
32
  override eq(other: CheckboxWidget) {
34
- return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
33
+ return this._checked === other._checked && this._indent === other._indent;
35
34
  }
36
35
 
37
36
  toDOM(view: EditorView) {
@@ -73,7 +72,6 @@ export const tasklist = (options: TasklistOptions = {}) => {
73
72
  const checked = match[2] === 'x' || match[2] === 'X';
74
73
  return Decoration.replace({
75
74
  widget: new CheckboxWidget(
76
- pos,
77
75
  checked,
78
76
  indent,
79
77
  view.state.readOnly
package/src/index.ts CHANGED
@@ -9,4 +9,5 @@ export { type Extension } from '@codemirror/state';
9
9
  export * from './components';
10
10
  export * from './extensions';
11
11
  export * from './hooks';
12
+ export { getToken } from './styles';
12
13
  export * from './themes';
@@ -2,6 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import get from 'lodash.get';
5
6
  import type { StyleSpec } from 'style-mod';
6
7
 
7
8
  import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
@@ -12,3 +13,5 @@ export type ThemeStyles = {
12
13
 
13
14
  // TODO(thure): Why export the whole theme? Can this be done differently?
14
15
  export const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
16
+
17
+ export const getToken = (path: string, defaultValue: any = undefined) => get(tokens, path, defaultValue);
@@ -49,12 +49,12 @@ export const defaultTheme: ThemeStyles = {
49
49
  '&.cm-focused': {
50
50
  outline: 'none',
51
51
  },
52
- '& .cm-scroller': {
52
+ '.cm-scroller': {
53
53
  overflow: 'visible',
54
54
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
55
55
  },
56
56
 
57
- '& .cm-content': {
57
+ '.cm-content': {
58
58
  padding: 0,
59
59
  // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
60
60
  fontSize: '16px',
@@ -77,17 +77,17 @@ export const defaultTheme: ThemeStyles = {
77
77
  '&dark .cm-cursor, &dark .cm-dropCursor': {
78
78
  borderLeft: '2px solid white',
79
79
  },
80
- '& .cm-placeholder': {
80
+ '.cm-placeholder': {
81
81
  fontWeight: 100,
82
82
  },
83
83
 
84
84
  //
85
85
  // line
86
86
  //
87
- '& .cm-line': {
87
+ '.cm-line': {
88
88
  paddingInline: 0,
89
89
  },
90
- '& .cm-activeLine': {
90
+ '.cm-activeLine': {
91
91
  background: 'inherit',
92
92
  },
93
93
 
@@ -131,28 +131,28 @@ export const defaultTheme: ThemeStyles = {
131
131
  mixBlendMode: 'screen',
132
132
  },
133
133
 
134
- '& .cm-ySelectionInfo': {
134
+ '.cm-ySelectionInfo': {
135
135
  padding: '2px 4px',
136
136
  marginBlockStart: '-4px',
137
137
  },
138
- '& .cm-ySelection, & .cm-selectionMatch': {
138
+ '.cm-ySelection, & .cm-selectionMatch': {
139
139
  paddingBlockStart: '.15em',
140
140
  paddingBlockEnd: '.15em',
141
141
  },
142
- '& .cm-ySelectionCaret': {
142
+ '.cm-ySelectionCaret': {
143
143
  display: 'inline-block',
144
144
  insetBlockStart: '.1em',
145
145
  blockSize: '1.4em',
146
146
  verticalAlign: 'top',
147
147
  },
148
- '& .cm-yLineSelection': {
148
+ '.cm-yLineSelection': {
149
149
  margin: 0,
150
150
  },
151
151
 
152
152
  //
153
153
  // link
154
154
  //
155
- '& .cm-link': {
155
+ '.cm-link': {
156
156
  color: get(tokens, 'extend.colors.primary.500'),
157
157
  textDecorationLine: 'underline',
158
158
  textDecorationThickness: '1px',
@@ -164,34 +164,34 @@ export const defaultTheme: ThemeStyles = {
164
164
  //
165
165
  // tooltip
166
166
  //
167
- '& .cm-tooltip': {
167
+ '.cm-tooltip': {
168
168
  border: 'none',
169
169
  },
170
- '& .cm-tooltip-below': {},
170
+ '.cm-tooltip-below': {},
171
171
 
172
172
  //
173
173
  // autocomplete
174
174
  //
175
- '& .cm-tooltip-autocomplete': {
175
+ '.cm-tooltip-autocomplete': {
176
176
  marginTop: '4px',
177
177
  marginLeft: '-3px',
178
178
  },
179
- '& .cm-tooltip-autocomplete ul li': {},
180
- '& .cm-tooltip-autocomplete ul li[aria-selected]': {},
181
- '& .cm-completionIcon': {
179
+ '.cm-tooltip-autocomplete ul li': {},
180
+ '.cm-tooltip-autocomplete ul li[aria-selected]': {},
181
+ '.cm-completionIcon': {
182
182
  display: 'none',
183
183
  },
184
- '& .cm-completionLabel': {
184
+ '.cm-completionLabel': {
185
185
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
186
186
  },
187
- '& .cm-completionMatchedText': {
187
+ '.cm-completionMatchedText': {
188
188
  textDecoration: 'none',
189
189
  },
190
190
 
191
191
  //
192
192
  // widgets
193
193
  //
194
- '& .cm-widgetBuffer': {
194
+ '.cm-widgetBuffer': {
195
195
  display: 'none',
196
196
  height: 0,
197
197
  },
@@ -199,26 +199,32 @@ export const defaultTheme: ThemeStyles = {
199
199
  //
200
200
  // table
201
201
  //
202
- '& .cm-table *': {
202
+ '.cm-table *': {
203
203
  fontFamily: `${get(tokens, 'fontFamily.mono', []).join(',')} !important`,
204
204
  textDecoration: 'none !important',
205
205
  },
206
- '& .cm-table-head': {
206
+ '.cm-table-head': {
207
207
  padding: '2px 16px 2px 0px',
208
208
  borderBottom: `1px solid ${get(tokens, 'extend.colors.neutral.500')}`,
209
209
  fontWeight: 100,
210
210
  textAlign: 'left',
211
211
  color: get(tokens, 'extend.colors.neutral.500'),
212
212
  },
213
- '& .cm-table-cell': {
213
+ '.cm-table-cell': {
214
214
  padding: '2px 16px 2px 0px',
215
215
  },
216
216
 
217
217
  //
218
218
  // image
219
219
  //
220
- '& .cm-image': {
221
- margin: '0.5rem 0',
220
+ '.cm-image': {
221
+ display: 'block',
222
+ height: '0',
223
+ },
224
+ '.cm-image.cm-loaded-image': {
225
+ height: 'auto',
226
+ borderTop: '0.5rem solid transparent',
227
+ borderBottom: '0.5rem solid transparent',
222
228
  },
223
229
 
224
230
  //
@@ -250,14 +256,14 @@ export const defaultTheme: ThemeStyles = {
250
256
  * </div>
251
257
  * </div
252
258
  */
253
- '& .cm-panels': {
259
+ '.cm-panels': {
254
260
  border: `1px solid ${get(tokens, 'extend.colors.neutral.200')}`,
255
261
  },
256
- '& .cm-panel': {
262
+ '.cm-panel': {
257
263
  background: get(tokens, 'extend.colors.neutral.50'),
258
264
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
259
265
  },
260
- '& .cm-button': {
266
+ '.cm-button': {
261
267
  margin: '4px',
262
268
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
263
269
  background: get(tokens, 'extend.colors.neutral.100'),
@@ -271,32 +277,32 @@ export const defaultTheme: ThemeStyles = {
271
277
  backgroundImage: 'none',
272
278
  },
273
279
  },
274
- '& .cm-panel input[type=checkbox]': {
280
+ '.cm-panel input[type=checkbox]': {
275
281
  marginRight: '0.4rem !important',
276
282
  },
277
283
  };
278
284
 
279
285
  export const textTheme: ThemeStyles = {
280
- '& .cm-scroller': {
286
+ '.cm-scroller': {
281
287
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
282
288
  },
283
- '& .cm-placeholder': {
289
+ '.cm-placeholder': {
284
290
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
285
291
  },
286
292
  };
287
293
 
288
294
  export const markdownTheme: ThemeStyles = {
289
295
  // NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
290
- '& .cm-placeholder': {
296
+ '.cm-placeholder': {
291
297
  fontFamily: get(tokens, 'fontFamily.body', []).join(','),
292
298
  },
293
299
  };
294
300
 
295
301
  export const codeTheme: ThemeStyles = {
296
- '& .cm-scroller': {
302
+ '.cm-scroller': {
297
303
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
298
304
  },
299
- '& .cm-placeholder': {
305
+ '.cm-placeholder': {
300
306
  fontFamily: get(tokens, 'fontFamily.mono', []).join(','),
301
307
  },
302
308
  };
@@ -1,3 +0,0 @@
1
- import { type Extension } from '@codemirror/state';
2
- export declare const mermaid: () => Extension;
3
- //# sourceMappingURL=mermaid.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../../../src/extensions/mermaid.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAInD,eAAO,MAAM,OAAO,QAAO,SAE1B,CAAC"}
@@ -1,11 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { type Extension } from '@codemirror/state';
6
-
7
- // TODO(burdon): How will this interact with the code extension?
8
- // TODO(burdon): Move to mermaid-plugin (which should contribute this extension).
9
- export const mermaid = (): Extension => {
10
- return [];
11
- };