@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.
- package/LICENSE.md +50 -0
- package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
- package/dist/cjs/engine.constants-CakmdKMW.js +28206 -0
- package/dist/cjs/index-BbbWU2aN.js +1742 -0
- package/dist/cjs/index.cjs.js +4232 -0
- package/dist/cjs/kritzel-awareness-cursors_5.cjs.entry.js +28004 -0
- package/dist/cjs/loader.cjs.js +13 -0
- package/dist/cjs/stencil.cjs.js +25 -0
- package/dist/collection/classes/core/core.class.js +1389 -0
- package/dist/collection/classes/core/reviver.class.js +124 -0
- package/dist/collection/classes/core/store.class.js +337 -0
- package/dist/collection/classes/core/viewport.class.js +794 -0
- package/dist/collection/classes/core/workspace.class.js +112 -0
- package/dist/collection/classes/handlers/base.handler.js +20 -0
- package/dist/collection/classes/handlers/context-menu.handler.js +170 -0
- package/dist/collection/classes/handlers/hover.handler.js +35 -0
- package/dist/collection/classes/handlers/key.handler.js +162 -0
- package/dist/collection/classes/handlers/line-handle.handler.js +475 -0
- package/dist/collection/classes/handlers/move.handler.js +295 -0
- package/dist/collection/classes/handlers/resize.handler.js +327 -0
- package/dist/collection/classes/handlers/rotation.handler.js +174 -0
- package/dist/collection/classes/handlers/selection.handler.js +526 -0
- package/dist/collection/classes/managers/anchor.manager.js +1476 -0
- package/dist/collection/classes/managers/browser-permission.manager.js +64 -0
- package/dist/collection/classes/managers/clipboard.manager.js +346 -0
- package/dist/collection/classes/managers/clipboard.types.js +1 -0
- package/dist/collection/classes/managers/cursor.manager.js +139 -0
- package/dist/collection/classes/managers/license.manager.js +341 -0
- package/dist/collection/classes/managers/localization.manager.js +227 -0
- package/dist/collection/classes/managers/theme.manager.js +206 -0
- package/dist/collection/classes/objects/base-object.class.js +462 -0
- package/dist/collection/classes/objects/dynamic-object.class.js +437 -0
- package/dist/collection/classes/objects/group.class.js +590 -0
- package/dist/collection/classes/objects/image.class.js +500 -0
- package/dist/collection/classes/objects/line.class.js +789 -0
- package/dist/collection/classes/objects/path.class.js +578 -0
- package/dist/collection/classes/objects/selection-box.class.js +32 -0
- package/dist/collection/classes/objects/selection-group.class.js +752 -0
- package/dist/collection/classes/objects/shape.class.js +628 -0
- package/dist/collection/classes/objects/text.class.js +461 -0
- package/dist/collection/classes/providers/assets/asset-resolver.class.js +536 -0
- package/dist/collection/classes/providers/assets/http-asset-provider.class.js +111 -0
- package/dist/collection/classes/providers/assets/indexeddb-asset-provider.class.js +241 -0
- package/dist/collection/classes/providers/assets/presigned-asset-provider.class.js +86 -0
- package/dist/collection/classes/providers/paste-intent/event-paste-intent.provider.js +48 -0
- package/dist/collection/classes/providers/paste-intent/ios-paste-intent.provider.js +16 -0
- package/dist/collection/classes/providers/paste-intent/navigator-paste-intent.provider.js +86 -0
- package/dist/collection/classes/providers/sync/broadcast-sync-provider.class.js +100 -0
- package/dist/collection/classes/providers/sync/hocuspocus-sync-provider.class.js +333 -0
- package/dist/collection/classes/providers/sync/in-memory-sync-provider.class.js +69 -0
- package/dist/collection/classes/providers/sync/indexeddb-sync-provider.class.js +39 -0
- package/dist/collection/classes/providers/sync/websocket-sync-provider.class.js +110 -0
- package/dist/collection/classes/registries/dynamic-object-renderer.registry.js +52 -0
- package/dist/collection/classes/registries/font-registry.class.js +161 -0
- package/dist/collection/classes/registries/icon-registry.class.js +118 -0
- package/dist/collection/classes/registries/paste-intent-provider.registry.js +26 -0
- package/dist/collection/classes/registries/tool.registry.js +69 -0
- package/dist/collection/classes/structures/app-state-map.structure.js +461 -0
- package/dist/collection/classes/structures/object-map.structure.js +1103 -0
- package/dist/collection/classes/structures/quadtree.structure.js +251 -0
- package/dist/collection/classes/tools/base-tool.class.js +88 -0
- package/dist/collection/classes/tools/brush-tool.class.js +236 -0
- package/dist/collection/classes/tools/eraser-tool.class.js +133 -0
- package/dist/collection/classes/tools/image-tool.class.js +152 -0
- package/dist/collection/classes/tools/line-tool.class.js +249 -0
- package/dist/collection/classes/tools/selection-tool.class.js +611 -0
- package/dist/collection/classes/tools/shape-tool.class.js +251 -0
- package/dist/collection/classes/tools/text-tool.class.js +179 -0
- package/dist/collection/collection-manifest.json +17 -0
- package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.css +115 -0
- package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.js +380 -0
- package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.css +10 -0
- package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.js +153 -0
- package/dist/collection/components/kritzel-engine/kritzel-engine.css +81 -0
- package/dist/collection/components/kritzel-engine/kritzel-engine.js +6345 -0
- package/dist/collection/components/kritzel-icon/kritzel-icon.css +18 -0
- package/dist/collection/components/kritzel-icon/kritzel-icon.js +93 -0
- package/dist/collection/components/kritzel-watermark/kritzel-watermark.css +29 -0
- package/dist/collection/components/kritzel-watermark/kritzel-watermark.js +83 -0
- package/dist/collection/configs/default-asset-storage.config.js +9 -0
- package/dist/collection/configs/default-brush-tool.config.js +9 -0
- package/dist/collection/configs/default-engine-config.js +73 -0
- package/dist/collection/configs/default-line-tool.config.js +12 -0
- package/dist/collection/configs/default-shape-tool.config.js +15 -0
- package/dist/collection/configs/default-sync.config.js +6 -0
- package/dist/collection/configs/default-text-tool.config.js +12 -0
- package/dist/collection/constants/color-palette.constants.js +40 -0
- package/dist/collection/constants/core.constants.js +1 -0
- package/dist/collection/constants/engine.constants.js +4 -0
- package/dist/collection/constants/license.constants.js +25 -0
- package/dist/collection/constants/schema.constants.js +4 -0
- package/dist/collection/constants/storage.constants.js +1 -0
- package/dist/collection/constants/stroke-size.constants.js +2 -0
- package/dist/collection/constants/text-tool-fonts.constants.js +18 -0
- package/dist/collection/constants/version.js +6 -0
- package/dist/collection/enums/alignment.enum.js +9 -0
- package/dist/collection/enums/event-button.enum.js +6 -0
- package/dist/collection/enums/handle-type.enum.js +7 -0
- package/dist/collection/enums/shape-type.enum.js +6 -0
- package/dist/collection/events/active-workspace-change.event.js +1 -0
- package/dist/collection/events/editor-is-ready.event.js +1 -0
- package/dist/collection/events/login.event.js +1 -0
- package/dist/collection/helpers/alignment.helper.js +73 -0
- package/dist/collection/helpers/class.helper.js +5 -0
- package/dist/collection/helpers/color.helper.js +106 -0
- package/dist/collection/helpers/cursor.helper.js +57 -0
- package/dist/collection/helpers/devices.helper.js +28 -0
- package/dist/collection/helpers/event.helper.js +58 -0
- package/dist/collection/helpers/geometry.helper.js +149 -0
- package/dist/collection/helpers/html.helper.js +116 -0
- package/dist/collection/helpers/image-file.helper.js +6 -0
- package/dist/collection/helpers/keyboard.helper.js +55 -0
- package/dist/collection/helpers/localization.helper.js +25 -0
- package/dist/collection/helpers/math.helper.js +8 -0
- package/dist/collection/helpers/migration.helper.js +45 -0
- package/dist/collection/helpers/object.helper.js +29 -0
- package/dist/collection/helpers/svg-export.helper.js +655 -0
- package/dist/collection/helpers/theme.helper.js +77 -0
- package/dist/collection/helpers/tool-config.helper.js +63 -0
- package/dist/collection/helpers/wheel.helper.js +113 -0
- package/dist/collection/index.js +107 -0
- package/dist/collection/interfaces/anchor.interface.js +1 -0
- package/dist/collection/interfaces/arrow-head.interface.js +1 -0
- package/dist/collection/interfaces/asset-provider.interface.js +1 -0
- package/dist/collection/interfaces/asset-storage-config.interface.js +1 -0
- package/dist/collection/interfaces/asset.interface.js +1 -0
- package/dist/collection/interfaces/bounding-box.interface.js +1 -0
- package/dist/collection/interfaces/clonable.interface.js +1 -0
- package/dist/collection/interfaces/context-menu-item.interface.js +1 -0
- package/dist/collection/interfaces/context-menu-state.interface.js +1 -0
- package/dist/collection/interfaces/debug-info.interface.js +1 -0
- package/dist/collection/interfaces/dialog.interface.js +1 -0
- package/dist/collection/interfaces/document-metadata.interface.js +1 -0
- package/dist/collection/interfaces/dynamic-object-renderer.interface.js +1 -0
- package/dist/collection/interfaces/engine-state.interface.js +1 -0
- package/dist/collection/interfaces/font.interface.js +1 -0
- package/dist/collection/interfaces/localization.interface.js +1 -0
- package/dist/collection/interfaces/login-config.interface.js +1 -0
- package/dist/collection/interfaces/master-detail.interface.js +1 -0
- package/dist/collection/interfaces/menu-item.interface.js +1 -0
- package/dist/collection/interfaces/migration.interface.js +1 -0
- package/dist/collection/interfaces/notification.interface.js +1 -0
- package/dist/collection/interfaces/object-change-event.interface.js +1 -0
- package/dist/collection/interfaces/object-export.interface.js +1 -0
- package/dist/collection/interfaces/object.interface.js +1 -0
- package/dist/collection/interfaces/paste-intent-provider.interface.js +1 -0
- package/dist/collection/interfaces/point.interface.js +1 -0
- package/dist/collection/interfaces/polygon.interface.js +1 -0
- package/dist/collection/interfaces/remote-cursor.interface.js +1 -0
- package/dist/collection/interfaces/selection-state.interface.js +1 -0
- package/dist/collection/interfaces/serializable.interface.js +1 -0
- package/dist/collection/interfaces/settings.interface.js +1 -0
- package/dist/collection/interfaces/share.interface.js +1 -0
- package/dist/collection/interfaces/shortcut.interface.js +1 -0
- package/dist/collection/interfaces/sync-config.interface.js +21 -0
- package/dist/collection/interfaces/sync-provider.interface.js +1 -0
- package/dist/collection/interfaces/theme.interface.js +1 -0
- package/dist/collection/interfaces/tool-config.interface.js +1 -0
- package/dist/collection/interfaces/tool-type.interface.js +1 -0
- package/dist/collection/interfaces/tool.interface.js +1 -0
- package/dist/collection/interfaces/toolbar-item.interface.js +1 -0
- package/dist/collection/interfaces/undo-state.interface.js +1 -0
- package/dist/collection/interfaces/user.interface.js +1 -0
- package/dist/collection/interfaces/viewport-state.interface.js +1 -0
- package/dist/collection/locales/de-locale.js +119 -0
- package/dist/collection/locales/en-locale.js +120 -0
- package/dist/collection/locales/fr-locale.js +119 -0
- package/dist/collection/migrations/app-state.migrations.js +47 -0
- package/dist/collection/migrations/workspace.migrations.js +67 -0
- package/dist/collection/themes/dark-theme.js +340 -0
- package/dist/collection/themes/light-theme.js +346 -0
- package/dist/collection/types/deep-readonly.type.js +1 -0
- package/dist/collection/types/state.types.js +1 -0
- package/dist/components/index.d.ts +47 -0
- package/dist/components/index.js +4143 -0
- package/dist/components/kritzel-awareness-cursors.d.ts +11 -0
- package/dist/components/kritzel-awareness-cursors.js +6 -0
- package/dist/components/kritzel-cursor-trail.d.ts +11 -0
- package/dist/components/kritzel-cursor-trail.js +6 -0
- package/dist/components/kritzel-engine.d.ts +11 -0
- package/dist/components/kritzel-engine.js +52641 -0
- package/dist/components/kritzel-icon.d.ts +11 -0
- package/dist/components/kritzel-icon.js +6 -0
- package/dist/components/kritzel-watermark.d.ts +11 -0
- package/dist/components/kritzel-watermark.js +6 -0
- package/dist/components/p-B2Htv9ou.js +49 -0
- package/dist/components/p-BvRGQvVI.js +3456 -0
- package/dist/components/p-CfSx6HU_.js +1469 -0
- package/dist/components/p-YP8wAcF4.js +122 -0
- package/dist/components/p-fx6msYHJ.js +167 -0
- package/dist/esm/app-globals-DQuL1Twl.js +3 -0
- package/dist/esm/engine.constants-Bl5-pbq-.js +28143 -0
- package/dist/esm/index-BU9Mv7y0.js +1715 -0
- package/dist/esm/index.js +4134 -0
- package/dist/esm/kritzel-awareness-cursors_5.entry.js +27978 -0
- package/dist/esm/loader.js +11 -0
- package/dist/esm/stencil.js +21 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/stencil/index.esm.js +4134 -0
- package/dist/stencil/p-BU9Mv7y0.js +1702 -0
- package/dist/stencil/p-Bl5-pbq-.js +28143 -0
- package/dist/stencil/p-DQuL1Twl.js +3 -0
- package/dist/stencil/p-f0a8b7af.entry.js +27978 -0
- package/dist/stencil/stencil.esm.js +21 -0
- package/dist/types/classes/core/core.class.d.ts +532 -0
- package/dist/types/classes/core/reviver.class.d.ts +32 -0
- package/dist/types/classes/core/store.class.d.ts +198 -0
- package/dist/types/classes/core/viewport.class.d.ts +256 -0
- package/dist/types/classes/core/workspace.class.d.ts +95 -0
- package/dist/types/classes/handlers/base.handler.d.ts +18 -0
- package/dist/types/classes/handlers/context-menu.handler.d.ts +67 -0
- package/dist/types/classes/handlers/hover.handler.d.ts +23 -0
- package/dist/types/classes/handlers/key.handler.d.ts +44 -0
- package/dist/types/classes/handlers/line-handle.handler.d.ts +115 -0
- package/dist/types/classes/handlers/move.handler.d.ts +94 -0
- package/dist/types/classes/handlers/resize.handler.d.ts +95 -0
- package/dist/types/classes/handlers/rotation.handler.d.ts +50 -0
- package/dist/types/classes/handlers/selection.handler.d.ts +153 -0
- package/dist/types/classes/managers/anchor.manager.d.ts +513 -0
- package/dist/types/classes/managers/browser-permission.manager.d.ts +41 -0
- package/dist/types/classes/managers/clipboard.manager.d.ts +71 -0
- package/dist/types/classes/managers/clipboard.types.d.ts +23 -0
- package/dist/types/classes/managers/cursor.manager.d.ts +66 -0
- package/dist/types/classes/managers/license.manager.d.ts +147 -0
- package/dist/types/classes/managers/localization.manager.d.ts +145 -0
- package/dist/types/classes/managers/theme.manager.d.ts +112 -0
- package/dist/types/classes/objects/base-object.class.d.ts +266 -0
- package/dist/types/classes/objects/dynamic-object.class.d.ts +148 -0
- package/dist/types/classes/objects/group.class.d.ts +231 -0
- package/dist/types/classes/objects/image.class.d.ts +246 -0
- package/dist/types/classes/objects/line.class.d.ts +285 -0
- package/dist/types/classes/objects/path.class.d.ts +220 -0
- package/dist/types/classes/objects/selection-box.class.d.ts +19 -0
- package/dist/types/classes/objects/selection-group.class.d.ts +267 -0
- package/dist/types/classes/objects/shape.class.d.ts +322 -0
- package/dist/types/classes/objects/text.class.d.ts +197 -0
- package/dist/types/classes/providers/assets/asset-resolver.class.d.ts +141 -0
- package/dist/types/classes/providers/assets/http-asset-provider.class.d.ts +79 -0
- package/dist/types/classes/providers/assets/indexeddb-asset-provider.class.d.ts +69 -0
- package/dist/types/classes/providers/assets/presigned-asset-provider.class.d.ts +69 -0
- package/dist/types/classes/providers/paste-intent/event-paste-intent.provider.d.ts +8 -0
- package/dist/types/classes/providers/paste-intent/ios-paste-intent.provider.d.ts +6 -0
- package/dist/types/classes/providers/paste-intent/navigator-paste-intent.provider.d.ts +9 -0
- package/dist/types/classes/providers/sync/broadcast-sync-provider.class.d.ts +23 -0
- package/dist/types/classes/providers/sync/hocuspocus-sync-provider.class.d.ts +163 -0
- package/dist/types/classes/providers/sync/in-memory-sync-provider.class.d.ts +39 -0
- package/dist/types/classes/providers/sync/indexeddb-sync-provider.class.d.ts +25 -0
- package/dist/types/classes/providers/sync/websocket-sync-provider.class.d.ts +63 -0
- package/dist/types/classes/registries/dynamic-object-renderer.registry.d.ts +32 -0
- package/dist/types/classes/registries/font-registry.class.d.ts +19 -0
- package/dist/types/classes/registries/icon-registry.class.d.ts +39 -0
- package/dist/types/classes/registries/paste-intent-provider.registry.d.ts +10 -0
- package/dist/types/classes/registries/tool.registry.d.ts +53 -0
- package/dist/types/classes/structures/app-state-map.structure.d.ts +171 -0
- package/dist/types/classes/structures/object-map.structure.d.ts +403 -0
- package/dist/types/classes/structures/quadtree.structure.d.ts +128 -0
- package/dist/types/classes/tools/base-tool.class.d.ts +72 -0
- package/dist/types/classes/tools/brush-tool.class.d.ts +68 -0
- package/dist/types/classes/tools/eraser-tool.class.d.ts +38 -0
- package/dist/types/classes/tools/image-tool.class.d.ts +74 -0
- package/dist/types/classes/tools/line-tool.class.d.ts +63 -0
- package/dist/types/classes/tools/selection-tool.class.d.ts +188 -0
- package/dist/types/classes/tools/shape-tool.class.d.ts +90 -0
- package/dist/types/classes/tools/text-tool.class.d.ts +66 -0
- package/dist/types/components/kritzel-awareness-cursors/kritzel-awareness-cursors.d.ts +29 -0
- package/dist/types/components/kritzel-cursor-trail/kritzel-cursor-trail.d.ts +19 -0
- package/dist/types/components/kritzel-engine/kritzel-engine.d.ts +695 -0
- package/dist/types/components/kritzel-icon/kritzel-icon.d.ts +6 -0
- package/dist/types/components/kritzel-watermark/kritzel-watermark.d.ts +20 -0
- package/dist/types/components.d.ts +981 -0
- package/dist/types/configs/default-asset-storage.config.d.ts +8 -0
- package/dist/types/configs/default-brush-tool.config.d.ts +2 -0
- package/dist/types/configs/default-engine-config.d.ts +2 -0
- package/dist/types/configs/default-line-tool.config.d.ts +2 -0
- package/dist/types/configs/default-shape-tool.config.d.ts +2 -0
- package/dist/types/configs/default-sync.config.d.ts +5 -0
- package/dist/types/configs/default-text-tool.config.d.ts +2 -0
- package/dist/types/constants/color-palette.constants.d.ts +31 -0
- package/dist/types/constants/core.constants.d.ts +0 -0
- package/dist/types/constants/engine.constants.d.ts +4 -0
- package/dist/types/constants/license.constants.d.ts +25 -0
- package/dist/types/constants/schema.constants.d.ts +4 -0
- package/dist/types/constants/storage.constants.d.ts +1 -0
- package/dist/types/constants/stroke-size.constants.d.ts +2 -0
- package/dist/types/constants/text-tool-fonts.constants.d.ts +3 -0
- package/dist/types/constants/version.d.ts +6 -0
- package/dist/types/enums/alignment.enum.d.ts +8 -0
- package/dist/types/enums/event-button.enum.d.ts +5 -0
- package/dist/types/enums/handle-type.enum.d.ts +6 -0
- package/dist/types/enums/shape-type.enum.d.ts +5 -0
- package/dist/types/events/active-workspace-change.event.d.ts +7 -0
- package/dist/types/events/editor-is-ready.event.d.ts +18 -0
- package/dist/types/events/login.event.d.ts +3 -0
- package/dist/types/helpers/alignment.helper.d.ts +21 -0
- package/dist/types/helpers/class.helper.d.ts +3 -0
- package/dist/types/helpers/color.helper.d.ts +34 -0
- package/dist/types/helpers/cursor.helper.d.ts +22 -0
- package/dist/types/helpers/devices.helper.d.ts +8 -0
- package/dist/types/helpers/event.helper.d.ts +6 -0
- package/dist/types/helpers/geometry.helper.d.ts +38 -0
- package/dist/types/helpers/html.helper.d.ts +8 -0
- package/dist/types/helpers/image-file.helper.d.ts +3 -0
- package/dist/types/helpers/keyboard.helper.d.ts +6 -0
- package/dist/types/helpers/localization.helper.d.ts +18 -0
- package/dist/types/helpers/math.helper.d.ts +4 -0
- package/dist/types/helpers/migration.helper.d.ts +30 -0
- package/dist/types/helpers/object.helper.d.ts +9 -0
- package/dist/types/helpers/svg-export.helper.d.ts +231 -0
- package/dist/types/helpers/theme.helper.d.ts +42 -0
- package/dist/types/helpers/tool-config.helper.d.ts +4 -0
- package/dist/types/helpers/wheel.helper.d.ts +9 -0
- package/dist/types/index.d.ts +109 -0
- package/dist/types/interfaces/anchor.interface.d.ts +147 -0
- package/dist/types/interfaces/arrow-head.interface.d.ts +27 -0
- package/dist/types/interfaces/asset-provider.interface.d.ts +88 -0
- package/dist/types/interfaces/asset-storage-config.interface.d.ts +40 -0
- package/dist/types/interfaces/asset.interface.d.ts +43 -0
- package/dist/types/interfaces/bounding-box.interface.d.ts +8 -0
- package/dist/types/interfaces/clonable.interface.d.ts +3 -0
- package/dist/types/interfaces/context-menu-item.interface.d.ts +25 -0
- package/dist/types/interfaces/context-menu-state.interface.d.ts +16 -0
- package/dist/types/interfaces/debug-info.interface.d.ts +7 -0
- package/dist/types/interfaces/dialog.interface.d.ts +4 -0
- package/dist/types/interfaces/document-metadata.interface.d.ts +6 -0
- package/dist/types/interfaces/dynamic-object-renderer.interface.d.ts +40 -0
- package/dist/types/interfaces/engine-state.interface.d.ts +82 -0
- package/dist/types/interfaces/font.interface.d.ts +15 -0
- package/dist/types/interfaces/localization.interface.d.ts +143 -0
- package/dist/types/interfaces/login-config.interface.d.ts +24 -0
- package/dist/types/interfaces/master-detail.interface.d.ts +14 -0
- package/dist/types/interfaces/menu-item.interface.d.ts +27 -0
- package/dist/types/interfaces/migration.interface.d.ts +16 -0
- package/dist/types/interfaces/notification.interface.d.ts +10 -0
- package/dist/types/interfaces/object-change-event.interface.d.ts +31 -0
- package/dist/types/interfaces/object-export.interface.d.ts +20 -0
- package/dist/types/interfaces/object.interface.d.ts +53 -0
- package/dist/types/interfaces/paste-intent-provider.interface.d.ts +10 -0
- package/dist/types/interfaces/point.interface.d.ts +4 -0
- package/dist/types/interfaces/polygon.interface.d.ts +7 -0
- package/dist/types/interfaces/remote-cursor.interface.d.ts +18 -0
- package/dist/types/interfaces/selection-state.interface.d.ts +7 -0
- package/dist/types/interfaces/serializable.interface.d.ts +5 -0
- package/dist/types/interfaces/settings.interface.d.ts +25 -0
- package/dist/types/interfaces/share.interface.d.ts +9 -0
- package/dist/types/interfaces/shortcut.interface.d.ts +10 -0
- package/dist/types/interfaces/sync-config.interface.d.ts +41 -0
- package/dist/types/interfaces/sync-provider.interface.d.ts +66 -0
- package/dist/types/interfaces/theme.interface.d.ts +553 -0
- package/dist/types/interfaces/tool-config.interface.d.ts +24 -0
- package/dist/types/interfaces/tool-type.interface.d.ts +1 -0
- package/dist/types/interfaces/tool.interface.d.ts +8 -0
- package/dist/types/interfaces/toolbar-item.interface.d.ts +66 -0
- package/dist/types/interfaces/undo-state.interface.d.ts +6 -0
- package/dist/types/interfaces/user.interface.d.ts +22 -0
- package/dist/types/interfaces/viewport-state.interface.d.ts +7 -0
- package/dist/types/locales/de-locale.d.ts +5 -0
- package/dist/types/locales/en-locale.d.ts +6 -0
- package/dist/types/locales/fr-locale.d.ts +5 -0
- package/dist/types/migrations/app-state.migrations.d.ts +11 -0
- package/dist/types/migrations/workspace.migrations.d.ts +11 -0
- package/dist/types/stencil-public-runtime.d.ts +1873 -0
- package/dist/types/themes/dark-theme.d.ts +5 -0
- package/dist/types/themes/light-theme.d.ts +5 -0
- package/dist/types/types/deep-readonly.type.d.ts +7 -0
- package/dist/types/types/state.types.d.ts +3 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +101 -0
- 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, '&')
|
|
650
|
+
.replace(/</g, '<')
|
|
651
|
+
.replace(/>/g, '>')
|
|
652
|
+
.replace(/"/g, '"')
|
|
653
|
+
.replace(/'/g, ''');
|
|
654
|
+
}
|
|
655
|
+
}
|