@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,20 @@
|
|
|
1
|
+
import { ReactNodeViewRenderer } from "@tiptap/react";
|
|
2
|
+
import { BaseFigure } from "../base";
|
|
3
|
+
import { FigureView } from "../views/node-views";
|
|
4
|
+
import { BaseLegacyFigure } from "../base/compatibility";
|
|
5
|
+
|
|
6
|
+
export const Figure = BaseFigure.extend({
|
|
7
|
+
addNodeView() {
|
|
8
|
+
return ReactNodeViewRenderer(FigureView);
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Legacy Figure extension for backward compatibility with old Tiptap content.
|
|
14
|
+
* @deprecated Use {@link Figure} extension instead.
|
|
15
|
+
*/
|
|
16
|
+
export const LegacyFigure = BaseLegacyFigure.extend({
|
|
17
|
+
addNodeView() {
|
|
18
|
+
return ReactNodeViewRenderer(FigureView);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Mathematics } from "@tiptap/extension-mathematics";
|
|
2
|
+
import { TableKit } from "@tiptap/extension-table";
|
|
3
|
+
|
|
4
|
+
import { Callout, LegacyBookAlert, LegacyBookTip } from "./callout";
|
|
5
|
+
import { CodeBlock } from "./code-block";
|
|
6
|
+
import {
|
|
7
|
+
CustomQuote,
|
|
8
|
+
CustomQuoteText,
|
|
9
|
+
CustomQuoteAuthor,
|
|
10
|
+
CustomQuoteSign,
|
|
11
|
+
LegacySEQuote,
|
|
12
|
+
} from "./custom-quote";
|
|
13
|
+
import { Video, LegacyYouTube } from "./video";
|
|
14
|
+
import { Commands } from "./commands";
|
|
15
|
+
import { getSharedExtensions } from "@/utils/get-shared-extensions";
|
|
16
|
+
import { CopyableText } from "./copyable-text";
|
|
17
|
+
import { BasePrompt } from "../base";
|
|
18
|
+
import { LegacyPromptSEContent, LegacyPromptSEHeader, PromptContent, PromptHeader } from "./prompt";
|
|
19
|
+
import { BaseLegacyPromptSE, BaseLegacyCustomBlockquote } from "../base/compatibility";
|
|
20
|
+
import { Figure, LegacyFigure } from "./figure";
|
|
21
|
+
|
|
22
|
+
import type { TiptapTranslations } from "@/utils/get-translations";
|
|
23
|
+
import { CharacterCount, Placeholder } from "@tiptap/extensions";
|
|
24
|
+
|
|
25
|
+
interface GetExtensionsOptions {
|
|
26
|
+
translations: TiptapTranslations;
|
|
27
|
+
suggestion?: any;
|
|
28
|
+
isEditable?: boolean;
|
|
29
|
+
getImageById?: (fileId: string) => string | null;
|
|
30
|
+
onKatexClick?: (latex: string, pos?: number) => void;
|
|
31
|
+
onFigureClick?: (attrs: any, pos: number) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get all Tiptap extensions for Editor instance
|
|
36
|
+
*
|
|
37
|
+
* This extensions can be used for static render (server-side)
|
|
38
|
+
*/
|
|
39
|
+
export function getEditorExtensions(options: GetExtensionsOptions) {
|
|
40
|
+
const {
|
|
41
|
+
suggestion,
|
|
42
|
+
translations,
|
|
43
|
+
isEditable = false,
|
|
44
|
+
getImageById,
|
|
45
|
+
onKatexClick,
|
|
46
|
+
onFigureClick,
|
|
47
|
+
} = options;
|
|
48
|
+
const sharedExtensions = getSharedExtensions();
|
|
49
|
+
|
|
50
|
+
return [
|
|
51
|
+
...sharedExtensions,
|
|
52
|
+
Callout,
|
|
53
|
+
CopyableText,
|
|
54
|
+
CodeBlock,
|
|
55
|
+
CustomQuote,
|
|
56
|
+
CustomQuoteText,
|
|
57
|
+
CustomQuoteAuthor,
|
|
58
|
+
CustomQuoteSign,
|
|
59
|
+
BasePrompt,
|
|
60
|
+
PromptHeader,
|
|
61
|
+
PromptContent,
|
|
62
|
+
Video,
|
|
63
|
+
|
|
64
|
+
// Configure with optional getImageById for URL resolution and onFigureClick callback
|
|
65
|
+
Figure.configure({ getImageById, onFigureClick }),
|
|
66
|
+
Mathematics.configure({
|
|
67
|
+
katexOptions: {
|
|
68
|
+
throwOnError: false,
|
|
69
|
+
},
|
|
70
|
+
inlineOptions: {
|
|
71
|
+
onClick: (node, pos) => {
|
|
72
|
+
onKatexClick?.(node.attrs.latex, pos);
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
Placeholder.configure({
|
|
77
|
+
includeChildren: true,
|
|
78
|
+
placeholder: ({ node }) => {
|
|
79
|
+
if (!translations) return "";
|
|
80
|
+
|
|
81
|
+
if (node.type.name === "detailsSummary") {
|
|
82
|
+
return translations.editor?.detailsSummaryPlaceholder || "";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (node.type.name === "tableCell") {
|
|
86
|
+
return "";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return translations.editor?.commandMenuPlaceholder || "";
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
TableKit.configure({
|
|
93
|
+
table: { resizable: isEditable },
|
|
94
|
+
}),
|
|
95
|
+
Commands.configure({
|
|
96
|
+
suggestion,
|
|
97
|
+
}),
|
|
98
|
+
CharacterCount,
|
|
99
|
+
|
|
100
|
+
// ========================================================================
|
|
101
|
+
// Legacy Extensions (Deprecated - for backward compatibility only)
|
|
102
|
+
// ========================================================================
|
|
103
|
+
LegacyBookAlert,
|
|
104
|
+
LegacyBookTip,
|
|
105
|
+
LegacySEQuote,
|
|
106
|
+
BaseLegacyPromptSE,
|
|
107
|
+
LegacyPromptSEHeader,
|
|
108
|
+
LegacyPromptSEContent,
|
|
109
|
+
BaseLegacyCustomBlockquote,
|
|
110
|
+
LegacyYouTube,
|
|
111
|
+
LegacyFigure.configure({ getImageById, onFigureClick }),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// export { Callout, BookAlert, BookTip } from "./callout";
|
|
2
|
+
// export { CodeBlock } from "./code-block";
|
|
3
|
+
// export { Commands } from "./commands";
|
|
4
|
+
// export { CopyableText } from "./copyable-text";
|
|
5
|
+
// export {
|
|
6
|
+
// CustomQuote,
|
|
7
|
+
// CustomQuoteText,
|
|
8
|
+
// CustomQuoteAuthor,
|
|
9
|
+
// CustomQuoteSign,
|
|
10
|
+
// CustomBlockquote,
|
|
11
|
+
// CustomQuoteSE,
|
|
12
|
+
// } from "./custom-quote";
|
|
13
|
+
// export { Figure, LegacyFigure, type FigureAttrs } from "./figure";
|
|
14
|
+
// export { InlineLatexSE } from "./inline-latex";
|
|
15
|
+
// export {
|
|
16
|
+
// Prompt,
|
|
17
|
+
// PromptHeader,
|
|
18
|
+
// PromptContent,
|
|
19
|
+
// PromptSE,
|
|
20
|
+
// PromptSEHeader,
|
|
21
|
+
// PromptSEContent,
|
|
22
|
+
// } from "./prompt";
|
|
23
|
+
// export { Video, YouTube } from "./video";
|
|
24
|
+
|
|
25
|
+
export { getEditorExtensions } from "./get-editor-extensions";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ReactNodeViewRenderer } from "@tiptap/react";
|
|
2
|
+
|
|
3
|
+
import { PromptHeaderView, PromptContentView } from "../views/node-views";
|
|
4
|
+
import { BasePromptContent, BasePromptHeader } from "../base";
|
|
5
|
+
import { BaseLegacyPromptSEContent, BaseLegacyPromptSEHeader } from "../base/compatibility";
|
|
6
|
+
|
|
7
|
+
export const PromptHeader = BasePromptHeader.extend({
|
|
8
|
+
addNodeView() {
|
|
9
|
+
return ReactNodeViewRenderer(PromptHeaderView);
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const PromptContent = BasePromptContent.extend({
|
|
14
|
+
addNodeView() {
|
|
15
|
+
return ReactNodeViewRenderer(PromptContentView);
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Legacy Prompt V2 Compatibility Extensions
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Legacy PromptV2Header extension for backward compatibility with old Tiptap copy editor content.
|
|
25
|
+
* @deprecated Use {@link PromptHeader} instead.
|
|
26
|
+
* This extension is kept only for rendering old content that uses the `promptV2Header` node type.
|
|
27
|
+
*/
|
|
28
|
+
export const LegacyPromptSEHeader = BaseLegacyPromptSEHeader.extend({
|
|
29
|
+
addNodeView() {
|
|
30
|
+
return ReactNodeViewRenderer(PromptHeaderView);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Legacy PromptV2Content extension for backward compatibility with old Tiptap copy editor content.
|
|
36
|
+
* @deprecated Use {@link PromptContent} instead.
|
|
37
|
+
* This extension is kept only for rendering old content that uses the `promptV2Content` node type.
|
|
38
|
+
*/
|
|
39
|
+
export const LegacyPromptSEContent = BaseLegacyPromptSEContent.extend({
|
|
40
|
+
addNodeView() {
|
|
41
|
+
return ReactNodeViewRenderer(PromptContentView);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ReactNodeViewRenderer } from "@tiptap/react";
|
|
2
|
+
|
|
3
|
+
import { BaseVideo } from "../base/video";
|
|
4
|
+
import { VideoView } from "../views/node-views";
|
|
5
|
+
import { BaseLegacyYouTube } from "../base/compatibility";
|
|
6
|
+
|
|
7
|
+
export const Video = BaseVideo.extend({
|
|
8
|
+
addNodeView() {
|
|
9
|
+
return ReactNodeViewRenderer(VideoView);
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Legacy YouTube extension for backward compatibility with old Tiptap content.
|
|
15
|
+
* @deprecated Use {@link Video} extension instead.
|
|
16
|
+
* This extension is kept only for rendering old content that uses the `youtube` node type.
|
|
17
|
+
* Renders using the same VideoView component.
|
|
18
|
+
*/
|
|
19
|
+
export const LegacyYouTube = BaseLegacyYouTube.extend({
|
|
20
|
+
addNodeView() {
|
|
21
|
+
return ReactNodeViewRenderer(VideoView);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Mathematics } from "@tiptap/extension-mathematics";
|
|
2
|
+
import { TableKit } from "@tiptap/extension-table";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
BasePrompt,
|
|
6
|
+
BasePromptContent,
|
|
7
|
+
BasePromptHeader,
|
|
8
|
+
BaseCallout,
|
|
9
|
+
BaseCodeBlock,
|
|
10
|
+
CopyableText,
|
|
11
|
+
BaseCustomQuote,
|
|
12
|
+
BaseCustomQuoteAuthor,
|
|
13
|
+
BaseCustomQuoteSign,
|
|
14
|
+
BaseCustomQuoteText,
|
|
15
|
+
BaseVideo,
|
|
16
|
+
BaseFigure,
|
|
17
|
+
} from "../base";
|
|
18
|
+
import { getSharedExtensions } from "@/utils/get-shared-extensions";
|
|
19
|
+
import {
|
|
20
|
+
BaseLegacyCustomBlockquote,
|
|
21
|
+
BaseLegacyPromptSE,
|
|
22
|
+
BaseLegacyPromptSEContent,
|
|
23
|
+
BaseLegacyPromptSEHeader,
|
|
24
|
+
BaseLegacyBookAlert,
|
|
25
|
+
BaseLegacyBookTip,
|
|
26
|
+
BaseLegacySEQuote,
|
|
27
|
+
BaseLegacyYouTube,
|
|
28
|
+
BaseLegacyFigure,
|
|
29
|
+
} from "../base/compatibility";
|
|
30
|
+
|
|
31
|
+
export function getRenderExtensions() {
|
|
32
|
+
const sharedExtensions = getSharedExtensions();
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
...sharedExtensions,
|
|
36
|
+
CopyableText,
|
|
37
|
+
BaseCallout,
|
|
38
|
+
BaseCodeBlock,
|
|
39
|
+
BaseCustomQuote,
|
|
40
|
+
BaseCustomQuoteText,
|
|
41
|
+
BaseCustomQuoteAuthor,
|
|
42
|
+
BaseCustomQuoteSign,
|
|
43
|
+
BasePrompt,
|
|
44
|
+
BasePromptHeader,
|
|
45
|
+
BasePromptContent,
|
|
46
|
+
BaseVideo,
|
|
47
|
+
BaseFigure,
|
|
48
|
+
|
|
49
|
+
// Need to update
|
|
50
|
+
TableKit.configure({
|
|
51
|
+
table: { resizable: false },
|
|
52
|
+
}),
|
|
53
|
+
|
|
54
|
+
Mathematics.configure({
|
|
55
|
+
katexOptions: {
|
|
56
|
+
throwOnError: false,
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
|
|
60
|
+
// ========================================================================
|
|
61
|
+
// Legacy Extensions (Deprecated - for backward compatibility only)
|
|
62
|
+
// ========================================================================
|
|
63
|
+
BaseLegacyBookAlert,
|
|
64
|
+
BaseLegacyBookTip,
|
|
65
|
+
BaseLegacySEQuote,
|
|
66
|
+
BaseLegacyPromptSE,
|
|
67
|
+
BaseLegacyPromptSEHeader,
|
|
68
|
+
BaseLegacyPromptSEContent,
|
|
69
|
+
BaseLegacyCustomBlockquote,
|
|
70
|
+
BaseLegacyYouTube,
|
|
71
|
+
BaseLegacyFigure,
|
|
72
|
+
// Update
|
|
73
|
+
];
|
|
74
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CalloutType } from "../../base";
|
|
2
|
+
import {
|
|
3
|
+
TiptapCalloutError,
|
|
4
|
+
TiptapCalloutInfo,
|
|
5
|
+
TiptapCalloutSuccess,
|
|
6
|
+
TiptapCalloutWarning,
|
|
7
|
+
} from "../../../icons/color";
|
|
8
|
+
import { ReactNode } from "react";
|
|
9
|
+
import { calloutIconComponents } from "../../../utils/custom-emoji";
|
|
10
|
+
|
|
11
|
+
const calloutIcons: Record<CalloutType, React.ReactNode> = {
|
|
12
|
+
info: <TiptapCalloutInfo />,
|
|
13
|
+
warning: <TiptapCalloutWarning />,
|
|
14
|
+
success: <TiptapCalloutSuccess />,
|
|
15
|
+
error: <TiptapCalloutError />,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const legacyTypeMap: Record<string, CalloutType> = {
|
|
19
|
+
question: "info",
|
|
20
|
+
idea: "warning",
|
|
21
|
+
info: "success",
|
|
22
|
+
error: "error",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
type: CalloutType;
|
|
27
|
+
bgColor?: string | null;
|
|
28
|
+
emoji?: string | null;
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
calloutType?: CalloutType;
|
|
31
|
+
iconWrapper?: (icon: ReactNode) => ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const Callout = ({ type, bgColor, emoji, children, iconWrapper, calloutType }: Props) => {
|
|
35
|
+
const isLegacySECallout = !!calloutType;
|
|
36
|
+
const finalType = isLegacySECallout ? legacyTypeMap[calloutType] : type;
|
|
37
|
+
|
|
38
|
+
const hasCustomColor = !!bgColor;
|
|
39
|
+
const className = hasCustomColor ? "callout-node" : `callout-node callout-${finalType}`;
|
|
40
|
+
const style = hasCustomColor ? { backgroundColor: bgColor } : undefined;
|
|
41
|
+
|
|
42
|
+
let icon: ReactNode;
|
|
43
|
+
if (emoji) {
|
|
44
|
+
if (emoji in calloutIconComponents) {
|
|
45
|
+
const IconComponent = calloutIconComponents[emoji as keyof typeof calloutIconComponents];
|
|
46
|
+
icon = <IconComponent />;
|
|
47
|
+
} else {
|
|
48
|
+
icon = emoji;
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
icon = calloutIcons[finalType];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const iconContent = iconWrapper ? iconWrapper(icon) : <div className="callout-icon">{icon}</div>;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
className={className}
|
|
59
|
+
data-callout-type={hasCustomColor ? undefined : type}
|
|
60
|
+
data-bg-color={bgColor || undefined}
|
|
61
|
+
data-emoji={emoji || undefined}
|
|
62
|
+
style={style}
|
|
63
|
+
>
|
|
64
|
+
{iconContent}
|
|
65
|
+
<div className="callout-content-wrapper">
|
|
66
|
+
<div className="callout-content">{children}</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ChangeEvent, ReactNode, useMemo } from "react";
|
|
2
|
+
import html from "highlight.js/lib/languages/xml";
|
|
3
|
+
import { common, createLowlight } from "lowlight";
|
|
4
|
+
|
|
5
|
+
import type { TiptapTranslations } from "../../../utils/get-translations";
|
|
6
|
+
import { CopyButton } from "./copy-button";
|
|
7
|
+
|
|
8
|
+
interface CodeBlockProps {
|
|
9
|
+
language: string;
|
|
10
|
+
isEditable: boolean;
|
|
11
|
+
onLanguageChange?: (language: string) => void;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
translations?: TiptapTranslations;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const CodeBlock = ({
|
|
17
|
+
language,
|
|
18
|
+
isEditable,
|
|
19
|
+
onLanguageChange,
|
|
20
|
+
children,
|
|
21
|
+
translations,
|
|
22
|
+
}: CodeBlockProps) => {
|
|
23
|
+
const languagesList = useMemo(() => {
|
|
24
|
+
const lowlight = createLowlight(common);
|
|
25
|
+
lowlight.register("html", html);
|
|
26
|
+
const list = lowlight.listLanguages();
|
|
27
|
+
return list.sort();
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
|
|
31
|
+
onLanguageChange?.(e.target.value);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="tiptap-code-block-wrapper">
|
|
36
|
+
<div className="tiptap-code-block-header">
|
|
37
|
+
{isEditable ? (
|
|
38
|
+
<select
|
|
39
|
+
className="tiptap-code-block-language-select"
|
|
40
|
+
value={language}
|
|
41
|
+
onChange={handleChange}
|
|
42
|
+
>
|
|
43
|
+
<option value="">{translations?.codeBlock.selectLanguage}</option>
|
|
44
|
+
{languagesList.map((lang) => (
|
|
45
|
+
<option
|
|
46
|
+
key={lang}
|
|
47
|
+
value={lang}
|
|
48
|
+
>
|
|
49
|
+
{lang}
|
|
50
|
+
</option>
|
|
51
|
+
))}
|
|
52
|
+
</select>
|
|
53
|
+
) : (
|
|
54
|
+
<span className="tiptap-code-block-language-text">
|
|
55
|
+
{language || translations?.codeBlock.selectLanguage}
|
|
56
|
+
</span>
|
|
57
|
+
)}
|
|
58
|
+
<CopyButton
|
|
59
|
+
iconSize={20}
|
|
60
|
+
color="white"
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
<pre className="tiptap-code-block">
|
|
64
|
+
<code
|
|
65
|
+
className="tiptap-code-block-content"
|
|
66
|
+
data-copy-content
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
</code>
|
|
70
|
+
</pre>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { TiptapCopy, TiptapCheck } from "../../../icons";
|
|
2
|
+
|
|
3
|
+
interface CopyButtonProps {
|
|
4
|
+
iconSize?: number;
|
|
5
|
+
color?: "white" | "black" | "prompt";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reusable copy button component for server-rendered content
|
|
10
|
+
* Works with a client-side interactivity provider that:
|
|
11
|
+
* - Handles click events via data-copy-action attribute
|
|
12
|
+
* - Copies content from nearest element marked with data-copy-content
|
|
13
|
+
* - Shows TiptapCopy icon by default
|
|
14
|
+
* - Switches to TiptapCheck icon for 1500ms after successful copy
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <div>
|
|
19
|
+
* <div data-copy-content>Text to copy</div>
|
|
20
|
+
* <CopyButton iconSize={16} />
|
|
21
|
+
* </div>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const CopyButton = ({ iconSize = 16, color = "black" }: CopyButtonProps) => {
|
|
25
|
+
return (
|
|
26
|
+
<button
|
|
27
|
+
className={`copy-button copy-icon-${color}`}
|
|
28
|
+
data-copy-action="copy"
|
|
29
|
+
type="button"
|
|
30
|
+
>
|
|
31
|
+
<span
|
|
32
|
+
className="copy-icon"
|
|
33
|
+
data-copy-icon="default"
|
|
34
|
+
>
|
|
35
|
+
<TiptapCopy
|
|
36
|
+
width={iconSize}
|
|
37
|
+
height={iconSize}
|
|
38
|
+
/>
|
|
39
|
+
</span>
|
|
40
|
+
<span
|
|
41
|
+
className="copy-icon-success"
|
|
42
|
+
data-copy-icon="success"
|
|
43
|
+
style={{ display: "none" }}
|
|
44
|
+
>
|
|
45
|
+
<TiptapCheck
|
|
46
|
+
width={iconSize}
|
|
47
|
+
height={iconSize}
|
|
48
|
+
/>
|
|
49
|
+
</span>
|
|
50
|
+
</button>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { CopyButton } from "./copy-button";
|
|
3
|
+
|
|
4
|
+
export const CopyableText = ({ children }: { children: ReactNode }) => {
|
|
5
|
+
return (
|
|
6
|
+
<span className="copyable">
|
|
7
|
+
<span data-copy-content>{children}</span>
|
|
8
|
+
<CopyButton iconSize={16} />
|
|
9
|
+
</span>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { TiptapQuote } from "../../../icons";
|
|
3
|
+
|
|
4
|
+
interface CustomQuoteProps {
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const CustomQuote = ({ children }: CustomQuoteProps) => (
|
|
9
|
+
<div data-custom-quote="">
|
|
10
|
+
<div className="custom-quote-divider" />
|
|
11
|
+
<div className="custom-quote-svg-wrapper">
|
|
12
|
+
<TiptapQuote />
|
|
13
|
+
</div>
|
|
14
|
+
{children}
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
interface CustomQuoteTextProps {
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
isEmpty?: boolean;
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const CustomQuoteText = ({ children, isEmpty, placeholder }: CustomQuoteTextProps) => (
|
|
25
|
+
<div
|
|
26
|
+
data-custom-quote-text
|
|
27
|
+
className={isEmpty ? "is-empty" : ""}
|
|
28
|
+
data-placeholder={placeholder}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
interface CustomQuoteAuthorProps {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
isEmpty?: boolean;
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const CustomQuoteAuthor = ({ children, isEmpty, placeholder }: CustomQuoteAuthorProps) => (
|
|
41
|
+
<div
|
|
42
|
+
data-custom-quote-author=""
|
|
43
|
+
className={isEmpty ? "is-empty" : ""}
|
|
44
|
+
data-placeholder={placeholder}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
interface CustomQuoteSignProps {
|
|
51
|
+
children: ReactNode;
|
|
52
|
+
isEmpty?: boolean;
|
|
53
|
+
placeholder?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const CustomQuoteSign = ({ children, isEmpty, placeholder }: CustomQuoteSignProps) => (
|
|
57
|
+
<div
|
|
58
|
+
data-custom-quote-sign=""
|
|
59
|
+
className={isEmpty ? "is-empty" : ""}
|
|
60
|
+
data-placeholder={placeholder}
|
|
61
|
+
>
|
|
62
|
+
{children}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { TiptapEdit } from "../../../icons";
|
|
4
|
+
import { resolveImageSrc } from "../../../utils/image-src-resolver";
|
|
5
|
+
|
|
6
|
+
interface FigureProps {
|
|
7
|
+
src?: string;
|
|
8
|
+
alt?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
caption?: string;
|
|
11
|
+
width?: number | null;
|
|
12
|
+
height?: number | null;
|
|
13
|
+
isEditable?: boolean;
|
|
14
|
+
onEditClick?: () => void;
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
fileId?: string | null;
|
|
17
|
+
getImageById?: (fileId: string) => string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Figure = ({
|
|
21
|
+
src,
|
|
22
|
+
alt,
|
|
23
|
+
title,
|
|
24
|
+
caption,
|
|
25
|
+
width,
|
|
26
|
+
height,
|
|
27
|
+
isEditable,
|
|
28
|
+
onEditClick,
|
|
29
|
+
children,
|
|
30
|
+
fileId,
|
|
31
|
+
getImageById,
|
|
32
|
+
}: FigureProps) => {
|
|
33
|
+
const resolvedSrc = resolveImageSrc({ fileId, src }, { getImageById });
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<figure>
|
|
37
|
+
{resolvedSrc && (
|
|
38
|
+
<div className="figure-image-container">
|
|
39
|
+
<img
|
|
40
|
+
src={resolvedSrc}
|
|
41
|
+
alt={alt || ""}
|
|
42
|
+
title={title}
|
|
43
|
+
width={width || undefined}
|
|
44
|
+
height={height || undefined}
|
|
45
|
+
/>
|
|
46
|
+
{isEditable && onEditClick && (
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
className="figure-edit-button"
|
|
50
|
+
onClick={onEditClick}
|
|
51
|
+
contentEditable={false}
|
|
52
|
+
aria-label="Редактировать изображение"
|
|
53
|
+
>
|
|
54
|
+
<TiptapEdit />
|
|
55
|
+
</button>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
{caption && <figcaption>{caption}</figcaption>}
|
|
60
|
+
{children}
|
|
61
|
+
</figure>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Callout } from "./callout";
|
|
2
|
+
export { CodeBlock } from "./code-block";
|
|
3
|
+
export { CopyableText } from "./copyable-text";
|
|
4
|
+
export { CustomQuote, CustomQuoteText, CustomQuoteAuthor, CustomQuoteSign } from "./custom-quote";
|
|
5
|
+
export { Figure } from "./figure";
|
|
6
|
+
export { PromptHeader, PromptContent } from "./prompt";
|
|
7
|
+
export { Video } from "./video";
|