@kritzel/engine 0.4.31

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 (373) hide show
  1. package/LICENSE.md +50 -0
  2. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  3. package/dist/cjs/engine.constants-CakmdKMW.js +28206 -0
  4. package/dist/cjs/index-BbbWU2aN.js +1742 -0
  5. package/dist/cjs/index.cjs.js +4232 -0
  6. package/dist/cjs/kritzel-awareness-cursors_5.cjs.entry.js +28004 -0
  7. package/dist/cjs/loader.cjs.js +13 -0
  8. package/dist/cjs/stencil.cjs.js +25 -0
  9. package/dist/collection/classes/core/core.class.js +1389 -0
  10. package/dist/collection/classes/core/reviver.class.js +124 -0
  11. package/dist/collection/classes/core/store.class.js +337 -0
  12. package/dist/collection/classes/core/viewport.class.js +794 -0
  13. package/dist/collection/classes/core/workspace.class.js +112 -0
  14. package/dist/collection/classes/handlers/base.handler.js +20 -0
  15. package/dist/collection/classes/handlers/context-menu.handler.js +170 -0
  16. package/dist/collection/classes/handlers/hover.handler.js +35 -0
  17. package/dist/collection/classes/handlers/key.handler.js +162 -0
  18. package/dist/collection/classes/handlers/line-handle.handler.js +475 -0
  19. package/dist/collection/classes/handlers/move.handler.js +295 -0
  20. package/dist/collection/classes/handlers/resize.handler.js +327 -0
  21. package/dist/collection/classes/handlers/rotation.handler.js +174 -0
  22. package/dist/collection/classes/handlers/selection.handler.js +526 -0
  23. package/dist/collection/classes/managers/anchor.manager.js +1476 -0
  24. package/dist/collection/classes/managers/browser-permission.manager.js +64 -0
  25. package/dist/collection/classes/managers/clipboard.manager.js +346 -0
  26. package/dist/collection/classes/managers/clipboard.types.js +1 -0
  27. package/dist/collection/classes/managers/cursor.manager.js +139 -0
  28. package/dist/collection/classes/managers/license.manager.js +341 -0
  29. package/dist/collection/classes/managers/localization.manager.js +227 -0
  30. package/dist/collection/classes/managers/theme.manager.js +206 -0
  31. package/dist/collection/classes/objects/base-object.class.js +462 -0
  32. package/dist/collection/classes/objects/dynamic-object.class.js +437 -0
  33. package/dist/collection/classes/objects/group.class.js +590 -0
  34. package/dist/collection/classes/objects/image.class.js +500 -0
  35. package/dist/collection/classes/objects/line.class.js +789 -0
  36. package/dist/collection/classes/objects/path.class.js +578 -0
  37. package/dist/collection/classes/objects/selection-box.class.js +32 -0
  38. package/dist/collection/classes/objects/selection-group.class.js +752 -0
  39. package/dist/collection/classes/objects/shape.class.js +628 -0
  40. package/dist/collection/classes/objects/text.class.js +461 -0
  41. package/dist/collection/classes/providers/assets/asset-resolver.class.js +536 -0
  42. package/dist/collection/classes/providers/assets/http-asset-provider.class.js +111 -0
  43. package/dist/collection/classes/providers/assets/indexeddb-asset-provider.class.js +241 -0
  44. package/dist/collection/classes/providers/assets/presigned-asset-provider.class.js +86 -0
  45. package/dist/collection/classes/providers/paste-intent/event-paste-intent.provider.js +48 -0
  46. package/dist/collection/classes/providers/paste-intent/ios-paste-intent.provider.js +16 -0
  47. package/dist/collection/classes/providers/paste-intent/navigator-paste-intent.provider.js +86 -0
  48. package/dist/collection/classes/providers/sync/broadcast-sync-provider.class.js +100 -0
  49. package/dist/collection/classes/providers/sync/hocuspocus-sync-provider.class.js +333 -0
  50. package/dist/collection/classes/providers/sync/in-memory-sync-provider.class.js +69 -0
  51. package/dist/collection/classes/providers/sync/indexeddb-sync-provider.class.js +39 -0
  52. package/dist/collection/classes/providers/sync/websocket-sync-provider.class.js +110 -0
  53. package/dist/collection/classes/registries/dynamic-object-renderer.registry.js +52 -0
  54. package/dist/collection/classes/registries/font-registry.class.js +161 -0
  55. package/dist/collection/classes/registries/icon-registry.class.js +118 -0
  56. package/dist/collection/classes/registries/paste-intent-provider.registry.js +26 -0
  57. package/dist/collection/classes/registries/tool.registry.js +69 -0
  58. package/dist/collection/classes/structures/app-state-map.structure.js +461 -0
  59. package/dist/collection/classes/structures/object-map.structure.js +1103 -0
  60. package/dist/collection/classes/structures/quadtree.structure.js +251 -0
  61. package/dist/collection/classes/tools/base-tool.class.js +88 -0
  62. package/dist/collection/classes/tools/brush-tool.class.js +236 -0
  63. package/dist/collection/classes/tools/eraser-tool.class.js +133 -0
  64. package/dist/collection/classes/tools/image-tool.class.js +152 -0
  65. package/dist/collection/classes/tools/line-tool.class.js +249 -0
  66. package/dist/collection/classes/tools/selection-tool.class.js +611 -0
  67. package/dist/collection/classes/tools/shape-tool.class.js +251 -0
  68. package/dist/collection/classes/tools/text-tool.class.js +179 -0
  69. package/dist/collection/collection-manifest.json +17 -0
  70. package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.css +115 -0
  71. package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.js +380 -0
  72. package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.css +10 -0
  73. package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.js +153 -0
  74. package/dist/collection/components/kritzel-engine/kritzel-engine.css +81 -0
  75. package/dist/collection/components/kritzel-engine/kritzel-engine.js +6345 -0
  76. package/dist/collection/components/kritzel-icon/kritzel-icon.css +18 -0
  77. package/dist/collection/components/kritzel-icon/kritzel-icon.js +93 -0
  78. package/dist/collection/components/kritzel-watermark/kritzel-watermark.css +29 -0
  79. package/dist/collection/components/kritzel-watermark/kritzel-watermark.js +83 -0
  80. package/dist/collection/configs/default-asset-storage.config.js +9 -0
  81. package/dist/collection/configs/default-brush-tool.config.js +9 -0
  82. package/dist/collection/configs/default-engine-config.js +73 -0
  83. package/dist/collection/configs/default-line-tool.config.js +12 -0
  84. package/dist/collection/configs/default-shape-tool.config.js +15 -0
  85. package/dist/collection/configs/default-sync.config.js +6 -0
  86. package/dist/collection/configs/default-text-tool.config.js +12 -0
  87. package/dist/collection/constants/color-palette.constants.js +40 -0
  88. package/dist/collection/constants/core.constants.js +1 -0
  89. package/dist/collection/constants/engine.constants.js +4 -0
  90. package/dist/collection/constants/license.constants.js +25 -0
  91. package/dist/collection/constants/schema.constants.js +4 -0
  92. package/dist/collection/constants/storage.constants.js +1 -0
  93. package/dist/collection/constants/stroke-size.constants.js +2 -0
  94. package/dist/collection/constants/text-tool-fonts.constants.js +18 -0
  95. package/dist/collection/constants/version.js +6 -0
  96. package/dist/collection/enums/alignment.enum.js +9 -0
  97. package/dist/collection/enums/event-button.enum.js +6 -0
  98. package/dist/collection/enums/handle-type.enum.js +7 -0
  99. package/dist/collection/enums/shape-type.enum.js +6 -0
  100. package/dist/collection/events/active-workspace-change.event.js +1 -0
  101. package/dist/collection/events/editor-is-ready.event.js +1 -0
  102. package/dist/collection/events/login.event.js +1 -0
  103. package/dist/collection/helpers/alignment.helper.js +73 -0
  104. package/dist/collection/helpers/class.helper.js +5 -0
  105. package/dist/collection/helpers/color.helper.js +106 -0
  106. package/dist/collection/helpers/cursor.helper.js +57 -0
  107. package/dist/collection/helpers/devices.helper.js +28 -0
  108. package/dist/collection/helpers/event.helper.js +58 -0
  109. package/dist/collection/helpers/geometry.helper.js +149 -0
  110. package/dist/collection/helpers/html.helper.js +116 -0
  111. package/dist/collection/helpers/image-file.helper.js +6 -0
  112. package/dist/collection/helpers/keyboard.helper.js +55 -0
  113. package/dist/collection/helpers/localization.helper.js +25 -0
  114. package/dist/collection/helpers/math.helper.js +8 -0
  115. package/dist/collection/helpers/migration.helper.js +45 -0
  116. package/dist/collection/helpers/object.helper.js +29 -0
  117. package/dist/collection/helpers/svg-export.helper.js +655 -0
  118. package/dist/collection/helpers/theme.helper.js +77 -0
  119. package/dist/collection/helpers/tool-config.helper.js +63 -0
  120. package/dist/collection/helpers/wheel.helper.js +113 -0
  121. package/dist/collection/index.js +107 -0
  122. package/dist/collection/interfaces/anchor.interface.js +1 -0
  123. package/dist/collection/interfaces/arrow-head.interface.js +1 -0
  124. package/dist/collection/interfaces/asset-provider.interface.js +1 -0
  125. package/dist/collection/interfaces/asset-storage-config.interface.js +1 -0
  126. package/dist/collection/interfaces/asset.interface.js +1 -0
  127. package/dist/collection/interfaces/bounding-box.interface.js +1 -0
  128. package/dist/collection/interfaces/clonable.interface.js +1 -0
  129. package/dist/collection/interfaces/context-menu-item.interface.js +1 -0
  130. package/dist/collection/interfaces/context-menu-state.interface.js +1 -0
  131. package/dist/collection/interfaces/debug-info.interface.js +1 -0
  132. package/dist/collection/interfaces/dialog.interface.js +1 -0
  133. package/dist/collection/interfaces/document-metadata.interface.js +1 -0
  134. package/dist/collection/interfaces/dynamic-object-renderer.interface.js +1 -0
  135. package/dist/collection/interfaces/engine-state.interface.js +1 -0
  136. package/dist/collection/interfaces/font.interface.js +1 -0
  137. package/dist/collection/interfaces/localization.interface.js +1 -0
  138. package/dist/collection/interfaces/login-config.interface.js +1 -0
  139. package/dist/collection/interfaces/master-detail.interface.js +1 -0
  140. package/dist/collection/interfaces/menu-item.interface.js +1 -0
  141. package/dist/collection/interfaces/migration.interface.js +1 -0
  142. package/dist/collection/interfaces/notification.interface.js +1 -0
  143. package/dist/collection/interfaces/object-change-event.interface.js +1 -0
  144. package/dist/collection/interfaces/object-export.interface.js +1 -0
  145. package/dist/collection/interfaces/object.interface.js +1 -0
  146. package/dist/collection/interfaces/paste-intent-provider.interface.js +1 -0
  147. package/dist/collection/interfaces/point.interface.js +1 -0
  148. package/dist/collection/interfaces/polygon.interface.js +1 -0
  149. package/dist/collection/interfaces/remote-cursor.interface.js +1 -0
  150. package/dist/collection/interfaces/selection-state.interface.js +1 -0
  151. package/dist/collection/interfaces/serializable.interface.js +1 -0
  152. package/dist/collection/interfaces/settings.interface.js +1 -0
  153. package/dist/collection/interfaces/share.interface.js +1 -0
  154. package/dist/collection/interfaces/shortcut.interface.js +1 -0
  155. package/dist/collection/interfaces/sync-config.interface.js +21 -0
  156. package/dist/collection/interfaces/sync-provider.interface.js +1 -0
  157. package/dist/collection/interfaces/theme.interface.js +1 -0
  158. package/dist/collection/interfaces/tool-config.interface.js +1 -0
  159. package/dist/collection/interfaces/tool-type.interface.js +1 -0
  160. package/dist/collection/interfaces/tool.interface.js +1 -0
  161. package/dist/collection/interfaces/toolbar-item.interface.js +1 -0
  162. package/dist/collection/interfaces/undo-state.interface.js +1 -0
  163. package/dist/collection/interfaces/user.interface.js +1 -0
  164. package/dist/collection/interfaces/viewport-state.interface.js +1 -0
  165. package/dist/collection/locales/de-locale.js +119 -0
  166. package/dist/collection/locales/en-locale.js +120 -0
  167. package/dist/collection/locales/fr-locale.js +119 -0
  168. package/dist/collection/migrations/app-state.migrations.js +47 -0
  169. package/dist/collection/migrations/workspace.migrations.js +67 -0
  170. package/dist/collection/themes/dark-theme.js +340 -0
  171. package/dist/collection/themes/light-theme.js +346 -0
  172. package/dist/collection/types/deep-readonly.type.js +1 -0
  173. package/dist/collection/types/state.types.js +1 -0
  174. package/dist/components/index.d.ts +47 -0
  175. package/dist/components/index.js +4143 -0
  176. package/dist/components/kritzel-awareness-cursors.d.ts +11 -0
  177. package/dist/components/kritzel-awareness-cursors.js +6 -0
  178. package/dist/components/kritzel-cursor-trail.d.ts +11 -0
  179. package/dist/components/kritzel-cursor-trail.js +6 -0
  180. package/dist/components/kritzel-engine.d.ts +11 -0
  181. package/dist/components/kritzel-engine.js +52641 -0
  182. package/dist/components/kritzel-icon.d.ts +11 -0
  183. package/dist/components/kritzel-icon.js +6 -0
  184. package/dist/components/kritzel-watermark.d.ts +11 -0
  185. package/dist/components/kritzel-watermark.js +6 -0
  186. package/dist/components/p-B2Htv9ou.js +49 -0
  187. package/dist/components/p-BvRGQvVI.js +3456 -0
  188. package/dist/components/p-CfSx6HU_.js +1469 -0
  189. package/dist/components/p-YP8wAcF4.js +122 -0
  190. package/dist/components/p-fx6msYHJ.js +167 -0
  191. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  192. package/dist/esm/engine.constants-Bl5-pbq-.js +28143 -0
  193. package/dist/esm/index-BU9Mv7y0.js +1715 -0
  194. package/dist/esm/index.js +4134 -0
  195. package/dist/esm/kritzel-awareness-cursors_5.entry.js +27978 -0
  196. package/dist/esm/loader.js +11 -0
  197. package/dist/esm/stencil.js +21 -0
  198. package/dist/index.cjs.js +1 -0
  199. package/dist/index.js +1 -0
  200. package/dist/stencil/index.esm.js +4134 -0
  201. package/dist/stencil/p-BU9Mv7y0.js +1702 -0
  202. package/dist/stencil/p-Bl5-pbq-.js +28143 -0
  203. package/dist/stencil/p-DQuL1Twl.js +3 -0
  204. package/dist/stencil/p-f0a8b7af.entry.js +27978 -0
  205. package/dist/stencil/stencil.esm.js +21 -0
  206. package/dist/types/classes/core/core.class.d.ts +532 -0
  207. package/dist/types/classes/core/reviver.class.d.ts +32 -0
  208. package/dist/types/classes/core/store.class.d.ts +198 -0
  209. package/dist/types/classes/core/viewport.class.d.ts +256 -0
  210. package/dist/types/classes/core/workspace.class.d.ts +95 -0
  211. package/dist/types/classes/handlers/base.handler.d.ts +18 -0
  212. package/dist/types/classes/handlers/context-menu.handler.d.ts +67 -0
  213. package/dist/types/classes/handlers/hover.handler.d.ts +23 -0
  214. package/dist/types/classes/handlers/key.handler.d.ts +44 -0
  215. package/dist/types/classes/handlers/line-handle.handler.d.ts +115 -0
  216. package/dist/types/classes/handlers/move.handler.d.ts +94 -0
  217. package/dist/types/classes/handlers/resize.handler.d.ts +95 -0
  218. package/dist/types/classes/handlers/rotation.handler.d.ts +50 -0
  219. package/dist/types/classes/handlers/selection.handler.d.ts +153 -0
  220. package/dist/types/classes/managers/anchor.manager.d.ts +513 -0
  221. package/dist/types/classes/managers/browser-permission.manager.d.ts +41 -0
  222. package/dist/types/classes/managers/clipboard.manager.d.ts +71 -0
  223. package/dist/types/classes/managers/clipboard.types.d.ts +23 -0
  224. package/dist/types/classes/managers/cursor.manager.d.ts +66 -0
  225. package/dist/types/classes/managers/license.manager.d.ts +147 -0
  226. package/dist/types/classes/managers/localization.manager.d.ts +145 -0
  227. package/dist/types/classes/managers/theme.manager.d.ts +112 -0
  228. package/dist/types/classes/objects/base-object.class.d.ts +266 -0
  229. package/dist/types/classes/objects/dynamic-object.class.d.ts +148 -0
  230. package/dist/types/classes/objects/group.class.d.ts +231 -0
  231. package/dist/types/classes/objects/image.class.d.ts +246 -0
  232. package/dist/types/classes/objects/line.class.d.ts +285 -0
  233. package/dist/types/classes/objects/path.class.d.ts +220 -0
  234. package/dist/types/classes/objects/selection-box.class.d.ts +19 -0
  235. package/dist/types/classes/objects/selection-group.class.d.ts +267 -0
  236. package/dist/types/classes/objects/shape.class.d.ts +322 -0
  237. package/dist/types/classes/objects/text.class.d.ts +197 -0
  238. package/dist/types/classes/providers/assets/asset-resolver.class.d.ts +141 -0
  239. package/dist/types/classes/providers/assets/http-asset-provider.class.d.ts +79 -0
  240. package/dist/types/classes/providers/assets/indexeddb-asset-provider.class.d.ts +69 -0
  241. package/dist/types/classes/providers/assets/presigned-asset-provider.class.d.ts +69 -0
  242. package/dist/types/classes/providers/paste-intent/event-paste-intent.provider.d.ts +8 -0
  243. package/dist/types/classes/providers/paste-intent/ios-paste-intent.provider.d.ts +6 -0
  244. package/dist/types/classes/providers/paste-intent/navigator-paste-intent.provider.d.ts +9 -0
  245. package/dist/types/classes/providers/sync/broadcast-sync-provider.class.d.ts +23 -0
  246. package/dist/types/classes/providers/sync/hocuspocus-sync-provider.class.d.ts +163 -0
  247. package/dist/types/classes/providers/sync/in-memory-sync-provider.class.d.ts +39 -0
  248. package/dist/types/classes/providers/sync/indexeddb-sync-provider.class.d.ts +25 -0
  249. package/dist/types/classes/providers/sync/websocket-sync-provider.class.d.ts +63 -0
  250. package/dist/types/classes/registries/dynamic-object-renderer.registry.d.ts +32 -0
  251. package/dist/types/classes/registries/font-registry.class.d.ts +19 -0
  252. package/dist/types/classes/registries/icon-registry.class.d.ts +39 -0
  253. package/dist/types/classes/registries/paste-intent-provider.registry.d.ts +10 -0
  254. package/dist/types/classes/registries/tool.registry.d.ts +53 -0
  255. package/dist/types/classes/structures/app-state-map.structure.d.ts +171 -0
  256. package/dist/types/classes/structures/object-map.structure.d.ts +403 -0
  257. package/dist/types/classes/structures/quadtree.structure.d.ts +128 -0
  258. package/dist/types/classes/tools/base-tool.class.d.ts +72 -0
  259. package/dist/types/classes/tools/brush-tool.class.d.ts +68 -0
  260. package/dist/types/classes/tools/eraser-tool.class.d.ts +38 -0
  261. package/dist/types/classes/tools/image-tool.class.d.ts +74 -0
  262. package/dist/types/classes/tools/line-tool.class.d.ts +63 -0
  263. package/dist/types/classes/tools/selection-tool.class.d.ts +188 -0
  264. package/dist/types/classes/tools/shape-tool.class.d.ts +90 -0
  265. package/dist/types/classes/tools/text-tool.class.d.ts +66 -0
  266. package/dist/types/components/kritzel-awareness-cursors/kritzel-awareness-cursors.d.ts +29 -0
  267. package/dist/types/components/kritzel-cursor-trail/kritzel-cursor-trail.d.ts +19 -0
  268. package/dist/types/components/kritzel-engine/kritzel-engine.d.ts +695 -0
  269. package/dist/types/components/kritzel-icon/kritzel-icon.d.ts +6 -0
  270. package/dist/types/components/kritzel-watermark/kritzel-watermark.d.ts +20 -0
  271. package/dist/types/components.d.ts +981 -0
  272. package/dist/types/configs/default-asset-storage.config.d.ts +8 -0
  273. package/dist/types/configs/default-brush-tool.config.d.ts +2 -0
  274. package/dist/types/configs/default-engine-config.d.ts +2 -0
  275. package/dist/types/configs/default-line-tool.config.d.ts +2 -0
  276. package/dist/types/configs/default-shape-tool.config.d.ts +2 -0
  277. package/dist/types/configs/default-sync.config.d.ts +5 -0
  278. package/dist/types/configs/default-text-tool.config.d.ts +2 -0
  279. package/dist/types/constants/color-palette.constants.d.ts +31 -0
  280. package/dist/types/constants/core.constants.d.ts +0 -0
  281. package/dist/types/constants/engine.constants.d.ts +4 -0
  282. package/dist/types/constants/license.constants.d.ts +25 -0
  283. package/dist/types/constants/schema.constants.d.ts +4 -0
  284. package/dist/types/constants/storage.constants.d.ts +1 -0
  285. package/dist/types/constants/stroke-size.constants.d.ts +2 -0
  286. package/dist/types/constants/text-tool-fonts.constants.d.ts +3 -0
  287. package/dist/types/constants/version.d.ts +6 -0
  288. package/dist/types/enums/alignment.enum.d.ts +8 -0
  289. package/dist/types/enums/event-button.enum.d.ts +5 -0
  290. package/dist/types/enums/handle-type.enum.d.ts +6 -0
  291. package/dist/types/enums/shape-type.enum.d.ts +5 -0
  292. package/dist/types/events/active-workspace-change.event.d.ts +7 -0
  293. package/dist/types/events/editor-is-ready.event.d.ts +18 -0
  294. package/dist/types/events/login.event.d.ts +3 -0
  295. package/dist/types/helpers/alignment.helper.d.ts +21 -0
  296. package/dist/types/helpers/class.helper.d.ts +3 -0
  297. package/dist/types/helpers/color.helper.d.ts +34 -0
  298. package/dist/types/helpers/cursor.helper.d.ts +22 -0
  299. package/dist/types/helpers/devices.helper.d.ts +8 -0
  300. package/dist/types/helpers/event.helper.d.ts +6 -0
  301. package/dist/types/helpers/geometry.helper.d.ts +38 -0
  302. package/dist/types/helpers/html.helper.d.ts +8 -0
  303. package/dist/types/helpers/image-file.helper.d.ts +3 -0
  304. package/dist/types/helpers/keyboard.helper.d.ts +6 -0
  305. package/dist/types/helpers/localization.helper.d.ts +18 -0
  306. package/dist/types/helpers/math.helper.d.ts +4 -0
  307. package/dist/types/helpers/migration.helper.d.ts +30 -0
  308. package/dist/types/helpers/object.helper.d.ts +9 -0
  309. package/dist/types/helpers/svg-export.helper.d.ts +231 -0
  310. package/dist/types/helpers/theme.helper.d.ts +42 -0
  311. package/dist/types/helpers/tool-config.helper.d.ts +4 -0
  312. package/dist/types/helpers/wheel.helper.d.ts +9 -0
  313. package/dist/types/index.d.ts +109 -0
  314. package/dist/types/interfaces/anchor.interface.d.ts +147 -0
  315. package/dist/types/interfaces/arrow-head.interface.d.ts +27 -0
  316. package/dist/types/interfaces/asset-provider.interface.d.ts +88 -0
  317. package/dist/types/interfaces/asset-storage-config.interface.d.ts +40 -0
  318. package/dist/types/interfaces/asset.interface.d.ts +43 -0
  319. package/dist/types/interfaces/bounding-box.interface.d.ts +8 -0
  320. package/dist/types/interfaces/clonable.interface.d.ts +3 -0
  321. package/dist/types/interfaces/context-menu-item.interface.d.ts +25 -0
  322. package/dist/types/interfaces/context-menu-state.interface.d.ts +16 -0
  323. package/dist/types/interfaces/debug-info.interface.d.ts +7 -0
  324. package/dist/types/interfaces/dialog.interface.d.ts +4 -0
  325. package/dist/types/interfaces/document-metadata.interface.d.ts +6 -0
  326. package/dist/types/interfaces/dynamic-object-renderer.interface.d.ts +40 -0
  327. package/dist/types/interfaces/engine-state.interface.d.ts +82 -0
  328. package/dist/types/interfaces/font.interface.d.ts +15 -0
  329. package/dist/types/interfaces/localization.interface.d.ts +143 -0
  330. package/dist/types/interfaces/login-config.interface.d.ts +24 -0
  331. package/dist/types/interfaces/master-detail.interface.d.ts +14 -0
  332. package/dist/types/interfaces/menu-item.interface.d.ts +27 -0
  333. package/dist/types/interfaces/migration.interface.d.ts +16 -0
  334. package/dist/types/interfaces/notification.interface.d.ts +10 -0
  335. package/dist/types/interfaces/object-change-event.interface.d.ts +31 -0
  336. package/dist/types/interfaces/object-export.interface.d.ts +20 -0
  337. package/dist/types/interfaces/object.interface.d.ts +53 -0
  338. package/dist/types/interfaces/paste-intent-provider.interface.d.ts +10 -0
  339. package/dist/types/interfaces/point.interface.d.ts +4 -0
  340. package/dist/types/interfaces/polygon.interface.d.ts +7 -0
  341. package/dist/types/interfaces/remote-cursor.interface.d.ts +18 -0
  342. package/dist/types/interfaces/selection-state.interface.d.ts +7 -0
  343. package/dist/types/interfaces/serializable.interface.d.ts +5 -0
  344. package/dist/types/interfaces/settings.interface.d.ts +25 -0
  345. package/dist/types/interfaces/share.interface.d.ts +9 -0
  346. package/dist/types/interfaces/shortcut.interface.d.ts +10 -0
  347. package/dist/types/interfaces/sync-config.interface.d.ts +41 -0
  348. package/dist/types/interfaces/sync-provider.interface.d.ts +66 -0
  349. package/dist/types/interfaces/theme.interface.d.ts +553 -0
  350. package/dist/types/interfaces/tool-config.interface.d.ts +24 -0
  351. package/dist/types/interfaces/tool-type.interface.d.ts +1 -0
  352. package/dist/types/interfaces/tool.interface.d.ts +8 -0
  353. package/dist/types/interfaces/toolbar-item.interface.d.ts +66 -0
  354. package/dist/types/interfaces/undo-state.interface.d.ts +6 -0
  355. package/dist/types/interfaces/user.interface.d.ts +22 -0
  356. package/dist/types/interfaces/viewport-state.interface.d.ts +7 -0
  357. package/dist/types/locales/de-locale.d.ts +5 -0
  358. package/dist/types/locales/en-locale.d.ts +6 -0
  359. package/dist/types/locales/fr-locale.d.ts +5 -0
  360. package/dist/types/migrations/app-state.migrations.d.ts +11 -0
  361. package/dist/types/migrations/workspace.migrations.d.ts +11 -0
  362. package/dist/types/stencil-public-runtime.d.ts +1873 -0
  363. package/dist/types/themes/dark-theme.d.ts +5 -0
  364. package/dist/types/themes/light-theme.d.ts +5 -0
  365. package/dist/types/types/deep-readonly.type.d.ts +7 -0
  366. package/dist/types/types/state.types.d.ts +3 -0
  367. package/loader/cdn.js +1 -0
  368. package/loader/index.cjs.js +1 -0
  369. package/loader/index.d.ts +24 -0
  370. package/loader/index.es2017.js +1 -0
  371. package/loader/index.js +2 -0
  372. package/package.json +101 -0
  373. package/readme.md +11 -0
@@ -0,0 +1,29 @@
1
+ export class ObjectHelper {
2
+ static generateUUID(length = 16) {
3
+ const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
4
+ const bytes = crypto.getRandomValues(new Uint8Array(length));
5
+ return Array.from(bytes)
6
+ .map(byte => alphabet[byte % alphabet.length])
7
+ .join('');
8
+ }
9
+ static isEmpty(obj) {
10
+ if (obj === null || obj === undefined) {
11
+ return true;
12
+ }
13
+ return Object?.keys(obj).length === 0 && obj?.constructor === Object;
14
+ }
15
+ static isClass(object, className) {
16
+ return !!object && object.__class__ === className;
17
+ }
18
+ static isKritzelExportable(object) {
19
+ if (!object || typeof object !== 'object') {
20
+ return false;
21
+ }
22
+ const candidate = object;
23
+ if (typeof candidate.getSupportedExportFormats !== 'function' || typeof candidate.exportAs !== 'function') {
24
+ return false;
25
+ }
26
+ const formats = candidate.getSupportedExportFormats();
27
+ return Array.isArray(formats) && formats.length > 0;
28
+ }
29
+ }
@@ -0,0 +1,655 @@
1
+ import { KritzelClassHelper } from "./class.helper";
2
+ import { KritzelColorHelper } from "./color.helper";
3
+ /**
4
+ * Helper class for exporting Kritzel objects to SVG format.
5
+ * Generates clean, standards-compliant SVG markup from canvas objects.
6
+ */
7
+ export class KritzelSvgExportHelper {
8
+ /**
9
+ * Calculates the combined bounding box that encompasses all objects.
10
+ * Uses rotatedBoundingBox to account for object rotation.
11
+ * @param objects - Array of objects to calculate bounds for
12
+ * @returns Combined bounding box, or null if no objects provided
13
+ */
14
+ static calculateCombinedBounds(objects) {
15
+ if (!objects || objects.length === 0) {
16
+ return null;
17
+ }
18
+ let minX = Infinity;
19
+ let minY = Infinity;
20
+ let maxX = -Infinity;
21
+ let maxY = -Infinity;
22
+ for (const obj of objects) {
23
+ const bbox = obj.rotatedBoundingBox;
24
+ minX = Math.min(minX, bbox.x);
25
+ minY = Math.min(minY, bbox.y);
26
+ maxX = Math.max(maxX, bbox.x + bbox.width);
27
+ maxY = Math.max(maxY, bbox.y + bbox.height);
28
+ }
29
+ return {
30
+ x: minX,
31
+ y: minY,
32
+ z: 1,
33
+ width: maxX - minX,
34
+ height: maxY - minY,
35
+ };
36
+ }
37
+ /**
38
+ * Generates a complete SVG document string from the given objects.
39
+ * @param objects - Objects to export
40
+ * @param options - Export options
41
+ * @returns SVG document string
42
+ */
43
+ static generateSvg(objects, options = {}) {
44
+ const { theme, padding = 0, includeXmlDeclaration = true, rasterSafeForCanvas = false, imageDataUrls } = options;
45
+ if (!objects || objects.length === 0) {
46
+ return '';
47
+ }
48
+ // Flatten groups to get all objects including nested children
49
+ const allObjects = this.flattenObjects(objects);
50
+ const bounds = this.calculateCombinedBounds(allObjects);
51
+ if (!bounds) {
52
+ return '';
53
+ }
54
+ // Add padding to bounds
55
+ const viewBoxX = bounds.x - padding;
56
+ const viewBoxY = bounds.y - padding;
57
+ const viewBoxWidth = bounds.width + padding * 2;
58
+ const viewBoxHeight = bounds.height + padding * 2;
59
+ // Collect all defs (markers, patterns, etc.)
60
+ const defs = this.collectDefs(allObjects, theme);
61
+ // Generate SVG elements for each object
62
+ const elements = objects
63
+ .sort((a, b) => a.zIndex - b.zIndex)
64
+ .map(obj => this.objectToSvgElement(obj, theme, imageDataUrls, rasterSafeForCanvas))
65
+ .filter(Boolean)
66
+ .join('\n ');
67
+ const xmlDecl = includeXmlDeclaration ? '<?xml version="1.0" encoding="UTF-8"?>\n' : '';
68
+ const defsContent = defs.length > 0 ? `\n <defs>\n ${defs.join('\n ')}\n </defs>` : '';
69
+ return `${xmlDecl}<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}" width="${viewBoxWidth}" height="${viewBoxHeight}">${defsContent}
70
+ ${elements}
71
+ </svg>`;
72
+ }
73
+ /**
74
+ * Flattens a list of objects, expanding groups into their children recursively.
75
+ * @param objects - Objects to flatten
76
+ * @returns Flat array of all objects including group children
77
+ */
78
+ static flattenObjects(objects) {
79
+ const result = [];
80
+ for (const obj of objects) {
81
+ if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
82
+ result.push(...this.flattenObjects(obj.children));
83
+ }
84
+ else {
85
+ result.push(obj);
86
+ }
87
+ }
88
+ return result;
89
+ }
90
+ /**
91
+ * Resolves every image referenced by the given objects (including those
92
+ * nested inside groups) to an inlined `data:` URL, keyed by object id.
93
+ *
94
+ * This is required for export flows that rasterize the SVG (e.g. PNG
95
+ * export): an SVG loaded into an `<img>` element will NOT load external
96
+ * or `blob:` resources, so a `<image href="blob:...">` renders as a
97
+ * broken-image placeholder. Embedding the bytes as a base64 `data:` URL
98
+ * makes the image self-contained and survive rasterization.
99
+ *
100
+ * Resolution is best-effort: images that fail to resolve are simply
101
+ * omitted from the map, and the SVG falls back to whatever `href` the
102
+ * synchronous path would have produced.
103
+ * @param objects - Objects to inline images for
104
+ * @param assetResolver - Resolver used to fetch asset bytes; optional
105
+ * @returns Map of image object id -> inlined `data:` URL
106
+ */
107
+ static async resolveImageDataUrls(objects, assetResolver) {
108
+ const dataUrls = new Map();
109
+ if (!objects || objects.length === 0) {
110
+ return dataUrls;
111
+ }
112
+ const images = this.flattenObjects(objects).filter((obj) => KritzelClassHelper.isInstanceOf(obj, 'KritzelImage'));
113
+ await Promise.all(images.map(async (image) => {
114
+ // Legacy inline data URL is already embeddable as-is.
115
+ if (image.src?.startsWith('data:')) {
116
+ dataUrls.set(image.id, image.src);
117
+ return;
118
+ }
119
+ // A resolvedSrc that is itself a data URL can be embedded directly.
120
+ if (image.resolvedSrc?.startsWith('data:')) {
121
+ dataUrls.set(image.id, image.resolvedSrc);
122
+ return;
123
+ }
124
+ if (!image.assetId || !assetResolver) {
125
+ return;
126
+ }
127
+ try {
128
+ const blob = await assetResolver.fetchBlob(image.assetId);
129
+ dataUrls.set(image.id, await this.blobToDataUrl(blob));
130
+ }
131
+ catch (err) {
132
+ console.warn(`[KritzelSvgExportHelper] Failed to inline image asset ${image.assetId} for export:`, err);
133
+ }
134
+ }));
135
+ return dataUrls;
136
+ }
137
+ /**
138
+ * Reads a Blob into a base64 `data:` URL.
139
+ * @param blob - The blob to encode
140
+ * @returns Promise resolving to the data URL string
141
+ */
142
+ static async blobToDataUrl(blob) {
143
+ // Prefer FileReader in browser-like runtimes for efficiency.
144
+ const hasFileReader = typeof FileReader !== 'undefined';
145
+ if (hasFileReader) {
146
+ try {
147
+ return await new Promise((resolve, reject) => {
148
+ const reader = new FileReader();
149
+ reader.onload = () => {
150
+ if (typeof reader.result === 'string') {
151
+ resolve(reader.result);
152
+ return;
153
+ }
154
+ reject(new Error('FileReader result is not a string'));
155
+ };
156
+ reader.onerror = () => reject(reader.error ?? new Error('Failed to read blob as data URL'));
157
+ reader.readAsDataURL(blob);
158
+ });
159
+ }
160
+ catch {
161
+ // Fall through to the ArrayBuffer-based encoder.
162
+ }
163
+ }
164
+ const bytes = new Uint8Array(await blob.arrayBuffer());
165
+ const mimeType = blob.type || 'application/octet-stream';
166
+ return `data:${mimeType};base64,${this.uint8ToBase64(bytes)}`;
167
+ }
168
+ /**
169
+ * Encodes a byte array to base64 across browser and Node-like runtimes.
170
+ */
171
+ static uint8ToBase64(bytes) {
172
+ if (typeof Buffer !== 'undefined') {
173
+ return Buffer.from(bytes).toString('base64');
174
+ }
175
+ let binary = '';
176
+ const chunkSize = 0x8000;
177
+ for (let i = 0; i < bytes.length; i += chunkSize) {
178
+ const chunk = bytes.subarray(i, Math.min(i + chunkSize, bytes.length));
179
+ binary += String.fromCharCode(...chunk);
180
+ }
181
+ if (typeof btoa === 'function') {
182
+ return btoa(binary);
183
+ }
184
+ throw new Error('No base64 encoder available in this runtime');
185
+ }
186
+ /**
187
+ * Collects SVG defs (markers, patterns, etc.) needed by the objects.
188
+ * @param objects - Objects to collect defs for
189
+ * @param theme - Theme for color resolution
190
+ * @returns Array of def element strings
191
+ */
192
+ static collectDefs(objects, theme) {
193
+ const defs = [];
194
+ for (const obj of objects) {
195
+ if (KritzelClassHelper.isInstanceOf(obj, 'KritzelLine')) {
196
+ if (obj.hasStartArrow) {
197
+ defs.push(this.generateArrowMarker(obj, 'start', theme));
198
+ }
199
+ if (obj.hasEndArrow) {
200
+ defs.push(this.generateArrowMarker(obj, 'end', theme));
201
+ }
202
+ }
203
+ }
204
+ return defs;
205
+ }
206
+ /**
207
+ * Generates an SVG marker element for line arrows.
208
+ * @param line - The line object
209
+ * @param end - Which end of the line ('start' or 'end')
210
+ * @param theme - Theme for color resolution
211
+ * @returns SVG marker element string
212
+ */
213
+ static generateArrowMarker(line, end, _theme) {
214
+ const markerId = end === 'start' ? line.startMarkerId : line.endMarkerId;
215
+ const size = line.getArrowSize(end);
216
+ const fill = line.getArrowFill(end);
217
+ const style = end === 'start' ? line.arrows?.start?.style : line.arrows?.end?.style;
218
+ const path = line.getArrowPath(style);
219
+ const orient = end === 'start' ? 'auto-start-reverse' : 'auto';
220
+ const refX = 0;
221
+ const refY = size / 2;
222
+ return `<marker id="${markerId}" markerWidth="${size}" markerHeight="${size}" refX="${refX}" refY="${refY}" orient="${orient}" markerUnits="userSpaceOnUse">
223
+ <path d="${path}" fill="${fill}" transform="scale(${size / 10})"/>
224
+ </marker>`;
225
+ }
226
+ /**
227
+ * Converts a Kritzel object to its SVG element representation.
228
+ * @param object - The object to convert
229
+ * @param theme - Theme for color resolution
230
+ * @param imageDataUrls - Optional map of image id -> inlined data URL
231
+ * @returns SVG element string, or empty string if object type is not supported
232
+ */
233
+ static objectToSvgElement(object, theme, imageDataUrls, rasterSafeForCanvas = false) {
234
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelPath')) {
235
+ return this.pathToSvg(object, theme);
236
+ }
237
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelLine')) {
238
+ return this.lineToSvg(object, theme);
239
+ }
240
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelShape')) {
241
+ return this.shapeToSvg(object, theme);
242
+ }
243
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelText')) {
244
+ return this.textToSvg(object, theme, rasterSafeForCanvas);
245
+ }
246
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelImage')) {
247
+ return this.imageToSvg(object, imageDataUrls);
248
+ }
249
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
250
+ return this.dynamicObjectToSvg(object, rasterSafeForCanvas);
251
+ }
252
+ if (KritzelClassHelper.isInstanceOf(object, 'KritzelGroup')) {
253
+ return this.groupToSvg(object, theme, imageDataUrls, rasterSafeForCanvas);
254
+ }
255
+ return '';
256
+ }
257
+ /**
258
+ * Converts a KritzelPath to SVG.
259
+ * @param path - The path object
260
+ * @param theme - Theme for color resolution
261
+ * @returns SVG element string
262
+ */
263
+ static pathToSvg(path, theme) {
264
+ const transform = this.buildTransform(path);
265
+ const fill = KritzelColorHelper.resolveThemeColor(path.fill, theme);
266
+ const stroke = KritzelColorHelper.resolveThemeColor(path.stroke, theme);
267
+ const opacity = path.opacity !== 1 ? ` opacity="${path.opacity}"` : '';
268
+ const scale = this.getObjectScale(path);
269
+ // Path needs to be wrapped in a g with translation since path.d uses local coordinates
270
+ return `<g transform="${transform}"${opacity}>
271
+ <svg viewBox="${path.viewBox}" width="${path.totalWidth / scale}" height="${path.totalHeight / scale}" overflow="visible">
272
+ <path d="${path.d}" fill="${fill || 'none'}" stroke="${stroke || 'none'}"${path.strokeWidth ? ` stroke-width="${path.strokeWidth}"` : ''}/>
273
+ </svg>
274
+ </g>`;
275
+ }
276
+ /**
277
+ * Converts a KritzelLine to SVG.
278
+ * @param line - The line object
279
+ * @param theme - Theme for color resolution
280
+ * @returns SVG element string
281
+ */
282
+ static lineToSvg(line, theme) {
283
+ const transform = this.buildTransform(line);
284
+ const stroke = KritzelColorHelper.resolveThemeColor(line.stroke, theme);
285
+ const opacity = line.opacity !== 1 ? ` opacity="${line.opacity}"` : '';
286
+ const scale = this.getObjectScale(line);
287
+ const markerStart = line.hasStartArrow ? ` marker-start="url(#${line.startMarkerId})"` : '';
288
+ const markerEnd = line.hasEndArrow ? ` marker-end="url(#${line.endMarkerId})"` : '';
289
+ return `<g transform="${transform}"${opacity}>
290
+ <svg viewBox="${line.viewBox}" width="${line.totalWidth / scale}" height="${line.totalHeight / scale}" overflow="visible">
291
+ <path d="${line.d}" fill="none" stroke="${stroke}" stroke-width="${line.strokeWidth}" stroke-linecap="round"${markerStart}${markerEnd}/>
292
+ </svg>
293
+ </g>`;
294
+ }
295
+ /**
296
+ * Converts a KritzelShape to SVG.
297
+ * @param shape - The shape object
298
+ * @param theme - Theme for color resolution
299
+ * @returns SVG element string
300
+ */
301
+ static shapeToSvg(shape, theme) {
302
+ const transform = this.buildTransform(shape);
303
+ const fill = KritzelColorHelper.resolveThemeColor(shape.fillColor, theme);
304
+ const stroke = KritzelColorHelper.resolveThemeColor(shape.strokeColor, theme);
305
+ const opacity = shape.opacity !== 1 ? ` opacity="${shape.opacity}"` : '';
306
+ const scale = this.getObjectScale(shape);
307
+ // Get the SVG path for the shape
308
+ const pathD = shape.getSvgPath();
309
+ // Handle text content if present
310
+ let textContent = '';
311
+ if (shape.editor && !shape.editor.state.doc.textContent.trim()) {
312
+ // No text content
313
+ }
314
+ else if (shape.content) {
315
+ const textHtml = this.prosemirrorToHtml(shape.content, shape, theme);
316
+ if (textHtml) {
317
+ const fontColor = KritzelColorHelper.resolveThemeColor(shape.fontColor, theme);
318
+ textContent = `
319
+ <foreignObject x="${shape.x}" y="${shape.y}" width="${shape.width}" height="${shape.height}">
320
+ <div xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; text-align: center; font-family: ${shape.fontFamily}; font-size: ${shape.fontSize}px; color: ${fontColor}; overflow: hidden;">
321
+ ${textHtml}
322
+ </div>
323
+ </foreignObject>`;
324
+ }
325
+ }
326
+ return `<g transform="${transform}"${opacity}>
327
+ <svg viewBox="${shape.viewBox}" width="${shape.totalWidth / scale}" height="${shape.totalHeight / scale}" overflow="visible" preserveAspectRatio="none">
328
+ <path d="${pathD}" fill="${fill || 'transparent'}" stroke="${stroke}" stroke-width="${shape.strokeWidth}"/>${textContent}
329
+ </svg>
330
+ </g>`;
331
+ }
332
+ /**
333
+ * Converts a KritzelText to SVG using foreignObject for rich text support.
334
+ * @param text - The text object
335
+ * @param theme - Theme for color resolution
336
+ * @returns SVG element string
337
+ */
338
+ static textToSvg(text, theme, rasterSafeForCanvas = false) {
339
+ if (rasterSafeForCanvas) {
340
+ return this.textToSvgRasterSafe(text, theme);
341
+ }
342
+ const transform = this.buildTransform(text);
343
+ const opacity = text.opacity !== 1 ? ` opacity="${text.opacity}"` : '';
344
+ const fontColor = KritzelColorHelper.resolveThemeColor(text.fontColor, theme);
345
+ const bgColor = KritzelColorHelper.resolveThemeColor(text.backgroundColor, theme);
346
+ const scale = this.getObjectScale(text);
347
+ // Convert ProseMirror content to HTML
348
+ const htmlContent = this.prosemirrorToHtml(text.content, text, theme);
349
+ // Calculate dimensions in SVG world units (world units = CSS px in a 1:1 SVG).
350
+ const width = text.totalWidth / scale;
351
+ const height = text.totalHeight / scale;
352
+ // Convert the font size from pt to SVG world units (px equivalent).
353
+ // text.fontSize is stored in CSS pt; 1 pt = 4/3 px at 96 dpi.
354
+ // We also bake in scaleFactor (user resize) and divide by the object scale so
355
+ // that the rendered font aligns with the world-unit dimensions above.
356
+ // The CSS transform: scale(scaleFactor) is removed from the div because the
357
+ // adjusted font-size already accounts for it – keeping both would double-scale
358
+ // the content and overflow the foreignObject.
359
+ const svgFontSizePx = (text.fontSize * (4 / 3) * (text.scaleFactor || 1) / scale).toFixed(4);
360
+ return `<g transform="${transform}"${opacity}>
361
+ <foreignObject x="0" y="0" width="${width}" height="${height}" overflow="visible">
362
+ <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: ${text.fontFamily}; font-size: ${svgFontSizePx}px; color: ${fontColor}; background-color: ${bgColor || 'transparent'}; white-space: pre-wrap; word-wrap: break-word; width: 100%; margin: 0; padding: 0; box-sizing: border-box;">${htmlContent.trim()}</div>
363
+ </foreignObject>
364
+ </g>`;
365
+ }
366
+ /**
367
+ * Converts a KritzelText to SVG using pure SVG text primitives.
368
+ * This path avoids `<foreignObject>`, which taints canvases when the
369
+ * generated SVG is rasterized through `<img>` and `drawImage`.
370
+ */
371
+ static textToSvgRasterSafe(text, theme) {
372
+ const transform = this.buildTransform(text);
373
+ const opacity = text.opacity !== 1 ? ` opacity="${text.opacity}"` : '';
374
+ const fontColor = KritzelColorHelper.resolveThemeColor(text.fontColor, theme);
375
+ const bgColor = KritzelColorHelper.resolveThemeColor(text.backgroundColor, theme);
376
+ const scale = this.getObjectScale(text);
377
+ const width = text.totalWidth / scale;
378
+ const height = text.totalHeight / scale;
379
+ const lines = this.prosemirrorToPlainTextLines(text.content);
380
+ const resolvedLines = lines.length > 0 ? lines : [''];
381
+ // Convert the font size from pt to SVG world units (px equivalent).
382
+ // text.fontSize is stored in CSS pt; 1 pt = 4/3 px at 96 dpi.
383
+ // We also bake in scaleFactor (user resize) and divide by the object scale so
384
+ // that the rendered lines align with the world-unit bounding box dimensions above.
385
+ const resolvedFontSizePx = Math.max(1, text.fontSize * (4 / 3) * (text.scaleFactor || 1) / scale);
386
+ const lineHeight = resolvedFontSizePx * 1.2;
387
+ const startY = resolvedFontSizePx;
388
+ const bgRect = bgColor && bgColor !== 'transparent'
389
+ ? `<rect x="0" y="0" width="${width}" height="${height}" fill="${bgColor}"/>`
390
+ : '';
391
+ const tspans = resolvedLines
392
+ .map((line, index) => {
393
+ const y = startY + index * lineHeight;
394
+ return `<tspan x="0" y="${y}" xml:space="preserve">${this.escapeHtml(line)}</tspan>`;
395
+ })
396
+ .join('');
397
+ return `<g transform="${transform}"${opacity}>
398
+ ${bgRect}
399
+ <text font-family="${this.escapeHtml(text.fontFamily)}" font-size="${resolvedFontSizePx}" fill="${fontColor}">${tspans}</text>
400
+ </g>`;
401
+ }
402
+ /**
403
+ * Converts a KritzelImage to SVG.
404
+ *
405
+ * Prefers an inlined `data:` URL from `imageDataUrls` (keyed by object
406
+ * id) when provided, so the embedded bytes survive rasterization to
407
+ * PNG. Falls back to `resolvedSrc` (populated by the renderer via the
408
+ * asset resolver) and finally the legacy inline `src` field for
409
+ * documents persisted before the asset layer existed.
410
+ *
411
+ * Note: a `blob:` `resolvedSrc` renders correctly on-canvas but becomes
412
+ * a broken-image placeholder once the SVG is loaded into an `<img>` and
413
+ * drawn to a canvas. Callers that rasterize the SVG must pass
414
+ * `imageDataUrls` (see {@link KritzelSvgExportHelper.resolveImageDataUrls}).
415
+ * @param image - The image object
416
+ * @param imageDataUrls - Optional map of image id -> inlined data URL
417
+ * @returns SVG element string
418
+ */
419
+ static imageToSvg(image, imageDataUrls) {
420
+ const transform = this.buildTransform(image);
421
+ const opacity = image.opacity !== 1 ? ` opacity="${image.opacity}"` : '';
422
+ const scale = this.getObjectScale(image);
423
+ // Calculate dimensions
424
+ const width = image.totalWidth / scale;
425
+ const height = image.totalHeight / scale;
426
+ const href = imageDataUrls?.get(image.id) || image.resolvedSrc || image.src || '';
427
+ return `<g transform="${transform}"${opacity}>
428
+ <image href="${href}" x="0" y="0" width="${width}" height="${height}" preserveAspectRatio="xMidYMid meet"/>
429
+ </g>`;
430
+ }
431
+ /**
432
+ * Converts a KritzelDynamicObject to SVG.
433
+ * SVG mode: embeds HTML in foreignObject (best-effort).
434
+ * PNG/raster mode: renders placeholder rect with warning.
435
+ * @param obj - The dynamic object
436
+ * @param rasterSafeForCanvas - If true, render placeholder (for PNG export)
437
+ * @returns SVG element string
438
+ */
439
+ static dynamicObjectToSvg(obj, rasterSafeForCanvas = false) {
440
+ const transform = this.buildTransform(obj);
441
+ const opacity = obj.opacity !== 1 ? ` opacity="${obj.opacity}"` : '';
442
+ const scale = this.getObjectScale(obj);
443
+ const width = obj.totalWidth / scale;
444
+ const height = obj.totalHeight / scale;
445
+ if (rasterSafeForCanvas) {
446
+ // PNG export: render placeholder rect (foreignObject doesn't rasterize to PNG well)
447
+ console.warn('[KritzelDynamicObject SVG Export] Dynamic objects do not serialize well to PNG. Use SVG export or screenshot instead.');
448
+ return `<g transform="${transform}"${opacity}>
449
+ <rect x="0" y="0" width="${width}" height="${height}" fill="#f0f0f0" stroke="#999" stroke-width="1" />
450
+ <text x="${width / 2}" y="${height / 2}" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#999">[Dynamic Object]</text>
451
+ </g>`;
452
+ }
453
+ // SVG export: use foreignObject to embed HTML
454
+ const html = obj.element?.outerHTML || '<div style="border:1px dashed #ccc">Empty</div>';
455
+ return `<g transform="${transform}"${opacity}>
456
+ <foreignObject x="0" y="0" width="${width}" height="${height}">
457
+ <div xmlns="http://www.w3.org/1999/xhtml" style="width:100%;height:100%;overflow:hidden;">
458
+ ${html}
459
+ </div>
460
+ </foreignObject>
461
+ </g>`;
462
+ }
463
+ /**
464
+ * Converts a KritzelGroup to SVG by recursively converting children.
465
+ * @param group - The group object
466
+ * @param theme - Theme for color resolution
467
+ * @param imageDataUrls - Optional map of image id -> inlined data URL
468
+ * @returns SVG element string
469
+ */
470
+ static groupToSvg(group, theme, imageDataUrls, rasterSafeForCanvas = false) {
471
+ const children = group.children
472
+ .sort((a, b) => a.zIndex - b.zIndex)
473
+ .map(child => this.objectToSvgElement(child, theme, imageDataUrls, rasterSafeForCanvas))
474
+ .filter(Boolean)
475
+ .join('\n ');
476
+ if (!children) {
477
+ return '';
478
+ }
479
+ const opacity = group.opacity !== 1 ? ` opacity="${group.opacity}"` : '';
480
+ return `<g${opacity}>
481
+ ${children}
482
+ </g>`;
483
+ }
484
+ /**
485
+ * Builds a transform attribute string for an object.
486
+ * Combines translation and rotation.
487
+ * @param object - The object to build transform for
488
+ * @returns Transform attribute value string
489
+ */
490
+ static buildTransform(object) {
491
+ const transforms = [];
492
+ const scale = this.getObjectScale(object);
493
+ // Translation
494
+ transforms.push(`translate(${object.translateX}, ${object.translateY})`);
495
+ // Rotation around center
496
+ if (object.rotation !== 0) {
497
+ const centerX = object.totalWidth / 2 / scale;
498
+ const centerY = object.totalHeight / 2 / scale;
499
+ const degrees = object.rotation * (180 / Math.PI);
500
+ transforms.push(`rotate(${degrees}, ${centerX}, ${centerY})`);
501
+ }
502
+ return transforms.join(' ');
503
+ }
504
+ /**
505
+ * Returns a safe non-zero scale for export geometry calculations.
506
+ */
507
+ static getObjectScale(object) {
508
+ const scale = object.scale ?? 1;
509
+ return scale === 0 ? 1 : scale;
510
+ }
511
+ /**
512
+ * Converts ProseMirror JSON content to HTML string.
513
+ * @param content - ProseMirror document JSON
514
+ * @param context - The text or shape object for styling context
515
+ * @param theme - Theme for color resolution
516
+ * @returns HTML string
517
+ */
518
+ static prosemirrorToHtml(content, _context, theme) {
519
+ if (!content || !content.content) {
520
+ return '';
521
+ }
522
+ return content.content
523
+ .map((node) => this.nodeToHtml(node, theme))
524
+ .join('');
525
+ }
526
+ /**
527
+ * Converts a ProseMirror node to HTML.
528
+ * @param node - ProseMirror node JSON
529
+ * @param theme - Theme for color resolution
530
+ * @returns HTML string
531
+ */
532
+ static nodeToHtml(node, theme) {
533
+ if (!node)
534
+ return '';
535
+ switch (node.type) {
536
+ case 'paragraph':
537
+ const pContent = node.content ? node.content.map((n) => this.nodeToHtml(n, theme)).join('') : '';
538
+ return `<p style="margin: 0;">${pContent || '<br/>'}</p>`;
539
+ case 'text':
540
+ let text = this.escapeHtml(node.text || '');
541
+ if (node.marks) {
542
+ for (const mark of node.marks) {
543
+ text = this.applyMark(text, mark, theme);
544
+ }
545
+ }
546
+ return text;
547
+ case 'hard_break':
548
+ return '<br/>';
549
+ case 'bullet_list':
550
+ const blContent = node.content ? node.content.map((n) => this.nodeToHtml(n, theme)).join('') : '';
551
+ return `<ul style="margin: 0; padding-left: 1.5em;">${blContent}</ul>`;
552
+ case 'ordered_list':
553
+ const olContent = node.content ? node.content.map((n) => this.nodeToHtml(n, theme)).join('') : '';
554
+ return `<ol style="margin: 0; padding-left: 1.5em;">${olContent}</ol>`;
555
+ case 'list_item':
556
+ const liContent = node.content ? node.content.map((n) => this.nodeToHtml(n, theme)).join('') : '';
557
+ return `<li>${liContent}</li>`;
558
+ default:
559
+ // For unknown node types, try to render content
560
+ if (node.content) {
561
+ return node.content.map((n) => this.nodeToHtml(n, theme)).join('');
562
+ }
563
+ return '';
564
+ }
565
+ }
566
+ /**
567
+ * Converts ProseMirror JSON content to plain-text lines.
568
+ * Used by raster-safe SVG export, which cannot depend on HTML/foreignObject.
569
+ */
570
+ static prosemirrorToPlainTextLines(content) {
571
+ if (!content || !Array.isArray(content.content)) {
572
+ return [];
573
+ }
574
+ const plainText = content.content
575
+ .map((node) => this.nodeToPlainText(node))
576
+ .join('')
577
+ .replace(/\r\n/g, '\n')
578
+ .replace(/\n+$/, '');
579
+ if (!plainText) {
580
+ return [];
581
+ }
582
+ return plainText.split('\n');
583
+ }
584
+ /**
585
+ * Converts a ProseMirror node tree into plain text.
586
+ */
587
+ static nodeToPlainText(node) {
588
+ if (!node) {
589
+ return '';
590
+ }
591
+ switch (node.type) {
592
+ case 'text':
593
+ return node.text || '';
594
+ case 'hard_break':
595
+ return '\n';
596
+ case 'paragraph': {
597
+ const paragraph = Array.isArray(node.content)
598
+ ? node.content.map((child) => this.nodeToPlainText(child)).join('')
599
+ : '';
600
+ return `${paragraph}\n`;
601
+ }
602
+ case 'list_item': {
603
+ const item = Array.isArray(node.content)
604
+ ? node.content.map((child) => this.nodeToPlainText(child)).join('')
605
+ : '';
606
+ return `- ${item.trim()}\n`;
607
+ }
608
+ default:
609
+ return Array.isArray(node.content)
610
+ ? node.content.map((child) => this.nodeToPlainText(child)).join('')
611
+ : '';
612
+ }
613
+ }
614
+ /**
615
+ * Applies a ProseMirror mark to text.
616
+ * @param text - The text to wrap
617
+ * @param mark - The mark to apply
618
+ * @param theme - Theme for color resolution
619
+ * @returns Wrapped HTML string
620
+ */
621
+ static applyMark(text, mark, _theme) {
622
+ switch (mark.type) {
623
+ case 'bold':
624
+ case 'strong':
625
+ return `<strong>${text}</strong>`;
626
+ case 'italic':
627
+ case 'em':
628
+ return `<em>${text}</em>`;
629
+ case 'underline':
630
+ return `<u>${text}</u>`;
631
+ case 'strike':
632
+ return `<s>${text}</s>`;
633
+ case 'code':
634
+ return `<code>${text}</code>`;
635
+ case 'link':
636
+ const href = mark.attrs?.href || '#';
637
+ return `<a href="${this.escapeHtml(href)}">${text}</a>`;
638
+ default:
639
+ return text;
640
+ }
641
+ }
642
+ /**
643
+ * Escapes HTML special characters.
644
+ * @param str - String to escape
645
+ * @returns Escaped string
646
+ */
647
+ static escapeHtml(str) {
648
+ return str
649
+ .replace(/&/g, '&amp;')
650
+ .replace(/</g, '&lt;')
651
+ .replace(/>/g, '&gt;')
652
+ .replace(/"/g, '&quot;')
653
+ .replace(/'/g, '&#039;');
654
+ }
655
+ }