@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.
Files changed (151) hide show
  1. package/BEHAVIOR.md +396 -0
  2. package/CHANGELOG.md +22 -0
  3. package/README.md +25 -6
  4. package/dist/core/blockTree.d.ts +14 -0
  5. package/dist/core/blockTree.js +126 -0
  6. package/dist/core/blockTypes.d.ts +6 -0
  7. package/dist/core/blockTypes.js +5 -0
  8. package/dist/core/exportImport.d.ts +59 -0
  9. package/dist/core/exportImport.js +51 -0
  10. package/dist/core/features.d.ts +59 -0
  11. package/dist/core/features.js +57 -0
  12. package/dist/core/imageBlockDiagnostics.d.ts +4 -0
  13. package/dist/core/imageBlockDiagnostics.js +19 -0
  14. package/dist/core/proFeatures.d.ts +60 -0
  15. package/dist/core/proFeatures.js +64 -0
  16. package/dist/{richText.d.ts → core/richText.d.ts} +2 -0
  17. package/dist/core/richText.js +566 -0
  18. package/dist/core/types.d.ts +78 -0
  19. package/dist/index.d.ts +14 -8
  20. package/dist/index.js +8 -5
  21. package/dist/react/editor/RichTextBody.d.ts +28 -0
  22. package/dist/react/editor/RichTextBody.js +131 -0
  23. package/dist/react/editor/RichTextEditor.d.ts +138 -0
  24. package/dist/react/editor/RichTextEditor.js +2925 -0
  25. package/dist/react/editor/RichTextRenderedBlock.d.ts +20 -0
  26. package/dist/react/editor/RichTextRenderedBlock.js +162 -0
  27. package/dist/react/editor/RichTextRenderer.d.ts +13 -0
  28. package/dist/react/editor/RichTextRenderer.js +16 -0
  29. package/dist/react/{RichTextTitleInput.d.ts → editor/RichTextTitleInput.d.ts} +11 -1
  30. package/dist/react/{RichTextTitleInput.js → editor/RichTextTitleInput.js} +17 -2
  31. package/dist/react/editor/blockActions.d.ts +48 -0
  32. package/dist/react/editor/blockActions.js +495 -0
  33. package/dist/react/editor/editorHistory.d.ts +55 -0
  34. package/dist/react/editor/editorHistory.js +111 -0
  35. package/dist/react/{editorNavigation.d.ts → editor/editorNavigation.d.ts} +2 -0
  36. package/dist/react/{editorNavigation.js → editor/editorNavigation.js} +16 -0
  37. package/dist/react/editor/editorOperations.d.ts +10 -0
  38. package/dist/react/editor/editorOperations.js +3 -0
  39. package/dist/react/editor/editorSelection.d.ts +3 -0
  40. package/dist/react/editor/editorSelection.js +215 -0
  41. package/dist/react/{editorShortcuts.d.ts → editor/editorShortcuts.d.ts} +10 -0
  42. package/dist/react/{editorShortcuts.js → editor/editorShortcuts.js} +17 -1
  43. package/dist/react/{RichTextIcons.d.ts → icons/RichTextIcons.d.ts} +3 -0
  44. package/dist/react/{RichTextIcons.js → icons/RichTextIcons.js} +9 -0
  45. package/dist/react/index.d.ts +12 -9
  46. package/dist/react/index.js +7 -6
  47. package/dist/react/{EditorSessionProvider.d.ts → session/EditorSessionProvider.d.ts} +2 -2
  48. package/dist/react/{EditorSessionProvider.js → session/EditorSessionProvider.js} +3 -3
  49. package/dist/react/{UnsavedChangesDialog.js → session/UnsavedChangesDialog.js} +1 -1
  50. package/dist/react/styles/RichTextStyles.js +1362 -0
  51. package/dist/react/{BlockActionTool.d.ts → tools/BlockActionTool.d.ts} +1 -1
  52. package/dist/react/{BlockActionTool.js → tools/BlockActionTool.js} +6 -2
  53. package/dist/react/tools/LinkCreationInput.d.ts +9 -0
  54. package/dist/react/tools/LinkCreationInput.js +38 -0
  55. package/dist/react/{SelectionFormatToolbar.d.ts → tools/SelectionFormatToolbar.d.ts} +3 -2
  56. package/dist/react/{SelectionFormatToolbar.js → tools/SelectionFormatToolbar.js} +3 -3
  57. package/dist/react/tools/SpecialBlockOption.d.ts +9 -0
  58. package/dist/react/tools/SpecialBlockOption.js +8 -0
  59. package/dist/react/tools/SpecialBlockTool.d.ts +91 -0
  60. package/dist/react/tools/SpecialBlockTool.js +125 -0
  61. package/dist/react/{TranscriptionControl.d.ts → tools/TranscriptionControl.d.ts} +9 -0
  62. package/dist/react/{TranscriptionControl.js → tools/TranscriptionControl.js} +70 -9
  63. package/dist/react/tools/blockActionToolState.d.ts +41 -0
  64. package/dist/react/tools/blockActionToolState.js +177 -0
  65. package/dist/react/tools/imageBlockDiagnostics.d.ts +2 -0
  66. package/dist/react/tools/imageBlockDiagnostics.js +12 -0
  67. package/dist/{session.d.ts → session/session.d.ts} +1 -1
  68. package/dist-cjs/core/blockTree.js +137 -0
  69. package/dist-cjs/core/blockTypes.js +9 -0
  70. package/dist-cjs/core/exportImport.js +56 -0
  71. package/dist-cjs/core/features.js +62 -0
  72. package/dist-cjs/core/proFeatures.js +70 -0
  73. package/dist-cjs/core/richText.js +578 -0
  74. package/dist-cjs/index.js +22 -6
  75. package/dist-cjs/react/editor/RichTextBody.js +134 -0
  76. package/dist-cjs/react/editor/RichTextEditor.js +2956 -0
  77. package/dist-cjs/react/editor/RichTextRenderedBlock.js +166 -0
  78. package/dist-cjs/react/editor/RichTextRenderer.js +20 -0
  79. package/dist-cjs/react/{RichTextTitleInput.js → editor/RichTextTitleInput.js} +18 -2
  80. package/dist-cjs/react/editor/blockActions.js +518 -0
  81. package/dist-cjs/react/editor/editorHistory.js +120 -0
  82. package/dist-cjs/react/{editorNavigation.js → editor/editorNavigation.js} +17 -0
  83. package/dist-cjs/react/editor/editorOperations.js +6 -0
  84. package/dist-cjs/react/editor/editorSelection.js +219 -0
  85. package/dist-cjs/react/{editorShortcuts.js → editor/editorShortcuts.js} +17 -1
  86. package/dist-cjs/react/{RichTextIcons.js → icons/RichTextIcons.js} +12 -0
  87. package/dist-cjs/react/index.js +9 -7
  88. package/dist-cjs/react/{EditorSessionProvider.js → session/EditorSessionProvider.js} +3 -3
  89. package/dist-cjs/react/{UnsavedChangesDialog.js → session/UnsavedChangesDialog.js} +1 -1
  90. package/dist-cjs/react/styles/RichTextStyles.js +1365 -0
  91. package/dist-cjs/react/{BlockActionTool.js → tools/BlockActionTool.js} +6 -2
  92. package/dist-cjs/react/tools/LinkCreationInput.js +41 -0
  93. package/dist-cjs/react/{SelectionFormatToolbar.js → tools/SelectionFormatToolbar.js} +3 -3
  94. package/dist-cjs/react/tools/SpecialBlockOption.js +11 -0
  95. package/dist-cjs/react/tools/SpecialBlockTool.js +129 -0
  96. package/dist-cjs/react/{TranscriptionControl.js → tools/TranscriptionControl.js} +71 -9
  97. package/dist-cjs/react/tools/blockActionToolState.js +186 -0
  98. package/package.json +3 -2
  99. package/dist/react/RichTextBody.d.ts +0 -18
  100. package/dist/react/RichTextBody.js +0 -66
  101. package/dist/react/RichTextEditor.d.ts +0 -45
  102. package/dist/react/RichTextEditor.js +0 -1096
  103. package/dist/react/RichTextRenderedBlock.d.ts +0 -4
  104. package/dist/react/RichTextRenderedBlock.js +0 -36
  105. package/dist/react/RichTextRenderer.d.ts +0 -4
  106. package/dist/react/RichTextRenderer.js +0 -8
  107. package/dist/react/RichTextStyles.js +0 -719
  108. package/dist/react/SpecialBlockOption.d.ts +0 -7
  109. package/dist/react/SpecialBlockOption.js +0 -7
  110. package/dist/react/SpecialBlockTool.d.ts +0 -42
  111. package/dist/react/SpecialBlockTool.js +0 -50
  112. package/dist/react/blockActionToolState.d.ts +0 -18
  113. package/dist/react/blockActionToolState.js +0 -53
  114. package/dist/react/blockActions.d.ts +0 -8
  115. package/dist/react/blockActions.js +0 -111
  116. package/dist/richText.js +0 -297
  117. package/dist/types.d.ts +0 -34
  118. package/dist-cjs/react/RichTextBody.js +0 -69
  119. package/dist-cjs/react/RichTextEditor.js +0 -1108
  120. package/dist-cjs/react/RichTextRenderedBlock.js +0 -39
  121. package/dist-cjs/react/RichTextRenderer.js +0 -11
  122. package/dist-cjs/react/RichTextStyles.js +0 -722
  123. package/dist-cjs/react/SpecialBlockOption.js +0 -10
  124. package/dist-cjs/react/SpecialBlockTool.js +0 -54
  125. package/dist-cjs/react/blockActionToolState.js +0 -58
  126. package/dist-cjs/react/blockActions.js +0 -119
  127. package/dist-cjs/richText.js +0 -307
  128. /package/dist/{types.js → core/types.js} +0 -0
  129. /package/dist/{writingStats.d.ts → core/writingStats.d.ts} +0 -0
  130. /package/dist/{writingStats.js → core/writingStats.js} +0 -0
  131. /package/dist/react/{RichTextDocumentSurface.d.ts → editor/RichTextDocumentSurface.d.ts} +0 -0
  132. /package/dist/react/{RichTextDocumentSurface.js → editor/RichTextDocumentSurface.js} +0 -0
  133. /package/dist/react/{UnsavedChangesDialog.d.ts → session/UnsavedChangesDialog.d.ts} +0 -0
  134. /package/dist/react/{RichTextStyles.d.ts → styles/RichTextStyles.d.ts} +0 -0
  135. /package/dist/react/{richTextBlockStyles.d.ts → styles/richTextBlockStyles.d.ts} +0 -0
  136. /package/dist/react/{richTextBlockStyles.js → styles/richTextBlockStyles.js} +0 -0
  137. /package/dist/react/{specialBlockStyles.d.ts → styles/specialBlockStyles.d.ts} +0 -0
  138. /package/dist/react/{specialBlockStyles.js → styles/specialBlockStyles.js} +0 -0
  139. /package/dist/{saveControl.d.ts → session/saveControl.d.ts} +0 -0
  140. /package/dist/{saveControl.js → session/saveControl.js} +0 -0
  141. /package/dist/{session.js → session/session.js} +0 -0
  142. /package/dist/{sessionRegistry.d.ts → session/sessionRegistry.d.ts} +0 -0
  143. /package/dist/{sessionRegistry.js → session/sessionRegistry.js} +0 -0
  144. /package/dist-cjs/{types.js → core/types.js} +0 -0
  145. /package/dist-cjs/{writingStats.js → core/writingStats.js} +0 -0
  146. /package/dist-cjs/react/{RichTextDocumentSurface.js → editor/RichTextDocumentSurface.js} +0 -0
  147. /package/dist-cjs/react/{richTextBlockStyles.js → styles/richTextBlockStyles.js} +0 -0
  148. /package/dist-cjs/react/{specialBlockStyles.js → styles/specialBlockStyles.js} +0 -0
  149. /package/dist-cjs/{saveControl.js → session/saveControl.js} +0 -0
  150. /package/dist-cjs/{session.js → session/session.js} +0 -0
  151. /package/dist-cjs/{sessionRegistry.js → session/sessionRegistry.js} +0 -0
@@ -0,0 +1,177 @@
1
+ const hoverLaneLeftWidth = 72;
2
+ const hoverLaneRightWidth = 36;
3
+ const hoverLaneVerticalPadding = 10;
4
+ const raisedBlockActionToolTopOffset = -4;
5
+ export function getBlockActionToolAnchorTop(blockType, top) {
6
+ return blockType === "checkbox" ? top + raisedBlockActionToolTopOffset : top;
7
+ }
8
+ export function getBlockActionToolRenderTop(placement) {
9
+ return placement.toolTop ?? placement.top;
10
+ }
11
+ export function getVisibleBlockActionToolPlacements(placements, state) {
12
+ const uniquePlacements = getUniqueBlockActionToolPlacements(placements);
13
+ const visibleBlockId = state.activeBlockId ??
14
+ state.draggedBlockId ??
15
+ state.hoveredBlockId ??
16
+ state.focusedBlockId;
17
+ return visibleBlockId
18
+ ? uniquePlacements.filter((placement) => placement.blockId === visibleBlockId)
19
+ : [];
20
+ }
21
+ export function getPointerDropTarget(placements, draggedBlockId, pointerClientY, pointerClientX) {
22
+ const targetPlacements = placements.filter((placement) => {
23
+ return placement.blockId !== draggedBlockId;
24
+ });
25
+ const rowPlacements = targetPlacements.filter((placement) => {
26
+ return placement.dropRole !== "content";
27
+ });
28
+ const contentPlacements = targetPlacements.filter((placement) => {
29
+ return placement.dropRole === "content";
30
+ });
31
+ const targetPlacement = findContainingPlacement(rowPlacements, pointerClientY) ??
32
+ findContainingPlacement(contentPlacements, pointerClientY) ??
33
+ getClosestPlacement(rowPlacements, pointerClientY) ??
34
+ getClosestPlacement(contentPlacements, pointerClientY);
35
+ if (!targetPlacement) {
36
+ return null;
37
+ }
38
+ if (targetPlacement.dropRole === "content") {
39
+ return {
40
+ placement: "inside",
41
+ targetBlockId: targetPlacement.blockId,
42
+ };
43
+ }
44
+ if (pointerClientX !== undefined &&
45
+ pointerClientX >= targetPlacement.left + 24) {
46
+ return {
47
+ placement: "inside",
48
+ targetBlockId: targetPlacement.blockId,
49
+ };
50
+ }
51
+ const visualAncestorDropTarget = pointerClientX === undefined
52
+ ? null
53
+ : getVisualAncestorDropTarget(rowPlacements, targetPlacement, pointerClientX);
54
+ if (visualAncestorDropTarget) {
55
+ return visualAncestorDropTarget;
56
+ }
57
+ return {
58
+ placement: pointerClientY >
59
+ targetPlacement.top + (targetPlacement.bottom - targetPlacement.top) / 2
60
+ ? "after"
61
+ : "before",
62
+ targetBlockId: targetPlacement.blockId,
63
+ };
64
+ }
65
+ export function getPointerDragDropResult(placements, draggedBlockId, pointerClientY, pointerClientX) {
66
+ const containingPlacement = findContainingPlacement(placements, pointerClientY);
67
+ if (containingPlacement?.blockId === draggedBlockId) {
68
+ return {
69
+ dropTarget: getDropTargetFromPlacement(containingPlacement, pointerClientY, pointerClientX),
70
+ status: "invalid",
71
+ };
72
+ }
73
+ const dropTarget = getPointerDropTarget(placements, draggedBlockId, pointerClientY, pointerClientX);
74
+ if (!dropTarget) {
75
+ return { status: "invalid" };
76
+ }
77
+ const targetPlacement = placements.find((placement) => {
78
+ return (placement.blockId === dropTarget.targetBlockId &&
79
+ (dropTarget.placement === "inside" || placement.dropRole !== "content"));
80
+ });
81
+ if (dropTarget.targetBlockId === draggedBlockId ||
82
+ targetPlacement?.ancestorBlockIds?.includes(draggedBlockId)) {
83
+ return { dropTarget, status: "invalid" };
84
+ }
85
+ return { dropTarget, status: "valid" };
86
+ }
87
+ function getDropTargetFromPlacement(placement, pointerClientY, pointerClientX) {
88
+ if (placement.dropRole === "content" ||
89
+ (pointerClientX !== undefined && pointerClientX >= placement.left + 24)) {
90
+ return {
91
+ placement: "inside",
92
+ targetBlockId: placement.blockId,
93
+ };
94
+ }
95
+ return {
96
+ placement: pointerClientY > placement.top + (placement.bottom - placement.top) / 2
97
+ ? "after"
98
+ : "before",
99
+ targetBlockId: placement.blockId,
100
+ };
101
+ }
102
+ export function getPointerHoverLaneBlockId(placements, pointerTop, pointerLeft) {
103
+ const placement = getUniqueBlockActionToolPlacements(placements).find((currentPlacement) => {
104
+ return (pointerTop >= currentPlacement.top - hoverLaneVerticalPadding &&
105
+ pointerTop <= currentPlacement.bottom + hoverLaneVerticalPadding &&
106
+ pointerLeft >= currentPlacement.left - hoverLaneLeftWidth &&
107
+ pointerLeft <= currentPlacement.left + hoverLaneRightWidth);
108
+ });
109
+ return placement?.blockId ?? null;
110
+ }
111
+ export function getUniqueBlockActionToolPlacements(placements) {
112
+ const seenBlockIds = new Set();
113
+ return placements.filter((placement) => {
114
+ if (placement.dropRole === "content") {
115
+ return false;
116
+ }
117
+ if (seenBlockIds.has(placement.blockId)) {
118
+ return false;
119
+ }
120
+ seenBlockIds.add(placement.blockId);
121
+ return true;
122
+ });
123
+ }
124
+ function getVisualAncestorDropTarget(rowPlacements, targetPlacement, pointerClientX) {
125
+ const targetDepth = targetPlacement.depth ?? targetPlacement.ancestorBlockIds?.length ?? 0;
126
+ if (targetDepth <= 0 || !targetPlacement.ancestorBlockIds?.length) {
127
+ return null;
128
+ }
129
+ const desiredDepth = clamp(Math.round((pointerClientX - getMinimumPlacementLeft(rowPlacements)) /
130
+ getPlacementIndentSize(rowPlacements)), 0, targetDepth);
131
+ if (desiredDepth >= targetDepth) {
132
+ return null;
133
+ }
134
+ const targetBlockId = targetPlacement.ancestorBlockIds[desiredDepth];
135
+ return targetBlockId
136
+ ? {
137
+ placement: "after",
138
+ targetBlockId,
139
+ }
140
+ : null;
141
+ }
142
+ function getPlacementIndentSize(placements) {
143
+ const leftPositions = Array.from(new Set(placements.map((placement) => Math.round(placement.left)))).sort((a, b) => a - b);
144
+ const positiveDiffs = leftPositions
145
+ .slice(1)
146
+ .map((left, index) => left - (leftPositions[index] ?? left))
147
+ .filter((diff) => diff > 0);
148
+ return Math.min(...positiveDiffs, 42);
149
+ }
150
+ function getMinimumPlacementLeft(placements) {
151
+ return Math.min(...placements.map((placement) => placement.left), 0);
152
+ }
153
+ function clamp(value, min, max) {
154
+ return Math.min(Math.max(value, min), max);
155
+ }
156
+ function getDistanceToPlacement(placement, pointerClientY) {
157
+ if (pointerClientY >= placement.top && pointerClientY <= placement.bottom) {
158
+ return 0;
159
+ }
160
+ return Math.min(Math.abs(pointerClientY - placement.top), Math.abs(pointerClientY - placement.bottom));
161
+ }
162
+ function findContainingPlacement(placements, pointerClientY) {
163
+ return placements.find((placement) => {
164
+ return (pointerClientY >= placement.top && pointerClientY <= placement.bottom);
165
+ });
166
+ }
167
+ function getClosestPlacement(placements, pointerClientY) {
168
+ return placements.reduce((closestPlacement, placement) => {
169
+ if (!closestPlacement) {
170
+ return placement;
171
+ }
172
+ return getDistanceToPlacement(placement, pointerClientY) <
173
+ getDistanceToPlacement(closestPlacement, pointerClientY)
174
+ ? placement
175
+ : closestPlacement;
176
+ }, null);
177
+ }
@@ -0,0 +1,2 @@
1
+ import type { RichTextBlock } from "../../core/types";
2
+ export declare function warnIfImageBlockMissingDisplayUrl(block: RichTextBlock): void;
@@ -0,0 +1,12 @@
1
+ export function warnIfImageBlockMissingDisplayUrl(block) {
2
+ if (block.type !== "image" ||
3
+ block.url ||
4
+ typeof block.uploadProgress === "number" ||
5
+ !block.alt?.trim()) {
6
+ return;
7
+ }
8
+ console.warn("Rich text image block is missing a display URL.", {
9
+ alt: block.alt,
10
+ blockId: block.id,
11
+ });
12
+ }
@@ -1,4 +1,4 @@
1
- import type { RichTextDocument } from "./types";
1
+ import type { RichTextDocument } from "../core/types";
2
2
  export type EditorDirtyScope = "all" | "content";
3
3
  export type EditorSessionValue<Metadata = unknown> = {
4
4
  document: RichTextDocument;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findBlockPath = findBlockPath;
4
+ exports.getBlockAtPath = getBlockAtPath;
5
+ exports.getBlocksAtPath = getBlocksAtPath;
6
+ exports.removeBlockAtPath = removeBlockAtPath;
7
+ exports.updateBlockAtPath = updateBlockAtPath;
8
+ exports.insertBlockAtPath = insertBlockAtPath;
9
+ exports.pathContains = pathContains;
10
+ exports.withNestableBlockChildren = withNestableBlockChildren;
11
+ exports.withBlockChildren = withBlockChildren;
12
+ const blockTypes_1 = require("./blockTypes");
13
+ function findBlockPath(blocks, blockId) {
14
+ for (let index = 0; index < blocks.length; index += 1) {
15
+ const block = blocks[index];
16
+ if (!block) {
17
+ continue;
18
+ }
19
+ if (block.id === blockId) {
20
+ return [index];
21
+ }
22
+ if ((0, blockTypes_1.isNestableRichTextBlock)(block) && block.children?.length) {
23
+ const childPath = findBlockPath(block.children, blockId);
24
+ if (childPath) {
25
+ return [index, ...childPath];
26
+ }
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ function getBlockAtPath(blocks, path) {
32
+ let currentBlocks = blocks;
33
+ let currentBlock;
34
+ for (const index of path) {
35
+ currentBlock = currentBlocks[index];
36
+ if (!currentBlock) {
37
+ return null;
38
+ }
39
+ currentBlocks = (0, blockTypes_1.isNestableRichTextBlock)(currentBlock)
40
+ ? currentBlock.children ?? []
41
+ : [];
42
+ }
43
+ return currentBlock ?? null;
44
+ }
45
+ function getBlocksAtPath(blocks, path) {
46
+ if (path.length === 0) {
47
+ return blocks;
48
+ }
49
+ const parent = getBlockAtPath(blocks, path);
50
+ return (0, blockTypes_1.isNestableRichTextBlock)(parent) ? parent.children ?? [] : null;
51
+ }
52
+ function removeBlockAtPath(blocks, path) {
53
+ const [index, ...rest] = path;
54
+ if (index === undefined) {
55
+ return null;
56
+ }
57
+ if (rest.length === 0) {
58
+ const block = blocks[index];
59
+ if (!block) {
60
+ return null;
61
+ }
62
+ return {
63
+ block,
64
+ blocks: [...blocks.slice(0, index), ...blocks.slice(index + 1)],
65
+ };
66
+ }
67
+ const parent = blocks[index];
68
+ if (!(0, blockTypes_1.isNestableRichTextBlock)(parent)) {
69
+ return null;
70
+ }
71
+ const removed = removeBlockAtPath(parent.children ?? [], rest);
72
+ if (!removed) {
73
+ return null;
74
+ }
75
+ const updatedParent = withNestableBlockChildren(parent, removed.blocks);
76
+ return {
77
+ block: removed.block,
78
+ blocks: [
79
+ ...blocks.slice(0, index),
80
+ updatedParent,
81
+ ...blocks.slice(index + 1),
82
+ ],
83
+ };
84
+ }
85
+ function updateBlockAtPath(blocks, path, updater) {
86
+ const [index, ...rest] = path;
87
+ if (index === undefined) {
88
+ return blocks;
89
+ }
90
+ const block = blocks[index];
91
+ if (!block) {
92
+ return blocks;
93
+ }
94
+ const updatedBlock = rest.length === 0
95
+ ? updater(block)
96
+ : (0, blockTypes_1.isNestableRichTextBlock)(block)
97
+ ? withNestableBlockChildren(block, updateBlockAtPath(block.children ?? [], rest, updater))
98
+ : block;
99
+ return [...blocks.slice(0, index), updatedBlock, ...blocks.slice(index + 1)];
100
+ }
101
+ function insertBlockAtPath(blocks, parentPath, index, insertedBlocks) {
102
+ if (parentPath.length === 0) {
103
+ return [
104
+ ...blocks.slice(0, index),
105
+ ...insertedBlocks,
106
+ ...blocks.slice(index),
107
+ ];
108
+ }
109
+ return updateBlockAtPath(blocks, parentPath, (parent) => {
110
+ if (!(0, blockTypes_1.isNestableRichTextBlock)(parent)) {
111
+ return parent;
112
+ }
113
+ return withNestableBlockChildren(parent, [
114
+ ...(parent.children ?? []).slice(0, index),
115
+ ...insertedBlocks,
116
+ ...(parent.children ?? []).slice(index),
117
+ ]);
118
+ });
119
+ }
120
+ function pathContains(parentPath, childPath) {
121
+ return parentPath.every((index, pathIndex) => childPath[pathIndex] === index);
122
+ }
123
+ function withNestableBlockChildren(block, children) {
124
+ if (block.type === "toggle") {
125
+ return { ...block, children };
126
+ }
127
+ if (children.length > 0) {
128
+ return { ...block, children };
129
+ }
130
+ const { children: _children, ...rest } = block;
131
+ return rest;
132
+ }
133
+ function withBlockChildren(block, children) {
134
+ return (0, blockTypes_1.isNestableRichTextBlock)(block)
135
+ ? withNestableBlockChildren(block, children)
136
+ : block;
137
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NESTABLE_RICH_TEXT_BLOCK_TYPES = void 0;
4
+ exports.isNestableRichTextBlock = isNestableRichTextBlock;
5
+ exports.NESTABLE_RICH_TEXT_BLOCK_TYPES = new Set(["bullet", "ordered", "checkbox", "toggle"]);
6
+ function isNestableRichTextBlock(block) {
7
+ return (!!block &&
8
+ exports.NESTABLE_RICH_TEXT_BLOCK_TYPES.has(block.type));
9
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.richTextJsonImportAdapter = exports.richTextJsonExportAdapter = void 0;
4
+ exports.exportRichTextBlocks = exportRichTextBlocks;
5
+ exports.importRichTextBlocks = importRichTextBlocks;
6
+ const features_1 = require("./features");
7
+ const richText_1 = require("./richText");
8
+ exports.richTextJsonExportAdapter = {
9
+ extension: "json",
10
+ exportBlocks: (blocks) => ({
11
+ data: JSON.stringify((0, richText_1.sanitizeRichTextBlocks)(blocks), null, 2),
12
+ extension: "json",
13
+ format: "bayon-rich-text-json",
14
+ mimeType: "application/json",
15
+ }),
16
+ id: "bayon-rich-text-json",
17
+ label: "Bayon rich text JSON",
18
+ mimeType: "application/json",
19
+ };
20
+ exports.richTextJsonImportAdapter = {
21
+ extension: "json",
22
+ id: "bayon-rich-text-json",
23
+ importData: (data) => JSON.parse(data),
24
+ label: "Bayon rich text JSON",
25
+ mimeType: "application/json",
26
+ };
27
+ async function exportRichTextBlocks({ adapter, blocks, features, }) {
28
+ const gate = getExportImportGate(features);
29
+ if (gate) {
30
+ return { gate, ok: false };
31
+ }
32
+ return {
33
+ ok: true,
34
+ value: adapter.exportBlocks((0, richText_1.sanitizeRichTextBlocks)(blocks)),
35
+ };
36
+ }
37
+ async function importRichTextBlocks({ adapter, data, features, }) {
38
+ const gate = getExportImportGate(features);
39
+ if (gate) {
40
+ return { gate, ok: false };
41
+ }
42
+ return {
43
+ ok: true,
44
+ value: (0, richText_1.sanitizeRichTextBlocks)(adapter.importData(data)),
45
+ };
46
+ }
47
+ function getExportImportGate(features) {
48
+ const access = (0, features_1.getRichTextFeatureAccess)(features, "export-import");
49
+ return access.enabled
50
+ ? null
51
+ : {
52
+ featureId: access.featureId,
53
+ label: access.label,
54
+ reason: access.reason,
55
+ };
56
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.richTextEditorFeatureCatalog = void 0;
4
+ exports.getRichTextFeatureAccess = getRichTextFeatureAccess;
5
+ exports.isRichTextFeatureEnabled = isRichTextFeatureEnabled;
6
+ exports.richTextEditorFeatureCatalog = {
7
+ "image-upload": {
8
+ label: "Image upload",
9
+ tier: "free",
10
+ },
11
+ transcription: {
12
+ label: "Transcription",
13
+ tier: "free",
14
+ },
15
+ "export-import": {
16
+ label: "Export and import",
17
+ tier: "pro",
18
+ },
19
+ comments: {
20
+ label: "Comments",
21
+ tier: "pro",
22
+ },
23
+ "review-marks": {
24
+ label: "Review marks",
25
+ tier: "pro",
26
+ },
27
+ "schema-migrations": {
28
+ label: "Schema migrations",
29
+ tier: "pro",
30
+ },
31
+ templates: {
32
+ label: "Templates",
33
+ tier: "pro",
34
+ },
35
+ collaboration: {
36
+ label: "Collaboration",
37
+ tier: "pro",
38
+ },
39
+ };
40
+ function getRichTextFeatureAccess(features, featureId) {
41
+ const feature = exports.richTextEditorFeatureCatalog[featureId];
42
+ const enabled = features?.[featureId] ?? true;
43
+ if (enabled) {
44
+ return {
45
+ enabled: true,
46
+ featureId,
47
+ label: feature.label,
48
+ reason: undefined,
49
+ tier: feature.tier,
50
+ };
51
+ }
52
+ return {
53
+ enabled: false,
54
+ featureId,
55
+ label: feature.label,
56
+ reason: feature.tier === "pro" ? "premium" : "disabled",
57
+ tier: feature.tier,
58
+ };
59
+ }
60
+ function isRichTextFeatureEnabled(features, featureId) {
61
+ return getRichTextFeatureAccess(features, featureId).enabled;
62
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runRichTextSchemaMigrations = runRichTextSchemaMigrations;
4
+ exports.instantiateRichTextTemplate = instantiateRichTextTemplate;
5
+ exports.connectRichTextCollaboration = connectRichTextCollaboration;
6
+ exports.createRichTextAnnotations = createRichTextAnnotations;
7
+ const features_1 = require("./features");
8
+ const richText_1 = require("./richText");
9
+ async function runRichTextSchemaMigrations({ blocks, features, migrations, }) {
10
+ const gate = getProFeatureGate(features, "schema-migrations");
11
+ if (gate) {
12
+ return { gate, ok: false };
13
+ }
14
+ let nextBlocks = (0, richText_1.sanitizeRichTextBlocks)(blocks);
15
+ const appliedMigrationIds = [];
16
+ for (const migration of migrations) {
17
+ nextBlocks = (0, richText_1.sanitizeRichTextBlocks)(migration.migrate(nextBlocks));
18
+ appliedMigrationIds.push(migration.id);
19
+ }
20
+ return {
21
+ ok: true,
22
+ value: { appliedMigrationIds, blocks: nextBlocks },
23
+ };
24
+ }
25
+ async function instantiateRichTextTemplate({ features, template, variables, }) {
26
+ const gate = getProFeatureGate(features, "templates");
27
+ if (gate) {
28
+ return { gate, ok: false };
29
+ }
30
+ return {
31
+ ok: true,
32
+ value: (0, richText_1.sanitizeRichTextBlocks)(template.createBlocks(variables)),
33
+ };
34
+ }
35
+ async function connectRichTextCollaboration({ adapter, blocks, features, }) {
36
+ const gate = getProFeatureGate(features, "collaboration");
37
+ if (gate) {
38
+ return { gate, ok: false };
39
+ }
40
+ return {
41
+ ok: true,
42
+ value: await adapter.connect({ blocks: (0, richText_1.sanitizeRichTextBlocks)(blocks) }),
43
+ };
44
+ }
45
+ function createRichTextAnnotations({ annotations, features, }) {
46
+ const reviewGate = annotations.some((annotation) => annotation.kind === "review-mark")
47
+ ? getProFeatureGate(features, "review-marks")
48
+ : null;
49
+ const commentGate = annotations.some((annotation) => annotation.kind === "comment")
50
+ ? getProFeatureGate(features, "comments")
51
+ : null;
52
+ const gate = reviewGate ?? commentGate;
53
+ if (gate) {
54
+ return { gate, ok: false };
55
+ }
56
+ return {
57
+ ok: true,
58
+ value: annotations.filter((annotation) => annotation.id.trim() && annotation.blockId.trim()),
59
+ };
60
+ }
61
+ function getProFeatureGate(features, featureId) {
62
+ const access = (0, features_1.getRichTextFeatureAccess)(features, featureId);
63
+ return access.enabled
64
+ ? null
65
+ : {
66
+ featureId: access.featureId,
67
+ label: access.label,
68
+ reason: access.reason,
69
+ };
70
+ }