@liveblocks/react-tiptap 2.12.1 → 2.12.2

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.
@@ -8,6 +8,7 @@ var core$1 = require('@tiptap/core');
8
8
  var Collaboration = require('@tiptap/extension-collaboration');
9
9
  var CollaborationCursor = require('@tiptap/extension-collaboration-cursor');
10
10
  var React = require('react');
11
+ var index_js = require('use-sync-external-store/shim/index.js');
11
12
  var yjs = require('yjs');
12
13
  var CommentsExtension = require('./comments/CommentsExtension.js');
13
14
  var MentionExtension = require('./mentions/MentionExtension.js');
@@ -15,6 +16,12 @@ var types = require('./types.js');
15
16
 
16
17
  const providersMap = /* @__PURE__ */ new Map();
17
18
  const docMap = /* @__PURE__ */ new Map();
19
+ const DEFAULT_OPTIONS = {
20
+ field: "default",
21
+ comments: true,
22
+ mentions: true,
23
+ offlineSupport_experimental: false
24
+ };
18
25
  const LiveblocksCollab = Collaboration.extend({
19
26
  onCreate() {
20
27
  if (!this.editor.extensionManager.extensions.find((e) => e.name === "doc")) {
@@ -41,15 +48,65 @@ const LiveblocksCollab = Collaboration.extend({
41
48
  }
42
49
  }
43
50
  });
44
- const useLiveblocksExtension = () => {
51
+ function useYjsProvider() {
52
+ const room = react.useRoom();
53
+ const subscribe = React.useCallback(
54
+ (onStoreChange) => {
55
+ return room[core.kInternal].yjsProviderDidChange.subscribe(onStoreChange);
56
+ },
57
+ [room]
58
+ );
59
+ const getSnapshot = React.useCallback(() => {
60
+ return room[core.kInternal].getYjsProvider();
61
+ }, [room]);
62
+ return index_js.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
63
+ }
64
+ function useIsEditorReady() {
65
+ const yjsProvider = useYjsProvider();
66
+ const getSnapshot = React.useCallback(() => {
67
+ const status = yjsProvider?.getStatus();
68
+ return status === "synchronizing" || status === "synchronized";
69
+ }, [yjsProvider]);
70
+ const subscribe = React.useCallback(
71
+ (callback) => {
72
+ if (yjsProvider === void 0)
73
+ return () => {
74
+ };
75
+ yjsProvider.on("status", callback);
76
+ return () => {
77
+ yjsProvider.off("status", callback);
78
+ };
79
+ },
80
+ [yjsProvider]
81
+ );
82
+ return index_js.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
83
+ }
84
+ const useLiveblocksExtension = (opts) => {
85
+ const options = {
86
+ ...DEFAULT_OPTIONS,
87
+ ...opts
88
+ };
89
+ const [editor, setEditor] = React.useState(null);
45
90
  const room = react.useRoom();
46
91
  react.useCommentsErrorListener((error) => {
47
92
  if (error instanceof _private.CreateThreadError) {
48
93
  }
49
94
  });
95
+ const isEditorReady = useIsEditorReady();
50
96
  const client = react.useClient();
51
97
  const store = _private.getUmbrellaStoreForClient(client);
52
98
  const roomId = room.id;
99
+ const yjsProvider = useYjsProvider();
100
+ React.useEffect(() => {
101
+ if (!isEditorReady || !yjsProvider || !options.initialContent || !editor)
102
+ return;
103
+ const ydoc = yjsProvider.getYDoc();
104
+ const hasContentSet = ydoc.getMap("liveblocks_config").get("hasContentSet");
105
+ if (!hasContentSet) {
106
+ ydoc.getMap("liveblocks_config").set("hasContentSet", true);
107
+ editor.commands.setContent(options.initialContent);
108
+ }
109
+ }, [isEditorReady, yjsProvider, options.initialContent, editor]);
53
110
  const reportTextEditorType = React.useCallback(
54
111
  (field) => {
55
112
  room[core.kInternal].reportTextEditor(core.TextEditorType.TipTap, field);
@@ -79,7 +136,13 @@ const useLiveblocksExtension = () => {
79
136
  return core$1.Extension.create({
80
137
  name: "liveblocksExtension",
81
138
  onCreate() {
82
- if (this.options.mentions && this.editor.extensionManager.extensions.find(
139
+ setEditor(this.editor);
140
+ if (this.editor.options.content) {
141
+ console.warn(
142
+ "[Liveblocks] Initial content must be set in the useLiveblocksExtension hook option. Remove content from your editor options."
143
+ );
144
+ }
145
+ if (options.mentions && this.editor.extensionManager.extensions.find(
83
146
  (e) => e.name.toLowerCase() === "mention"
84
147
  )) {
85
148
  console.warn(
@@ -105,7 +168,7 @@ const useLiveblocksExtension = () => {
105
168
  }
106
169
  })
107
170
  );
108
- if (this.options.comments) {
171
+ if (options.comments) {
109
172
  const commentMarkType = core$1.getMarkType(
110
173
  types.LIVEBLOCKS_COMMENT_MARK_TYPE,
111
174
  this.editor.schema
@@ -147,7 +210,7 @@ const useLiveblocksExtension = () => {
147
210
  })
148
211
  );
149
212
  }
150
- reportTextEditorType(this.options.field ?? "default");
213
+ reportTextEditorType(options.field ?? DEFAULT_OPTIONS.field);
151
214
  },
152
215
  onDestroy() {
153
216
  this.storage.unsubs.forEach((unsub) => unsub());
@@ -156,7 +219,12 @@ const useLiveblocksExtension = () => {
156
219
  if (!providersMap.has(room.id)) {
157
220
  const doc = new yjs.Doc();
158
221
  docMap.set(room.id, doc);
159
- providersMap.set(room.id, new yjs$1.LiveblocksYjsProvider(room, doc));
222
+ providersMap.set(
223
+ room.id,
224
+ new yjs$1.LiveblocksYjsProvider(room, doc, {
225
+ offlineSupport_experimental: options.offlineSupport_experimental
226
+ })
227
+ );
160
228
  }
161
229
  return {
162
230
  doc: docMap.get(room.id),
@@ -164,27 +232,20 @@ const useLiveblocksExtension = () => {
164
232
  unsubs: []
165
233
  };
166
234
  },
167
- addOptions() {
168
- return {
169
- field: "default",
170
- mentions: true,
171
- comments: true
172
- };
173
- },
174
235
  addExtensions() {
175
236
  const extensions = [
176
237
  LiveblocksCollab.configure({
177
238
  document: this.storage.doc,
178
- field: this.options.field
239
+ field: options.field
179
240
  }),
180
241
  CollaborationCursor.configure({
181
242
  provider: this.storage.provider
182
243
  })
183
244
  ];
184
- if (this.options.comments) {
245
+ if (options.comments) {
185
246
  extensions.push(CommentsExtension.CommentsExtension);
186
247
  }
187
- if (this.options.mentions) {
248
+ if (options.mentions) {
188
249
  extensions.push(
189
250
  MentionExtension.MentionExtension.configure({
190
251
  onCreateMention,
@@ -197,5 +258,6 @@ const useLiveblocksExtension = () => {
197
258
  });
198
259
  };
199
260
 
261
+ exports.useIsEditorReady = useIsEditorReady;
200
262
  exports.useLiveblocksExtension = useLiveblocksExtension;
201
263
  //# sourceMappingURL=LiveblocksExtension.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LiveblocksExtension.js","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal, TextEditorType } from \"@liveblocks/core\";\nimport {\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport {\n CreateThreadError,\n getUmbrellaStoreForClient,\n} from \"@liveblocks/react/_private\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension, getMarkType } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments: boolean; // | CommentsConfiguration\n mentions: boolean; // | MentionsConfiguration\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nexport const useLiveblocksExtension = (): Extension => {\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n // handleThreadDelete(error.context.threadId);\n }\n });\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n\n const reportTextEditorType = useCallback(\n (field: string) => {\n room[kInternal].reportTextEditor(TextEditorType.TipTap, field);\n },\n [room]\n );\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n LiveblocksExtensionOptions,\n {\n unsubs: (() => void)[];\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n if (\n this.options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsubs.push(\n room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n })\n );\n if (this.options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.subscribe(() => {\n const threadMap = new Map(\n store\n .getFullState()\n .threadsDB.findMany(roomId, { resolved: false }, \"asc\")\n .map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n\n reportTextEditorType(this.options.field ?? \"default\");\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsubs: [],\n };\n },\n\n addOptions() {\n return {\n field: \"default\",\n mentions: true,\n comments: true,\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: this.options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (this.options.comments) {\n extensions.push(CommentsExtension);\n }\n if (this.options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":["useRoom","useCommentsErrorListener","CreateThreadError","useClient","getUmbrellaStoreForClient","useCallback","kInternal","TextEditorType","Extension","getMarkType","LIVEBLOCKS_COMMENT_MARK_TYPE","Doc","LiveblocksYjsProvider","CommentsExtension","MentionExtension"],"mappings":"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"}
1
+ {"version":3,"file":"LiveblocksExtension.js","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal, TextEditorType } from \"@liveblocks/core\";\nimport {\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport {\n CreateThreadError,\n getUmbrellaStoreForClient,\n} from \"@liveblocks/react/_private\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension, Content, Editor } from \"@tiptap/core\";\nimport { Extension, getMarkType } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments?: boolean; // | CommentsConfiguration\n mentions?: boolean; // | MentionsConfiguration\n offlineSupport_experimental?: boolean;\n initialContent?: Content;\n};\n\nconst DEFAULT_OPTIONS = {\n field: \"default\",\n comments: true,\n mentions: true,\n offlineSupport_experimental: false,\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nfunction useYjsProvider() {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return room[kInternal].yjsProviderDidChange.subscribe(onStoreChange);\n },\n [room]\n );\n\n const getSnapshot = useCallback(() => {\n return room[kInternal].getYjsProvider();\n }, [room]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Returns whether the editor has loaded the initial text contents from the\n * server and is ready to be used.\n */\nexport function useIsEditorReady(): boolean {\n const yjsProvider = useYjsProvider();\n\n const getSnapshot = useCallback(() => {\n const status = yjsProvider?.getStatus();\n return status === \"synchronizing\" || status === \"synchronized\";\n }, [yjsProvider]);\n\n const subscribe = useCallback(\n (callback: () => void) => {\n if (yjsProvider === undefined) return () => {};\n yjsProvider.on(\"status\", callback);\n return () => {\n yjsProvider.off(\"status\", callback);\n };\n },\n [yjsProvider]\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport const useLiveblocksExtension = (\n opts?: LiveblocksExtensionOptions\n): Extension => {\n const options = {\n ...DEFAULT_OPTIONS,\n ...opts,\n };\n const [editor, setEditor] = useState<Editor | null>(null);\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n // handleThreadDelete(error.context.threadId);\n }\n });\n const isEditorReady = useIsEditorReady();\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n const yjsProvider = useYjsProvider();\n\n // If the user provided initialContent, wait for ready and then set it\n useEffect(() => {\n if (!isEditorReady || !yjsProvider || !options.initialContent || !editor)\n return;\n\n // As noted in the tiptap documentation, you may not set initial content with collaboration.\n // The docs provide the following workaround:\n const ydoc = (yjsProvider as LiveblocksYjsProvider).getYDoc();\n const hasContentSet = ydoc.getMap(\"liveblocks_config\").get(\"hasContentSet\");\n if (!hasContentSet) {\n ydoc.getMap(\"liveblocks_config\").set(\"hasContentSet\", true);\n editor.commands.setContent(options.initialContent);\n }\n }, [isEditorReady, yjsProvider, options.initialContent, editor]);\n\n const reportTextEditorType = useCallback(\n (field: string) => {\n room[kInternal].reportTextEditor(TextEditorType.TipTap, field);\n },\n [room]\n );\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n never,\n {\n unsubs: (() => void)[];\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n setEditor(this.editor);\n if (this.editor.options.content) {\n console.warn(\n \"[Liveblocks] Initial content must be set in the useLiveblocksExtension hook option. Remove content from your editor options.\"\n );\n }\n if (\n options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsubs.push(\n room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n })\n );\n if (options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.subscribe(() => {\n const threadMap = new Map(\n store\n .getFullState()\n .threadsDB.findMany(roomId, { resolved: false }, \"asc\")\n .map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n\n reportTextEditorType(options.field ?? DEFAULT_OPTIONS.field);\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(\n room.id,\n new LiveblocksYjsProvider(room, doc, {\n offlineSupport_experimental: options.offlineSupport_experimental,\n })\n );\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsubs: [],\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (options.comments) {\n extensions.push(CommentsExtension);\n }\n if (options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":["useRoom","useCallback","kInternal","useSyncExternalStore","useState","useCommentsErrorListener","CreateThreadError","useClient","getUmbrellaStoreForClient","useEffect","TextEditorType","Extension","getMarkType","LIVEBLOCKS_COMMENT_MARK_TYPE","Doc","LiveblocksYjsProvider","CommentsExtension","MentionExtension"],"mappings":"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"}
@@ -5,7 +5,8 @@ import { LiveblocksYjsProvider } from '@liveblocks/yjs';
5
5
  import { Extension, getMarkType } from '@tiptap/core';
6
6
  import Collaboration from '@tiptap/extension-collaboration';
7
7
  import CollaborationCursor from '@tiptap/extension-collaboration-cursor';
8
- import { useCallback } from 'react';
8
+ import { useCallback, useState, useEffect } from 'react';
9
+ import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js';
9
10
  import { Doc } from 'yjs';
10
11
  import { CommentsExtension } from './comments/CommentsExtension.mjs';
11
12
  import { MentionExtension } from './mentions/MentionExtension.mjs';
@@ -13,6 +14,12 @@ import { LIVEBLOCKS_COMMENT_MARK_TYPE } from './types.mjs';
13
14
 
14
15
  const providersMap = /* @__PURE__ */ new Map();
15
16
  const docMap = /* @__PURE__ */ new Map();
17
+ const DEFAULT_OPTIONS = {
18
+ field: "default",
19
+ comments: true,
20
+ mentions: true,
21
+ offlineSupport_experimental: false
22
+ };
16
23
  const LiveblocksCollab = Collaboration.extend({
17
24
  onCreate() {
18
25
  if (!this.editor.extensionManager.extensions.find((e) => e.name === "doc")) {
@@ -39,15 +46,65 @@ const LiveblocksCollab = Collaboration.extend({
39
46
  }
40
47
  }
41
48
  });
42
- const useLiveblocksExtension = () => {
49
+ function useYjsProvider() {
50
+ const room = useRoom();
51
+ const subscribe = useCallback(
52
+ (onStoreChange) => {
53
+ return room[kInternal].yjsProviderDidChange.subscribe(onStoreChange);
54
+ },
55
+ [room]
56
+ );
57
+ const getSnapshot = useCallback(() => {
58
+ return room[kInternal].getYjsProvider();
59
+ }, [room]);
60
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
61
+ }
62
+ function useIsEditorReady() {
63
+ const yjsProvider = useYjsProvider();
64
+ const getSnapshot = useCallback(() => {
65
+ const status = yjsProvider?.getStatus();
66
+ return status === "synchronizing" || status === "synchronized";
67
+ }, [yjsProvider]);
68
+ const subscribe = useCallback(
69
+ (callback) => {
70
+ if (yjsProvider === void 0)
71
+ return () => {
72
+ };
73
+ yjsProvider.on("status", callback);
74
+ return () => {
75
+ yjsProvider.off("status", callback);
76
+ };
77
+ },
78
+ [yjsProvider]
79
+ );
80
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
81
+ }
82
+ const useLiveblocksExtension = (opts) => {
83
+ const options = {
84
+ ...DEFAULT_OPTIONS,
85
+ ...opts
86
+ };
87
+ const [editor, setEditor] = useState(null);
43
88
  const room = useRoom();
44
89
  useCommentsErrorListener((error) => {
45
90
  if (error instanceof CreateThreadError) {
46
91
  }
47
92
  });
93
+ const isEditorReady = useIsEditorReady();
48
94
  const client = useClient();
49
95
  const store = getUmbrellaStoreForClient(client);
50
96
  const roomId = room.id;
97
+ const yjsProvider = useYjsProvider();
98
+ useEffect(() => {
99
+ if (!isEditorReady || !yjsProvider || !options.initialContent || !editor)
100
+ return;
101
+ const ydoc = yjsProvider.getYDoc();
102
+ const hasContentSet = ydoc.getMap("liveblocks_config").get("hasContentSet");
103
+ if (!hasContentSet) {
104
+ ydoc.getMap("liveblocks_config").set("hasContentSet", true);
105
+ editor.commands.setContent(options.initialContent);
106
+ }
107
+ }, [isEditorReady, yjsProvider, options.initialContent, editor]);
51
108
  const reportTextEditorType = useCallback(
52
109
  (field) => {
53
110
  room[kInternal].reportTextEditor(TextEditorType.TipTap, field);
@@ -77,7 +134,13 @@ const useLiveblocksExtension = () => {
77
134
  return Extension.create({
78
135
  name: "liveblocksExtension",
79
136
  onCreate() {
80
- if (this.options.mentions && this.editor.extensionManager.extensions.find(
137
+ setEditor(this.editor);
138
+ if (this.editor.options.content) {
139
+ console.warn(
140
+ "[Liveblocks] Initial content must be set in the useLiveblocksExtension hook option. Remove content from your editor options."
141
+ );
142
+ }
143
+ if (options.mentions && this.editor.extensionManager.extensions.find(
81
144
  (e) => e.name.toLowerCase() === "mention"
82
145
  )) {
83
146
  console.warn(
@@ -103,7 +166,7 @@ const useLiveblocksExtension = () => {
103
166
  }
104
167
  })
105
168
  );
106
- if (this.options.comments) {
169
+ if (options.comments) {
107
170
  const commentMarkType = getMarkType(
108
171
  LIVEBLOCKS_COMMENT_MARK_TYPE,
109
172
  this.editor.schema
@@ -145,7 +208,7 @@ const useLiveblocksExtension = () => {
145
208
  })
146
209
  );
147
210
  }
148
- reportTextEditorType(this.options.field ?? "default");
211
+ reportTextEditorType(options.field ?? DEFAULT_OPTIONS.field);
149
212
  },
150
213
  onDestroy() {
151
214
  this.storage.unsubs.forEach((unsub) => unsub());
@@ -154,7 +217,12 @@ const useLiveblocksExtension = () => {
154
217
  if (!providersMap.has(room.id)) {
155
218
  const doc = new Doc();
156
219
  docMap.set(room.id, doc);
157
- providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));
220
+ providersMap.set(
221
+ room.id,
222
+ new LiveblocksYjsProvider(room, doc, {
223
+ offlineSupport_experimental: options.offlineSupport_experimental
224
+ })
225
+ );
158
226
  }
159
227
  return {
160
228
  doc: docMap.get(room.id),
@@ -162,27 +230,20 @@ const useLiveblocksExtension = () => {
162
230
  unsubs: []
163
231
  };
164
232
  },
165
- addOptions() {
166
- return {
167
- field: "default",
168
- mentions: true,
169
- comments: true
170
- };
171
- },
172
233
  addExtensions() {
173
234
  const extensions = [
174
235
  LiveblocksCollab.configure({
175
236
  document: this.storage.doc,
176
- field: this.options.field
237
+ field: options.field
177
238
  }),
178
239
  CollaborationCursor.configure({
179
240
  provider: this.storage.provider
180
241
  })
181
242
  ];
182
- if (this.options.comments) {
243
+ if (options.comments) {
183
244
  extensions.push(CommentsExtension);
184
245
  }
185
- if (this.options.mentions) {
246
+ if (options.mentions) {
186
247
  extensions.push(
187
248
  MentionExtension.configure({
188
249
  onCreateMention,
@@ -195,5 +256,5 @@ const useLiveblocksExtension = () => {
195
256
  });
196
257
  };
197
258
 
198
- export { useLiveblocksExtension };
259
+ export { useIsEditorReady, useLiveblocksExtension };
199
260
  //# sourceMappingURL=LiveblocksExtension.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"LiveblocksExtension.mjs","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal, TextEditorType } from \"@liveblocks/core\";\nimport {\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport {\n CreateThreadError,\n getUmbrellaStoreForClient,\n} from \"@liveblocks/react/_private\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension, getMarkType } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments: boolean; // | CommentsConfiguration\n mentions: boolean; // | MentionsConfiguration\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nexport const useLiveblocksExtension = (): Extension => {\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n // handleThreadDelete(error.context.threadId);\n }\n });\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n\n const reportTextEditorType = useCallback(\n (field: string) => {\n room[kInternal].reportTextEditor(TextEditorType.TipTap, field);\n },\n [room]\n );\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n LiveblocksExtensionOptions,\n {\n unsubs: (() => void)[];\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n if (\n this.options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsubs.push(\n room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n })\n );\n if (this.options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.subscribe(() => {\n const threadMap = new Map(\n store\n .getFullState()\n .threadsDB.findMany(roomId, { resolved: false }, \"asc\")\n .map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n\n reportTextEditorType(this.options.field ?? \"default\");\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsubs: [],\n };\n },\n\n addOptions() {\n return {\n field: \"default\",\n mentions: true,\n comments: true,\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: this.options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (this.options.comments) {\n extensions.push(CommentsExtension);\n }\n if (this.options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LiveblocksExtension.mjs","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal, TextEditorType } from \"@liveblocks/core\";\nimport {\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport {\n CreateThreadError,\n getUmbrellaStoreForClient,\n} from \"@liveblocks/react/_private\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension, Content, Editor } from \"@tiptap/core\";\nimport { Extension, getMarkType } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments?: boolean; // | CommentsConfiguration\n mentions?: boolean; // | MentionsConfiguration\n offlineSupport_experimental?: boolean;\n initialContent?: Content;\n};\n\nconst DEFAULT_OPTIONS = {\n field: \"default\",\n comments: true,\n mentions: true,\n offlineSupport_experimental: false,\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nfunction useYjsProvider() {\n const room = useRoom();\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n return room[kInternal].yjsProviderDidChange.subscribe(onStoreChange);\n },\n [room]\n );\n\n const getSnapshot = useCallback(() => {\n return room[kInternal].getYjsProvider();\n }, [room]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Returns whether the editor has loaded the initial text contents from the\n * server and is ready to be used.\n */\nexport function useIsEditorReady(): boolean {\n const yjsProvider = useYjsProvider();\n\n const getSnapshot = useCallback(() => {\n const status = yjsProvider?.getStatus();\n return status === \"synchronizing\" || status === \"synchronized\";\n }, [yjsProvider]);\n\n const subscribe = useCallback(\n (callback: () => void) => {\n if (yjsProvider === undefined) return () => {};\n yjsProvider.on(\"status\", callback);\n return () => {\n yjsProvider.off(\"status\", callback);\n };\n },\n [yjsProvider]\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\nexport const useLiveblocksExtension = (\n opts?: LiveblocksExtensionOptions\n): Extension => {\n const options = {\n ...DEFAULT_OPTIONS,\n ...opts,\n };\n const [editor, setEditor] = useState<Editor | null>(null);\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n // handleThreadDelete(error.context.threadId);\n }\n });\n const isEditorReady = useIsEditorReady();\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n const yjsProvider = useYjsProvider();\n\n // If the user provided initialContent, wait for ready and then set it\n useEffect(() => {\n if (!isEditorReady || !yjsProvider || !options.initialContent || !editor)\n return;\n\n // As noted in the tiptap documentation, you may not set initial content with collaboration.\n // The docs provide the following workaround:\n const ydoc = (yjsProvider as LiveblocksYjsProvider).getYDoc();\n const hasContentSet = ydoc.getMap(\"liveblocks_config\").get(\"hasContentSet\");\n if (!hasContentSet) {\n ydoc.getMap(\"liveblocks_config\").set(\"hasContentSet\", true);\n editor.commands.setContent(options.initialContent);\n }\n }, [isEditorReady, yjsProvider, options.initialContent, editor]);\n\n const reportTextEditorType = useCallback(\n (field: string) => {\n room[kInternal].reportTextEditor(TextEditorType.TipTap, field);\n },\n [room]\n );\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n never,\n {\n unsubs: (() => void)[];\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n setEditor(this.editor);\n if (this.editor.options.content) {\n console.warn(\n \"[Liveblocks] Initial content must be set in the useLiveblocksExtension hook option. Remove content from your editor options.\"\n );\n }\n if (\n options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsubs.push(\n room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n })\n );\n if (options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.subscribe(() => {\n const threadMap = new Map(\n store\n .getFullState()\n .threadsDB.findMany(roomId, { resolved: false }, \"asc\")\n .map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n\n reportTextEditorType(options.field ?? DEFAULT_OPTIONS.field);\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(\n room.id,\n new LiveblocksYjsProvider(room, doc, {\n offlineSupport_experimental: options.offlineSupport_experimental,\n })\n );\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsubs: [],\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (options.comments) {\n extensions.push(CommentsExtension);\n }\n if (options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ var reactDom$1 = require('react-dom');
10
10
  const FLOATING_COMPOSER_COLLISION_PADDING = 10;
11
11
  const FloatingComposer = React.forwardRef(function FloatingComposer2(props, forwardedRef) {
12
12
  const createThread = react.useCreateThread();
13
- const { editor } = props;
13
+ const { editor, onComposerSubmit, onKeyDown } = props;
14
14
  const { showComposer } = react$1.useEditorState({
15
15
  editor,
16
16
  selector: (ctx) => ({
@@ -66,18 +66,29 @@ const FloatingComposer = React.forwardRef(function FloatingComposer2(props, forw
66
66
  }, [editor, updateRef, showComposer]);
67
67
  React.useLayoutEffect(updateRef, [updateRef]);
68
68
  const handleComposerSubmit = React.useCallback(
69
- ({ body }, event) => {
69
+ (comment, event) => {
70
+ onComposerSubmit?.(comment, event);
71
+ if (event.defaultPrevented)
72
+ return;
70
73
  if (!editor) {
71
74
  return;
72
75
  }
73
76
  event.preventDefault();
74
77
  const thread = createThread({
75
- body
78
+ body: comment.body,
79
+ attachments: comment.attachments,
80
+ metadata: props.metadata ?? {}
76
81
  });
77
82
  editor.commands.addComment(thread.id);
78
83
  },
79
- [editor, createThread]
84
+ [onComposerSubmit, editor, createThread, props.metadata]
80
85
  );
86
+ const handleKeyDown = React.useCallback((event) => {
87
+ if (event.key === "Escape" && editor) {
88
+ editor.commands.focus();
89
+ }
90
+ onKeyDown?.(event);
91
+ }, [editor, onKeyDown]);
81
92
  if (!showComposer || !editor) {
82
93
  return null;
83
94
  }
@@ -95,6 +106,7 @@ const FloatingComposer = React.forwardRef(function FloatingComposer2(props, forw
95
106
  }, /* @__PURE__ */ React.createElement(reactUi.Composer, {
96
107
  ref: forwardedRef,
97
108
  ...props,
109
+ onKeyDown: handleKeyDown,
98
110
  onComposerSubmit: handleComposerSubmit,
99
111
  onClick: (e) => {
100
112
  e.stopPropagation();
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingComposer.js","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import { autoUpdate, flip, hide, limitShift, offset, shift, size, useFloating } from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport type { ComposerProps, ComposerSubmitComment } from \"@liveblocks/react-ui\";\nimport { Composer } from \"@liveblocks/react-ui\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentRef, FormEvent } from \"react\";\nimport React, { forwardRef, useCallback, useEffect, useLayoutEffect } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport type { CommentsExtensionStorage } from \"../types\";\n\nexport type FloatingComposerProps<M extends BaseMetadata = DM> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n> & {\n editor: Editor | null;\n};\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const createThread = useCreateThread();\n const { editor } = props;\n const { showComposer } = useEditorState({\n editor,\n selector: (ctx) => ({\n showComposer: !!(ctx.editor?.storage.liveblocksComments as CommentsExtensionStorage | undefined)?.pendingCommentSelection,\n }),\n equalityFn: (prev, next) => {\n if (!next) return false;\n return prev.showComposer === next.showComposer;\n },\n }) ?? { showComposer: false };\n\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n\n const updateRef = useCallback(() => {\n if (!editor || !showComposer) {\n return;\n }\n const el = editor.view.dom.querySelector(\".lb-tiptap-active-selection\");\n if (el) {\n setReference(el);\n }\n }, [setReference, editor, showComposer]);\n\n // Remote cursor updates and other edits can cause the ref to break\n useEffect(() => {\n if (!editor || !showComposer) {\n return;\n }\n editor.on(\"transaction\", updateRef)\n return () => {\n editor.off(\"transaction\", updateRef);\n }\n }, [editor, updateRef, showComposer]);\n\n useLayoutEffect(updateRef, [updateRef]);\n\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n ({ body }: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n if (!editor) {\n return;\n }\n event.preventDefault();\n\n const thread = createThread({\n body,\n });\n editor.commands.addComment(thread.id);\n\n },\n [editor, createThread]\n );\n\n if (!showComposer || !editor) {\n return null;\n }\n\n return createPortal(\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating} style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}>\n <Composer\n ref={forwardedRef}\n {...props}\n onComposerSubmit={handleComposerSubmit}\n onClick={(e) => {\n // Don't send up a click event from emoji popout and close the composer\n e.stopPropagation();\n }}\n autoFocus={true}\n />\n </div>,\n document.body\n );\n});"],"names":["forwardRef","FloatingComposer","useCreateThread","useEditorState","useFloating","flip","offset","hide","shift","limitShift","size","autoUpdate","useCallback","useEffect","useLayoutEffect","createPortal","Composer"],"mappings":"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"}
1
+ {"version":3,"file":"FloatingComposer.js","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import { autoUpdate, flip, hide, limitShift, offset, shift, size, useFloating } from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport type { ComposerProps, ComposerSubmitComment } from \"@liveblocks/react-ui\";\nimport { Composer } from \"@liveblocks/react-ui\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentRef, FormEvent, KeyboardEvent } from \"react\";\nimport React, { forwardRef, useCallback, useEffect, useLayoutEffect } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport type { CommentsExtensionStorage } from \"../types\";\n\nexport type FloatingComposerProps<M extends BaseMetadata = DM> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n> & {\n editor: Editor | null;\n};\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const createThread = useCreateThread();\n const { editor, onComposerSubmit, onKeyDown } = props;\n const { showComposer } = useEditorState({\n editor,\n selector: (ctx) => ({\n showComposer: !!(ctx.editor?.storage.liveblocksComments as CommentsExtensionStorage | undefined)?.pendingCommentSelection,\n }),\n equalityFn: (prev, next) => {\n if (!next) return false;\n return prev.showComposer === next.showComposer;\n },\n }) ?? { showComposer: false };\n\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n\n const updateRef = useCallback(() => {\n if (!editor || !showComposer) {\n return;\n }\n const el = editor.view.dom.querySelector(\".lb-tiptap-active-selection\");\n if (el) {\n setReference(el);\n }\n }, [setReference, editor, showComposer]);\n\n // Remote cursor updates and other edits can cause the ref to break\n useEffect(() => {\n if (!editor || !showComposer) {\n return;\n }\n editor.on(\"transaction\", updateRef)\n return () => {\n editor.off(\"transaction\", updateRef);\n }\n }, [editor, updateRef, showComposer]);\n\n useLayoutEffect(updateRef, [updateRef]);\n\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n if (event.defaultPrevented) return;\n\n if (!editor) {\n return;\n }\n event.preventDefault();\n\n const thread = createThread({\n body: comment.body,\n attachments: comment.attachments,\n metadata: props.metadata ?? {},\n });\n editor.commands.addComment(thread.id);\n\n },\n [onComposerSubmit, editor, createThread, props.metadata]\n );\n\n const handleKeyDown = useCallback((event: KeyboardEvent<HTMLFormElement>) => {\n if (event.key === \"Escape\" && editor) {\n editor.commands.focus();\n }\n onKeyDown?.(event);\n }, [editor, onKeyDown]);\n\n\n if (!showComposer || !editor) {\n return null;\n }\n\n return createPortal(\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating} style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}>\n <Composer\n ref={forwardedRef}\n {...props}\n onKeyDown={handleKeyDown}\n onComposerSubmit={handleComposerSubmit}\n onClick={(e) => {\n // Don't send up a click event from emoji popout and close the composer\n e.stopPropagation();\n }}\n autoFocus={true}\n />\n </div>,\n document.body\n );\n});"],"names":["forwardRef","FloatingComposer","useCreateThread","useEditorState","useFloating","flip","offset","hide","shift","limitShift","size","autoUpdate","useCallback","useEffect","useLayoutEffect","createPortal","Composer"],"mappings":"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"}
@@ -8,7 +8,7 @@ import { createPortal } from 'react-dom';
8
8
  const FLOATING_COMPOSER_COLLISION_PADDING = 10;
9
9
  const FloatingComposer = forwardRef(function FloatingComposer2(props, forwardedRef) {
10
10
  const createThread = useCreateThread();
11
- const { editor } = props;
11
+ const { editor, onComposerSubmit, onKeyDown } = props;
12
12
  const { showComposer } = useEditorState({
13
13
  editor,
14
14
  selector: (ctx) => ({
@@ -64,18 +64,29 @@ const FloatingComposer = forwardRef(function FloatingComposer2(props, forwardedR
64
64
  }, [editor, updateRef, showComposer]);
65
65
  useLayoutEffect(updateRef, [updateRef]);
66
66
  const handleComposerSubmit = useCallback(
67
- ({ body }, event) => {
67
+ (comment, event) => {
68
+ onComposerSubmit?.(comment, event);
69
+ if (event.defaultPrevented)
70
+ return;
68
71
  if (!editor) {
69
72
  return;
70
73
  }
71
74
  event.preventDefault();
72
75
  const thread = createThread({
73
- body
76
+ body: comment.body,
77
+ attachments: comment.attachments,
78
+ metadata: props.metadata ?? {}
74
79
  });
75
80
  editor.commands.addComment(thread.id);
76
81
  },
77
- [editor, createThread]
82
+ [onComposerSubmit, editor, createThread, props.metadata]
78
83
  );
84
+ const handleKeyDown = useCallback((event) => {
85
+ if (event.key === "Escape" && editor) {
86
+ editor.commands.focus();
87
+ }
88
+ onKeyDown?.(event);
89
+ }, [editor, onKeyDown]);
79
90
  if (!showComposer || !editor) {
80
91
  return null;
81
92
  }
@@ -93,6 +104,7 @@ const FloatingComposer = forwardRef(function FloatingComposer2(props, forwardedR
93
104
  }, /* @__PURE__ */ React.createElement(Composer, {
94
105
  ref: forwardedRef,
95
106
  ...props,
107
+ onKeyDown: handleKeyDown,
96
108
  onComposerSubmit: handleComposerSubmit,
97
109
  onClick: (e) => {
98
110
  e.stopPropagation();
@@ -1 +1 @@
1
- {"version":3,"file":"FloatingComposer.mjs","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import { autoUpdate, flip, hide, limitShift, offset, shift, size, useFloating } from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport type { ComposerProps, ComposerSubmitComment } from \"@liveblocks/react-ui\";\nimport { Composer } from \"@liveblocks/react-ui\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentRef, FormEvent } from \"react\";\nimport React, { forwardRef, useCallback, useEffect, useLayoutEffect } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport type { CommentsExtensionStorage } from \"../types\";\n\nexport type FloatingComposerProps<M extends BaseMetadata = DM> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n> & {\n editor: Editor | null;\n};\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const createThread = useCreateThread();\n const { editor } = props;\n const { showComposer } = useEditorState({\n editor,\n selector: (ctx) => ({\n showComposer: !!(ctx.editor?.storage.liveblocksComments as CommentsExtensionStorage | undefined)?.pendingCommentSelection,\n }),\n equalityFn: (prev, next) => {\n if (!next) return false;\n return prev.showComposer === next.showComposer;\n },\n }) ?? { showComposer: false };\n\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n\n const updateRef = useCallback(() => {\n if (!editor || !showComposer) {\n return;\n }\n const el = editor.view.dom.querySelector(\".lb-tiptap-active-selection\");\n if (el) {\n setReference(el);\n }\n }, [setReference, editor, showComposer]);\n\n // Remote cursor updates and other edits can cause the ref to break\n useEffect(() => {\n if (!editor || !showComposer) {\n return;\n }\n editor.on(\"transaction\", updateRef)\n return () => {\n editor.off(\"transaction\", updateRef);\n }\n }, [editor, updateRef, showComposer]);\n\n useLayoutEffect(updateRef, [updateRef]);\n\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n ({ body }: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n if (!editor) {\n return;\n }\n event.preventDefault();\n\n const thread = createThread({\n body,\n });\n editor.commands.addComment(thread.id);\n\n },\n [editor, createThread]\n );\n\n if (!showComposer || !editor) {\n return null;\n }\n\n return createPortal(\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating} style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}>\n <Composer\n ref={forwardedRef}\n {...props}\n onComposerSubmit={handleComposerSubmit}\n onClick={(e) => {\n // Don't send up a click event from emoji popout and close the composer\n e.stopPropagation();\n }}\n autoFocus={true}\n />\n </div>,\n document.body\n );\n});"],"names":["FloatingComposer"],"mappings":"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"}
1
+ {"version":3,"file":"FloatingComposer.mjs","sources":["../../src/comments/FloatingComposer.tsx"],"sourcesContent":["import { autoUpdate, flip, hide, limitShift, offset, shift, size, useFloating } from \"@floating-ui/react-dom\";\nimport type { BaseMetadata } from \"@liveblocks/client\";\nimport type { DM } from \"@liveblocks/core\";\nimport { useCreateThread } from \"@liveblocks/react\";\nimport type { ComposerProps, ComposerSubmitComment } from \"@liveblocks/react-ui\";\nimport { Composer } from \"@liveblocks/react-ui\";\nimport { type Editor, useEditorState } from \"@tiptap/react\";\nimport type { ComponentRef, FormEvent, KeyboardEvent } from \"react\";\nimport React, { forwardRef, useCallback, useEffect, useLayoutEffect } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport type { CommentsExtensionStorage } from \"../types\";\n\nexport type FloatingComposerProps<M extends BaseMetadata = DM> = Omit<\n ComposerProps<M>,\n \"threadId\" | \"commentId\"\n> & {\n editor: Editor | null;\n};\n\ntype ComposerElement = ComponentRef<typeof Composer>;\n\nexport const FLOATING_COMPOSER_COLLISION_PADDING = 10;\n\nexport const FloatingComposer = forwardRef<\n ComposerElement,\n FloatingComposerProps\n>(function FloatingComposer(props, forwardedRef) {\n const createThread = useCreateThread();\n const { editor, onComposerSubmit, onKeyDown } = props;\n const { showComposer } = useEditorState({\n editor,\n selector: (ctx) => ({\n showComposer: !!(ctx.editor?.storage.liveblocksComments as CommentsExtensionStorage | undefined)?.pendingCommentSelection,\n }),\n equalityFn: (prev, next) => {\n if (!next) return false;\n return prev.showComposer === next.showComposer;\n },\n }) ?? { showComposer: false };\n\n const {\n refs: { setReference, setFloating },\n strategy,\n x,\n y,\n } = useFloating({\n strategy: \"fixed\",\n placement: \"bottom\",\n middleware: [\n flip({ padding: FLOATING_COMPOSER_COLLISION_PADDING, crossAxis: false }),\n offset(10),\n hide({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n shift({\n padding: FLOATING_COMPOSER_COLLISION_PADDING,\n limiter: limitShift(),\n }),\n size({ padding: FLOATING_COMPOSER_COLLISION_PADDING }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n });\n\n\n const updateRef = useCallback(() => {\n if (!editor || !showComposer) {\n return;\n }\n const el = editor.view.dom.querySelector(\".lb-tiptap-active-selection\");\n if (el) {\n setReference(el);\n }\n }, [setReference, editor, showComposer]);\n\n // Remote cursor updates and other edits can cause the ref to break\n useEffect(() => {\n if (!editor || !showComposer) {\n return;\n }\n editor.on(\"transaction\", updateRef)\n return () => {\n editor.off(\"transaction\", updateRef);\n }\n }, [editor, updateRef, showComposer]);\n\n useLayoutEffect(updateRef, [updateRef]);\n\n\n // Submit a new thread and update the comment highlight to show a completed highlight\n const handleComposerSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n if (event.defaultPrevented) return;\n\n if (!editor) {\n return;\n }\n event.preventDefault();\n\n const thread = createThread({\n body: comment.body,\n attachments: comment.attachments,\n metadata: props.metadata ?? {},\n });\n editor.commands.addComment(thread.id);\n\n },\n [onComposerSubmit, editor, createThread, props.metadata]\n );\n\n const handleKeyDown = useCallback((event: KeyboardEvent<HTMLFormElement>) => {\n if (event.key === \"Escape\" && editor) {\n editor.commands.focus();\n }\n onKeyDown?.(event);\n }, [editor, onKeyDown]);\n\n\n if (!showComposer || !editor) {\n return null;\n }\n\n return createPortal(\n <div\n className=\"lb-root lb-portal lb-elevation lb-tiptap-floating lb-tiptap-floating-composer\"\n ref={setFloating} style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n minWidth: \"max-content\",\n }}>\n <Composer\n ref={forwardedRef}\n {...props}\n onKeyDown={handleKeyDown}\n onComposerSubmit={handleComposerSubmit}\n onClick={(e) => {\n // Don't send up a click event from emoji popout and close the composer\n e.stopPropagation();\n }}\n autoFocus={true}\n />\n </div>,\n document.body\n );\n});"],"names":["FloatingComposer"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ import { ThreadProps, ComposerProps } from '@liveblocks/react-ui';
3
3
  import { Editor } from '@tiptap/react';
4
4
  import React, { ComponentPropsWithoutRef, ComponentType, HTMLAttributes } from 'react';
5
5
  import { BaseMetadata as BaseMetadata$1 } from '@liveblocks/client';
6
- import { Extension } from '@tiptap/core';
6
+ import { Extension, Content } from '@tiptap/core';
7
7
 
8
8
  declare type AnchoredThreadsComponents = {
9
9
  Thread: ComponentType<ThreadProps>;
@@ -47,7 +47,19 @@ interface FloatingThreadsProps<M extends BaseMetadata = DM> extends Omit<HTMLAtt
47
47
  }
48
48
  declare function FloatingThreads({ threads, components, editor, ...props }: FloatingThreadsProps): React.JSX.Element | null;
49
49
 
50
- declare const useLiveblocksExtension: () => Extension;
50
+ declare type LiveblocksExtensionOptions = {
51
+ field?: string;
52
+ comments?: boolean;
53
+ mentions?: boolean;
54
+ offlineSupport_experimental?: boolean;
55
+ initialContent?: Content;
56
+ };
57
+ /**
58
+ * Returns whether the editor has loaded the initial text contents from the
59
+ * server and is ready to be used.
60
+ */
61
+ declare function useIsEditorReady(): boolean;
62
+ declare const useLiveblocksExtension: (opts?: LiveblocksExtensionOptions) => Extension;
51
63
 
52
64
  interface HistoryVersionPreviewProps extends ComponentPropsWithoutRef<"div"> {
53
65
  version: HistoryVersion;
@@ -75,4 +87,4 @@ declare module "@tiptap/core" {
75
87
  }
76
88
  }
77
89
 
78
- export { AnchoredThreads, FloatingComposer, FloatingThreads, HistoryVersionPreview, useLiveblocksExtension };
90
+ export { AnchoredThreads, FloatingComposer, FloatingThreads, HistoryVersionPreview, useIsEditorReady, useLiveblocksExtension };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { ThreadProps, ComposerProps } from '@liveblocks/react-ui';
3
3
  import { Editor } from '@tiptap/react';
4
4
  import React, { ComponentPropsWithoutRef, ComponentType, HTMLAttributes } from 'react';
5
5
  import { BaseMetadata as BaseMetadata$1 } from '@liveblocks/client';
6
- import { Extension } from '@tiptap/core';
6
+ import { Extension, Content } from '@tiptap/core';
7
7
 
8
8
  declare type AnchoredThreadsComponents = {
9
9
  Thread: ComponentType<ThreadProps>;
@@ -47,7 +47,19 @@ interface FloatingThreadsProps<M extends BaseMetadata = DM> extends Omit<HTMLAtt
47
47
  }
48
48
  declare function FloatingThreads({ threads, components, editor, ...props }: FloatingThreadsProps): React.JSX.Element | null;
49
49
 
50
- declare const useLiveblocksExtension: () => Extension;
50
+ declare type LiveblocksExtensionOptions = {
51
+ field?: string;
52
+ comments?: boolean;
53
+ mentions?: boolean;
54
+ offlineSupport_experimental?: boolean;
55
+ initialContent?: Content;
56
+ };
57
+ /**
58
+ * Returns whether the editor has loaded the initial text contents from the
59
+ * server and is ready to be used.
60
+ */
61
+ declare function useIsEditorReady(): boolean;
62
+ declare const useLiveblocksExtension: (opts?: LiveblocksExtensionOptions) => Extension;
51
63
 
52
64
  interface HistoryVersionPreviewProps extends ComponentPropsWithoutRef<"div"> {
53
65
  version: HistoryVersion;
@@ -75,4 +87,4 @@ declare module "@tiptap/core" {
75
87
  }
76
88
  }
77
89
 
78
- export { AnchoredThreads, FloatingComposer, FloatingThreads, HistoryVersionPreview, useLiveblocksExtension };
90
+ export { AnchoredThreads, FloatingComposer, FloatingThreads, HistoryVersionPreview, useIsEditorReady, useLiveblocksExtension };
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ core.detectDupes(version.PKG_NAME, version.PKG_VERSION, version.PKG_FORMAT);
13
13
  exports.AnchoredThreads = AnchoredThreads.AnchoredThreads;
14
14
  exports.FloatingComposer = FloatingComposer.FloatingComposer;
15
15
  exports.FloatingThreads = FloatingThreads.FloatingThreads;
16
+ exports.useIsEditorReady = LiveblocksExtension.useIsEditorReady;
16
17
  exports.useLiveblocksExtension = LiveblocksExtension.useLiveblocksExtension;
17
18
  exports.HistoryVersionPreview = HistoryVersionPreview.HistoryVersionPreview;
18
19
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport { AnchoredThreads } from \"./comments/AnchoredThreads\";\nexport { FloatingComposer } from \"./comments/FloatingComposer\";\nexport { FloatingThreads } from \"./comments/FloatingThreads\";\nexport { useLiveblocksExtension } from \"./LiveblocksExtension\";\nexport { HistoryVersionPreview } from \"./version-history/HistoryVersionPreview\";\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n comments: {\n /**\n * Add a comment\n */\n addComment: (id: string) => ReturnType;\n selectThread: (id: string | null) => ReturnType;\n addPendingComment: () => ReturnType;\n };\n }\n}\n"],"names":["detectDupes","PKG_NAME","PKG_VERSION","PKG_FORMAT"],"mappings":";;;;;;;;;;AAIAA,gBAAY,CAAAC,gBAAA,EAAUC,qBAAaC,kBAAU,CAAA;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport { AnchoredThreads } from \"./comments/AnchoredThreads\";\nexport { FloatingComposer } from \"./comments/FloatingComposer\";\nexport { FloatingThreads } from \"./comments/FloatingThreads\";\nexport { useLiveblocksExtension } from \"./LiveblocksExtension\";\nexport { useIsEditorReady } from \"./LiveblocksExtension\";\nexport { HistoryVersionPreview } from \"./version-history/HistoryVersionPreview\";\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n comments: {\n /**\n * Add a comment\n */\n addComment: (id: string) => ReturnType;\n selectThread: (id: string | null) => ReturnType;\n addPendingComment: () => ReturnType;\n };\n }\n}\n"],"names":["detectDupes","PKG_NAME","PKG_VERSION","PKG_FORMAT"],"mappings":";;;;;;;;;;AAIAA,gBAAY,CAAAC,gBAAA,EAAUC,qBAAaC,kBAAU,CAAA;;;;;;;;;"}
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import { PKG_NAME, PKG_VERSION, PKG_FORMAT } from './version.mjs';
3
3
  export { AnchoredThreads } from './comments/AnchoredThreads.mjs';
4
4
  export { FloatingComposer } from './comments/FloatingComposer.mjs';
5
5
  export { FloatingThreads } from './comments/FloatingThreads.mjs';
6
- export { useLiveblocksExtension } from './LiveblocksExtension.mjs';
6
+ export { useIsEditorReady, useLiveblocksExtension } from './LiveblocksExtension.mjs';
7
7
  export { HistoryVersionPreview } from './version-history/HistoryVersionPreview.mjs';
8
8
 
9
9
  detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport { AnchoredThreads } from \"./comments/AnchoredThreads\";\nexport { FloatingComposer } from \"./comments/FloatingComposer\";\nexport { FloatingThreads } from \"./comments/FloatingThreads\";\nexport { useLiveblocksExtension } from \"./LiveblocksExtension\";\nexport { HistoryVersionPreview } from \"./version-history/HistoryVersionPreview\";\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n comments: {\n /**\n * Add a comment\n */\n addComment: (id: string) => ReturnType;\n selectThread: (id: string | null) => ReturnType;\n addPendingComment: () => ReturnType;\n };\n }\n}\n"],"names":[],"mappings":";;;;;;;;AAIA,WAAY,CAAA,QAAA,EAAU,aAAa,UAAU,CAAA"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport { AnchoredThreads } from \"./comments/AnchoredThreads\";\nexport { FloatingComposer } from \"./comments/FloatingComposer\";\nexport { FloatingThreads } from \"./comments/FloatingThreads\";\nexport { useLiveblocksExtension } from \"./LiveblocksExtension\";\nexport { useIsEditorReady } from \"./LiveblocksExtension\";\nexport { HistoryVersionPreview } from \"./version-history/HistoryVersionPreview\";\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n comments: {\n /**\n * Add a comment\n */\n addComment: (id: string) => ReturnType;\n selectThread: (id: string | null) => ReturnType;\n addPendingComment: () => ReturnType;\n };\n }\n}\n"],"names":[],"mappings":";;;;;;;;AAIA,WAAY,CAAA,QAAA,EAAU,aAAa,UAAU,CAAA"}
package/dist/version.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const PKG_NAME = "@liveblocks/react-tiptap";
4
- const PKG_VERSION = typeof "2.12.1" === "string" && "2.12.1";
4
+ const PKG_VERSION = typeof "2.12.2" === "string" && "2.12.2";
5
5
  const PKG_FORMAT = typeof "cjs" === "string" && "cjs";
6
6
 
7
7
  exports.PKG_FORMAT = PKG_FORMAT;
package/dist/version.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  const PKG_NAME = "@liveblocks/react-tiptap";
2
- const PKG_VERSION = typeof "2.12.1" === "string" && "2.12.1";
2
+ const PKG_VERSION = typeof "2.12.2" === "string" && "2.12.2";
3
3
  const PKG_FORMAT = typeof "esm" === "string" && "esm";
4
4
 
5
5
  export { PKG_FORMAT, PKG_NAME, PKG_VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liveblocks/react-tiptap",
3
- "version": "2.12.1",
3
+ "version": "2.12.2",
4
4
  "description": "A tiptap react plugin to enable collaboration, comments, live cursors, and more.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
@@ -42,11 +42,11 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@floating-ui/react-dom": "^2.1.2",
45
- "@liveblocks/client": "2.12.1",
46
- "@liveblocks/core": "2.12.1",
47
- "@liveblocks/react": "2.12.1",
48
- "@liveblocks/react-ui": "2.12.1",
49
- "@liveblocks/yjs": "2.12.1",
45
+ "@liveblocks/client": "2.12.2",
46
+ "@liveblocks/core": "2.12.2",
47
+ "@liveblocks/react": "2.12.2",
48
+ "@liveblocks/react-ui": "2.12.2",
49
+ "@liveblocks/yjs": "2.12.2",
50
50
  "@tiptap/core": "^2.7.2",
51
51
  "@tiptap/react": "^2.7.2",
52
52
  "@tiptap/suggestion": "^2.7.2",