@bayonai/rich-text-editor 0.1.2 → 1.0.0
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/BEHAVIOR.md +396 -0
- package/CHANGELOG.md +22 -0
- package/README.md +25 -6
- package/dist/core/blockTree.d.ts +14 -0
- package/dist/core/blockTree.js +126 -0
- package/dist/core/blockTypes.d.ts +6 -0
- package/dist/core/blockTypes.js +5 -0
- package/dist/core/exportImport.d.ts +59 -0
- package/dist/core/exportImport.js +51 -0
- package/dist/core/features.d.ts +59 -0
- package/dist/core/features.js +57 -0
- package/dist/core/imageBlockDiagnostics.d.ts +4 -0
- package/dist/core/imageBlockDiagnostics.js +19 -0
- package/dist/core/proFeatures.d.ts +60 -0
- package/dist/core/proFeatures.js +64 -0
- package/dist/{richText.d.ts → core/richText.d.ts} +2 -0
- package/dist/core/richText.js +566 -0
- package/dist/core/types.d.ts +78 -0
- package/dist/index.d.ts +14 -8
- package/dist/index.js +8 -5
- package/dist/react/editor/RichTextBody.d.ts +28 -0
- package/dist/react/editor/RichTextBody.js +131 -0
- package/dist/react/editor/RichTextEditor.d.ts +138 -0
- package/dist/react/editor/RichTextEditor.js +2925 -0
- package/dist/react/editor/RichTextRenderedBlock.d.ts +20 -0
- package/dist/react/editor/RichTextRenderedBlock.js +162 -0
- package/dist/react/editor/RichTextRenderer.d.ts +13 -0
- package/dist/react/editor/RichTextRenderer.js +16 -0
- package/dist/react/{RichTextTitleInput.d.ts → editor/RichTextTitleInput.d.ts} +11 -1
- package/dist/react/{RichTextTitleInput.js → editor/RichTextTitleInput.js} +17 -2
- package/dist/react/editor/blockActions.d.ts +48 -0
- package/dist/react/editor/blockActions.js +495 -0
- package/dist/react/editor/editorHistory.d.ts +55 -0
- package/dist/react/editor/editorHistory.js +111 -0
- package/dist/react/{editorNavigation.d.ts → editor/editorNavigation.d.ts} +2 -0
- package/dist/react/{editorNavigation.js → editor/editorNavigation.js} +16 -0
- package/dist/react/editor/editorOperations.d.ts +10 -0
- package/dist/react/editor/editorOperations.js +3 -0
- package/dist/react/editor/editorSelection.d.ts +3 -0
- package/dist/react/editor/editorSelection.js +215 -0
- package/dist/react/{editorShortcuts.d.ts → editor/editorShortcuts.d.ts} +10 -0
- package/dist/react/{editorShortcuts.js → editor/editorShortcuts.js} +17 -1
- package/dist/react/{RichTextIcons.d.ts → icons/RichTextIcons.d.ts} +3 -0
- package/dist/react/{RichTextIcons.js → icons/RichTextIcons.js} +9 -0
- package/dist/react/index.d.ts +12 -9
- package/dist/react/index.js +7 -6
- package/dist/react/{EditorSessionProvider.d.ts → session/EditorSessionProvider.d.ts} +2 -2
- package/dist/react/{EditorSessionProvider.js → session/EditorSessionProvider.js} +3 -3
- package/dist/react/{UnsavedChangesDialog.js → session/UnsavedChangesDialog.js} +1 -1
- package/dist/react/styles/RichTextStyles.js +1362 -0
- package/dist/react/{BlockActionTool.d.ts → tools/BlockActionTool.d.ts} +1 -1
- package/dist/react/{BlockActionTool.js → tools/BlockActionTool.js} +6 -2
- package/dist/react/tools/LinkCreationInput.d.ts +9 -0
- package/dist/react/tools/LinkCreationInput.js +38 -0
- package/dist/react/{SelectionFormatToolbar.d.ts → tools/SelectionFormatToolbar.d.ts} +3 -2
- package/dist/react/{SelectionFormatToolbar.js → tools/SelectionFormatToolbar.js} +3 -3
- package/dist/react/tools/SpecialBlockOption.d.ts +9 -0
- package/dist/react/tools/SpecialBlockOption.js +8 -0
- package/dist/react/tools/SpecialBlockTool.d.ts +91 -0
- package/dist/react/tools/SpecialBlockTool.js +125 -0
- package/dist/react/{TranscriptionControl.d.ts → tools/TranscriptionControl.d.ts} +9 -0
- package/dist/react/{TranscriptionControl.js → tools/TranscriptionControl.js} +70 -9
- package/dist/react/tools/blockActionToolState.d.ts +41 -0
- package/dist/react/tools/blockActionToolState.js +177 -0
- package/dist/react/tools/imageBlockDiagnostics.d.ts +2 -0
- package/dist/react/tools/imageBlockDiagnostics.js +12 -0
- package/dist/{session.d.ts → session/session.d.ts} +1 -1
- package/dist-cjs/core/blockTree.js +137 -0
- package/dist-cjs/core/blockTypes.js +9 -0
- package/dist-cjs/core/exportImport.js +56 -0
- package/dist-cjs/core/features.js +62 -0
- package/dist-cjs/core/proFeatures.js +70 -0
- package/dist-cjs/core/richText.js +578 -0
- package/dist-cjs/index.js +22 -6
- package/dist-cjs/react/editor/RichTextBody.js +134 -0
- package/dist-cjs/react/editor/RichTextEditor.js +2956 -0
- package/dist-cjs/react/editor/RichTextRenderedBlock.js +166 -0
- package/dist-cjs/react/editor/RichTextRenderer.js +20 -0
- package/dist-cjs/react/{RichTextTitleInput.js → editor/RichTextTitleInput.js} +18 -2
- package/dist-cjs/react/editor/blockActions.js +518 -0
- package/dist-cjs/react/editor/editorHistory.js +120 -0
- package/dist-cjs/react/{editorNavigation.js → editor/editorNavigation.js} +17 -0
- package/dist-cjs/react/editor/editorOperations.js +6 -0
- package/dist-cjs/react/editor/editorSelection.js +219 -0
- package/dist-cjs/react/{editorShortcuts.js → editor/editorShortcuts.js} +17 -1
- package/dist-cjs/react/{RichTextIcons.js → icons/RichTextIcons.js} +12 -0
- package/dist-cjs/react/index.js +9 -7
- package/dist-cjs/react/{EditorSessionProvider.js → session/EditorSessionProvider.js} +3 -3
- package/dist-cjs/react/{UnsavedChangesDialog.js → session/UnsavedChangesDialog.js} +1 -1
- package/dist-cjs/react/styles/RichTextStyles.js +1365 -0
- package/dist-cjs/react/{BlockActionTool.js → tools/BlockActionTool.js} +6 -2
- package/dist-cjs/react/tools/LinkCreationInput.js +41 -0
- package/dist-cjs/react/{SelectionFormatToolbar.js → tools/SelectionFormatToolbar.js} +3 -3
- package/dist-cjs/react/tools/SpecialBlockOption.js +11 -0
- package/dist-cjs/react/tools/SpecialBlockTool.js +129 -0
- package/dist-cjs/react/{TranscriptionControl.js → tools/TranscriptionControl.js} +71 -9
- package/dist-cjs/react/tools/blockActionToolState.js +186 -0
- package/package.json +3 -2
- package/dist/react/RichTextBody.d.ts +0 -18
- package/dist/react/RichTextBody.js +0 -66
- package/dist/react/RichTextEditor.d.ts +0 -45
- package/dist/react/RichTextEditor.js +0 -1096
- package/dist/react/RichTextRenderedBlock.d.ts +0 -4
- package/dist/react/RichTextRenderedBlock.js +0 -36
- package/dist/react/RichTextRenderer.d.ts +0 -4
- package/dist/react/RichTextRenderer.js +0 -8
- package/dist/react/RichTextStyles.js +0 -719
- package/dist/react/SpecialBlockOption.d.ts +0 -7
- package/dist/react/SpecialBlockOption.js +0 -7
- package/dist/react/SpecialBlockTool.d.ts +0 -42
- package/dist/react/SpecialBlockTool.js +0 -50
- package/dist/react/blockActionToolState.d.ts +0 -18
- package/dist/react/blockActionToolState.js +0 -53
- package/dist/react/blockActions.d.ts +0 -8
- package/dist/react/blockActions.js +0 -111
- package/dist/richText.js +0 -297
- package/dist/types.d.ts +0 -34
- package/dist-cjs/react/RichTextBody.js +0 -69
- package/dist-cjs/react/RichTextEditor.js +0 -1108
- package/dist-cjs/react/RichTextRenderedBlock.js +0 -39
- package/dist-cjs/react/RichTextRenderer.js +0 -11
- package/dist-cjs/react/RichTextStyles.js +0 -722
- package/dist-cjs/react/SpecialBlockOption.js +0 -10
- package/dist-cjs/react/SpecialBlockTool.js +0 -54
- package/dist-cjs/react/blockActionToolState.js +0 -58
- package/dist-cjs/react/blockActions.js +0 -119
- package/dist-cjs/richText.js +0 -307
- /package/dist/{types.js → core/types.js} +0 -0
- /package/dist/{writingStats.d.ts → core/writingStats.d.ts} +0 -0
- /package/dist/{writingStats.js → core/writingStats.js} +0 -0
- /package/dist/react/{RichTextDocumentSurface.d.ts → editor/RichTextDocumentSurface.d.ts} +0 -0
- /package/dist/react/{RichTextDocumentSurface.js → editor/RichTextDocumentSurface.js} +0 -0
- /package/dist/react/{UnsavedChangesDialog.d.ts → session/UnsavedChangesDialog.d.ts} +0 -0
- /package/dist/react/{RichTextStyles.d.ts → styles/RichTextStyles.d.ts} +0 -0
- /package/dist/react/{richTextBlockStyles.d.ts → styles/richTextBlockStyles.d.ts} +0 -0
- /package/dist/react/{richTextBlockStyles.js → styles/richTextBlockStyles.js} +0 -0
- /package/dist/react/{specialBlockStyles.d.ts → styles/specialBlockStyles.d.ts} +0 -0
- /package/dist/react/{specialBlockStyles.js → styles/specialBlockStyles.js} +0 -0
- /package/dist/{saveControl.d.ts → session/saveControl.d.ts} +0 -0
- /package/dist/{saveControl.js → session/saveControl.js} +0 -0
- /package/dist/{session.js → session/session.js} +0 -0
- /package/dist/{sessionRegistry.d.ts → session/sessionRegistry.d.ts} +0 -0
- /package/dist/{sessionRegistry.js → session/sessionRegistry.js} +0 -0
- /package/dist-cjs/{types.js → core/types.js} +0 -0
- /package/dist-cjs/{writingStats.js → core/writingStats.js} +0 -0
- /package/dist-cjs/react/{RichTextDocumentSurface.js → editor/RichTextDocumentSurface.js} +0 -0
- /package/dist-cjs/react/{richTextBlockStyles.js → styles/richTextBlockStyles.js} +0 -0
- /package/dist-cjs/react/{specialBlockStyles.js → styles/specialBlockStyles.js} +0 -0
- /package/dist-cjs/{saveControl.js → session/saveControl.js} +0 -0
- /package/dist-cjs/{session.js → session/session.js} +0 -0
- /package/dist-cjs/{sessionRegistry.js → session/sessionRegistry.js} +0 -0
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
"use client";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.specialBlockActions = void 0;
|
|
5
|
-
exports.SpecialBlockTool = SpecialBlockTool;
|
|
6
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
-
const RichTextIcons_1 = require("./RichTextIcons");
|
|
8
|
-
const SpecialBlockOption_1 = require("./SpecialBlockOption");
|
|
9
|
-
exports.specialBlockActions = [
|
|
10
|
-
{
|
|
11
|
-
html: '<figure data-placeholder="image"><p>Image placeholder</p></figure>',
|
|
12
|
-
icon: RichTextIcons_1.ImageIcon,
|
|
13
|
-
label: "Image",
|
|
14
|
-
selectDefault: true,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
html: '<blockquote data-placeholder="quote"></blockquote>',
|
|
18
|
-
icon: RichTextIcons_1.QuoteIcon,
|
|
19
|
-
label: "Quote",
|
|
20
|
-
selectDefault: true,
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
html: "<h2>Title</h2>",
|
|
24
|
-
icon: RichTextIcons_1.TitleIcon,
|
|
25
|
-
label: "Title",
|
|
26
|
-
selectDefault: true,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
html: "<pre><code>Code block</code></pre>",
|
|
30
|
-
icon: RichTextIcons_1.CodeIcon,
|
|
31
|
-
label: "Code block",
|
|
32
|
-
selectDefault: true,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
html: "<pre><code>Embedded HTML</code></pre>",
|
|
36
|
-
icon: RichTextIcons_1.DataObjectIcon,
|
|
37
|
-
label: "Embedded HTML",
|
|
38
|
-
selectDefault: true,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
html: "<hr>",
|
|
42
|
-
icon: RichTextIcons_1.DividerIcon,
|
|
43
|
-
label: "Divider",
|
|
44
|
-
selectDefault: false,
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
// Renders the floating insertion control for image, quote, title, code, and divider blocks.
|
|
48
|
-
function SpecialBlockTool({ onHoverChange, onInsert, toolHover, top, visible, }) {
|
|
49
|
-
return ((0, jsx_runtime_1.jsxs)("div", { "aria-hidden": !visible, className: `bayon-rte-special-tool${visible ? " bayon-rte-special-tool--visible" : ""}`, onMouseEnter: () => onHoverChange(true), onMouseLeave: () => onHoverChange(false), style: { "--bayon-rte-special-tool-top": `${top}px` }, children: [(0, jsx_runtime_1.jsx)("button", { "aria-label": toolHover ? "Close special blocks" : "Add special block", className: "bayon-rte-icon-button bayon-rte-special-button bayon-rte-special-toggle", onClick: () => onHoverChange(!toolHover), style: {
|
|
50
|
-
transform: visible
|
|
51
|
-
? "scale(1) rotate(0deg)"
|
|
52
|
-
: "scale(0.84) rotate(-12deg)",
|
|
53
|
-
}, title: toolHover ? "Close special blocks" : "Add special block", type: "button", children: toolHover ? (0, jsx_runtime_1.jsx)(RichTextIcons_1.CloseIcon, { size: 20 }) : (0, jsx_runtime_1.jsx)(RichTextIcons_1.AddIcon, { size: 20 }) }), toolHover ? ((0, jsx_runtime_1.jsx)("div", { className: "bayon-rte-special-tool__actions", children: exports.specialBlockActions.map((action) => ((0, jsx_runtime_1.jsx)(SpecialBlockOption_1.SpecialBlockOption, { action: action, onInsert: onInsert }, action.label))) })) : null] }));
|
|
54
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getVisibleBlockActionToolPlacements = getVisibleBlockActionToolPlacements;
|
|
4
|
-
exports.getPointerDropTarget = getPointerDropTarget;
|
|
5
|
-
exports.getUniqueBlockActionToolPlacements = getUniqueBlockActionToolPlacements;
|
|
6
|
-
function getVisibleBlockActionToolPlacements(placements, state) {
|
|
7
|
-
const uniquePlacements = getUniqueBlockActionToolPlacements(placements);
|
|
8
|
-
const visibleBlockId = state.activeBlockId ??
|
|
9
|
-
state.draggedBlockId ??
|
|
10
|
-
state.focusedBlockId ??
|
|
11
|
-
state.hoveredBlockId;
|
|
12
|
-
return visibleBlockId
|
|
13
|
-
? uniquePlacements.filter((placement) => placement.blockId === visibleBlockId)
|
|
14
|
-
: [];
|
|
15
|
-
}
|
|
16
|
-
function getPointerDropTarget(placements, draggedBlockId, pointerClientY) {
|
|
17
|
-
const targetPlacements = getUniqueBlockActionToolPlacements(placements).filter((placement) => {
|
|
18
|
-
return placement.blockId !== draggedBlockId;
|
|
19
|
-
});
|
|
20
|
-
const targetPlacement = targetPlacements.find((placement) => {
|
|
21
|
-
return (pointerClientY >= placement.top && pointerClientY <= placement.bottom);
|
|
22
|
-
}) ??
|
|
23
|
-
targetPlacements.reduce((closestPlacement, placement) => {
|
|
24
|
-
if (!closestPlacement) {
|
|
25
|
-
return placement;
|
|
26
|
-
}
|
|
27
|
-
return getDistanceToPlacement(placement, pointerClientY) <
|
|
28
|
-
getDistanceToPlacement(closestPlacement, pointerClientY)
|
|
29
|
-
? placement
|
|
30
|
-
: closestPlacement;
|
|
31
|
-
}, null);
|
|
32
|
-
if (!targetPlacement) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
placement: pointerClientY >
|
|
37
|
-
targetPlacement.top + (targetPlacement.bottom - targetPlacement.top) / 2
|
|
38
|
-
? "after"
|
|
39
|
-
: "before",
|
|
40
|
-
targetBlockId: targetPlacement.blockId,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function getUniqueBlockActionToolPlacements(placements) {
|
|
44
|
-
const seenBlockIds = new Set();
|
|
45
|
-
return placements.filter((placement) => {
|
|
46
|
-
if (seenBlockIds.has(placement.blockId)) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
seenBlockIds.add(placement.blockId);
|
|
50
|
-
return true;
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
function getDistanceToPlacement(placement, pointerClientY) {
|
|
54
|
-
if (pointerClientY >= placement.top && pointerClientY <= placement.bottom) {
|
|
55
|
-
return 0;
|
|
56
|
-
}
|
|
57
|
-
return Math.min(Math.abs(pointerClientY - placement.top), Math.abs(pointerClientY - placement.bottom));
|
|
58
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isBlockActionTarget = isBlockActionTarget;
|
|
4
|
-
exports.reorderBlock = reorderBlock;
|
|
5
|
-
exports.deleteBlockById = deleteBlockById;
|
|
6
|
-
exports.convertCheckboxBlockToParagraph = convertCheckboxBlockToParagraph;
|
|
7
|
-
exports.blockToClipboardText = blockToClipboardText;
|
|
8
|
-
exports.blockActionToClipboardText = blockActionToClipboardText;
|
|
9
|
-
const richText_1 = require("../richText");
|
|
10
|
-
function isBlockActionTarget(block) {
|
|
11
|
-
if (block.type === "paragraph") {
|
|
12
|
-
return blockToClipboardText(block).trim().length > 0;
|
|
13
|
-
}
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
function reorderBlock(blocks, draggedBlockId, targetBlockId, placement = "before") {
|
|
17
|
-
if (draggedBlockId === targetBlockId) {
|
|
18
|
-
return (0, richText_1.sanitizeRichTextBlocks)(blocks);
|
|
19
|
-
}
|
|
20
|
-
const sanitizedBlocks = (0, richText_1.sanitizeRichTextBlocks)(blocks);
|
|
21
|
-
const draggedRange = getSingleBlockRange(sanitizedBlocks, draggedBlockId);
|
|
22
|
-
const targetRange = getSingleBlockRange(sanitizedBlocks, targetBlockId);
|
|
23
|
-
if (!draggedRange ||
|
|
24
|
-
!targetRange ||
|
|
25
|
-
rangesOverlap(draggedRange, targetRange)) {
|
|
26
|
-
return sanitizedBlocks;
|
|
27
|
-
}
|
|
28
|
-
const draggedBlocks = sanitizedBlocks.slice(draggedRange.start, draggedRange.end);
|
|
29
|
-
const withoutDraggedBlocks = [
|
|
30
|
-
...sanitizedBlocks.slice(0, draggedRange.start),
|
|
31
|
-
...sanitizedBlocks.slice(draggedRange.end),
|
|
32
|
-
];
|
|
33
|
-
const remainingTargetRange = getSingleBlockRange(withoutDraggedBlocks, targetBlockId);
|
|
34
|
-
if (!remainingTargetRange) {
|
|
35
|
-
return sanitizedBlocks;
|
|
36
|
-
}
|
|
37
|
-
const insertionIndex = placement === "after"
|
|
38
|
-
? remainingTargetRange.end
|
|
39
|
-
: remainingTargetRange.start;
|
|
40
|
-
return [
|
|
41
|
-
...withoutDraggedBlocks.slice(0, insertionIndex),
|
|
42
|
-
...draggedBlocks,
|
|
43
|
-
...withoutDraggedBlocks.slice(insertionIndex),
|
|
44
|
-
];
|
|
45
|
-
}
|
|
46
|
-
function deleteBlockById(blocks, blockId) {
|
|
47
|
-
const sanitizedBlocks = (0, richText_1.sanitizeRichTextBlocks)(blocks);
|
|
48
|
-
const range = getSingleBlockRange(sanitizedBlocks, blockId);
|
|
49
|
-
if (!range) {
|
|
50
|
-
return sanitizedBlocks;
|
|
51
|
-
}
|
|
52
|
-
const remainingBlocks = [
|
|
53
|
-
...sanitizedBlocks.slice(0, range.start),
|
|
54
|
-
...sanitizedBlocks.slice(range.end),
|
|
55
|
-
];
|
|
56
|
-
return remainingBlocks.length > 0
|
|
57
|
-
? remainingBlocks
|
|
58
|
-
: [{ id: "block-empty", markdown: "", type: "paragraph" }];
|
|
59
|
-
}
|
|
60
|
-
function convertCheckboxBlockToParagraph(blocks, blockId) {
|
|
61
|
-
return (0, richText_1.sanitizeRichTextBlocks)(blocks).map((block) => {
|
|
62
|
-
if (block.id !== blockId || block.type !== "checkbox") {
|
|
63
|
-
return block;
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
id: block.id,
|
|
67
|
-
markdown: block.markdown,
|
|
68
|
-
type: "paragraph",
|
|
69
|
-
};
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
function blockToClipboardText(block) {
|
|
73
|
-
if (block.type === "checkbox") {
|
|
74
|
-
const text = (0, richText_1.richTextBlocksToPlainText)([block]);
|
|
75
|
-
return text ? `[${block.checked ? "x" : " "}] ${text}` : "";
|
|
76
|
-
}
|
|
77
|
-
return (0, richText_1.richTextBlocksToPlainText)([block]);
|
|
78
|
-
}
|
|
79
|
-
function blockActionToClipboardText(blocks, blockId) {
|
|
80
|
-
const sanitizedBlocks = (0, richText_1.sanitizeRichTextBlocks)(blocks);
|
|
81
|
-
const range = getActionBlockRange(sanitizedBlocks, blockId);
|
|
82
|
-
if (!range) {
|
|
83
|
-
return "";
|
|
84
|
-
}
|
|
85
|
-
return sanitizedBlocks
|
|
86
|
-
.slice(range.start, range.end)
|
|
87
|
-
.map(blockToClipboardText)
|
|
88
|
-
.filter((text) => text.trim())
|
|
89
|
-
.join("\n");
|
|
90
|
-
}
|
|
91
|
-
function getSingleBlockRange(blocks, blockId) {
|
|
92
|
-
const index = blocks.findIndex((block) => {
|
|
93
|
-
return block.id === blockId;
|
|
94
|
-
});
|
|
95
|
-
return index === -1 ? null : { end: index + 1, start: index };
|
|
96
|
-
}
|
|
97
|
-
function getActionBlockRange(blocks, blockId) {
|
|
98
|
-
const index = blocks.findIndex((block) => {
|
|
99
|
-
return block.id === blockId;
|
|
100
|
-
});
|
|
101
|
-
if (index === -1) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
if (blocks[index]?.type !== "checkbox") {
|
|
105
|
-
return { end: index + 1, start: index };
|
|
106
|
-
}
|
|
107
|
-
let start = index;
|
|
108
|
-
let end = index + 1;
|
|
109
|
-
while (start > 0 && blocks[start - 1]?.type === "checkbox") {
|
|
110
|
-
start -= 1;
|
|
111
|
-
}
|
|
112
|
-
while (end < blocks.length && blocks[end]?.type === "checkbox") {
|
|
113
|
-
end += 1;
|
|
114
|
-
}
|
|
115
|
-
return { end, start };
|
|
116
|
-
}
|
|
117
|
-
function rangesOverlap(first, second) {
|
|
118
|
-
return first.start < second.end && second.start < first.end;
|
|
119
|
-
}
|
package/dist-cjs/richText.js
DELETED
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createEmptyRichTextBlocks = createEmptyRichTextBlocks;
|
|
4
|
-
exports.createRichTextBlockId = createRichTextBlockId;
|
|
5
|
-
exports.sanitizeRichTextBlocks = sanitizeRichTextBlocks;
|
|
6
|
-
exports.isRichTextBlocksEmpty = isRichTextBlocksEmpty;
|
|
7
|
-
exports.richTextBlocksToPlainText = richTextBlocksToPlainText;
|
|
8
|
-
exports.editorHtmlToMarkdown = editorHtmlToMarkdown;
|
|
9
|
-
exports.markdownToEditorHtml = markdownToEditorHtml;
|
|
10
|
-
exports.sanitizeRichTextHtml = sanitizeRichTextHtml;
|
|
11
|
-
const allowedInlineTags = new Set(["a", "br", "code", "em", "strong"]);
|
|
12
|
-
const textBlockTypes = new Set(["paragraph", "heading", "quote", "checkbox"]);
|
|
13
|
-
function createEmptyRichTextBlocks() {
|
|
14
|
-
return [{ id: createRichTextBlockId(), type: "paragraph", markdown: "" }];
|
|
15
|
-
}
|
|
16
|
-
function createRichTextBlockId() {
|
|
17
|
-
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
|
|
18
|
-
return crypto.randomUUID();
|
|
19
|
-
}
|
|
20
|
-
return `block-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Sanitizes an array of RichTextBlock objects by:
|
|
24
|
-
* - Filtering out invalid blocks
|
|
25
|
-
* - Ensuring each block has a unique ID
|
|
26
|
-
* - Returning an empty array if no valid blocks remain
|
|
27
|
-
*/
|
|
28
|
-
function sanitizeRichTextBlocks(value) {
|
|
29
|
-
const blocks = Array.isArray(value) ? value : [];
|
|
30
|
-
const seenBlockIds = new Set();
|
|
31
|
-
const sanitized = blocks
|
|
32
|
-
.flatMap((block) => sanitizeRichTextBlock(block))
|
|
33
|
-
.map((block) => {
|
|
34
|
-
if (!seenBlockIds.has(block.id)) {
|
|
35
|
-
seenBlockIds.add(block.id);
|
|
36
|
-
return block;
|
|
37
|
-
}
|
|
38
|
-
const blockWithUniqueId = {
|
|
39
|
-
...block,
|
|
40
|
-
id: createUniqueRichTextBlockId(seenBlockIds),
|
|
41
|
-
};
|
|
42
|
-
seenBlockIds.add(blockWithUniqueId.id);
|
|
43
|
-
return blockWithUniqueId;
|
|
44
|
-
});
|
|
45
|
-
return sanitized.length > 0 ? sanitized : createEmptyRichTextBlocks();
|
|
46
|
-
}
|
|
47
|
-
function isRichTextBlocksEmpty(blocks) {
|
|
48
|
-
return sanitizeRichTextBlocks(blocks).every((block) => {
|
|
49
|
-
if (block.type === "divider") {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
if (block.type === "checkbox") {
|
|
53
|
-
return markdownToPlainText(block.markdown).trim() === "";
|
|
54
|
-
}
|
|
55
|
-
if (block.type === "image") {
|
|
56
|
-
return !block.assetId && !block.alt?.trim();
|
|
57
|
-
}
|
|
58
|
-
const text = block.type === "code" ? block.text : block.markdown;
|
|
59
|
-
return markdownToPlainText(text).trim() === "";
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
function richTextBlocksToPlainText(blocks) {
|
|
63
|
-
return sanitizeRichTextBlocks(blocks)
|
|
64
|
-
.map((block) => {
|
|
65
|
-
if (block.type === "divider") {
|
|
66
|
-
return "";
|
|
67
|
-
}
|
|
68
|
-
if (block.type === "checkbox") {
|
|
69
|
-
return markdownToPlainText(block.markdown);
|
|
70
|
-
}
|
|
71
|
-
if (block.type === "image") {
|
|
72
|
-
return block.alt ?? "";
|
|
73
|
-
}
|
|
74
|
-
return block.type === "code"
|
|
75
|
-
? block.text
|
|
76
|
-
: markdownToPlainText(block.markdown);
|
|
77
|
-
})
|
|
78
|
-
.join(" ")
|
|
79
|
-
.replace(/\s+/g, " ")
|
|
80
|
-
.trim();
|
|
81
|
-
}
|
|
82
|
-
function editorHtmlToMarkdown(html) {
|
|
83
|
-
const sanitized = normalizeTypography(decodeHtmlText(sanitizeRichTextHtml(html)
|
|
84
|
-
.replace(/<br>/g, " \n")
|
|
85
|
-
.replace(/<strong>([\s\S]*?)<\/strong>/g, (_, value) => {
|
|
86
|
-
return `**${decodeHtmlText(stripTags(value))}**`;
|
|
87
|
-
})
|
|
88
|
-
.replace(/<em>([\s\S]*?)<\/em>/g, (_, value) => {
|
|
89
|
-
return `_${decodeHtmlText(stripTags(value))}_`;
|
|
90
|
-
})
|
|
91
|
-
.replace(/<code>([\s\S]*?)<\/code>/g, (_, value) => {
|
|
92
|
-
return `\`${decodeHtmlText(stripTags(value)).replace(/`/g, "\\`")}\``;
|
|
93
|
-
})
|
|
94
|
-
.replace(/<a href="([^"]*)">([\s\S]*?)<\/a>/g, (_, href, value) => {
|
|
95
|
-
return `[${decodeHtmlText(stripTags(value))}](${decodeHtmlText(href)})`;
|
|
96
|
-
})
|
|
97
|
-
.replace(/<a>([\s\S]*?)<\/a>/g, (_, value) => {
|
|
98
|
-
return decodeHtmlText(stripTags(value));
|
|
99
|
-
})
|
|
100
|
-
.replace(/<[^>]+>/g, "")))
|
|
101
|
-
.replace(/\u00a0/g, " ")
|
|
102
|
-
.trim();
|
|
103
|
-
return sanitized;
|
|
104
|
-
}
|
|
105
|
-
function markdownToEditorHtml(markdown) {
|
|
106
|
-
const tokens = [];
|
|
107
|
-
let html = escapeHtmlText(normalizeTypography(markdown));
|
|
108
|
-
html = html.replace(/`([^`\n]+)`/g, (_, value) => {
|
|
109
|
-
const token = pushToken(tokens, `<code>${value}</code>`);
|
|
110
|
-
return token;
|
|
111
|
-
});
|
|
112
|
-
html = html.replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g, (_, label, href) => {
|
|
113
|
-
const safeHref = getSafeUrl(decodeHtmlText(href));
|
|
114
|
-
return safeHref ? `<a href="${safeHref}">${label}</a>` : label;
|
|
115
|
-
});
|
|
116
|
-
html = html
|
|
117
|
-
.replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>")
|
|
118
|
-
.replace(/_([^_\n]+)_/g, "<em>$1</em>")
|
|
119
|
-
.replace(/ {2}\n/g, "<br>")
|
|
120
|
-
.replace(/\n/g, "<br>");
|
|
121
|
-
tokens.forEach((value, index) => {
|
|
122
|
-
html = html.replace(tokenPlaceholder(index), value);
|
|
123
|
-
});
|
|
124
|
-
return html;
|
|
125
|
-
}
|
|
126
|
-
function sanitizeRichTextHtml(html) {
|
|
127
|
-
return html
|
|
128
|
-
.replace(/<!--[\s\S]*?-->/g, "")
|
|
129
|
-
.replace(/<script[\s\S]*?<\/script>/gi, "")
|
|
130
|
-
.replace(/<style[\s\S]*?<\/style>/gi, "")
|
|
131
|
-
.replace(/<b(\s[^>]*)?>/gi, "<strong>")
|
|
132
|
-
.replace(/<\/b>/gi, "</strong>")
|
|
133
|
-
.replace(/<i(\s[^>]*)?>/gi, "<em>")
|
|
134
|
-
.replace(/<\/i>/gi, "</em>")
|
|
135
|
-
.replace(/<\/?([a-z0-9]+)([^>]*)>/gi, (match, rawTag, rawAttrs) => {
|
|
136
|
-
const tag = String(rawTag).toLowerCase();
|
|
137
|
-
const closing = match.startsWith("</");
|
|
138
|
-
if (!allowedInlineTags.has(tag)) {
|
|
139
|
-
return "";
|
|
140
|
-
}
|
|
141
|
-
if (closing) {
|
|
142
|
-
return tag === "br" ? "" : `</${tag}>`;
|
|
143
|
-
}
|
|
144
|
-
if (tag === "a") {
|
|
145
|
-
const href = getSafeAttribute(rawAttrs, "href");
|
|
146
|
-
return href ? `<a href="${href}">` : "<a>";
|
|
147
|
-
}
|
|
148
|
-
return tag === "br" ? "<br>" : `<${tag}>`;
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
function sanitizeRichTextBlock(block) {
|
|
152
|
-
if (!isRecord(block)) {
|
|
153
|
-
return [];
|
|
154
|
-
}
|
|
155
|
-
const id = readBlockId(block);
|
|
156
|
-
const type = block.type;
|
|
157
|
-
if (type === "code") {
|
|
158
|
-
return [{ id, type, text: String(block.text ?? "") }];
|
|
159
|
-
}
|
|
160
|
-
if (type === "divider") {
|
|
161
|
-
return [{ id, type }];
|
|
162
|
-
}
|
|
163
|
-
if (type === "image") {
|
|
164
|
-
return [
|
|
165
|
-
{
|
|
166
|
-
id,
|
|
167
|
-
type,
|
|
168
|
-
...(typeof block.assetId === "string" && block.assetId.trim()
|
|
169
|
-
? { assetId: block.assetId.trim() }
|
|
170
|
-
: {}),
|
|
171
|
-
...(typeof block.alt === "string" && block.alt.trim()
|
|
172
|
-
? { alt: markdownToPlainText(editorHtmlToMarkdown(block.alt)) }
|
|
173
|
-
: {}),
|
|
174
|
-
},
|
|
175
|
-
];
|
|
176
|
-
}
|
|
177
|
-
if (typeof type === "string" && textBlockTypes.has(type)) {
|
|
178
|
-
const markdown = typeof block.markdown === "string"
|
|
179
|
-
? block.markdown
|
|
180
|
-
: typeof block.text === "string"
|
|
181
|
-
? block.text
|
|
182
|
-
: typeof block.html === "string"
|
|
183
|
-
? editorHtmlToMarkdown(block.html)
|
|
184
|
-
: "";
|
|
185
|
-
if (type === "checkbox") {
|
|
186
|
-
return [
|
|
187
|
-
{
|
|
188
|
-
id,
|
|
189
|
-
type,
|
|
190
|
-
checked: Boolean(block.checked),
|
|
191
|
-
markdown: sanitizeMarkdown(markdown),
|
|
192
|
-
},
|
|
193
|
-
];
|
|
194
|
-
}
|
|
195
|
-
return [
|
|
196
|
-
{
|
|
197
|
-
id,
|
|
198
|
-
type: type,
|
|
199
|
-
markdown: sanitizeMarkdown(markdown),
|
|
200
|
-
},
|
|
201
|
-
];
|
|
202
|
-
}
|
|
203
|
-
return [];
|
|
204
|
-
}
|
|
205
|
-
function readBlockId(block) {
|
|
206
|
-
return typeof block.id === "string" && block.id.trim()
|
|
207
|
-
? block.id.trim()
|
|
208
|
-
: createRichTextBlockId();
|
|
209
|
-
}
|
|
210
|
-
function createUniqueRichTextBlockId(existingIds) {
|
|
211
|
-
let id = createRichTextBlockId();
|
|
212
|
-
while (existingIds.has(id)) {
|
|
213
|
-
id = createRichTextBlockId();
|
|
214
|
-
}
|
|
215
|
-
return id;
|
|
216
|
-
}
|
|
217
|
-
function markdownToPlainText(value) {
|
|
218
|
-
return value
|
|
219
|
-
.replace(/`([^`]*)`/g, "$1")
|
|
220
|
-
.replace(/\*\*([^*]*)\*\*/g, "$1")
|
|
221
|
-
.replace(/_([^_]*)_/g, "$1")
|
|
222
|
-
.replace(/\[([^\]]*)\]\([^)]+\)/g, "$1")
|
|
223
|
-
.replace(/[#>*`_[\]()]/g, " ")
|
|
224
|
-
.replace(/\s+/g, " ")
|
|
225
|
-
.trim();
|
|
226
|
-
}
|
|
227
|
-
function sanitizeMarkdown(value) {
|
|
228
|
-
return normalizeTypography(value)
|
|
229
|
-
.replace(/<!--[\s\S]*?-->/g, "")
|
|
230
|
-
.trim();
|
|
231
|
-
}
|
|
232
|
-
function normalizeTypography(value) {
|
|
233
|
-
const tokens = [];
|
|
234
|
-
let normalized = value.replace(/`([^`\n]+)`/g, (match) => {
|
|
235
|
-
return pushToken(tokens, match);
|
|
236
|
-
});
|
|
237
|
-
normalized = normalized.replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g, (_match, label, href) => {
|
|
238
|
-
return `[${label}](${pushToken(tokens, href)})`;
|
|
239
|
-
});
|
|
240
|
-
normalized = normalized
|
|
241
|
-
.replace(/––/g, "—")
|
|
242
|
-
.replace(/–-/g, "—")
|
|
243
|
-
.replace(/---/g, "—")
|
|
244
|
-
.replace(/--/g, "–");
|
|
245
|
-
tokens.forEach((token, index) => {
|
|
246
|
-
normalized = normalized.replace(tokenPlaceholder(index), token);
|
|
247
|
-
});
|
|
248
|
-
return normalized;
|
|
249
|
-
}
|
|
250
|
-
function stripTags(value) {
|
|
251
|
-
return value.replace(/<[^>]+>/g, "");
|
|
252
|
-
}
|
|
253
|
-
function getSafeAttribute(attrs, name) {
|
|
254
|
-
const value = getPlainAttribute(attrs, name);
|
|
255
|
-
const safeValue = getSafeUrl(value);
|
|
256
|
-
if (!safeValue) {
|
|
257
|
-
return "";
|
|
258
|
-
}
|
|
259
|
-
return escapeAttribute(safeValue);
|
|
260
|
-
}
|
|
261
|
-
function getSafeUrl(value) {
|
|
262
|
-
const trimmed = value.trim();
|
|
263
|
-
if (!trimmed || /^javascript:/i.test(trimmed)) {
|
|
264
|
-
return "";
|
|
265
|
-
}
|
|
266
|
-
return trimmed;
|
|
267
|
-
}
|
|
268
|
-
function getPlainAttribute(attrs, name) {
|
|
269
|
-
const pattern = new RegExp(`${name}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`, "i");
|
|
270
|
-
const match = attrs.match(pattern);
|
|
271
|
-
return match?.[2] ?? match?.[3] ?? match?.[4] ?? "";
|
|
272
|
-
}
|
|
273
|
-
function escapeAttribute(value) {
|
|
274
|
-
return value
|
|
275
|
-
.replace(/&/g, "&")
|
|
276
|
-
.replace(/</g, "<")
|
|
277
|
-
.replace(/>/g, ">")
|
|
278
|
-
.replace(/"/g, """);
|
|
279
|
-
}
|
|
280
|
-
function escapeHtmlText(value) {
|
|
281
|
-
return value
|
|
282
|
-
.replace(/&/g, "&")
|
|
283
|
-
.replace(/</g, "<")
|
|
284
|
-
.replace(/>/g, ">");
|
|
285
|
-
}
|
|
286
|
-
function decodeHtmlText(value) {
|
|
287
|
-
return value
|
|
288
|
-
.replace(/ /g, " ")
|
|
289
|
-
.replace(/ /g, " ")
|
|
290
|
-
.replace(/ /gi, " ")
|
|
291
|
-
.replace(/&/g, "&")
|
|
292
|
-
.replace(/</g, "<")
|
|
293
|
-
.replace(/>/g, ">")
|
|
294
|
-
.replace(/"/g, '"')
|
|
295
|
-
.replace(/'/g, "'")
|
|
296
|
-
.replace(/'/g, "'");
|
|
297
|
-
}
|
|
298
|
-
function pushToken(tokens, value) {
|
|
299
|
-
const index = tokens.push(value) - 1;
|
|
300
|
-
return tokenPlaceholder(index);
|
|
301
|
-
}
|
|
302
|
-
function tokenPlaceholder(index) {
|
|
303
|
-
return `@@RICHTEXTTOKEN${index}@@`;
|
|
304
|
-
}
|
|
305
|
-
function isRecord(value) {
|
|
306
|
-
return typeof value === "object" && value !== null;
|
|
307
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|