@liveblocks/react-tiptap 2.16.1-ai2 → 2.16.1-ai3

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.
package/dist/utils.js CHANGED
@@ -58,8 +58,8 @@ const mapFragment = (fragment, callback) => {
58
58
  });
59
59
  return model.Fragment.from(content);
60
60
  };
61
- function getDomRangeFromSelection(selection, editor) {
62
- const { from, to } = typeof selection === "number" ? { from: selection, to: selection } : selection;
61
+ function getDomRange(editor, range) {
62
+ const { from, to } = range;
63
63
  const fromPos = editor.view.domAtPos(from);
64
64
  const endPos = editor.view.domAtPos(to);
65
65
  const domRange = document.createRange();
@@ -73,9 +73,91 @@ function compareSelections(a, b) {
73
73
  }
74
74
  return a.eq(b);
75
75
  }
76
+ const DOCUMENT_TEXT_TRUNCATION = "[\u2026]";
77
+ const DOCUMENT_TEXT_BLOCK_SEPARATOR = " ";
78
+ function getDocumentText(editor, maxLength = 1e4) {
79
+ const { selection, doc } = editor.state;
80
+ const selectionLength = selection.to - selection.from;
81
+ if (maxLength >= doc.content.size) {
82
+ return {
83
+ beforeSelection: doc.textBetween(
84
+ 0,
85
+ selection.from,
86
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
87
+ ),
88
+ selection: doc.textBetween(
89
+ selection.from,
90
+ selection.to,
91
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
92
+ ),
93
+ afterSelection: doc.textBetween(
94
+ selection.to,
95
+ doc.content.size,
96
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
97
+ )
98
+ };
99
+ } else if (selectionLength > maxLength) {
100
+ const selectionStart = doc.textBetween(
101
+ selection.from,
102
+ selection.from + Math.floor(maxLength / 2) - DOCUMENT_TEXT_TRUNCATION.length,
103
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
104
+ );
105
+ const selectionEnd = doc.textBetween(
106
+ selection.to - Math.floor(maxLength / 2) + DOCUMENT_TEXT_TRUNCATION.length,
107
+ selection.to,
108
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
109
+ );
110
+ return {
111
+ beforeSelection: "",
112
+ selection: `${selectionStart}${DOCUMENT_TEXT_TRUNCATION}${selectionEnd}`,
113
+ afterSelection: ""
114
+ };
115
+ } else {
116
+ let beforeLength = Math.min(
117
+ selection.from,
118
+ Math.floor((maxLength - selectionLength) / 2)
119
+ );
120
+ const afterLength = Math.min(
121
+ doc.content.size - selection.to,
122
+ maxLength - selectionLength - beforeLength
123
+ );
124
+ if (beforeLength + afterLength + selectionLength < maxLength) {
125
+ beforeLength = Math.min(
126
+ selection.from,
127
+ maxLength - selectionLength - afterLength
128
+ );
129
+ }
130
+ let beforeSelection = doc.textBetween(
131
+ Math.max(0, selection.from - beforeLength),
132
+ selection.from,
133
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
134
+ );
135
+ let afterSelection = doc.textBetween(
136
+ selection.to,
137
+ Math.min(doc.content.size, selection.to + afterLength),
138
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
139
+ );
140
+ if (selection.from - beforeLength > 0) {
141
+ beforeSelection = `${DOCUMENT_TEXT_TRUNCATION}${beforeSelection}`;
142
+ }
143
+ if (selection.to + afterLength < doc.content.size) {
144
+ afterSelection = `${afterSelection}${DOCUMENT_TEXT_TRUNCATION}`;
145
+ }
146
+ return {
147
+ beforeSelection,
148
+ selection: doc.textBetween(
149
+ selection.from,
150
+ selection.to,
151
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
152
+ ),
153
+ afterSelection
154
+ };
155
+ }
156
+ }
76
157
 
77
158
  exports.compareSelections = compareSelections;
78
- exports.getDomRangeFromSelection = getDomRangeFromSelection;
159
+ exports.getDocumentText = getDocumentText;
160
+ exports.getDomRange = getDomRange;
79
161
  exports.getMentionsFromNode = getMentionsFromNode;
80
162
  exports.getRangeFromRelativeSelections = getRangeFromRelativeSelections;
81
163
  exports.getRectFromCoords = getRectFromCoords;
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["import type { ClientRectObject } from \"@floating-ui/react-dom\";\nimport type { Editor, Range } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport { Fragment } from \"@tiptap/pm/model\";\nimport type { EditorState, Selection } from \"@tiptap/pm/state\";\nimport {\n getRelativeSelection,\n relativePositionToAbsolutePosition,\n ySyncPluginKey,\n} from \"y-prosemirror\";\nimport type { RelativePosition } from \"yjs\";\n\nimport type { YSyncPluginState } from \"./types\";\nimport { LIVEBLOCKS_MENTION_TYPE } from \"./types\";\n\nexport const getRelativeSelectionFromState = (state: EditorState) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState) return null;\n return getRelativeSelection(pluginState.binding, state);\n};\n\nexport const getRangeFromRelativeSelections = (\n pos: { anchor: RelativePosition; head: RelativePosition },\n state: EditorState\n) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState || !pluginState.binding) return { from: 0, to: 0 };\n const { doc, type, mapping } = pluginState.binding;\n const anchor =\n relativePositionToAbsolutePosition(doc, type, pos.anchor, mapping) ?? 0;\n const head =\n relativePositionToAbsolutePosition(doc, type, pos.head, mapping) ?? 0;\n\n const from = anchor > head ? head : anchor;\n const to = anchor > head ? anchor : head;\n return { from, to };\n};\n\nexport const getRectFromCoords = (coords: {\n top: number;\n left: number;\n right: number;\n bottom: number;\n}): ClientRectObject => {\n return {\n ...coords,\n x: coords.left,\n y: coords.top,\n width: coords.right - coords.left,\n height: coords.bottom - coords.top,\n };\n};\n\nexport const getMentionsFromNode = (\n node: ProseMirrorNode,\n range: Range\n): { notificationId: string; userId: string }[] => {\n const result: { notificationId: string; userId: string }[] = [];\n node.nodesBetween(range.from, range.to, (child) => {\n if (child.type.name === LIVEBLOCKS_MENTION_TYPE) {\n const mention = child.attrs as { id?: string; notificationId?: string };\n if (mention.id && mention.notificationId) {\n result.push({\n notificationId: mention.notificationId,\n userId: mention.id,\n });\n }\n }\n });\n return result;\n};\n\n// How to modify data in transformPasted, inspired by: https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901/4\nexport const mapFragment = (\n fragment: Fragment,\n callback: (\n node: ProseMirrorNode\n ) => ProseMirrorNode | ProseMirrorNode[] | Fragment | null\n): Fragment => {\n const content: ProseMirrorNode[] = [];\n fragment.forEach((node) => {\n if (node.content.childCount > 0) {\n content.push(\n node.type.create(node.attrs, mapFragment(node.content, callback))\n );\n return;\n }\n content.push(callback(node) as ProseMirrorNode);\n });\n\n return Fragment.from(content);\n};\n\nexport function getDomRangeFromSelection(\n selection: { from: number; to: number } | number,\n editor: Editor\n) {\n const { from, to } =\n typeof selection === \"number\"\n ? { from: selection, to: selection }\n : selection;\n const fromPos = editor.view.domAtPos(from);\n const endPos = editor.view.domAtPos(to);\n\n const domRange = document.createRange();\n domRange.setStart(fromPos.node, fromPos.offset);\n domRange.setEnd(endPos.node, endPos.offset);\n\n return domRange;\n}\n\nexport function compareSelections(\n a: Selection | null | undefined,\n b: Selection | null | undefined\n) {\n if (!a || !b) {\n return false;\n }\n\n return a.eq(b);\n}\n"],"names":["ySyncPluginKey","getRelativeSelection","relativePositionToAbsolutePosition","LIVEBLOCKS_MENTION_TYPE","Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["import type { ClientRectObject } from \"@floating-ui/react-dom\";\nimport type { Editor, Range } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport { Fragment } from \"@tiptap/pm/model\";\nimport type { EditorState, Selection } from \"@tiptap/pm/state\";\nimport {\n getRelativeSelection,\n relativePositionToAbsolutePosition,\n ySyncPluginKey,\n} from \"y-prosemirror\";\nimport type { RelativePosition } from \"yjs\";\n\nimport type { YSyncPluginState } from \"./types\";\nimport { LIVEBLOCKS_MENTION_TYPE } from \"./types\";\n\nexport const getRelativeSelectionFromState = (state: EditorState) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState) return null;\n return getRelativeSelection(pluginState.binding, state);\n};\n\nexport const getRangeFromRelativeSelections = (\n pos: { anchor: RelativePosition; head: RelativePosition },\n state: EditorState\n) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState || !pluginState.binding) return { from: 0, to: 0 };\n const { doc, type, mapping } = pluginState.binding;\n const anchor =\n relativePositionToAbsolutePosition(doc, type, pos.anchor, mapping) ?? 0;\n const head =\n relativePositionToAbsolutePosition(doc, type, pos.head, mapping) ?? 0;\n\n const from = anchor > head ? head : anchor;\n const to = anchor > head ? anchor : head;\n return { from, to };\n};\n\nexport const getRectFromCoords = (coords: {\n top: number;\n left: number;\n right: number;\n bottom: number;\n}): ClientRectObject => {\n return {\n ...coords,\n x: coords.left,\n y: coords.top,\n width: coords.right - coords.left,\n height: coords.bottom - coords.top,\n };\n};\n\nexport const getMentionsFromNode = (\n node: ProseMirrorNode,\n range: Range\n): { notificationId: string; userId: string }[] => {\n const result: { notificationId: string; userId: string }[] = [];\n node.nodesBetween(range.from, range.to, (child) => {\n if (child.type.name === LIVEBLOCKS_MENTION_TYPE) {\n const mention = child.attrs as { id?: string; notificationId?: string };\n if (mention.id && mention.notificationId) {\n result.push({\n notificationId: mention.notificationId,\n userId: mention.id,\n });\n }\n }\n });\n return result;\n};\n\n// How to modify data in transformPasted, inspired by: https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901/4\nexport const mapFragment = (\n fragment: Fragment,\n callback: (\n node: ProseMirrorNode\n ) => ProseMirrorNode | ProseMirrorNode[] | Fragment | null\n): Fragment => {\n const content: ProseMirrorNode[] = [];\n fragment.forEach((node) => {\n if (node.content.childCount > 0) {\n content.push(\n node.type.create(node.attrs, mapFragment(node.content, callback))\n );\n return;\n }\n content.push(callback(node) as ProseMirrorNode);\n });\n\n return Fragment.from(content);\n};\n\nexport function getDomRange(editor: Editor, range: Range) {\n const { from, to } = range;\n const fromPos = editor.view.domAtPos(from);\n const endPos = editor.view.domAtPos(to);\n\n const domRange = document.createRange();\n domRange.setStart(fromPos.node, fromPos.offset);\n domRange.setEnd(endPos.node, endPos.offset);\n\n return domRange;\n}\n\nexport function compareSelections(\n a: Selection | null | undefined,\n b: Selection | null | undefined\n) {\n if (!a || !b) {\n return false;\n }\n\n return a.eq(b);\n}\n\nexport type DocumentText = {\n beforeSelection: string;\n selection: string;\n afterSelection: string;\n};\n\nconst DOCUMENT_TEXT_TRUNCATION = \"[…]\";\nconst DOCUMENT_TEXT_BLOCK_SEPARATOR = \" \";\n\nexport function getDocumentText(\n editor: Editor,\n maxLength = 10_000\n): DocumentText {\n const { selection, doc } = editor.state;\n\n const selectionLength = selection.to - selection.from;\n\n if (maxLength >= doc.content.size) {\n // If the document is smaller than the maximum length, return the entire document\n return {\n beforeSelection: doc.textBetween(\n 0,\n selection.from,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n selection: doc.textBetween(\n selection.from,\n selection.to,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n afterSelection: doc.textBetween(\n selection.to,\n doc.content.size,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n };\n } else if (selectionLength > maxLength) {\n // If the selection is too large, truncate its middle to still allow continuations\n const selectionStart = doc.textBetween(\n selection.from,\n selection.from +\n Math.floor(maxLength / 2) -\n DOCUMENT_TEXT_TRUNCATION.length,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n const selectionEnd = doc.textBetween(\n selection.to -\n Math.floor(maxLength / 2) +\n DOCUMENT_TEXT_TRUNCATION.length,\n selection.to,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n\n return {\n beforeSelection: \"\",\n selection: `${selectionStart}${DOCUMENT_TEXT_TRUNCATION}${selectionEnd}`,\n afterSelection: \"\",\n };\n } else {\n // If the selection is smaller than (or equal to) the maximum length, extract as much as possible from the document around the selection\n\n // Start by taking as much as possible after the selection\n let beforeLength = Math.min(\n selection.from,\n Math.floor((maxLength - selectionLength) / 2)\n );\n const afterLength = Math.min(\n doc.content.size - selection.to,\n maxLength - selectionLength - beforeLength\n );\n\n // If needed (e.g. the selection is near the end), compensate before the selection\n if (beforeLength + afterLength + selectionLength < maxLength) {\n beforeLength = Math.min(\n selection.from,\n maxLength - selectionLength - afterLength\n );\n }\n\n let beforeSelection = doc.textBetween(\n Math.max(0, selection.from - beforeLength),\n selection.from,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n let afterSelection = doc.textBetween(\n selection.to,\n Math.min(doc.content.size, selection.to + afterLength),\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n\n // Add leading truncation if `beforeSelection` doesn't contain the document's start\n if (selection.from - beforeLength > 0) {\n beforeSelection = `${DOCUMENT_TEXT_TRUNCATION}${beforeSelection}`;\n }\n\n // Add trailing truncation if `afterSelection` doesn't contain the document's end\n if (selection.to + afterLength < doc.content.size) {\n afterSelection = `${afterSelection}${DOCUMENT_TEXT_TRUNCATION}`;\n }\n\n return {\n beforeSelection,\n selection: doc.textBetween(\n selection.from,\n selection.to,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n afterSelection,\n };\n }\n}\n"],"names":["ySyncPluginKey","getRelativeSelection","relativePositionToAbsolutePosition","LIVEBLOCKS_MENTION_TYPE","Fragment"],"mappings":"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"}
package/dist/utils.mjs CHANGED
@@ -56,8 +56,8 @@ const mapFragment = (fragment, callback) => {
56
56
  });
57
57
  return Fragment.from(content);
58
58
  };
59
- function getDomRangeFromSelection(selection, editor) {
60
- const { from, to } = typeof selection === "number" ? { from: selection, to: selection } : selection;
59
+ function getDomRange(editor, range) {
60
+ const { from, to } = range;
61
61
  const fromPos = editor.view.domAtPos(from);
62
62
  const endPos = editor.view.domAtPos(to);
63
63
  const domRange = document.createRange();
@@ -71,6 +71,87 @@ function compareSelections(a, b) {
71
71
  }
72
72
  return a.eq(b);
73
73
  }
74
+ const DOCUMENT_TEXT_TRUNCATION = "[\u2026]";
75
+ const DOCUMENT_TEXT_BLOCK_SEPARATOR = " ";
76
+ function getDocumentText(editor, maxLength = 1e4) {
77
+ const { selection, doc } = editor.state;
78
+ const selectionLength = selection.to - selection.from;
79
+ if (maxLength >= doc.content.size) {
80
+ return {
81
+ beforeSelection: doc.textBetween(
82
+ 0,
83
+ selection.from,
84
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
85
+ ),
86
+ selection: doc.textBetween(
87
+ selection.from,
88
+ selection.to,
89
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
90
+ ),
91
+ afterSelection: doc.textBetween(
92
+ selection.to,
93
+ doc.content.size,
94
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
95
+ )
96
+ };
97
+ } else if (selectionLength > maxLength) {
98
+ const selectionStart = doc.textBetween(
99
+ selection.from,
100
+ selection.from + Math.floor(maxLength / 2) - DOCUMENT_TEXT_TRUNCATION.length,
101
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
102
+ );
103
+ const selectionEnd = doc.textBetween(
104
+ selection.to - Math.floor(maxLength / 2) + DOCUMENT_TEXT_TRUNCATION.length,
105
+ selection.to,
106
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
107
+ );
108
+ return {
109
+ beforeSelection: "",
110
+ selection: `${selectionStart}${DOCUMENT_TEXT_TRUNCATION}${selectionEnd}`,
111
+ afterSelection: ""
112
+ };
113
+ } else {
114
+ let beforeLength = Math.min(
115
+ selection.from,
116
+ Math.floor((maxLength - selectionLength) / 2)
117
+ );
118
+ const afterLength = Math.min(
119
+ doc.content.size - selection.to,
120
+ maxLength - selectionLength - beforeLength
121
+ );
122
+ if (beforeLength + afterLength + selectionLength < maxLength) {
123
+ beforeLength = Math.min(
124
+ selection.from,
125
+ maxLength - selectionLength - afterLength
126
+ );
127
+ }
128
+ let beforeSelection = doc.textBetween(
129
+ Math.max(0, selection.from - beforeLength),
130
+ selection.from,
131
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
132
+ );
133
+ let afterSelection = doc.textBetween(
134
+ selection.to,
135
+ Math.min(doc.content.size, selection.to + afterLength),
136
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
137
+ );
138
+ if (selection.from - beforeLength > 0) {
139
+ beforeSelection = `${DOCUMENT_TEXT_TRUNCATION}${beforeSelection}`;
140
+ }
141
+ if (selection.to + afterLength < doc.content.size) {
142
+ afterSelection = `${afterSelection}${DOCUMENT_TEXT_TRUNCATION}`;
143
+ }
144
+ return {
145
+ beforeSelection,
146
+ selection: doc.textBetween(
147
+ selection.from,
148
+ selection.to,
149
+ DOCUMENT_TEXT_BLOCK_SEPARATOR
150
+ ),
151
+ afterSelection
152
+ };
153
+ }
154
+ }
74
155
 
75
- export { compareSelections, getDomRangeFromSelection, getMentionsFromNode, getRangeFromRelativeSelections, getRectFromCoords, getRelativeSelectionFromState, mapFragment };
156
+ export { compareSelections, getDocumentText, getDomRange, getMentionsFromNode, getRangeFromRelativeSelections, getRectFromCoords, getRelativeSelectionFromState, mapFragment };
76
157
  //# sourceMappingURL=utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","sources":["../src/utils.ts"],"sourcesContent":["import type { ClientRectObject } from \"@floating-ui/react-dom\";\nimport type { Editor, Range } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport { Fragment } from \"@tiptap/pm/model\";\nimport type { EditorState, Selection } from \"@tiptap/pm/state\";\nimport {\n getRelativeSelection,\n relativePositionToAbsolutePosition,\n ySyncPluginKey,\n} from \"y-prosemirror\";\nimport type { RelativePosition } from \"yjs\";\n\nimport type { YSyncPluginState } from \"./types\";\nimport { LIVEBLOCKS_MENTION_TYPE } from \"./types\";\n\nexport const getRelativeSelectionFromState = (state: EditorState) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState) return null;\n return getRelativeSelection(pluginState.binding, state);\n};\n\nexport const getRangeFromRelativeSelections = (\n pos: { anchor: RelativePosition; head: RelativePosition },\n state: EditorState\n) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState || !pluginState.binding) return { from: 0, to: 0 };\n const { doc, type, mapping } = pluginState.binding;\n const anchor =\n relativePositionToAbsolutePosition(doc, type, pos.anchor, mapping) ?? 0;\n const head =\n relativePositionToAbsolutePosition(doc, type, pos.head, mapping) ?? 0;\n\n const from = anchor > head ? head : anchor;\n const to = anchor > head ? anchor : head;\n return { from, to };\n};\n\nexport const getRectFromCoords = (coords: {\n top: number;\n left: number;\n right: number;\n bottom: number;\n}): ClientRectObject => {\n return {\n ...coords,\n x: coords.left,\n y: coords.top,\n width: coords.right - coords.left,\n height: coords.bottom - coords.top,\n };\n};\n\nexport const getMentionsFromNode = (\n node: ProseMirrorNode,\n range: Range\n): { notificationId: string; userId: string }[] => {\n const result: { notificationId: string; userId: string }[] = [];\n node.nodesBetween(range.from, range.to, (child) => {\n if (child.type.name === LIVEBLOCKS_MENTION_TYPE) {\n const mention = child.attrs as { id?: string; notificationId?: string };\n if (mention.id && mention.notificationId) {\n result.push({\n notificationId: mention.notificationId,\n userId: mention.id,\n });\n }\n }\n });\n return result;\n};\n\n// How to modify data in transformPasted, inspired by: https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901/4\nexport const mapFragment = (\n fragment: Fragment,\n callback: (\n node: ProseMirrorNode\n ) => ProseMirrorNode | ProseMirrorNode[] | Fragment | null\n): Fragment => {\n const content: ProseMirrorNode[] = [];\n fragment.forEach((node) => {\n if (node.content.childCount > 0) {\n content.push(\n node.type.create(node.attrs, mapFragment(node.content, callback))\n );\n return;\n }\n content.push(callback(node) as ProseMirrorNode);\n });\n\n return Fragment.from(content);\n};\n\nexport function getDomRangeFromSelection(\n selection: { from: number; to: number } | number,\n editor: Editor\n) {\n const { from, to } =\n typeof selection === \"number\"\n ? { from: selection, to: selection }\n : selection;\n const fromPos = editor.view.domAtPos(from);\n const endPos = editor.view.domAtPos(to);\n\n const domRange = document.createRange();\n domRange.setStart(fromPos.node, fromPos.offset);\n domRange.setEnd(endPos.node, endPos.offset);\n\n return domRange;\n}\n\nexport function compareSelections(\n a: Selection | null | undefined,\n b: Selection | null | undefined\n) {\n if (!a || !b) {\n return false;\n }\n\n return a.eq(b);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.mjs","sources":["../src/utils.ts"],"sourcesContent":["import type { ClientRectObject } from \"@floating-ui/react-dom\";\nimport type { Editor, Range } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport { Fragment } from \"@tiptap/pm/model\";\nimport type { EditorState, Selection } from \"@tiptap/pm/state\";\nimport {\n getRelativeSelection,\n relativePositionToAbsolutePosition,\n ySyncPluginKey,\n} from \"y-prosemirror\";\nimport type { RelativePosition } from \"yjs\";\n\nimport type { YSyncPluginState } from \"./types\";\nimport { LIVEBLOCKS_MENTION_TYPE } from \"./types\";\n\nexport const getRelativeSelectionFromState = (state: EditorState) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState) return null;\n return getRelativeSelection(pluginState.binding, state);\n};\n\nexport const getRangeFromRelativeSelections = (\n pos: { anchor: RelativePosition; head: RelativePosition },\n state: EditorState\n) => {\n const pluginState = ySyncPluginKey.getState(state) as YSyncPluginState;\n if (!pluginState || !pluginState.binding) return { from: 0, to: 0 };\n const { doc, type, mapping } = pluginState.binding;\n const anchor =\n relativePositionToAbsolutePosition(doc, type, pos.anchor, mapping) ?? 0;\n const head =\n relativePositionToAbsolutePosition(doc, type, pos.head, mapping) ?? 0;\n\n const from = anchor > head ? head : anchor;\n const to = anchor > head ? anchor : head;\n return { from, to };\n};\n\nexport const getRectFromCoords = (coords: {\n top: number;\n left: number;\n right: number;\n bottom: number;\n}): ClientRectObject => {\n return {\n ...coords,\n x: coords.left,\n y: coords.top,\n width: coords.right - coords.left,\n height: coords.bottom - coords.top,\n };\n};\n\nexport const getMentionsFromNode = (\n node: ProseMirrorNode,\n range: Range\n): { notificationId: string; userId: string }[] => {\n const result: { notificationId: string; userId: string }[] = [];\n node.nodesBetween(range.from, range.to, (child) => {\n if (child.type.name === LIVEBLOCKS_MENTION_TYPE) {\n const mention = child.attrs as { id?: string; notificationId?: string };\n if (mention.id && mention.notificationId) {\n result.push({\n notificationId: mention.notificationId,\n userId: mention.id,\n });\n }\n }\n });\n return result;\n};\n\n// How to modify data in transformPasted, inspired by: https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901/4\nexport const mapFragment = (\n fragment: Fragment,\n callback: (\n node: ProseMirrorNode\n ) => ProseMirrorNode | ProseMirrorNode[] | Fragment | null\n): Fragment => {\n const content: ProseMirrorNode[] = [];\n fragment.forEach((node) => {\n if (node.content.childCount > 0) {\n content.push(\n node.type.create(node.attrs, mapFragment(node.content, callback))\n );\n return;\n }\n content.push(callback(node) as ProseMirrorNode);\n });\n\n return Fragment.from(content);\n};\n\nexport function getDomRange(editor: Editor, range: Range) {\n const { from, to } = range;\n const fromPos = editor.view.domAtPos(from);\n const endPos = editor.view.domAtPos(to);\n\n const domRange = document.createRange();\n domRange.setStart(fromPos.node, fromPos.offset);\n domRange.setEnd(endPos.node, endPos.offset);\n\n return domRange;\n}\n\nexport function compareSelections(\n a: Selection | null | undefined,\n b: Selection | null | undefined\n) {\n if (!a || !b) {\n return false;\n }\n\n return a.eq(b);\n}\n\nexport type DocumentText = {\n beforeSelection: string;\n selection: string;\n afterSelection: string;\n};\n\nconst DOCUMENT_TEXT_TRUNCATION = \"[…]\";\nconst DOCUMENT_TEXT_BLOCK_SEPARATOR = \" \";\n\nexport function getDocumentText(\n editor: Editor,\n maxLength = 10_000\n): DocumentText {\n const { selection, doc } = editor.state;\n\n const selectionLength = selection.to - selection.from;\n\n if (maxLength >= doc.content.size) {\n // If the document is smaller than the maximum length, return the entire document\n return {\n beforeSelection: doc.textBetween(\n 0,\n selection.from,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n selection: doc.textBetween(\n selection.from,\n selection.to,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n afterSelection: doc.textBetween(\n selection.to,\n doc.content.size,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n };\n } else if (selectionLength > maxLength) {\n // If the selection is too large, truncate its middle to still allow continuations\n const selectionStart = doc.textBetween(\n selection.from,\n selection.from +\n Math.floor(maxLength / 2) -\n DOCUMENT_TEXT_TRUNCATION.length,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n const selectionEnd = doc.textBetween(\n selection.to -\n Math.floor(maxLength / 2) +\n DOCUMENT_TEXT_TRUNCATION.length,\n selection.to,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n\n return {\n beforeSelection: \"\",\n selection: `${selectionStart}${DOCUMENT_TEXT_TRUNCATION}${selectionEnd}`,\n afterSelection: \"\",\n };\n } else {\n // If the selection is smaller than (or equal to) the maximum length, extract as much as possible from the document around the selection\n\n // Start by taking as much as possible after the selection\n let beforeLength = Math.min(\n selection.from,\n Math.floor((maxLength - selectionLength) / 2)\n );\n const afterLength = Math.min(\n doc.content.size - selection.to,\n maxLength - selectionLength - beforeLength\n );\n\n // If needed (e.g. the selection is near the end), compensate before the selection\n if (beforeLength + afterLength + selectionLength < maxLength) {\n beforeLength = Math.min(\n selection.from,\n maxLength - selectionLength - afterLength\n );\n }\n\n let beforeSelection = doc.textBetween(\n Math.max(0, selection.from - beforeLength),\n selection.from,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n let afterSelection = doc.textBetween(\n selection.to,\n Math.min(doc.content.size, selection.to + afterLength),\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n );\n\n // Add leading truncation if `beforeSelection` doesn't contain the document's start\n if (selection.from - beforeLength > 0) {\n beforeSelection = `${DOCUMENT_TEXT_TRUNCATION}${beforeSelection}`;\n }\n\n // Add trailing truncation if `afterSelection` doesn't contain the document's end\n if (selection.to + afterLength < doc.content.size) {\n afterSelection = `${afterSelection}${DOCUMENT_TEXT_TRUNCATION}`;\n }\n\n return {\n beforeSelection,\n selection: doc.textBetween(\n selection.from,\n selection.to,\n DOCUMENT_TEXT_BLOCK_SEPARATOR\n ),\n afterSelection,\n };\n }\n}\n"],"names":[],"mappings":"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"}
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.16.1-ai2" === "string" && "2.16.1-ai2";
4
+ const PKG_VERSION = typeof "2.16.1-ai3" === "string" && "2.16.1-ai3";
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.16.1-ai2" === "string" && "2.16.1-ai2";
2
+ const PKG_VERSION = typeof "2.16.1-ai3" === "string" && "2.16.1-ai3";
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.16.1-ai2",
3
+ "version": "2.16.1-ai3",
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.16.1-ai2",
46
- "@liveblocks/core": "2.16.1-ai2",
47
- "@liveblocks/react": "2.16.1-ai2",
48
- "@liveblocks/react-ui": "2.16.1-ai2",
49
- "@liveblocks/yjs": "2.16.1-ai2",
45
+ "@liveblocks/client": "2.16.1-ai3",
46
+ "@liveblocks/core": "2.16.1-ai3",
47
+ "@liveblocks/react": "2.16.1-ai3",
48
+ "@liveblocks/react-ui": "2.16.1-ai3",
49
+ "@liveblocks/yjs": "2.16.1-ai3",
50
50
  "@radix-ui/react-select": "^2.1.2",
51
51
  "@radix-ui/react-toggle": "^1.1.0",
52
52
  "@tiptap/core": "^2.7.2",
@@ -276,8 +276,16 @@
276
276
  }
277
277
 
278
278
  .lb-tiptap-ai-toolbar-portal {
279
+ display: flex;
280
+ flex-direction: column;
281
+ gap: 8px;
279
282
  inline-size: var(--lb-tiptap-editor-width);
280
283
  outline: none;
284
+ pointer-events: none;
285
+
286
+ &:where([data-liveblocks-ai-toolbar-flip]) {
287
+ flex-direction: column-reverse;
288
+ }
281
289
  }
282
290
 
283
291
  .lb-tiptap-ai-toolbar-container {
@@ -294,38 +302,16 @@
294
302
  display: flex;
295
303
  flex-direction: column;
296
304
  color: var(--lb-foreground);
305
+ pointer-events: auto;
297
306
  }
298
307
 
299
- .lb-tiptap-ai-toolbar-output-container {
300
- display: flex;
301
- flex-direction: column;
302
- border-block-end: 1px solid var(--lb-foreground-subtle);
303
- }
304
-
305
- .lb-tiptap-ai-toolbar-output {
306
- &::before {
307
- content: "\FEFF";
308
- display: inline-block;
309
- vertical-align: middle;
310
- user-select: none;
311
- }
312
-
313
- /* stylelint-disable-next-line selector-class-pattern */
314
- :where(.collaboration-cursor__label) {
315
- display: flex;
316
- gap: 0.1875em;
317
- align-items: center;
318
-
319
- :where(.lb-icon-container) {
320
- margin-inline-start: -0.125em;
308
+ .lb-tiptap-ai-toolbar-output-container,
309
+ .lb-tiptap-ai-toolbar-content {
310
+ max-block-size: calc(6lh + 2 * var(--lb-tiptap-ai-toolbar-padding));
321
311
 
322
- &,
323
- & svg {
324
- inline-size: 1em;
325
- block-size: 1em;
326
- }
327
- }
328
- }
312
+ /* overflow-block: auto; doesn't work as expected */
313
+ /* stylelint-disable-next-line plugin/use-logical-properties-and-values */
314
+ overflow-y: auto;
329
315
  }
330
316
 
331
317
  .lb-tiptap-ai-toolbar-content {
@@ -333,30 +319,26 @@
333
319
  grid-template-columns: auto 1fr auto;
334
320
  inline-size: 100%;
335
321
  min-inline-size: 0;
322
+ padding: var(--lb-tiptap-ai-toolbar-padding);
336
323
  outline: none;
337
324
  }
338
325
 
339
- .lb-tiptap-ai-toolbar-output-container,
340
- .lb-tiptap-ai-toolbar-content {
341
- max-block-size: calc(6lh + 2 * var(--lb-tiptap-ai-toolbar-padding));
342
- padding: var(--lb-tiptap-ai-toolbar-padding);
326
+ .lb-tiptap-ai-toolbar-output-container {
327
+ --lb-line-height-crop: calc(1lh - 1em) / -2;
343
328
 
344
- /* overflow-block: auto; doesn't work as expected */
345
- /* stylelint-disable-next-line plugin/use-logical-properties-and-values */
346
- overflow-y: auto;
329
+ display: flex;
330
+ flex-direction: column;
331
+ padding: calc(var(--lb-spacing) + var(--lb-line-height-crop))
332
+ var(--lb-spacing);
333
+ border-block-end: 1px solid var(--lb-foreground-subtle);
347
334
  }
348
335
 
349
- .lb-tiptap-ai-toolbar-custom-prompt-container {
350
- position: relative;
351
- z-index: auto;
352
- display: grid;
353
- margin-block: calc(-1 * var(--lb-tiptap-ai-toolbar-padding));
354
- line-height: $lb-button-size;
355
-
336
+ .lb-tiptap-ai-toolbar-output {
356
337
  &::before {
357
- /* The space is important when handling new lines */
358
- content: attr(data-value) " ";
359
- visibility: hidden;
338
+ content: "\FEFF";
339
+ display: inline-block;
340
+ vertical-align: middle;
341
+ user-select: none;
360
342
  }
361
343
  }
362
344
 
@@ -394,6 +376,19 @@
394
376
  }
395
377
  }
396
378
 
379
+ .lb-tiptap-ai-toolbar-custom-prompt-container {
380
+ position: relative;
381
+ z-index: auto;
382
+ display: grid;
383
+ margin-block: calc(-1 * var(--lb-tiptap-ai-toolbar-padding));
384
+
385
+ &::before {
386
+ /* The space is important when handling new lines */
387
+ content: attr(data-value) " ";
388
+ visibility: hidden;
389
+ }
390
+ }
391
+
397
392
  .lb-tiptap-ai-toolbar-custom-prompt,
398
393
  .lb-tiptap-ai-toolbar-custom-prompt-container::before {
399
394
  /* Overlap the textarea and its value to auto-size it */
@@ -401,12 +396,42 @@
401
396
  box-sizing: inherit;
402
397
  inline-size: 100%;
403
398
  min-inline-size: 0;
404
- padding: var(--lb-tiptap-ai-toolbar-padding);
399
+ padding: calc(
400
+ var(--lb-tiptap-ai-toolbar-padding) + ($lb-button-size - 1lh) / 2
401
+ )
402
+ var(--lb-tiptap-ai-toolbar-padding);
405
403
  font: inherit;
406
404
  letter-spacing: inherit;
407
405
  white-space: pre-wrap;
408
406
  }
409
407
 
408
+ .lb-tiptap-ai-toolbar-error {
409
+ --lb-dynamic-background: var(--lb-background-destructive-subtle);
410
+
411
+ position: relative;
412
+ display: flex;
413
+ gap: calc(0.5 * var(--lb-spacing));
414
+ align-items: center;
415
+ padding: var(--lb-tiptap-ai-toolbar-padding);
416
+ background: var(--lb-dynamic-background);
417
+ color: var(--lb-destructive);
418
+ font-size: 0.875em;
419
+ text-wrap: balance;
420
+
421
+ &::after {
422
+ content: "";
423
+ position: absolute;
424
+ inset: 0;
425
+ border-block-start: 1px solid var(--lb-destructive-moderate);
426
+ opacity: 0.35;
427
+ pointer-events: none;
428
+ }
429
+
430
+ :where(.lb-icon-container) {
431
+ color: var(--lb-destructive-secondary);
432
+ }
433
+ }
434
+
410
435
  .lb-tiptap-ai-toolbar-halo {
411
436
  --lb-tiptap-ai-toolbar-halo-blur: 16px;
412
437
  --lb-tiptap-ai-toolbar-halo-outset: 8px;
@@ -422,6 +447,8 @@
422
447
  pointer-events: none;
423
448
  transition-duration: 1s;
424
449
  transition-property: opacity;
450
+ animation: lb-animation-ai-toolbar-halo-scale-in 1s
451
+ cubic-bezier(0.165, 0.84, 0.44, 1) both;
425
452
 
426
453
  &:where(:not([data-active])) {
427
454
  opacity: 0.5;
@@ -440,7 +467,7 @@
440
467
  content: "";
441
468
  position: absolute;
442
469
  inset: 0;
443
- opacity: 0.25;
470
+ opacity: 0.175;
444
471
  animation-timing-function: cubic-bezier(0.455, 0.03, 0.515, 0.955);
445
472
  animation-iteration-count: infinite;
446
473
  }
@@ -474,8 +501,7 @@
474
501
  transparent 80%
475
502
  );
476
503
  background-position: top left;
477
- background-size: 800px 100%;
478
- background-repeat: space;
504
+ background-size: 75% 100%;
479
505
  animation-name: lb-animation-ai-toolbar-halo-horizontal;
480
506
  animation-duration: 6s;
481
507
  animation-delay: -2s;
@@ -484,8 +510,6 @@
484
510
  }
485
511
 
486
512
  .lb-tiptap-ai-toolbar-halo-vertical {
487
- opacity: 0;
488
-
489
513
  &::before {
490
514
  inset-block-start: -50%;
491
515
  inline-size: 100%;
@@ -526,20 +550,20 @@
526
550
  }
527
551
 
528
552
  .lb-tiptap-ai-toolbar-thinking {
529
- display: flex;
530
- flex: 1 0 auto;
531
- align-items: center;
553
+ @include truncate;
554
+
555
+ align-self: center;
532
556
  min-inline-size: 0;
557
+ max-inline-size: fit-content;
533
558
  padding-inline: var(--lb-tiptap-ai-toolbar-padding);
534
- color: var(--lb-foreground-moderate);
535
- cursor: wait;
559
+ color: var(--lb-foreground-tertiary);
536
560
  user-select: none;
537
- animation: lb-animation-shimmer 8s linear infinite;
561
+ animation: lb-animation-ai-toolbar-shimmer 8s linear infinite;
538
562
  }
539
563
 
540
564
  .lb-tiptap-ai-toolbar-dropdown {
541
565
  inline-size: min(250px, 100%);
542
- margin-block-start: 8px;
566
+ pointer-events: auto;
543
567
  }
544
568
 
545
569
  /*************************************
@@ -600,6 +624,38 @@
600
624
  * Animations *
601
625
  *************************************/
602
626
 
627
+ @keyframes lb-animation-ai-toolbar-shimmer {
628
+ from,
629
+ to {
630
+ mask-image: linear-gradient(
631
+ 90deg,
632
+ #000 8%,
633
+ rgb(0 0 0 / 50%) 20%,
634
+ rgb(0 0 0 / 50%) 80%,
635
+ #000 92%
636
+ );
637
+ mask-size: 400% 100%;
638
+ }
639
+
640
+ from {
641
+ mask-position: 200% 0;
642
+ }
643
+
644
+ to {
645
+ mask-position: -200% 0;
646
+ }
647
+ }
648
+
649
+ @keyframes lb-animation-ai-toolbar-halo-scale-in {
650
+ from {
651
+ transform: scale(0.5);
652
+ }
653
+
654
+ to {
655
+ transform: scale(1);
656
+ }
657
+ }
658
+
603
659
  @keyframes lb-animation-ai-toolbar-halo-horizontal {
604
660
  from {
605
661
  transform: translateX(-50%);
@@ -15,3 +15,9 @@
15
15
  display: none;
16
16
  }
17
17
  }
18
+
19
+ @mixin truncate {
20
+ overflow: hidden;
21
+ text-overflow: ellipsis;
22
+ white-space: nowrap;
23
+ }