@liveblocks/react-tiptap 2.8.3-tiptap1 → 2.10.2-tiptap1

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 (42) hide show
  1. package/dist/LiveblocksExtension.js +72 -15
  2. package/dist/LiveblocksExtension.js.map +1 -1
  3. package/dist/LiveblocksExtension.mjs +76 -19
  4. package/dist/LiveblocksExtension.mjs.map +1 -1
  5. package/dist/comments/AnchoredThreads.js +7 -5
  6. package/dist/comments/AnchoredThreads.js.map +1 -1
  7. package/dist/comments/AnchoredThreads.mjs +7 -5
  8. package/dist/comments/AnchoredThreads.mjs.map +1 -1
  9. package/dist/comments/CommentsExtension.js +18 -1
  10. package/dist/comments/CommentsExtension.js.map +1 -1
  11. package/dist/comments/CommentsExtension.mjs +18 -1
  12. package/dist/comments/CommentsExtension.mjs.map +1 -1
  13. package/dist/comments/FloatingComposer.js +36 -39
  14. package/dist/comments/FloatingComposer.js.map +1 -1
  15. package/dist/comments/FloatingComposer.mjs +37 -40
  16. package/dist/comments/FloatingComposer.mjs.map +1 -1
  17. package/dist/comments/FloatingThreads.js +29 -24
  18. package/dist/comments/FloatingThreads.js.map +1 -1
  19. package/dist/comments/FloatingThreads.mjs +29 -24
  20. package/dist/comments/FloatingThreads.mjs.map +1 -1
  21. package/dist/index.d.mts +13 -0
  22. package/dist/index.d.ts +13 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/mentions/MentionExtension.js +1 -0
  26. package/dist/mentions/MentionExtension.js.map +1 -1
  27. package/dist/mentions/MentionExtension.mjs +1 -0
  28. package/dist/mentions/MentionExtension.mjs.map +1 -1
  29. package/dist/mentions/MentionsList.js +19 -6
  30. package/dist/mentions/MentionsList.js.map +1 -1
  31. package/dist/mentions/MentionsList.mjs +19 -6
  32. package/dist/mentions/MentionsList.mjs.map +1 -1
  33. package/dist/types.js.map +1 -1
  34. package/dist/types.mjs.map +1 -1
  35. package/dist/version.js +1 -1
  36. package/dist/version.js.map +1 -1
  37. package/dist/version.mjs +1 -1
  38. package/dist/version.mjs.map +1 -1
  39. package/package.json +6 -6
  40. package/src/styles/index.css +5 -2
  41. package/styles.css +1 -1
  42. package/styles.css.map +1 -1
@@ -2,6 +2,7 @@
2
2
 
3
3
  var core = require('@liveblocks/core');
4
4
  var react = require('@liveblocks/react');
5
+ var _private = require('@liveblocks/react/_private');
5
6
  var yjs$1 = require('@liveblocks/yjs');
6
7
  var core$1 = require('@tiptap/core');
7
8
  var Collaboration = require('@tiptap/extension-collaboration');
@@ -10,6 +11,7 @@ var React = require('react');
10
11
  var yjs = require('yjs');
11
12
  var CommentsExtension = require('./comments/CommentsExtension.js');
12
13
  var MentionExtension = require('./mentions/MentionExtension.js');
14
+ var types = require('./types.js');
13
15
 
14
16
  const providersMap = /* @__PURE__ */ new Map();
15
17
  const docMap = /* @__PURE__ */ new Map();
@@ -41,8 +43,19 @@ const LiveblocksCollab = Collaboration.extend({
41
43
  });
42
44
  const useLiveblocksExtension = () => {
43
45
  const room = react.useRoom();
44
- React.useEffect(() => {
45
- }, [room]);
46
+ react.useCommentsErrorListener((error) => {
47
+ if (error instanceof _private.CreateThreadError) {
48
+ }
49
+ });
50
+ const client = react.useClient();
51
+ const store = _private.getUmbrellaStoreForClient(client);
52
+ const roomId = room.id;
53
+ const reportTextEditorType = React.useCallback(
54
+ (field) => {
55
+ room[core.kInternal].reportTextEditor(core.TextEditorType.TipTap, field);
56
+ },
57
+ [room]
58
+ );
46
59
  const onCreateMention = React.useCallback(
47
60
  (userId, notificationId) => {
48
61
  try {
@@ -80,19 +93,64 @@ const useLiveblocksExtension = () => {
80
93
  color: self.info.color
81
94
  });
82
95
  }
83
- this.storage.unsub = room.events.self.subscribe(({ info }) => {
84
- if (!info) {
85
- return;
86
- }
87
- const { name, color } = info;
88
- const { user } = this.storage.provider.awareness.getLocalState();
89
- if (name !== user?.name || color !== user?.color) {
90
- this.editor.commands.updateUser({ name, color });
91
- }
92
- });
96
+ this.storage.unsubs.push(
97
+ room.events.self.subscribe(({ info }) => {
98
+ if (!info) {
99
+ return;
100
+ }
101
+ const { name, color } = info;
102
+ const { user } = this.storage.provider.awareness.getLocalState();
103
+ if (name !== user?.name || color !== user?.color) {
104
+ this.editor.commands.updateUser({ name, color });
105
+ }
106
+ })
107
+ );
108
+ if (this.options.comments) {
109
+ const commentMarkType = core$1.getMarkType(
110
+ types.LIVEBLOCKS_COMMENT_MARK_TYPE,
111
+ this.editor.schema
112
+ );
113
+ this.storage.unsubs.push(
114
+ store.subscribe(() => {
115
+ const threadMap = new Map(
116
+ store.getFullState().threadsDB.findMany(roomId, { resolved: false }, "asc").map((thread) => [thread.id, true])
117
+ );
118
+ function isComment(mark) {
119
+ return mark.type.name === types.LIVEBLOCKS_COMMENT_MARK_TYPE;
120
+ }
121
+ this.editor.state.doc.descendants((node, pos) => {
122
+ node.marks.forEach((mark) => {
123
+ if (isComment(mark)) {
124
+ const markThreadId = mark.attrs.threadId;
125
+ const isOrphan = !threadMap.has(markThreadId);
126
+ if (isOrphan !== mark.attrs.orphan) {
127
+ const { tr } = this.editor.state;
128
+ const trimmedFrom = Math.max(pos, 0);
129
+ const trimmedTo = Math.min(
130
+ pos + node.nodeSize,
131
+ this.editor.state.doc.content.size - 1
132
+ );
133
+ tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);
134
+ tr.addMark(
135
+ trimmedFrom,
136
+ trimmedTo,
137
+ commentMarkType.create({
138
+ ...mark.attrs,
139
+ orphan: isOrphan
140
+ })
141
+ );
142
+ this.editor.view.dispatch(tr);
143
+ }
144
+ }
145
+ });
146
+ });
147
+ })
148
+ );
149
+ }
150
+ reportTextEditorType(this.options.field ?? "default");
93
151
  },
94
152
  onDestroy() {
95
- this.storage.unsub();
153
+ this.storage.unsubs.forEach((unsub) => unsub());
96
154
  },
97
155
  addStorage() {
98
156
  if (!providersMap.has(room.id)) {
@@ -103,8 +161,7 @@ const useLiveblocksExtension = () => {
103
161
  return {
104
162
  doc: docMap.get(room.id),
105
163
  provider: providersMap.get(room.id),
106
- unsub: () => {
107
- }
164
+ unsubs: []
108
165
  };
109
166
  },
110
167
  addOptions() {
@@ -1 +1 @@
1
- {"version":3,"file":"LiveblocksExtension.js","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport { useCallback, useEffect } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\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 useEffect(() => {\n // Report that this is lexical and root is the rootKey\n // TODO: put this back in\n // room[kInternal].reportTextEditor(TextEditorType.TipTap, \"root\");\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 unsub: () => 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.unsub = 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 onDestroy() {\n this.storage.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 unsub: () => {},\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","useEffect","useCallback","kInternal","Extension","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 } 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,13 +1,15 @@
1
- import { kInternal } from '@liveblocks/core';
2
- import { useRoom } from '@liveblocks/react';
1
+ import { kInternal, TextEditorType } from '@liveblocks/core';
2
+ import { useRoom, useCommentsErrorListener, useClient } from '@liveblocks/react';
3
+ import { CreateThreadError, getUmbrellaStoreForClient } from '@liveblocks/react/_private';
3
4
  import { LiveblocksYjsProvider } from '@liveblocks/yjs';
4
- import { Extension } from '@tiptap/core';
5
+ import { Extension, getMarkType } from '@tiptap/core';
5
6
  import Collaboration from '@tiptap/extension-collaboration';
6
7
  import CollaborationCursor from '@tiptap/extension-collaboration-cursor';
7
- import { useEffect, useCallback } from 'react';
8
+ import { useCallback } from 'react';
8
9
  import { Doc } from 'yjs';
9
10
  import { CommentsExtension } from './comments/CommentsExtension.mjs';
10
11
  import { MentionExtension } from './mentions/MentionExtension.mjs';
12
+ import { LIVEBLOCKS_COMMENT_MARK_TYPE } from './types.mjs';
11
13
 
12
14
  const providersMap = /* @__PURE__ */ new Map();
13
15
  const docMap = /* @__PURE__ */ new Map();
@@ -39,8 +41,19 @@ const LiveblocksCollab = Collaboration.extend({
39
41
  });
40
42
  const useLiveblocksExtension = () => {
41
43
  const room = useRoom();
42
- useEffect(() => {
43
- }, [room]);
44
+ useCommentsErrorListener((error) => {
45
+ if (error instanceof CreateThreadError) {
46
+ }
47
+ });
48
+ const client = useClient();
49
+ const store = getUmbrellaStoreForClient(client);
50
+ const roomId = room.id;
51
+ const reportTextEditorType = useCallback(
52
+ (field) => {
53
+ room[kInternal].reportTextEditor(TextEditorType.TipTap, field);
54
+ },
55
+ [room]
56
+ );
44
57
  const onCreateMention = useCallback(
45
58
  (userId, notificationId) => {
46
59
  try {
@@ -78,19 +91,64 @@ const useLiveblocksExtension = () => {
78
91
  color: self.info.color
79
92
  });
80
93
  }
81
- this.storage.unsub = room.events.self.subscribe(({ info }) => {
82
- if (!info) {
83
- return;
84
- }
85
- const { name, color } = info;
86
- const { user } = this.storage.provider.awareness.getLocalState();
87
- if (name !== user?.name || color !== user?.color) {
88
- this.editor.commands.updateUser({ name, color });
89
- }
90
- });
94
+ this.storage.unsubs.push(
95
+ room.events.self.subscribe(({ info }) => {
96
+ if (!info) {
97
+ return;
98
+ }
99
+ const { name, color } = info;
100
+ const { user } = this.storage.provider.awareness.getLocalState();
101
+ if (name !== user?.name || color !== user?.color) {
102
+ this.editor.commands.updateUser({ name, color });
103
+ }
104
+ })
105
+ );
106
+ if (this.options.comments) {
107
+ const commentMarkType = getMarkType(
108
+ LIVEBLOCKS_COMMENT_MARK_TYPE,
109
+ this.editor.schema
110
+ );
111
+ this.storage.unsubs.push(
112
+ store.subscribe(() => {
113
+ const threadMap = new Map(
114
+ store.getFullState().threadsDB.findMany(roomId, { resolved: false }, "asc").map((thread) => [thread.id, true])
115
+ );
116
+ function isComment(mark) {
117
+ return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;
118
+ }
119
+ this.editor.state.doc.descendants((node, pos) => {
120
+ node.marks.forEach((mark) => {
121
+ if (isComment(mark)) {
122
+ const markThreadId = mark.attrs.threadId;
123
+ const isOrphan = !threadMap.has(markThreadId);
124
+ if (isOrphan !== mark.attrs.orphan) {
125
+ const { tr } = this.editor.state;
126
+ const trimmedFrom = Math.max(pos, 0);
127
+ const trimmedTo = Math.min(
128
+ pos + node.nodeSize,
129
+ this.editor.state.doc.content.size - 1
130
+ );
131
+ tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);
132
+ tr.addMark(
133
+ trimmedFrom,
134
+ trimmedTo,
135
+ commentMarkType.create({
136
+ ...mark.attrs,
137
+ orphan: isOrphan
138
+ })
139
+ );
140
+ this.editor.view.dispatch(tr);
141
+ }
142
+ }
143
+ });
144
+ });
145
+ })
146
+ );
147
+ }
148
+ reportTextEditorType(this.options.field ?? "default");
91
149
  },
92
150
  onDestroy() {
93
- this.storage.unsub();
151
+ this.storage.unsubs.forEach((unsub) => unsub());
94
152
  },
95
153
  addStorage() {
96
154
  if (!providersMap.has(room.id)) {
@@ -101,8 +159,7 @@ const useLiveblocksExtension = () => {
101
159
  return {
102
160
  doc: docMap.get(room.id),
103
161
  provider: providersMap.get(room.id),
104
- unsub: () => {
105
- }
162
+ unsubs: []
106
163
  };
107
164
  },
108
165
  addOptions() {
@@ -1 +1 @@
1
- {"version":3,"file":"LiveblocksExtension.mjs","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport { useCallback, useEffect } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\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 useEffect(() => {\n // Report that this is lexical and root is the rootKey\n // TODO: put this back in\n // room[kInternal].reportTextEditor(TextEditorType.TipTap, \"root\");\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 unsub: () => 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.unsub = 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 onDestroy() {\n this.storage.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 unsub: () => {},\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 } 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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"}
@@ -68,7 +68,7 @@ function AnchoredThreads({
68
68
  if (!pluginState)
69
69
  return;
70
70
  setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {
71
- const thread = threads.find((thread2) => thread2.id === threadId);
71
+ const thread = threads.find((thread2) => thread2.id === threadId && !thread2.resolved);
72
72
  if (!thread)
73
73
  return acc;
74
74
  acc.push({ thread, position });
@@ -76,16 +76,18 @@ function AnchoredThreads({
76
76
  }, []));
77
77
  handlePositionThreads();
78
78
  }, [pluginState, threads]);
79
- React.useLayoutEffect(() => {
80
- handlePositionThreads();
81
- }, [handlePositionThreads]);
79
+ React.useLayoutEffect(handlePositionThreads, [handlePositionThreads]);
82
80
  React.useEffect(() => {
83
81
  const observer = new ResizeObserver(handlePositionThreads);
82
+ const container = editor?.view.dom;
83
+ if (container) {
84
+ observer.observe(container);
85
+ }
84
86
  for (const element of elements.values()) {
85
87
  observer.observe(element);
86
88
  }
87
89
  return () => observer.disconnect();
88
- }, [elements, handlePositionThreads]);
90
+ }, [elements, editor, handlePositionThreads]);
89
91
  const onItemAdd = React.useCallback((id, el) => {
90
92
  setElements((prev) => new Map(prev).set(id, el));
91
93
  }, []);
@@ -1 +1 @@
1
- {"version":3,"file":"AnchoredThreads.js","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<M extends BaseMetadata = DM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n /**\n * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n\n const handleRef = useCallback(\n (el: HTMLDivElement) => {\n onItemAdd(thread.id, el);\n return () => onItemRemove(thread.id);\n },\n [thread.id, onItemAdd, onItemRemove]\n );\n\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["DefaultThread","useRef","useState","THREADS_PLUGIN_KEY","useCallback","getRectFromCoords","position","useEffect","thread","useLayoutEffect","classNames"],"mappings":"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"}
1
+ {"version":3,"file":"AnchoredThreads.js","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<M extends BaseMetadata = DM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n /**\n * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId && !thread.resolved);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(handlePositionThreads, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n const container = editor?.view.dom;\n if (container) {\n observer.observe(container);\n }\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, editor, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n\n const handleRef = useCallback(\n (el: HTMLDivElement) => {\n onItemAdd(thread.id, el);\n return () => onItemRemove(thread.id);\n },\n [thread.id, onItemAdd, onItemRemove]\n );\n\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["DefaultThread","useRef","useState","THREADS_PLUGIN_KEY","useCallback","getRectFromCoords","position","useEffect","thread","useLayoutEffect","classNames"],"mappings":"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"}
@@ -66,7 +66,7 @@ function AnchoredThreads({
66
66
  if (!pluginState)
67
67
  return;
68
68
  setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {
69
- const thread = threads.find((thread2) => thread2.id === threadId);
69
+ const thread = threads.find((thread2) => thread2.id === threadId && !thread2.resolved);
70
70
  if (!thread)
71
71
  return acc;
72
72
  acc.push({ thread, position });
@@ -74,16 +74,18 @@ function AnchoredThreads({
74
74
  }, []));
75
75
  handlePositionThreads();
76
76
  }, [pluginState, threads]);
77
- useLayoutEffect(() => {
78
- handlePositionThreads();
79
- }, [handlePositionThreads]);
77
+ useLayoutEffect(handlePositionThreads, [handlePositionThreads]);
80
78
  useEffect(() => {
81
79
  const observer = new ResizeObserver(handlePositionThreads);
80
+ const container = editor?.view.dom;
81
+ if (container) {
82
+ observer.observe(container);
83
+ }
82
84
  for (const element of elements.values()) {
83
85
  observer.observe(element);
84
86
  }
85
87
  return () => observer.disconnect();
86
- }, [elements, handlePositionThreads]);
88
+ }, [elements, editor, handlePositionThreads]);
87
89
  const onItemAdd = useCallback((id, el) => {
88
90
  setElements((prev) => new Map(prev).set(id, el));
89
91
  }, []);
@@ -1 +1 @@
1
- {"version":3,"file":"AnchoredThreads.mjs","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<M extends BaseMetadata = DM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n /**\n * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n\n const handleRef = useCallback(\n (el: HTMLDivElement) => {\n onItemAdd(thread.id, el);\n return () => onItemRemove(thread.id);\n },\n [thread.id, onItemAdd, onItemRemove]\n );\n\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["Thread","DefaultThread","position","thread"],"mappings":"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"}
1
+ {"version":3,"file":"AnchoredThreads.mjs","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<M extends BaseMetadata = DM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n /**\n * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId && !thread.resolved);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(handlePositionThreads, [handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n const container = editor?.view.dom;\n if (container) {\n observer.observe(container);\n }\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, editor, handlePositionThreads]);\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\n\n const handleRef = useCallback(\n (el: HTMLDivElement) => {\n onItemAdd(thread.id, el);\n return () => onItemRemove(thread.id);\n },\n [thread.id, onItemAdd, onItemRemove]\n );\n\n\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["Thread","DefaultThread","position","thread"],"mappings":"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"}
@@ -13,6 +13,15 @@ const Comment = core.Mark.create({
13
13
  keepOnSplit: true,
14
14
  addAttributes() {
15
15
  return {
16
+ orphan: {
17
+ parseHTML: (element) => !!element.getAttribute("data-orphan"),
18
+ renderHTML: (attributes) => {
19
+ return attributes.orphan ? {
20
+ "data-orphan": "true"
21
+ } : {};
22
+ },
23
+ default: false
24
+ },
16
25
  threadId: {
17
26
  parseHTML: (element) => element.getAttribute("data-lb-thread-id"),
18
27
  renderHTML: (attributes) => {
@@ -117,7 +126,14 @@ const Comment = core.Mark.create({
117
126
  selectThread(null);
118
127
  return;
119
128
  }
120
- const threadId = node.marks.find((mark) => mark.type === this.type)?.attrs.threadId;
129
+ const commentMark = node.marks.find(
130
+ (mark) => mark.type === this.type
131
+ );
132
+ if (commentMark?.attrs.orphan) {
133
+ selectThread(null);
134
+ return;
135
+ }
136
+ const threadId = commentMark?.attrs.threadId;
121
137
  selectThread(threadId ?? null);
122
138
  }
123
139
  }
@@ -127,6 +143,7 @@ const Comment = core.Mark.create({
127
143
  });
128
144
  const CommentsExtension = core.Extension.create({
129
145
  name: "liveblocksComments",
146
+ priority: 95,
130
147
  addExtensions() {
131
148
  return [Comment];
132
149
  },