smartrte-core 0.2.1 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +23 -0
  3. package/dist/command.d.ts +8 -8
  4. package/dist/editor.d.ts +32 -0
  5. package/dist/editor.js +121 -0
  6. package/dist/foundation/annotations/index.d.ts +2 -0
  7. package/dist/foundation/annotations/index.js +2 -0
  8. package/dist/foundation/annotations/range.d.ts +54 -0
  9. package/dist/foundation/annotations/range.js +158 -0
  10. package/dist/foundation/annotations/types.d.ts +33 -0
  11. package/dist/foundation/annotations/types.js +1 -0
  12. package/dist/foundation/atom/commands.d.ts +11 -0
  13. package/dist/foundation/atom/commands.js +118 -0
  14. package/dist/foundation/atom/composition.d.ts +8 -0
  15. package/dist/foundation/atom/composition.js +22 -0
  16. package/dist/foundation/atom/declarations.d.ts +2 -0
  17. package/dist/foundation/atom/declarations.js +17 -0
  18. package/dist/foundation/atom/formats.d.ts +21 -0
  19. package/dist/foundation/atom/formats.js +77 -0
  20. package/dist/foundation/atom/index.d.ts +10 -0
  21. package/dist/foundation/atom/index.js +10 -0
  22. package/dist/foundation/atom/lifecycle.d.ts +12 -0
  23. package/dist/foundation/atom/lifecycle.js +41 -0
  24. package/dist/foundation/atom/persistence.d.ts +3 -0
  25. package/dist/foundation/atom/persistence.js +20 -0
  26. package/dist/foundation/atom/plugin.d.ts +20 -0
  27. package/dist/foundation/atom/plugin.js +55 -0
  28. package/dist/foundation/atom/schema.d.ts +3 -0
  29. package/dist/foundation/atom/schema.js +29 -0
  30. package/dist/foundation/atom/security.d.ts +10 -0
  31. package/dist/foundation/atom/security.js +20 -0
  32. package/dist/foundation/atom/selection.d.ts +3 -0
  33. package/dist/foundation/atom/selection.js +4 -0
  34. package/dist/foundation/atom/types.d.ts +47 -0
  35. package/dist/foundation/atom/types.js +1 -0
  36. package/dist/foundation/block/commands.d.ts +17 -0
  37. package/dist/foundation/block/commands.js +197 -0
  38. package/dist/foundation/block/formats.d.ts +33 -0
  39. package/dist/foundation/block/formats.js +57 -0
  40. package/dist/foundation/block/index.d.ts +5 -0
  41. package/dist/foundation/block/index.js +5 -0
  42. package/dist/foundation/block/input.d.ts +19 -0
  43. package/dist/foundation/block/input.js +67 -0
  44. package/dist/foundation/block/plugin.d.ts +26 -0
  45. package/dist/foundation/block/plugin.js +79 -0
  46. package/dist/foundation/block/schema.d.ts +49 -0
  47. package/dist/foundation/block/schema.js +27 -0
  48. package/dist/foundation/block/types.d.ts +40 -0
  49. package/dist/foundation/block/types.js +1 -0
  50. package/dist/foundation/boundaries.d.ts +11 -0
  51. package/dist/foundation/boundaries.js +117 -0
  52. package/dist/foundation/clipboard/detection.d.ts +3 -0
  53. package/dist/foundation/clipboard/detection.js +29 -0
  54. package/dist/foundation/clipboard/diagnostics.d.ts +24 -0
  55. package/dist/foundation/clipboard/diagnostics.js +59 -0
  56. package/dist/foundation/clipboard/index.d.ts +8 -0
  57. package/dist/foundation/clipboard/index.js +8 -0
  58. package/dist/foundation/clipboard/insertion.d.ts +22 -0
  59. package/dist/foundation/clipboard/insertion.js +210 -0
  60. package/dist/foundation/clipboard/normalizers.d.ts +8 -0
  61. package/dist/foundation/clipboard/normalizers.js +229 -0
  62. package/dist/foundation/clipboard/pipeline.d.ts +13 -0
  63. package/dist/foundation/clipboard/pipeline.js +58 -0
  64. package/dist/foundation/clipboard/sanitizer.d.ts +3 -0
  65. package/dist/foundation/clipboard/sanitizer.js +58 -0
  66. package/dist/foundation/clipboard/serialization.d.ts +5 -0
  67. package/dist/foundation/clipboard/serialization.js +33 -0
  68. package/dist/foundation/clipboard/transfer.d.ts +11 -0
  69. package/dist/foundation/clipboard/transfer.js +108 -0
  70. package/dist/foundation/clipboard/types.d.ts +65 -0
  71. package/dist/foundation/clipboard/types.js +1 -0
  72. package/dist/foundation/collab/index.d.ts +3 -0
  73. package/dist/foundation/collab/index.js +3 -0
  74. package/dist/foundation/collab/rebase.d.ts +28 -0
  75. package/dist/foundation/collab/rebase.js +82 -0
  76. package/dist/foundation/collab/transform.d.ts +73 -0
  77. package/dist/foundation/collab/transform.js +251 -0
  78. package/dist/foundation/collab/types.d.ts +52 -0
  79. package/dist/foundation/collab/types.js +1 -0
  80. package/dist/foundation/comments/fromSelection.d.ts +16 -0
  81. package/dist/foundation/comments/fromSelection.js +24 -0
  82. package/dist/foundation/comments/index.d.ts +3 -0
  83. package/dist/foundation/comments/index.js +3 -0
  84. package/dist/foundation/comments/rebase.d.ts +31 -0
  85. package/dist/foundation/comments/rebase.js +43 -0
  86. package/dist/foundation/comments/types.d.ts +37 -0
  87. package/dist/foundation/comments/types.js +1 -0
  88. package/dist/foundation/diff/documentDiff.d.ts +12 -0
  89. package/dist/foundation/diff/documentDiff.js +148 -0
  90. package/dist/foundation/diff/index.d.ts +3 -0
  91. package/dist/foundation/diff/index.js +3 -0
  92. package/dist/foundation/diff/textDiff.d.ts +12 -0
  93. package/dist/foundation/diff/textDiff.js +114 -0
  94. package/dist/foundation/diff/types.d.ts +74 -0
  95. package/dist/foundation/diff/types.js +1 -0
  96. package/dist/foundation/editor.d.ts +138 -0
  97. package/dist/foundation/editor.js +398 -0
  98. package/dist/foundation/formats/codec.d.ts +45 -0
  99. package/dist/foundation/formats/codec.js +1 -0
  100. package/dist/foundation/formats/docx/export.d.ts +4 -0
  101. package/dist/foundation/formats/docx/export.js +285 -0
  102. package/dist/foundation/formats/docx/import.d.ts +9 -0
  103. package/dist/foundation/formats/docx/import.js +37 -0
  104. package/dist/foundation/formats/docx/nestingGuard.d.ts +40 -0
  105. package/dist/foundation/formats/docx/nestingGuard.js +66 -0
  106. package/dist/foundation/formats/docx/portableAtoms.d.ts +4 -0
  107. package/dist/foundation/formats/docx/portableAtoms.js +66 -0
  108. package/dist/foundation/formats/docx/styledImport.d.ts +10 -0
  109. package/dist/foundation/formats/docx/styledImport.js +205 -0
  110. package/dist/foundation/formats/featureCodecs.d.ts +37 -0
  111. package/dist/foundation/formats/featureCodecs.js +69 -0
  112. package/dist/foundation/formats/fidelity.d.ts +31 -0
  113. package/dist/foundation/formats/fidelity.js +123 -0
  114. package/dist/foundation/formats/index.d.ts +7 -0
  115. package/dist/foundation/formats/index.js +7 -0
  116. package/dist/foundation/formats/pdf/format.d.ts +29 -0
  117. package/dist/foundation/formats/pdf/format.js +190 -0
  118. package/dist/foundation/history.d.ts +20 -0
  119. package/dist/foundation/history.js +121 -0
  120. package/dist/foundation/identity.d.ts +8 -0
  121. package/dist/foundation/identity.js +30 -0
  122. package/dist/foundation/index.d.ts +30 -0
  123. package/dist/foundation/index.js +30 -0
  124. package/dist/foundation/list/commands.d.ts +28 -0
  125. package/dist/foundation/list/commands.js +422 -0
  126. package/dist/foundation/list/formats.d.ts +20 -0
  127. package/dist/foundation/list/formats.js +795 -0
  128. package/dist/foundation/list/index.d.ts +8 -0
  129. package/dist/foundation/list/index.js +8 -0
  130. package/dist/foundation/list/input.d.ts +35 -0
  131. package/dist/foundation/list/input.js +320 -0
  132. package/dist/foundation/list/plugin.d.ts +21 -0
  133. package/dist/foundation/list/plugin.js +71 -0
  134. package/dist/foundation/list/presets.d.ts +16 -0
  135. package/dist/foundation/list/presets.js +21 -0
  136. package/dist/foundation/list/schema.d.ts +35 -0
  137. package/dist/foundation/list/schema.js +29 -0
  138. package/dist/foundation/list/shadow.d.ts +54 -0
  139. package/dist/foundation/list/shadow.js +75 -0
  140. package/dist/foundation/list/shadowCorpus.d.ts +10 -0
  141. package/dist/foundation/list/shadowCorpus.js +41 -0
  142. package/dist/foundation/list/types.d.ts +50 -0
  143. package/dist/foundation/list/types.js +1 -0
  144. package/dist/foundation/mapping.d.ts +11 -0
  145. package/dist/foundation/mapping.js +40 -0
  146. package/dist/foundation/marks/canonical.d.ts +9 -0
  147. package/dist/foundation/marks/canonical.js +72 -0
  148. package/dist/foundation/marks/commands.d.ts +21 -0
  149. package/dist/foundation/marks/commands.js +136 -0
  150. package/dist/foundation/marks/dom.d.ts +2 -0
  151. package/dist/foundation/marks/dom.js +46 -0
  152. package/dist/foundation/marks/editor.d.ts +8 -0
  153. package/dist/foundation/marks/editor.js +50 -0
  154. package/dist/foundation/marks/formats.d.ts +37 -0
  155. package/dist/foundation/marks/formats.js +49 -0
  156. package/dist/foundation/marks/hardBreak.d.ts +8 -0
  157. package/dist/foundation/marks/hardBreak.js +80 -0
  158. package/dist/foundation/marks/index.d.ts +11 -0
  159. package/dist/foundation/marks/index.js +11 -0
  160. package/dist/foundation/marks/normalization.d.ts +2 -0
  161. package/dist/foundation/marks/normalization.js +49 -0
  162. package/dist/foundation/marks/plugin.d.ts +14 -0
  163. package/dist/foundation/marks/plugin.js +63 -0
  164. package/dist/foundation/marks/resolveMarkRun.d.ts +8 -0
  165. package/dist/foundation/marks/resolveMarkRun.js +47 -0
  166. package/dist/foundation/marks/schema.d.ts +167 -0
  167. package/dist/foundation/marks/schema.js +50 -0
  168. package/dist/foundation/marks/stored.d.ts +2 -0
  169. package/dist/foundation/marks/stored.js +31 -0
  170. package/dist/foundation/marks/types.d.ts +40 -0
  171. package/dist/foundation/marks/types.js +1 -0
  172. package/dist/foundation/modelDom.d.ts +38 -0
  173. package/dist/foundation/modelDom.js +327 -0
  174. package/dist/foundation/normalization.d.ts +22 -0
  175. package/dist/foundation/normalization.js +132 -0
  176. package/dist/foundation/operations.d.ts +28 -0
  177. package/dist/foundation/operations.js +568 -0
  178. package/dist/foundation/plugin/builtins.d.ts +10 -0
  179. package/dist/foundation/plugin/builtins.js +27 -0
  180. package/dist/foundation/plugin/dispatch.d.ts +33 -0
  181. package/dist/foundation/plugin/dispatch.js +34 -0
  182. package/dist/foundation/plugin/index.d.ts +4 -0
  183. package/dist/foundation/plugin/index.js +4 -0
  184. package/dist/foundation/plugin/registry.d.ts +36 -0
  185. package/dist/foundation/plugin/registry.js +115 -0
  186. package/dist/foundation/plugin/types.d.ts +141 -0
  187. package/dist/foundation/plugin/types.js +13 -0
  188. package/dist/foundation/positions.d.ts +20 -0
  189. package/dist/foundation/positions.js +121 -0
  190. package/dist/foundation/schema.d.ts +57 -0
  191. package/dist/foundation/schema.js +371 -0
  192. package/dist/foundation/schemaBuilder.d.ts +41 -0
  193. package/dist/foundation/schemaBuilder.js +96 -0
  194. package/dist/foundation/scope/index.d.ts +2 -0
  195. package/dist/foundation/scope/index.js +2 -0
  196. package/dist/foundation/scope/resolveScope.d.ts +13 -0
  197. package/dist/foundation/scope/resolveScope.js +860 -0
  198. package/dist/foundation/scope/types.d.ts +122 -0
  199. package/dist/foundation/scope/types.js +1 -0
  200. package/dist/foundation/security/urlPolicy.d.ts +21 -0
  201. package/dist/foundation/security/urlPolicy.js +75 -0
  202. package/dist/foundation/structural/contentTarget.d.ts +21 -0
  203. package/dist/foundation/structural/contentTarget.js +51 -0
  204. package/dist/foundation/structural/index.d.ts +2 -0
  205. package/dist/foundation/structural/index.js +2 -0
  206. package/dist/foundation/structural/move.d.ts +7 -0
  207. package/dist/foundation/structural/move.js +35 -0
  208. package/dist/foundation/suggestions/index.d.ts +3 -0
  209. package/dist/foundation/suggestions/index.js +3 -0
  210. package/dist/foundation/suggestions/inline.d.ts +64 -0
  211. package/dist/foundation/suggestions/inline.js +116 -0
  212. package/dist/foundation/suggestions/structural.d.ts +40 -0
  213. package/dist/foundation/suggestions/structural.js +47 -0
  214. package/dist/foundation/suggestions/types.d.ts +19 -0
  215. package/dist/foundation/suggestions/types.js +1 -0
  216. package/dist/foundation/surface/index.d.ts +3 -0
  217. package/dist/foundation/surface/index.js +3 -0
  218. package/dist/foundation/surface/input.d.ts +127 -0
  219. package/dist/foundation/surface/input.js +1761 -0
  220. package/dist/foundation/surface/renderer.d.ts +84 -0
  221. package/dist/foundation/surface/renderer.js +974 -0
  222. package/dist/foundation/surface/types.d.ts +48 -0
  223. package/dist/foundation/surface/types.js +1 -0
  224. package/dist/foundation/table/commands.d.ts +31 -0
  225. package/dist/foundation/table/commands.js +470 -0
  226. package/dist/foundation/table/formats.d.ts +26 -0
  227. package/dist/foundation/table/formats.js +40 -0
  228. package/dist/foundation/table/grid.d.ts +8 -0
  229. package/dist/foundation/table/grid.js +178 -0
  230. package/dist/foundation/table/index.d.ts +6 -0
  231. package/dist/foundation/table/index.js +6 -0
  232. package/dist/foundation/table/plugin.d.ts +23 -0
  233. package/dist/foundation/table/plugin.js +119 -0
  234. package/dist/foundation/table/schema.d.ts +2 -0
  235. package/dist/foundation/table/schema.js +38 -0
  236. package/dist/foundation/table/selection.d.ts +7 -0
  237. package/dist/foundation/table/selection.js +35 -0
  238. package/dist/foundation/table/types.d.ts +109 -0
  239. package/dist/foundation/table/types.js +1 -0
  240. package/dist/foundation/transactions.d.ts +41 -0
  241. package/dist/foundation/transactions.js +90 -0
  242. package/dist/foundation/types.d.ts +261 -0
  243. package/dist/foundation/types.js +1 -0
  244. package/dist/foundation/versioning/diffVersions.d.ts +5 -0
  245. package/dist/foundation/versioning/diffVersions.js +3 -0
  246. package/dist/foundation/versioning/index.d.ts +2 -0
  247. package/dist/foundation/versioning/index.js +2 -0
  248. package/dist/foundation/versioning/types.d.ts +17 -0
  249. package/dist/foundation/versioning/types.js +1 -0
  250. package/dist/history.d.ts +20 -0
  251. package/dist/history.js +165 -0
  252. package/dist/html/compatibility.js +7 -1
  253. package/dist/index.d.ts +1 -14
  254. package/dist/index.js +9 -14
  255. package/dist/legacy/index.d.ts +45 -0
  256. package/dist/legacy/index.js +45 -0
  257. package/dist/legacyCommands/alignment.d.ts +20 -0
  258. package/dist/{commands → legacyCommands}/alignment.js +17 -0
  259. package/dist/legacyCommands/blocks.d.ts +14 -0
  260. package/dist/legacyCommands/blocks.js +105 -0
  261. package/dist/legacyCommands/checklist.d.ts +11 -0
  262. package/dist/legacyCommands/checklist.js +45 -0
  263. package/dist/legacyCommands/code.d.ts +15 -0
  264. package/dist/{commands → legacyCommands}/code.js +25 -1
  265. package/dist/legacyCommands/formula.d.ts +7 -0
  266. package/dist/legacyCommands/formula.js +67 -0
  267. package/dist/legacyCommands/inlineAtoms.d.ts +7 -0
  268. package/dist/legacyCommands/inlineAtoms.js +58 -0
  269. package/dist/legacyCommands/list.d.ts +38 -0
  270. package/dist/legacyCommands/list.js +477 -0
  271. package/dist/{commands → legacyCommands}/marks.d.ts +3 -0
  272. package/dist/{commands → legacyCommands}/marks.js +28 -11
  273. package/dist/legacyCommands/media.d.ts +31 -0
  274. package/dist/legacyCommands/media.js +205 -0
  275. package/dist/legacyCommands/move.d.ts +14 -0
  276. package/dist/legacyCommands/move.js +91 -0
  277. package/dist/legacyCommands/table.d.ts +91 -0
  278. package/dist/legacyCommands/table.js +547 -0
  279. package/dist/listPresets.d.ts +12 -0
  280. package/dist/listPresets.js +6 -0
  281. package/dist/listScope.d.ts +27 -0
  282. package/dist/listScope.js +104 -0
  283. package/dist/markdown/compatibility.d.ts +2 -0
  284. package/dist/markdown/compatibility.js +94 -2
  285. package/dist/marks.d.ts +5 -5
  286. package/dist/model.d.ts +65 -9
  287. package/dist/plugin.d.ts +15 -0
  288. package/dist/plugin.js +31 -0
  289. package/dist/plugins/alignment.d.ts +3 -0
  290. package/dist/plugins/alignment.js +6 -0
  291. package/dist/plugins/basicFormatting.d.ts +9 -0
  292. package/dist/plugins/basicFormatting.js +24 -0
  293. package/dist/plugins/blockType.d.ts +3 -0
  294. package/dist/plugins/blockType.js +6 -0
  295. package/dist/plugins/blockquote.d.ts +3 -0
  296. package/dist/plugins/blockquote.js +6 -0
  297. package/dist/plugins/checklist.d.ts +3 -0
  298. package/dist/plugins/checklist.js +10 -0
  299. package/dist/plugins/codeBlock.d.ts +3 -0
  300. package/dist/plugins/codeBlock.js +6 -0
  301. package/dist/plugins/formula.d.ts +3 -0
  302. package/dist/plugins/formula.js +9 -0
  303. package/dist/plugins/list.d.ts +3 -0
  304. package/dist/plugins/list.js +10 -0
  305. package/dist/plugins/media.d.ts +3 -0
  306. package/dist/plugins/media.js +12 -0
  307. package/dist/plugins/move.d.ts +3 -0
  308. package/dist/plugins/move.js +9 -0
  309. package/dist/plugins/table.d.ts +3 -0
  310. package/dist/plugins/table.js +22 -0
  311. package/dist/preset.d.ts +12 -0
  312. package/dist/preset.js +40 -0
  313. package/dist/schema.d.ts +22 -0
  314. package/dist/schema.js +333 -0
  315. package/dist/security/urlPolicy.d.ts +1 -17
  316. package/dist/security/urlPolicy.js +3 -69
  317. package/dist/selection.d.ts +1 -1
  318. package/dist/selectionMapping.d.ts +4 -0
  319. package/dist/selectionMapping.js +222 -0
  320. package/dist/table.d.ts +3 -3
  321. package/dist/transaction.d.ts +35 -12
  322. package/dist/transaction.js +174 -28
  323. package/dist/tree.d.ts +8 -2
  324. package/dist/tree.js +44 -0
  325. package/package.json +27 -8
  326. package/dist/commands/alignment.d.ts +0 -18
  327. package/dist/commands/code.d.ts +0 -13
  328. package/dist/commands/list.d.ts +0 -20
  329. package/dist/commands/list.js +0 -52
@@ -0,0 +1,17 @@
1
+ import { safeFormulaSource, sanitizeAtomSource } from "./security.js";
2
+ const positiveDimensions = (attrs) => [attrs.width, attrs.height].every((value) => value === undefined || Number.isFinite(value) && Number(value) > 0 && Number(value) <= 100000);
3
+ const imageValid = (attrs) => typeof attrs.alt === "string"
4
+ && (attrs.alt.length > 0 || attrs.decorative === true)
5
+ && positiveDimensions(attrs)
6
+ && Boolean(sanitizeAtomSource(String(attrs.src || ""), { kind: "image", allowBlobPreview: attrs.status === "pending" }));
7
+ const formulaValid = (attrs) => safeFormulaSource(attrs.source) && (attrs.notation === "latex" || attrs.notation === "mathml");
8
+ const mediaValid = (kind) => (attrs) => positiveDimensions(attrs)
9
+ && Boolean(sanitizeAtomSource(String(attrs.src || ""), { kind, allowBlobPreview: attrs.status === "pending" }));
10
+ export const atomDeclarations = [
11
+ { type: "image", kind: "image", group: "inline", validate: imageValid },
12
+ { type: "block_image", kind: "image", group: "block", validate: imageValid },
13
+ { type: "formula", kind: "formula", group: "inline", validate: formulaValid },
14
+ { type: "block_formula", kind: "formula", group: "block", validate: formulaValid },
15
+ { type: "video", kind: "video", group: "block", validate: mediaValid("video") },
16
+ { type: "audio", kind: "audio", group: "block", validate: mediaValid("audio") },
17
+ ];
@@ -0,0 +1,21 @@
1
+ import type { SmartElementNode } from "../types.js";
2
+ export declare const atomToHtml: (node: SmartElementNode) => string;
3
+ export declare const atomFromHtmlElement: (element: Element) => SmartElementNode | null;
4
+ export declare const atomToMarkdown: (node: SmartElementNode) => string;
5
+ export interface AtomDocxRun {
6
+ readonly kind: "image" | "text";
7
+ readonly source: string;
8
+ readonly alt?: string;
9
+ }
10
+ /**
11
+ * Matches packages/core/src/foundation/formats/docx/export.ts's actual
12
+ * behavior (SS2.1): formulas are written as literal LaTeX text inside an
13
+ * <m:oMath> zone, not translated to real OMML and not rendered as an
14
+ * image. Word will show the raw LaTeX string, not typeset math - this is
15
+ * `kind: "text"`, not `"image"`, to describe that honestly.
16
+ */
17
+ export declare const atomToDocx: (node: SmartElementNode) => AtomDocxRun;
18
+ export declare const atomToPdf: (node: SmartElementNode) => {
19
+ kind: "image" | "text";
20
+ value: string;
21
+ };
@@ -0,0 +1,77 @@
1
+ import { createNodeId } from "../identity.js";
2
+ import { sanitizeAtomSource } from "./security.js";
3
+ const escape = (value) => String(value ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
4
+ const attr = (name, value) => value === undefined ? "" : ` ${name}="${escape(value)}"`;
5
+ const dimensions = (node) => `${attr("width", node.attrs?.width)}${attr("height", node.attrs?.height)}`;
6
+ export const atomToHtml = (node) => {
7
+ if (node.type === "image" || node.type === "block_image") {
8
+ const src = sanitizeAtomSource(String(node.attrs?.src || ""), { kind: "image", allowBlobPreview: node.attrs?.status === "pending" }) || "";
9
+ return `<img data-smart-id="${escape(node.id)}" data-smart-type="${node.type}" src="${escape(src)}" alt="${escape(node.attrs?.alt)}"${dimensions(node)}${attr("data-smart-status", node.attrs?.status || "ready")}${node.attrs?.decorative === true ? ' data-smart-decorative="true"' : ""}${attr("data-smart-align", node.attrs?.align)}>`;
10
+ }
11
+ if (node.type === "formula" || node.type === "block_formula") {
12
+ const tag = node.type === "formula" ? "span" : "div";
13
+ return `<${tag} data-smart-id="${escape(node.id)}" data-smart-type="${node.type}" data-smart-notation="${escape(node.attrs?.notation || "latex")}" data-smart-formula="${escape(node.attrs?.source)}" role="math" aria-label="${escape(`Mathematical formula: ${String(node.attrs?.source || "")}`)}"></${tag}>`;
14
+ }
15
+ if (node.type === "video" || node.type === "audio") {
16
+ const src = sanitizeAtomSource(String(node.attrs?.src || ""), { kind: node.type }) || "";
17
+ return `<${node.type} data-smart-id="${escape(node.id)}" data-smart-type="${node.type}" src="${escape(src)}" controls${dimensions(node)}${node.type === "video" ? attr("poster", node.attrs?.poster) : ""}></${node.type}>`;
18
+ }
19
+ if (node.type === "divider")
20
+ return `<hr data-smart-id="${escape(node.id)}" data-smart-type="divider">`;
21
+ throw new Error(`Unsupported atom type "${node.type}".`);
22
+ };
23
+ export const atomFromHtmlElement = (element) => {
24
+ const declared = element.getAttribute("data-smart-type");
25
+ const type = declared || (element.tagName === "IMG" ? "image" : element.tagName.toLowerCase());
26
+ const id = element.getAttribute("data-smart-id") || createNodeId();
27
+ const number = (name) => { const value = Number(element.getAttribute(name)); return Number.isFinite(value) && value > 0 ? value : undefined; };
28
+ if (type === "image" || type === "block_image") {
29
+ const src = sanitizeAtomSource(element.getAttribute("src"), { kind: "image" });
30
+ if (!src)
31
+ return null;
32
+ return { type, id, attrs: { src, alt: element.getAttribute("alt") || "", status: element.getAttribute("data-smart-status") || "ready", ...(element.getAttribute("data-smart-decorative") === "true" ? { decorative: true } : {}), ...(element.getAttribute("data-smart-align") ? { align: element.getAttribute("data-smart-align") } : {}), ...(number("width") ? { width: number("width") } : {}), ...(number("height") ? { height: number("height") } : {}) } };
33
+ }
34
+ if (type === "formula" || type === "block_formula") {
35
+ return { type, id, attrs: { source: element.getAttribute("data-smart-formula") || "", notation: element.getAttribute("data-smart-notation") === "mathml" ? "mathml" : "latex" } };
36
+ }
37
+ if (type === "video" || type === "audio") {
38
+ const src = sanitizeAtomSource(element.getAttribute("src"), { kind: type });
39
+ if (!src)
40
+ return null;
41
+ return { type, id, attrs: { src, status: "ready", ...(type === "video" && element.getAttribute("poster") ? { poster: element.getAttribute("poster") } : {}), ...(number("width") ? { width: number("width") } : {}), ...(number("height") ? { height: number("height") } : {}) } };
42
+ }
43
+ if (type === "divider" || element.tagName === "HR")
44
+ return { type: "divider", id };
45
+ return null;
46
+ };
47
+ export const atomToMarkdown = (node) => {
48
+ if (node.type === "image" || node.type === "block_image")
49
+ return `![${String(node.attrs?.alt || "")}](${String(node.attrs?.src || "")})`;
50
+ if (node.type === "formula" || node.type === "block_formula")
51
+ return node.type === "formula" ? `$${String(node.attrs?.source || "")}$` : `$$\n${String(node.attrs?.source || "")}\n$$`;
52
+ // Media is unsupported in Markdown. Preserve a readable link instead of dropping content.
53
+ if (node.type === "video" || node.type === "audio")
54
+ return `[${node.type}: ${String(node.attrs?.src || "")}](${String(node.attrs?.src || "")})`;
55
+ if (node.type === "divider")
56
+ return "---";
57
+ return "";
58
+ };
59
+ /**
60
+ * Matches packages/core/src/foundation/formats/docx/export.ts's actual
61
+ * behavior (SS2.1): formulas are written as literal LaTeX text inside an
62
+ * <m:oMath> zone, not translated to real OMML and not rendered as an
63
+ * image. Word will show the raw LaTeX string, not typeset math - this is
64
+ * `kind: "text"`, not `"image"`, to describe that honestly.
65
+ */
66
+ export const atomToDocx = (node) => node.type === "formula" || node.type === "block_formula"
67
+ ? { kind: "text", source: String(node.attrs?.source || "") }
68
+ : node.type === "image" || node.type === "block_image"
69
+ ? { kind: "image", source: String(node.attrs?.src || ""), alt: String(node.attrs?.alt || "") }
70
+ : node.type === "divider"
71
+ ? { kind: "text", source: "---" }
72
+ : { kind: "text", source: `[${node.type}: ${String(node.attrs?.src || "")}]` };
73
+ export const atomToPdf = (node) => node.type === "formula" || node.type === "block_formula" ? { kind: "text", value: String(node.attrs?.source || "") }
74
+ : node.type === "video" ? { kind: "image", value: String(node.attrs?.poster || node.attrs?.src || "") }
75
+ : node.type === "image" || node.type === "block_image" ? { kind: "image", value: String(node.attrs?.src || "") }
76
+ : node.type === "divider" ? { kind: "text", value: "---" }
77
+ : { kind: "text", value: String(node.attrs?.src || "") };
@@ -0,0 +1,10 @@
1
+ export * from "./types.js";
2
+ export * from "./schema.js";
3
+ export * from "./security.js";
4
+ export * from "./declarations.js";
5
+ export * from "./commands.js";
6
+ export * from "./composition.js";
7
+ export * from "./persistence.js";
8
+ export * from "./lifecycle.js";
9
+ export * from "./selection.js";
10
+ export * from "./formats.js";
@@ -0,0 +1,10 @@
1
+ export * from "./types.js";
2
+ export * from "./schema.js";
3
+ export * from "./security.js";
4
+ export * from "./declarations.js";
5
+ export * from "./commands.js";
6
+ export * from "./composition.js";
7
+ export * from "./persistence.js";
8
+ export * from "./lifecycle.js";
9
+ export * from "./selection.js";
10
+ export * from "./formats.js";
@@ -0,0 +1,12 @@
1
+ import type { FoundationEditor } from "../editor.js";
2
+ export interface AtomUploadCompletion {
3
+ readonly src?: string;
4
+ readonly id?: string;
5
+ readonly error?: string;
6
+ }
7
+ /** Applies upload completion outside the insertion transaction and never resurrects deleted nodes. */
8
+ export declare const completeAtomUpload: (editor: FoundationEditor, nodeId: string, completion: AtomUploadCompletion) => boolean;
9
+ export declare const runAtomUpload: (editor: FoundationEditor, nodeId: string, upload: () => Promise<{
10
+ src: string;
11
+ id?: string;
12
+ }>) => Promise<boolean>;
@@ -0,0 +1,41 @@
1
+ import { isTextNode } from "../identity.js";
2
+ import { atomDeclarations } from "./declarations.js";
3
+ import { sanitizeAtomSource } from "./security.js";
4
+ /** Applies upload completion outside the insertion transaction and never resurrects deleted nodes. */
5
+ export const completeAtomUpload = (editor, nodeId, completion) => {
6
+ const resolved = editor.positions.positionOf(nodeId);
7
+ if (!resolved)
8
+ return false;
9
+ const index = resolved.kind === "inline" ? (() => {
10
+ let offset = 0;
11
+ for (let childIndex = 0; childIndex < (resolved.parent.children?.length || 0); childIndex += 1) {
12
+ const child = resolved.parent.children[childIndex];
13
+ if (!isTextNode(child) && child.id === nodeId && offset === resolved.pos.offset)
14
+ return childIndex;
15
+ offset += isTextNode(child) ? child.text.length : 1;
16
+ }
17
+ return -1;
18
+ })() : resolved.pos.offset;
19
+ const node = resolved.parent.children?.[index];
20
+ if (!node || isTextNode(node) || node.id !== nodeId)
21
+ return false;
22
+ const before = node.attrs || {};
23
+ const declaration = atomDeclarations.find((entry) => entry.type === node.type);
24
+ const safeSource = completion.src && declaration && declaration.kind !== "formula"
25
+ ? sanitizeAtomSource(completion.src, { kind: declaration.kind }) : null;
26
+ const after = safeSource
27
+ ? { ...before, src: safeSource, status: "ready", ...(completion.id ? { uploadId: completion.id } : { uploadId: undefined }), error: undefined }
28
+ : { ...before, status: "error", uploadId: undefined, error: completion.error || "Upload failed" };
29
+ editor.transact((transaction) => transaction.operations.push({
30
+ type: "setNodeAttributes", pos: { path: [...resolved.pos.path, index], offset: 0 }, before, after: Object.fromEntries(Object.entries(after).filter(([, value]) => value !== undefined)),
31
+ }), { source: "api", addToHistory: false });
32
+ return true;
33
+ };
34
+ export const runAtomUpload = async (editor, nodeId, upload) => {
35
+ try {
36
+ return completeAtomUpload(editor, nodeId, await upload());
37
+ }
38
+ catch (error) {
39
+ return completeAtomUpload(editor, nodeId, { error: error instanceof Error ? error.message : "Upload failed" });
40
+ }
41
+ };
@@ -0,0 +1,3 @@
1
+ import type { PersistedEditorDocument } from "../types.js";
2
+ export declare const assertNoBlobAtomUrls: (envelope: PersistedEditorDocument) => void;
3
+ export declare const serializePersistedEditorDocument: (envelope: PersistedEditorDocument) => string;
@@ -0,0 +1,20 @@
1
+ import { isTextNode } from "../identity.js";
2
+ export const assertNoBlobAtomUrls = (envelope) => {
3
+ const visit = (node) => {
4
+ if (isTextNode(node))
5
+ return;
6
+ if (node.attrs?.status === "pending")
7
+ throw new Error(`Cannot persist pending atom "${node.id}"; wait for completion or cancel it.`);
8
+ for (const key of ["src", "poster"]) {
9
+ if (typeof node.attrs?.[key] === "string" && /^blob:/i.test(node.attrs[key])) {
10
+ throw new Error(`Cannot persist transient blob URL from ${node.type}.${key}.`);
11
+ }
12
+ }
13
+ node.children?.forEach(visit);
14
+ };
15
+ visit(envelope.document);
16
+ };
17
+ export const serializePersistedEditorDocument = (envelope) => {
18
+ assertNoBlobAtomUrls(envelope);
19
+ return JSON.stringify(envelope);
20
+ };
@@ -0,0 +1,20 @@
1
+ import { type PluginCommand } from "../plugin/types.js";
2
+ import { atomDeclarations } from "./declarations.js";
3
+ /**
4
+ * Wraps the four existing, already-tested atom commands (atom/commands.ts)
5
+ * with the description Phase 10's doc generation needs. Six atom node
6
+ * types (image, block_image, formula, block_formula, video, audio, per
7
+ * atomDeclarations) share these same four generic commands via
8
+ * `declaration`/`attrs` params, the same way marks' seven commands cover
9
+ * every mark type - there is no per-atom-type command to convert
10
+ * separately.
11
+ *
12
+ * MediaProvider/mediaPicker (packages/react/src/mediaProvider.ts) are left
13
+ * exactly as they are - direct CanonicalAuthorityEditor props, already
14
+ * working - rather than routed through this manifest. They are a
15
+ * react-layer upload/picker concern orthogonal to the core command surface
16
+ * atom.insert consumes (a MediaProvider's job ends at producing a URL;
17
+ * atom.insert takes it from there like any other src).
18
+ */
19
+ export declare const atomPluginCommands: Record<string, PluginCommand>;
20
+ export { atomDeclarations };
@@ -0,0 +1,55 @@
1
+ import { definePluginCommand } from "../plugin/types.js";
2
+ import { atomCommands } from "./commands.js";
3
+ import { atomDeclarations } from "./declarations.js";
4
+ /**
5
+ * Wraps the four existing, already-tested atom commands (atom/commands.ts)
6
+ * with the description Phase 10's doc generation needs. Six atom node
7
+ * types (image, block_image, formula, block_formula, video, audio, per
8
+ * atomDeclarations) share these same four generic commands via
9
+ * `declaration`/`attrs` params, the same way marks' seven commands cover
10
+ * every mark type - there is no per-atom-type command to convert
11
+ * separately.
12
+ *
13
+ * MediaProvider/mediaPicker (packages/react/src/mediaProvider.ts) are left
14
+ * exactly as they are - direct CanonicalAuthorityEditor props, already
15
+ * working - rather than routed through this manifest. They are a
16
+ * react-layer upload/picker concern orthogonal to the core command surface
17
+ * atom.insert consumes (a MediaProvider's job ends at producing a URL;
18
+ * atom.insert takes it from there like any other src).
19
+ */
20
+ export const atomPluginCommands = {
21
+ "atom.insert": definePluginCommand({
22
+ id: "atom.insert", run: atomCommands["atom.insert"],
23
+ description: "Inserts an atom node (image, formula, video, or audio) at the given position.",
24
+ options: {
25
+ declaration: { description: "Which atom type to insert, from atomDeclarations (schema.ts).", required: true },
26
+ nodeId: { description: "Caller-provided id for the new node.", required: true },
27
+ attrs: { description: "The new node's attributes (e.g. src, alt for an image).", required: true },
28
+ ownerId: { description: "The inline container to insert into - required for inline atoms." },
29
+ offset: { description: "The inline offset to insert at." },
30
+ parentId: { description: "The block container to insert into - required for block atoms." },
31
+ index: { description: "The block index to insert at." },
32
+ },
33
+ }),
34
+ "atom.update": definePluginCommand({
35
+ id: "atom.update", run: atomCommands["atom.update"],
36
+ description: "Updates the selected atom's attributes.",
37
+ options: { attrs: { description: "The new attributes.", required: true } },
38
+ }),
39
+ "atom.delete": definePluginCommand({
40
+ id: "atom.delete", run: atomCommands["atom.delete"],
41
+ description: "Deletes the selected atom.",
42
+ }),
43
+ "atom.resize": definePluginCommand({
44
+ id: "atom.resize", run: atomCommands["atom.resize"],
45
+ description: "Resizes the selected atom, optionally preserving its aspect ratio.",
46
+ options: {
47
+ width: { description: "The new width.", required: true },
48
+ height: { description: "The new height.", required: true },
49
+ minWidth: { description: "A minimum width to clamp to." },
50
+ minHeight: { description: "A minimum height to clamp to." },
51
+ preserveAspectRatio: { description: "Whether to scale height proportionally to width (or vice versa)." },
52
+ },
53
+ }),
54
+ };
55
+ export { atomDeclarations };
@@ -0,0 +1,3 @@
1
+ import type { NodeSpec } from "../types.js";
2
+ /** Inline and block variants are distinct because schema groups are static. */
3
+ export declare const atomNodeSpecs: readonly NodeSpec[];
@@ -0,0 +1,29 @@
1
+ const optionalString = { validate: (value) => typeof value === "string" };
2
+ const requiredString = { required: true, validate: (value) => typeof value === "string" };
3
+ const dimension = { validate: (value) => Number.isFinite(value) && Number(value) > 0 && Number(value) <= 100000 };
4
+ const status = { default: "ready", validate: (value) => value === "pending" || value === "ready" || value === "error" };
5
+ const imageAttrs = {
6
+ src: requiredString, alt: requiredString, width: dimension, height: dimension,
7
+ status, uploadId: optionalString, error: optionalString, decorative: { validate: (value) => typeof value === "boolean" },
8
+ align: { validate: (value) => value === "center" || value === "left" || value === "right" },
9
+ };
10
+ const formulaAttrs = {
11
+ source: requiredString,
12
+ notation: { required: true, default: "latex", validate: (value) => value === "latex" || value === "mathml" },
13
+ error: optionalString,
14
+ };
15
+ const mediaAttrs = { src: requiredString, poster: optionalString, width: dimension, height: dimension, status, uploadId: optionalString, error: optionalString };
16
+ /** Inline and block variants are distinct because schema groups are static. */
17
+ export const atomNodeSpecs = [
18
+ { type: "image", group: "inline", atomic: true, selectable: true, marks: "", attributes: imageAttrs },
19
+ { type: "block_image", group: "block", atomic: true, selectable: true, marks: "", attributes: imageAttrs },
20
+ { type: "formula", group: "inline", atomic: true, selectable: true, marks: "", attributes: formulaAttrs },
21
+ { type: "block_formula", group: "block", atomic: true, selectable: true, marks: "", attributes: formulaAttrs },
22
+ { type: "video", group: "block", atomic: true, selectable: true, marks: "", attributes: mediaAttrs },
23
+ { type: "audio", group: "block", atomic: true, selectable: true, marks: "", attributes: mediaAttrs },
24
+ // A horizontal rule (<hr>) - no content, no attrs, just a block-level
25
+ // separator atom. Previously unrecognized entirely (parseBlock had no
26
+ // case for "hr"), so every pasted <hr> fell to the generic
27
+ // "unrecognized tag" fallback and rendered as "[Unsupported: hr]".
28
+ { type: "divider", group: "block", atomic: true, selectable: true, marks: "" },
29
+ ];
@@ -0,0 +1,10 @@
1
+ import type { AtomDeclaration } from "./types.js";
2
+ export declare const SAFE_IMAGE_MIME_TYPES: Set<string>;
3
+ export declare const SAFE_AUDIO_MIME_TYPES: Set<string>;
4
+ export declare const SAFE_VIDEO_MIME_TYPES: Set<string>;
5
+ export declare const validateAtomMime: (kind: AtomDeclaration["kind"], mime: string) => boolean;
6
+ export declare const sanitizeAtomSource: (value: string | undefined | null, options: {
7
+ kind: AtomDeclaration["kind"];
8
+ allowBlobPreview?: boolean;
9
+ }) => string | null;
10
+ export declare const safeFormulaSource: (source: unknown) => source is string;
@@ -0,0 +1,20 @@
1
+ import { sanitizeResourceUrl } from "../security/urlPolicy.js";
2
+ export const SAFE_IMAGE_MIME_TYPES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/avif", "image/svg+xml"]);
3
+ const SAFE_IMAGE_DATA_MIME_TYPES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/avif"]);
4
+ export const SAFE_AUDIO_MIME_TYPES = new Set(["audio/mpeg", "audio/mp4", "audio/ogg", "audio/wav", "audio/webm"]);
5
+ export const SAFE_VIDEO_MIME_TYPES = new Set(["video/mp4", "video/webm", "video/ogg"]);
6
+ export const validateAtomMime = (kind, mime) => {
7
+ const normalized = mime.trim().toLowerCase().split(";", 1)[0];
8
+ if (kind === "image")
9
+ return SAFE_IMAGE_MIME_TYPES.has(normalized);
10
+ if (kind === "audio")
11
+ return SAFE_AUDIO_MIME_TYPES.has(normalized);
12
+ if (kind === "video")
13
+ return SAFE_VIDEO_MIME_TYPES.has(normalized);
14
+ return false;
15
+ };
16
+ export const sanitizeAtomSource = (value, options) => sanitizeResourceUrl(value, {
17
+ allowBlob: options.allowBlobPreview === true,
18
+ allowedDataMimeTypes: options.kind === "image" ? SAFE_IMAGE_DATA_MIME_TYPES : new Set(),
19
+ });
20
+ export const safeFormulaSource = (source) => typeof source === "string" && source.length > 0 && source.length <= 100000 && !/[\u0000]/.test(source);
@@ -0,0 +1,3 @@
1
+ import type { PositionLookup } from "../scope/types.js";
2
+ import type { SmartSelection } from "../types.js";
3
+ export declare const nodeSelectionForAtom: (nodeId: string, positions: PositionLookup) => SmartSelection | null;
@@ -0,0 +1,4 @@
1
+ export const nodeSelectionForAtom = (nodeId, positions) => {
2
+ const range = positions.rangeOf(nodeId);
3
+ return range ? { type: "node", anchor: range.from, head: range.to } : null;
4
+ };
@@ -0,0 +1,47 @@
1
+ import type { Attrs, SmartDocument, SmartOperation, SmartSchema } from "../types.js";
2
+ import type { PositionLookup, ResolvedScope } from "../scope/types.js";
3
+ export type AtomKind = "image" | "formula" | "video" | "audio";
4
+ export type AtomStatus = "pending" | "ready" | "error";
5
+ export interface AtomDeclaration {
6
+ readonly type: string;
7
+ readonly kind: AtomKind;
8
+ readonly group: "inline" | "block";
9
+ readonly validate: (attrs: Attrs) => boolean;
10
+ }
11
+ export interface AtomCommandContext {
12
+ readonly schema: SmartSchema;
13
+ readonly positions: PositionLookup;
14
+ }
15
+ export type AtomCommand<P> = (document: SmartDocument, scope: ResolvedScope, params: P, ctx: AtomCommandContext) => SmartOperation[];
16
+ export interface InsertAtomParams {
17
+ readonly declaration: AtomDeclaration;
18
+ readonly nodeId: string;
19
+ readonly attrs: Attrs;
20
+ /** Required for inline atoms. */
21
+ readonly ownerId?: string;
22
+ readonly offset?: number;
23
+ /** Required for block atoms. */
24
+ readonly parentId?: string;
25
+ readonly index?: number;
26
+ }
27
+ export interface UpdateAtomParams {
28
+ readonly attrs: Attrs;
29
+ }
30
+ export interface ResizeAtomParams {
31
+ readonly width: number;
32
+ readonly height: number;
33
+ readonly minWidth?: number;
34
+ readonly minHeight?: number;
35
+ readonly preserveAspectRatio?: boolean;
36
+ }
37
+ export interface CompositionAtomToken {
38
+ readonly kind: "atom";
39
+ readonly nodeId: string;
40
+ readonly atomType: string;
41
+ }
42
+ export interface CompositionTextToken {
43
+ readonly kind: "text";
44
+ readonly text: string;
45
+ readonly marks: readonly import("../types.js").SmartMark[];
46
+ }
47
+ export type CompositionToken = CompositionAtomToken | CompositionTextToken;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { BlockCommand, IndentBlocksParams, MoveBlocksParams, OutdentBlocksParams, SetBlockAttributesParams, SetBlockTypeParams, UnwrapBlocksParams, WrapBlocksParams } from "./types.js";
2
+ export declare const setBlockTypeCommand: BlockCommand<SetBlockTypeParams>;
3
+ export declare const wrapBlocks: BlockCommand<WrapBlocksParams>;
4
+ export declare const unwrapBlocks: BlockCommand<UnwrapBlocksParams>;
5
+ export declare const setBlockAttributes: BlockCommand<SetBlockAttributesParams>;
6
+ export declare const moveBlockCommand: BlockCommand<MoveBlocksParams>;
7
+ export declare const indentBlockCommand: BlockCommand<IndentBlocksParams>;
8
+ export declare const outdentBlockCommand: BlockCommand<OutdentBlocksParams>;
9
+ export declare const blockCommands: {
10
+ readonly "block.setType": BlockCommand<SetBlockTypeParams>;
11
+ readonly "block.wrap": BlockCommand<WrapBlocksParams>;
12
+ readonly "block.unwrap": BlockCommand<UnwrapBlocksParams>;
13
+ readonly "block.setAttributes": BlockCommand<SetBlockAttributesParams>;
14
+ readonly "block.move": BlockCommand<MoveBlocksParams>;
15
+ readonly "block.indent": BlockCommand<IndentBlocksParams>;
16
+ readonly "block.outdent": BlockCommand<OutdentBlocksParams>;
17
+ };