@dxos/react-ui-editor 0.6.9 → 0.6.10-main.3cfcc89

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 (75) hide show
  1. package/dist/lib/browser/index.mjs +759 -732
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/InputMode.stories.d.ts +2 -1
  5. package/dist/types/src/InputMode.stories.d.ts.map +1 -1
  6. package/dist/types/src/TextEditor.stories.d.ts +20 -13
  7. package/dist/types/src/TextEditor.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  9. package/dist/types/src/defaults.d.ts +5 -1
  10. package/dist/types/src/defaults.d.ts.map +1 -1
  11. package/dist/types/src/extensions/autocomplete.d.ts +3 -2
  12. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  13. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +2 -1
  14. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  15. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  16. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  17. package/dist/types/src/extensions/factories.d.ts +2 -2
  18. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  19. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  20. package/dist/types/src/extensions/markdown/action.d.ts +1 -1
  21. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  22. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  23. package/dist/types/src/extensions/markdown/changes.d.ts +10 -0
  24. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -0
  25. package/dist/types/src/extensions/markdown/changes.test.d.ts +2 -0
  26. package/dist/types/src/extensions/markdown/changes.test.d.ts.map +1 -0
  27. package/dist/types/src/extensions/markdown/debug.d.ts +11 -0
  28. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -0
  29. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  30. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  31. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  32. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  33. package/dist/types/src/extensions/markdown/styles.d.ts +4 -0
  34. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -0
  35. package/dist/types/src/index.d.ts +0 -1
  36. package/dist/types/src/index.d.ts.map +1 -1
  37. package/dist/types/src/styles/theme.d.ts +1 -1
  38. package/dist/types/src/styles/theme.d.ts.map +1 -1
  39. package/dist/types/src/styles/tokens.d.ts +2 -5
  40. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  41. package/dist/types/src/translations.d.ts +1 -0
  42. package/dist/types/src/translations.d.ts.map +1 -1
  43. package/package.json +26 -27
  44. package/src/InputMode.stories.tsx +1 -1
  45. package/src/TextEditor.stories.tsx +125 -77
  46. package/src/components/Toolbar/Toolbar.tsx +91 -92
  47. package/src/defaults.ts +16 -11
  48. package/src/extensions/annotations.ts +2 -2
  49. package/src/extensions/autocomplete.ts +4 -1
  50. package/src/extensions/automerge/automerge.stories.tsx +1 -1
  51. package/src/extensions/awareness/awareness.ts +1 -1
  52. package/src/extensions/comments.ts +11 -45
  53. package/src/extensions/dnd.ts +3 -5
  54. package/src/extensions/factories.ts +8 -8
  55. package/src/extensions/folding.tsx +3 -4
  56. package/src/extensions/markdown/action.ts +1 -0
  57. package/src/extensions/markdown/bundle.ts +3 -1
  58. package/src/extensions/markdown/{link-paste.test.ts → changes.test.ts} +2 -2
  59. package/src/extensions/markdown/changes.ts +148 -0
  60. package/src/extensions/markdown/debug.ts +44 -0
  61. package/src/extensions/markdown/decorate.ts +35 -108
  62. package/src/extensions/markdown/formatting.ts +1 -2
  63. package/src/extensions/markdown/highlight.ts +2 -2
  64. package/src/extensions/markdown/index.ts +1 -0
  65. package/src/extensions/markdown/parser.test.ts +29 -0
  66. package/src/extensions/markdown/styles.ts +103 -0
  67. package/src/index.ts +0 -2
  68. package/src/styles/theme.ts +85 -147
  69. package/src/styles/tokens.ts +6 -6
  70. package/src/translations.ts +1 -0
  71. package/dist/types/src/extensions/markdown/link-paste.d.ts +0 -9
  72. package/dist/types/src/extensions/markdown/link-paste.d.ts.map +0 -1
  73. package/dist/types/src/extensions/markdown/link-paste.test.d.ts +0 -2
  74. package/dist/types/src/extensions/markdown/link-paste.test.d.ts.map +0 -1
  75. package/src/extensions/markdown/link-paste.ts +0 -107
@@ -4,14 +4,10 @@
4
4
 
5
5
  import { type StyleSpec } from 'style-mod';
6
6
 
7
- import { getToken } from './tokens';
7
+ import { fontBody } from './tokens';
8
8
 
9
9
  export type ThemeStyles = Record<string, StyleSpec>;
10
10
 
11
- // TODO(burdon): Factor out theme.
12
- // TODO(burdon): Can we use @apply and import css file?
13
- // https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply?
14
-
15
11
  /**
16
12
  * Minimal styles.
17
13
  * https://codemirror.net/examples/styling
@@ -43,8 +39,8 @@ export type ThemeStyles = Record<string, StyleSpec>;
43
39
  * </div>
44
40
  * </div>
45
41
  *
46
- * NOTE: Use one of '&', '&light', and '&dark' prefix to scope instance.
47
42
  * NOTE: `light` and `dark` selectors are preprocessed by CodeMirror and can only be in the base theme.
43
+ * NOTE: Use 'unset' to remove default CM style.
48
44
  */
49
45
  export const defaultTheme: ThemeStyles = {
50
46
  '&': {},
@@ -65,16 +61,11 @@ export const defaultTheme: ThemeStyles = {
65
61
  */
66
62
  '.cm-content': {
67
63
  padding: 'unset',
64
+ fontFamily: fontBody,
68
65
  // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
69
66
  fontSize: '16px',
70
- fontFamily: getToken('fontFamily.body'),
71
67
  lineHeight: 1.5,
72
- },
73
- '&light .cm-content': {
74
- color: getToken('extend.semanticColors.base.fg.light', 'black'),
75
- },
76
- '&dark .cm-content': {
77
- color: getToken('extend.semanticColors.base.fg.dark', 'white'),
68
+ color: 'unset',
78
69
  },
79
70
 
80
71
  /**
@@ -82,107 +73,85 @@ export const defaultTheme: ThemeStyles = {
82
73
  * NOTE: Gutters should have the same top margin as the content.
83
74
  */
84
75
  '.cm-gutters': {
85
- background: 'transparent',
76
+ background: 'unset',
86
77
  },
87
78
  '.cm-gutter': {},
88
79
  '.cm-gutterElement': {
80
+ fontSize: '16px',
89
81
  lineHeight: 1.5,
90
82
  },
91
83
 
92
- //
93
- // Cursor
94
- //
95
- '&light .cm-cursor, &light .cm-dropCursor': {
96
- borderLeft: '2px solid black',
97
- },
98
- '&dark .cm-cursor, &dark .cm-dropCursor': {
99
- borderLeft: '2px solid white',
100
- },
101
- '&light .cm-placeholder': {
102
- color: getToken('extend.semanticColors.description.light', 'rgba(0,0,0,.2)'),
103
- },
104
- '&dark .cm-placeholder': {
105
- color: getToken('extend.semanticColors.description.dark', 'rgba(255,255,255,.2)'),
84
+ '.cm-lineNumbers': {
85
+ minWidth: '36px',
106
86
  },
107
87
 
108
- //
109
- // line
110
- //
88
+ /**
89
+ * Line.
90
+ */
111
91
  '.cm-line': {
112
92
  paddingInline: 0,
113
93
  },
114
94
  '.cm-activeLine': {
115
- background: 'transparent',
95
+ background: 'var(--dx-hoverSurface)',
116
96
  },
117
97
 
118
- //
119
- // gutter
120
- //
121
- '.cm-lineNumbers': {
122
- minWidth: '36px',
123
- },
124
-
125
- //
126
- // Selection
127
- //
128
-
129
- '&light .cm-selectionBackground': {
130
- background: getToken('extend.colors.primary.100'),
131
- },
132
- '&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
133
- background: getToken('extend.colors.primary.200'),
134
- },
135
- '&dark .cm-selectionBackground': {
136
- background: getToken('extend.colors.primary.700'),
98
+ /**
99
+ * Cursor (layer).
100
+ */
101
+ '.cm-cursor, .cm-dropCursor': {
102
+ borderLeft: '2px solid var(--dx-cmCursor)',
137
103
  },
138
- '&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
139
- background: getToken('extend.colors.primary.600'),
104
+ '.cm-placeholder': {
105
+ color: 'var(--dx-subdued)',
140
106
  },
141
107
 
142
- //
143
- // Search
144
- //
108
+ /**
109
+ * Selection (layer).
110
+ */
111
+ '.cm-selectionBackground': {
112
+ background: 'var(--dx-cmSelection)',
113
+ },
145
114
 
146
- '&light .cm-searchMatch': {
147
- backgroundColor: getToken('extend.colors.yellow.100'),
115
+ /**
116
+ * Search.
117
+ * NOTE: Matches comment.
118
+ */
119
+ '.cm-searchMatch': {
120
+ margin: '0 -3px',
121
+ padding: '3px',
122
+ borderRadius: '3px',
123
+ background: 'var(--dx-cmHighlightSurface)',
124
+ color: 'var(--dx-cmHighlight)',
148
125
  },
149
- '&dark .cm-searchMatch': {
150
- backgroundColor: getToken('extend.colors.yellow.700'),
126
+ '.cm-searchMatch-selected': {
127
+ textDecoration: 'underline',
151
128
  },
152
129
 
153
- //
154
- // link
155
- //
130
+ /**
131
+ * Link.
132
+ */
156
133
  '.cm-link': {
157
134
  textDecorationLine: 'underline',
158
135
  textDecorationThickness: '1px',
159
136
  textUnderlineOffset: '2px',
160
137
  borderRadius: '.125rem',
161
- fontFamily: getToken('fontFamily.body'),
162
138
  },
163
- '&light .cm-link > span': {
164
- color: getToken('extend.colors.primary.600'),
165
- },
166
- '&dark .cm-link > span': {
167
- color: getToken('extend.colors.primary.400'),
139
+ '.cm-link > span': {
140
+ color: 'var(--dx-accentText)',
168
141
  },
169
142
 
170
- //
171
- // tooltip
172
- //
173
- '.cm-tooltip': {},
174
- '&light .cm-tooltip': {
175
- background: `${getToken('extend.colors.neutral.100')} !important`,
176
- },
177
- '&dark .cm-tooltip': {
178
- background: `${getToken('extend.colors.neutral.900')} !important`,
143
+ /**
144
+ * Tooltip.
145
+ */
146
+ '.cm-tooltip': {
147
+ background: 'var(--dx-base)',
179
148
  },
180
149
  '.cm-tooltip-below': {},
181
150
 
182
- //
183
- // autocomplete
184
- // https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
185
- //
151
+ /**
152
+ * Autocomplete.
153
+ * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
154
+ */
186
155
  '.cm-tooltip.cm-tooltip-autocomplete': {
187
156
  marginTop: '4px',
188
157
  marginLeft: '-3px',
@@ -195,31 +164,27 @@ export const defaultTheme: ThemeStyles = {
195
164
  '.cm-tooltip.cm-tooltip-autocomplete > ul > completion-section': {
196
165
  paddingLeft: '4px !important',
197
166
  borderBottom: 'none !important',
198
- color: getToken('extend.colors.primary.500'),
167
+ color: 'var(--dx-accentText)',
199
168
  },
200
169
  '.cm-tooltip.cm-completionInfo': {
201
- border: getToken('extend.colors.neutral.500'),
202
170
  width: '360px !important',
203
171
  margin: '-10px 1px 0 1px',
204
172
  padding: '8px !important',
173
+ borderColor: 'var(--dx-separator)',
205
174
  },
206
175
  '.cm-completionIcon': {
207
176
  display: 'none',
208
177
  },
209
178
  '.cm-completionLabel': {
210
- fontFamily: getToken('fontFamily.body'),
179
+ fontFamily: fontBody,
211
180
  },
212
181
  '.cm-completionMatchedText': {
213
182
  textDecoration: 'none !important',
214
183
  opacity: 0.5,
215
184
  },
216
185
 
217
- // TODO(burdon): Override vars --cm-background.
218
- // https://www.npmjs.com/package/codemirror-theme-vars
219
-
220
186
  /**
221
187
  * Panels
222
- * TODO(burdon): Needs styling attention (esp. dark mode).
223
188
  * https://github.com/codemirror/search/blob/main/src/search.ts#L745
224
189
  *
225
190
  * Find/replace panel.
@@ -233,76 +198,49 @@ export const defaultTheme: ThemeStyles = {
233
198
  * </div>
234
199
  * </div
235
200
  */
201
+ // TODO(burdon): Apply react-ui-theme or replace panel.
236
202
  '.cm-panels': {},
237
203
  '.cm-panel': {
238
- fontFamily: getToken('fontFamily.body'),
204
+ fontFamily: fontBody,
205
+ backgroundColor: 'var(--dx-base)',
239
206
  },
240
- '.cm-panel input[type=checkbox]': {
241
- marginRight: '0.4rem !important',
207
+ '.cm-panel input, .cm-panel button, .cm-panel label': {
208
+ fontFamily: fontBody,
209
+ fontSize: '14px',
210
+ all: 'unset',
211
+ margin: '3px !important',
212
+ padding: '2px 6px !important',
213
+ outline: '1px solid transparent',
242
214
  },
243
- '&light .cm-panel': {
244
- background: getToken('extend.colors.neutral.50'),
215
+ '.cm-panel input, .cm-panel button': {
216
+ backgroundColor: 'var(--dx-input)',
245
217
  },
246
- '&dark .cm-panel': {
247
- background: getToken('extend.colors.neutral.850'),
218
+ '.cm-panel input:focus, .cm-panel button:focus': {
219
+ outline: '1px solid var(--dx-accentFocusIndicator)',
248
220
  },
249
- '.cm-button': {
250
- margin: '4px',
251
- fontFamily: getToken('fontFamily.body'),
252
- backgroundImage: 'none',
253
- border: 'none',
254
- '&:active': {
255
- backgroundImage: 'none',
256
- },
221
+ '.cm-panel label': {
222
+ display: 'inline-flex',
223
+ alignItems: 'center',
224
+ cursor: 'pointer',
257
225
  },
258
- '&light .cm-button': {
259
- background: getToken('extend.colors.neutral.100'),
260
- '&:hover': {
261
- background: getToken('extend.colors.neutral.200'),
262
- },
263
- '&:active': {
264
- background: getToken('extend.colors.neutral.300'),
265
- },
226
+ '.cm-panel input.cm-textfield': {},
227
+ '.cm-panel input[type=checkbox]': {
228
+ width: '8px',
229
+ height: '8px',
230
+ marginRight: '6px !important',
231
+ padding: '2px !important',
232
+ color: 'var(--dx-accentFocusIndicator)',
266
233
  },
267
- '&dark .cm-button': {
268
- background: getToken('extend.colors.neutral.800'),
234
+ '.cm-panel button': {
269
235
  '&:hover': {
270
- background: getToken('extend.colors.neutral.700'),
236
+ backgroundColor: 'var(--dx-accentSurfaceHover) !important',
271
237
  },
272
238
  '&:active': {
273
- background: getToken('extend.colors.neutral.600'),
239
+ backgroundColor: 'var(--dx-accentSurfaceHover)',
274
240
  },
275
241
  },
276
-
277
- // TODO(burdon): Factor out element specific logic.
278
-
279
- //
280
- // table
281
- //
282
- '.cm-table *': {
283
- fontFamily: `${getToken('fontFamily.mono')} !important`,
284
- textDecoration: 'none !important',
285
- },
286
- '.cm-table-head': {
287
- padding: '2px 16px 2px 0px',
288
- textAlign: 'left',
289
- borderBottom: `1px solid ${getToken('extend.colors.primary.500')}`,
290
- color: getToken('extend.colors.neutral.500'),
291
- },
292
- '.cm-table-cell': {
293
- padding: '2px 16px 2px 0px',
294
- },
295
-
296
- //
297
- // image
298
- //
299
- '.cm-image': {
300
- display: 'block',
301
- height: '0',
302
- },
303
- '.cm-image.cm-loaded-image': {
304
- height: 'auto',
305
- borderTop: '0.5rem solid transparent',
306
- borderBottom: '0.5rem solid transparent',
242
+ '.cm-panel.cm-search': {
243
+ padding: '4px',
244
+ borderTop: '1px solid var(--dx-separator)',
307
245
  },
308
246
  };
@@ -4,15 +4,15 @@
4
4
 
5
5
  import get from 'lodash.get';
6
6
 
7
- import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
8
-
9
- const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
7
+ import { tokens } from '@dxos/react-ui-theme';
10
8
 
11
9
  /**
12
- * @deprecated
13
- * Replace with CSS vars.
10
+ * Returns the tailwind token value.
14
11
  */
15
- export const getToken = (path: string, defaultValue?: string | string[]): string => {
12
+ const getToken = (path: string, defaultValue?: string | string[]): string => {
16
13
  const value = get(tokens, path, defaultValue);
17
14
  return value?.toString() ?? '';
18
15
  };
16
+
17
+ export const fontBody = getToken('fontFamily.body');
18
+ export const fontMono = getToken('fontFamily.mono');
@@ -30,6 +30,7 @@ export default [
30
30
  'view mode label': 'Editor view',
31
31
  'preview mode label': 'Live preview',
32
32
  'readonly mode label': 'Read only',
33
+ 'search label': 'Search',
33
34
  'source mode label': 'Source',
34
35
  },
35
36
  },
@@ -1,9 +0,0 @@
1
- import { ViewPlugin, type ViewUpdate } from '@codemirror/view';
2
- /**
3
- * Formats pasted URLs as markdown links and images.
4
- */
5
- export declare const linkPastePlugin: ViewPlugin<{
6
- update(update: ViewUpdate): void;
7
- }>;
8
- export declare const createLinkLabel: (url: URL) => string;
9
- //# sourceMappingURL=link-paste.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"link-paste.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/markdown/link-paste.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AAGjF;;GAEG;AACH,eAAO,MAAM,eAAe;mBAET,UAAU;EA2B5B,CAAC;AAmBF,eAAO,MAAM,eAAe,QAAS,GAAG,KAAG,MAW1C,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=link-paste.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"link-paste.test.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/markdown/link-paste.test.ts"],"names":[],"mappings":""}
@@ -1,107 +0,0 @@
1
- //
2
- // Copyright 2024 DXOS.org
3
- //
4
-
5
- import { syntaxTree } from '@codemirror/language';
6
- import { type EditorState, Transaction } from '@codemirror/state';
7
- import { ViewPlugin, type ViewUpdate, type PluginValue } from '@codemirror/view';
8
- import { type SyntaxNode } from '@lezer/common';
9
-
10
- /**
11
- * Formats pasted URLs as markdown links and images.
12
- */
13
- export const linkPastePlugin = ViewPlugin.fromClass(
14
- class implements PluginValue {
15
- update(update: ViewUpdate) {
16
- for (const tr of update.transactions) {
17
- const event = tr.annotation(Transaction.userEvent);
18
- if (event === 'input.paste') {
19
- const changes = tr.changes;
20
- if (changes.empty) {
21
- return;
22
- }
23
-
24
- changes.iterChangedRanges((fromA, toA, fromB, toB) => {
25
- const insertedUrl = getValidUrl(update.view.state.sliceDoc(fromB, toB));
26
- if (insertedUrl && isValidPosition(update.view.state, fromB)) {
27
- // We might be pasting over an existing text.
28
- const replacedText = tr.startState.sliceDoc(fromA, toA);
29
- setTimeout(() => {
30
- update.view.dispatch(
31
- update.view.state.update({
32
- changes: { from: fromA, to: toB, insert: createLink(insertedUrl, replacedText) },
33
- }),
34
- );
35
- });
36
- }
37
- });
38
- }
39
- }
40
- }
41
- },
42
- );
43
-
44
- const createLink = (url: URL, label: string): string => {
45
- // Check if image.
46
- // Example: https://dxos.network/dxos-logotype-blue.png
47
- const { host, pathname } = url;
48
- const [, extension] = pathname.split('.');
49
- const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'];
50
- if (imageExtensions.includes(extension)) {
51
- return `![${label || host}](${url})`;
52
- }
53
-
54
- if (!label) {
55
- label = createLinkLabel(url);
56
- }
57
-
58
- return `[${label}](${url})`;
59
- };
60
-
61
- export const createLinkLabel = (url: URL): string => {
62
- let { protocol, host, pathname } = url;
63
- if (protocol === 'http:' || protocol === 'https:') {
64
- protocol = '';
65
- }
66
-
67
- // NOTE(Zan): Consult: https://github.com/dxos/dxos/issues/7331 before changing this.
68
- // Remove 'www.' if at the beginning of the URL
69
- host = host.replace(/^www\./, '');
70
-
71
- return [protocol, host].filter(Boolean).join('//') + (pathname !== '/' ? pathname : '');
72
- };
73
-
74
- /**
75
- * Returns a valid URL if appropriate for a link.
76
- */
77
- const getValidUrl = (str: string): URL | undefined => {
78
- const validProtocols = ['http:', 'https:', 'mailto:', 'tel:'];
79
- try {
80
- const url = new URL(str);
81
- if (!validProtocols.includes(url.protocol)) {
82
- return undefined;
83
- }
84
-
85
- return url;
86
- } catch (_err) {
87
- return undefined;
88
- }
89
- };
90
-
91
- /**
92
- * Traverses the syntax tree upwards from the position.
93
- */
94
- const isValidPosition = (state: EditorState, pos: number): boolean => {
95
- const invalidPositions = new Set(['Link', 'LinkMark', 'Code', 'FencedCode']);
96
- const tree = syntaxTree(state);
97
- let node: SyntaxNode | null = tree.resolveInner(pos, -1);
98
- while (node) {
99
- if (invalidPositions.has(node.name)) {
100
- return false;
101
- }
102
-
103
- node = node.parent;
104
- }
105
-
106
- return true;
107
- };