@rtif-sdk/web 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 (215) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/block-drag-handler.d.ts +189 -0
  4. package/dist/block-drag-handler.d.ts.map +1 -0
  5. package/dist/block-drag-handler.js +745 -0
  6. package/dist/block-drag-handler.js.map +1 -0
  7. package/dist/block-renderer.d.ts +402 -0
  8. package/dist/block-renderer.d.ts.map +1 -0
  9. package/dist/block-renderer.js +424 -0
  10. package/dist/block-renderer.js.map +1 -0
  11. package/dist/clipboard.d.ts +178 -0
  12. package/dist/clipboard.d.ts.map +1 -0
  13. package/dist/clipboard.js +432 -0
  14. package/dist/clipboard.js.map +1 -0
  15. package/dist/command-bus.d.ts +113 -0
  16. package/dist/command-bus.d.ts.map +1 -0
  17. package/dist/command-bus.js +70 -0
  18. package/dist/command-bus.js.map +1 -0
  19. package/dist/composition.d.ts +220 -0
  20. package/dist/composition.d.ts.map +1 -0
  21. package/dist/composition.js +271 -0
  22. package/dist/composition.js.map +1 -0
  23. package/dist/content-extraction.d.ts +69 -0
  24. package/dist/content-extraction.d.ts.map +1 -0
  25. package/dist/content-extraction.js +228 -0
  26. package/dist/content-extraction.js.map +1 -0
  27. package/dist/content-handler-file.d.ts +40 -0
  28. package/dist/content-handler-file.d.ts.map +1 -0
  29. package/dist/content-handler-file.js +91 -0
  30. package/dist/content-handler-file.js.map +1 -0
  31. package/dist/content-handler-image.d.ts +82 -0
  32. package/dist/content-handler-image.d.ts.map +1 -0
  33. package/dist/content-handler-image.js +120 -0
  34. package/dist/content-handler-image.js.map +1 -0
  35. package/dist/content-handler-url.d.ts +129 -0
  36. package/dist/content-handler-url.d.ts.map +1 -0
  37. package/dist/content-handler-url.js +244 -0
  38. package/dist/content-handler-url.js.map +1 -0
  39. package/dist/content-handlers.d.ts +67 -0
  40. package/dist/content-handlers.d.ts.map +1 -0
  41. package/dist/content-handlers.js +263 -0
  42. package/dist/content-handlers.js.map +1 -0
  43. package/dist/content-pipeline.d.ts +383 -0
  44. package/dist/content-pipeline.d.ts.map +1 -0
  45. package/dist/content-pipeline.js +232 -0
  46. package/dist/content-pipeline.js.map +1 -0
  47. package/dist/cursor-nav.d.ts +149 -0
  48. package/dist/cursor-nav.d.ts.map +1 -0
  49. package/dist/cursor-nav.js +230 -0
  50. package/dist/cursor-nav.js.map +1 -0
  51. package/dist/cursor-rect.d.ts +65 -0
  52. package/dist/cursor-rect.d.ts.map +1 -0
  53. package/dist/cursor-rect.js +98 -0
  54. package/dist/cursor-rect.js.map +1 -0
  55. package/dist/drop-indicator.d.ts +108 -0
  56. package/dist/drop-indicator.d.ts.map +1 -0
  57. package/dist/drop-indicator.js +236 -0
  58. package/dist/drop-indicator.js.map +1 -0
  59. package/dist/editor.d.ts +41 -0
  60. package/dist/editor.d.ts.map +1 -0
  61. package/dist/editor.js +710 -0
  62. package/dist/editor.js.map +1 -0
  63. package/dist/floating-toolbar.d.ts +93 -0
  64. package/dist/floating-toolbar.d.ts.map +1 -0
  65. package/dist/floating-toolbar.js +159 -0
  66. package/dist/floating-toolbar.js.map +1 -0
  67. package/dist/index.d.ts +62 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +119 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/input-bridge.d.ts +273 -0
  72. package/dist/input-bridge.d.ts.map +1 -0
  73. package/dist/input-bridge.js +884 -0
  74. package/dist/input-bridge.js.map +1 -0
  75. package/dist/link-popover.d.ts +38 -0
  76. package/dist/link-popover.d.ts.map +1 -0
  77. package/dist/link-popover.js +278 -0
  78. package/dist/link-popover.js.map +1 -0
  79. package/dist/mark-renderer.d.ts +275 -0
  80. package/dist/mark-renderer.d.ts.map +1 -0
  81. package/dist/mark-renderer.js +210 -0
  82. package/dist/mark-renderer.js.map +1 -0
  83. package/dist/perf.d.ts +145 -0
  84. package/dist/perf.d.ts.map +1 -0
  85. package/dist/perf.js +260 -0
  86. package/dist/perf.js.map +1 -0
  87. package/dist/plugin-kit.d.ts +265 -0
  88. package/dist/plugin-kit.d.ts.map +1 -0
  89. package/dist/plugin-kit.js +234 -0
  90. package/dist/plugin-kit.js.map +1 -0
  91. package/dist/plugins/alignment-plugin.d.ts +68 -0
  92. package/dist/plugins/alignment-plugin.d.ts.map +1 -0
  93. package/dist/plugins/alignment-plugin.js +98 -0
  94. package/dist/plugins/alignment-plugin.js.map +1 -0
  95. package/dist/plugins/block-utils.d.ts +113 -0
  96. package/dist/plugins/block-utils.d.ts.map +1 -0
  97. package/dist/plugins/block-utils.js +191 -0
  98. package/dist/plugins/block-utils.js.map +1 -0
  99. package/dist/plugins/blockquote-plugin.d.ts +39 -0
  100. package/dist/plugins/blockquote-plugin.d.ts.map +1 -0
  101. package/dist/plugins/blockquote-plugin.js +88 -0
  102. package/dist/plugins/blockquote-plugin.js.map +1 -0
  103. package/dist/plugins/bold-plugin.d.ts +37 -0
  104. package/dist/plugins/bold-plugin.d.ts.map +1 -0
  105. package/dist/plugins/bold-plugin.js +48 -0
  106. package/dist/plugins/bold-plugin.js.map +1 -0
  107. package/dist/plugins/callout-plugin.d.ts +100 -0
  108. package/dist/plugins/callout-plugin.d.ts.map +1 -0
  109. package/dist/plugins/callout-plugin.js +200 -0
  110. package/dist/plugins/callout-plugin.js.map +1 -0
  111. package/dist/plugins/code-block-plugin.d.ts +62 -0
  112. package/dist/plugins/code-block-plugin.d.ts.map +1 -0
  113. package/dist/plugins/code-block-plugin.js +176 -0
  114. package/dist/plugins/code-block-plugin.js.map +1 -0
  115. package/dist/plugins/code-plugin.d.ts +37 -0
  116. package/dist/plugins/code-plugin.d.ts.map +1 -0
  117. package/dist/plugins/code-plugin.js +48 -0
  118. package/dist/plugins/code-plugin.js.map +1 -0
  119. package/dist/plugins/embed-plugin.d.ts +90 -0
  120. package/dist/plugins/embed-plugin.d.ts.map +1 -0
  121. package/dist/plugins/embed-plugin.js +147 -0
  122. package/dist/plugins/embed-plugin.js.map +1 -0
  123. package/dist/plugins/font-family-plugin.d.ts +58 -0
  124. package/dist/plugins/font-family-plugin.d.ts.map +1 -0
  125. package/dist/plugins/font-family-plugin.js +57 -0
  126. package/dist/plugins/font-family-plugin.js.map +1 -0
  127. package/dist/plugins/font-size-plugin.d.ts +57 -0
  128. package/dist/plugins/font-size-plugin.d.ts.map +1 -0
  129. package/dist/plugins/font-size-plugin.js +56 -0
  130. package/dist/plugins/font-size-plugin.js.map +1 -0
  131. package/dist/plugins/heading-plugin.d.ts +52 -0
  132. package/dist/plugins/heading-plugin.d.ts.map +1 -0
  133. package/dist/plugins/heading-plugin.js +114 -0
  134. package/dist/plugins/heading-plugin.js.map +1 -0
  135. package/dist/plugins/hr-plugin.d.ts +33 -0
  136. package/dist/plugins/hr-plugin.d.ts.map +1 -0
  137. package/dist/plugins/hr-plugin.js +75 -0
  138. package/dist/plugins/hr-plugin.js.map +1 -0
  139. package/dist/plugins/image-plugin.d.ts +115 -0
  140. package/dist/plugins/image-plugin.d.ts.map +1 -0
  141. package/dist/plugins/image-plugin.js +199 -0
  142. package/dist/plugins/image-plugin.js.map +1 -0
  143. package/dist/plugins/indent-plugin.d.ts +62 -0
  144. package/dist/plugins/indent-plugin.d.ts.map +1 -0
  145. package/dist/plugins/indent-plugin.js +128 -0
  146. package/dist/plugins/indent-plugin.js.map +1 -0
  147. package/dist/plugins/index.d.ts +45 -0
  148. package/dist/plugins/index.d.ts.map +1 -0
  149. package/dist/plugins/index.js +42 -0
  150. package/dist/plugins/index.js.map +1 -0
  151. package/dist/plugins/italic-plugin.d.ts +37 -0
  152. package/dist/plugins/italic-plugin.d.ts.map +1 -0
  153. package/dist/plugins/italic-plugin.js +48 -0
  154. package/dist/plugins/italic-plugin.js.map +1 -0
  155. package/dist/plugins/link-plugin.d.ts +129 -0
  156. package/dist/plugins/link-plugin.d.ts.map +1 -0
  157. package/dist/plugins/link-plugin.js +212 -0
  158. package/dist/plugins/link-plugin.js.map +1 -0
  159. package/dist/plugins/list-plugin.d.ts +53 -0
  160. package/dist/plugins/list-plugin.d.ts.map +1 -0
  161. package/dist/plugins/list-plugin.js +309 -0
  162. package/dist/plugins/list-plugin.js.map +1 -0
  163. package/dist/plugins/mark-utils.d.ts +173 -0
  164. package/dist/plugins/mark-utils.d.ts.map +1 -0
  165. package/dist/plugins/mark-utils.js +425 -0
  166. package/dist/plugins/mark-utils.js.map +1 -0
  167. package/dist/plugins/mention-plugin.d.ts +191 -0
  168. package/dist/plugins/mention-plugin.d.ts.map +1 -0
  169. package/dist/plugins/mention-plugin.js +295 -0
  170. package/dist/plugins/mention-plugin.js.map +1 -0
  171. package/dist/plugins/strikethrough-plugin.d.ts +37 -0
  172. package/dist/plugins/strikethrough-plugin.d.ts.map +1 -0
  173. package/dist/plugins/strikethrough-plugin.js +48 -0
  174. package/dist/plugins/strikethrough-plugin.js.map +1 -0
  175. package/dist/plugins/text-color-plugin.d.ts +57 -0
  176. package/dist/plugins/text-color-plugin.d.ts.map +1 -0
  177. package/dist/plugins/text-color-plugin.js +56 -0
  178. package/dist/plugins/text-color-plugin.js.map +1 -0
  179. package/dist/plugins/underline-plugin.d.ts +37 -0
  180. package/dist/plugins/underline-plugin.d.ts.map +1 -0
  181. package/dist/plugins/underline-plugin.js +48 -0
  182. package/dist/plugins/underline-plugin.js.map +1 -0
  183. package/dist/presets.d.ts +95 -0
  184. package/dist/presets.d.ts.map +1 -0
  185. package/dist/presets.js +159 -0
  186. package/dist/presets.js.map +1 -0
  187. package/dist/renderer.d.ts +125 -0
  188. package/dist/renderer.d.ts.map +1 -0
  189. package/dist/renderer.js +415 -0
  190. package/dist/renderer.js.map +1 -0
  191. package/dist/scroll-to-cursor.d.ts +25 -0
  192. package/dist/scroll-to-cursor.d.ts.map +1 -0
  193. package/dist/scroll-to-cursor.js +59 -0
  194. package/dist/scroll-to-cursor.js.map +1 -0
  195. package/dist/selection-sync.d.ts +159 -0
  196. package/dist/selection-sync.d.ts.map +1 -0
  197. package/dist/selection-sync.js +527 -0
  198. package/dist/selection-sync.js.map +1 -0
  199. package/dist/shortcut-handler.d.ts +98 -0
  200. package/dist/shortcut-handler.d.ts.map +1 -0
  201. package/dist/shortcut-handler.js +155 -0
  202. package/dist/shortcut-handler.js.map +1 -0
  203. package/dist/toolbar.d.ts +103 -0
  204. package/dist/toolbar.d.ts.map +1 -0
  205. package/dist/toolbar.js +134 -0
  206. package/dist/toolbar.js.map +1 -0
  207. package/dist/trigger-manager.d.ts +205 -0
  208. package/dist/trigger-manager.d.ts.map +1 -0
  209. package/dist/trigger-manager.js +466 -0
  210. package/dist/trigger-manager.js.map +1 -0
  211. package/dist/types.d.ts +216 -0
  212. package/dist/types.d.ts.map +1 -0
  213. package/dist/types.js +2 -0
  214. package/dist/types.js.map +1 -0
  215. package/package.json +30 -0
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Block renderer registry — maps block types to DOM rendering functions.
3
+ *
4
+ * The renderer consults this registry when creating block elements to apply
5
+ * visual block-type effects (CSS classes, ARIA roles, data attributes).
6
+ * Built-in renderers handle heading, blockquote, code block, list item,
7
+ * horizontal rule, callout, image, and embed. Plugins can register custom
8
+ * renderers for additional block types.
9
+ *
10
+ * Additionally, the registry supports attr-based renderers that apply visual
11
+ * styling based on block attributes (e.g., alignment, indentation) regardless
12
+ * of block type.
13
+ *
14
+ * @module
15
+ */
16
+ /**
17
+ * Create a new block renderer registry.
18
+ *
19
+ * @returns An empty registry ready for renderer registration
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const registry = createBlockRendererRegistry();
24
+ * registry.register('heading', headingRenderer);
25
+ * registry.registerAttrRenderer('align', alignAttrRenderer);
26
+ * ```
27
+ */
28
+ export function createBlockRendererRegistry() {
29
+ const renderers = new Map();
30
+ const attrRenderers = new Map();
31
+ return {
32
+ register(blockType, renderer) {
33
+ renderers.set(blockType, renderer);
34
+ },
35
+ get(blockType) {
36
+ return renderers.get(blockType);
37
+ },
38
+ registerAttrRenderer(attrName, renderer) {
39
+ attrRenderers.set(attrName, renderer);
40
+ },
41
+ getAttrRenderer(attrName) {
42
+ return attrRenderers.get(attrName);
43
+ },
44
+ isAtomic(blockType) {
45
+ const renderer = renderers.get(blockType);
46
+ return renderer?.atomic === true;
47
+ },
48
+ applyToElement(el, block) {
49
+ const renderer = renderers.get(block.type);
50
+ if (renderer) {
51
+ renderer.apply(el, block);
52
+ if (renderer.atomic === true) {
53
+ el.setAttribute('contenteditable', 'false');
54
+ }
55
+ }
56
+ if (block.attrs) {
57
+ for (const [key, value] of Object.entries(block.attrs)) {
58
+ if (value == null)
59
+ continue;
60
+ const attrRenderer = attrRenderers.get(key);
61
+ if (attrRenderer) {
62
+ attrRenderer.apply(el, value);
63
+ }
64
+ }
65
+ }
66
+ },
67
+ };
68
+ }
69
+ // ---------------------------------------------------------------------------
70
+ // Built-in block renderers
71
+ // ---------------------------------------------------------------------------
72
+ /**
73
+ * Clamp a heading level to the valid range 1-6.
74
+ *
75
+ * @param level - The raw level value from block attrs
76
+ * @returns A valid heading level between 1 and 6
77
+ */
78
+ function clampHeadingLevel(level) {
79
+ if (typeof level !== 'number' || !Number.isFinite(level)) {
80
+ return 1;
81
+ }
82
+ return Math.max(1, Math.min(6, Math.floor(level)));
83
+ }
84
+ /**
85
+ * Built-in renderer for the `heading` block type.
86
+ *
87
+ * Adds CSS classes `rtif-heading` and `rtif-h{level}` where level comes
88
+ * from `block.attrs?.level` (defaults to 1, clamped to 1-6).
89
+ * Sets `data-heading-level`, `role="heading"`, and `aria-level` for
90
+ * accessibility.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * // Block: { type: 'heading', attrs: { level: 2 }, ... }
95
+ * // Result: <div class="rtif-heading rtif-h2" role="heading" aria-level="2" data-heading-level="2">
96
+ * ```
97
+ */
98
+ export const headingRenderer = {
99
+ apply(el, block) {
100
+ const level = clampHeadingLevel(block.attrs?.level);
101
+ el.classList.add('rtif-heading', `rtif-h${level}`);
102
+ el.setAttribute('data-heading-level', String(level));
103
+ el.setAttribute('role', 'heading');
104
+ el.setAttribute('aria-level', String(level));
105
+ },
106
+ };
107
+ /**
108
+ * Built-in renderer for the `blockquote` block type.
109
+ *
110
+ * Adds the `rtif-blockquote` CSS class to the block element.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * // Block: { type: 'blockquote', ... }
115
+ * // Result: <div class="rtif-blockquote">
116
+ * ```
117
+ */
118
+ export const blockquoteRenderer = {
119
+ apply(el) {
120
+ el.classList.add('rtif-blockquote');
121
+ },
122
+ };
123
+ /**
124
+ * Built-in renderer for the `code-block` block type.
125
+ *
126
+ * Adds the `rtif-code-block` CSS class. If `block.attrs?.language` is a
127
+ * non-empty string, sets a `data-language` attribute for syntax highlighting
128
+ * integration.
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * // Block: { type: 'code-block', attrs: { language: 'typescript' }, ... }
133
+ * // Result: <div class="rtif-code-block" data-language="typescript">
134
+ * ```
135
+ */
136
+ export const codeBlockRenderer = {
137
+ apply(el, block) {
138
+ el.classList.add('rtif-code-block');
139
+ const language = block.attrs?.language;
140
+ if (typeof language === 'string' && language.length > 0) {
141
+ el.setAttribute('data-language', language);
142
+ }
143
+ },
144
+ };
145
+ /**
146
+ * Built-in renderer for the `list-item` block type.
147
+ *
148
+ * Adds CSS classes `rtif-list-item` and either `rtif-list-bullet` or
149
+ * `rtif-list-ordered` based on `block.attrs?.listStyle` (defaults to
150
+ * `'bullet'`). Sets `data-list-depth` from `block.attrs?.depth` (defaults
151
+ * to 0) and `role="listitem"` for accessibility.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * // Block: { type: 'list-item', attrs: { listStyle: 'ordered', depth: 1 }, ... }
156
+ * // Result: <div class="rtif-list-item rtif-list-ordered" data-list-depth="1" role="listitem">
157
+ * ```
158
+ */
159
+ export const listItemRenderer = {
160
+ apply(el, block) {
161
+ el.classList.add('rtif-list-item');
162
+ const listStyle = block.attrs?.listStyle;
163
+ if (listStyle === 'ordered') {
164
+ el.classList.add('rtif-list-ordered');
165
+ }
166
+ else {
167
+ el.classList.add('rtif-list-bullet');
168
+ }
169
+ const depth = block.attrs?.depth;
170
+ const depthValue = typeof depth === 'number' && Number.isFinite(depth) ? Math.max(0, Math.floor(depth)) : 0;
171
+ el.setAttribute('data-list-depth', String(depthValue));
172
+ el.setAttribute('role', 'listitem');
173
+ },
174
+ };
175
+ /**
176
+ * Built-in renderer for the `hr` (horizontal rule) block type.
177
+ *
178
+ * Adds the `rtif-hr` CSS class and sets `role="separator"` for
179
+ * accessibility. Marked as atomic — the block is non-editable.
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * // Block: { type: 'hr', ... }
184
+ * // Result: <div class="rtif-hr" role="separator" contenteditable="false">
185
+ * ```
186
+ */
187
+ export const hrRenderer = {
188
+ apply(el) {
189
+ el.classList.add('rtif-hr');
190
+ el.setAttribute('role', 'separator');
191
+ },
192
+ atomic: true,
193
+ };
194
+ // ---------------------------------------------------------------------------
195
+ // Phase 7 block renderers
196
+ // ---------------------------------------------------------------------------
197
+ /**
198
+ * Resolve a callout variant string from block attrs, defaulting to `"info"`.
199
+ *
200
+ * @param variant - The raw variant value from block attrs
201
+ * @returns A valid variant string
202
+ */
203
+ function resolveCalloutVariant(variant) {
204
+ if (typeof variant === 'string' && variant.length > 0) {
205
+ return variant;
206
+ }
207
+ return 'info';
208
+ }
209
+ /**
210
+ * Built-in renderer for the `callout` block type.
211
+ *
212
+ * Creates a composite block with an icon span and a content container.
213
+ * The icon span has `contenteditable="false"` so it cannot be edited.
214
+ * The content div (class `rtif-callout-content`) is where inline spans
215
+ * are rendered, returned by {@link BlockRenderer.getContentContainer}.
216
+ *
217
+ * Supports a `variant` attribute (defaults to `"info"`) which is set as
218
+ * `data-variant` on the element. Implements {@link BlockRenderer.updateElement}
219
+ * for efficient in-place variant updates.
220
+ *
221
+ * @example
222
+ * ```ts
223
+ * // Block: { type: 'callout', attrs: { variant: 'warning' }, ... }
224
+ * // Result:
225
+ * // <div class="rtif-callout" data-variant="warning">
226
+ * // <span class="rtif-callout-icon" contenteditable="false"></span>
227
+ * // <div class="rtif-callout-content">
228
+ * // <!-- span elements rendered here -->
229
+ * // </div>
230
+ * // </div>
231
+ * ```
232
+ */
233
+ export const calloutRenderer = {
234
+ apply(el, block) {
235
+ const variant = resolveCalloutVariant(block.attrs?.variant);
236
+ el.classList.add('rtif-callout');
237
+ el.setAttribute('data-variant', variant);
238
+ const iconSpan = document.createElement('span');
239
+ iconSpan.className = 'rtif-callout-icon';
240
+ iconSpan.setAttribute('contenteditable', 'false');
241
+ el.appendChild(iconSpan);
242
+ const contentDiv = document.createElement('div');
243
+ contentDiv.className = 'rtif-callout-content';
244
+ el.appendChild(contentDiv);
245
+ },
246
+ getContentContainer(el) {
247
+ const content = el.querySelector('.rtif-callout-content');
248
+ // Defensive: if the content container is missing, fall back to the element itself
249
+ return content ?? el;
250
+ },
251
+ updateElement(el, block) {
252
+ const variant = resolveCalloutVariant(block.attrs?.variant);
253
+ el.setAttribute('data-variant', variant);
254
+ return true;
255
+ },
256
+ };
257
+ /**
258
+ * Built-in renderer for the `image` block type.
259
+ *
260
+ * Creates an atomic (non-editable) block containing an `<img>` element.
261
+ * Reads `src`, `alt`, `width`, and `height` from `block.attrs`.
262
+ * Sets `role="img"` on the block element for accessibility.
263
+ *
264
+ * Implements {@link BlockRenderer.updateElement} to efficiently update
265
+ * image attributes without full re-creation.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * // Block: { type: 'image', attrs: { src: '/photo.jpg', alt: 'A photo', width: 400 }, ... }
270
+ * // Result:
271
+ * // <div class="rtif-image" role="img" contenteditable="false">
272
+ * // <img src="/photo.jpg" alt="A photo" width="400">
273
+ * // </div>
274
+ * ```
275
+ */
276
+ export const imageRenderer = {
277
+ apply(el, block) {
278
+ el.classList.add('rtif-image');
279
+ el.setAttribute('role', 'img');
280
+ const img = document.createElement('img');
281
+ applyImageAttrs(img, block);
282
+ el.appendChild(img);
283
+ },
284
+ updateElement(el, block) {
285
+ const img = el.querySelector('img');
286
+ if (!img) {
287
+ return false;
288
+ }
289
+ applyImageAttrs(img, block);
290
+ return true;
291
+ },
292
+ atomic: true,
293
+ };
294
+ /**
295
+ * Apply image-related block attrs to an `<img>` element.
296
+ *
297
+ * @param img - The `<img>` element to update
298
+ * @param block - The block containing image attrs (src, alt, width, height)
299
+ */
300
+ function applyImageAttrs(img, block) {
301
+ const src = block.attrs?.src;
302
+ if (typeof src === 'string') {
303
+ img.setAttribute('src', src);
304
+ }
305
+ const alt = block.attrs?.alt;
306
+ if (typeof alt === 'string') {
307
+ img.setAttribute('alt', alt);
308
+ }
309
+ else {
310
+ img.removeAttribute('alt');
311
+ }
312
+ const width = block.attrs?.width;
313
+ if (typeof width === 'number' && width > 0) {
314
+ img.setAttribute('width', String(width));
315
+ }
316
+ else {
317
+ img.removeAttribute('width');
318
+ }
319
+ const height = block.attrs?.height;
320
+ if (typeof height === 'number' && height > 0) {
321
+ img.setAttribute('height', String(height));
322
+ }
323
+ else {
324
+ img.removeAttribute('height');
325
+ }
326
+ }
327
+ /**
328
+ * Built-in renderer for the `embed` block type.
329
+ *
330
+ * Creates an atomic (non-editable) block with a container div for embedded
331
+ * content (e.g., iframe, video, tweet). Sets `data-embed-url` from
332
+ * `block.attrs?.url`.
333
+ *
334
+ * @example
335
+ * ```ts
336
+ * // Block: { type: 'embed', attrs: { url: 'https://youtube.com/watch?v=abc' }, ... }
337
+ * // Result:
338
+ * // <div class="rtif-embed" data-embed-url="https://youtube.com/watch?v=abc" contenteditable="false">
339
+ * // <div class="rtif-embed-container"></div>
340
+ * // </div>
341
+ * ```
342
+ */
343
+ export const embedRenderer = {
344
+ apply(el, block) {
345
+ el.classList.add('rtif-embed');
346
+ const url = block.attrs?.url;
347
+ if (typeof url === 'string' && url.length > 0) {
348
+ el.setAttribute('data-embed-url', url);
349
+ }
350
+ const container = document.createElement('div');
351
+ container.className = 'rtif-embed-container';
352
+ el.appendChild(container);
353
+ },
354
+ atomic: true,
355
+ };
356
+ // ---------------------------------------------------------------------------
357
+ // Built-in block attr renderers
358
+ // ---------------------------------------------------------------------------
359
+ /**
360
+ * Built-in attr renderer for the `align` block attribute.
361
+ *
362
+ * Sets `text-align` on the block element when the value is a string
363
+ * (e.g., "left", "center", "right", "justify").
364
+ *
365
+ * @example
366
+ * ```ts
367
+ * // Block: { attrs: { align: 'center' }, ... }
368
+ * // Result: <div style="text-align: center;">
369
+ * ```
370
+ */
371
+ export const alignAttrRenderer = {
372
+ apply(el, value) {
373
+ if (typeof value === 'string') {
374
+ el.style.textAlign = value;
375
+ }
376
+ },
377
+ };
378
+ /**
379
+ * Built-in attr renderer for the `indent` block attribute.
380
+ *
381
+ * Sets `padding-left` on the block element based on the indent level.
382
+ * Each indent level adds 2em of padding. Values of 0 or less are ignored.
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * // Block: { attrs: { indent: 2 }, ... }
387
+ * // Result: <div style="padding-left: 4em;">
388
+ * ```
389
+ */
390
+ export const indentAttrRenderer = {
391
+ apply(el, value) {
392
+ if (typeof value === 'number' && value > 0) {
393
+ el.style.paddingLeft = `${value * 2}em`;
394
+ }
395
+ },
396
+ };
397
+ /**
398
+ * Register all built-in block renderers into a registry.
399
+ *
400
+ * Registers type renderers for: heading, blockquote, code-block, list-item,
401
+ * hr, callout, image, embed.
402
+ * Registers attr renderers for: align, indent.
403
+ *
404
+ * @param registry - The registry to populate
405
+ *
406
+ * @example
407
+ * ```ts
408
+ * const registry = createBlockRendererRegistry();
409
+ * registerBuiltinBlockRenderers(registry);
410
+ * ```
411
+ */
412
+ export function registerBuiltinBlockRenderers(registry) {
413
+ registry.register('heading', headingRenderer);
414
+ registry.register('blockquote', blockquoteRenderer);
415
+ registry.register('code_block', codeBlockRenderer);
416
+ registry.register('list-item', listItemRenderer);
417
+ registry.register('hr', hrRenderer);
418
+ registry.register('callout', calloutRenderer);
419
+ registry.register('image', imageRenderer);
420
+ registry.register('embed', embedRenderer);
421
+ registry.registerAttrRenderer('align', alignAttrRenderer);
422
+ registry.registerAttrRenderer('indent', indentAttrRenderer);
423
+ }
424
+ //# sourceMappingURL=block-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-renderer.js","sourceRoot":"","sources":["../src/block-renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Clipboard handling — copy, cut, and paste for the RTIF web editor.
3
+ *
4
+ * Handles copy and cut with both plain text and RTIF JSON clipboard formats.
5
+ * Paste can be delegated to the content pipeline (skipPaste mode) or handled
6
+ * directly for plain text fallback.
7
+ *
8
+ * @see docs/spec/platform-requirements.md section 4 (Clipboard)
9
+ */
10
+ import type { Document, Block, Selection, Operation } from '@rtif-sdk/core';
11
+ /**
12
+ * Dependencies injected into {@link ClipboardHandler}.
13
+ *
14
+ * This keeps the clipboard module decoupled from the editor wiring and
15
+ * makes it testable in isolation.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const deps: ClipboardDeps = {
20
+ * getSelection: () => engine.state.selection,
21
+ * getDoc: () => engine.state.doc,
22
+ * dispatch: (ops) => engine.dispatch(ops),
23
+ * isReadOnly: () => config.readOnly ?? false,
24
+ * isComposing: () => compositionHandler.isComposing(),
25
+ * generateBlockId: () => crypto.randomUUID(),
26
+ * skipPaste: true,
27
+ * };
28
+ * ```
29
+ */
30
+ export interface ClipboardDeps {
31
+ /** Return the current RTIF selection. */
32
+ readonly getSelection: () => Selection;
33
+ /** Return the current RTIF document. */
34
+ readonly getDoc: () => Document;
35
+ /** Dispatch one or more RTIF operations to the engine. */
36
+ readonly dispatch: (ops: Operation | Operation[]) => void;
37
+ /** Whether the editor is in read-only mode. */
38
+ readonly isReadOnly: () => boolean;
39
+ /** Whether an IME composition session is active. */
40
+ readonly isComposing: () => boolean;
41
+ /** Generate a unique block ID for split_block operations. */
42
+ readonly generateBlockId: () => string;
43
+ /**
44
+ * When true, the paste event listener is not attached.
45
+ * The content pipeline handles paste instead.
46
+ * Default: false.
47
+ */
48
+ readonly skipPaste?: boolean;
49
+ }
50
+ /**
51
+ * Extract plain text from a document range defined by absolute offsets.
52
+ *
53
+ * Walks blocks and spans within `[start, end)`, inserting `'\n'` at
54
+ * virtual block boundaries. This is a pure function with no side effects.
55
+ *
56
+ * @param doc - The RTIF document
57
+ * @param start - Start offset (inclusive)
58
+ * @param end - End offset (exclusive)
59
+ * @returns The plain text string within the range
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const doc = {
64
+ * version: 1,
65
+ * blocks: [
66
+ * { id: 'b1', type: 'text', spans: [{ text: 'hello' }] },
67
+ * { id: 'b2', type: 'text', spans: [{ text: 'world' }] },
68
+ * ],
69
+ * };
70
+ * extractPlainText(doc, 3, 8); // => 'lo\nwo'
71
+ * ```
72
+ */
73
+ export declare function extractPlainText(doc: Document, start: number, end: number): string;
74
+ /**
75
+ * Extract an RTIF block slice from a document range for clipboard serialization.
76
+ *
77
+ * Extracts blocks and spans within the absolute offset range `[start, end)`,
78
+ * preserving span marks and block attributes. The resulting blocks form a
79
+ * self-contained document fragment suitable for clipboard storage.
80
+ *
81
+ * @param doc - The RTIF document
82
+ * @param start - Start offset (inclusive)
83
+ * @param end - End offset (exclusive)
84
+ * @returns Array of extracted blocks with sliced spans
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * const doc = {
89
+ * version: 1,
90
+ * blocks: [
91
+ * {
92
+ * id: 'b1', type: 'text',
93
+ * spans: [
94
+ * { text: 'hello ', marks: { bold: true } },
95
+ * { text: 'world' },
96
+ * ],
97
+ * },
98
+ * ],
99
+ * };
100
+ * const blocks = extractRtifSlice(doc, 0, 5);
101
+ * // => [{ id: 'b1', type: 'text', spans: [{ text: 'hello', marks: { bold: true } }] }]
102
+ * ```
103
+ */
104
+ export declare function extractRtifSlice(doc: Document, start: number, end: number): Block[];
105
+ /**
106
+ * Generate the RTIF operations needed to delete a selection range.
107
+ *
108
+ * For same-block selections, produces a single `delete_text`. For cross-block
109
+ * selections, composes `delete_text` and `merge_block` operations to remove
110
+ * all content within the range and merge the remaining fragments.
111
+ *
112
+ * The generated operations are designed to be dispatched as a single batch.
113
+ * After each operation, the merge point stays at the same absolute offset
114
+ * (`start`), which makes the sequencing correct.
115
+ *
116
+ * @param doc - The current RTIF document
117
+ * @param selection - The selection to delete
118
+ * @returns An array of operations (may be empty if selection is collapsed)
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * // Single-block deletion
123
+ * const ops = deleteSelectionOps(doc, {
124
+ * anchor: { offset: 2 },
125
+ * focus: { offset: 5 },
126
+ * });
127
+ * // => [{ type: 'delete_text', offset: 2, count: 3 }]
128
+ * ```
129
+ */
130
+ export declare function deleteSelectionOps(doc: Document, selection: Selection): Operation[];
131
+ /**
132
+ * Handles copy, cut, and paste events on a contenteditable root element.
133
+ *
134
+ * Copy and cut write both `text/plain` and `application/x-rtif+json` to the
135
+ * clipboard. Paste handling can be delegated to the content pipeline by
136
+ * setting `skipPaste: true` in the deps.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * const handler = new ClipboardHandler(root, deps);
141
+ * handler.attach(); // start listening
142
+ * // ... later ...
143
+ * handler.detach(); // stop listening
144
+ * ```
145
+ */
146
+ export declare class ClipboardHandler {
147
+ private readonly _root;
148
+ private readonly _deps;
149
+ private _copyHandler;
150
+ private _cutHandler;
151
+ private _pasteHandler;
152
+ constructor(root: HTMLElement, deps: ClipboardDeps);
153
+ /**
154
+ * Attach copy, cut, and (optionally) paste event listeners to the root element.
155
+ *
156
+ * When `skipPaste` is true in the deps, the paste listener is not attached.
157
+ * The content pipeline handles paste instead.
158
+ *
159
+ * @example
160
+ * ```ts
161
+ * handler.attach();
162
+ * ```
163
+ */
164
+ attach(): void;
165
+ /**
166
+ * Remove all clipboard event listeners from the root element.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * handler.detach();
171
+ * ```
172
+ */
173
+ detach(): void;
174
+ private _onCopy;
175
+ private _onCut;
176
+ private _onPaste;
177
+ }
178
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAQ,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAclF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,YAAY,EAAE,MAAM,SAAS,CAAC;IAEvC,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,QAAQ,CAAC;IAEhC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,EAAE,KAAK,IAAI,CAAC;IAE1D,+CAA+C;IAC/C,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC;IAEnC,oDAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC;IAEpC,6DAA6D;IAC7D,QAAQ,CAAC,eAAe,EAAE,MAAM,MAAM,CAAC;IAEvC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,MAAM,CAiDR;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,KAAK,EAAE,CAoCT;AAoDD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,EACb,SAAS,EAAE,SAAS,GACnB,SAAS,EAAE,CA6Db;AAMD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,aAAa,CAA8C;gBAEvD,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa;IAKlD;;;;;;;;;;OAUG;IACH,MAAM,IAAI,IAAI;IAcd;;;;;;;OAOG;IACH,MAAM,IAAI,IAAI;IAmBd,OAAO,CAAC,OAAO;IAsBf,OAAO,CAAC,MAAM;IAuCd,OAAO,CAAC,QAAQ;CAqDjB"}