@cyberyozh/text-editor 0.1.1

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 (209) hide show
  1. package/README.md +102 -0
  2. package/dist/editor.css +2069 -0
  3. package/dist/editor.css.map +1 -0
  4. package/dist/editor.d.mts +45 -0
  5. package/dist/editor.d.ts +45 -0
  6. package/dist/editor.js +6155 -0
  7. package/dist/editor.js.map +1 -0
  8. package/dist/editor.mjs +6118 -0
  9. package/dist/editor.mjs.map +1 -0
  10. package/dist/render/client.d.mts +24 -0
  11. package/dist/render/client.d.ts +24 -0
  12. package/dist/render/client.js +98 -0
  13. package/dist/render/client.js.map +1 -0
  14. package/dist/render/client.mjs +74 -0
  15. package/dist/render/client.mjs.map +1 -0
  16. package/dist/render.d.mts +58 -0
  17. package/dist/render.d.ts +58 -0
  18. package/dist/render.js +2521 -0
  19. package/dist/render.js.map +1 -0
  20. package/dist/render.mjs +2484 -0
  21. package/dist/render.mjs.map +1 -0
  22. package/dist/styles/index.css +2068 -0
  23. package/package.json +92 -0
  24. package/src/editor/commands/items.ts +374 -0
  25. package/src/editor/commands/suggestion.tsx +146 -0
  26. package/src/editor/components/button/button.css +56 -0
  27. package/src/editor/components/button/button.tsx +35 -0
  28. package/src/editor/components/button/index.ts +1 -0
  29. package/src/editor/components/common-modal/common-modal.css +85 -0
  30. package/src/editor/components/common-modal/common-modal.tsx +58 -0
  31. package/src/editor/components/common-modal/index.ts +1 -0
  32. package/src/editor/components/index.ts +3 -0
  33. package/src/editor/components/input/index.ts +1 -0
  34. package/src/editor/components/input/input.css +43 -0
  35. package/src/editor/components/input/input.tsx +22 -0
  36. package/src/editor/context/dialog-command.tsx +47 -0
  37. package/src/editor/context/index.ts +2 -0
  38. package/src/editor/context/translations-context.tsx +28 -0
  39. package/src/editor/controls/bubble-menu-content.tsx +394 -0
  40. package/src/editor/controls/callout-menu.tsx +169 -0
  41. package/src/editor/controls/character-count.tsx +36 -0
  42. package/src/editor/controls/commands-list.tsx +117 -0
  43. package/src/editor/controls/drag-handle.tsx +98 -0
  44. package/src/editor/controls/emoji-picker.tsx +62 -0
  45. package/src/editor/controls/index.ts +5 -0
  46. package/src/editor/controls/table-menu.tsx +322 -0
  47. package/src/editor/dialogs/add-katex-dialog/add-katex-dialog.css +115 -0
  48. package/src/editor/dialogs/add-katex-dialog/add-katex-dialog.tsx +116 -0
  49. package/src/editor/dialogs/add-katex-dialog/index.ts +1 -0
  50. package/src/editor/dialogs/add-link-dialog/add-link-dialog.css +105 -0
  51. package/src/editor/dialogs/add-link-dialog/add-link-dialog.tsx +177 -0
  52. package/src/editor/dialogs/add-link-dialog/index.ts +1 -0
  53. package/src/editor/dialogs/add-video-dialog/add-video-dialog.css +146 -0
  54. package/src/editor/dialogs/add-video-dialog/add-video-dialog.tsx +157 -0
  55. package/src/editor/dialogs/add-video-dialog/index.ts +1 -0
  56. package/src/editor/dialogs/image-dialog/image-dialog.css +101 -0
  57. package/src/editor/dialogs/image-dialog/image-dialog.tsx +276 -0
  58. package/src/editor/dialogs/image-dialog/index.ts +1 -0
  59. package/src/editor/dialogs/index.ts +4 -0
  60. package/src/editor/editor.tsx +247 -0
  61. package/src/editor/handlers/create-image-upload-handler.ts +24 -0
  62. package/src/editor/handlers/dialog-handlers.ts +143 -0
  63. package/src/editor/handlers/index.ts +8 -0
  64. package/src/editor/index.ts +3 -0
  65. package/src/extensions/base/callout.ts +138 -0
  66. package/src/extensions/base/code-block.ts +24 -0
  67. package/src/extensions/base/compatibility.ts +493 -0
  68. package/src/extensions/base/copyable-text.ts +56 -0
  69. package/src/extensions/base/custom-quote.ts +174 -0
  70. package/src/extensions/base/figure.ts +141 -0
  71. package/src/extensions/base/index.ts +19 -0
  72. package/src/extensions/base/link.ts +18 -0
  73. package/src/extensions/base/myth-reality.ts +45 -0
  74. package/src/extensions/base/prompt.ts +109 -0
  75. package/src/extensions/base/video.ts +29 -0
  76. package/src/extensions/editor/callout.ts +43 -0
  77. package/src/extensions/editor/code-block.ts +9 -0
  78. package/src/extensions/editor/commands.ts +43 -0
  79. package/src/extensions/editor/copyable-text.ts +16 -0
  80. package/src/extensions/editor/custom-quote.ts +54 -0
  81. package/src/extensions/editor/figure.ts +20 -0
  82. package/src/extensions/editor/get-editor-extensions.ts +113 -0
  83. package/src/extensions/editor/index.ts +25 -0
  84. package/src/extensions/editor/prompt.ts +43 -0
  85. package/src/extensions/editor/video.ts +23 -0
  86. package/src/extensions/render/get-render-extensions.ts +74 -0
  87. package/src/extensions/render/index.ts +6 -0
  88. package/src/extensions/views/components/callout.tsx +70 -0
  89. package/src/extensions/views/components/code-block.tsx +73 -0
  90. package/src/extensions/views/components/copy-button.tsx +52 -0
  91. package/src/extensions/views/components/copyable-text.tsx +11 -0
  92. package/src/extensions/views/components/custom-quote.tsx +64 -0
  93. package/src/extensions/views/components/figure.tsx +63 -0
  94. package/src/extensions/views/components/index.ts +7 -0
  95. package/src/extensions/views/components/prompt.tsx +43 -0
  96. package/src/extensions/views/components/video.tsx +40 -0
  97. package/src/extensions/views/node-views/callout.tsx +51 -0
  98. package/src/extensions/views/node-views/code-block.tsx +30 -0
  99. package/src/extensions/views/node-views/copyable-text.tsx +10 -0
  100. package/src/extensions/views/node-views/custom-quote.tsx +66 -0
  101. package/src/extensions/views/node-views/figure.tsx +38 -0
  102. package/src/extensions/views/node-views/index.ts +7 -0
  103. package/src/extensions/views/node-views/prompt.tsx +35 -0
  104. package/src/extensions/views/node-views/video.tsx +19 -0
  105. package/src/icons/Check.tsx +12 -0
  106. package/src/icons/Copy.tsx +15 -0
  107. package/src/icons/DragIcon.tsx +10 -0
  108. package/src/icons/Edit.tsx +12 -0
  109. package/src/icons/Quote.tsx +10 -0
  110. package/src/icons/X.tsx +13 -0
  111. package/src/icons/color/TiptapCalloutError.tsx +25 -0
  112. package/src/icons/color/TiptapCalloutInfo.tsx +18 -0
  113. package/src/icons/color/TiptapCalloutSuccess.tsx +49 -0
  114. package/src/icons/color/TiptapCalloutWarning.tsx +28 -0
  115. package/src/icons/color/index.ts +4 -0
  116. package/src/icons/index.ts +60 -0
  117. package/src/icons/menu/AddImage.tsx +10 -0
  118. package/src/icons/menu/AlignCenter.tsx +10 -0
  119. package/src/icons/menu/AlignLeft.tsx +10 -0
  120. package/src/icons/menu/AlignRight.tsx +10 -0
  121. package/src/icons/menu/ArrowDown.tsx +13 -0
  122. package/src/icons/menu/ArrowLeft.tsx +13 -0
  123. package/src/icons/menu/ArrowRight.tsx +13 -0
  124. package/src/icons/menu/ArrowUp.tsx +13 -0
  125. package/src/icons/menu/Blockquote.tsx +7 -0
  126. package/src/icons/menu/Bold.tsx +10 -0
  127. package/src/icons/menu/BulletList.tsx +10 -0
  128. package/src/icons/menu/CalloutError.tsx +10 -0
  129. package/src/icons/menu/CalloutInfo.tsx +12 -0
  130. package/src/icons/menu/CalloutSuccess.tsx +10 -0
  131. package/src/icons/menu/CalloutWarning.tsx +12 -0
  132. package/src/icons/menu/ClearFormatting.tsx +10 -0
  133. package/src/icons/menu/Code.tsx +10 -0
  134. package/src/icons/menu/CodeBlock.tsx +10 -0
  135. package/src/icons/menu/Copyable.tsx +10 -0
  136. package/src/icons/menu/CustomQuote.tsx +17 -0
  137. package/src/icons/menu/DeleteColumn.tsx +17 -0
  138. package/src/icons/menu/DeleteRow.tsx +17 -0
  139. package/src/icons/menu/Details.tsx +10 -0
  140. package/src/icons/menu/H2.tsx +10 -0
  141. package/src/icons/menu/H3.tsx +10 -0
  142. package/src/icons/menu/H4.tsx +10 -0
  143. package/src/icons/menu/H5.tsx +10 -0
  144. package/src/icons/menu/H6.tsx +10 -0
  145. package/src/icons/menu/Highlight.tsx +13 -0
  146. package/src/icons/menu/HorizontalRule.tsx +7 -0
  147. package/src/icons/menu/Italic.tsx +10 -0
  148. package/src/icons/menu/Link.tsx +10 -0
  149. package/src/icons/menu/LinkOff.tsx +10 -0
  150. package/src/icons/menu/Math.tsx +10 -0
  151. package/src/icons/menu/MergeCells.tsx +10 -0
  152. package/src/icons/menu/MythReality.tsx +10 -0
  153. package/src/icons/menu/OrderedList.tsx +10 -0
  154. package/src/icons/menu/Prompt.tsx +18 -0
  155. package/src/icons/menu/Redo.tsx +14 -0
  156. package/src/icons/menu/SplitCells.tsx +7 -0
  157. package/src/icons/menu/Strikethrough.tsx +10 -0
  158. package/src/icons/menu/Subscript.tsx +10 -0
  159. package/src/icons/menu/Superscript.tsx +10 -0
  160. package/src/icons/menu/Table.tsx +14 -0
  161. package/src/icons/menu/TableColumnHeading.tsx +7 -0
  162. package/src/icons/menu/TableRowHeading.tsx +10 -0
  163. package/src/icons/menu/TextColor.tsx +13 -0
  164. package/src/icons/menu/Underline.tsx +10 -0
  165. package/src/icons/menu/Undo.tsx +14 -0
  166. package/src/icons/menu/Video.tsx +14 -0
  167. package/src/icons/menu/index.ts +49 -0
  168. package/src/render/index.ts +22 -0
  169. package/src/render/static-renderer-config.tsx +206 -0
  170. package/src/render/tiptap-interactivity-provider.tsx +78 -0
  171. package/src/render/tiptap-render.tsx +102 -0
  172. package/src/styles/components.css +10 -0
  173. package/src/styles/editor/base.css +39 -0
  174. package/src/styles/editor/callout-menu.css +81 -0
  175. package/src/styles/editor/controls.css +57 -0
  176. package/src/styles/editor/emoji-picker.css +113 -0
  177. package/src/styles/editor/index.css +9 -0
  178. package/src/styles/editor/menus.css +16 -0
  179. package/src/styles/editor/states.css +43 -0
  180. package/src/styles/editor/typography.css +165 -0
  181. package/src/styles/editor/utilities.css +71 -0
  182. package/src/styles/editor/variables.css +70 -0
  183. package/src/styles/extensions/callout.css +155 -0
  184. package/src/styles/extensions/code-block.css +109 -0
  185. package/src/styles/extensions/commands-list.css +83 -0
  186. package/src/styles/extensions/custom-quote.css +103 -0
  187. package/src/styles/extensions/details.css +87 -0
  188. package/src/styles/extensions/figure.css +66 -0
  189. package/src/styles/extensions/index.css +11 -0
  190. package/src/styles/extensions/math.css +32 -0
  191. package/src/styles/extensions/myth-reality.css +59 -0
  192. package/src/styles/extensions/prompt.css +75 -0
  193. package/src/styles/extensions/table.css +253 -0
  194. package/src/styles/extensions/video.css +53 -0
  195. package/src/styles/styles.css +3 -0
  196. package/src/translations/en.json +158 -0
  197. package/src/translations/ru.json +158 -0
  198. package/src/types/image-handler.ts +33 -0
  199. package/src/types/index.ts +1 -0
  200. package/src/utils/copy-to-clipboard.ts +23 -0
  201. package/src/utils/custom-emoji.ts +174 -0
  202. package/src/utils/detect-video-hosting.ts +106 -0
  203. package/src/utils/get-shared-extensions.ts +45 -0
  204. package/src/utils/get-translations.ts +25 -0
  205. package/src/utils/handle-exit-node.ts +61 -0
  206. package/src/utils/image-src-resolver.ts +74 -0
  207. package/src/utils/index.ts +2 -0
  208. package/src/utils/migrate-legacy-callouts.ts +59 -0
  209. package/src/utils/pluralize.ts +38 -0
@@ -0,0 +1,3 @@
1
+ export { Tiptap } from "./editor";
2
+ export type { SupportedLanguage as TiptapSupportedLanguages } from "../utils/get-translations";
3
+ export { type JSONContent } from "@tiptap/react";
@@ -0,0 +1,138 @@
1
+ import { handleExitNode } from "@/utils/handle-exit-node";
2
+ import { migrateLegacyCallouts } from "@/utils/migrate-legacy-callouts";
3
+ import { mergeAttributes, Node } from "@tiptap/core";
4
+
5
+ export type CalloutType = "info" | "warning" | "success" | "error";
6
+
7
+ const legacyTypeMap: Record<string, CalloutType> = {
8
+ question: "info",
9
+ idea: "warning",
10
+ info: "success",
11
+ error: "error",
12
+ };
13
+
14
+ export const BaseCallout = Node.create({
15
+ name: "callout",
16
+
17
+ group: "block",
18
+ content: "block+",
19
+
20
+ draggable: true,
21
+ isolating: true,
22
+
23
+ addAttributes() {
24
+ return {
25
+ type: {
26
+ default: "info" as CalloutType,
27
+
28
+ parseHTML: (element) => {
29
+ if (typeof element === "string") return "info";
30
+
31
+ const dataCallout = element.getAttribute("data-callout");
32
+
33
+ if (dataCallout) {
34
+ return dataCallout as CalloutType;
35
+ }
36
+
37
+ const legacyType = element.getAttribute("calloutType");
38
+ if (legacyType) {
39
+ return legacyTypeMap[legacyType];
40
+ }
41
+
42
+ if (element.classList) {
43
+ if (element.classList.contains("callout--error")) return "error";
44
+ if (element.classList.contains("callout--question")) return "info";
45
+ if (element.classList.contains("callout--idea")) return "warning";
46
+ if (element.classList.contains("callout--info")) return "success";
47
+ }
48
+
49
+ return "info";
50
+ },
51
+ renderHTML: (attributes) => {
52
+ return {
53
+ "data-callout": attributes.type,
54
+ };
55
+ },
56
+ },
57
+ bgColor: {
58
+ default: null,
59
+ parseHTML: (element) => {
60
+ if (typeof element === "string") return null;
61
+ return element.getAttribute("data-bg-color");
62
+ },
63
+ renderHTML: (attributes) => {
64
+ if (!attributes.bgColor) return {};
65
+ return {
66
+ "data-bg-color": attributes.bgColor,
67
+ };
68
+ },
69
+ },
70
+ emoji: {
71
+ default: null,
72
+ parseHTML: (element) => {
73
+ if (typeof element === "string") return null;
74
+ return element.getAttribute("data-emoji");
75
+ },
76
+ renderHTML: (attributes) => {
77
+ if (!attributes.emoji) return {};
78
+ return {
79
+ "data-emoji": attributes.emoji,
80
+ };
81
+ },
82
+ },
83
+ calloutType: {
84
+ default: null,
85
+ parseHTML: (element) => element.getAttribute("calloutType"),
86
+ renderHTML: () => ({}),
87
+ },
88
+ callout: {
89
+ default: null,
90
+ parseHTML: (element) => element.getAttribute("callout"),
91
+ renderHTML: () => ({}),
92
+ },
93
+ };
94
+ },
95
+
96
+ onCreate() {
97
+ migrateLegacyCallouts(this.editor);
98
+ },
99
+
100
+ addKeyboardShortcuts() {
101
+ return {
102
+ Enter: () => handleExitNode(this.editor, this.name),
103
+ };
104
+ },
105
+
106
+ parseHTML() {
107
+ return [
108
+ {
109
+ tag: "div[data-callout]",
110
+ },
111
+ {
112
+ tag: "callout",
113
+ priority: 51,
114
+ },
115
+ {
116
+ tag: "div",
117
+ priority: 50,
118
+ getAttrs: (element) => {
119
+ if (typeof element === "string") return false;
120
+ if (
121
+ element.classList.contains("callout") ||
122
+ element.classList.contains("callout--error") ||
123
+ element.classList.contains("callout--question") ||
124
+ element.classList.contains("callout--idea") ||
125
+ element.classList.contains("callout--info")
126
+ ) {
127
+ return {};
128
+ }
129
+ return false;
130
+ },
131
+ },
132
+ ];
133
+ },
134
+
135
+ renderHTML({ HTMLAttributes }) {
136
+ return ["div", mergeAttributes(HTMLAttributes), 0];
137
+ },
138
+ });
@@ -0,0 +1,24 @@
1
+ import { CodeBlockLowlight } from "@tiptap/extension-code-block-lowlight";
2
+ import html from "highlight.js/lib/languages/xml";
3
+ import { common, createLowlight } from "lowlight";
4
+
5
+ const lowlight = createLowlight(common);
6
+ lowlight.register("html", html);
7
+
8
+ export const BaseCodeBlock = CodeBlockLowlight.configure({
9
+ lowlight,
10
+ }).extend({
11
+ selectable: false,
12
+ draggable: true,
13
+
14
+ addAttributes() {
15
+ return {
16
+ language: {
17
+ default: null,
18
+ parseHTML: (element: HTMLElement) => {
19
+ return element.querySelector("code")?.getAttribute("data-language") || "bash";
20
+ },
21
+ },
22
+ };
23
+ },
24
+ });
@@ -0,0 +1,493 @@
1
+ import { mergeAttributes, Node } from "@tiptap/core";
2
+ import { TextSelection } from "@tiptap/pm/state";
3
+ import { resolveImageSrc } from "../../utils/image-src-resolver";
4
+ import Image from "@tiptap/extension-image";
5
+ import { FigureOptions } from "./figure";
6
+ import { handleExitNode } from "@/utils/handle-exit-node";
7
+
8
+ /**
9
+ * Legacy callout extension for backward compatibility with old Tiptap content.
10
+ * @deprecated Use {@link Callout} with `type="info"` instead.
11
+ * This extension is kept only for rendering old content that uses the `calloutTip` node type.
12
+ */
13
+ export const BaseLegacyBookAlert = Node.create({
14
+ name: "calloutAlert",
15
+
16
+ group: "block",
17
+ content: "block+",
18
+
19
+ draggable: true,
20
+ isolating: true,
21
+
22
+ addAttributes() {
23
+ return {
24
+ type: {
25
+ default: "error",
26
+ },
27
+ };
28
+ },
29
+
30
+ parseHTML() {
31
+ return [
32
+ {
33
+ tag: 'div[data-type="callout-alert"]',
34
+ },
35
+ ];
36
+ },
37
+
38
+ addKeyboardShortcuts() {
39
+ return {
40
+ Enter: () => handleExitNode(this.editor, this.name),
41
+ };
42
+ },
43
+
44
+ renderHTML({ HTMLAttributes }) {
45
+ return ["div", mergeAttributes(HTMLAttributes, { "data-type": "callout-alert" }), 0];
46
+ },
47
+ });
48
+
49
+ /**
50
+ * Legacy callout extension for backward compatibility with old Tiptap content.
51
+ * @deprecated Use {@link Callout} with `type="info"` instead.
52
+ * This extension is kept only for rendering old content that uses the `calloutTip` node type.
53
+ */
54
+ export const BaseLegacyBookTip = Node.create({
55
+ name: "calloutTip",
56
+
57
+ group: "block",
58
+ content: "block+",
59
+
60
+ draggable: true,
61
+ isolating: true,
62
+
63
+ addAttributes() {
64
+ return {
65
+ type: {
66
+ default: "info",
67
+ },
68
+ };
69
+ },
70
+
71
+ parseHTML() {
72
+ return [
73
+ {
74
+ tag: 'div[data-type="callout-tip"]',
75
+ },
76
+ ];
77
+ },
78
+
79
+ renderHTML({ HTMLAttributes }) {
80
+ return ["div", mergeAttributes(HTMLAttributes, { "data-type": "callout-tip" }), 0];
81
+ },
82
+
83
+ addKeyboardShortcuts() {
84
+ return {
85
+ Enter: () => handleExitNode(this.editor, this.name),
86
+ };
87
+ },
88
+ });
89
+
90
+ /**
91
+ * Legacy callout extension for backward compatibility with old Tiptap content.
92
+ * @deprecated
93
+ */
94
+ export const BaseLegacySEQuote = Node.create({
95
+ name: "customQuoteV2",
96
+ group: "block",
97
+ content: "customQuoteText customQuoteAuthor customQuoteSign",
98
+ selectable: false,
99
+ draggable: true,
100
+ isolating: true,
101
+
102
+ addKeyboardShortcuts() {
103
+ return {
104
+ Enter: () => {
105
+ const { view, schema } = this.editor;
106
+ const { state } = view;
107
+ const { selection } = state;
108
+
109
+ const $pos = selection.$from;
110
+ let depth = $pos.depth;
111
+ let isInsideCustomQuoteV2 = false;
112
+
113
+ while (depth > 0) {
114
+ const parentNode = $pos.node(depth);
115
+ if (parentNode.type.name === this.name) {
116
+ isInsideCustomQuoteV2 = true;
117
+ break;
118
+ }
119
+ depth--;
120
+ }
121
+
122
+ if (!isInsideCustomQuoteV2) {
123
+ return false;
124
+ }
125
+
126
+ const { tr } = state;
127
+ const posAfter = $pos.after(depth);
128
+
129
+ const paragraphNode = schema.nodes.paragraph.createAndFill();
130
+ if (!paragraphNode) return false;
131
+
132
+ tr.insert(posAfter, paragraphNode);
133
+ tr.setSelection(TextSelection.create(tr.doc, posAfter + 1));
134
+ view.dispatch(tr);
135
+
136
+ return true;
137
+ },
138
+ };
139
+ },
140
+
141
+ parseHTML() {
142
+ return [
143
+ {
144
+ tag: "div[data-custom-quote-v2]",
145
+ },
146
+ ];
147
+ },
148
+
149
+ renderHTML({ HTMLAttributes }) {
150
+ return ["div", mergeAttributes(HTMLAttributes, { "data-custom-quote-v2": "" }), 0];
151
+ },
152
+ });
153
+
154
+ /**
155
+ * Legacy PromptV2 extension for backward compatibility with old Tiptap copy editor content.
156
+ * @deprecated Use {@link Prompt} instead.
157
+ * This extension is kept only for rendering old content that uses the `promptV2` node type.
158
+ */
159
+ export const BaseLegacyPromptSE = Node.create({
160
+ name: "promptV2",
161
+ group: "block",
162
+ content: "(promptHeader | promptV2Header) (promptContent | promptV2Content)",
163
+ selectable: false,
164
+ draggable: true,
165
+
166
+ addKeyboardShortcuts() {
167
+ return {
168
+ Enter: () => {
169
+ const { view, schema } = this.editor;
170
+ const { state } = view;
171
+ const { selection } = state;
172
+
173
+ const $pos = selection.$from;
174
+ let depth = $pos.depth;
175
+ let isInsidePromptV2 = false;
176
+
177
+ while (depth > 0) {
178
+ const parentNode = $pos.node(depth);
179
+ if (parentNode.type.name === this.name) {
180
+ isInsidePromptV2 = true;
181
+ break;
182
+ }
183
+ depth--;
184
+ }
185
+
186
+ if (!isInsidePromptV2) {
187
+ return false;
188
+ }
189
+
190
+ const { tr } = state;
191
+ const posAfter = $pos.after(depth);
192
+
193
+ const paragraphNode = schema.nodes.paragraph.createAndFill();
194
+ if (!paragraphNode) return false;
195
+
196
+ tr.insert(posAfter, paragraphNode);
197
+ tr.setSelection(TextSelection.create(tr.doc, posAfter + 1));
198
+ view.dispatch(tr);
199
+
200
+ return true;
201
+ },
202
+ };
203
+ },
204
+
205
+ parseHTML() {
206
+ return [
207
+ {
208
+ tag: "div[data-prompt-v2]",
209
+ },
210
+ ];
211
+ },
212
+
213
+ renderHTML({ HTMLAttributes }) {
214
+ return ["div", mergeAttributes(HTMLAttributes, { "data-prompt": "" }), 0];
215
+ },
216
+ });
217
+
218
+ /**
219
+ * Legacy PromptV2Header extension for backward compatibility with old Tiptap copy editor content.
220
+ * @deprecated Use {@link PromptHeader} instead.
221
+ * This extension is kept only for rendering old content that uses the `promptV2Header` node type.
222
+ */
223
+ export const BaseLegacyPromptSEHeader = Node.create({
224
+ name: "promptV2Header",
225
+ group: "block",
226
+ content: "inline*",
227
+ selectable: true,
228
+ draggable: false,
229
+
230
+ parseHTML() {
231
+ return [
232
+ {
233
+ tag: "div[data-prompt-v2-header]",
234
+ },
235
+ ];
236
+ },
237
+
238
+ renderHTML({ HTMLAttributes }) {
239
+ return ["div", mergeAttributes(HTMLAttributes, { "data-prompt-header": "" }), 0];
240
+ },
241
+
242
+ // addNodeView() {
243
+ // return ReactNodeViewRenderer(PromptHeaderView);
244
+ // },
245
+ });
246
+
247
+ /**
248
+ * Legacy PromptV2Content extension for backward compatibility with old Tiptap copy editor content.
249
+ * @deprecated Use {@link PromptContent} instead.
250
+ * This extension is kept only for rendering old content that uses the `promptV2Content` node type.
251
+ */
252
+ export const BaseLegacyPromptSEContent = Node.create({
253
+ name: "promptV2Content",
254
+ group: "block",
255
+ content: "text*",
256
+ draggable: false,
257
+ selectable: true,
258
+
259
+ parseHTML() {
260
+ return [
261
+ {
262
+ tag: "div[data-prompt-v2-content]",
263
+ },
264
+ ];
265
+ },
266
+
267
+ renderHTML({ HTMLAttributes }) {
268
+ return ["div", mergeAttributes(HTMLAttributes, { "data-prompt-content": "" }), 0];
269
+ },
270
+ });
271
+
272
+ /**
273
+ * Legacy Custom Quote extension for backward compatibility with old Tiptap copy editor content.
274
+ * @deprecated
275
+ */
276
+ export const BaseLegacyCustomBlockquote = Node.create({
277
+ name: "customBlockquote",
278
+
279
+ group: "block",
280
+ content: "block+",
281
+
282
+ draggable: true,
283
+
284
+ parseHTML() {
285
+ return [
286
+ {
287
+ tag: 'div[data-type="custom-blockquote"]',
288
+ },
289
+ ];
290
+ },
291
+
292
+ renderHTML() {
293
+ return ["blockquote", 0];
294
+ },
295
+ });
296
+
297
+ /**
298
+ * Legacy YouTube extension for backward compatibility with old Tiptap content.
299
+ * @deprecated Use {@link Video} extension instead.
300
+ * This extension is kept only for rendering old content that uses the `youtube` node type.
301
+ * Renders using the same VideoView component.
302
+ */
303
+ export const BaseLegacyYouTube = Node.create({
304
+ name: "youtube",
305
+
306
+ group: "block",
307
+ selectable: true,
308
+ draggable: true,
309
+ isolating: true,
310
+
311
+ addAttributes() {
312
+ return {
313
+ src: {
314
+ default: null,
315
+ },
316
+ width: {
317
+ default: 640,
318
+ },
319
+ height: {
320
+ default: 480,
321
+ },
322
+ start: {
323
+ default: 0,
324
+ },
325
+ };
326
+ },
327
+
328
+ parseHTML() {
329
+ return [
330
+ {
331
+ tag: 'iframe[src*="youtube.com"]',
332
+ },
333
+ {
334
+ tag: 'iframe[src*="youtu.be"]',
335
+ },
336
+ ];
337
+ },
338
+
339
+ renderHTML({ HTMLAttributes }) {
340
+ return ["video-embed", { src: HTMLAttributes.src }];
341
+ },
342
+ });
343
+
344
+ /**
345
+ * Legacy InlineLatex extension for backward compatibility with old Tiptap copy editor content.
346
+ * @deprecated Use the Mathematics extension instead.
347
+ * This extension renders old `inlineLatex` nodes as inline code for basic compatibility.
348
+ */
349
+ export const LegacyInlineLatexSE = Node.create({
350
+ name: "inlineLatex",
351
+ group: "inline",
352
+ inline: true,
353
+ content: "text*",
354
+ atom: true,
355
+
356
+ addAttributes() {
357
+ return {
358
+ latex: {
359
+ default: "",
360
+ parseHTML: (element) => element.getAttribute("data-latex") || element.textContent || "",
361
+ },
362
+ };
363
+ },
364
+
365
+ parseHTML() {
366
+ return [
367
+ {
368
+ tag: "span.inline-latex",
369
+ },
370
+ ];
371
+ },
372
+
373
+ renderHTML({ HTMLAttributes }) {
374
+ return ["code", mergeAttributes(HTMLAttributes, { class: "inline-latex" }), 0];
375
+ },
376
+ });
377
+
378
+ /**
379
+ * Legacy figure extension for backward compatibility with old Tiptap copy editor content.
380
+ * @deprecated Use {@link Figure} instead.
381
+ * This extension is kept only for rendering old content that uses the `figure` node type.
382
+ */
383
+ export const BaseLegacyFigure = Image.extend<FigureOptions>({
384
+ name: "figure",
385
+ group: "block",
386
+ content: "inline*",
387
+ draggable: true,
388
+ isolating: true,
389
+ selectable: false,
390
+
391
+ addOptions() {
392
+ return {
393
+ HTMLAttributes: {},
394
+ getImageById: undefined,
395
+ };
396
+ },
397
+
398
+ addAttributes() {
399
+ return {
400
+ ...this.parent?.(),
401
+ fileId: {
402
+ default: null,
403
+ parseHTML: (element) => {
404
+ const img = element.querySelector("img");
405
+ const fileIdFromImg = img?.getAttribute("fileId") || img?.getAttribute("data-file-id");
406
+ const fileIdFromFigure =
407
+ element.getAttribute("fileId") || element.getAttribute("data-file-id");
408
+ return fileIdFromImg ?? fileIdFromFigure;
409
+ },
410
+ renderHTML: (attributes) => {
411
+ if (!attributes.fileId) return {};
412
+ return { "data-file-id": attributes.fileId };
413
+ },
414
+ },
415
+ src: {
416
+ default: null,
417
+ parseHTML: (element) => {
418
+ const img = element.querySelector("img") || element;
419
+ return img.getAttribute("src");
420
+ },
421
+ },
422
+ alt: {
423
+ default: null,
424
+ parseHTML: (element) => {
425
+ const img = element.querySelector("img") || element;
426
+ return img.getAttribute("alt");
427
+ },
428
+ },
429
+ title: {
430
+ default: null,
431
+ parseHTML: (element) => {
432
+ const img = element.querySelector("img") || element;
433
+ return img.getAttribute("title");
434
+ },
435
+ },
436
+ caption: {
437
+ default: null,
438
+ parseHTML: (element) => {
439
+ const caption = element.querySelector("figcaption");
440
+ return caption ? caption.innerText : null;
441
+ },
442
+ },
443
+ };
444
+ },
445
+
446
+ parseHTML() {
447
+ return [
448
+ {
449
+ tag: "figure",
450
+ getAttrs: (element) => {
451
+ if (typeof element === "string") return false;
452
+ const img = element.querySelector("img");
453
+ return img ? {} : false;
454
+ },
455
+ },
456
+ {
457
+ tag: "img[src]",
458
+ },
459
+ ];
460
+ },
461
+
462
+ renderHTML({ node }) {
463
+ const resolvedSrc = resolveImageSrc(
464
+ {
465
+ fileId: node.attrs.fileId,
466
+ src: node.attrs.src,
467
+ },
468
+ { getImageById: this.options.getImageById }
469
+ );
470
+
471
+ const figureAttrs = mergeAttributes(this.options.HTMLAttributes);
472
+
473
+ const imgAttrs: Record<string, unknown> = {
474
+ src: resolvedSrc,
475
+ };
476
+
477
+ if (node.attrs.fileId) imgAttrs["data-file-id"] = node.attrs.fileId;
478
+ if (node.attrs.alt) imgAttrs.alt = node.attrs.alt;
479
+ if (node.attrs.title) imgAttrs.title = node.attrs.title;
480
+ if (node.attrs.width) imgAttrs.width = node.attrs.width;
481
+ if (node.attrs.height) imgAttrs.height = node.attrs.height;
482
+
483
+ const hasCaption = node.attrs.caption && node.attrs.caption.trim().length > 0;
484
+ const figcaptionContent = hasCaption ? ["figcaption", {}, node.attrs.caption] : null;
485
+
486
+ return [
487
+ "figure",
488
+ figureAttrs,
489
+ ["img", imgAttrs],
490
+ ...(figcaptionContent ? [figcaptionContent] : []),
491
+ ];
492
+ },
493
+ });
@@ -0,0 +1,56 @@
1
+ import { Mark, mergeAttributes } from "@tiptap/core";
2
+
3
+ declare module "@tiptap/core" {
4
+ interface Commands<ReturnType> {
5
+ copyable: {
6
+ setCopyable: (attributes?: { class: "copyable" }) => ReturnType;
7
+ toggleCopyable: (attributes?: { class: "copyable" }) => ReturnType;
8
+ unsetCopyable: () => ReturnType;
9
+ };
10
+ }
11
+ }
12
+
13
+ export const CopyableText = Mark.create({
14
+ name: "copyable-text",
15
+
16
+ addOptions() {
17
+ return {
18
+ HTMLAttributes: {
19
+ class: "copyable",
20
+ },
21
+ };
22
+ },
23
+
24
+ parseHTML() {
25
+ return [
26
+ {
27
+ tag: "span",
28
+ getAttrs: (element) => (element.classList.contains("copyable") ? {} : false),
29
+ },
30
+ ];
31
+ },
32
+
33
+ renderHTML({ HTMLAttributes }) {
34
+ return ["span", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
35
+ },
36
+
37
+ addCommands() {
38
+ return {
39
+ setCopyable:
40
+ (attributes) =>
41
+ ({ commands }) => {
42
+ return commands.setMark(this.name, attributes);
43
+ },
44
+ toggleCopyable:
45
+ (attributes) =>
46
+ ({ commands }) => {
47
+ return commands.toggleMark(this.name, attributes);
48
+ },
49
+ unsetCopyable:
50
+ () =>
51
+ ({ commands }) => {
52
+ return commands.unsetMark(this.name);
53
+ },
54
+ };
55
+ },
56
+ });