@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,752 @@
1
+ import { KritzelGeometryHelper } from "../../helpers/geometry.helper";
2
+ import { KritzelBaseObject } from "./base-object.class";
3
+ import { KritzelGroup } from "./group.class";
4
+ import { KritzelClassHelper } from "../../helpers/class.helper";
5
+ import { KritzelMathHelper } from "../../helpers/math.helper";
6
+ /**
7
+ * Represents a selection group that manages multiple selected objects on the canvas.
8
+ * The selection group allows batch operations like move, resize, and rotate on multiple objects simultaneously.
9
+ * It maintains its own bounding box that encompasses all selected objects.
10
+ */
11
+ export class KritzelSelectionGroup extends KritzelBaseObject {
12
+ __class__ = 'KritzelSelectionGroup';
13
+ constructor(config) {
14
+ super();
15
+ this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
16
+ }
17
+ // Store only object IDs instead of full objects
18
+ _objectIds = [];
19
+ // Cached objects array - invalidated when objectIds changes
20
+ _cachedObjects = null;
21
+ _cachedObjectIdsHash = '';
22
+ // Store snapshots of object state for transformations (rotation, resize)
23
+ unchangedObjectSnapshots = new Map();
24
+ snapshotRotation = 0;
25
+ snapshotWidth = 0;
26
+ snapshotHeight = 0;
27
+ snapshotTranslateX = 0;
28
+ snapshotTranslateY = 0;
29
+ clientId;
30
+ minX = 0;
31
+ maxX = 0;
32
+ minY = 0;
33
+ maxY = 0;
34
+ // Selection styling properties
35
+ handleColor;
36
+ handleStrokeColor;
37
+ handleSize = 6;
38
+ /** Timestamp of the last Yjs persist for children during a drag */
39
+ _lastChildPersistTime = 0;
40
+ /** Minimum interval (ms) between child Yjs persists during drag */
41
+ static CHILD_PERSIST_THROTTLE_MS = 100;
42
+ /** Ignore tiny floating-point diffs when comparing refreshed bounds */
43
+ static DIMENSION_EPSILON = 0.001;
44
+ /**
45
+ * Gets the array of object IDs contained in this selection group.
46
+ * @returns Array of string IDs for the selected objects
47
+ */
48
+ get objectIds() {
49
+ return this._objectIds;
50
+ }
51
+ /**
52
+ * Sets the array of object IDs contained in this selection group.
53
+ * Invalidates the cached objects array when changed.
54
+ * @param value - Array of string IDs to set as the selected objects
55
+ */
56
+ set objectIds(value) {
57
+ this._objectIds = value;
58
+ this._cachedObjects = null; // Invalidate cache
59
+ }
60
+ /**
61
+ * Invalidate the cached objects array. Call this when objects might have changed
62
+ * externally (e.g., after undo/redo operations).
63
+ */
64
+ invalidateObjectsCache() {
65
+ this._cachedObjects = null;
66
+ }
67
+ /**
68
+ * Retrieves the actual object instances from the store by their IDs.
69
+ * Uses caching to avoid redundant lookups - only recomputes when objectIds changes.
70
+ * Preserves the original insertion order of objects.
71
+ * @returns Array of KritzelBaseObject instances corresponding to the stored IDs
72
+ */
73
+ get objects() {
74
+ const currentHash = this._objectIds.join(',');
75
+ if (this._cachedObjects !== null && this._cachedObjectIdsHash === currentHash) {
76
+ return this._cachedObjects;
77
+ }
78
+ const idSet = new Set(this._objectIds);
79
+ const foundObjects = this._core.store.objects.filter(obj => idSet.has(obj.id));
80
+ // Preserve the original order from objectIds
81
+ const objectMap = new Map(foundObjects.map(obj => [obj.id, obj]));
82
+ this._cachedObjects = this._objectIds
83
+ .map(id => objectMap.get(id))
84
+ .filter((obj) => obj !== undefined);
85
+ this._cachedObjectIdsHash = currentHash;
86
+ return this._cachedObjects;
87
+ }
88
+ syncChildSelectionState(children) {
89
+ const shouldBeSelected = this.isSelected === true;
90
+ for (const child of children) {
91
+ child.isSelected = shouldBeSelected;
92
+ }
93
+ }
94
+ shouldSyncChildSelectionState() {
95
+ const localClientId = this._core.store.objects?.localClientId ?? null;
96
+ if (localClientId != null && this.clientId != null) {
97
+ return this.clientId === localClientId;
98
+ }
99
+ const localUserId = this._core.user?.id ?? null;
100
+ if (localUserId != null && this.userId != null) {
101
+ return this.userId === localUserId;
102
+ }
103
+ return this.userId == null;
104
+ }
105
+ /**
106
+ * Gets the number of objects in the selection group.
107
+ * @returns The count of selected objects
108
+ */
109
+ get length() {
110
+ return this.objectIds.length;
111
+ }
112
+ /**
113
+ * Factory method to create a new KritzelSelectionGroup instance.
114
+ * Initializes the selection group with default properties and associates it with the core instance.
115
+ * Default styling uses values from the theme system (selection section).
116
+ * @param core - The KritzelCore instance to associate with this selection group
117
+ * @returns A new KritzelSelectionGroup instance configured with default settings
118
+ */
119
+ static create(core, config) {
120
+ const object = new KritzelSelectionGroup(config);
121
+ object._core = core;
122
+ object.id = object.generateId();
123
+ object.workspaceId = core.store.state.activeWorkspace.id;
124
+ object.userId = core.user?.id;
125
+ object.clientId = core.store.objects?.localClientId ?? undefined;
126
+ object.scale = core.store.state.scale;
127
+ object.zIndex = 99999;
128
+ // Initialize styling with theme-aware defaults
129
+ object.borderColor = undefined;
130
+ object.borderWidth = 2;
131
+ object.handleColor = undefined;
132
+ object.handleStrokeColor = undefined;
133
+ object.handleSize = 6;
134
+ return object;
135
+ }
136
+ /**
137
+ * Adds multiple objects to the selection group.
138
+ * Only adds objects that are not already in the selection.
139
+ * Updates the group's rotation based on the number of selected objects:
140
+ * - Single object: inherits the object's rotation
141
+ * - Multiple objects: resets rotation to 0
142
+ * Refreshes bounding box dimensions after adding objects.
143
+ * @param objects - Array of KritzelBaseObject instances to add to the selection
144
+ */
145
+ addObjects(objects) {
146
+ let changed = false;
147
+ for (const object of objects) {
148
+ if (!this._objectIds.includes(object.id)) {
149
+ this._objectIds.push(object.id);
150
+ object.isSelected = true;
151
+ changed = true;
152
+ }
153
+ }
154
+ if (changed) {
155
+ this._cachedObjects = null;
156
+ const children = this.objects;
157
+ if (children.length === 1) {
158
+ this.rotation = children[0].rotation;
159
+ }
160
+ else if (children.length > 1) {
161
+ this.rotation = 0;
162
+ }
163
+ this.captureUnchangedSnapshots(children);
164
+ this.refreshObjectDimensions(children);
165
+ }
166
+ }
167
+ /**
168
+ * Toggles an object's presence in the selection group.
169
+ * If the object is already selected, it will be removed.
170
+ * If the object is not selected, it will be added.
171
+ * Updates group rotation and refreshes bounding box dimensions after the change.
172
+ * @param object - The KritzelBaseObject instance to toggle in the selection
173
+ */
174
+ addOrRemove(object) {
175
+ const index = this._objectIds.findIndex(id => id === object.id);
176
+ if (index === -1) {
177
+ this._objectIds.push(object.id);
178
+ object.isSelected = true;
179
+ }
180
+ else {
181
+ this._objectIds.splice(index, 1);
182
+ object.isSelected = false;
183
+ }
184
+ this._cachedObjects = null; // Invalidate cache
185
+ const children = this.objects; // Get cached objects once
186
+ if (children.length === 1) {
187
+ this.rotation = children[0].rotation;
188
+ }
189
+ else if (children.length > 1) {
190
+ this.rotation = 0;
191
+ }
192
+ this.captureUnchangedSnapshots(children);
193
+ this.refreshObjectDimensions(children);
194
+ }
195
+ /**
196
+ * Prepares the selection group and its children for a transform interaction (rotate/resize).
197
+ * Ensures snapshot state is aligned with the current visual state.
198
+ */
199
+ beginTransform() {
200
+ const children = this.objects; // Get cached objects once
201
+ // Keep the selection group's bounding box current before snapshotting.
202
+ this.refreshObjectDimensions(children);
203
+ this.captureUnchangedSnapshots(children);
204
+ // Groups need their own child snapshots aligned with the current transform.
205
+ for (const obj of children) {
206
+ if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
207
+ obj.finalize();
208
+ }
209
+ }
210
+ }
211
+ /**
212
+ * Sets the isSelected flag to false for all objects in the selection group.
213
+ * Used when clearing or changing the selection state.
214
+ *
215
+ * For KritzelGroup members, also clears the isSelected flag on all nested
216
+ * descendants. A descendant can retain a lingering isSelected from the marquee
217
+ * preview before it was resolved to its parent group during selection, which
218
+ * would otherwise leave that object selected after the group is deselected and
219
+ * cause the whole group to be re-selected on the next interaction.
220
+ */
221
+ deselectAllChildren() {
222
+ const children = this.objects;
223
+ const descendantIds = new Set();
224
+ for (const obj of children) {
225
+ obj.isSelected = false;
226
+ if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
227
+ KritzelGroup.collectDescendantIds(obj, descendantIds);
228
+ }
229
+ }
230
+ if (descendantIds.size > 0) {
231
+ const descendants = this._core.store.objects.filter(o => descendantIds.has(o.id));
232
+ for (const descendant of descendants) {
233
+ descendant.isSelected = false;
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * Updates the workspace ID for the selection group and all its contained objects.
239
+ * Used when moving objects between workspaces or updating workspace associations.
240
+ * @param workspaceId - The new workspace ID to assign to the group and its children
241
+ */
242
+ updateWorkspaceId(workspaceId) {
243
+ this.workspaceId = workspaceId;
244
+ const children = this.objects;
245
+ for (const obj of children) {
246
+ obj.workspaceId = workspaceId;
247
+ }
248
+ }
249
+ /**
250
+ * Updates the z-index values for all objects in the selection group.
251
+ * Assigns sequential z-indices starting from the provided value.
252
+ * @param startZIndex - The starting z-index value; subsequent objects get startZIndex + 1, startZIndex + 2, etc.
253
+ */
254
+ updateZIndices(startZIndex) {
255
+ const children = this.objects;
256
+ for (let i = 0; i < children.length; i++) {
257
+ children[i].zIndex = startZIndex + i;
258
+ }
259
+ }
260
+ /**
261
+ * Updates the absolute position of the selection group and all its contained objects.
262
+ * Maintains the relative positions of child objects within the group.
263
+ * Also updates the unchangedObjectSnapshots to reflect the new positions.
264
+ * @param x - The new x-coordinate for the selection group's top-left corner
265
+ * @param y - The new y-coordinate for the selection group's top-left corner
266
+ */
267
+ updatePosition(x, y) {
268
+ const children = this.objects;
269
+ for (const obj of children) {
270
+ const deltaX = obj.translateX - this.translateX;
271
+ const deltaY = obj.translateY - this.translateY;
272
+ obj.updatePosition(x + deltaX, y + deltaY);
273
+ }
274
+ // Update snapshots
275
+ this.unchangedObjectSnapshots.forEach(snapshot => {
276
+ const deltaX = snapshot.translateX - this.translateX;
277
+ const deltaY = snapshot.translateY - this.translateY;
278
+ snapshot.translateX = x + deltaX;
279
+ snapshot.translateY = y + deltaY;
280
+ });
281
+ this.translateX = x;
282
+ this.translateY = y;
283
+ this._core.store.objects.update(this);
284
+ }
285
+ /**
286
+ * Capture snapshots of current object states for undo/redo operations
287
+ * @param cachedObjects Optional pre-fetched objects array to avoid redundant getter calls
288
+ */
289
+ captureUnchangedSnapshots(cachedObjects) {
290
+ this.unchangedObjectSnapshots.clear();
291
+ this.snapshotRotation = this.rotation;
292
+ this.snapshotWidth = this.width;
293
+ this.snapshotHeight = this.height;
294
+ this.snapshotTranslateX = this.translateX;
295
+ this.snapshotTranslateY = this.translateY;
296
+ const children = cachedObjects ?? this.objects;
297
+ children.forEach(obj => {
298
+ this.unchangedObjectSnapshots.set(obj.id, {
299
+ id: obj.id,
300
+ translateX: obj.translateX,
301
+ translateY: obj.translateY,
302
+ rotation: obj.rotation,
303
+ width: obj.width,
304
+ height: obj.height,
305
+ totalWidth: obj.totalWidth,
306
+ totalHeight: obj.totalHeight,
307
+ scale: obj.scale,
308
+ });
309
+ });
310
+ }
311
+ /**
312
+ * Serializes the selection group to a plain object for persistence or transmission.
313
+ * Excludes internal references like _core, element refs, and cached data.
314
+ * Converts the unchangedObjectSnapshots Map to a plain object for JSON compatibility.
315
+ * @returns A serializable object representation of the selection group
316
+ */
317
+ serialize() {
318
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, _cachedObjects, _cachedObjectIdsHash, snapshotWidth, snapshotHeight, snapshotTranslateX, snapshotTranslateY, snapshotRotation, ...remainingProps } = this;
319
+ const clonedProps = structuredClone(remainingProps);
320
+ // Ensure objectIds is serialized with the correct key name (getter returns _objectIds)
321
+ clonedProps.objectIds = this.objectIds;
322
+ delete clonedProps._objectIds;
323
+ if (element && typeof element === 'object' && 'nodeType' in element && element.nodeType === 1) {
324
+ clonedProps.element = element.cloneNode(true);
325
+ }
326
+ // Convert Map to plain object for serialization
327
+ clonedProps.unchangedObjectSnapshots = Object.fromEntries(this.unchangedObjectSnapshots);
328
+ return clonedProps;
329
+ }
330
+ /**
331
+ * Deserializes a plain object back into a KritzelSelectionGroup instance.
332
+ * Restores objectIds through the setter and reconstructs the unchangedObjectSnapshots Map.
333
+ * @template T - The return type for the deserialized object
334
+ * @param object - The serialized object containing the selection group data
335
+ * @returns The deserialized selection group instance cast to type T
336
+ */
337
+ deserialize(object) {
338
+ // First, deserialize all base properties using parent's deserialize
339
+ super.deserialize(object);
340
+ // Selection groups intentionally persist their selected state.
341
+ this.isSelected = object.isSelected === true;
342
+ // Restore objectIds through setter
343
+ if (object.objectIds) {
344
+ this.objectIds = object.objectIds;
345
+ }
346
+ // Restore the Map from serialized object
347
+ if (object.unchangedObjectSnapshots) {
348
+ this.unchangedObjectSnapshots = new Map(Object.entries(object.unchangedObjectSnapshots));
349
+ }
350
+ if (this.shouldSyncChildSelectionState()) {
351
+ this.syncChildSelectionState(this.objects);
352
+ }
353
+ return this;
354
+ }
355
+ /**
356
+ * Persists the selection group's current state to the store.
357
+ * Only updates the selection group itself, not child objects.
358
+ * Child objects are updated during their respective move/resize/rotate operations
359
+ * to avoid redundant Yjs updates.
360
+ */
361
+ update() {
362
+ // Only update the selection group itself
363
+ // Child objects are already updated during move/resize/rotate operations
364
+ // Updating them again here would create redundant y.js updates
365
+ this._core.store.objects.update(this);
366
+ }
367
+ /**
368
+ * Moves the selection group and all its contained objects by calculating the delta from drag coordinates.
369
+ * Updates anchor points for any lines connected to the moved objects.
370
+ *
371
+ * Child objects are moved locally without writing to Yjs during the drag to avoid
372
+ * N+1 full serializations per frame, which can overwhelm the WebSocket server.
373
+ * Children must be persisted at drag end via {@link persistChildren}.
374
+ *
375
+ * @param startX - The starting x-coordinate of the drag operation (in screen space)
376
+ * @param startY - The starting y-coordinate of the drag operation (in screen space)
377
+ * @param endX - The ending x-coordinate of the drag operation (in screen space)
378
+ * @param endY - The ending y-coordinate of the drag operation (in screen space)
379
+ */
380
+ move(startX, startY, endX, endY) {
381
+ const deltaX = (startX - endX) / this._core.store.state.scale;
382
+ const deltaY = (startY - endY) / this._core.store.state.scale;
383
+ this.translateX += deltaX;
384
+ this.translateY += deltaY;
385
+ const now = Date.now();
386
+ const shouldPersistChildren = now - this._lastChildPersistTime >= KritzelSelectionGroup.CHILD_PERSIST_THROTTLE_MS;
387
+ if (shouldPersistChildren) {
388
+ // Periodically persist children to Yjs so remote peers see movement in real-time
389
+ this._core.store.objects.transaction(() => {
390
+ this._core.store.objects.update(this);
391
+ const children = this.objects;
392
+ for (const obj of children) {
393
+ obj.move(startX, startY, endX, endY);
394
+ this._core.anchorManager.updateAnchorsForObject(obj.id);
395
+ }
396
+ });
397
+ this._lastChildPersistTime = now;
398
+ }
399
+ else {
400
+ // Between throttle intervals, only persist the selection group itself.
401
+ // Children are moved locally for smooth local rendering.
402
+ this._core.store.objects.update(this);
403
+ this._core.store.objects.withLocalUpdatesOnly(() => {
404
+ const children = this.objects;
405
+ for (const obj of children) {
406
+ obj.move(startX, startY, endX, endY);
407
+ this._core.anchorManager.updateAnchorsForObject(obj.id);
408
+ }
409
+ });
410
+ }
411
+ // Update snapshots
412
+ this.unchangedObjectSnapshots.forEach(snapshot => {
413
+ snapshot.translateX += deltaX;
414
+ snapshot.translateY += deltaY;
415
+ });
416
+ }
417
+ /**
418
+ * Persists all child objects to Yjs in a single transaction.
419
+ * Must be called after a drag operation completes to sync the final
420
+ * positions of children that were only updated locally during the drag.
421
+ */
422
+ persistChildren() {
423
+ this._core.store.objects.transaction(() => {
424
+ const children = this.objects;
425
+ for (const obj of children) {
426
+ this._core.store.objects.update(obj);
427
+ }
428
+ });
429
+ }
430
+ /**
431
+ * Returns true when the selection contains a line anchored to an object
432
+ * that is not part of this selection group.
433
+ */
434
+ hasExternalAnchoredLineConnections(cachedObjects) {
435
+ const children = cachedObjects ?? this.objects;
436
+ if (children.length === 0) {
437
+ return false;
438
+ }
439
+ const selectedIds = new Set(children.map(child => child.id));
440
+ for (const child of children) {
441
+ if (!KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
442
+ continue;
443
+ }
444
+ const line = child;
445
+ if (line.startAnchor && !selectedIds.has(line.startAnchor.objectId)) {
446
+ return true;
447
+ }
448
+ if (line.endAnchor && !selectedIds.has(line.endAnchor.objectId)) {
449
+ return true;
450
+ }
451
+ }
452
+ return false;
453
+ }
454
+ /**
455
+ * Resizes the selection group and scales all contained objects proportionally.
456
+ * Uses snapshot values to avoid compounding errors during continuous drag operations.
457
+ * Handles rotation by transforming coordinates into and out of the group's local space.
458
+ * Updates anchor points for any lines connected to resized objects.
459
+ * @param x - The new x-coordinate for the selection group's position
460
+ * @param y - The new y-coordinate for the selection group's position
461
+ * @param width - The new width for the selection group
462
+ * @param height - The new height for the selection group
463
+ */
464
+ resize(x, y, width, height) {
465
+ // Use snapshot dimensions for scale factor calculation to avoid compounding errors during drag
466
+ const widthScaleFactor = width / this.snapshotWidth;
467
+ const heightScaleFactor = height / this.snapshotHeight;
468
+ // Calculate old center from snapshot position
469
+ const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
470
+ const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
471
+ const oldCenterX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
472
+ const oldCenterY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
473
+ // Calculate new center
474
+ const newTotalWidth = width + this.padding * 2;
475
+ const newTotalHeight = height + this.padding * 2;
476
+ const newCenterX = x + newTotalWidth / 2 / this.scale;
477
+ const newCenterY = y + newTotalHeight / 2 / this.scale;
478
+ const rotation = this.rotation;
479
+ const cos = Math.cos(-rotation);
480
+ const sin = Math.sin(-rotation);
481
+ const cosR = Math.cos(rotation);
482
+ const sinR = Math.sin(rotation);
483
+ this._core.store.objects.transaction(() => {
484
+ // Cache objects once to avoid repeated getter calls during resize
485
+ const children = this.objects;
486
+ children.forEach(child => {
487
+ // Use snapshot values for child to avoid compounding errors during drag
488
+ const snapshot = this.unchangedObjectSnapshots.get(child.id);
489
+ if (!snapshot)
490
+ return;
491
+ // Calculate child center from snapshot position
492
+ const childCenterX = snapshot.translateX + snapshot.totalWidth / 2 / snapshot.scale;
493
+ const childCenterY = snapshot.translateY + snapshot.totalHeight / 2 / snapshot.scale;
494
+ // Vector from old group center to child center
495
+ const dx = childCenterX - oldCenterX;
496
+ const dy = childCenterY - oldCenterY;
497
+ // Rotate to local space (align with group axes)
498
+ const localX = dx * cos - dy * sin;
499
+ const localY = dx * sin + dy * cos;
500
+ // Scale in local space
501
+ const scaledLocalX = localX * widthScaleFactor;
502
+ const scaledLocalY = localY * heightScaleFactor;
503
+ // Rotate back to world space
504
+ const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
505
+ const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
506
+ // New child center
507
+ const newChildCenterX = newCenterX + rotatedX;
508
+ const newChildCenterY = newCenterY + rotatedY;
509
+ // New child top-left
510
+ // Calculate relative rotation using snapshot rotation
511
+ const relativeRotation = snapshot.rotation - rotation;
512
+ const cosRel = Math.cos(relativeRotation);
513
+ const sinRel = Math.sin(relativeRotation);
514
+ // Project the group's scale factors onto the child's local axes
515
+ // We use absolute values because scaling is magnitude-based
516
+ // If the child is aligned (0 deg), cos=1, sin=0 -> scales match
517
+ // If the child is 90 deg, cos=0, sin=1 -> scales swap
518
+ const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
519
+ const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
520
+ // Use snapshot dimensions to calculate new size, avoiding compounding errors
521
+ const updatedWidth = snapshot.width * newChildWidthScale;
522
+ const updatedHeight = snapshot.height * newChildHeightScale;
523
+ const updatedTotalWidth = updatedWidth + child.padding * 2;
524
+ const updatedTotalHeight = updatedHeight + child.padding * 2;
525
+ const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
526
+ const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
527
+ child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
528
+ // Update any lines that are anchored to this child object
529
+ this._core.anchorManager.updateAnchorsForObject(child.id);
530
+ });
531
+ // Refresh dimensions and update the SelectionGroup to propagate changes to other tabs
532
+ // Note: Don't capture new snapshots here - we need to preserve the original snapshots
533
+ // until the transform operation is complete to avoid compounding errors during drag
534
+ this.refreshObjectDimensions(children);
535
+ this._core.store.objects.update(this);
536
+ });
537
+ }
538
+ /**
539
+ * Rotates the selection group and all contained objects around the group's center.
540
+ * Objects rotate around the group center while also adjusting their individual rotations.
541
+ * Uses snapshot values to calculate incremental rotation from the initial state.
542
+ * @param value - The new rotation angle in radians for the selection group
543
+ */
544
+ rotate(value) {
545
+ this.rotation = value;
546
+ // Use the snapshot center as the rotation pivot rather than the live center.
547
+ // refreshObjectDimensions() below can grow the box when an anchored line is pinned
548
+ // to an external object; keeping the pivot fixed prevents the children from drifting.
549
+ const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
550
+ const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
551
+ const centerX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
552
+ const centerY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
553
+ const angle = value - this.snapshotRotation;
554
+ const cos = Math.cos(angle);
555
+ const sin = Math.sin(angle);
556
+ // Cache objects once to avoid repeated getter calls during rotation
557
+ const children = this.objects;
558
+ this._core.store.objects.transaction(() => {
559
+ // Update the SelectionGroup itself to propagate rotation to other tabs
560
+ this._core.store.objects.update(this);
561
+ const childCount = children.length;
562
+ for (const child of children) {
563
+ const unchangedSnapshot = this.unchangedObjectSnapshots.get(child.id);
564
+ if (!unchangedSnapshot)
565
+ continue;
566
+ const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
567
+ const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
568
+ const rotatedX = cos * offsetX - sin * offsetY;
569
+ const rotatedY = sin * offsetX + cos * offsetY;
570
+ child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
571
+ child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
572
+ child.rotate(childCount === 1 ? value : unchangedSnapshot.rotation + angle);
573
+ }
574
+ // Re-sync any anchored lines so their stored geometry follows the rotated objects
575
+ // (including lines anchored to an object outside this selection), then grow the
576
+ // selection box to enclose the reshaped lines. Rotation sets state.isRotating
577
+ // rather than state.isDragging, so the refresh must be triggered explicitly here.
578
+ const anchorTargetIds = new Set();
579
+ for (const child of children) {
580
+ anchorTargetIds.add(child.id);
581
+ if (KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
582
+ const line = child;
583
+ if (line.startAnchor)
584
+ anchorTargetIds.add(line.startAnchor.objectId);
585
+ if (line.endAnchor)
586
+ anchorTargetIds.add(line.endAnchor.objectId);
587
+ }
588
+ }
589
+ let hasAnchoredLines = false;
590
+ for (const targetId of anchorTargetIds) {
591
+ if (this._core.anchorManager.getLinesAnchoredTo(targetId).length > 0) {
592
+ hasAnchoredLines = true;
593
+ this._core.anchorManager.updateAnchorsForObject(targetId);
594
+ }
595
+ }
596
+ if (hasAnchoredLines) {
597
+ this.refreshObjectDimensions(children);
598
+ }
599
+ });
600
+ }
601
+ /**
602
+ * Creates a deep copy of the selection group including copies of all contained objects.
603
+ * Preserves z-index ordering by sorting children before copying.
604
+ * Copies maintain their relative positions within the new selection group.
605
+ * @returns A new KritzelSelectionGroup with copies of all contained objects
606
+ */
607
+ copy() {
608
+ const selectionGroup = KritzelSelectionGroup.create(this._core);
609
+ const children = this.objects;
610
+ // Sort children by zIndex and copy them
611
+ const sortedChildren = [...children].sort((a, b) => a.zIndex - b.zIndex);
612
+ for (const obj of sortedChildren) {
613
+ const copiedObject = obj.copy();
614
+ selectionGroup.addOrRemove(copiedObject);
615
+ }
616
+ selectionGroup.captureUnchangedSnapshots();
617
+ if (children.length === 1) {
618
+ selectionGroup.rotation = children[0].rotation;
619
+ }
620
+ return selectionGroup;
621
+ }
622
+ /**
623
+ * Refreshes the selection group's bounding box dimensions based on contained objects.
624
+ * @param cachedObjects Optional pre-fetched objects array to avoid redundant getter calls
625
+ * @param skipPersist If true, only updates local state without persisting to Yjs (used during undo/redo)
626
+ */
627
+ refreshObjectDimensions(cachedObjects, skipPersist = false) {
628
+ const previous = {
629
+ minX: this.minX,
630
+ minY: this.minY,
631
+ maxX: this.maxX,
632
+ maxY: this.maxY,
633
+ width: this.width,
634
+ height: this.height,
635
+ translateX: this.translateX,
636
+ translateY: this.translateY,
637
+ };
638
+ const children = cachedObjects ?? this.objects;
639
+ if (children.length === 1) {
640
+ const obj = children[0];
641
+ this.minX = obj.boundingBox.x / this.scale;
642
+ this.maxX = obj.boundingBox.x / this.scale + obj.boundingBox.width;
643
+ this.minY = obj.boundingBox.y / this.scale;
644
+ this.maxY = obj.boundingBox.y / this.scale + obj.boundingBox.height;
645
+ this.translateX = (this.minX - this.padding) * this.scale;
646
+ this.translateY = (this.minY - this.padding) * this.scale;
647
+ this.width = (this.maxX - this.minX - this.padding) * this.scale;
648
+ this.height = (this.maxY - this.minY - this.padding) * this.scale;
649
+ }
650
+ else {
651
+ const rotation = this.rotation;
652
+ const cos = Math.cos(-rotation);
653
+ const sin = Math.sin(-rotation);
654
+ let minX = Infinity;
655
+ let maxX = -Infinity;
656
+ let minY = Infinity;
657
+ let maxY = -Infinity;
658
+ children.forEach(obj => {
659
+ const polygon = obj.rotatedPolygon;
660
+ const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
661
+ corners.forEach(corner => {
662
+ // Rotate corner into local space (aligned with group rotation)
663
+ const rx = corner.x * cos - corner.y * sin;
664
+ const ry = corner.x * sin + corner.y * cos;
665
+ if (rx < minX)
666
+ minX = rx;
667
+ if (rx > maxX)
668
+ maxX = rx;
669
+ if (ry < minY)
670
+ minY = ry;
671
+ if (ry > maxY)
672
+ maxY = ry;
673
+ });
674
+ });
675
+ // Dimensions in world units (unrotated)
676
+ const worldWidth = maxX - minX;
677
+ const worldHeight = maxY - minY;
678
+ this.width = (worldWidth - this.padding) * this.scale;
679
+ this.height = (worldHeight - this.padding) * this.scale;
680
+ // Center of the box in rotated space
681
+ const cRx = (minX + maxX) / 2;
682
+ const cRy = (minY + maxY) / 2;
683
+ // Rotate center back to world space
684
+ const cosR = Math.cos(rotation);
685
+ const sinR = Math.sin(rotation);
686
+ const cx = cRx * cosR - cRy * sinR;
687
+ const cy = cRx * sinR + cRy * cosR;
688
+ this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
689
+ this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
690
+ }
691
+ const hasChanged = Math.abs(this.minX - previous.minX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
692
+ Math.abs(this.minY - previous.minY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
693
+ Math.abs(this.maxX - previous.maxX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
694
+ Math.abs(this.maxY - previous.maxY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
695
+ Math.abs(this.width - previous.width) > KritzelSelectionGroup.DIMENSION_EPSILON ||
696
+ Math.abs(this.height - previous.height) > KritzelSelectionGroup.DIMENSION_EPSILON ||
697
+ Math.abs(this.translateX - previous.translateX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
698
+ Math.abs(this.translateY - previous.translateY) > KritzelSelectionGroup.DIMENSION_EPSILON;
699
+ if (!skipPersist && hasChanged) {
700
+ this._core.store.objects.update(this);
701
+ }
702
+ return hasChanged;
703
+ }
704
+ /**
705
+ * Calculates the horizontal offset from the group's center to an object's center using snapshot data.
706
+ * Used during rotation to maintain relative positioning of objects.
707
+ * @param snapshot - The snapshot containing the object's recorded position and dimensions
708
+ * @returns The x-axis offset from the group center to the object center
709
+ */
710
+ getOffsetXToCenterFromSnapshot(snapshot) {
711
+ const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
712
+ const groupCenterX = this.snapshotTranslateX + (this.snapshotWidth + this.padding * 2) / this.scale / 2;
713
+ return objCenterX - groupCenterX;
714
+ }
715
+ /**
716
+ * Calculates the vertical offset from the group's center to an object's center using snapshot data.
717
+ * Used during rotation to maintain relative positioning of objects.
718
+ * @param snapshot - The snapshot containing the object's recorded position and dimensions
719
+ * @returns The y-axis offset from the group center to the object center
720
+ */
721
+ getOffsetYToCenterFromSnapshot(snapshot) {
722
+ const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
723
+ const groupCenterY = this.snapshotTranslateY + (this.snapshotHeight + this.padding * 2) / this.scale / 2;
724
+ return objCenterY - groupCenterY;
725
+ }
726
+ /**
727
+ * Tests whether a point intersects with the selection group or any of its contained objects.
728
+ * For a single line object, delegates hit testing to the line's own hit test (more precise).
729
+ * For other cases, first checks the group's rotated polygon bounding box, then individual objects.
730
+ * @param x - The x-coordinate of the point to test (in world space)
731
+ * @param y - The y-coordinate of the point to test (in world space)
732
+ * @returns True if the point is inside the selection group or any contained object, false otherwise
733
+ */
734
+ hitTest(x, y) {
735
+ const children = this.objects;
736
+ const isSingleLine = children.length === 1 && children[0].__class__ === 'KritzelLine';
737
+ if (isSingleLine) {
738
+ return children[0].hitTest(x, y);
739
+ }
740
+ const polygon = this.rotatedPolygon;
741
+ const polyPoints = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
742
+ if (KritzelGeometryHelper.isPointInPolygon({ x, y }, polyPoints)) {
743
+ return true;
744
+ }
745
+ for (const obj of children) {
746
+ if (obj.hitTest(x, y)) {
747
+ return true;
748
+ }
749
+ }
750
+ return false;
751
+ }
752
+ }