kritzel-stencil 0.3.18 → 0.3.20
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/dist/cjs/index-Xav9JFHg.js +2 -2
- package/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/{kritzel-active-users_44.cjs.entry.js → kritzel-active-users_45.cjs.entry.js} +739 -310
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{schema.constants-DrHO_CYF.js → schema.constants-IBG5VW_J.js} +116 -59
- package/dist/cjs/stencil.cjs.js +1 -1
- package/dist/collection/classes/core/core.class.js +22 -210
- package/dist/collection/classes/handlers/key.handler.js +1 -22
- package/dist/collection/classes/managers/browser-permission.manager.js +64 -0
- package/dist/collection/classes/managers/clipboard.manager.js +401 -0
- package/dist/collection/classes/objects/image.class.js +42 -0
- package/dist/collection/classes/tools/image-tool.class.js +1 -59
- package/dist/collection/collection-manifest.json +1 -0
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.css +53 -0
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +133 -53
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +101 -43
- package/dist/collection/components/core/kritzel-watermark/kritzel-watermark.js +1 -1
- package/dist/collection/components/shared/kritzel-avatar/kritzel-avatar.js +3 -3
- package/dist/collection/components/shared/kritzel-button/kritzel-button.js +2 -2
- package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
- package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
- package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
- package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
- package/dist/collection/components/shared/kritzel-input/kritzel-input.js +1 -1
- package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
- package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
- package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
- package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
- package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +1 -1
- package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
- package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
- package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
- package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
- package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +2 -2
- package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
- package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +6 -6
- package/dist/collection/components/ui/kritzel-current-user/kritzel-current-user.js +1 -1
- package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +1 -1
- package/dist/collection/components/ui/kritzel-export/kritzel-export.js +1 -1
- package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
- package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +1 -1
- package/dist/collection/components/ui/kritzel-notification-card/kritzel-notification-card.css +101 -0
- package/dist/collection/components/ui/kritzel-notification-card/kritzel-notification-card.js +173 -0
- package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +1 -1
- package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +2 -2
- package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
- package/dist/collection/components/ui/kritzel-zoom-panel/kritzel-zoom-panel.js +1 -1
- package/dist/collection/configs/default-engine-config.js +0 -1
- package/dist/collection/constants/version.js +1 -1
- package/dist/collection/interfaces/notification.interface.js +1 -0
- package/dist/collection/themes/dark-theme.js +36 -0
- package/dist/collection/themes/light-theme.js +36 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -1
- package/dist/components/kritzel-active-users.js +1 -1
- package/dist/components/kritzel-avatar.js +1 -1
- package/dist/components/kritzel-awareness-cursors.js +1 -1
- package/dist/components/kritzel-back-to-content.js +1 -1
- package/dist/components/kritzel-button.js +1 -1
- package/dist/components/kritzel-color-palette.js +1 -1
- package/dist/components/kritzel-color.js +1 -1
- package/dist/components/kritzel-controls.js +1 -1
- package/dist/components/kritzel-current-user-dialog.js +1 -1
- package/dist/components/kritzel-current-user.js +1 -1
- package/dist/components/kritzel-editor.js +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-export.js +1 -1
- package/dist/components/kritzel-font-size.js +1 -1
- package/dist/components/kritzel-font.js +1 -1
- package/dist/components/kritzel-input.js +1 -1
- package/dist/components/kritzel-login-dialog.js +1 -1
- package/dist/components/kritzel-master-detail.js +1 -1
- package/dist/components/kritzel-menu-item.js +1 -1
- package/dist/components/kritzel-menu.js +1 -1
- package/dist/components/kritzel-more-menu.js +1 -1
- package/dist/components/kritzel-notification-card.d.ts +11 -0
- package/dist/components/kritzel-notification-card.js +1 -0
- package/dist/components/kritzel-numeric-input.js +1 -1
- package/dist/components/kritzel-opacity-slider.js +1 -1
- package/dist/components/kritzel-portal.js +1 -1
- package/dist/components/kritzel-settings.js +1 -1
- package/dist/components/kritzel-share-dialog.js +1 -1
- package/dist/components/kritzel-slide-toggle.js +1 -1
- package/dist/components/kritzel-split-button.js +1 -1
- package/dist/components/kritzel-stroke-size.js +1 -1
- package/dist/components/kritzel-tool-config.js +1 -1
- package/dist/components/kritzel-tooltip.js +1 -1
- package/dist/components/kritzel-utility-panel.js +1 -1
- package/dist/components/kritzel-watermark.js +1 -1
- package/dist/components/kritzel-workspace-manager.js +1 -1
- package/dist/components/kritzel-zoom-panel.js +1 -1
- package/dist/components/{p-BvRrA4hN.js → p-6Ca7Eolf.js} +1 -1
- package/dist/components/{p-BpnIvNvq.js → p-BO1CnEOT.js} +1 -1
- package/dist/components/{p-CyqRcqsO.js → p-BPoDE87Q.js} +1 -1
- package/dist/components/p-BX-nQAyH.js +1 -0
- package/dist/components/{p-DNdXJp8F.js → p-Bg0xIZiR.js} +1 -1
- package/dist/components/{p-DmWSRsjK.js → p-BhODnvMO.js} +1 -1
- package/dist/components/{p-mz3pUWW4.js → p-Bm0aKWPw.js} +1 -1
- package/dist/components/{p-3HxnBrCM.js → p-BrAeYciD.js} +1 -1
- package/dist/components/{p-f8aW1ye7.js → p-BwuemYET.js} +1 -1
- package/dist/components/{p-U4oawa1x.js → p-C-RiEw9X.js} +1 -1
- package/dist/components/{p-DdH1cKED.js → p-C1jg5IQi.js} +1 -1
- package/dist/components/{p-SDZNC8GF.js → p-C4-Som48.js} +1 -1
- package/dist/components/{p-C9HGoDHE.js → p-C5TZjIE1.js} +1 -1
- package/dist/components/{p-7NsK0uHu.js → p-CC0J_kEP.js} +1 -1
- package/dist/components/{p-CIcLzcfA.js → p-CJEIUs0x.js} +1 -1
- package/dist/components/{p-Bzv9Px8v.js → p-CO0eEzos.js} +1 -1
- package/dist/components/{p-DDkmsPpV.js → p-COdwK_mi.js} +1 -1
- package/dist/components/{p-CPtDfadX.js → p-COj8EVzL.js} +1 -1
- package/dist/components/{p-CEnEDaix.js → p-CVV-HzFR.js} +1 -1
- package/dist/components/{p-6RjeGuvH.js → p-Cb5YwXbC.js} +1 -1
- package/dist/components/{p-DX5K8xnh.js → p-CbMLbDou.js} +1 -1
- package/dist/components/{p-DZdgXCAx.js → p-CdkNxxut.js} +1 -1
- package/dist/components/{p-BKJSh8qQ.js → p-CpR9lc-o.js} +1 -1
- package/dist/components/p-CpzicliK.js +1 -0
- package/dist/components/{p-IpoC5EEY.js → p-D0RYktBV.js} +1 -1
- package/dist/components/{p-DI4vQRE3.js → p-D0v9WP5w.js} +1 -1
- package/dist/components/{p-DgmtCdnL.js → p-D28ZylHn.js} +1 -1
- package/dist/components/{p-F5_X4dZG.js → p-DEpnJBA3.js} +1 -1
- package/dist/components/{p-BCNyR5Sw.js → p-DRsD38Nl.js} +1 -1
- package/dist/components/p-DWhWFDcC.js +9 -0
- package/dist/components/{p-Dz-Ti24X.js → p-DeqALrzV.js} +1 -1
- package/dist/components/{p-BKvHg9cv.js → p-Ndib1Jb3.js} +1 -1
- package/dist/components/{p-vAeiXe6c.js → p-NwN6r0N0.js} +1 -1
- package/dist/components/{p-NuLP1xHe.js → p-W1ZwImdd.js} +1 -1
- package/dist/components/{p-Jn6TNdfe.js → p-YFR180v-.js} +1 -1
- package/dist/components/p-pagW8YF7.js +1 -0
- package/dist/components/{p-v7dxxrL5.js → p-vf28uSsc.js} +1 -1
- package/dist/esm/index-Dhio9uis.js +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/{kritzel-active-users_44.entry.js → kritzel-active-users_45.entry.js} +739 -311
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{schema.constants-DchTXG3V.js → schema.constants-D35cwCYd.js} +116 -60
- package/dist/esm/stencil.js +1 -1
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/p-D35cwCYd.js +1 -0
- package/dist/stencil/p-e8e69a16.entry.js +9 -0
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/core/core.class.d.ts +16 -19
- package/dist/types/classes/handlers/key.handler.d.ts +1 -1
- package/dist/types/classes/managers/browser-permission.manager.d.ts +41 -0
- package/dist/types/classes/managers/clipboard.manager.d.ts +86 -0
- package/dist/types/classes/objects/image.class.d.ts +10 -0
- package/dist/types/classes/tools/image-tool.class.d.ts +0 -14
- package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +20 -1
- package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +8 -4
- package/dist/types/components/ui/kritzel-notification-card/kritzel-notification-card.d.ts +22 -0
- package/dist/types/components.d.ts +83 -9
- package/dist/types/constants/version.d.ts +1 -1
- package/dist/types/interfaces/engine-state.interface.d.ts +0 -3
- package/dist/types/interfaces/notification.interface.d.ts +10 -0
- package/dist/types/interfaces/theme.interface.d.ts +43 -0
- package/package.json +8 -2
- package/readme.md +7 -111
- package/dist/components/p-Bc55X65h.js +0 -1
- package/dist/components/p-C_fKgKHu.js +0 -9
- package/dist/components/p-Cu9KYyoq.js +0 -1
- package/dist/stencil/p-86348986.entry.js +0 -9
- package/dist/stencil/p-DchTXG3V.js +0 -1
|
@@ -17,6 +17,8 @@ import { KritzelCursorManager } from "../managers/cursor.manager";
|
|
|
17
17
|
import { KritzelThemeManager } from "../managers/theme.manager";
|
|
18
18
|
import { KritzelLocalizationManager } from "../managers/localization.manager";
|
|
19
19
|
import { KritzelLicenseManager } from "../managers/license.manager";
|
|
20
|
+
import { KritzelBrowserPermissionManager } from "../managers/browser-permission.manager";
|
|
21
|
+
import { KritzelClipboardManager } from "../managers/clipboard.manager";
|
|
20
22
|
import { KritzelClassHelper } from "../../helpers/class.helper";
|
|
21
23
|
/**
|
|
22
24
|
* Central orchestrator class for the Kritzel canvas system.
|
|
@@ -46,6 +48,10 @@ export class KritzelCore {
|
|
|
46
48
|
_localizationManager;
|
|
47
49
|
/** Manager for license validation and watermark gating */
|
|
48
50
|
_licenseManager;
|
|
51
|
+
/** Manager for browser permission checks and permission prompts */
|
|
52
|
+
_permissionManager;
|
|
53
|
+
/** Manager for clipboard copy/cut/paste orchestration */
|
|
54
|
+
_clipboardManager;
|
|
49
55
|
/** Per-core registry of drawing tools (one instance per editor). */
|
|
50
56
|
_toolRegistry;
|
|
51
57
|
/** Optional unique identifier for namespacing storage keys across multiple editor instances */
|
|
@@ -108,6 +114,13 @@ export class KritzelCore {
|
|
|
108
114
|
get licenseManager() {
|
|
109
115
|
return this._licenseManager;
|
|
110
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Gets the browser permission manager.
|
|
119
|
+
* @returns The KritzelBrowserPermissionManager for browser permission checks and requests
|
|
120
|
+
*/
|
|
121
|
+
get permissionManager() {
|
|
122
|
+
return this._permissionManager;
|
|
123
|
+
}
|
|
111
124
|
/**
|
|
112
125
|
* Gets the tool registry scoped to this core instance.
|
|
113
126
|
* @returns The KritzelToolRegistry owned by this core
|
|
@@ -115,6 +128,13 @@ export class KritzelCore {
|
|
|
115
128
|
get toolRegistry() {
|
|
116
129
|
return this._toolRegistry;
|
|
117
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Gets the clipboard manager scoped to this core instance.
|
|
133
|
+
* @returns The KritzelClipboardManager owned by this core
|
|
134
|
+
*/
|
|
135
|
+
get clipboardManager() {
|
|
136
|
+
return this._clipboardManager;
|
|
137
|
+
}
|
|
118
138
|
/**
|
|
119
139
|
* Gets the editor ID used for namespacing storage keys.
|
|
120
140
|
* @returns The editor ID or undefined if not set
|
|
@@ -162,6 +182,8 @@ export class KritzelCore {
|
|
|
162
182
|
this._themeManager = new KritzelThemeManager(this);
|
|
163
183
|
this._localizationManager = new KritzelLocalizationManager(this);
|
|
164
184
|
this._licenseManager = new KritzelLicenseManager(this);
|
|
185
|
+
this._permissionManager = new KritzelBrowserPermissionManager(this);
|
|
186
|
+
this._clipboardManager = new KritzelClipboardManager(this);
|
|
165
187
|
this._toolRegistry = new KritzelToolRegistry(this);
|
|
166
188
|
this._assetResolver = new KritzelAssetResolver();
|
|
167
189
|
}
|
|
@@ -659,216 +681,6 @@ export class KritzelCore {
|
|
|
659
681
|
this.rerender();
|
|
660
682
|
}
|
|
661
683
|
}
|
|
662
|
-
/**
|
|
663
|
-
* Copies the currently selected objects to the clipboard.
|
|
664
|
-
* Creates deep copies of objects and tracks ID mappings for anchor remapping.
|
|
665
|
-
*/
|
|
666
|
-
copy() {
|
|
667
|
-
const selectionGroup = this._store.selectionGroup;
|
|
668
|
-
if (selectionGroup) {
|
|
669
|
-
// Copy each object and store them in an array, also track the ID mapping
|
|
670
|
-
const idMapping = new Map();
|
|
671
|
-
const copiedObjects = selectionGroup.objects
|
|
672
|
-
.sort((a, b) => a.zIndex - b.zIndex)
|
|
673
|
-
.map(obj => {
|
|
674
|
-
const copiedObj = obj.copy();
|
|
675
|
-
// Map: newId -> originalId
|
|
676
|
-
idMapping.set(copiedObj.id, obj.id);
|
|
677
|
-
return copiedObj;
|
|
678
|
-
});
|
|
679
|
-
this._store.state.copiedObjects = copiedObjects;
|
|
680
|
-
this._store.state.copiedObjectIdMapping = idMapping;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Cuts the currently selected objects (copy then delete).
|
|
685
|
-
* Objects are copied to clipboard and then removed from the canvas.
|
|
686
|
-
*/
|
|
687
|
-
cut() {
|
|
688
|
-
const selectionGroup = this._store.selectionGroup;
|
|
689
|
-
if (!selectionGroup) {
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
// Copy first, then delete
|
|
693
|
-
this.copy();
|
|
694
|
-
this.delete();
|
|
695
|
-
}
|
|
696
|
-
/**
|
|
697
|
-
* Pastes objects from the clipboard to the canvas.
|
|
698
|
-
* Handles positioning (explicit, offset, or centered for cross-workspace paste),
|
|
699
|
-
* workspace assignment, z-index ordering, group children, and anchor remapping.
|
|
700
|
-
* Creates a selection group containing the pasted objects.
|
|
701
|
-
* @param x - Optional explicit X position for paste
|
|
702
|
-
* @param y - Optional explicit Y position for paste
|
|
703
|
-
*/
|
|
704
|
-
paste(x, y) {
|
|
705
|
-
const copiedObjects = this._store.state.copiedObjects;
|
|
706
|
-
if (!copiedObjects || copiedObjects.length === 0) {
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
const activeWorkspace = this._store.state.activeWorkspace;
|
|
710
|
-
const originalIdMapping = this._store.state.copiedObjectIdMapping;
|
|
711
|
-
// Check if we're pasting from a different workspace
|
|
712
|
-
const isDifferentWorkspace = copiedObjects.some(obj => obj.workspaceId !== activeWorkspace.id);
|
|
713
|
-
// Calculate the bounding box of all copied objects
|
|
714
|
-
const minX = Math.min(...copiedObjects.map(obj => obj.translateX));
|
|
715
|
-
const minY = Math.min(...copiedObjects.map(obj => obj.translateY));
|
|
716
|
-
// Determine the paste position
|
|
717
|
-
let pasteX;
|
|
718
|
-
let pasteY;
|
|
719
|
-
if (x !== undefined && y !== undefined) {
|
|
720
|
-
// Explicit position provided (e.g., right-click paste)
|
|
721
|
-
pasteX = x;
|
|
722
|
-
pasteY = y;
|
|
723
|
-
}
|
|
724
|
-
else if (isDifferentWorkspace) {
|
|
725
|
-
// Pasting to different workspace without explicit position
|
|
726
|
-
// Keep original position; will be centered by centerInViewport() later
|
|
727
|
-
pasteX = minX;
|
|
728
|
-
pasteY = minY;
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
// Same workspace paste: offset by 25 pixels from original position
|
|
732
|
-
pasteX = minX + 25 / this._store.state.scale;
|
|
733
|
-
pasteY = minY + 25 / this._store.state.scale;
|
|
734
|
-
}
|
|
735
|
-
// Calculate the offset to apply to all objects
|
|
736
|
-
const offsetX = pasteX - minX;
|
|
737
|
-
const offsetY = pasteY - minY;
|
|
738
|
-
this.removeSelectionGroup();
|
|
739
|
-
this.removeSelectionBox();
|
|
740
|
-
// Create a new selection group for the pasted objects
|
|
741
|
-
const selectionGroup = KritzelSelectionGroup.create(this);
|
|
742
|
-
// Build a reverse mapping: originalId -> newCopiedObjectId
|
|
743
|
-
// This is used to remap anchor references from original IDs to the copied object IDs
|
|
744
|
-
const originalToNewIdMap = new Map();
|
|
745
|
-
if (originalIdMapping) {
|
|
746
|
-
originalIdMapping.forEach((originalId, copiedId) => {
|
|
747
|
-
originalToNewIdMap.set(originalId, copiedId);
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
// Precompute zIndex once to avoid O(N * total_objects) repeated scans
|
|
751
|
-
const baseZIndex = this._store.currentZIndex;
|
|
752
|
-
// Batch all inserts and updates in a single Y.js transaction to avoid
|
|
753
|
-
// N separate observer callbacks and N rerenders (fires only once at commit)
|
|
754
|
-
this._store.objects.transaction(() => {
|
|
755
|
-
// Recursively flush a group's pending children (and any nested groups'
|
|
756
|
-
// pending children) into the store. Without recursion, nested groups
|
|
757
|
-
// would have their grandchildren stranded in `_pendingChildren` and they
|
|
758
|
-
// would never appear on the canvas, even though the parent group's
|
|
759
|
-
// bounding box looks correct.
|
|
760
|
-
const flushPendingChildren = (group, parentZIndex) => {
|
|
761
|
-
if (group._pendingChildren.length === 0) {
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
const pendingChildren = group._pendingChildren;
|
|
765
|
-
group._pendingChildren = [];
|
|
766
|
-
pendingChildren.forEach((child, childIndex) => {
|
|
767
|
-
if (child.workspaceId !== activeWorkspace.id) {
|
|
768
|
-
child.workspaceId = activeWorkspace.id;
|
|
769
|
-
}
|
|
770
|
-
child.updatePosition(child.translateX + offsetX, child.translateY + offsetY);
|
|
771
|
-
child.zIndex = parentZIndex + childIndex + 1;
|
|
772
|
-
// Recurse before adding so grandchildren are inserted as well
|
|
773
|
-
if (KritzelClassHelper.isInstanceOf(child, 'KritzelGroup')) {
|
|
774
|
-
flushPendingChildren(child, child.zIndex);
|
|
775
|
-
}
|
|
776
|
-
this.addObject(child);
|
|
777
|
-
});
|
|
778
|
-
group.finalize();
|
|
779
|
-
};
|
|
780
|
-
const newlyAddedObjects = [];
|
|
781
|
-
// First add all copied objects to the objectsMap with updated positions
|
|
782
|
-
copiedObjects.forEach((obj, i) => {
|
|
783
|
-
// Update workspace if pasting to a different workspace
|
|
784
|
-
if (obj.workspaceId !== activeWorkspace.id) {
|
|
785
|
-
obj.workspaceId = activeWorkspace.id;
|
|
786
|
-
}
|
|
787
|
-
// Update position
|
|
788
|
-
obj.updatePosition(obj.translateX + offsetX, obj.translateY + offsetY);
|
|
789
|
-
// Update z-index
|
|
790
|
-
obj.zIndex = baseZIndex + i;
|
|
791
|
-
// Handle KritzelGroup: also add pending children (and grandchildren) with offset
|
|
792
|
-
if (KritzelClassHelper.isInstanceOf(obj, 'KritzelGroup')) {
|
|
793
|
-
flushPendingChildren(obj, obj.zIndex);
|
|
794
|
-
}
|
|
795
|
-
// Add to objectsMap
|
|
796
|
-
this.addObject(obj);
|
|
797
|
-
newlyAddedObjects.push(obj);
|
|
798
|
-
// Add to selection group
|
|
799
|
-
selectionGroup.addOrRemove(obj);
|
|
800
|
-
});
|
|
801
|
-
this.engine.emitObjectsAdded(newlyAddedObjects);
|
|
802
|
-
// Update line anchors to point to the newly pasted objects
|
|
803
|
-
// Only remap if the anchor target was also part of the copied selection
|
|
804
|
-
copiedObjects.forEach(obj => {
|
|
805
|
-
if (KritzelClassHelper.isInstanceOf(obj, 'KritzelLine')) {
|
|
806
|
-
let updated = false;
|
|
807
|
-
if (obj.startAnchor && originalToNewIdMap.has(obj.startAnchor.objectId)) {
|
|
808
|
-
obj.startAnchor = { objectId: originalToNewIdMap.get(obj.startAnchor.objectId) };
|
|
809
|
-
updated = true;
|
|
810
|
-
}
|
|
811
|
-
if (obj.endAnchor && originalToNewIdMap.has(obj.endAnchor.objectId)) {
|
|
812
|
-
obj.endAnchor = { objectId: originalToNewIdMap.get(obj.endAnchor.objectId) };
|
|
813
|
-
updated = true;
|
|
814
|
-
}
|
|
815
|
-
// If anchors were updated, rebuild the anchor index and persist the change
|
|
816
|
-
if (updated) {
|
|
817
|
-
this._store.objects.update(obj);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
});
|
|
821
|
-
});
|
|
822
|
-
// Rebuild anchor index after all anchor updates
|
|
823
|
-
this._anchorManager.rebuildIndex();
|
|
824
|
-
// Mark selection group as selected
|
|
825
|
-
selectionGroup.isSelected = true;
|
|
826
|
-
// Set rotation for single object
|
|
827
|
-
if (copiedObjects.length === 1) {
|
|
828
|
-
selectionGroup.rotation = copiedObjects[0].rotation;
|
|
829
|
-
}
|
|
830
|
-
// For cross-workspace paste, set the selection group's position before inserting
|
|
831
|
-
// it into the store. This mirrors the existing pattern where objects have
|
|
832
|
-
// updatePosition called before addObject, so the correct position is captured
|
|
833
|
-
// in the single initial insert with no separate Yjs update (and no extra undo step).
|
|
834
|
-
if (isDifferentWorkspace) {
|
|
835
|
-
if (x !== undefined && y !== undefined) {
|
|
836
|
-
// Explicit cursor position provided (e.g. right-click paste)
|
|
837
|
-
selectionGroup.updatePosition(x, y);
|
|
838
|
-
}
|
|
839
|
-
else {
|
|
840
|
-
// Ctrl+V paste: center objects in the current viewport
|
|
841
|
-
selectionGroup.centerInViewport();
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
// Add the selection group (inserts with the already-adjusted position)
|
|
845
|
-
this.addSelectionGroup(selectionGroup);
|
|
846
|
-
// Bring pasted objects into view with a smooth animation if they are not
|
|
847
|
-
// fully visible. These only mutate in-memory viewport state (scale, translateX/Y)
|
|
848
|
-
// and do not produce any Yjs writes, so they have no impact on the undo history.
|
|
849
|
-
this.engine.viewport.bringIntoViewIfNeeded(selectionGroup);
|
|
850
|
-
this._store.state.isSelecting = false;
|
|
851
|
-
// Defer creating copies for future pastes to next frame to avoid
|
|
852
|
-
// doubling the object creation cost in the current paste frame
|
|
853
|
-
requestAnimationFrame(() => {
|
|
854
|
-
const newSelectionGroup = this._store.selectionGroup;
|
|
855
|
-
if (newSelectionGroup) {
|
|
856
|
-
const newIdMapping = new Map();
|
|
857
|
-
const newCopiedObjects = newSelectionGroup.objects
|
|
858
|
-
.sort((a, b) => a.zIndex - b.zIndex)
|
|
859
|
-
.map(obj => {
|
|
860
|
-
const copiedObj = obj.copy();
|
|
861
|
-
newIdMapping.set(copiedObj.id, obj.id);
|
|
862
|
-
return copiedObj;
|
|
863
|
-
});
|
|
864
|
-
this._store.state.copiedObjects = newCopiedObjects;
|
|
865
|
-
this._store.state.copiedObjectIdMapping = newIdMapping;
|
|
866
|
-
}
|
|
867
|
-
});
|
|
868
|
-
this._store.setState('activeTool', this._toolRegistry.getTool('selection'));
|
|
869
|
-
this.engine.emitObjectsChange();
|
|
870
|
-
this.rerender();
|
|
871
|
-
}
|
|
872
684
|
/**
|
|
873
685
|
* Moves objects one step forward in the z-order stack.
|
|
874
686
|
* @param object - Optional specific object to move; defaults to all selected objects
|
|
@@ -3,7 +3,7 @@ import { KritzelBaseHandler } from "./base.handler";
|
|
|
3
3
|
const NUDGE_AMOUNT = 10;
|
|
4
4
|
/**
|
|
5
5
|
* Handles keyboard events and manages keyboard shortcuts for the Kritzel canvas.
|
|
6
|
-
* Provides functionality for tool switching,
|
|
6
|
+
* Provides functionality for tool switching, object manipulation,
|
|
7
7
|
* undo/redo, and navigation via keyboard input.
|
|
8
8
|
*/
|
|
9
9
|
export class KritzelKeyHandler extends KritzelBaseHandler {
|
|
@@ -46,27 +46,6 @@ export class KritzelKeyHandler extends KritzelBaseHandler {
|
|
|
46
46
|
{ key: '7', ctrl: true, label: 'Tool 7', category: 'Tool Selection', action: () => this.switchToolByIndex(6) },
|
|
47
47
|
{ key: '8', ctrl: true, label: 'Tool 8', category: 'Tool Selection', action: () => this.switchToolByIndex(7) },
|
|
48
48
|
{ key: '9', ctrl: true, label: 'Tool 9', category: 'Tool Selection', action: () => this.switchToolByIndex(8) },
|
|
49
|
-
// Clipboard
|
|
50
|
-
{
|
|
51
|
-
key: 'c',
|
|
52
|
-
ctrl: true,
|
|
53
|
-
label: 'Copy',
|
|
54
|
-
category: 'Clipboard',
|
|
55
|
-
condition: c => !!c.store.selectionGroup,
|
|
56
|
-
action: c => {
|
|
57
|
-
c.copy();
|
|
58
|
-
c.rerender();
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
{ key: 'x', ctrl: true, label: 'Cut', category: 'Clipboard', condition: c => !!c.store.selectionGroup, action: c => c.cut() },
|
|
62
|
-
{
|
|
63
|
-
key: 'v',
|
|
64
|
-
ctrl: true,
|
|
65
|
-
label: 'Paste',
|
|
66
|
-
category: 'Clipboard',
|
|
67
|
-
condition: c => !!c.store.state.copiedObjects && !c.store.activeText && !c.store.activeShape,
|
|
68
|
-
action: c => c.paste(),
|
|
69
|
-
},
|
|
70
49
|
// Object layering
|
|
71
50
|
{ key: '+', ctrl: true, label: 'Bring Forward', category: 'Object Layering', condition: c => !!c.store.selectionGroup, action: c => c.bringForward() },
|
|
72
51
|
{ key: '-', ctrl: true, label: 'Send Backward', category: 'Object Layering', condition: c => !!c.store.selectionGroup, action: c => c.sendBackward() },
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages browser permission checks and prompts for a core instance.
|
|
3
|
+
*
|
|
4
|
+
* Permission-specific prompt logic is routed through a handler registry so new
|
|
5
|
+
* permissions can be added without changing the core control flow.
|
|
6
|
+
*/
|
|
7
|
+
export class KritzelBrowserPermissionManager {
|
|
8
|
+
_isFirefox = /firefox|fxios/i.test(navigator.userAgent);
|
|
9
|
+
_isSafari = /safari/i.test(navigator.userAgent) && !/chrome|chromium|crios|android|edg/i.test(navigator.userAgent);
|
|
10
|
+
_requestHandlers = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new KritzelBrowserPermissionManager instance.
|
|
13
|
+
* @param core - The KritzelCore instance this manager belongs to
|
|
14
|
+
*/
|
|
15
|
+
constructor(_core) {
|
|
16
|
+
this.registerRequestPermissionHandler('clipboard-read', async () => {
|
|
17
|
+
// Firefox and Safari open native paste menus when readText() is called
|
|
18
|
+
// proactively, so rely on the permission state instead of an eager read.
|
|
19
|
+
if (this._isFirefox || this._isSafari) {
|
|
20
|
+
return 'granted';
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
await navigator.clipboard.readText();
|
|
24
|
+
return 'granted';
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return 'denied';
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Registers a custom request handler for a browser permission name.
|
|
33
|
+
* Existing handlers are replaced, which makes the registry easy to extend.
|
|
34
|
+
*
|
|
35
|
+
* @param permissionName - The browser permission name to handle
|
|
36
|
+
* @param handler - The request implementation for that permission
|
|
37
|
+
*/
|
|
38
|
+
registerRequestPermissionHandler(permissionName, handler) {
|
|
39
|
+
this._requestHandlers.set(permissionName, handler);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Checks the current state for a browser permission via the Permissions API.
|
|
43
|
+
*
|
|
44
|
+
* @param permissionName - The browser permission name to inspect
|
|
45
|
+
*/
|
|
46
|
+
async checkPermission(permissionName) {
|
|
47
|
+
const status = await navigator.permissions.query({ name: permissionName });
|
|
48
|
+
return status.state;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Requests a browser permission using the registered handler for the given
|
|
52
|
+
* permission name, then falls back to a permission check when no handler is
|
|
53
|
+
* registered.
|
|
54
|
+
*
|
|
55
|
+
* @param permissionName - The browser permission name to request
|
|
56
|
+
*/
|
|
57
|
+
async requestPermission(permissionName) {
|
|
58
|
+
const handler = this._requestHandlers.get(permissionName);
|
|
59
|
+
if (handler) {
|
|
60
|
+
return handler();
|
|
61
|
+
}
|
|
62
|
+
return this.checkPermission(permissionName);
|
|
63
|
+
}
|
|
64
|
+
}
|