@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,174 @@
1
+ import { mergeAttributes, Node } from "@tiptap/core";
2
+ import { TextSelection } from "@tiptap/pm/state";
3
+
4
+ export const BaseCustomQuote = Node.create({
5
+ name: "customQuote",
6
+ group: "block",
7
+ content: "customQuoteText customQuoteAuthor customQuoteSign",
8
+ selectable: false,
9
+ draggable: true,
10
+ isolating: true,
11
+
12
+ addKeyboardShortcuts() {
13
+ return {
14
+ Enter: () => {
15
+ const { view, schema } = this.editor;
16
+ const { state } = view;
17
+ const { selection } = state;
18
+
19
+ const $pos = selection.$from;
20
+ let depth = $pos.depth;
21
+ let isInsideCustomQuote = false;
22
+
23
+ while (depth > 0) {
24
+ const parentNode = $pos.node(depth);
25
+ if (parentNode.type.name === this.name) {
26
+ isInsideCustomQuote = true;
27
+ break;
28
+ }
29
+ depth--;
30
+ }
31
+
32
+ if (!isInsideCustomQuote) {
33
+ return false;
34
+ }
35
+
36
+ const { tr } = state;
37
+ const posAfter = $pos.after(depth);
38
+
39
+ const paragraphNode = schema.nodes.paragraph.createAndFill();
40
+ if (!paragraphNode) return false;
41
+
42
+ tr.insert(posAfter, paragraphNode);
43
+ tr.setSelection(TextSelection.create(tr.doc, posAfter + 1));
44
+ view.dispatch(tr);
45
+
46
+ return true;
47
+ },
48
+ };
49
+ },
50
+
51
+ parseHTML() {
52
+ return [
53
+ {
54
+ tag: "div[data-custom-quote]",
55
+ },
56
+ ];
57
+ },
58
+
59
+ renderHTML({ HTMLAttributes }) {
60
+ return ["div", mergeAttributes(HTMLAttributes, { "data-custom-quote": "" }), 0];
61
+ },
62
+ });
63
+
64
+ export const BaseCustomQuoteText = Node.create({
65
+ name: "customQuoteText",
66
+ group: "block",
67
+ content: "inline*",
68
+ draggable: false,
69
+ selectable: true,
70
+
71
+ addAttributes() {
72
+ return {
73
+ open: {
74
+ default: true,
75
+ },
76
+ };
77
+ },
78
+
79
+ addKeyboardShortcuts() {
80
+ return {
81
+ Enter: () => {
82
+ const { view, state } = this.editor;
83
+ const { tr, selection } = state;
84
+ const { $from } = selection;
85
+
86
+ if ($from.parent.type.name !== "customQuoteText") {
87
+ return false;
88
+ }
89
+
90
+ const posAfter = $from.after();
91
+ const nextNode = state.doc.nodeAt(posAfter);
92
+ if (!nextNode || nextNode.type.name !== "customQuoteAuthor") return false;
93
+
94
+ const nextNodeEnd = posAfter + nextNode.nodeSize - 1;
95
+ tr.setSelection(TextSelection.create(tr.doc, nextNodeEnd));
96
+ view.dispatch(tr);
97
+ return true;
98
+ },
99
+ };
100
+ },
101
+
102
+ parseHTML() {
103
+ return [
104
+ {
105
+ tag: "div[data-custom-quote-text]",
106
+ },
107
+ ];
108
+ },
109
+
110
+ renderHTML({ HTMLAttributes }) {
111
+ return ["div", mergeAttributes(HTMLAttributes, { "data-custom-quote-text": "" }), 0];
112
+ },
113
+ });
114
+
115
+ export const BaseCustomQuoteAuthor = Node.create({
116
+ name: "customQuoteAuthor",
117
+ group: "block",
118
+ content: "inline*",
119
+ selectable: true,
120
+
121
+ addKeyboardShortcuts() {
122
+ return {
123
+ Enter: () => {
124
+ const { view, state } = this.editor;
125
+ const { tr, selection } = state;
126
+ const { $from } = selection;
127
+
128
+ if ($from.parent.type.name !== "customQuoteAuthor") {
129
+ return false;
130
+ }
131
+
132
+ const posAfter = $from.after();
133
+ const nextNode = state.doc.nodeAt(posAfter);
134
+ if (!nextNode || nextNode.type.name !== "customQuoteSign") return false;
135
+
136
+ const nextNodeEnd = posAfter + nextNode.nodeSize - 1;
137
+ tr.setSelection(TextSelection.create(tr.doc, nextNodeEnd));
138
+ view.dispatch(tr);
139
+ return true;
140
+ },
141
+ };
142
+ },
143
+
144
+ parseHTML() {
145
+ return [
146
+ {
147
+ tag: "div[data-custom-quote-author]",
148
+ },
149
+ ];
150
+ },
151
+
152
+ renderHTML({ HTMLAttributes }) {
153
+ return ["div", mergeAttributes(HTMLAttributes, { "data-custom-quote-author": "" }), 0];
154
+ },
155
+ });
156
+
157
+ export const BaseCustomQuoteSign = Node.create({
158
+ name: "customQuoteSign",
159
+ group: "block",
160
+ content: "inline*",
161
+ selectable: true,
162
+
163
+ parseHTML() {
164
+ return [
165
+ {
166
+ tag: "div[data-custom-quote-sign]",
167
+ },
168
+ ];
169
+ },
170
+
171
+ renderHTML({ HTMLAttributes }) {
172
+ return ["div", mergeAttributes(HTMLAttributes, { "data-custom-quote-sign": "" }), 0];
173
+ },
174
+ });
@@ -0,0 +1,141 @@
1
+ import { mergeAttributes, nodePasteRule } from "@tiptap/core";
2
+ import Image from "@tiptap/extension-image";
3
+ import { resolveImageSrc } from "../../utils/image-src-resolver";
4
+ import type { FigureAttrs } from "../../types";
5
+
6
+ export interface FigureOptions {
7
+ HTMLAttributes: Record<string, unknown>;
8
+ getImageById?: (fileId: string) => string | null;
9
+ onFigureClick?: (attrs: FigureAttrs, pos: number) => void;
10
+ }
11
+
12
+ declare module "@tiptap/core" {
13
+ interface Commands<ReturnType> {
14
+ figure: {
15
+ setFigure: (options: FigureAttrs) => ReturnType;
16
+ updateFigure: (options: Partial<FigureAttrs>) => ReturnType;
17
+ };
18
+ }
19
+ }
20
+
21
+ export const BaseFigure = Image.extend<FigureOptions>({
22
+ name: "image",
23
+ group: "block",
24
+ content: "",
25
+ draggable: true,
26
+ isolating: true,
27
+ selectable: false,
28
+
29
+ addOptions() {
30
+ return {
31
+ HTMLAttributes: {},
32
+ getImageById: undefined,
33
+ onFigureClick: undefined,
34
+ };
35
+ },
36
+
37
+ addAttributes() {
38
+ return {
39
+ ...this.parent?.(),
40
+ fileId: {
41
+ default: null,
42
+ parseHTML: (element) => {
43
+ const img = element.querySelector("img") || element;
44
+ return img.getAttribute("data-file-id") || element.getAttribute("data-file-id");
45
+ },
46
+ renderHTML: (attributes) => {
47
+ if (!attributes.fileId) return {};
48
+ return { "data-file-id": attributes.fileId };
49
+ },
50
+ },
51
+ caption: {
52
+ default: null,
53
+ parseHTML: (element) => element.querySelector("figcaption")?.textContent || null,
54
+ },
55
+ };
56
+ },
57
+
58
+ parseHTML() {
59
+ return [
60
+ {
61
+ tag: "figure",
62
+ getAttrs: (element) => {
63
+ if (typeof element === "string") return false;
64
+ const img = element.querySelector("img");
65
+ return img ? {} : false;
66
+ },
67
+ },
68
+ {
69
+ tag: "img[src]",
70
+ getAttrs: (element) => {
71
+ if (typeof element === "string") return false;
72
+ return {};
73
+ },
74
+ },
75
+ ];
76
+ },
77
+
78
+ renderHTML({ node }) {
79
+ const resolvedSrc = resolveImageSrc(
80
+ {
81
+ fileId: node.attrs.fileId,
82
+ src: node.attrs.src,
83
+ },
84
+ { getImageById: this.options.getImageById }
85
+ );
86
+
87
+ const figureAttrs = mergeAttributes(this.options.HTMLAttributes);
88
+
89
+ const imgAttrs: Record<string, unknown> = {
90
+ src: resolvedSrc,
91
+ };
92
+
93
+ if (node.attrs.fileId) imgAttrs["data-file-id"] = node.attrs.fileId;
94
+ if (node.attrs.alt) imgAttrs.alt = node.attrs.alt;
95
+ if (node.attrs.title) imgAttrs.title = node.attrs.title;
96
+ if (node.attrs.width) imgAttrs.width = node.attrs.width;
97
+ if (node.attrs.height) imgAttrs.height = node.attrs.height;
98
+
99
+ const hasCaption = node.attrs.caption && node.attrs.caption.trim().length > 0;
100
+ const figcaptionContent = hasCaption ? ["figcaption", {}, node.attrs.caption] : null;
101
+
102
+ return [
103
+ "figure",
104
+ figureAttrs,
105
+ ["img", imgAttrs],
106
+ ...(figcaptionContent ? [figcaptionContent] : []),
107
+ ];
108
+ },
109
+
110
+ addCommands() {
111
+ return {
112
+ ...this.parent?.(),
113
+ setFigure:
114
+ (options: FigureAttrs) =>
115
+ ({ commands }) => {
116
+ return commands.insertContent({
117
+ type: this.name,
118
+ attrs: options,
119
+ });
120
+ },
121
+ updateFigure:
122
+ (options: Partial<FigureAttrs>) =>
123
+ ({ commands }) => {
124
+ return commands.updateAttributes(this.name, options);
125
+ },
126
+ };
127
+ },
128
+
129
+ addPasteRules() {
130
+ return [
131
+ nodePasteRule({
132
+ find: /!\[(.*?)\]\((.*?)(?:\s+"(.*?)")?\)/g,
133
+ type: this.type,
134
+ getAttributes: (match) => {
135
+ const [, alt, src, title] = match;
136
+ return { src, alt, title };
137
+ },
138
+ }),
139
+ ];
140
+ },
141
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Base extensions - core node definitions without UI bindings
3
+ * Used by both editor and render/server contexts
4
+ */
5
+
6
+ export { BaseCallout, type CalloutType } from "./callout";
7
+ export { BaseCodeBlock } from "./code-block";
8
+ export { CopyableText } from "./copyable-text";
9
+ export {
10
+ BaseCustomQuote,
11
+ BaseCustomQuoteAuthor,
12
+ BaseCustomQuoteSign,
13
+ BaseCustomQuoteText,
14
+ } from "./custom-quote";
15
+ export { LinkExtension } from "./link";
16
+ export { BaseMythReality, BaseMythRealityBlock } from "./myth-reality";
17
+ export { BasePrompt, BasePromptContent, BasePromptHeader } from "./prompt";
18
+ export { BaseVideo } from "./video";
19
+ export { BaseFigure } from "./figure";
@@ -0,0 +1,18 @@
1
+ import Link from '@tiptap/extension-link';
2
+
3
+ export const LinkExtension = Link.extend({
4
+ addOptions() {
5
+ return {
6
+ ...this.parent?.(),
7
+ openOnClick: false,
8
+ linkOnPaste: true,
9
+ autolink: true,
10
+ protocols: ['http', 'https', 'mailto', 'tel'],
11
+ };
12
+ },
13
+ }).configure({
14
+ openOnClick: false,
15
+ linkOnPaste: true,
16
+ autolink: true,
17
+ protocols: ['http', 'https', 'mailto', 'tel'],
18
+ });
@@ -0,0 +1,45 @@
1
+ import { mergeAttributes, Node } from "@tiptap/core";
2
+ import { TextSelection } from "@tiptap/pm/state";
3
+ import { handleExitNode } from "../../utils/handle-exit-node";
4
+
5
+ export const BaseMythRealityBlock = Node.create({
6
+ name: "mythRealityBlock",
7
+
8
+ group: "block",
9
+ content: "block+",
10
+
11
+ parseHTML() {
12
+ return [{ tag: "div[data-myth-reality-block]" }];
13
+ },
14
+
15
+ renderHTML({ HTMLAttributes }) {
16
+ return [
17
+ "div",
18
+ mergeAttributes(HTMLAttributes, { "data-myth-reality-block": "" }),
19
+ ["div", { class: "myth-reality-block-content" }, 0],
20
+ ];
21
+ },
22
+ });
23
+
24
+ export const BaseMythReality = Node.create({
25
+ name: "mythReality",
26
+ group: "block",
27
+ content: "(mythRealityBlock) (mythRealityBlock)",
28
+
29
+ draggable: true,
30
+ isolating: true,
31
+
32
+ parseHTML() {
33
+ return [{ tag: "div[data-myth-reality]" }];
34
+ },
35
+
36
+ renderHTML({ HTMLAttributes }) {
37
+ return ["div", mergeAttributes(HTMLAttributes, { "data-myth-reality": "" }), 0];
38
+ },
39
+
40
+ addKeyboardShortcuts() {
41
+ return {
42
+ Enter: () => handleExitNode(this.editor, this.name),
43
+ };
44
+ },
45
+ });
@@ -0,0 +1,109 @@
1
+ import { Node, mergeAttributes } from "@tiptap/core";
2
+ import { TextSelection } from "@tiptap/pm/state";
3
+
4
+ export const BasePrompt = Node.create({
5
+ name: "prompt",
6
+ group: "block",
7
+ content: "(promptHeader | promptV2Header) (promptContent | promptV2Content)",
8
+ selectable: false,
9
+ draggable: true,
10
+
11
+ addKeyboardShortcuts() {
12
+ return {
13
+ Enter: () => {
14
+ const { view, schema } = this.editor;
15
+ const { state } = view;
16
+ const { selection } = state;
17
+
18
+ const $pos = selection.$from;
19
+ let depth = $pos.depth;
20
+ let isInsidePrompt = false;
21
+
22
+ while (depth > 0) {
23
+ const parentNode = $pos.node(depth);
24
+ if (parentNode.type.name === this.name) {
25
+ isInsidePrompt = true;
26
+ break;
27
+ }
28
+ depth--;
29
+ }
30
+
31
+ if (!isInsidePrompt) {
32
+ return false;
33
+ }
34
+
35
+ const { tr } = state;
36
+ const posAfter = $pos.after(depth);
37
+
38
+ const paragraphNode = schema.nodes.paragraph.createAndFill();
39
+ if (!paragraphNode) return false;
40
+
41
+ tr.insert(posAfter, paragraphNode);
42
+ tr.setSelection(TextSelection.create(tr.doc, posAfter + 1));
43
+ view.dispatch(tr);
44
+
45
+ return true;
46
+ },
47
+ };
48
+ },
49
+
50
+ parseHTML() {
51
+ return [
52
+ {
53
+ tag: "div[data-prompt]",
54
+ },
55
+ ];
56
+ },
57
+
58
+ renderHTML({ HTMLAttributes }) {
59
+ return ["div", mergeAttributes(HTMLAttributes, { "data-prompt": "" }), 0];
60
+ },
61
+ });
62
+
63
+ export const BasePromptHeader = Node.create({
64
+ name: "promptHeader",
65
+ group: "block",
66
+ content: "inline*",
67
+ selectable: true,
68
+ draggable: false,
69
+
70
+ parseHTML() {
71
+ return [
72
+ {
73
+ tag: "div[data-prompt-header]",
74
+ },
75
+ ];
76
+ },
77
+
78
+ renderHTML({ HTMLAttributes }) {
79
+ return ["div", mergeAttributes(HTMLAttributes, { "data-prompt-header": "" }), 0];
80
+ },
81
+
82
+ // addNodeView() {
83
+ // return ReactNodeViewRenderer(PromptHeaderView);
84
+ // },
85
+ });
86
+
87
+ export const BasePromptContent = Node.create({
88
+ name: "promptContent",
89
+ group: "block",
90
+ content: "text*",
91
+ draggable: false,
92
+ selectable: true,
93
+
94
+ parseHTML() {
95
+ return [
96
+ {
97
+ tag: "div[data-prompt-content]",
98
+ },
99
+ ];
100
+ },
101
+
102
+ renderHTML({ HTMLAttributes }) {
103
+ return ["div", mergeAttributes(HTMLAttributes, { "data-prompt-content": "" }), 0];
104
+ },
105
+
106
+ // addNodeView() {
107
+ // return ReactNodeViewRenderer(PromptContentView);
108
+ // },
109
+ });
@@ -0,0 +1,29 @@
1
+ import { Node } from "@tiptap/core";
2
+
3
+ export const BaseVideo = Node.create({
4
+ name: "video",
5
+ group: "block",
6
+ selectable: true,
7
+ draggable: true,
8
+ isolating: true,
9
+
10
+ addAttributes() {
11
+ return {
12
+ src: {
13
+ default: null,
14
+ },
15
+ };
16
+ },
17
+
18
+ parseHTML() {
19
+ return [
20
+ {
21
+ tag: "video-embed",
22
+ },
23
+ ];
24
+ },
25
+
26
+ renderHTML({ HTMLAttributes }) {
27
+ return ["video-embed", HTMLAttributes];
28
+ },
29
+ });
@@ -0,0 +1,43 @@
1
+ import { ReactNodeViewRenderer } from "@tiptap/react";
2
+
3
+ import { BaseCallout } from "../base";
4
+ import { BaseLegacyBookAlert, BaseLegacyBookTip } from "../base/compatibility";
5
+ import { CalloutView } from "../views/node-views";
6
+
7
+ /**
8
+ * Editor version Callout extension
9
+ * Extends the base Callout with:
10
+ * - Interactive NodeView for the editor
11
+ * - Keyboard shortcuts for better UX
12
+ */
13
+ export const Callout = BaseCallout.extend({
14
+ addNodeView() {
15
+ return ReactNodeViewRenderer(CalloutView);
16
+ },
17
+ });
18
+
19
+ // ============================================================================
20
+ // Legacy Callout Compatibility Extensions
21
+ // ============================================================================
22
+
23
+ /**
24
+ * Legacy callout extension for backward compatibility with old Tiptap content.
25
+ * @deprecated Use {@link Callout} with `type="error"` instead.
26
+ * This extension is kept only for rendering old content that uses the `calloutAlert` node type.
27
+ */
28
+ export const LegacyBookAlert = BaseLegacyBookAlert.extend({
29
+ addNodeView() {
30
+ return ReactNodeViewRenderer(CalloutView);
31
+ },
32
+ });
33
+
34
+ /**
35
+ * Legacy callout extension for backward compatibility with old Tiptap content.
36
+ * @deprecated Use {@link Callout} with `type="info"` instead.
37
+ * This extension is kept only for rendering old content that uses the `calloutTip` node type.
38
+ */
39
+ export const LegacyBookTip = BaseLegacyBookTip.extend({
40
+ addNodeView() {
41
+ return ReactNodeViewRenderer(CalloutView);
42
+ },
43
+ });
@@ -0,0 +1,9 @@
1
+ import { ReactNodeViewRenderer } from "@tiptap/react";
2
+ import { BaseCodeBlock } from "../base";
3
+ import { CodeBlockView } from "../views/node-views/code-block";
4
+
5
+ export const CodeBlock = BaseCodeBlock.extend({
6
+ addNodeView() {
7
+ return ReactNodeViewRenderer(CodeBlockView);
8
+ },
9
+ });
@@ -0,0 +1,43 @@
1
+ import type { Editor } from "@tiptap/core";
2
+ import { Extension } from "@tiptap/core";
3
+ import Suggestion from "@tiptap/suggestion";
4
+
5
+ interface CommandRange {
6
+ from: number;
7
+ to: number;
8
+ }
9
+
10
+ interface CommandCallback {
11
+ (params: { editor: Editor; range: CommandRange }): void;
12
+ }
13
+
14
+ export interface CommandsExtensionOptions {
15
+ suggestion: Record<string, unknown>;
16
+ }
17
+
18
+ export const Commands = Extension.create<CommandsExtensionOptions>({
19
+ name: "slashCommands",
20
+
21
+ addOptions() {
22
+ return {
23
+ suggestion: {},
24
+ };
25
+ },
26
+
27
+ addProseMirrorPlugins() {
28
+ const options = this.options;
29
+ const editor = this.editor;
30
+
31
+ return [
32
+ Suggestion({
33
+ editor,
34
+ char: "/",
35
+ command: ({ editor: e, range, props }) => {
36
+ const commandFn = props.command as CommandCallback;
37
+ commandFn({ editor: e, range });
38
+ },
39
+ ...options.suggestion,
40
+ }),
41
+ ];
42
+ },
43
+ });
@@ -0,0 +1,16 @@
1
+ import { ReactMarkViewRenderer } from "@tiptap/react";
2
+
3
+ import { CopyableText as BaseCopyableText } from "../base";
4
+ import { CopyableTextView } from "../views/node-views";
5
+
6
+ /**
7
+ * Editor version CopyableText extension
8
+ * Extends the base CopyableText with:
9
+ * - Interactive MarkView for the editor with copy button
10
+ * - React-based rendering
11
+ */
12
+ export const CopyableText = BaseCopyableText.extend({
13
+ addMarkView() {
14
+ return ReactMarkViewRenderer(CopyableTextView);
15
+ },
16
+ });
@@ -0,0 +1,54 @@
1
+ import { ReactNodeViewRenderer } from "@tiptap/react";
2
+
3
+ import {
4
+ BaseCustomQuote,
5
+ BaseCustomQuoteText,
6
+ BaseCustomQuoteAuthor,
7
+ BaseCustomQuoteSign,
8
+ } from "../base";
9
+ import { BaseLegacySEQuote } from "../base/compatibility";
10
+ import {
11
+ CustomQuoteView,
12
+ CustomQuoteTextView,
13
+ CustomQuoteAuthorView,
14
+ CustomQuoteSignView,
15
+ } from "../views/node-views";
16
+
17
+ export const CustomQuote = BaseCustomQuote.extend({
18
+ addNodeView() {
19
+ return ReactNodeViewRenderer(CustomQuoteView);
20
+ },
21
+ });
22
+
23
+ export const CustomQuoteText = BaseCustomQuoteText.extend({
24
+ addNodeView() {
25
+ return ReactNodeViewRenderer(CustomQuoteTextView);
26
+ },
27
+ });
28
+
29
+ export const CustomQuoteAuthor = BaseCustomQuoteAuthor.extend({
30
+ addNodeView() {
31
+ return ReactNodeViewRenderer(CustomQuoteAuthorView);
32
+ },
33
+ });
34
+
35
+ export const CustomQuoteSign = BaseCustomQuoteSign.extend({
36
+ addNodeView() {
37
+ return ReactNodeViewRenderer(CustomQuoteSignView);
38
+ },
39
+ });
40
+
41
+ // ============================================================================
42
+ // Legacy CustomBlockquote Compatibility Extension
43
+ // ============================================================================
44
+
45
+ /**
46
+ * Legacy CustomQuoteV2 extension for backward compatibility with old Tiptap copy editor content.
47
+ * @deprecated Use {@link CustomQuote} instead.
48
+ * This extension is kept only for rendering old content that uses the `customQuoteV2` node type.
49
+ */
50
+ export const LegacySEQuote = BaseLegacySEQuote.extend({
51
+ addNodeView() {
52
+ return ReactNodeViewRenderer(CustomQuoteView);
53
+ },
54
+ });