@dxos/react-ui-editor 0.3.11-main.69484a6 → 0.3.11-main.69d4f1c

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 (40) hide show
  1. package/dist/lib/browser/index.mjs +1183 -168
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  5. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/Toolbar/Toolbar.d.ts +8 -4
  7. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  8. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +5 -3
  9. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  10. package/dist/types/src/extensions/comments.d.ts +5 -1
  11. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  12. package/dist/types/src/extensions/markdown/formatting.d.ts +54 -10
  13. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  14. package/dist/types/src/extensions/markdown/formatting.test.d.ts +3 -0
  15. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -0
  16. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  17. package/dist/types/src/hooks/useTextModel.d.ts +12 -0
  18. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  19. package/dist/types/src/index.d.ts +1 -1
  20. package/dist/types/src/index.d.ts.map +1 -1
  21. package/dist/types/src/themes/default.d.ts.map +1 -1
  22. package/package.json +24 -24
  23. package/src/components/TextEditor/MarkdownEditor.stories.tsx +2 -2
  24. package/src/components/TextEditor/TextEditor.tsx +3 -4
  25. package/src/components/TextEditor/hooks.stories.tsx +6 -1
  26. package/src/components/Toolbar/Toolbar.stories.tsx +15 -3
  27. package/src/components/Toolbar/Toolbar.tsx +108 -29
  28. package/src/extensions/awareness.ts +4 -1
  29. package/src/extensions/blast.ts +4 -1
  30. package/src/extensions/comments.ts +14 -4
  31. package/src/extensions/markdown/formatting.test.ts +481 -0
  32. package/src/extensions/markdown/formatting.ts +1080 -70
  33. package/src/extensions/markdown/link.ts +8 -2
  34. package/src/hooks/useActionHandler.ts +47 -16
  35. package/src/hooks/useTextModel.ts +17 -1
  36. package/src/index.ts +1 -1
  37. package/src/themes/default.ts +7 -4
  38. package/dist/types/src/components/TextEditor/comments.stories.d.ts +0 -26
  39. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
  40. package/src/components/TextEditor/comments.stories.tsx +0 -357
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.3.11-main.69484a6",
3
+ "version": "0.3.11-main.69d4f1c",
4
4
  "description": "Document editing experience within a DXOS shell.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -23,7 +23,7 @@
23
23
  "@codemirror/state": "^6.4.0",
24
24
  "@codemirror/theme-one-dark": "^6.1.2",
25
25
  "@codemirror/view": "^6.23.0",
26
- "@fluentui/react-tabster": "^9.17.4",
26
+ "@fluentui/react-tabster": "^9.18.0",
27
27
  "@lezer/common": "^1.2.1",
28
28
  "@lezer/highlight": "^1.2.0",
29
29
  "@lezer/markdown": "^1.2.0",
@@ -36,20 +36,20 @@
36
36
  "lodash.pick": "^4.4.0",
37
37
  "lodash.sortby": "^4.7.0",
38
38
  "style-mod": "^4.1.0",
39
- "@dxos/async": "0.3.11-main.69484a6",
40
- "@dxos/context": "0.3.11-main.69484a6",
41
- "@dxos/automerge": "0.3.11-main.69484a6",
42
- "@dxos/debug": "0.3.11-main.69484a6",
43
- "@dxos/echo-schema": "0.3.11-main.69484a6",
44
- "@dxos/display-name": "0.3.11-main.69484a6",
45
- "@dxos/invariant": "0.3.11-main.69484a6",
46
- "@dxos/log": "0.3.11-main.69484a6",
47
- "@dxos/protocols": "0.3.11-main.69484a6",
48
- "@dxos/react-async": "0.3.11-main.69484a6",
49
- "@dxos/react-ui-theme": "0.3.11-main.69484a6",
50
- "@dxos/react-ui": "0.3.11-main.69484a6",
51
- "@dxos/text-model": "0.3.11-main.69484a6",
52
- "@dxos/util": "0.3.11-main.69484a6"
39
+ "@dxos/async": "0.3.11-main.69d4f1c",
40
+ "@dxos/automerge": "0.3.11-main.69d4f1c",
41
+ "@dxos/context": "0.3.11-main.69d4f1c",
42
+ "@dxos/debug": "0.3.11-main.69d4f1c",
43
+ "@dxos/display-name": "0.3.11-main.69d4f1c",
44
+ "@dxos/echo-schema": "0.3.11-main.69d4f1c",
45
+ "@dxos/invariant": "0.3.11-main.69d4f1c",
46
+ "@dxos/log": "0.3.11-main.69d4f1c",
47
+ "@dxos/react-async": "0.3.11-main.69d4f1c",
48
+ "@dxos/react-ui": "0.3.11-main.69d4f1c",
49
+ "@dxos/protocols": "0.3.11-main.69d4f1c",
50
+ "@dxos/text-model": "0.3.11-main.69d4f1c",
51
+ "@dxos/react-ui-theme": "0.3.11-main.69d4f1c",
52
+ "@dxos/util": "0.3.11-main.69d4f1c"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@automerge/automerge-repo-network-broadcastchannel": "^1.1.0",
@@ -77,19 +77,19 @@
77
77
  "vite-plugin-top-level-await": "^1.4.1",
78
78
  "vite-plugin-wasm": "^3.3.0",
79
79
  "vitest": "^1.2.1",
80
- "@dxos/config": "0.3.11-main.69484a6",
81
- "@dxos/echo-typegen": "0.3.11-main.69484a6",
82
- "@dxos/keyboard": "0.3.11-main.69484a6",
83
- "@dxos/echo-signals": "0.3.11-main.69484a6",
84
- "@dxos/react-client": "0.3.11-main.69484a6",
85
- "@dxos/react-ui": "0.3.11-main.69484a6",
86
- "@dxos/storybook-utils": "0.3.11-main.69484a6"
80
+ "@dxos/config": "0.3.11-main.69d4f1c",
81
+ "@dxos/echo-signals": "0.3.11-main.69d4f1c",
82
+ "@dxos/echo-typegen": "0.3.11-main.69d4f1c",
83
+ "@dxos/keyboard": "0.3.11-main.69d4f1c",
84
+ "@dxos/react-client": "0.3.11-main.69d4f1c",
85
+ "@dxos/storybook-utils": "0.3.11-main.69d4f1c",
86
+ "@dxos/react-ui": "0.3.11-main.69d4f1c"
87
87
  },
88
88
  "peerDependencies": {
89
89
  "@phosphor-icons/react": "^2.0.5",
90
90
  "react": "^18.2.0",
91
91
  "react-dom": "^18.2.0",
92
- "@dxos/react-client": "0.3.11-main.69484a6"
92
+ "@dxos/react-client": "0.3.11-main.69d4f1c"
93
93
  },
94
94
  "publishConfig": {
95
95
  "access": "public"
@@ -350,9 +350,9 @@ export const Comments = {
350
350
  extensions={[
351
351
  comments({
352
352
  onHover: onCommentsHover,
353
- onCreate: (range) => {
353
+ onCreate: ({ cursor }) => {
354
354
  const id = PublicKey.random().toHex();
355
- setComments((commentRanges) => [...commentRanges, { id, cursor: range }]);
355
+ setComments((commentRanges) => [...commentRanges, { id, cursor }]);
356
356
  return id;
357
357
  },
358
358
  onSelect: (state) => {
@@ -20,7 +20,7 @@ import React, {
20
20
 
21
21
  import { log } from '@dxos/log';
22
22
  import { useThemeContext } from '@dxos/react-ui';
23
- import { attentionSurface, mx } from '@dxos/react-ui-theme';
23
+ import { focusRing, mx } from '@dxos/react-ui-theme';
24
24
  import { isNotFalsy } from '@dxos/util';
25
25
 
26
26
  import { createBasicBundle, createMarkdownExtensions } from '../../extensions';
@@ -258,11 +258,10 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
258
258
 
259
259
  export const defaultSlots: TextEditorSlots = {
260
260
  root: {
261
- // TODO(burdon): Add focusRing by default/as property?
262
- className: mx('flex flex-col grow overflow-y-auto', attentionSurface),
261
+ className: mx('grow', focusRing),
263
262
  },
264
263
  editor: {
265
- className: 'h-full p-2',
264
+ className: 'bs-full',
266
265
  },
267
266
  };
268
267
 
@@ -21,6 +21,7 @@ import {
21
21
  link,
22
22
  table,
23
23
  tasklist,
24
+ useFormattingState,
24
25
  } from '../../extensions';
25
26
  import { createDataExtensions, createThemeExtensions, useActionHandler, useTextEditor } from '../../hooks';
26
27
  import { markdownTheme } from '../../themes';
@@ -41,6 +42,7 @@ type StoryProps = {
41
42
 
42
43
  const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
43
44
  const { themeMode } = useThemeContext();
45
+ const [formattingState, trackFormatting] = useFormattingState();
44
46
  const { parentRef, view } = useTextEditor({
45
47
  autoFocus,
46
48
  doc,
@@ -69,16 +71,19 @@ const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
69
71
  link(),
70
72
  table(),
71
73
  tasklist(),
74
+ trackFormatting,
72
75
  ],
73
76
  });
74
77
 
75
78
  const handleAction = useActionHandler(view);
76
79
 
80
+ // FIXME This doesn't update the state on view changes. Also not
81
+ // sure if view is even guaranteed to exist at this point.
77
82
  return (
78
83
  <div className={mx(fixedInsetFlexLayout, groupSurface)}>
79
84
  <div className='flex h-full justify-center'>
80
85
  <div className='flex flex-col h-full w-[800px]'>
81
- <Toolbar.Root onAction={handleAction}>
86
+ <Toolbar.Root onAction={handleAction} state={formattingState}>
82
87
  <Toolbar.Markdown />
83
88
  </Toolbar.Root>
84
89
 
@@ -13,7 +13,17 @@ import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
13
13
  import { withTheme } from '@dxos/storybook-utils';
14
14
 
15
15
  import { Toolbar } from './Toolbar';
16
- import { code, comments, formatting, heading, image, table, tasklist, useComments } from '../../extensions';
16
+ import {
17
+ code,
18
+ comments,
19
+ formatting,
20
+ heading,
21
+ image,
22
+ table,
23
+ tasklist,
24
+ useComments,
25
+ useFormattingState,
26
+ } from '../../extensions';
17
27
  import { type Comment, useActionHandler, useEditorView, useTextModel } from '../../hooks';
18
28
  import { MarkdownEditor } from '../TextEditor';
19
29
 
@@ -24,11 +34,12 @@ const Story: FC<{ content: string }> = ({ content }) => {
24
34
  const [_comments, setComments] = useState<Comment[]>([]);
25
35
  const [editorRef, editorView] = useEditorView();
26
36
  const model = useTextModel({ text: item.text });
37
+ const [formattingState, formattingObserver] = useFormattingState();
27
38
  const extensions = useMemo(
28
39
  () => [
29
40
  code(),
30
41
  comments({
31
- onCreate: (cursor) => {
42
+ onCreate: ({ cursor }) => {
32
43
  const id = PublicKey.random().toHex();
33
44
  setComments((comments) => [...comments, { id, cursor }]);
34
45
  return id;
@@ -39,6 +50,7 @@ const Story: FC<{ content: string }> = ({ content }) => {
39
50
  image(),
40
51
  table(),
41
52
  tasklist(),
53
+ formattingObserver,
42
54
  ],
43
55
  [],
44
56
  );
@@ -54,7 +66,7 @@ const Story: FC<{ content: string }> = ({ content }) => {
54
66
  <div className={mx(fixedInsetFlexLayout, groupSurface)}>
55
67
  <div className='flex h-full justify-center'>
56
68
  <div className='flex flex-col h-full w-[800px]'>
57
- <Toolbar.Root onAction={handleAction}>
69
+ <Toolbar.Root onAction={handleAction} state={formattingState}>
58
70
  <Toolbar.Markdown />
59
71
  <Toolbar.Separator />
60
72
  <Toolbar.Extended />
@@ -4,15 +4,14 @@
4
4
 
5
5
  import {
6
6
  type Icon,
7
- At,
8
7
  CaretRight,
9
8
  ChatText,
10
9
  Code,
11
- Image,
12
10
  Link,
13
11
  ListBullets,
14
12
  ListChecks,
15
13
  ListNumbers,
14
+ Paragraph,
16
15
  TextStrikethrough,
17
16
  Table,
18
17
  TextB,
@@ -22,19 +21,22 @@ import { createContext } from '@radix-ui/react-context';
22
21
  import React, { type PropsWithChildren } from 'react';
23
22
 
24
23
  import { Button, type ButtonProps, DensityProvider, Select } from '@dxos/react-ui';
25
- import { getSize } from '@dxos/react-ui-theme';
24
+ import { getSize, mx } from '@dxos/react-ui-theme';
25
+
26
+ import { type Formatting } from '../../extensions';
26
27
 
27
28
  // TODO(burdon): Revert to string to make extensible?
28
29
  export type ActionType =
29
30
  | 'blockquote'
30
- | 'bold'
31
+ | 'strong'
31
32
  | 'codeblock'
32
33
  | 'comment'
33
34
  | 'heading'
34
35
  | 'image'
35
- | 'italic'
36
+ | 'emphasis'
37
+ | 'code'
36
38
  | 'link'
37
- | 'list'
39
+ | 'list-bullet'
38
40
  | 'list-ordered'
39
41
  | 'list-tasks'
40
42
  | 'mention'
@@ -48,16 +50,17 @@ export type Action = {
48
50
  };
49
51
 
50
52
  export type ToolbarProps = PropsWithChildren<{
53
+ state: Formatting | null;
51
54
  onAction?: (action: Action) => void;
52
55
  }>;
53
56
 
54
57
  const [ToolbarContextProvider, useToolbarContext] = createContext<ToolbarProps>('Toolbar');
55
58
 
56
- const ToolbarRoot = ({ children, onAction }: ToolbarProps) => {
59
+ const ToolbarRoot = ({ children, onAction, state }: ToolbarProps) => {
57
60
  return (
58
- <ToolbarContextProvider onAction={onAction}>
61
+ <ToolbarContextProvider onAction={onAction} state={state}>
59
62
  <DensityProvider density='fine'>
60
- <div role='toolbar' className='flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden'>
63
+ <div role='toolbar' className='flex w-full shrink-0 p-1 gap-2 items-center whitespace-nowrap overflow-hidden'>
61
64
  {children}
62
65
  </div>
63
66
  </DensityProvider>
@@ -67,20 +70,32 @@ const ToolbarRoot = ({ children, onAction }: ToolbarProps) => {
67
70
 
68
71
  type ToolbarButtonProps = {
69
72
  Icon: Icon;
70
- onClick: () => Action | undefined;
73
+ disable?: (state: Formatting) => boolean;
74
+ getState?: (state: Formatting) => boolean;
75
+ onClick: (state: Formatting | null) => Action | undefined;
71
76
  } & NonNullable<Pick<ButtonProps, 'title'>>;
72
77
 
73
- const ToolbarButton = ({ Icon, onClick, title }: ToolbarButtonProps) => {
74
- const { onAction } = useToolbarContext('ToolbarButton');
75
- const handleClick = () => {
76
- const action = onClick();
78
+ const ToolbarButton = ({ Icon, onClick, title, getState, disable }: ToolbarButtonProps) => {
79
+ const { onAction, state } = useToolbarContext('ToolbarButton');
80
+ const active = getState && state ? getState(state) : false;
81
+ const disabled = disable && state ? disable(state) : false;
82
+
83
+ const handleClick = (event: React.MouseEvent) => {
84
+ const action = onClick(state);
77
85
  if (action) {
78
86
  onAction?.(action);
87
+ event.preventDefault();
79
88
  }
80
89
  };
81
90
 
82
91
  return (
83
- <Button variant='ghost' classNames='p-2' onClick={handleClick} title={title}>
92
+ <Button
93
+ variant='ghost'
94
+ classNames={mx('p-2', active && 'ring-[1px]')}
95
+ onMouseDown={handleClick}
96
+ title={title}
97
+ disabled={disabled}
98
+ >
84
99
  <Icon className={getSize(5)} />
85
100
  </Button>
86
101
  );
@@ -89,10 +104,19 @@ const ToolbarButton = ({ Icon, onClick, title }: ToolbarButtonProps) => {
89
104
  const ToolbarSeparator = () => <div className='grow' />;
90
105
 
91
106
  const MarkdownHeading = () => {
92
- const { onAction } = useToolbarContext('MarkdownFormatting');
107
+ const { onAction, state } = useToolbarContext('MarkdownFormatting');
108
+ const blockType = state ? state.blockType : 'paragraph';
109
+ const header = blockType && /heading(\d)/.exec(blockType);
110
+ const value = header ? header[1] : blockType === 'paragraph' || !blockType ? '0' : null;
93
111
  return (
94
- <Select.Root defaultValue='0' onValueChange={(value) => onAction?.({ type: 'heading', data: parseInt(value) })}>
95
- <Select.TriggerButton classNames='w-[8rem]' />
112
+ <Select.Root
113
+ disabled={value === null}
114
+ value={value ?? '0'}
115
+ onValueChange={(value) => onAction?.({ type: 'heading', data: parseInt(value) })}
116
+ >
117
+ <Select.TriggerButton>
118
+ <Paragraph className={getSize(5)} />
119
+ </Select.TriggerButton>
96
120
  <Select.Portal>
97
121
  <Select.Content>
98
122
  <Select.Viewport>
@@ -111,33 +135,88 @@ const MarkdownHeading = () => {
111
135
 
112
136
  const MarkdownStyles = () => (
113
137
  <div role='none'>
114
- <ToolbarButton Icon={TextB} title='Bold' onClick={() => ({ type: 'bold' })} />
115
- <ToolbarButton Icon={TextItalic} title='Italic' onClick={() => ({ type: 'italic' })} />
116
- <ToolbarButton Icon={TextStrikethrough} title='Strike-through' onClick={() => ({ type: 'strikethrough' })} />
138
+ <ToolbarButton
139
+ Icon={TextB}
140
+ title='String'
141
+ disable={(s) => s.blockType === 'codeblock'}
142
+ getState={(s) => s.strong}
143
+ onClick={(s) => ({ type: 'strong', data: s ? !s.strong : null })}
144
+ />
145
+ <ToolbarButton
146
+ Icon={TextItalic}
147
+ title='Emphasis'
148
+ disable={(s) => s.blockType === 'codeblock'}
149
+ getState={(s) => s.emphasis}
150
+ onClick={(s) => ({ type: 'emphasis', data: s ? !s.emphasis : null })}
151
+ />
152
+ <ToolbarButton
153
+ Icon={TextStrikethrough}
154
+ title='Strike-through'
155
+ disable={(s) => s.blockType === 'codeblock'}
156
+ getState={(s) => s.strikethrough}
157
+ onClick={(s) => ({ type: 'strikethrough', data: s ? !s.strikethrough : null })}
158
+ />
159
+ <ToolbarButton
160
+ Icon={Code}
161
+ title='Inline code'
162
+ disable={(s) => s.blockType === 'codeblock'}
163
+ getState={(s) => s.code}
164
+ onClick={(s) => ({ type: 'code', data: s ? !s.code : null })}
165
+ />
117
166
  </div>
118
167
  );
119
168
 
120
169
  const MarkdownLists = () => (
121
170
  <div role='none'>
122
- <ToolbarButton Icon={ListBullets} title='Bullet list' onClick={() => ({ type: 'list' })} />
123
- <ToolbarButton Icon={ListNumbers} title='Numbered list' onClick={() => ({ type: 'list-ordered' })} />
124
- <ToolbarButton Icon={ListChecks} title='Task list' onClick={() => ({ type: 'list-tasks' })} />
171
+ <ToolbarButton
172
+ Icon={ListBullets}
173
+ title='Bullet list'
174
+ getState={(s) => s.listStyle === 'bullet'}
175
+ onClick={(s) => ({ type: 'list-bullet', data: s ? s.listStyle !== 'bullet' : null })}
176
+ />
177
+ <ToolbarButton
178
+ Icon={ListNumbers}
179
+ title='Numbered list'
180
+ getState={(s) => s.listStyle === 'ordered'}
181
+ onClick={(s) => ({ type: 'list-ordered', data: s ? s.listStyle !== 'ordered' : null })}
182
+ />
183
+ <ToolbarButton
184
+ Icon={ListChecks}
185
+ title='Task list'
186
+ getState={(s) => s.listStyle === 'task'}
187
+ onClick={(s) => ({ type: 'list-tasks', data: s ? s.listStyle !== 'task' : null })}
188
+ />
125
189
  </div>
126
190
  );
127
191
 
128
192
  const MarkdownBlocks = () => (
129
193
  <div role='none'>
130
- <ToolbarButton Icon={CaretRight} title='Block quite' onClick={() => ({ type: 'blockquote' })} />
131
- <ToolbarButton Icon={Code} title='Code block' onClick={() => ({ type: 'codeblock' })} />
194
+ <ToolbarButton
195
+ Icon={CaretRight}
196
+ title='Block quote'
197
+ getState={(s) => s.blockquote}
198
+ onClick={(s) => ({ type: 'blockquote', data: s ? !s.blockquote : null })}
199
+ />
200
+ <ToolbarButton
201
+ Icon={Code}
202
+ title='Code block'
203
+ getState={(s) => s.blockType === 'codeblock'}
204
+ onClick={(s) => ({ type: 'codeblock', data: s ? s.blockType !== 'codeblock' : null })}
205
+ />
132
206
  <ToolbarButton Icon={Table} title='Table' onClick={() => ({ type: 'table' })} />
133
207
  </div>
134
208
  );
135
209
 
136
210
  const MarkdownLinks = () => (
137
211
  <div role='none'>
138
- <ToolbarButton Icon={Link} title='Link' onClick={() => ({ type: 'link' })} />
139
- <ToolbarButton Icon={At} title='Mention' onClick={() => ({ type: 'mention' })} />
140
- <ToolbarButton Icon={Image} title='Image' onClick={() => ({ type: 'image' })} />
212
+ <ToolbarButton
213
+ Icon={Link}
214
+ title='Link'
215
+ getState={(s) => s.link}
216
+ onClick={(s) => ({ type: 'link', data: s ? !s.link : null })}
217
+ />
218
+ {/* <ToolbarButton Icon={At} title='Mention' onClick={() => ({ type: 'mention' })} /> */}
219
+ {/* <ToolbarButton Icon={Image} title='Image' onClick={() => ({ type: 'image' })} /> */}
141
220
  </div>
142
221
  );
143
222
 
@@ -207,7 +207,10 @@ export class RemoteSelectionsDecorator implements PluginValue {
207
207
  }
208
208
 
209
209
  class RemoteCaretWidget extends WidgetType {
210
- constructor(private readonly _name: string, private readonly _color: string) {
210
+ constructor(
211
+ private readonly _name: string,
212
+ private readonly _color: string,
213
+ ) {
211
214
  super();
212
215
  }
213
216
 
@@ -125,7 +125,10 @@ class Blaster {
125
125
 
126
126
  _lastPoint = { x: 0, y: 0 };
127
127
 
128
- constructor(private readonly _node: HTMLElement, private readonly _options: BlastOptions) {
128
+ constructor(
129
+ private readonly _node: HTMLElement,
130
+ private readonly _options: BlastOptions,
131
+ ) {
129
132
  this._effect = this._options.effect === 1 ? new Effect1(_options) : new Effect2(_options);
130
133
  }
131
134
 
@@ -18,12 +18,22 @@ import { callbackWrapper } from '../util';
18
18
 
19
19
  // TODO(burdon): Reconcile with theme.
20
20
  const styles = EditorView.baseTheme({
21
- '& .cm-comment': {
21
+ '&light .cm-comment, &light .cm-comment-current': { mixBlendMode: 'darken' },
22
+ '&dark .cm-comment, &dark .cm-comment-current': { mixBlendMode: 'plus-lighter' },
23
+ '&light .cm-comment': {
22
24
  backgroundColor: getToken('extend.colors.yellow.50'),
23
25
  },
24
- '& .cm-comment-current': {
26
+ '&light .cm-comment-current': {
25
27
  backgroundColor: getToken('extend.colors.yellow.100'),
26
28
  },
29
+ '&dark .cm-comment': {
30
+ color: getToken('extend.colors.yellow.50'),
31
+ backgroundColor: getToken('extend.colors.yellow.900'),
32
+ },
33
+ '&dark .cm-comment-current': {
34
+ color: getToken('extend.colors.yellow.100'),
35
+ backgroundColor: getToken('extend.colors.yellow.950'),
36
+ },
27
37
  });
28
38
 
29
39
  const commentMark = Decoration.mark({ class: 'cm-comment' });
@@ -146,7 +156,7 @@ export type CommentsOptions = {
146
156
  /**
147
157
  * Called to create a new thread and return the thread id.
148
158
  */
149
- onCreate?: (cursor: string, location?: Rect | null) => string | undefined;
159
+ onCreate?: (params: { cursor: string; from: number; location?: Rect | null }) => string | undefined;
150
160
  /**
151
161
  * Selection cut/deleted.
152
162
  */
@@ -306,7 +316,7 @@ export const createComment: Command = (view) => {
306
316
  const cursor = Cursor.getCursorFromRange(view.state, { from, to });
307
317
  if (cursor) {
308
318
  // Create thread via callback.
309
- const id = options.onCreate?.(cursor, view.coordsAtPos(from));
319
+ const id = options.onCreate?.({ cursor, from, location: view.coordsAtPos(from) });
310
320
  if (id) {
311
321
  // Update range.
312
322
  view.dispatch({