kritzel-stencil 0.3.31 → 0.3.33
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.cjs.js +1 -1
- package/dist/cjs/kritzel-active-users_45.cjs.entry.js +214 -108
- package/dist/cjs/{schema.constants-DItwOm2z.js → schema.constants-BHr5Bcqs.js} +3 -2
- package/dist/collection/classes/managers/anchor.manager.js +3 -2
- package/dist/collection/classes/managers/browser-permission.manager.js +4 -4
- package/dist/collection/classes/managers/clipboard.manager.js +36 -104
- package/dist/collection/classes/managers/clipboard.types.js +1 -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/registries/paste-intent-provider.registry.js +26 -0
- package/dist/collection/constants/version.js +1 -1
- package/dist/collection/interfaces/paste-intent-provider.interface.js +1 -0
- package/dist/components/index.js +1 -1
- package/dist/components/kritzel-editor.js +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-settings.js +1 -1
- package/dist/components/{p-BohZHzi_.js → p-BEucPbiz.js} +1 -1
- package/dist/components/p-CQZYGZzF.js +9 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/kritzel-active-users_45.entry.js +214 -108
- package/dist/esm/{schema.constants-CH2FWTVr.js → schema.constants-3oYjlURq.js} +3 -2
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/{p-CH2FWTVr.js → p-3oYjlURq.js} +1 -1
- package/dist/stencil/{p-16a19285.entry.js → p-767b5a14.entry.js} +2 -2
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/managers/clipboard.manager.d.ts +21 -37
- package/dist/types/classes/managers/clipboard.types.d.ts +23 -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/registries/paste-intent-provider.registry.d.ts +10 -0
- package/dist/types/constants/version.d.ts +1 -1
- package/dist/types/interfaces/paste-intent-provider.interface.d.ts +10 -0
- package/package.json +1 -1
- package/dist/components/p-dxwV_Hq6.js +0 -9
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as HocuspocusProvider, a as HocuspocusProviderWebsocket } from './schema.constants-
|
|
2
|
-
export { N as APP_STATE_MIGRATIONS, A as AssetNotFoundError, P as CURRENT_APP_STATE_SCHEMA_VERSION, Q as CURRENT_WORKSPACE_SCHEMA_VERSION, B as DEFAULT_ASSET_STORAGE_CONFIG, D as DEFAULT_BRUSH_CONFIG, z as DEFAULT_LINE_TOOL_CONFIG, y as DEFAULT_TEXT_CONFIG, G as DE_LOCALE, F as EN_LOCALE, J as FR_LOCALE, I as IndexedDBAssetProvider, L as KritzelAlignment, t as KritzelAnchorManager, r as KritzelAssetResolver, K as KritzelBaseObject, i as KritzelBaseTool, j as KritzelBrushTool, p as KritzelCursorHelper, h as KritzelCustomElement, x as KritzelCustomElementRendererRegistry, l as KritzelEraserTool, f as KritzelGroup, d as KritzelImage, m as KritzelImageTool, w as KritzelLicenseManager, e as KritzelLine, k as KritzelLineTool, v as KritzelLocalizationManager, c as KritzelPath, q as KritzelSelectionTool, g as KritzelShape, o as KritzelShapeTool, b as KritzelText, n as KritzelTextTool, u as KritzelThemeManager, s as KritzelWorkspace, S as ShapeType, W as WORKSPACE_EXPORT_VERSION, O as WORKSPACE_MIGRATIONS, E as darkTheme, C as lightTheme, M as runMigrations } from './schema.constants-
|
|
1
|
+
import { H as HocuspocusProvider, a as HocuspocusProviderWebsocket } from './schema.constants-3oYjlURq.js';
|
|
2
|
+
export { N as APP_STATE_MIGRATIONS, A as AssetNotFoundError, P as CURRENT_APP_STATE_SCHEMA_VERSION, Q as CURRENT_WORKSPACE_SCHEMA_VERSION, B as DEFAULT_ASSET_STORAGE_CONFIG, D as DEFAULT_BRUSH_CONFIG, z as DEFAULT_LINE_TOOL_CONFIG, y as DEFAULT_TEXT_CONFIG, G as DE_LOCALE, F as EN_LOCALE, J as FR_LOCALE, I as IndexedDBAssetProvider, L as KritzelAlignment, t as KritzelAnchorManager, r as KritzelAssetResolver, K as KritzelBaseObject, i as KritzelBaseTool, j as KritzelBrushTool, p as KritzelCursorHelper, h as KritzelCustomElement, x as KritzelCustomElementRendererRegistry, l as KritzelEraserTool, f as KritzelGroup, d as KritzelImage, m as KritzelImageTool, w as KritzelLicenseManager, e as KritzelLine, k as KritzelLineTool, v as KritzelLocalizationManager, c as KritzelPath, q as KritzelSelectionTool, g as KritzelShape, o as KritzelShapeTool, b as KritzelText, n as KritzelTextTool, u as KritzelThemeManager, s as KritzelWorkspace, S as ShapeType, W as WORKSPACE_EXPORT_VERSION, O as WORKSPACE_MIGRATIONS, E as darkTheme, C as lightTheme, M as runMigrations } from './schema.constants-3oYjlURq.js';
|
|
3
3
|
import * as Y from 'yjs';
|
|
4
4
|
import { IndexeddbPersistence } from 'y-indexeddb';
|
|
5
5
|
import { WebsocketProvider } from 'y-websocket';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as registerInstance, h, H as Host, c as createEvent, g as getElement } from './index-Dhio9uis.js';
|
|
2
|
-
import { c as KritzelPath, e as KritzelLine, R as KritzelColorHelper, T as KritzelDevicesHelper, q as KritzelSelectionTool, n as KritzelTextTool, U as KritzelMouseButton, V as KritzelHTMLHelper, X as DEFAULT_STROKE_SIZES, Y as DEFAULT_COLOR_PALETTE, S as ShapeType, D as DEFAULT_BRUSH_CONFIG, j as KritzelBrushTool, l as KritzelEraserTool, z as DEFAULT_LINE_TOOL_CONFIG, k as KritzelLineTool, o as KritzelShapeTool, y as DEFAULT_TEXT_CONFIG, m as KritzelImageTool, L as KritzelAlignment, B as DEFAULT_ASSET_STORAGE_CONFIG, Z as ObjectHelper, _ as ThemeHelper, E as darkTheme, C as lightTheme, $ as KritzelSelectionGroup, a0 as KritzelSelectionBox, a1 as KritzelIconRegistry, a2 as KritzelKeyboardHelper, a3 as KritzelBaseHandler, s as KritzelWorkspace, f as KritzelGroup, h as KritzelCustomElement, d as KritzelImage, g as KritzelShape, b as KritzelText, M as runMigrations, Q as CURRENT_WORKSPACE_SCHEMA_VERSION, O as WORKSPACE_MIGRATIONS, a4 as KritzelClassHelper, P as CURRENT_APP_STATE_SCHEMA_VERSION, N as APP_STATE_MIGRATIONS, p as KritzelCursorHelper, a5 as imageCompression, t as KritzelAnchorManager, u as KritzelThemeManager, v as KritzelLocalizationManager, w as KritzelLicenseManager, r as KritzelAssetResolver, a6 as KritzelEventHelper, W as WORKSPACE_EXPORT_VERSION } from './schema.constants-
|
|
2
|
+
import { c as KritzelPath, e as KritzelLine, R as KritzelColorHelper, T as KritzelDevicesHelper, q as KritzelSelectionTool, n as KritzelTextTool, U as KritzelMouseButton, V as KritzelHTMLHelper, X as DEFAULT_STROKE_SIZES, Y as DEFAULT_COLOR_PALETTE, S as ShapeType, D as DEFAULT_BRUSH_CONFIG, j as KritzelBrushTool, l as KritzelEraserTool, z as DEFAULT_LINE_TOOL_CONFIG, k as KritzelLineTool, o as KritzelShapeTool, y as DEFAULT_TEXT_CONFIG, m as KritzelImageTool, L as KritzelAlignment, B as DEFAULT_ASSET_STORAGE_CONFIG, Z as ObjectHelper, _ as ThemeHelper, E as darkTheme, C as lightTheme, $ as KritzelSelectionGroup, a0 as KritzelSelectionBox, a1 as KritzelIconRegistry, a2 as KritzelKeyboardHelper, a3 as KritzelBaseHandler, s as KritzelWorkspace, f as KritzelGroup, h as KritzelCustomElement, d as KritzelImage, g as KritzelShape, b as KritzelText, M as runMigrations, Q as CURRENT_WORKSPACE_SCHEMA_VERSION, O as WORKSPACE_MIGRATIONS, a4 as KritzelClassHelper, P as CURRENT_APP_STATE_SCHEMA_VERSION, N as APP_STATE_MIGRATIONS, p as KritzelCursorHelper, a5 as imageCompression, t as KritzelAnchorManager, u as KritzelThemeManager, v as KritzelLocalizationManager, w as KritzelLicenseManager, r as KritzelAssetResolver, a6 as KritzelEventHelper, W as WORKSPACE_EXPORT_VERSION } from './schema.constants-3oYjlURq.js';
|
|
3
3
|
import * as Y from 'yjs';
|
|
4
4
|
import 'y-indexeddb';
|
|
5
5
|
import 'y-websocket';
|
|
@@ -24033,17 +24033,17 @@ class KritzelBrowserPermissionManager {
|
|
|
24033
24033
|
*/
|
|
24034
24034
|
constructor(_core) {
|
|
24035
24035
|
this.registerRequestPermissionHandler('clipboard-read', async () => {
|
|
24036
|
-
// Firefox and Safari open native paste menus when
|
|
24036
|
+
// Firefox and Safari open native paste menus when the clipboard is read
|
|
24037
24037
|
// proactively, so rely on the permission state instead of an eager read.
|
|
24038
24038
|
if (this._isFirefox || this._isSafari) {
|
|
24039
24039
|
return 'granted';
|
|
24040
24040
|
}
|
|
24041
24041
|
try {
|
|
24042
|
-
await navigator.
|
|
24043
|
-
return 'granted';
|
|
24042
|
+
const status = await navigator.permissions.query({ name: 'clipboard-read' });
|
|
24043
|
+
return status.state === 'denied' ? 'denied' : 'granted';
|
|
24044
24044
|
}
|
|
24045
24045
|
catch {
|
|
24046
|
-
return '
|
|
24046
|
+
return 'granted';
|
|
24047
24047
|
}
|
|
24048
24048
|
});
|
|
24049
24049
|
}
|
|
@@ -24082,37 +24082,55 @@ class KritzelBrowserPermissionManager {
|
|
|
24082
24082
|
}
|
|
24083
24083
|
}
|
|
24084
24084
|
|
|
24085
|
-
|
|
24086
|
-
/**
|
|
24087
|
-
* Manages clipboard operations (copy/cut/paste) for a core instance.
|
|
24088
|
-
*
|
|
24089
|
-
* The system clipboard is the single source of truth: copy serializes the current
|
|
24090
|
-
* selection into a Kritzel payload, paste reads + revives it. No copied objects are
|
|
24091
|
-
* cached on the engine state.
|
|
24092
|
-
*/
|
|
24093
|
-
class KritzelClipboardManager {
|
|
24085
|
+
class KritzelPasteIntentProviderRegistry {
|
|
24094
24086
|
_core;
|
|
24095
|
-
|
|
24096
|
-
|
|
24097
|
-
|
|
24098
|
-
*/
|
|
24099
|
-
_pasteOffset = 0;
|
|
24100
|
-
constructor(core) {
|
|
24101
|
-
this._core = core;
|
|
24087
|
+
_providers = [];
|
|
24088
|
+
constructor(_core) {
|
|
24089
|
+
this._core = _core;
|
|
24102
24090
|
}
|
|
24103
|
-
|
|
24104
|
-
|
|
24105
|
-
|
|
24106
|
-
|
|
24107
|
-
|
|
24108
|
-
|
|
24109
|
-
|
|
24110
|
-
|
|
24091
|
+
register(provider) {
|
|
24092
|
+
this._providers.push(provider);
|
|
24093
|
+
}
|
|
24094
|
+
async resolve(event) {
|
|
24095
|
+
const context = {
|
|
24096
|
+
core: this._core,
|
|
24097
|
+
event,
|
|
24098
|
+
};
|
|
24099
|
+
for (const provider of this._providers) {
|
|
24100
|
+
if (!provider.canHandle(context)) {
|
|
24101
|
+
continue;
|
|
24102
|
+
}
|
|
24103
|
+
const intent = await provider.getPasteIntent(context);
|
|
24104
|
+
if (intent.type !== 'unknown') {
|
|
24105
|
+
return intent;
|
|
24106
|
+
}
|
|
24111
24107
|
}
|
|
24112
|
-
return
|
|
24108
|
+
return { type: 'unknown' };
|
|
24113
24109
|
}
|
|
24114
|
-
|
|
24115
|
-
|
|
24110
|
+
}
|
|
24111
|
+
|
|
24112
|
+
const CLIPBOARD_PAYLOAD_PREFIX = 'kritzel-clipboard/v1:';
|
|
24113
|
+
|
|
24114
|
+
class KritzelEventPasteIntentProvider {
|
|
24115
|
+
canHandle(context) {
|
|
24116
|
+
return !!context.event?.clipboardData;
|
|
24117
|
+
}
|
|
24118
|
+
async getPasteIntent(context) {
|
|
24119
|
+
const data = context.event?.clipboardData;
|
|
24120
|
+
if (!data) {
|
|
24121
|
+
return { type: 'unknown' };
|
|
24122
|
+
}
|
|
24123
|
+
const imageFiles = Array.from(data.files ?? []).filter(file => file.type.startsWith('image/'));
|
|
24124
|
+
if (imageFiles.length > 0) {
|
|
24125
|
+
return { type: 'image_files', files: imageFiles };
|
|
24126
|
+
}
|
|
24127
|
+
if (data.types.includes('text/plain')) {
|
|
24128
|
+
const textIntent = this.determineTextPasteIntent(data.getData('text/plain'));
|
|
24129
|
+
if (textIntent) {
|
|
24130
|
+
return textIntent;
|
|
24131
|
+
}
|
|
24132
|
+
}
|
|
24133
|
+
return { type: 'unknown' };
|
|
24116
24134
|
}
|
|
24117
24135
|
parseClipboardPayload(text) {
|
|
24118
24136
|
if (!text || !text.startsWith(CLIPBOARD_PAYLOAD_PREFIX)) {
|
|
@@ -24139,6 +24157,165 @@ class KritzelClipboardManager {
|
|
|
24139
24157
|
}
|
|
24140
24158
|
return null;
|
|
24141
24159
|
}
|
|
24160
|
+
}
|
|
24161
|
+
|
|
24162
|
+
class KritzelNavigatorPasteIntentProvider {
|
|
24163
|
+
canHandle(context) {
|
|
24164
|
+
return !context.event?.clipboardData && typeof navigator !== 'undefined' && !!navigator.clipboard;
|
|
24165
|
+
}
|
|
24166
|
+
async getPasteIntent(context) {
|
|
24167
|
+
if (typeof navigator === 'undefined' || !navigator.clipboard) {
|
|
24168
|
+
return { type: 'unknown' };
|
|
24169
|
+
}
|
|
24170
|
+
const { clipboard } = navigator;
|
|
24171
|
+
const permissionState = await context.core.permissionManager.checkPermission('clipboard-read');
|
|
24172
|
+
if (permissionState !== 'granted') {
|
|
24173
|
+
const requestedState = await context.core.permissionManager.requestPermission('clipboard-read');
|
|
24174
|
+
if (requestedState !== 'granted') {
|
|
24175
|
+
return { type: 'unknown' };
|
|
24176
|
+
}
|
|
24177
|
+
}
|
|
24178
|
+
if (clipboard.read) {
|
|
24179
|
+
try {
|
|
24180
|
+
const items = await clipboard.read();
|
|
24181
|
+
const { imageFiles, text } = await this.processClipboardItems(items);
|
|
24182
|
+
if (imageFiles.length > 0) {
|
|
24183
|
+
return { type: 'image_files', files: imageFiles };
|
|
24184
|
+
}
|
|
24185
|
+
if (text !== null) {
|
|
24186
|
+
const textIntent = this.determineTextPasteIntent(text);
|
|
24187
|
+
if (textIntent) {
|
|
24188
|
+
return textIntent;
|
|
24189
|
+
}
|
|
24190
|
+
}
|
|
24191
|
+
}
|
|
24192
|
+
catch {
|
|
24193
|
+
// Fall back to readText below when read() is denied or unavailable at runtime.
|
|
24194
|
+
}
|
|
24195
|
+
}
|
|
24196
|
+
if (clipboard.readText) {
|
|
24197
|
+
const textIntent = this.determineTextPasteIntent(await clipboard.readText());
|
|
24198
|
+
if (textIntent) {
|
|
24199
|
+
return textIntent;
|
|
24200
|
+
}
|
|
24201
|
+
}
|
|
24202
|
+
return { type: 'unknown' };
|
|
24203
|
+
}
|
|
24204
|
+
async processClipboardItems(items) {
|
|
24205
|
+
const imageFiles = [];
|
|
24206
|
+
let text = null;
|
|
24207
|
+
await Promise.all(items.map(async (item) => {
|
|
24208
|
+
const imageType = item.types.find(type => type.startsWith('image/'));
|
|
24209
|
+
if (imageType) {
|
|
24210
|
+
const blob = await item.getType(imageType);
|
|
24211
|
+
imageFiles.push(new File([blob], 'pasted-image', { type: imageType }));
|
|
24212
|
+
return;
|
|
24213
|
+
}
|
|
24214
|
+
if (text === null && item.types.includes('text/plain')) {
|
|
24215
|
+
const blob = await item.getType('text/plain');
|
|
24216
|
+
text = await blob.text();
|
|
24217
|
+
}
|
|
24218
|
+
}));
|
|
24219
|
+
return { imageFiles, text };
|
|
24220
|
+
}
|
|
24221
|
+
parseClipboardPayload(text) {
|
|
24222
|
+
if (!text || !text.startsWith(CLIPBOARD_PAYLOAD_PREFIX)) {
|
|
24223
|
+
return null;
|
|
24224
|
+
}
|
|
24225
|
+
try {
|
|
24226
|
+
const parsed = JSON.parse(text.slice(CLIPBOARD_PAYLOAD_PREFIX.length));
|
|
24227
|
+
if (parsed?.version !== 1 || !Array.isArray(parsed.objects)) {
|
|
24228
|
+
return null;
|
|
24229
|
+
}
|
|
24230
|
+
return parsed;
|
|
24231
|
+
}
|
|
24232
|
+
catch {
|
|
24233
|
+
return null;
|
|
24234
|
+
}
|
|
24235
|
+
}
|
|
24236
|
+
determineTextPasteIntent(text) {
|
|
24237
|
+
const payload = this.parseClipboardPayload(text);
|
|
24238
|
+
if (payload) {
|
|
24239
|
+
return { type: 'kritzel_objects', payload };
|
|
24240
|
+
}
|
|
24241
|
+
if (text && text.trim().length > 0) {
|
|
24242
|
+
return { type: 'plain_text', text };
|
|
24243
|
+
}
|
|
24244
|
+
return null;
|
|
24245
|
+
}
|
|
24246
|
+
}
|
|
24247
|
+
|
|
24248
|
+
class KritzelIosPasteIntentProvider {
|
|
24249
|
+
canHandle(context) {
|
|
24250
|
+
return !context.event?.clipboardData && KritzelDevicesHelper.isIOS();
|
|
24251
|
+
}
|
|
24252
|
+
async getPasteIntent(context) {
|
|
24253
|
+
// On iOS Safari, eventless programmatic clipboard reads are commonly unavailable
|
|
24254
|
+
// (e.g. the context-menu paste calls engine.paste() without a native ClipboardEvent).
|
|
24255
|
+
// Fall back to the in-memory session buffer so internal object copy/paste still works.
|
|
24256
|
+
const payload = context.core.clipboardManager.sessionClipboardPayload;
|
|
24257
|
+
if (payload) {
|
|
24258
|
+
return { type: 'kritzel_objects', payload };
|
|
24259
|
+
}
|
|
24260
|
+
return { type: 'unknown' };
|
|
24261
|
+
}
|
|
24262
|
+
}
|
|
24263
|
+
|
|
24264
|
+
/**
|
|
24265
|
+
* Manages clipboard operations (copy/cut/paste) for a core instance.
|
|
24266
|
+
*
|
|
24267
|
+
* The system clipboard is the preferred source of truth: copy serializes the current
|
|
24268
|
+
* selection into a Kritzel payload, paste reads + revives it.
|
|
24269
|
+
*
|
|
24270
|
+
* A copy also mirrors the payload into an in-memory session buffer. On platforms where
|
|
24271
|
+
* eventless programmatic clipboard access is unavailable (notably iOS Safari, where the
|
|
24272
|
+
* context menu triggers copy/paste without a native ClipboardEvent), the session buffer
|
|
24273
|
+
* is used as the fallback source so internal object copy/paste keeps working.
|
|
24274
|
+
*/
|
|
24275
|
+
class KritzelClipboardManager {
|
|
24276
|
+
_core;
|
|
24277
|
+
_pasteIntentRegistry;
|
|
24278
|
+
/**
|
|
24279
|
+
* In-memory mirror of the most recent copy payload. Acts as the fallback clipboard
|
|
24280
|
+
* source when the system clipboard cannot be read programmatically (e.g. iOS Safari
|
|
24281
|
+
* context-menu paste). Reset is not required; a new copy overwrites it.
|
|
24282
|
+
*/
|
|
24283
|
+
_sessionClipboardPayload = null;
|
|
24284
|
+
/**
|
|
24285
|
+
* In-memory cascade offset (world units) applied to successive pastes of the same
|
|
24286
|
+
* clipboard content so they don't stack exactly on top of each other. Reset on copy/cut.
|
|
24287
|
+
*/
|
|
24288
|
+
_pasteOffset = 0;
|
|
24289
|
+
constructor(core) {
|
|
24290
|
+
this._core = core;
|
|
24291
|
+
this._pasteIntentRegistry = new KritzelPasteIntentProviderRegistry(this._core);
|
|
24292
|
+
// Provider order is intentional: iOS eventless path should short-circuit,
|
|
24293
|
+
// native paste events remain preferred, and async navigator APIs are fallback.
|
|
24294
|
+
this._pasteIntentRegistry.register(new KritzelIosPasteIntentProvider());
|
|
24295
|
+
this._pasteIntentRegistry.register(new KritzelEventPasteIntentProvider());
|
|
24296
|
+
this._pasteIntentRegistry.register(new KritzelNavigatorPasteIntentProvider());
|
|
24297
|
+
}
|
|
24298
|
+
/**
|
|
24299
|
+
* The most recent copy payload mirrored in memory, used as a fallback clipboard
|
|
24300
|
+
* source when the system clipboard cannot be read programmatically.
|
|
24301
|
+
*/
|
|
24302
|
+
get sessionClipboardPayload() {
|
|
24303
|
+
return this._sessionClipboardPayload;
|
|
24304
|
+
}
|
|
24305
|
+
/**
|
|
24306
|
+
* Serializes an object for the clipboard payload. Groups additionally embed their
|
|
24307
|
+
* (recursively serialized) children under `__children__` so the payload is self-contained.
|
|
24308
|
+
*/
|
|
24309
|
+
serializeForClipboard(object) {
|
|
24310
|
+
const serialized = (typeof object.serialize === 'function' ? object.serialize() : object);
|
|
24311
|
+
if (KritzelClassHelper.isInstanceOf(object, 'KritzelGroup')) {
|
|
24312
|
+
serialized.__children__ = object.children.map(child => this.serializeForClipboard(child));
|
|
24313
|
+
}
|
|
24314
|
+
return serialized;
|
|
24315
|
+
}
|
|
24316
|
+
serializeClipboardPayload(payload) {
|
|
24317
|
+
return `${CLIPBOARD_PAYLOAD_PREFIX}${JSON.stringify(payload)}`;
|
|
24318
|
+
}
|
|
24142
24319
|
async writeBrowserClipboardPayload(payload, event) {
|
|
24143
24320
|
const serialized = this.serializeClipboardPayload(payload);
|
|
24144
24321
|
if (event?.clipboardData) {
|
|
@@ -24218,80 +24395,6 @@ class KritzelClipboardManager {
|
|
|
24218
24395
|
}
|
|
24219
24396
|
}
|
|
24220
24397
|
}
|
|
24221
|
-
/**
|
|
24222
|
-
* Evaluates the clipboard event to determine exactly what the user is trying to paste.
|
|
24223
|
-
* Returns a PasteIntent covering the 3 main cases: Kritzel objects, Images, or Plain text.
|
|
24224
|
-
*/
|
|
24225
|
-
async determinePasteIntent(event) {
|
|
24226
|
-
if (event?.clipboardData) {
|
|
24227
|
-
const data = event.clipboardData;
|
|
24228
|
-
const imageFiles = Array.from(data.files ?? []).filter(file => file.type.startsWith('image/'));
|
|
24229
|
-
if (imageFiles.length > 0) {
|
|
24230
|
-
return { type: 'image_files', files: imageFiles };
|
|
24231
|
-
}
|
|
24232
|
-
if (data.types.includes('text/plain')) {
|
|
24233
|
-
const textIntent = this.determineTextPasteIntent(data.getData('text/plain'));
|
|
24234
|
-
if (textIntent)
|
|
24235
|
-
return textIntent;
|
|
24236
|
-
}
|
|
24237
|
-
return { type: 'unknown' };
|
|
24238
|
-
}
|
|
24239
|
-
if (typeof navigator === 'undefined' || !navigator.clipboard) {
|
|
24240
|
-
return { type: 'unknown' };
|
|
24241
|
-
}
|
|
24242
|
-
const { clipboard } = navigator;
|
|
24243
|
-
const permissionState = await this._core.permissionManager.checkPermission('clipboard-read');
|
|
24244
|
-
if (permissionState !== 'granted') {
|
|
24245
|
-
const requestedState = await this._core.permissionManager.requestPermission('clipboard-read');
|
|
24246
|
-
if (requestedState !== 'granted') {
|
|
24247
|
-
return { type: 'unknown' };
|
|
24248
|
-
}
|
|
24249
|
-
}
|
|
24250
|
-
if (clipboard.read) {
|
|
24251
|
-
try {
|
|
24252
|
-
const items = await clipboard.read();
|
|
24253
|
-
const { imageFiles, text } = await this.processClipboardItems(items);
|
|
24254
|
-
if (imageFiles.length > 0) {
|
|
24255
|
-
return { type: 'image_files', files: imageFiles };
|
|
24256
|
-
}
|
|
24257
|
-
if (text !== null) {
|
|
24258
|
-
const textIntent = this.determineTextPasteIntent(text);
|
|
24259
|
-
if (textIntent)
|
|
24260
|
-
return textIntent;
|
|
24261
|
-
}
|
|
24262
|
-
}
|
|
24263
|
-
catch {
|
|
24264
|
-
// Fall back to readText below when read() is denied or unavailable at runtime.
|
|
24265
|
-
}
|
|
24266
|
-
}
|
|
24267
|
-
if (clipboard.readText) {
|
|
24268
|
-
const textIntent = this.determineTextPasteIntent(await clipboard.readText());
|
|
24269
|
-
if (textIntent)
|
|
24270
|
-
return textIntent;
|
|
24271
|
-
}
|
|
24272
|
-
return { type: 'unknown' };
|
|
24273
|
-
}
|
|
24274
|
-
/**
|
|
24275
|
-
* Helper to parse clipboard items in parallel
|
|
24276
|
-
*/
|
|
24277
|
-
async processClipboardItems(items) {
|
|
24278
|
-
const imageFiles = [];
|
|
24279
|
-
let text = null;
|
|
24280
|
-
// Process items concurrently
|
|
24281
|
-
await Promise.all(items.map(async (item) => {
|
|
24282
|
-
const imageType = item.types.find(type => type.startsWith('image/'));
|
|
24283
|
-
if (imageType) {
|
|
24284
|
-
const blob = await item.getType(imageType);
|
|
24285
|
-
imageFiles.push(new File([blob], 'pasted-image', { type: imageType }));
|
|
24286
|
-
return;
|
|
24287
|
-
}
|
|
24288
|
-
if (text === null && item.types.includes('text/plain')) {
|
|
24289
|
-
const blob = await item.getType('text/plain');
|
|
24290
|
-
text = await blob.text();
|
|
24291
|
-
}
|
|
24292
|
-
}));
|
|
24293
|
-
return { imageFiles, text };
|
|
24294
|
-
}
|
|
24295
24398
|
/**
|
|
24296
24399
|
* Copies currently selected objects into internal clipboard state.
|
|
24297
24400
|
*/
|
|
@@ -24307,6 +24410,9 @@ class KritzelClipboardManager {
|
|
|
24307
24410
|
};
|
|
24308
24411
|
// A fresh copy resets the cascade so the first paste lands at the default offset.
|
|
24309
24412
|
this._pasteOffset = 0;
|
|
24413
|
+
// Mirror the payload in memory so eventless paste still works where the system
|
|
24414
|
+
// clipboard is not programmatically accessible (e.g. iOS Safari context menu).
|
|
24415
|
+
this._sessionClipboardPayload = payload;
|
|
24310
24416
|
void this.writeBrowserClipboardPayload(payload, event);
|
|
24311
24417
|
}
|
|
24312
24418
|
/**
|
|
@@ -24365,7 +24471,7 @@ class KritzelClipboardManager {
|
|
|
24365
24471
|
* Pastes objects from clipboard with optional explicit world coordinates.
|
|
24366
24472
|
*/
|
|
24367
24473
|
async paste(x, y, event) {
|
|
24368
|
-
const pasteIntent = await this.
|
|
24474
|
+
const pasteIntent = await this._pasteIntentRegistry.resolve(event);
|
|
24369
24475
|
let objects = null;
|
|
24370
24476
|
if (pasteIntent.type === 'kritzel_objects') {
|
|
24371
24477
|
objects = this.materializeKritzelObjects(pasteIntent);
|
|
@@ -31274,7 +31380,7 @@ const KritzelPortal = class {
|
|
|
31274
31380
|
* This file is auto-generated by the version bump scripts.
|
|
31275
31381
|
* Do not modify manually.
|
|
31276
31382
|
*/
|
|
31277
|
-
const KRITZEL_VERSION = '0.3.
|
|
31383
|
+
const KRITZEL_VERSION = '0.3.33';
|
|
31278
31384
|
|
|
31279
31385
|
const kritzelSettingsCss = () => `:host{display:contents}kritzel-dialog{--kritzel-dialog-body-padding:0;--kritzel-dialog-width-large:800px;--kritzel-dialog-height-large:500px}.footer-button{padding:8px 16px;border-radius:6px;cursor:pointer;font-size:14px}.cancel-button{border:1px solid #ebebeb;background:#fff;color:inherit}.cancel-button:hover{background:#f5f5f5}.settings-content{padding:0}.settings-content h3{margin:0 0 16px 0;font-size:18px;font-weight:600;color:var(--kritzel-settings-content-heading-color, #333333)}.settings-content p{margin:0;font-size:14px;color:var(--kritzel-settings-content-text-color, #666666);line-height:1.5}.settings-group{display:flex;flex-direction:column;gap:24px}.settings-item{display:flex;flex-direction:column;gap:8px}.settings-row{display:flex;align-items:center;justify-content:space-between;gap:16px}.settings-label{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.settings-description{font-size:12px;color:var(--kritzel-settings-description-color, #888888);margin:0;line-height:1.4}.shortcuts-list{display:flex;flex-direction:column;gap:24px}.shortcuts-category{display:flex;flex-direction:column;gap:8px}.shortcuts-category-title{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.shortcuts-group{display:flex;flex-direction:column;gap:4px}.shortcut-item{display:flex;justify-content:space-between;align-items:center;padding:6px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-item-bg, rgba(0, 0, 0, 0.02))}.shortcut-label{font-size:14px;color:var(--kritzel-settings-content-text-color, #666666)}.shortcut-key{font-family:monospace;font-size:12px;padding:2px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-key-bg, #f0f0f0);color:var(--kritzel-settings-shortcut-key-color, #333333);border:1px solid var(--kritzel-settings-shortcut-key-border, #ddd)}`;
|
|
31280
31386
|
|
|
@@ -28182,10 +28182,11 @@ class KritzelAnchorManager {
|
|
|
28182
28182
|
}
|
|
28183
28183
|
getSnapRadius(object, pointerType) {
|
|
28184
28184
|
const baseRadius = Math.min(object.width, object.height) / 5;
|
|
28185
|
+
const safeScale = this.getSafeScale(this._core.store.state.scale);
|
|
28185
28186
|
if (pointerType === 'touch' || pointerType === 'pen') {
|
|
28186
|
-
return Math.max(baseRadius * 1.75, 24 /
|
|
28187
|
+
return Math.max(baseRadius * 1.75, 24 / safeScale);
|
|
28187
28188
|
}
|
|
28188
|
-
return baseRadius;
|
|
28189
|
+
return Math.max(baseRadius, 12 / safeScale);
|
|
28189
28190
|
}
|
|
28190
28191
|
/**
|
|
28191
28192
|
* Sets the current snap candidate for visual feedback during line endpoint dragging.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{H as a,a as T}from"./p-CH2FWTVr.js";export{N as APP_STATE_MIGRATIONS,A as AssetNotFoundError,P as CURRENT_APP_STATE_SCHEMA_VERSION,Q as CURRENT_WORKSPACE_SCHEMA_VERSION,B as DEFAULT_ASSET_STORAGE_CONFIG,D as DEFAULT_BRUSH_CONFIG,z as DEFAULT_LINE_TOOL_CONFIG,y as DEFAULT_TEXT_CONFIG,G as DE_LOCALE,F as EN_LOCALE,J as FR_LOCALE,I as IndexedDBAssetProvider,L as KritzelAlignment,t as KritzelAnchorManager,r as KritzelAssetResolver,K as KritzelBaseObject,i as KritzelBaseTool,j as KritzelBrushTool,p as KritzelCursorHelper,h as KritzelCustomElement,x as KritzelCustomElementRendererRegistry,l as KritzelEraserTool,f as KritzelGroup,d as KritzelImage,m as KritzelImageTool,w as KritzelLicenseManager,e as KritzelLine,k as KritzelLineTool,v as KritzelLocalizationManager,c as KritzelPath,q as KritzelSelectionTool,g as KritzelShape,o as KritzelShapeTool,b as KritzelText,n as KritzelTextTool,u as KritzelThemeManager,s as KritzelWorkspace,S as ShapeType,W as WORKSPACE_EXPORT_VERSION,O as WORKSPACE_MIGRATIONS,E as darkTheme,C as lightTheme,M as runMigrations}from"./p-CH2FWTVr.js";import*as $ from"yjs";import{IndexeddbPersistence as _}from"y-indexeddb";import{WebsocketProvider as R}from"y-websocket";const H=Math.floor,U=127,V=Number.MAX_SAFE_INTEGER;class X{constructor(){this.cpos=0,this.cbuf=new Uint8Array(100),this.bufs=[]}}const Y=()=>new X,Z=t=>{const e=new Uint8Array((t=>{let e=t.cpos;for(let s=0;s<t.bufs.length;s++)e+=t.bufs[s].length;return e})(t));let s=0;for(let i=0;i<t.bufs.length;i++){const n=t.bufs[i];e.set(n,s),s+=n.length}return e.set(new Uint8Array(t.cbuf.buffer,0,t.cpos),s),e},tt=(t,e)=>{const s=t.cbuf.length;t.cpos===s&&(t.bufs.push(t.cbuf),t.cbuf=new Uint8Array(2*s),t.cpos=0),t.cbuf[t.cpos++]=e},et=(t,e)=>{for(;e>U;)tt(t,128|U&e),e=H(e/128);tt(t,U&e)},st=(t,e)=>{et(t,e.byteLength),((t,e)=>{const s=t.cbuf.length,i=t.cpos,n=((t,e)=>t<e?t:e)(s-i,e.length),o=e.length-n;t.cbuf.set(e.subarray(0,n),i),t.cpos+=n,o>0&&(t.bufs.push(t.cbuf),t.cbuf=new Uint8Array(((t,e)=>t>e?t:e)(2*s,o)),t.cbuf.set(e.subarray(n)),t.cpos=o)})(t,e)},it=t=>new Error(t),nt=it("Unexpected end of array"),ot=it("Integer out of Range");class rt{constructor(t){this.arr=t,this.pos=0}}const ct=t=>((t,e)=>{const s=new Uint8Array(t.arr.buffer,t.pos+t.arr.byteOffset,e);return t.pos+=e,s})(t,at(t)),at=t=>{let e=0,s=1;const i=t.arr.length;for(;t.pos<i;){const i=t.arr[t.pos++];if(e+=(i&U)*s,s*=128,i<128)return e;if(e>V)throw ot}throw nt};class ht{type="local";doc;channel;_synced=!1;constructor(t,e,s){this.doc=e,this.channel=new BroadcastChannel(t),this.channel.onmessage=t=>{this.handleMessage(t.data)},this.doc.on("update",this.handleDocUpdate),this.broadcastSync(),setTimeout((()=>{this._synced=!0}),100),s?.quiet||console.info(`BroadcastChannel Provider initialized: ${t}`)}handleDocUpdate=(t,e)=>{if(e!==this){const e=Y();et(e,0),st(e,t),this.channel.postMessage(Z(e))}};handleMessage(t){const e=(s=new Uint8Array(t),new rt(s));var s;switch(at(e)){case 0:const t=ct(e);$.applyUpdate(this.doc,t,this);break;case 1:this.broadcastSync();break;case 2:const s=ct(e),i=$.encodeStateAsUpdate(this.doc,s);if(i.length>0){const t=Y();et(t,0),st(t,i),this.channel.postMessage(Z(t))}}}broadcastSync(){const t=Y();et(t,2),st(t,$.encodeStateVector(this.doc)),this.channel.postMessage(Z(t))}async connect(){if(!this._synced)return new Promise((t=>{const e=()=>{this._synced?t():setTimeout(e,50)};e()}))}disconnect(){}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleDocUpdate),this.channel.close()}}const lt=new Map;class dt{type="local";doc;cacheKey;isConnected=!1;constructor(t,e,s){this.doc=e,this.cacheKey=s?.name??t}handleUpdate=(t,e)=>{e!==this&<.set(this.cacheKey,$.encodeStateAsUpdate(this.doc))};async connect(){if(this.isConnected)return;const t=lt.get(this.cacheKey);t&&$.applyUpdate(this.doc,t,this),this.doc.on("update",this.handleUpdate),this.isConnected=!0}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleUpdate),this.isConnected=!1}static clear(t){void 0!==t?lt.delete(t):lt.clear()}}class ut{type="local";provider;isConnected=!1;constructor(t,e,s){this.provider=new _(s?.name||t,e)}async connect(){if(!this.isConnected)return new Promise((t=>{this.provider.on("synced",(()=>{this.isConnected=!0,t()}))}))}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class yt{type="network";provider;isConnected=!1;_quiet=!1;get awareness(){return this.provider.awareness}constructor(t,e,s){const i=s?.url||"ws://localhost:1234",n=s?.roomName||t;this.provider=new R(i,n,e,{params:s?.params,protocols:s?.protocols,WebSocketPolyfill:s?.WebSocketPolyfill,awareness:s?.awareness,maxBackoffTime:s?.maxBackoffTime,disableBc:!0}),this._quiet=s?.quiet??!1,this.setupEventListeners(),this._quiet||console.info(`WebSocket Provider initialized: ${i}/${n}`)}static with(t){return{create:(e,s,i)=>{const n=i?{...t,...i}:t;return new yt(e,s,n)}}}setupEventListeners(){this.provider.on("status",(({status:t})=>{"connected"===t?(this.isConnected=!0,this._quiet||console.info("WebSocket connected")):"disconnected"===t&&(this.isConnected=!1,this._quiet||console.info("WebSocket disconnected"))})),this.provider.on("sync",(t=>{t&&!this._quiet&&console.info("WebSocket synced")}))}async connect(){if(!this.isConnected)return new Promise(((t,e)=>{const s=setTimeout((()=>{e(new Error("WebSocket connection timeout"))}),1e4),i=({status:e})=>{"connected"===e&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,t())};this.provider.on("status",i),this.provider.wsconnected&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,t())}))}disconnect(){this.provider&&this.provider.disconnect(),this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class wt{type="network";provider;isConnected=!1;isSynced=!1;usesSharedSocket=!1;isDestroyed=!1;connectTimeout=null;pendingConnectReject=null;connectionTimeoutMs;_connectionStatus="disconnected";visibilityHandler=null;onlineHandler=null;get awareness(){return this.provider.awareness}get connectionStatus(){return this._connectionStatus}static sharedWebSocketProvider=null;constructor(t,e,s){const i=s?.name||t,n=s?.url||"ws://localhost:1234";this.connectionTimeoutMs=s?.connectionTimeout??1e4;const o=s?.websocketProvider||wt.sharedWebSocketProvider,r={};void 0!==s?.delay&&(r.delay=s.delay),void 0!==s?.factor&&(r.factor=s.factor),void 0!==s?.maxAttempts&&(r.maxAttempts=s.maxAttempts),void 0!==s?.minDelay&&(r.minDelay=s.minDelay),void 0!==s?.maxDelay&&(r.maxDelay=s.maxDelay);const c=()=>{this.isDestroyed||(this.isConnected=!0,this._connectionStatus="connected",s?.quiet||console.info(`Hocuspocus connected: ${i}`),s?.onConnect&&s.onConnect())},h=()=>{this.isDestroyed||(this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected",s?.quiet||console.info(`Hocuspocus disconnected: ${i}`),s?.onDisconnect&&s.onDisconnect())},l=()=>{this.isDestroyed||(this.isSynced=!0,this._connectionStatus="synced",s?.quiet||console.info(`Hocuspocus synced: ${i}`),s?.onSynced&&s.onSynced())},d=t=>{this.isDestroyed||("connecting"===t.status&&(this._connectionStatus="connecting"),s?.onStatus&&s.onStatus(t))};if(o){this.usesSharedSocket=!0;const t={websocketProvider:o,name:i,document:e,token:s?.token||null,onStatus:d,onConnect:c,onDisconnect:h,onSynced:l,...r};void 0!==s?.forceSyncInterval&&(t.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(t.onAuthenticationFailed=s.onAuthenticationFailed),this.provider=new a(t),this.provider.attach(),s?.quiet||console.info(`Hocuspocus Provider initialized (multiplexed): ${i}`)}else{this.usesSharedSocket=!1;const t={url:n,name:i,document:e,token:s?.token||null,autoConnect:!1,onStatus:d,onConnect:c,onDisconnect:h,onSynced:l,...r};void 0!==s?.forceSyncInterval&&(t.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(t.onAuthenticationFailed=s.onAuthenticationFailed),s?.WebSocketPolyfill&&(t.WebSocketPolyfill=s.WebSocketPolyfill),this.provider=new a(t),s?.quiet||console.info(`Hocuspocus Provider initialized: ${n}/${i}`)}this.setupBrowserEventListeners()}setupBrowserEventListeners(){"undefined"!=typeof document&&(this.visibilityHandler=()=>{"visible"!==document.visibilityState||this.isConnected||this.isDestroyed||this.provider.connect()},document.addEventListener("visibilitychange",this.visibilityHandler)),"undefined"!=typeof window&&(this.onlineHandler=()=>{this.isConnected||this.isDestroyed||this.provider.connect()},window.addEventListener("online",this.onlineHandler))}removeBrowserEventListeners(){this.visibilityHandler&&"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.onlineHandler&&"undefined"!=typeof window&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null)}static createSharedWebSocket(t){if(wt.sharedWebSocketProvider)return console.warn("Shared WebSocket already exists. Returning existing instance."),wt.sharedWebSocketProvider;const e={url:t.url};return t.WebSocketPolyfill&&(e.WebSocketPolyfill=t.WebSocketPolyfill),t.onConnect&&(e.onConnect=t.onConnect),t.onDisconnect&&(e.onDisconnect=t.onDisconnect),t.onStatus&&(e.onStatus=t.onStatus),wt.sharedWebSocketProvider=new T(e),console.info(`Shared Hocuspocus WebSocket created: ${t.url}`),wt.sharedWebSocketProvider}static destroySharedWebSocket(){wt.sharedWebSocketProvider&&(wt.sharedWebSocketProvider.destroy(),wt.sharedWebSocketProvider=null,console.info("Shared Hocuspocus WebSocket destroyed"))}static getSharedWebSocket(){return wt.sharedWebSocketProvider}static with(t){return{create:(e,s,i)=>{const n=i?{...t,...i}:t;return new wt(e,s,n)}}}async connect(){if(!this.isSynced&&!this.isDestroyed)return this._connectionStatus="connecting",new Promise(((t,e)=>{this.pendingConnectReject=e,this.connectTimeout=setTimeout((()=>{this.pendingConnectReject=null,this.connectTimeout=null,e(new Error("Hocuspocus connection timeout"))}),this.connectionTimeoutMs);const s=()=>{this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),this.isDestroyed||t()};if(this.provider.on("synced",s),this.provider.isSynced)return this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),void t();this.isConnected||this.usesSharedSocket||this.provider.connect()}))}async reconnect(){return this.disconnect(),this.connect()}disconnect(){this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.provider&&(this.usesSharedSocket?this.provider.detach():this.provider.disconnect()),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}destroy(){this.isDestroyed=!0,this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.removeBrowserEventListeners(),this.provider&&this.provider.destroy(),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}}class pt{type="remote";name="HttpAssetProvider";_options;constructor(t){this._options=t}static with(t){return{create:()=>new pt(t)}}async init(){this._options.quiet||console.info("HttpAssetProvider initialized")}destroy(){}canResolve(t){return!0}async put(t,e){const s=e.id??this.generateUuid(),i={...e,id:s},n=await(this._options.headers?.())??{},o=await this._options.uploadUrl(i),r=this._options.upload??this.defaultUpload;return{id:(await r(o,t,i,n)).id??s,kind:e.kind??"file",mimeType:e.mimeType,size:t.size,createdAt:Date.now(),width:e.width,height:e.height,durationMs:e.durationMs,originalFilename:e.originalFilename}}async resolve(t){return this._options.resolveUrl(t)}async fetch(t){const e=await this._options.resolveUrl(t),s=await(this._options.headers?.())??{},i=await fetch(e,{headers:s});if(!i.ok)throw new Error(`[HttpAssetProvider] Failed to fetch asset ${t}: ${i.status} ${i.statusText}`);return i.blob()}async delete(t){if(!this._options.deleteUrl)return;const e=await this._options.deleteUrl(t),s=await(this._options.headers?.())??{},i=await fetch(e,{method:"DELETE",headers:s});if(!i.ok)throw new Error(`[HttpAssetProvider] Failed to delete asset ${t}: ${i.status} ${i.statusText}`);this._options.quiet||console.info(`HttpAssetProvider: deleted asset ${t}`)}defaultUpload=async(t,e,s,i)=>{const n=new FormData;n.append("metadata",JSON.stringify(s)),n.append("file",e,s.originalFilename||`${s.id}.${this.extensionFromMime(s.mimeType)}`);const o=await fetch(t,{method:"POST",headers:i,body:n});if(!o.ok)throw new Error(`[HttpAssetProvider] Upload failed: ${o.status} ${o.statusText}`);return(o.headers.get("content-type")||"").includes("application/json")?await o.json():{}};extensionFromMime(t){const e=t.indexOf("/");return e>=0?t.slice(e+1):"bin"}generateUuid(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)return crypto.randomUUID();const t=crypto.getRandomValues(new Uint8Array(16));t[6]=15&t[6]|64,t[8]=63&t[8]|128;const e=Array.from(t,(t=>t.toString(16).padStart(2,"0"))).join("");return`${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20)}`}}class mt{type="remote";name="PresignedAssetProvider";_options;constructor(t){this._options=t}static with(t){return{create:()=>new mt(t)}}async init(){this._options.quiet||console.info("PresignedAssetProvider initialized")}destroy(){}canResolve(t){return!0}async put(t,e){const s=await this._options.getUploadDescriptor(e),i=s.method??"PUT",n={...s.headers??{}};let o;if(n["Content-Type"]||n["content-type"]||(n["Content-Type"]=e.mimeType),"POST"===i&&s.fields){const e=new FormData;for(const[t,i]of Object.entries(s.fields))e.append(t,i);e.append("file",t),o=e,delete n["Content-Type"],delete n["content-type"]}else o=t;const r=await fetch(s.url,{method:i,headers:n,body:o});if(!r.ok)throw new Error(`[PresignedAssetProvider] Upload failed: ${r.status} ${r.statusText}`);return{id:s.id,kind:e.kind??"file",mimeType:e.mimeType,size:t.size,createdAt:Date.now(),width:e.width,height:e.height,durationMs:e.durationMs,originalFilename:e.originalFilename}}async resolve(t){return this._options.getDownloadUrl(t)}async fetch(t){const e=await this._options.getDownloadUrl(t),s=await fetch(e);if(!s.ok)throw new Error(`[PresignedAssetProvider] Failed to fetch asset ${t}: ${s.status} ${s.statusText}`);return s.blob()}async delete(t){this._options.deleteAsset&&(await this._options.deleteAsset(t),this._options.quiet||console.info(`PresignedAssetProvider: deleted asset ${t}`))}}export{ht as BroadcastSyncProvider,wt as HocuspocusSyncProvider,pt as HttpAssetProvider,dt as InMemorySyncProvider,ut as IndexedDBSyncProvider,mt as PresignedAssetProvider,yt as WebSocketSyncProvider}
|
|
1
|
+
import{H as a,a as T}from"./p-3oYjlURq.js";export{N as APP_STATE_MIGRATIONS,A as AssetNotFoundError,P as CURRENT_APP_STATE_SCHEMA_VERSION,Q as CURRENT_WORKSPACE_SCHEMA_VERSION,B as DEFAULT_ASSET_STORAGE_CONFIG,D as DEFAULT_BRUSH_CONFIG,z as DEFAULT_LINE_TOOL_CONFIG,y as DEFAULT_TEXT_CONFIG,G as DE_LOCALE,F as EN_LOCALE,J as FR_LOCALE,I as IndexedDBAssetProvider,L as KritzelAlignment,t as KritzelAnchorManager,r as KritzelAssetResolver,K as KritzelBaseObject,i as KritzelBaseTool,j as KritzelBrushTool,p as KritzelCursorHelper,h as KritzelCustomElement,x as KritzelCustomElementRendererRegistry,l as KritzelEraserTool,f as KritzelGroup,d as KritzelImage,m as KritzelImageTool,w as KritzelLicenseManager,e as KritzelLine,k as KritzelLineTool,v as KritzelLocalizationManager,c as KritzelPath,q as KritzelSelectionTool,g as KritzelShape,o as KritzelShapeTool,b as KritzelText,n as KritzelTextTool,u as KritzelThemeManager,s as KritzelWorkspace,S as ShapeType,W as WORKSPACE_EXPORT_VERSION,O as WORKSPACE_MIGRATIONS,E as darkTheme,C as lightTheme,M as runMigrations}from"./p-3oYjlURq.js";import*as $ from"yjs";import{IndexeddbPersistence as _}from"y-indexeddb";import{WebsocketProvider as R}from"y-websocket";const U=Math.floor,H=127,V=Number.MAX_SAFE_INTEGER;class X{constructor(){this.cpos=0,this.cbuf=new Uint8Array(100),this.bufs=[]}}const Y=()=>new X,Z=t=>{const e=new Uint8Array((t=>{let e=t.cpos;for(let s=0;s<t.bufs.length;s++)e+=t.bufs[s].length;return e})(t));let s=0;for(let i=0;i<t.bufs.length;i++){const n=t.bufs[i];e.set(n,s),s+=n.length}return e.set(new Uint8Array(t.cbuf.buffer,0,t.cpos),s),e},tt=(t,e)=>{const s=t.cbuf.length;t.cpos===s&&(t.bufs.push(t.cbuf),t.cbuf=new Uint8Array(2*s),t.cpos=0),t.cbuf[t.cpos++]=e},et=(t,e)=>{for(;e>H;)tt(t,128|H&e),e=U(e/128);tt(t,H&e)},st=(t,e)=>{et(t,e.byteLength),((t,e)=>{const s=t.cbuf.length,i=t.cpos,n=((t,e)=>t<e?t:e)(s-i,e.length),o=e.length-n;t.cbuf.set(e.subarray(0,n),i),t.cpos+=n,o>0&&(t.bufs.push(t.cbuf),t.cbuf=new Uint8Array(((t,e)=>t>e?t:e)(2*s,o)),t.cbuf.set(e.subarray(n)),t.cpos=o)})(t,e)},it=t=>new Error(t),nt=it("Unexpected end of array"),ot=it("Integer out of Range");class rt{constructor(t){this.arr=t,this.pos=0}}const ct=t=>((t,e)=>{const s=new Uint8Array(t.arr.buffer,t.pos+t.arr.byteOffset,e);return t.pos+=e,s})(t,at(t)),at=t=>{let e=0,s=1;const i=t.arr.length;for(;t.pos<i;){const i=t.arr[t.pos++];if(e+=(i&H)*s,s*=128,i<128)return e;if(e>V)throw ot}throw nt};class ht{type="local";doc;channel;_synced=!1;constructor(t,e,s){this.doc=e,this.channel=new BroadcastChannel(t),this.channel.onmessage=t=>{this.handleMessage(t.data)},this.doc.on("update",this.handleDocUpdate),this.broadcastSync(),setTimeout((()=>{this._synced=!0}),100),s?.quiet||console.info(`BroadcastChannel Provider initialized: ${t}`)}handleDocUpdate=(t,e)=>{if(e!==this){const e=Y();et(e,0),st(e,t),this.channel.postMessage(Z(e))}};handleMessage(t){const e=(s=new Uint8Array(t),new rt(s));var s;switch(at(e)){case 0:const t=ct(e);$.applyUpdate(this.doc,t,this);break;case 1:this.broadcastSync();break;case 2:const s=ct(e),i=$.encodeStateAsUpdate(this.doc,s);if(i.length>0){const t=Y();et(t,0),st(t,i),this.channel.postMessage(Z(t))}}}broadcastSync(){const t=Y();et(t,2),st(t,$.encodeStateVector(this.doc)),this.channel.postMessage(Z(t))}async connect(){if(!this._synced)return new Promise((t=>{const e=()=>{this._synced?t():setTimeout(e,50)};e()}))}disconnect(){}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleDocUpdate),this.channel.close()}}const lt=new Map;class dt{type="local";doc;cacheKey;isConnected=!1;constructor(t,e,s){this.doc=e,this.cacheKey=s?.name??t}handleUpdate=(t,e)=>{e!==this&<.set(this.cacheKey,$.encodeStateAsUpdate(this.doc))};async connect(){if(this.isConnected)return;const t=lt.get(this.cacheKey);t&&$.applyUpdate(this.doc,t,this),this.doc.on("update",this.handleUpdate),this.isConnected=!0}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleUpdate),this.isConnected=!1}static clear(t){void 0!==t?lt.delete(t):lt.clear()}}class ut{type="local";provider;isConnected=!1;constructor(t,e,s){this.provider=new _(s?.name||t,e)}async connect(){if(!this.isConnected)return new Promise((t=>{this.provider.on("synced",(()=>{this.isConnected=!0,t()}))}))}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class yt{type="network";provider;isConnected=!1;_quiet=!1;get awareness(){return this.provider.awareness}constructor(t,e,s){const i=s?.url||"ws://localhost:1234",n=s?.roomName||t;this.provider=new R(i,n,e,{params:s?.params,protocols:s?.protocols,WebSocketPolyfill:s?.WebSocketPolyfill,awareness:s?.awareness,maxBackoffTime:s?.maxBackoffTime,disableBc:!0}),this._quiet=s?.quiet??!1,this.setupEventListeners(),this._quiet||console.info(`WebSocket Provider initialized: ${i}/${n}`)}static with(t){return{create:(e,s,i)=>{const n=i?{...t,...i}:t;return new yt(e,s,n)}}}setupEventListeners(){this.provider.on("status",(({status:t})=>{"connected"===t?(this.isConnected=!0,this._quiet||console.info("WebSocket connected")):"disconnected"===t&&(this.isConnected=!1,this._quiet||console.info("WebSocket disconnected"))})),this.provider.on("sync",(t=>{t&&!this._quiet&&console.info("WebSocket synced")}))}async connect(){if(!this.isConnected)return new Promise(((t,e)=>{const s=setTimeout((()=>{e(new Error("WebSocket connection timeout"))}),1e4),i=({status:e})=>{"connected"===e&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,t())};this.provider.on("status",i),this.provider.wsconnected&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,t())}))}disconnect(){this.provider&&this.provider.disconnect(),this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class wt{type="network";provider;isConnected=!1;isSynced=!1;usesSharedSocket=!1;isDestroyed=!1;connectTimeout=null;pendingConnectReject=null;connectionTimeoutMs;_connectionStatus="disconnected";visibilityHandler=null;onlineHandler=null;get awareness(){return this.provider.awareness}get connectionStatus(){return this._connectionStatus}static sharedWebSocketProvider=null;constructor(t,e,s){const i=s?.name||t,n=s?.url||"ws://localhost:1234";this.connectionTimeoutMs=s?.connectionTimeout??1e4;const o=s?.websocketProvider||wt.sharedWebSocketProvider,r={};void 0!==s?.delay&&(r.delay=s.delay),void 0!==s?.factor&&(r.factor=s.factor),void 0!==s?.maxAttempts&&(r.maxAttempts=s.maxAttempts),void 0!==s?.minDelay&&(r.minDelay=s.minDelay),void 0!==s?.maxDelay&&(r.maxDelay=s.maxDelay);const c=()=>{this.isDestroyed||(this.isConnected=!0,this._connectionStatus="connected",s?.quiet||console.info(`Hocuspocus connected: ${i}`),s?.onConnect&&s.onConnect())},h=()=>{this.isDestroyed||(this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected",s?.quiet||console.info(`Hocuspocus disconnected: ${i}`),s?.onDisconnect&&s.onDisconnect())},l=()=>{this.isDestroyed||(this.isSynced=!0,this._connectionStatus="synced",s?.quiet||console.info(`Hocuspocus synced: ${i}`),s?.onSynced&&s.onSynced())},d=t=>{this.isDestroyed||("connecting"===t.status&&(this._connectionStatus="connecting"),s?.onStatus&&s.onStatus(t))};if(o){this.usesSharedSocket=!0;const t={websocketProvider:o,name:i,document:e,token:s?.token||null,onStatus:d,onConnect:c,onDisconnect:h,onSynced:l,...r};void 0!==s?.forceSyncInterval&&(t.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(t.onAuthenticationFailed=s.onAuthenticationFailed),this.provider=new a(t),this.provider.attach(),s?.quiet||console.info(`Hocuspocus Provider initialized (multiplexed): ${i}`)}else{this.usesSharedSocket=!1;const t={url:n,name:i,document:e,token:s?.token||null,autoConnect:!1,onStatus:d,onConnect:c,onDisconnect:h,onSynced:l,...r};void 0!==s?.forceSyncInterval&&(t.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(t.onAuthenticationFailed=s.onAuthenticationFailed),s?.WebSocketPolyfill&&(t.WebSocketPolyfill=s.WebSocketPolyfill),this.provider=new a(t),s?.quiet||console.info(`Hocuspocus Provider initialized: ${n}/${i}`)}this.setupBrowserEventListeners()}setupBrowserEventListeners(){"undefined"!=typeof document&&(this.visibilityHandler=()=>{"visible"!==document.visibilityState||this.isConnected||this.isDestroyed||this.provider.connect()},document.addEventListener("visibilitychange",this.visibilityHandler)),"undefined"!=typeof window&&(this.onlineHandler=()=>{this.isConnected||this.isDestroyed||this.provider.connect()},window.addEventListener("online",this.onlineHandler))}removeBrowserEventListeners(){this.visibilityHandler&&"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.onlineHandler&&"undefined"!=typeof window&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null)}static createSharedWebSocket(t){if(wt.sharedWebSocketProvider)return console.warn("Shared WebSocket already exists. Returning existing instance."),wt.sharedWebSocketProvider;const e={url:t.url};return t.WebSocketPolyfill&&(e.WebSocketPolyfill=t.WebSocketPolyfill),t.onConnect&&(e.onConnect=t.onConnect),t.onDisconnect&&(e.onDisconnect=t.onDisconnect),t.onStatus&&(e.onStatus=t.onStatus),wt.sharedWebSocketProvider=new T(e),console.info(`Shared Hocuspocus WebSocket created: ${t.url}`),wt.sharedWebSocketProvider}static destroySharedWebSocket(){wt.sharedWebSocketProvider&&(wt.sharedWebSocketProvider.destroy(),wt.sharedWebSocketProvider=null,console.info("Shared Hocuspocus WebSocket destroyed"))}static getSharedWebSocket(){return wt.sharedWebSocketProvider}static with(t){return{create:(e,s,i)=>{const n=i?{...t,...i}:t;return new wt(e,s,n)}}}async connect(){if(!this.isSynced&&!this.isDestroyed)return this._connectionStatus="connecting",new Promise(((t,e)=>{this.pendingConnectReject=e,this.connectTimeout=setTimeout((()=>{this.pendingConnectReject=null,this.connectTimeout=null,e(new Error("Hocuspocus connection timeout"))}),this.connectionTimeoutMs);const s=()=>{this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),this.isDestroyed||t()};if(this.provider.on("synced",s),this.provider.isSynced)return this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),void t();this.isConnected||this.usesSharedSocket||this.provider.connect()}))}async reconnect(){return this.disconnect(),this.connect()}disconnect(){this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.provider&&(this.usesSharedSocket?this.provider.detach():this.provider.disconnect()),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}destroy(){this.isDestroyed=!0,this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.removeBrowserEventListeners(),this.provider&&this.provider.destroy(),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}}class pt{type="remote";name="HttpAssetProvider";_options;constructor(t){this._options=t}static with(t){return{create:()=>new pt(t)}}async init(){this._options.quiet||console.info("HttpAssetProvider initialized")}destroy(){}canResolve(t){return!0}async put(t,e){const s=e.id??this.generateUuid(),i={...e,id:s},n=await(this._options.headers?.())??{},o=await this._options.uploadUrl(i),r=this._options.upload??this.defaultUpload;return{id:(await r(o,t,i,n)).id??s,kind:e.kind??"file",mimeType:e.mimeType,size:t.size,createdAt:Date.now(),width:e.width,height:e.height,durationMs:e.durationMs,originalFilename:e.originalFilename}}async resolve(t){return this._options.resolveUrl(t)}async fetch(t){const e=await this._options.resolveUrl(t),s=await(this._options.headers?.())??{},i=await fetch(e,{headers:s});if(!i.ok)throw new Error(`[HttpAssetProvider] Failed to fetch asset ${t}: ${i.status} ${i.statusText}`);return i.blob()}async delete(t){if(!this._options.deleteUrl)return;const e=await this._options.deleteUrl(t),s=await(this._options.headers?.())??{},i=await fetch(e,{method:"DELETE",headers:s});if(!i.ok)throw new Error(`[HttpAssetProvider] Failed to delete asset ${t}: ${i.status} ${i.statusText}`);this._options.quiet||console.info(`HttpAssetProvider: deleted asset ${t}`)}defaultUpload=async(t,e,s,i)=>{const n=new FormData;n.append("metadata",JSON.stringify(s)),n.append("file",e,s.originalFilename||`${s.id}.${this.extensionFromMime(s.mimeType)}`);const o=await fetch(t,{method:"POST",headers:i,body:n});if(!o.ok)throw new Error(`[HttpAssetProvider] Upload failed: ${o.status} ${o.statusText}`);return(o.headers.get("content-type")||"").includes("application/json")?await o.json():{}};extensionFromMime(t){const e=t.indexOf("/");return e>=0?t.slice(e+1):"bin"}generateUuid(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)return crypto.randomUUID();const t=crypto.getRandomValues(new Uint8Array(16));t[6]=15&t[6]|64,t[8]=63&t[8]|128;const e=Array.from(t,(t=>t.toString(16).padStart(2,"0"))).join("");return`${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20)}`}}class mt{type="remote";name="PresignedAssetProvider";_options;constructor(t){this._options=t}static with(t){return{create:()=>new mt(t)}}async init(){this._options.quiet||console.info("PresignedAssetProvider initialized")}destroy(){}canResolve(t){return!0}async put(t,e){const s=await this._options.getUploadDescriptor(e),i=s.method??"PUT",n={...s.headers??{}};let o;if(n["Content-Type"]||n["content-type"]||(n["Content-Type"]=e.mimeType),"POST"===i&&s.fields){const e=new FormData;for(const[t,i]of Object.entries(s.fields))e.append(t,i);e.append("file",t),o=e,delete n["Content-Type"],delete n["content-type"]}else o=t;const r=await fetch(s.url,{method:i,headers:n,body:o});if(!r.ok)throw new Error(`[PresignedAssetProvider] Upload failed: ${r.status} ${r.statusText}`);return{id:s.id,kind:e.kind??"file",mimeType:e.mimeType,size:t.size,createdAt:Date.now(),width:e.width,height:e.height,durationMs:e.durationMs,originalFilename:e.originalFilename}}async resolve(t){return this._options.getDownloadUrl(t)}async fetch(t){const e=await this._options.getDownloadUrl(t),s=await fetch(e);if(!s.ok)throw new Error(`[PresignedAssetProvider] Failed to fetch asset ${t}: ${s.status} ${s.statusText}`);return s.blob()}async delete(t){this._options.deleteAsset&&(await this._options.deleteAsset(t),this._options.quiet||console.info(`PresignedAssetProvider: deleted asset ${t}`))}}export{ht as BroadcastSyncProvider,wt as HocuspocusSyncProvider,pt as HttpAssetProvider,dt as InMemorySyncProvider,ut as IndexedDBSyncProvider,mt as PresignedAssetProvider,yt as WebSocketSyncProvider}
|