@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.
- package/README.md +102 -0
- package/dist/editor.css +2069 -0
- package/dist/editor.css.map +1 -0
- package/dist/editor.d.mts +45 -0
- package/dist/editor.d.ts +45 -0
- package/dist/editor.js +6155 -0
- package/dist/editor.js.map +1 -0
- package/dist/editor.mjs +6118 -0
- package/dist/editor.mjs.map +1 -0
- package/dist/render/client.d.mts +24 -0
- package/dist/render/client.d.ts +24 -0
- package/dist/render/client.js +98 -0
- package/dist/render/client.js.map +1 -0
- package/dist/render/client.mjs +74 -0
- package/dist/render/client.mjs.map +1 -0
- package/dist/render.d.mts +58 -0
- package/dist/render.d.ts +58 -0
- package/dist/render.js +2521 -0
- package/dist/render.js.map +1 -0
- package/dist/render.mjs +2484 -0
- package/dist/render.mjs.map +1 -0
- package/dist/styles/index.css +2068 -0
- package/package.json +92 -0
- package/src/editor/commands/items.ts +374 -0
- package/src/editor/commands/suggestion.tsx +146 -0
- package/src/editor/components/button/button.css +56 -0
- package/src/editor/components/button/button.tsx +35 -0
- package/src/editor/components/button/index.ts +1 -0
- package/src/editor/components/common-modal/common-modal.css +85 -0
- package/src/editor/components/common-modal/common-modal.tsx +58 -0
- package/src/editor/components/common-modal/index.ts +1 -0
- package/src/editor/components/index.ts +3 -0
- package/src/editor/components/input/index.ts +1 -0
- package/src/editor/components/input/input.css +43 -0
- package/src/editor/components/input/input.tsx +22 -0
- package/src/editor/context/dialog-command.tsx +47 -0
- package/src/editor/context/index.ts +2 -0
- package/src/editor/context/translations-context.tsx +28 -0
- package/src/editor/controls/bubble-menu-content.tsx +394 -0
- package/src/editor/controls/callout-menu.tsx +169 -0
- package/src/editor/controls/character-count.tsx +36 -0
- package/src/editor/controls/commands-list.tsx +117 -0
- package/src/editor/controls/drag-handle.tsx +98 -0
- package/src/editor/controls/emoji-picker.tsx +62 -0
- package/src/editor/controls/index.ts +5 -0
- package/src/editor/controls/table-menu.tsx +322 -0
- package/src/editor/dialogs/add-katex-dialog/add-katex-dialog.css +115 -0
- package/src/editor/dialogs/add-katex-dialog/add-katex-dialog.tsx +116 -0
- package/src/editor/dialogs/add-katex-dialog/index.ts +1 -0
- package/src/editor/dialogs/add-link-dialog/add-link-dialog.css +105 -0
- package/src/editor/dialogs/add-link-dialog/add-link-dialog.tsx +177 -0
- package/src/editor/dialogs/add-link-dialog/index.ts +1 -0
- package/src/editor/dialogs/add-video-dialog/add-video-dialog.css +146 -0
- package/src/editor/dialogs/add-video-dialog/add-video-dialog.tsx +157 -0
- package/src/editor/dialogs/add-video-dialog/index.ts +1 -0
- package/src/editor/dialogs/image-dialog/image-dialog.css +101 -0
- package/src/editor/dialogs/image-dialog/image-dialog.tsx +276 -0
- package/src/editor/dialogs/image-dialog/index.ts +1 -0
- package/src/editor/dialogs/index.ts +4 -0
- package/src/editor/editor.tsx +247 -0
- package/src/editor/handlers/create-image-upload-handler.ts +24 -0
- package/src/editor/handlers/dialog-handlers.ts +143 -0
- package/src/editor/handlers/index.ts +8 -0
- package/src/editor/index.ts +3 -0
- package/src/extensions/base/callout.ts +138 -0
- package/src/extensions/base/code-block.ts +24 -0
- package/src/extensions/base/compatibility.ts +493 -0
- package/src/extensions/base/copyable-text.ts +56 -0
- package/src/extensions/base/custom-quote.ts +174 -0
- package/src/extensions/base/figure.ts +141 -0
- package/src/extensions/base/index.ts +19 -0
- package/src/extensions/base/link.ts +18 -0
- package/src/extensions/base/myth-reality.ts +45 -0
- package/src/extensions/base/prompt.ts +109 -0
- package/src/extensions/base/video.ts +29 -0
- package/src/extensions/editor/callout.ts +43 -0
- package/src/extensions/editor/code-block.ts +9 -0
- package/src/extensions/editor/commands.ts +43 -0
- package/src/extensions/editor/copyable-text.ts +16 -0
- package/src/extensions/editor/custom-quote.ts +54 -0
- package/src/extensions/editor/figure.ts +20 -0
- package/src/extensions/editor/get-editor-extensions.ts +113 -0
- package/src/extensions/editor/index.ts +25 -0
- package/src/extensions/editor/prompt.ts +43 -0
- package/src/extensions/editor/video.ts +23 -0
- package/src/extensions/render/get-render-extensions.ts +74 -0
- package/src/extensions/render/index.ts +6 -0
- package/src/extensions/views/components/callout.tsx +70 -0
- package/src/extensions/views/components/code-block.tsx +73 -0
- package/src/extensions/views/components/copy-button.tsx +52 -0
- package/src/extensions/views/components/copyable-text.tsx +11 -0
- package/src/extensions/views/components/custom-quote.tsx +64 -0
- package/src/extensions/views/components/figure.tsx +63 -0
- package/src/extensions/views/components/index.ts +7 -0
- package/src/extensions/views/components/prompt.tsx +43 -0
- package/src/extensions/views/components/video.tsx +40 -0
- package/src/extensions/views/node-views/callout.tsx +51 -0
- package/src/extensions/views/node-views/code-block.tsx +30 -0
- package/src/extensions/views/node-views/copyable-text.tsx +10 -0
- package/src/extensions/views/node-views/custom-quote.tsx +66 -0
- package/src/extensions/views/node-views/figure.tsx +38 -0
- package/src/extensions/views/node-views/index.ts +7 -0
- package/src/extensions/views/node-views/prompt.tsx +35 -0
- package/src/extensions/views/node-views/video.tsx +19 -0
- package/src/icons/Check.tsx +12 -0
- package/src/icons/Copy.tsx +15 -0
- package/src/icons/DragIcon.tsx +10 -0
- package/src/icons/Edit.tsx +12 -0
- package/src/icons/Quote.tsx +10 -0
- package/src/icons/X.tsx +13 -0
- package/src/icons/color/TiptapCalloutError.tsx +25 -0
- package/src/icons/color/TiptapCalloutInfo.tsx +18 -0
- package/src/icons/color/TiptapCalloutSuccess.tsx +49 -0
- package/src/icons/color/TiptapCalloutWarning.tsx +28 -0
- package/src/icons/color/index.ts +4 -0
- package/src/icons/index.ts +60 -0
- package/src/icons/menu/AddImage.tsx +10 -0
- package/src/icons/menu/AlignCenter.tsx +10 -0
- package/src/icons/menu/AlignLeft.tsx +10 -0
- package/src/icons/menu/AlignRight.tsx +10 -0
- package/src/icons/menu/ArrowDown.tsx +13 -0
- package/src/icons/menu/ArrowLeft.tsx +13 -0
- package/src/icons/menu/ArrowRight.tsx +13 -0
- package/src/icons/menu/ArrowUp.tsx +13 -0
- package/src/icons/menu/Blockquote.tsx +7 -0
- package/src/icons/menu/Bold.tsx +10 -0
- package/src/icons/menu/BulletList.tsx +10 -0
- package/src/icons/menu/CalloutError.tsx +10 -0
- package/src/icons/menu/CalloutInfo.tsx +12 -0
- package/src/icons/menu/CalloutSuccess.tsx +10 -0
- package/src/icons/menu/CalloutWarning.tsx +12 -0
- package/src/icons/menu/ClearFormatting.tsx +10 -0
- package/src/icons/menu/Code.tsx +10 -0
- package/src/icons/menu/CodeBlock.tsx +10 -0
- package/src/icons/menu/Copyable.tsx +10 -0
- package/src/icons/menu/CustomQuote.tsx +17 -0
- package/src/icons/menu/DeleteColumn.tsx +17 -0
- package/src/icons/menu/DeleteRow.tsx +17 -0
- package/src/icons/menu/Details.tsx +10 -0
- package/src/icons/menu/H2.tsx +10 -0
- package/src/icons/menu/H3.tsx +10 -0
- package/src/icons/menu/H4.tsx +10 -0
- package/src/icons/menu/H5.tsx +10 -0
- package/src/icons/menu/H6.tsx +10 -0
- package/src/icons/menu/Highlight.tsx +13 -0
- package/src/icons/menu/HorizontalRule.tsx +7 -0
- package/src/icons/menu/Italic.tsx +10 -0
- package/src/icons/menu/Link.tsx +10 -0
- package/src/icons/menu/LinkOff.tsx +10 -0
- package/src/icons/menu/Math.tsx +10 -0
- package/src/icons/menu/MergeCells.tsx +10 -0
- package/src/icons/menu/MythReality.tsx +10 -0
- package/src/icons/menu/OrderedList.tsx +10 -0
- package/src/icons/menu/Prompt.tsx +18 -0
- package/src/icons/menu/Redo.tsx +14 -0
- package/src/icons/menu/SplitCells.tsx +7 -0
- package/src/icons/menu/Strikethrough.tsx +10 -0
- package/src/icons/menu/Subscript.tsx +10 -0
- package/src/icons/menu/Superscript.tsx +10 -0
- package/src/icons/menu/Table.tsx +14 -0
- package/src/icons/menu/TableColumnHeading.tsx +7 -0
- package/src/icons/menu/TableRowHeading.tsx +10 -0
- package/src/icons/menu/TextColor.tsx +13 -0
- package/src/icons/menu/Underline.tsx +10 -0
- package/src/icons/menu/Undo.tsx +14 -0
- package/src/icons/menu/Video.tsx +14 -0
- package/src/icons/menu/index.ts +49 -0
- package/src/render/index.ts +22 -0
- package/src/render/static-renderer-config.tsx +206 -0
- package/src/render/tiptap-interactivity-provider.tsx +78 -0
- package/src/render/tiptap-render.tsx +102 -0
- package/src/styles/components.css +10 -0
- package/src/styles/editor/base.css +39 -0
- package/src/styles/editor/callout-menu.css +81 -0
- package/src/styles/editor/controls.css +57 -0
- package/src/styles/editor/emoji-picker.css +113 -0
- package/src/styles/editor/index.css +9 -0
- package/src/styles/editor/menus.css +16 -0
- package/src/styles/editor/states.css +43 -0
- package/src/styles/editor/typography.css +165 -0
- package/src/styles/editor/utilities.css +71 -0
- package/src/styles/editor/variables.css +70 -0
- package/src/styles/extensions/callout.css +155 -0
- package/src/styles/extensions/code-block.css +109 -0
- package/src/styles/extensions/commands-list.css +83 -0
- package/src/styles/extensions/custom-quote.css +103 -0
- package/src/styles/extensions/details.css +87 -0
- package/src/styles/extensions/figure.css +66 -0
- package/src/styles/extensions/index.css +11 -0
- package/src/styles/extensions/math.css +32 -0
- package/src/styles/extensions/myth-reality.css +59 -0
- package/src/styles/extensions/prompt.css +75 -0
- package/src/styles/extensions/table.css +253 -0
- package/src/styles/extensions/video.css +53 -0
- package/src/styles/styles.css +3 -0
- package/src/translations/en.json +158 -0
- package/src/translations/ru.json +158 -0
- package/src/types/image-handler.ts +33 -0
- package/src/types/index.ts +1 -0
- package/src/utils/copy-to-clipboard.ts +23 -0
- package/src/utils/custom-emoji.ts +174 -0
- package/src/utils/detect-video-hosting.ts +106 -0
- package/src/utils/get-shared-extensions.ts +45 -0
- package/src/utils/get-translations.ts +25 -0
- package/src/utils/handle-exit-node.ts +61 -0
- package/src/utils/image-src-resolver.ts +74 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/migrate-legacy-callouts.ts +59 -0
- package/src/utils/pluralize.ts +38 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client component that initializes Tiptap interactivity
|
|
3
|
+
* Use this in your layout to enable interactive features for server-rendered content
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* // app/layout.tsx (Server Component)
|
|
8
|
+
* import { CalloutInteractivityProvider } from '@cyberyozh/tiptap/render';
|
|
9
|
+
*
|
|
10
|
+
* export default function Layout({ children }) {
|
|
11
|
+
* return (
|
|
12
|
+
* <html>
|
|
13
|
+
* <body>
|
|
14
|
+
* <CalloutInteractivityProvider />
|
|
15
|
+
* {children}
|
|
16
|
+
* </body>
|
|
17
|
+
* </html>
|
|
18
|
+
* );
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare function TiptapInteractivityProvider(): null;
|
|
23
|
+
|
|
24
|
+
export { TiptapInteractivityProvider };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client component that initializes Tiptap interactivity
|
|
3
|
+
* Use this in your layout to enable interactive features for server-rendered content
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* // app/layout.tsx (Server Component)
|
|
8
|
+
* import { CalloutInteractivityProvider } from '@cyberyozh/tiptap/render';
|
|
9
|
+
*
|
|
10
|
+
* export default function Layout({ children }) {
|
|
11
|
+
* return (
|
|
12
|
+
* <html>
|
|
13
|
+
* <body>
|
|
14
|
+
* <CalloutInteractivityProvider />
|
|
15
|
+
* {children}
|
|
16
|
+
* </body>
|
|
17
|
+
* </html>
|
|
18
|
+
* );
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare function TiptapInteractivityProvider(): null;
|
|
23
|
+
|
|
24
|
+
export { TiptapInteractivityProvider };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/render/tiptap-interactivity-provider.tsx
|
|
23
|
+
var tiptap_interactivity_provider_exports = {};
|
|
24
|
+
__export(tiptap_interactivity_provider_exports, {
|
|
25
|
+
TiptapInteractivityProvider: () => TiptapInteractivityProvider
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(tiptap_interactivity_provider_exports);
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
|
|
30
|
+
// src/utils/copy-to-clipboard.ts
|
|
31
|
+
var copyToClipboard = (text) => {
|
|
32
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
33
|
+
return navigator.clipboard.writeText(text);
|
|
34
|
+
}
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const textarea = document.createElement("textarea");
|
|
37
|
+
textarea.value = text;
|
|
38
|
+
textarea.style.position = "fixed";
|
|
39
|
+
textarea.style.opacity = "0";
|
|
40
|
+
document.body.appendChild(textarea);
|
|
41
|
+
try {
|
|
42
|
+
textarea.select();
|
|
43
|
+
document.execCommand("copy");
|
|
44
|
+
resolve();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
reject(error);
|
|
47
|
+
} finally {
|
|
48
|
+
document.body.removeChild(textarea);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/render/tiptap-interactivity-provider.tsx
|
|
54
|
+
function TiptapInteractivityProvider() {
|
|
55
|
+
(0, import_react.useEffect)(() => {
|
|
56
|
+
function findCopyContent(button) {
|
|
57
|
+
const parent = button.closest(":has([data-copy-content])");
|
|
58
|
+
return parent?.querySelector("[data-copy-content]") || null;
|
|
59
|
+
}
|
|
60
|
+
function showSuccessIcon(button) {
|
|
61
|
+
const defaultIcon = button.querySelector('[data-copy-icon="default"]');
|
|
62
|
+
const successIcon = button.querySelector('[data-copy-icon="success"]');
|
|
63
|
+
if (defaultIcon && successIcon) {
|
|
64
|
+
defaultIcon.style.display = "none";
|
|
65
|
+
successIcon.style.display = "flex";
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
defaultIcon.style.display = "flex";
|
|
68
|
+
successIcon.style.display = "none";
|
|
69
|
+
}, 1500);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function handleCopy(e) {
|
|
73
|
+
const target = e.target;
|
|
74
|
+
const button = target.closest('[data-copy-action="copy"]');
|
|
75
|
+
if (button) {
|
|
76
|
+
const contentElement = findCopyContent(button);
|
|
77
|
+
if (contentElement) {
|
|
78
|
+
const textContent = contentElement.textContent?.trim() || "";
|
|
79
|
+
copyToClipboard(textContent).then(() => {
|
|
80
|
+
showSuccessIcon(button);
|
|
81
|
+
}).catch((error) => {
|
|
82
|
+
console.error("\u274C Failed to copy:", error);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
document.addEventListener("click", handleCopy);
|
|
88
|
+
return () => {
|
|
89
|
+
document.removeEventListener("click", handleCopy);
|
|
90
|
+
};
|
|
91
|
+
}, []);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
95
|
+
0 && (module.exports = {
|
|
96
|
+
TiptapInteractivityProvider
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/render/tiptap-interactivity-provider.tsx","../../src/utils/copy-to-clipboard.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\nimport { copyToClipboard } from \"../utils/copy-to-clipboard\";\n\n/**\n * Client component that initializes Tiptap interactivity\n * Use this in your layout to enable interactive features for server-rendered content\n *\n * @example\n * ```tsx\n * // app/layout.tsx (Server Component)\n * import { CalloutInteractivityProvider } from '@cyberyozh/tiptap/render';\n *\n * export default function Layout({ children }) {\n * return (\n * <html>\n * <body>\n * <CalloutInteractivityProvider />\n * {children}\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function TiptapInteractivityProvider() {\n useEffect(() => {\n function findCopyContent(button: HTMLElement): Element | null {\n const parent = button.closest(\":has([data-copy-content])\");\n return parent?.querySelector(\"[data-copy-content]\") || null;\n }\n\n function showSuccessIcon(button: HTMLElement) {\n const defaultIcon = button.querySelector('[data-copy-icon=\"default\"]') as HTMLElement;\n const successIcon = button.querySelector('[data-copy-icon=\"success\"]') as HTMLElement;\n\n if (defaultIcon && successIcon) {\n defaultIcon.style.display = \"none\";\n successIcon.style.display = \"flex\";\n\n setTimeout(() => {\n defaultIcon.style.display = \"flex\";\n successIcon.style.display = \"none\";\n }, 1500);\n }\n }\n\n function handleCopy(e: MouseEvent) {\n const target = e.target as HTMLElement;\n const button = target.closest('[data-copy-action=\"copy\"]') as HTMLElement;\n\n if (button) {\n const contentElement = findCopyContent(button);\n\n if (contentElement) {\n const textContent = contentElement.textContent?.trim() || \"\";\n\n copyToClipboard(textContent)\n .then(() => {\n showSuccessIcon(button);\n })\n .catch((error) => {\n console.error(\"❌ Failed to copy:\", error);\n });\n }\n }\n }\n\n document.addEventListener(\"click\", handleCopy);\n\n return () => {\n document.removeEventListener(\"click\", handleCopy);\n };\n }, []);\n\n return null;\n}\n","export const copyToClipboard = (text: string): Promise<void> => {\n if (navigator.clipboard && window.isSecureContext) {\n return navigator.clipboard.writeText(text);\n }\n\n return new Promise((resolve, reject) => {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n document.body.appendChild(textarea);\n\n try {\n textarea.select();\n document.execCommand(\"copy\");\n resolve();\n } catch (error) {\n reject(error);\n } finally {\n document.body.removeChild(textarea);\n }\n });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,mBAA0B;;;ACFnB,IAAM,kBAAkB,CAAC,SAAgC;AAC9D,MAAI,UAAU,aAAa,OAAO,iBAAiB;AACjD,WAAO,UAAU,UAAU,UAAU,IAAI;AAAA,EAC3C;AAEA,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,WAAW,SAAS,cAAc,UAAU;AAClD,aAAS,QAAQ;AACjB,aAAS,MAAM,WAAW;AAC1B,aAAS,MAAM,UAAU;AACzB,aAAS,KAAK,YAAY,QAAQ;AAElC,QAAI;AACF,eAAS,OAAO;AAChB,eAAS,YAAY,MAAM;AAC3B,cAAQ;AAAA,IACV,SAAS,OAAO;AACd,aAAO,KAAK;AAAA,IACd,UAAE;AACA,eAAS,KAAK,YAAY,QAAQ;AAAA,IACpC;AAAA,EACF,CAAC;AACH;;;ADIO,SAAS,8BAA8B;AAC5C,8BAAU,MAAM;AACd,aAAS,gBAAgB,QAAqC;AAC5D,YAAM,SAAS,OAAO,QAAQ,2BAA2B;AACzD,aAAO,QAAQ,cAAc,qBAAqB,KAAK;AAAA,IACzD;AAEA,aAAS,gBAAgB,QAAqB;AAC5C,YAAM,cAAc,OAAO,cAAc,4BAA4B;AACrE,YAAM,cAAc,OAAO,cAAc,4BAA4B;AAErE,UAAI,eAAe,aAAa;AAC9B,oBAAY,MAAM,UAAU;AAC5B,oBAAY,MAAM,UAAU;AAE5B,mBAAW,MAAM;AACf,sBAAY,MAAM,UAAU;AAC5B,sBAAY,MAAM,UAAU;AAAA,QAC9B,GAAG,IAAI;AAAA,MACT;AAAA,IACF;AAEA,aAAS,WAAW,GAAe;AACjC,YAAM,SAAS,EAAE;AACjB,YAAM,SAAS,OAAO,QAAQ,2BAA2B;AAEzD,UAAI,QAAQ;AACV,cAAM,iBAAiB,gBAAgB,MAAM;AAE7C,YAAI,gBAAgB;AAClB,gBAAM,cAAc,eAAe,aAAa,KAAK,KAAK;AAE1D,0BAAgB,WAAW,EACxB,KAAK,MAAM;AACV,4BAAgB,MAAM;AAAA,UACxB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,oBAAQ,MAAM,0BAAqB,KAAK;AAAA,UAC1C,CAAC;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAEA,aAAS,iBAAiB,SAAS,UAAU;AAE7C,WAAO,MAAM;AACX,eAAS,oBAAoB,SAAS,UAAU;AAAA,IAClD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/render/tiptap-interactivity-provider.tsx
|
|
5
|
+
import { useEffect } from "react";
|
|
6
|
+
|
|
7
|
+
// src/utils/copy-to-clipboard.ts
|
|
8
|
+
var copyToClipboard = (text) => {
|
|
9
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
10
|
+
return navigator.clipboard.writeText(text);
|
|
11
|
+
}
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const textarea = document.createElement("textarea");
|
|
14
|
+
textarea.value = text;
|
|
15
|
+
textarea.style.position = "fixed";
|
|
16
|
+
textarea.style.opacity = "0";
|
|
17
|
+
document.body.appendChild(textarea);
|
|
18
|
+
try {
|
|
19
|
+
textarea.select();
|
|
20
|
+
document.execCommand("copy");
|
|
21
|
+
resolve();
|
|
22
|
+
} catch (error) {
|
|
23
|
+
reject(error);
|
|
24
|
+
} finally {
|
|
25
|
+
document.body.removeChild(textarea);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/render/tiptap-interactivity-provider.tsx
|
|
31
|
+
function TiptapInteractivityProvider() {
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
function findCopyContent(button) {
|
|
34
|
+
const parent = button.closest(":has([data-copy-content])");
|
|
35
|
+
return parent?.querySelector("[data-copy-content]") || null;
|
|
36
|
+
}
|
|
37
|
+
function showSuccessIcon(button) {
|
|
38
|
+
const defaultIcon = button.querySelector('[data-copy-icon="default"]');
|
|
39
|
+
const successIcon = button.querySelector('[data-copy-icon="success"]');
|
|
40
|
+
if (defaultIcon && successIcon) {
|
|
41
|
+
defaultIcon.style.display = "none";
|
|
42
|
+
successIcon.style.display = "flex";
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
defaultIcon.style.display = "flex";
|
|
45
|
+
successIcon.style.display = "none";
|
|
46
|
+
}, 1500);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function handleCopy(e) {
|
|
50
|
+
const target = e.target;
|
|
51
|
+
const button = target.closest('[data-copy-action="copy"]');
|
|
52
|
+
if (button) {
|
|
53
|
+
const contentElement = findCopyContent(button);
|
|
54
|
+
if (contentElement) {
|
|
55
|
+
const textContent = contentElement.textContent?.trim() || "";
|
|
56
|
+
copyToClipboard(textContent).then(() => {
|
|
57
|
+
showSuccessIcon(button);
|
|
58
|
+
}).catch((error) => {
|
|
59
|
+
console.error("\u274C Failed to copy:", error);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
document.addEventListener("click", handleCopy);
|
|
65
|
+
return () => {
|
|
66
|
+
document.removeEventListener("click", handleCopy);
|
|
67
|
+
};
|
|
68
|
+
}, []);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
TiptapInteractivityProvider
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/render/tiptap-interactivity-provider.tsx","../../src/utils/copy-to-clipboard.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect } from \"react\";\nimport { copyToClipboard } from \"../utils/copy-to-clipboard\";\n\n/**\n * Client component that initializes Tiptap interactivity\n * Use this in your layout to enable interactive features for server-rendered content\n *\n * @example\n * ```tsx\n * // app/layout.tsx (Server Component)\n * import { CalloutInteractivityProvider } from '@cyberyozh/tiptap/render';\n *\n * export default function Layout({ children }) {\n * return (\n * <html>\n * <body>\n * <CalloutInteractivityProvider />\n * {children}\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function TiptapInteractivityProvider() {\n useEffect(() => {\n function findCopyContent(button: HTMLElement): Element | null {\n const parent = button.closest(\":has([data-copy-content])\");\n return parent?.querySelector(\"[data-copy-content]\") || null;\n }\n\n function showSuccessIcon(button: HTMLElement) {\n const defaultIcon = button.querySelector('[data-copy-icon=\"default\"]') as HTMLElement;\n const successIcon = button.querySelector('[data-copy-icon=\"success\"]') as HTMLElement;\n\n if (defaultIcon && successIcon) {\n defaultIcon.style.display = \"none\";\n successIcon.style.display = \"flex\";\n\n setTimeout(() => {\n defaultIcon.style.display = \"flex\";\n successIcon.style.display = \"none\";\n }, 1500);\n }\n }\n\n function handleCopy(e: MouseEvent) {\n const target = e.target as HTMLElement;\n const button = target.closest('[data-copy-action=\"copy\"]') as HTMLElement;\n\n if (button) {\n const contentElement = findCopyContent(button);\n\n if (contentElement) {\n const textContent = contentElement.textContent?.trim() || \"\";\n\n copyToClipboard(textContent)\n .then(() => {\n showSuccessIcon(button);\n })\n .catch((error) => {\n console.error(\"❌ Failed to copy:\", error);\n });\n }\n }\n }\n\n document.addEventListener(\"click\", handleCopy);\n\n return () => {\n document.removeEventListener(\"click\", handleCopy);\n };\n }, []);\n\n return null;\n}\n","export const copyToClipboard = (text: string): Promise<void> => {\n if (navigator.clipboard && window.isSecureContext) {\n return navigator.clipboard.writeText(text);\n }\n\n return new Promise((resolve, reject) => {\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.opacity = \"0\";\n document.body.appendChild(textarea);\n\n try {\n textarea.select();\n document.execCommand(\"copy\");\n resolve();\n } catch (error) {\n reject(error);\n } finally {\n document.body.removeChild(textarea);\n }\n });\n};\n"],"mappings":";;;;AAEA,SAAS,iBAAiB;;;ACFnB,IAAM,kBAAkB,CAAC,SAAgC;AAC9D,MAAI,UAAU,aAAa,OAAO,iBAAiB;AACjD,WAAO,UAAU,UAAU,UAAU,IAAI;AAAA,EAC3C;AAEA,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,WAAW,SAAS,cAAc,UAAU;AAClD,aAAS,QAAQ;AACjB,aAAS,MAAM,WAAW;AAC1B,aAAS,MAAM,UAAU;AACzB,aAAS,KAAK,YAAY,QAAQ;AAElC,QAAI;AACF,eAAS,OAAO;AAChB,eAAS,YAAY,MAAM;AAC3B,cAAQ;AAAA,IACV,SAAS,OAAO;AACd,aAAO,KAAK;AAAA,IACd,UAAE;AACA,eAAS,KAAK,YAAY,QAAQ;AAAA,IACpC;AAAA,EACF,CAAC;AACH;;;ADIO,SAAS,8BAA8B;AAC5C,YAAU,MAAM;AACd,aAAS,gBAAgB,QAAqC;AAC5D,YAAM,SAAS,OAAO,QAAQ,2BAA2B;AACzD,aAAO,QAAQ,cAAc,qBAAqB,KAAK;AAAA,IACzD;AAEA,aAAS,gBAAgB,QAAqB;AAC5C,YAAM,cAAc,OAAO,cAAc,4BAA4B;AACrE,YAAM,cAAc,OAAO,cAAc,4BAA4B;AAErE,UAAI,eAAe,aAAa;AAC9B,oBAAY,MAAM,UAAU;AAC5B,oBAAY,MAAM,UAAU;AAE5B,mBAAW,MAAM;AACf,sBAAY,MAAM,UAAU;AAC5B,sBAAY,MAAM,UAAU;AAAA,QAC9B,GAAG,IAAI;AAAA,MACT;AAAA,IACF;AAEA,aAAS,WAAW,GAAe;AACjC,YAAM,SAAS,EAAE;AACjB,YAAM,SAAS,OAAO,QAAQ,2BAA2B;AAEzD,UAAI,QAAQ;AACV,cAAM,iBAAiB,gBAAgB,MAAM;AAE7C,YAAI,gBAAgB;AAClB,gBAAM,cAAc,eAAe,aAAa,KAAK,KAAK;AAE1D,0BAAgB,WAAW,EACxB,KAAK,MAAM;AACV,4BAAgB,MAAM;AAAA,UACxB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,oBAAQ,MAAM,0BAAqB,KAAK;AAAA,UAC1C,CAAC;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAEA,aAAS,iBAAiB,SAAS,UAAU;AAE7C,WAAO,MAAM;AACX,eAAS,oBAAoB,SAAS,UAAU;AAAA,IAClD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
type JSONContent = Record<string, any>;
|
|
4
|
+
interface TiptapRenderProps {
|
|
5
|
+
/**
|
|
6
|
+
* Tiptap JSON content (parsed object or JSON string)
|
|
7
|
+
*/
|
|
8
|
+
content: JSONContent | string;
|
|
9
|
+
/**
|
|
10
|
+
* Function to resolve image URLs from file IDs
|
|
11
|
+
* Used for static rendering of figure nodes with file references
|
|
12
|
+
*/
|
|
13
|
+
getImageById?: (fileId: string) => string | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Server component that renders Tiptap JSON content as React
|
|
17
|
+
*
|
|
18
|
+
* Designed for Next.js Server Components. Converts Tiptap JSON to rendered React elements
|
|
19
|
+
* without any client-side dependencies.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* // app/blog/[slug]/page.tsx (Server Component)
|
|
24
|
+
* import { TiptapRender } from '@cyberyozh/tiptap/server';
|
|
25
|
+
*
|
|
26
|
+
* export default function BlogPost({ article }) {
|
|
27
|
+
* return (
|
|
28
|
+
* <article>
|
|
29
|
+
* <h1>{article.title}</h1>
|
|
30
|
+
* <TiptapRender content={article.content} lang="en" />
|
|
31
|
+
* </article>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* // With custom image resolver
|
|
39
|
+
* import { TiptapRender } from '@cyberyozh/tiptap/server';
|
|
40
|
+
*
|
|
41
|
+
* const getImageById = (fileId: string) => `/api/images/${fileId}`;
|
|
42
|
+
*
|
|
43
|
+
* export default function BlogPost({ article }) {
|
|
44
|
+
* return (
|
|
45
|
+
* <article>
|
|
46
|
+
* <TiptapRender
|
|
47
|
+
* content={article.content}
|
|
48
|
+
* lang="en"
|
|
49
|
+
* getImageById={getImageById}
|
|
50
|
+
* />
|
|
51
|
+
* </article>
|
|
52
|
+
* );
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function TiptapRender({ content, getImageById }: TiptapRenderProps): ReactNode;
|
|
57
|
+
|
|
58
|
+
export { TiptapRender };
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
type JSONContent = Record<string, any>;
|
|
4
|
+
interface TiptapRenderProps {
|
|
5
|
+
/**
|
|
6
|
+
* Tiptap JSON content (parsed object or JSON string)
|
|
7
|
+
*/
|
|
8
|
+
content: JSONContent | string;
|
|
9
|
+
/**
|
|
10
|
+
* Function to resolve image URLs from file IDs
|
|
11
|
+
* Used for static rendering of figure nodes with file references
|
|
12
|
+
*/
|
|
13
|
+
getImageById?: (fileId: string) => string | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Server component that renders Tiptap JSON content as React
|
|
17
|
+
*
|
|
18
|
+
* Designed for Next.js Server Components. Converts Tiptap JSON to rendered React elements
|
|
19
|
+
* without any client-side dependencies.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* // app/blog/[slug]/page.tsx (Server Component)
|
|
24
|
+
* import { TiptapRender } from '@cyberyozh/tiptap/server';
|
|
25
|
+
*
|
|
26
|
+
* export default function BlogPost({ article }) {
|
|
27
|
+
* return (
|
|
28
|
+
* <article>
|
|
29
|
+
* <h1>{article.title}</h1>
|
|
30
|
+
* <TiptapRender content={article.content} lang="en" />
|
|
31
|
+
* </article>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```tsx
|
|
38
|
+
* // With custom image resolver
|
|
39
|
+
* import { TiptapRender } from '@cyberyozh/tiptap/server';
|
|
40
|
+
*
|
|
41
|
+
* const getImageById = (fileId: string) => `/api/images/${fileId}`;
|
|
42
|
+
*
|
|
43
|
+
* export default function BlogPost({ article }) {
|
|
44
|
+
* return (
|
|
45
|
+
* <article>
|
|
46
|
+
* <TiptapRender
|
|
47
|
+
* content={article.content}
|
|
48
|
+
* lang="en"
|
|
49
|
+
* getImageById={getImageById}
|
|
50
|
+
* />
|
|
51
|
+
* </article>
|
|
52
|
+
* );
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function TiptapRender({ content, getImageById }: TiptapRenderProps): ReactNode;
|
|
57
|
+
|
|
58
|
+
export { TiptapRender };
|