@preprio/toolkit 0.4.3 → 0.5.0-rc.10
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/README.md +416 -23
- package/dist/astro/index.cjs +1290 -1
- package/dist/astro/index.d.cts +47 -1
- package/dist/astro/index.d.ts +47 -1
- package/dist/astro/index.js +1297 -1
- package/dist/chunk-F46GJS6Y.js +26 -0
- package/dist/{chunk-H7VA7NGY.js → chunk-IDEC77LZ.js} +1480 -621
- package/dist/chunk-IPMB32AZ.js +246 -0
- package/dist/chunk-KD3GK6TO.js +1221 -0
- package/dist/chunk-S3VT4UWM.js +75 -0
- package/dist/chunk-TBBU3LDD.js +184 -0
- package/dist/chunk-URXSDJOU.js +356 -0
- package/dist/codegen/index.cjs +82 -0
- package/dist/codegen/index.d.cts +70 -0
- package/dist/codegen/index.d.ts +70 -0
- package/dist/codegen/index.js +64 -0
- package/dist/index.cjs +3412 -409
- package/dist/index.d.cts +287 -5
- package/dist/index.d.ts +287 -5
- package/dist/index.js +307 -144
- package/dist/live-preview-DLXGXRE5.js +18 -0
- package/dist/nextjs/components.cjs +1662 -388
- package/dist/nextjs/components.d.cts +1 -1
- package/dist/nextjs/components.d.ts +1 -1
- package/dist/nextjs/components.js +4 -1
- package/dist/nextjs/index.cjs +1181 -6
- package/dist/nextjs/index.d.cts +83 -3
- package/dist/nextjs/index.d.ts +83 -3
- package/dist/nextjs/index.js +1179 -7
- package/dist/nextjs/live-action.cjs +285 -0
- package/dist/nextjs/live-action.d.cts +12 -0
- package/dist/nextjs/live-action.d.ts +12 -0
- package/dist/nextjs/live-action.js +68 -0
- package/dist/nextjs/live.cjs +1899 -0
- package/dist/nextjs/live.d.cts +54 -0
- package/dist/nextjs/live.d.ts +54 -0
- package/dist/nextjs/live.js +268 -0
- package/dist/nuxt/chunk-BWPGO25E.js +340 -0
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/nuxt/live-preview-2RCU35W5.js +1189 -0
- package/dist/nuxt/live.cjs +1816 -0
- package/dist/nuxt/live.d.cts +43 -0
- package/dist/nuxt/live.d.ts +43 -0
- package/dist/nuxt/live.js +198 -0
- package/dist/{pixel-e1fUWl2Y.d.cts → pixel-CX422dCO.d.cts} +18 -3
- package/dist/{pixel-e1fUWl2Y.d.ts → pixel-CX422dCO.d.ts} +18 -3
- package/dist/react/components.cjs +1662 -388
- package/dist/react/components.d.cts +17 -2
- package/dist/react/components.d.ts +17 -2
- package/dist/react/components.js +1662 -388
- package/dist/sveltekit/chunk-BWPGO25E.js +340 -0
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/dist/sveltekit/live-preview-2RCU35W5.js +1189 -0
- package/dist/sveltekit/live.cjs +1830 -0
- package/dist/sveltekit/live.d.cts +59 -0
- package/dist/sveltekit/live.d.ts +59 -0
- package/dist/sveltekit/live.js +204 -0
- package/package.json +37 -11
- package/src/astro/PreprToolbar.astro +7 -1
package/dist/react/components.js
CHANGED
|
@@ -71,6 +71,40 @@ var ALL_FEATURES_ENABLED = Object.freeze({
|
|
|
71
71
|
editMode: true
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
// src/core/locales/de.json
|
|
75
|
+
var de_default = {
|
|
76
|
+
common: {
|
|
77
|
+
viewingAs: "Ansicht als:",
|
|
78
|
+
user: "Benutzer",
|
|
79
|
+
off: "Aus",
|
|
80
|
+
on: "An",
|
|
81
|
+
reset: "Zur\xFCcksetzen",
|
|
82
|
+
toolbar: "Symbolleiste"
|
|
83
|
+
},
|
|
84
|
+
adaptiveContent: {
|
|
85
|
+
adaptiveContent: "Adaptive Inhalte",
|
|
86
|
+
enablePreview: "Vorschaumodus",
|
|
87
|
+
segment: "Segment",
|
|
88
|
+
ABVariant: "A/B-Variante",
|
|
89
|
+
chooseSegment: "Segment w\xE4hlen",
|
|
90
|
+
searchSegments: "Segmente suchen",
|
|
91
|
+
none: "Keine Segmente",
|
|
92
|
+
allOtherUsers: "Alle anderen Benutzer",
|
|
93
|
+
offDescription: "Die Website als echter Benutzer ansehen (Cookies verwenden)",
|
|
94
|
+
onDescription: "Mit der Symbolleiste Segmente und A/B-Varianten simulieren"
|
|
95
|
+
},
|
|
96
|
+
editingTools: {
|
|
97
|
+
editingTools: "Bearbeitungswerkzeuge",
|
|
98
|
+
editMode: "Bearbeitungsmodus",
|
|
99
|
+
ariaCloseEditMode: "Bearbeitungsmodus schlie\xDFen"
|
|
100
|
+
},
|
|
101
|
+
visualEditor: {
|
|
102
|
+
edit: "Bearbeiten",
|
|
103
|
+
otherItem: "anderes Element",
|
|
104
|
+
linkedItem: "verkn\xFCpftes Element"
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
74
108
|
// src/core/locales/en.json
|
|
75
109
|
var en_default = {
|
|
76
110
|
common: {
|
|
@@ -97,6 +131,45 @@ var en_default = {
|
|
|
97
131
|
editingTools: "Editing tools",
|
|
98
132
|
editMode: "Edit mode",
|
|
99
133
|
ariaCloseEditMode: "Close edit mode"
|
|
134
|
+
},
|
|
135
|
+
visualEditor: {
|
|
136
|
+
edit: "Edit",
|
|
137
|
+
otherItem: "other item",
|
|
138
|
+
linkedItem: "linked item"
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/core/locales/fr.json
|
|
143
|
+
var fr_default = {
|
|
144
|
+
common: {
|
|
145
|
+
viewingAs: "Affichage en tant que :",
|
|
146
|
+
user: "Utilisateur",
|
|
147
|
+
off: "D\xE9sactiv\xE9",
|
|
148
|
+
on: "Activ\xE9",
|
|
149
|
+
reset: "R\xE9initialiser",
|
|
150
|
+
toolbar: "Barre d'outils"
|
|
151
|
+
},
|
|
152
|
+
adaptiveContent: {
|
|
153
|
+
adaptiveContent: "Contenu adaptatif",
|
|
154
|
+
enablePreview: "Mode aper\xE7u",
|
|
155
|
+
segment: "Segment",
|
|
156
|
+
ABVariant: "Variante A/B",
|
|
157
|
+
chooseSegment: "Choisir un segment",
|
|
158
|
+
searchSegments: "Rechercher des segments",
|
|
159
|
+
none: "Aucun segment",
|
|
160
|
+
allOtherUsers: "Tous les autres utilisateurs",
|
|
161
|
+
offDescription: "Voir le site comme un vrai utilisateur (utiliser les cookies)",
|
|
162
|
+
onDescription: "Utilisez la barre d'outils pour simuler des segments et des variantes A/B"
|
|
163
|
+
},
|
|
164
|
+
editingTools: {
|
|
165
|
+
editingTools: "Outils d'\xE9dition",
|
|
166
|
+
editMode: "Mode \xE9dition",
|
|
167
|
+
ariaCloseEditMode: "Fermer le mode \xE9dition"
|
|
168
|
+
},
|
|
169
|
+
visualEditor: {
|
|
170
|
+
edit: "Modifier",
|
|
171
|
+
otherItem: "autre \xE9l\xE9ment",
|
|
172
|
+
linkedItem: "\xE9l\xE9ment li\xE9"
|
|
100
173
|
}
|
|
101
174
|
};
|
|
102
175
|
|
|
@@ -126,13 +199,20 @@ var nl_default = {
|
|
|
126
199
|
editingTools: "Editing Tools",
|
|
127
200
|
editMode: "Edit mode",
|
|
128
201
|
ariaCloseEditMode: "Edit mode sluiten"
|
|
202
|
+
},
|
|
203
|
+
visualEditor: {
|
|
204
|
+
edit: "Bewerken",
|
|
205
|
+
otherItem: "ander item",
|
|
206
|
+
linkedItem: "gekoppeld item"
|
|
129
207
|
}
|
|
130
208
|
};
|
|
131
209
|
|
|
132
210
|
// src/core/i18n.ts
|
|
133
211
|
var dictionaries = {
|
|
134
212
|
en: en_default,
|
|
135
|
-
nl: nl_default
|
|
213
|
+
nl: nl_default,
|
|
214
|
+
fr: fr_default,
|
|
215
|
+
de: de_default
|
|
136
216
|
};
|
|
137
217
|
var LOCALES = Object.keys(dictionaries);
|
|
138
218
|
function isLocale(value) {
|
|
@@ -225,51 +305,53 @@ function createScopedLogger(scopeName) {
|
|
|
225
305
|
prefix: `[Prepr][${scopeName}]`
|
|
226
306
|
});
|
|
227
307
|
}
|
|
228
|
-
function throttle(func, delay) {
|
|
229
|
-
let timeoutId = null;
|
|
230
|
-
let lastExecTime = 0;
|
|
231
|
-
const throttledFunc = ((...args) => {
|
|
232
|
-
const currentTime = Date.now();
|
|
233
|
-
const timeSinceLastExec = currentTime - lastExecTime;
|
|
234
|
-
if (timeSinceLastExec >= delay) {
|
|
235
|
-
func(...args);
|
|
236
|
-
lastExecTime = currentTime;
|
|
237
|
-
} else {
|
|
238
|
-
if (timeoutId) {
|
|
239
|
-
clearTimeout(timeoutId);
|
|
240
|
-
}
|
|
241
|
-
timeoutId = setTimeout(() => {
|
|
242
|
-
func(...args);
|
|
243
|
-
lastExecTime = Date.now();
|
|
244
|
-
timeoutId = null;
|
|
245
|
-
}, delay - timeSinceLastExec);
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
throttledFunc.cancel = () => {
|
|
249
|
-
if (timeoutId) {
|
|
250
|
-
clearTimeout(timeoutId);
|
|
251
|
-
timeoutId = null;
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
return throttledFunc;
|
|
255
|
-
}
|
|
256
|
-
function createElementCache(query, ttl = 1e3) {
|
|
257
|
-
let cache = null;
|
|
258
|
-
let lastCacheTime = 0;
|
|
259
|
-
return () => {
|
|
260
|
-
const now = Date.now();
|
|
261
|
-
if (!cache || now - lastCacheTime > ttl) {
|
|
262
|
-
cache = document.querySelectorAll(query);
|
|
263
|
-
lastCacheTime = now;
|
|
264
|
-
}
|
|
265
|
-
return cache;
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
308
|
|
|
269
309
|
// src/core/iframe-bridge.ts
|
|
310
|
+
var debug = createScopedLogger("iframe-bridge");
|
|
311
|
+
var MAX_REFERENCES = 500;
|
|
312
|
+
var MAX_HREF_LENGTH = 8192;
|
|
313
|
+
var MAX_ASSETS = 500;
|
|
314
|
+
var MAX_ASSET_KEY_LENGTH = 2048;
|
|
315
|
+
var MAX_ASSET_FIELD_LENGTH = 512;
|
|
316
|
+
function parseIds(value) {
|
|
317
|
+
if (!Array.isArray(value)) return void 0;
|
|
318
|
+
return value.slice(0, MAX_REFERENCES).filter((id) => typeof id === "string" && id.length > 0);
|
|
319
|
+
}
|
|
320
|
+
function parseReferences(value) {
|
|
321
|
+
if (!Array.isArray(value)) return void 0;
|
|
322
|
+
const list = [];
|
|
323
|
+
for (const entry of value.slice(0, MAX_REFERENCES)) {
|
|
324
|
+
const id = entry?.id;
|
|
325
|
+
const field = entry?.field;
|
|
326
|
+
if (typeof id === "string" && id && typeof field === "string" && field) {
|
|
327
|
+
list.push({ id, field });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return list;
|
|
331
|
+
}
|
|
332
|
+
function boundedString(value, max) {
|
|
333
|
+
return typeof value === "string" && value && value.length <= max ? value : void 0;
|
|
334
|
+
}
|
|
335
|
+
function parseAssets(value) {
|
|
336
|
+
if (!Array.isArray(value)) return void 0;
|
|
337
|
+
const list = [];
|
|
338
|
+
for (const entry of value.slice(0, MAX_ASSETS)) {
|
|
339
|
+
if (!entry || typeof entry !== "object") continue;
|
|
340
|
+
const raw = entry;
|
|
341
|
+
const key = boundedString(raw.key, MAX_ASSET_KEY_LENGTH);
|
|
342
|
+
const field = boundedString(raw.field, MAX_ASSET_FIELD_LENGTH);
|
|
343
|
+
if (!key || !field) continue;
|
|
344
|
+
const entryId = boundedString(raw.entryId, MAX_ASSET_FIELD_LENGTH);
|
|
345
|
+
list.push(entryId ? { key, field, entryId } : { key, field });
|
|
346
|
+
}
|
|
347
|
+
return list;
|
|
348
|
+
}
|
|
270
349
|
var EDITOR_BASE_DOMAIN = "prepr.io";
|
|
271
350
|
function isAllowedEditorOrigin(origin, allowed) {
|
|
272
|
-
if (allowed)
|
|
351
|
+
if (allowed) {
|
|
352
|
+
if (allowed.includes("*")) return origin !== "null";
|
|
353
|
+
return allowed.includes(origin);
|
|
354
|
+
}
|
|
273
355
|
let url;
|
|
274
356
|
try {
|
|
275
357
|
url = new URL(origin);
|
|
@@ -284,6 +366,11 @@ function isAllowedEditorOrigin(origin, allowed) {
|
|
|
284
366
|
}
|
|
285
367
|
function createIframeBridge(store, options = {}) {
|
|
286
368
|
const allowedOrigins = options.allowedEditorOrigins;
|
|
369
|
+
const onScrollToField = options.onScrollToField;
|
|
370
|
+
const onHandshake = options.onHandshake;
|
|
371
|
+
const onReferences = options.onReferences;
|
|
372
|
+
const onAssets = options.onAssets;
|
|
373
|
+
const onAllowNavigation = options.onAllowNavigation;
|
|
287
374
|
let parentOrigin = null;
|
|
288
375
|
const onKeyDown = (event) => {
|
|
289
376
|
const key = event.key.toLowerCase();
|
|
@@ -293,7 +380,10 @@ function createIframeBridge(store, options = {}) {
|
|
|
293
380
|
const onMessage = (evt) => {
|
|
294
381
|
const data = evt?.data;
|
|
295
382
|
if (data?.event === "prepr:initVE" && !parentOrigin) {
|
|
296
|
-
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins))
|
|
383
|
+
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins)) {
|
|
384
|
+
debug.warn("ignored editor handshake from origin", evt.origin);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
297
387
|
parentOrigin = evt.origin;
|
|
298
388
|
setTrustedParentOrigin(parentOrigin);
|
|
299
389
|
if (data.scrollPosition != null) {
|
|
@@ -301,12 +391,76 @@ function createIframeBridge(store, options = {}) {
|
|
|
301
391
|
setTimeout(() => window.scrollTo(0, top), 1);
|
|
302
392
|
}
|
|
303
393
|
store?.set({ previewMode: true, editMode: data.editMode ?? true });
|
|
394
|
+
const itemId = typeof data.itemId === "string" && data.itemId ? data.itemId : void 0;
|
|
395
|
+
const referencedIds = parseReferences(data.referencedIds);
|
|
396
|
+
const componentIds = parseIds(data.componentIds);
|
|
397
|
+
const uiLocale = isLocale(data.uiLocale) ? data.uiLocale : void 0;
|
|
398
|
+
const navigationGuard = data.navigationGuard === true;
|
|
399
|
+
const assets = parseAssets(data.assets);
|
|
400
|
+
try {
|
|
401
|
+
onHandshake?.({
|
|
402
|
+
...itemId && { itemId },
|
|
403
|
+
...referencedIds && { referencedIds },
|
|
404
|
+
...componentIds && { componentIds },
|
|
405
|
+
...uiLocale && { uiLocale },
|
|
406
|
+
...navigationGuard && { navigationGuard },
|
|
407
|
+
...assets && { assets }
|
|
408
|
+
});
|
|
409
|
+
} catch (error) {
|
|
410
|
+
debug.warn("handshake handler failed", error);
|
|
411
|
+
}
|
|
304
412
|
}
|
|
305
413
|
if (!parentOrigin || evt.origin !== parentOrigin) return;
|
|
414
|
+
if (data?.event === "prepr:references") {
|
|
415
|
+
const assets = parseAssets(data.assets);
|
|
416
|
+
if (assets) {
|
|
417
|
+
try {
|
|
418
|
+
onAssets?.(assets);
|
|
419
|
+
} catch (error) {
|
|
420
|
+
debug.warn("assets handler failed", error);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const referencedIds = parseReferences(data.referencedIds);
|
|
424
|
+
if (!referencedIds) return;
|
|
425
|
+
try {
|
|
426
|
+
onReferences?.(referencedIds, parseIds(data.componentIds));
|
|
427
|
+
} catch (error) {
|
|
428
|
+
debug.warn("references handler failed", error);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
306
431
|
if (data?.event === "prepr:getScrollPosition") {
|
|
307
432
|
const currentScrollY = window.scrollY || document.documentElement.scrollTop;
|
|
308
433
|
sendPreprEvent("getScrollPosition", { value: currentScrollY });
|
|
309
434
|
}
|
|
435
|
+
if (data?.event === "prepr:scrollToField") {
|
|
436
|
+
if (typeof data.field !== "string" || !data.field) return;
|
|
437
|
+
if (data.id !== void 0 && typeof data.id !== "string") return;
|
|
438
|
+
const block = typeof data.block === "number" && Number.isInteger(data.block) ? data.block : void 0;
|
|
439
|
+
const entryId = typeof data.entryId === "string" && data.entryId ? data.entryId : void 0;
|
|
440
|
+
const fieldById = typeof data.fieldById === "string" && data.fieldById ? data.fieldById : void 0;
|
|
441
|
+
try {
|
|
442
|
+
onScrollToField?.({
|
|
443
|
+
field: data.field,
|
|
444
|
+
id: data.id,
|
|
445
|
+
block,
|
|
446
|
+
entryId,
|
|
447
|
+
fieldById
|
|
448
|
+
});
|
|
449
|
+
} catch (error) {
|
|
450
|
+
debug.warn("scrollToField handler failed", error);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (data?.event === "prepr:allowNavigation") {
|
|
454
|
+
const href = data.href;
|
|
455
|
+
if (typeof href !== "string" || !href || href.length > MAX_HREF_LENGTH) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
onAllowNavigation?.(href);
|
|
460
|
+
} catch (error) {
|
|
461
|
+
debug.warn("allowNavigation handler failed", error);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
310
464
|
};
|
|
311
465
|
return {
|
|
312
466
|
start() {
|
|
@@ -331,7 +485,7 @@ import { vercelStegaSplit } from "@vercel/stega";
|
|
|
331
485
|
|
|
332
486
|
// src/core/stega/clean.ts
|
|
333
487
|
import { vercelStegaClean, vercelStegaDecode } from "@vercel/stega";
|
|
334
|
-
var
|
|
488
|
+
var debug2 = createScopedLogger("stega:clean");
|
|
335
489
|
function decodeStega(str) {
|
|
336
490
|
if (!str) return null;
|
|
337
491
|
try {
|
|
@@ -340,7 +494,7 @@ function decodeStega(str) {
|
|
|
340
494
|
return decoded;
|
|
341
495
|
}
|
|
342
496
|
} catch (error) {
|
|
343
|
-
|
|
497
|
+
debug2.log("error decoding stega data", error);
|
|
344
498
|
const match = str.match(/{"origin.*?}/);
|
|
345
499
|
if (match) {
|
|
346
500
|
try {
|
|
@@ -349,7 +503,7 @@ function decodeStega(str) {
|
|
|
349
503
|
return decodedMatch;
|
|
350
504
|
}
|
|
351
505
|
} catch (innerError) {
|
|
352
|
-
|
|
506
|
+
debug2.log("error decoding stega regex match", innerError);
|
|
353
507
|
}
|
|
354
508
|
}
|
|
355
509
|
}
|
|
@@ -358,6 +512,7 @@ function decodeStega(str) {
|
|
|
358
512
|
|
|
359
513
|
// src/core/stega/dom.ts
|
|
360
514
|
var IGNORED_ANCESTORS = "script, style, noscript";
|
|
515
|
+
var AUTO_ENTRY_ATTR = "data-prepr-entry-auto";
|
|
361
516
|
function isIgnoredTextNode(node) {
|
|
362
517
|
if (node.parentElement?.closest(IGNORED_ANCESTORS)) return true;
|
|
363
518
|
return !node.textContent?.trim();
|
|
@@ -385,42 +540,90 @@ function isRenderedElement(element) {
|
|
|
385
540
|
if (!style) return true;
|
|
386
541
|
return style.visibility !== "hidden" && style.display !== "none";
|
|
387
542
|
}
|
|
543
|
+
function parseEditHref(href) {
|
|
544
|
+
try {
|
|
545
|
+
const url = new URL(href, window.location.origin);
|
|
546
|
+
return {
|
|
547
|
+
id: url.pathname.split("/").pop() || void 0,
|
|
548
|
+
field: url.searchParams.get("field") || void 0,
|
|
549
|
+
locale: url.searchParams.get("locale") || void 0,
|
|
550
|
+
// Added by the toolkit's `annotateEditLinks`, not by Prepr.
|
|
551
|
+
entry: url.searchParams.get("entry") || void 0
|
|
552
|
+
};
|
|
553
|
+
} catch {
|
|
554
|
+
return {};
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function setOrRemove(element, name, value) {
|
|
558
|
+
if (value) {
|
|
559
|
+
element.setAttribute(name, value);
|
|
560
|
+
} else {
|
|
561
|
+
element.removeAttribute(name);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
388
564
|
function tagEncodedElement(element, decoded) {
|
|
565
|
+
if (isSiteTagged(element)) return false;
|
|
389
566
|
element.setAttribute("data-prepr-encoded", "");
|
|
390
567
|
element.setAttribute("data-prepr-href", decoded.href);
|
|
391
568
|
element.setAttribute("data-prepr-origin", decoded.origin);
|
|
569
|
+
const { id, field, locale, entry } = parseEditHref(decoded.href);
|
|
570
|
+
setOrRemove(element, "data-prepr-id", id);
|
|
571
|
+
const fieldHint = element.getAttribute("data-prepr-edit-target")?.trim() || void 0;
|
|
572
|
+
setOrRemove(element, "data-prepr-field", field ?? fieldHint);
|
|
573
|
+
setOrRemove(element, "data-prepr-locale", locale);
|
|
574
|
+
const siteEntry = element.hasAttribute("data-prepr-entry-id") && !element.hasAttribute(AUTO_ENTRY_ATTR);
|
|
575
|
+
if (!siteEntry) {
|
|
576
|
+
setOrRemove(element, "data-prepr-entry-id", entry);
|
|
577
|
+
setOrRemove(element, AUTO_ENTRY_ATTR, entry && "true");
|
|
578
|
+
}
|
|
579
|
+
return true;
|
|
580
|
+
}
|
|
581
|
+
function isSiteTagged(element) {
|
|
582
|
+
return element.hasAttribute("data-prepr-encoded") && !element.hasAttribute("data-prepr-href");
|
|
583
|
+
}
|
|
584
|
+
function readEditPayload(element) {
|
|
585
|
+
return {
|
|
586
|
+
href: element.getAttribute("data-prepr-href") ?? "",
|
|
587
|
+
origin: element.getAttribute("data-prepr-origin") ?? "",
|
|
588
|
+
id: element.getAttribute("data-prepr-id") ?? void 0,
|
|
589
|
+
field: element.getAttribute("data-prepr-field") ?? void 0,
|
|
590
|
+
locale: element.getAttribute("data-prepr-locale") ?? void 0,
|
|
591
|
+
entryId: element.getAttribute("data-prepr-entry-id") || void 0
|
|
592
|
+
};
|
|
392
593
|
}
|
|
393
594
|
|
|
394
595
|
// src/core/stega/auto-clean.ts
|
|
395
|
-
var
|
|
596
|
+
var debug3 = createScopedLogger("stega:auto-clean");
|
|
597
|
+
var cleanedNodes = /* @__PURE__ */ new WeakMap();
|
|
396
598
|
function createStegaAutoClean(options) {
|
|
397
599
|
const stripText = options?.enabled ?? true;
|
|
398
600
|
let observer = null;
|
|
399
601
|
let processing = false;
|
|
400
|
-
let cleaned = /* @__PURE__ */ new WeakMap();
|
|
401
602
|
let pendingHidden = /* @__PURE__ */ new Set();
|
|
402
603
|
let pendingDirty = /* @__PURE__ */ new Set();
|
|
403
604
|
let debounceTimeout = null;
|
|
404
605
|
function cleanTextNode(textNode) {
|
|
405
606
|
if (isIgnoredTextNode(textNode)) return false;
|
|
406
607
|
const textContent = textNode.textContent;
|
|
407
|
-
const info =
|
|
608
|
+
const info = cleanedNodes.get(textNode);
|
|
408
609
|
const settled = stripText ? info?.cleaned : info?.original;
|
|
409
610
|
if (info && textContent === settled) {
|
|
410
611
|
if (!info.decoded) return false;
|
|
411
612
|
const shown = resolveEditTarget(textNode);
|
|
412
|
-
if (!shown)
|
|
613
|
+
if (!shown) {
|
|
614
|
+
pendingHidden.add(textNode);
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
413
617
|
pendingHidden.delete(textNode);
|
|
414
618
|
if (shown.hasAttribute("data-prepr-encoded")) return false;
|
|
415
|
-
tagEncodedElement(shown, info.decoded);
|
|
416
|
-
return true;
|
|
619
|
+
return tagEncodedElement(shown, info.decoded);
|
|
417
620
|
}
|
|
418
621
|
try {
|
|
419
622
|
const { cleaned: stripped, encoded } = vercelStegaSplit(textContent);
|
|
420
623
|
if (!encoded || stripped === textContent) return false;
|
|
421
624
|
const decoded = decodeStega(textContent);
|
|
422
625
|
if (!decoded?.href && !stripText) return false;
|
|
423
|
-
|
|
626
|
+
cleanedNodes.set(textNode, {
|
|
424
627
|
original: textContent,
|
|
425
628
|
cleaned: stripped,
|
|
426
629
|
decoded: decoded?.href ? decoded : null
|
|
@@ -430,12 +633,11 @@ function createStegaAutoClean(options) {
|
|
|
430
633
|
const target = resolveEditTarget(textNode);
|
|
431
634
|
if (target) {
|
|
432
635
|
pendingHidden.delete(textNode);
|
|
433
|
-
tagEncodedElement(target, decoded);
|
|
434
|
-
return true;
|
|
636
|
+
return tagEncodedElement(target, decoded);
|
|
435
637
|
}
|
|
436
638
|
pendingHidden.add(textNode);
|
|
437
639
|
} catch (error) {
|
|
438
|
-
|
|
640
|
+
debug3.log("error cleaning text node", error);
|
|
439
641
|
}
|
|
440
642
|
return false;
|
|
441
643
|
}
|
|
@@ -462,7 +664,7 @@ function createStegaAutoClean(options) {
|
|
|
462
664
|
}
|
|
463
665
|
if (mutation.type === "characterData" && mutation.target.nodeType === Node.TEXT_NODE) {
|
|
464
666
|
const textNode = mutation.target;
|
|
465
|
-
const info =
|
|
667
|
+
const info = cleanedNodes.get(textNode);
|
|
466
668
|
const text = textNode.textContent ?? "";
|
|
467
669
|
const settled = stripText ? info?.cleaned : info?.original;
|
|
468
670
|
if (text !== settled && vercelStegaSplit(text).encoded) {
|
|
@@ -505,7 +707,16 @@ function createStegaAutoClean(options) {
|
|
|
505
707
|
retryPendingHidden();
|
|
506
708
|
}, 50);
|
|
507
709
|
}
|
|
710
|
+
let resizeTimeout = null;
|
|
711
|
+
function handleResize() {
|
|
712
|
+
if (pendingHidden.size === 0 || resizeTimeout) return;
|
|
713
|
+
resizeTimeout = setTimeout(() => {
|
|
714
|
+
resizeTimeout = null;
|
|
715
|
+
retryPendingHidden();
|
|
716
|
+
}, 100);
|
|
717
|
+
}
|
|
508
718
|
function setupObserver() {
|
|
719
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
509
720
|
observer = new MutationObserver(handleMutations);
|
|
510
721
|
observer.observe(document.body, {
|
|
511
722
|
childList: true,
|
|
@@ -521,27 +732,464 @@ function createStegaAutoClean(options) {
|
|
|
521
732
|
if (observer || typeof window === "undefined") return;
|
|
522
733
|
scanAndClean();
|
|
523
734
|
setupObserver();
|
|
524
|
-
|
|
735
|
+
debug3.log("stega auto-clean started");
|
|
525
736
|
}
|
|
526
737
|
function stop() {
|
|
527
738
|
if (debounceTimeout) {
|
|
528
739
|
clearTimeout(debounceTimeout);
|
|
529
740
|
debounceTimeout = null;
|
|
530
741
|
}
|
|
742
|
+
if (resizeTimeout) {
|
|
743
|
+
clearTimeout(resizeTimeout);
|
|
744
|
+
resizeTimeout = null;
|
|
745
|
+
}
|
|
531
746
|
if (observer) {
|
|
532
747
|
observer.disconnect();
|
|
533
748
|
observer = null;
|
|
749
|
+
window.removeEventListener("resize", handleResize);
|
|
534
750
|
}
|
|
535
|
-
cleaned = /* @__PURE__ */ new WeakMap();
|
|
536
751
|
pendingHidden = /* @__PURE__ */ new Set();
|
|
537
752
|
pendingDirty = /* @__PURE__ */ new Set();
|
|
538
|
-
|
|
753
|
+
debug3.log("stega auto-clean stopped");
|
|
539
754
|
}
|
|
540
755
|
return { start, stop };
|
|
541
756
|
}
|
|
542
757
|
|
|
758
|
+
// src/core/visual-editor/overlay.ts
|
|
759
|
+
var HOST_TAG = "prepr-visual-editor";
|
|
760
|
+
var SCROLL_SETTLE_MS = 120;
|
|
761
|
+
var LABEL_SPACE_PX = 22;
|
|
762
|
+
var BOX_PADDING_PX = 3;
|
|
763
|
+
var LABEL_MARGIN_PX = 4;
|
|
764
|
+
var PROBE_POINTS = [
|
|
765
|
+
[0.5, 0.5],
|
|
766
|
+
[0.15, 0.2],
|
|
767
|
+
[0.85, 0.2],
|
|
768
|
+
[0.15, 0.8],
|
|
769
|
+
[0.85, 0.8]
|
|
770
|
+
];
|
|
771
|
+
function overlapsBox(area, rect) {
|
|
772
|
+
return area.left < rect.right + BOX_PADDING_PX && area.right > rect.left - BOX_PADDING_PX && area.top < rect.bottom + BOX_PADDING_PX && area.bottom > rect.top - BOX_PADDING_PX;
|
|
773
|
+
}
|
|
774
|
+
function overlaps(a2, b2) {
|
|
775
|
+
return a2.left < b2.right && b2.left < a2.right && a2.top < b2.bottom && b2.top < a2.bottom;
|
|
776
|
+
}
|
|
777
|
+
function coverOf(hit, target, x2, y2) {
|
|
778
|
+
let cover = hit.getBoundingClientRect();
|
|
779
|
+
for (let el = hit.parentElement; el && !el.contains(target) && el !== document.body; el = el.parentElement) {
|
|
780
|
+
const r2 = el.getBoundingClientRect();
|
|
781
|
+
if (x2 < r2.left || x2 >= r2.right || y2 < r2.top || y2 >= r2.bottom) break;
|
|
782
|
+
cover = r2;
|
|
783
|
+
}
|
|
784
|
+
return cover;
|
|
785
|
+
}
|
|
786
|
+
function mergeAreas(areas) {
|
|
787
|
+
const merged = areas.slice();
|
|
788
|
+
for (let i2 = 0; i2 < merged.length; i2++) {
|
|
789
|
+
for (let j2 = i2 + 1; j2 < merged.length; j2++) {
|
|
790
|
+
if (!overlaps(merged[i2], merged[j2])) continue;
|
|
791
|
+
merged[i2] = {
|
|
792
|
+
left: Math.min(merged[i2].left, merged[j2].left),
|
|
793
|
+
top: Math.min(merged[i2].top, merged[j2].top),
|
|
794
|
+
right: Math.max(merged[i2].right, merged[j2].right),
|
|
795
|
+
bottom: Math.max(merged[i2].bottom, merged[j2].bottom)
|
|
796
|
+
};
|
|
797
|
+
merged.splice(j2, 1);
|
|
798
|
+
j2 = i2;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
return merged;
|
|
802
|
+
}
|
|
803
|
+
function findCovers(target, rect) {
|
|
804
|
+
const width = window.innerWidth;
|
|
805
|
+
const height = window.innerHeight;
|
|
806
|
+
const covers = [];
|
|
807
|
+
const seen = /* @__PURE__ */ new Set();
|
|
808
|
+
let probed = false;
|
|
809
|
+
let reached = false;
|
|
810
|
+
for (const [fx, fy] of PROBE_POINTS) {
|
|
811
|
+
const x2 = rect.left + rect.width * fx;
|
|
812
|
+
const y2 = rect.top + rect.height * fy;
|
|
813
|
+
if (x2 < 0 || y2 < 0 || x2 >= width || y2 >= height) continue;
|
|
814
|
+
probed = true;
|
|
815
|
+
const hit = document.elementFromPoint(x2, y2);
|
|
816
|
+
if (!hit || hit === target || target.contains(hit)) {
|
|
817
|
+
reached = true;
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
if (seen.has(hit)) continue;
|
|
821
|
+
seen.add(hit);
|
|
822
|
+
if (covers.some(
|
|
823
|
+
(c2) => x2 >= c2.left && x2 < c2.right && y2 >= c2.top && y2 < c2.bottom
|
|
824
|
+
)) {
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
covers.push(coverOf(hit, target, x2, y2));
|
|
828
|
+
}
|
|
829
|
+
if (!probed) return [];
|
|
830
|
+
return reached ? mergeAreas(covers) : null;
|
|
831
|
+
}
|
|
832
|
+
function clipPath(x2, y2, covers) {
|
|
833
|
+
const far = 1e5;
|
|
834
|
+
const points = [
|
|
835
|
+
`${-far}px ${-far}px`,
|
|
836
|
+
`${far}px ${-far}px`,
|
|
837
|
+
`${far}px ${far}px`,
|
|
838
|
+
`${-far}px ${far}px`,
|
|
839
|
+
`${-far}px ${-far}px`
|
|
840
|
+
];
|
|
841
|
+
for (const c2 of covers) {
|
|
842
|
+
const l2 = c2.left - x2;
|
|
843
|
+
const t3 = c2.top - y2;
|
|
844
|
+
const r2 = c2.right - x2;
|
|
845
|
+
const b2 = c2.bottom - y2;
|
|
846
|
+
points.push(
|
|
847
|
+
`${l2}px ${t3}px`,
|
|
848
|
+
`${r2}px ${t3}px`,
|
|
849
|
+
`${r2}px ${b2}px`,
|
|
850
|
+
`${l2}px ${b2}px`,
|
|
851
|
+
`${l2}px ${t3}px`,
|
|
852
|
+
`${-far}px ${-far}px`
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
return `polygon(evenodd, ${points.join(", ")})`;
|
|
856
|
+
}
|
|
857
|
+
var STYLES = `
|
|
858
|
+
:host {
|
|
859
|
+
all: initial;
|
|
860
|
+
position: fixed;
|
|
861
|
+
inset: 0;
|
|
862
|
+
pointer-events: none;
|
|
863
|
+
z-index: 2147483646;
|
|
864
|
+
contain: strict;
|
|
865
|
+
}
|
|
866
|
+
.box {
|
|
867
|
+
position: absolute;
|
|
868
|
+
top: 0;
|
|
869
|
+
left: 0;
|
|
870
|
+
box-sizing: border-box;
|
|
871
|
+
border-radius: 6px;
|
|
872
|
+
opacity: 0;
|
|
873
|
+
will-change: transform, width, height;
|
|
874
|
+
transition:
|
|
875
|
+
transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
876
|
+
width 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
877
|
+
height 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
878
|
+
opacity 140ms ease;
|
|
879
|
+
}
|
|
880
|
+
.box[data-visible] { opacity: 1; }
|
|
881
|
+
.box[data-instant], :host([data-scrolling]) .box {
|
|
882
|
+
transition: opacity 140ms ease;
|
|
883
|
+
}
|
|
884
|
+
.hover {
|
|
885
|
+
border: 1.5px solid rgba(79, 70, 229, 0.9);
|
|
886
|
+
background: rgba(79, 70, 229, 0.06);
|
|
887
|
+
}
|
|
888
|
+
.selected {
|
|
889
|
+
border: 2px solid #4f46e5;
|
|
890
|
+
box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.18);
|
|
891
|
+
}
|
|
892
|
+
.label {
|
|
893
|
+
position: absolute;
|
|
894
|
+
left: -2px;
|
|
895
|
+
bottom: 100%;
|
|
896
|
+
margin-bottom: 4px;
|
|
897
|
+
padding: 3px 7px;
|
|
898
|
+
border-radius: 5px;
|
|
899
|
+
background: #4f46e5;
|
|
900
|
+
color: #fff;
|
|
901
|
+
font: 500 11px/1.3 ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
902
|
+
letter-spacing: 0.01em;
|
|
903
|
+
white-space: nowrap;
|
|
904
|
+
display: flex;
|
|
905
|
+
max-width: 240px;
|
|
906
|
+
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.25);
|
|
907
|
+
}
|
|
908
|
+
.text {
|
|
909
|
+
min-width: 0;
|
|
910
|
+
overflow: hidden;
|
|
911
|
+
text-overflow: ellipsis;
|
|
912
|
+
}
|
|
913
|
+
.hover .label { background: rgba(79, 70, 229, 0.92); }
|
|
914
|
+
/* Content of another item than the one open in the editor: still clickable,
|
|
915
|
+
but visibly not part of the item being edited. */
|
|
916
|
+
.hover[data-item-kind='other'] {
|
|
917
|
+
border: 1.5px dashed #a5b4fc;
|
|
918
|
+
background: rgba(100, 116, 139, 0.05);
|
|
919
|
+
}
|
|
920
|
+
.hover[data-item-kind='other'] .label { background: #64748b; }
|
|
921
|
+
.hover[data-item-kind='referenced'] {
|
|
922
|
+
border: 1.5px solid #a5b4fc;
|
|
923
|
+
background: rgba(79, 70, 229, 0.03);
|
|
924
|
+
}
|
|
925
|
+
.hover[data-item-kind='referenced'] .label { background: #818cf8; }
|
|
926
|
+
.box[data-label-covered] .label { visibility: hidden; }
|
|
927
|
+
.box[data-label-inside] .label {
|
|
928
|
+
bottom: auto;
|
|
929
|
+
top: 2px;
|
|
930
|
+
left: 2px;
|
|
931
|
+
margin: 0;
|
|
932
|
+
}
|
|
933
|
+
/* A clickable hover label (edit mode on the live site). Only while shown, so
|
|
934
|
+
a fading box never leaves an invisible click target behind. */
|
|
935
|
+
:host([data-label-action]) .hover[data-visible] .label {
|
|
936
|
+
pointer-events: auto;
|
|
937
|
+
cursor: pointer;
|
|
938
|
+
}
|
|
939
|
+
:host([data-label-action]) .hover .label::after {
|
|
940
|
+
content: '\\2197';
|
|
941
|
+
margin-left: 5px;
|
|
942
|
+
}
|
|
943
|
+
:host([data-label-action]) .hover .label:hover { background: #4338ca; }
|
|
944
|
+
/* Bridges the gap between the label and its element, so the pointer can
|
|
945
|
+
travel from one to the other without the hover ending on the way. */
|
|
946
|
+
:host([data-label-action]) .hover .label::before {
|
|
947
|
+
content: '';
|
|
948
|
+
position: absolute;
|
|
949
|
+
left: 0;
|
|
950
|
+
right: 0;
|
|
951
|
+
top: 100%;
|
|
952
|
+
height: ${LABEL_MARGIN_PX + BOX_PADDING_PX + 2}px;
|
|
953
|
+
}
|
|
954
|
+
@media (prefers-reduced-motion: reduce) {
|
|
955
|
+
.box { transition: opacity 80ms linear; }
|
|
956
|
+
}
|
|
957
|
+
`;
|
|
958
|
+
var VisualEditorOverlay = class {
|
|
959
|
+
constructor(options = {}) {
|
|
960
|
+
this.host = null;
|
|
961
|
+
this.hoverBox = null;
|
|
962
|
+
this.selectedBox = null;
|
|
963
|
+
this.frame = null;
|
|
964
|
+
this.scrollTimeout = null;
|
|
965
|
+
this.resizeObserver = null;
|
|
966
|
+
this.onLabelClick = options.onLabelClick;
|
|
967
|
+
}
|
|
968
|
+
mount() {
|
|
969
|
+
if (this.host) return;
|
|
970
|
+
const host = document.createElement(HOST_TAG);
|
|
971
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
972
|
+
const style = document.createElement("style");
|
|
973
|
+
style.textContent = STYLES;
|
|
974
|
+
shadow.appendChild(style);
|
|
975
|
+
this.hoverBox = this.createBox(shadow, "hover");
|
|
976
|
+
this.selectedBox = this.createBox(shadow, "selected");
|
|
977
|
+
const onLabelClick = this.onLabelClick;
|
|
978
|
+
if (onLabelClick) {
|
|
979
|
+
host.setAttribute("data-label-action", "");
|
|
980
|
+
const hoverBox = this.hoverBox;
|
|
981
|
+
hoverBox.label.addEventListener("click", () => {
|
|
982
|
+
if (hoverBox.target) onLabelClick(hoverBox.target);
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
document.documentElement.appendChild(host);
|
|
986
|
+
this.host = host;
|
|
987
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
988
|
+
this.resizeObserver = new ResizeObserver(() => this.schedule());
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
unmount() {
|
|
992
|
+
if (this.frame !== null) cancelAnimationFrame(this.frame);
|
|
993
|
+
if (this.scrollTimeout) clearTimeout(this.scrollTimeout);
|
|
994
|
+
this.frame = null;
|
|
995
|
+
this.scrollTimeout = null;
|
|
996
|
+
this.resizeObserver?.disconnect();
|
|
997
|
+
this.resizeObserver = null;
|
|
998
|
+
this.host?.remove();
|
|
999
|
+
this.host = null;
|
|
1000
|
+
this.hoverBox = null;
|
|
1001
|
+
this.selectedBox = null;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Whether a pointer event target is this chrome: events from inside the
|
|
1005
|
+
* shadow root reach the page retargeted to its host.
|
|
1006
|
+
*/
|
|
1007
|
+
owns(node) {
|
|
1008
|
+
return node !== null && node === this.host;
|
|
1009
|
+
}
|
|
1010
|
+
getHoverTarget() {
|
|
1011
|
+
return this.hoverBox?.target ?? null;
|
|
1012
|
+
}
|
|
1013
|
+
getSelectedTarget() {
|
|
1014
|
+
return this.selectedBox?.target ?? null;
|
|
1015
|
+
}
|
|
1016
|
+
setHover(target, label, options = {}) {
|
|
1017
|
+
if (target) {
|
|
1018
|
+
this.hoverBox?.root.setAttribute(
|
|
1019
|
+
"data-item-kind",
|
|
1020
|
+
options.kind ?? "current"
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
this.retarget(this.hoverBox, target, label);
|
|
1024
|
+
}
|
|
1025
|
+
setSelected(target, label) {
|
|
1026
|
+
this.retarget(this.selectedBox, target, label);
|
|
1027
|
+
}
|
|
1028
|
+
/** Call on every scroll; positions follow in the next frame, untransitioned. */
|
|
1029
|
+
onScroll() {
|
|
1030
|
+
if (!this.host) return;
|
|
1031
|
+
this.host.setAttribute("data-scrolling", "");
|
|
1032
|
+
if (this.scrollTimeout) clearTimeout(this.scrollTimeout);
|
|
1033
|
+
this.scrollTimeout = setTimeout(() => {
|
|
1034
|
+
this.host?.removeAttribute("data-scrolling");
|
|
1035
|
+
this.scrollTimeout = null;
|
|
1036
|
+
}, SCROLL_SETTLE_MS);
|
|
1037
|
+
this.schedule();
|
|
1038
|
+
}
|
|
1039
|
+
/** Re-measure both boxes in the next animation frame. */
|
|
1040
|
+
schedule() {
|
|
1041
|
+
if (this.frame !== null || !this.host) return;
|
|
1042
|
+
this.frame = requestAnimationFrame(() => {
|
|
1043
|
+
this.frame = null;
|
|
1044
|
+
this.render();
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
createBox(shadow, kind) {
|
|
1048
|
+
const root = document.createElement("div");
|
|
1049
|
+
root.className = `box ${kind}`;
|
|
1050
|
+
const label = document.createElement("div");
|
|
1051
|
+
label.className = "label";
|
|
1052
|
+
const text = document.createElement("span");
|
|
1053
|
+
text.className = "text";
|
|
1054
|
+
label.appendChild(text);
|
|
1055
|
+
root.appendChild(label);
|
|
1056
|
+
shadow.appendChild(root);
|
|
1057
|
+
return { root, label, text, target: null };
|
|
1058
|
+
}
|
|
1059
|
+
retarget(box, target, label) {
|
|
1060
|
+
if (!box || box.target === target) return;
|
|
1061
|
+
const previous = box.target;
|
|
1062
|
+
if (previous) this.resizeObserver?.unobserve(previous);
|
|
1063
|
+
box.target = target;
|
|
1064
|
+
if (target) {
|
|
1065
|
+
this.resizeObserver?.observe(target);
|
|
1066
|
+
box.text.textContent = label;
|
|
1067
|
+
if (!box.root.hasAttribute("data-visible")) {
|
|
1068
|
+
box.root.setAttribute("data-instant", "");
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
this.schedule();
|
|
1072
|
+
}
|
|
1073
|
+
render() {
|
|
1074
|
+
const hover = this.hoverBox ? this.measure(this.hoverBox) : null;
|
|
1075
|
+
const selected = this.selectedBox ? this.measure(this.selectedBox) : null;
|
|
1076
|
+
if (this.hoverBox) this.paint(this.hoverBox, hover, selected);
|
|
1077
|
+
if (this.selectedBox) this.paint(this.selectedBox, selected, hover);
|
|
1078
|
+
}
|
|
1079
|
+
measure(box) {
|
|
1080
|
+
const target = box.target;
|
|
1081
|
+
if (!target) return null;
|
|
1082
|
+
if (!target.isConnected) {
|
|
1083
|
+
this.resizeObserver?.unobserve(target);
|
|
1084
|
+
box.target = null;
|
|
1085
|
+
return null;
|
|
1086
|
+
}
|
|
1087
|
+
const rect = target.getBoundingClientRect();
|
|
1088
|
+
if (rect.width === 0 && rect.height === 0) return null;
|
|
1089
|
+
const covers = findCovers(target, rect);
|
|
1090
|
+
if (!covers) return null;
|
|
1091
|
+
return {
|
|
1092
|
+
rect,
|
|
1093
|
+
covers,
|
|
1094
|
+
labelWidth: covers.length ? box.label.offsetWidth : 0,
|
|
1095
|
+
labelHeight: covers.length ? box.label.offsetHeight : 0
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
paint(box, measurement, other = null) {
|
|
1099
|
+
const root = box.root;
|
|
1100
|
+
if (!measurement) {
|
|
1101
|
+
root.removeAttribute("data-visible");
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
const { rect, covers } = measurement;
|
|
1105
|
+
const x2 = rect.left - BOX_PADDING_PX;
|
|
1106
|
+
const y2 = rect.top - BOX_PADDING_PX;
|
|
1107
|
+
const labelAbove = {
|
|
1108
|
+
left: x2 - 2,
|
|
1109
|
+
top: y2 - LABEL_SPACE_PX,
|
|
1110
|
+
right: x2 - 2 + (measurement.labelWidth || Math.min(240, rect.width)),
|
|
1111
|
+
bottom: y2
|
|
1112
|
+
};
|
|
1113
|
+
const labelInside = y2 < LABEL_SPACE_PX || other !== null && other.rect !== rect && overlapsBox(labelAbove, other.rect);
|
|
1114
|
+
root.style.transform = `translate3d(${x2}px, ${y2}px, 0)`;
|
|
1115
|
+
root.style.width = `${rect.width + BOX_PADDING_PX * 2}px`;
|
|
1116
|
+
root.style.height = `${rect.height + BOX_PADDING_PX * 2}px`;
|
|
1117
|
+
root.toggleAttribute("data-label-inside", labelInside);
|
|
1118
|
+
root.style.clipPath = covers.length ? clipPath(x2, y2, covers) : "";
|
|
1119
|
+
const labelLeft = labelInside ? x2 + 2 : x2 - 2;
|
|
1120
|
+
const labelTop = labelInside ? y2 + 2 : y2 - LABEL_MARGIN_PX - measurement.labelHeight;
|
|
1121
|
+
const label = {
|
|
1122
|
+
left: labelLeft,
|
|
1123
|
+
top: labelTop,
|
|
1124
|
+
right: labelLeft + measurement.labelWidth,
|
|
1125
|
+
bottom: labelTop + measurement.labelHeight
|
|
1126
|
+
};
|
|
1127
|
+
root.toggleAttribute(
|
|
1128
|
+
"data-label-covered",
|
|
1129
|
+
covers.some((c2) => overlaps(c2, label))
|
|
1130
|
+
);
|
|
1131
|
+
if (root.hasAttribute("data-instant")) {
|
|
1132
|
+
root.setAttribute("data-visible", "");
|
|
1133
|
+
requestAnimationFrame(() => root.removeAttribute("data-instant"));
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
root.setAttribute("data-visible", "");
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
// src/core/visual-editor/field-path.ts
|
|
1141
|
+
var INDEX_SEGMENT = /^\d+$/;
|
|
1142
|
+
var WORD_SEPARATORS = /[_-]+/g;
|
|
1143
|
+
function normalizeFieldPath(path) {
|
|
1144
|
+
return path.split(".").map(
|
|
1145
|
+
(segment) => INDEX_SEGMENT.test(segment) ? String(Number.parseInt(segment, 10)) : segment
|
|
1146
|
+
).join(".");
|
|
1147
|
+
}
|
|
1148
|
+
var GENERIC_LEAVES = /* @__PURE__ */ new Set(["value", "text", "body", "content", "label"]);
|
|
1149
|
+
function humanize(name) {
|
|
1150
|
+
const words = name.replace(WORD_SEPARATORS, " ").trim();
|
|
1151
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
1152
|
+
}
|
|
1153
|
+
function fieldLabel(path, locale = "en") {
|
|
1154
|
+
if (!path) return t("visualEditor.edit", locale);
|
|
1155
|
+
const segments = path.split(".").filter((s2) => s2 && s2 !== "items" && !INDEX_SEGMENT.test(s2));
|
|
1156
|
+
const name = segments[segments.length - 1];
|
|
1157
|
+
if (!name) return t("visualEditor.edit", locale);
|
|
1158
|
+
const parent = segments[segments.length - 2];
|
|
1159
|
+
if (parent && GENERIC_LEAVES.has(name)) {
|
|
1160
|
+
return `${humanize(parent)} \u203A ${humanize(name)}`;
|
|
1161
|
+
}
|
|
1162
|
+
return humanize(name);
|
|
1163
|
+
}
|
|
1164
|
+
function findFieldElement(lookup) {
|
|
1165
|
+
if (!lookup.field) return null;
|
|
1166
|
+
const candidates = document.querySelectorAll(
|
|
1167
|
+
"[data-prepr-encoded]"
|
|
1168
|
+
);
|
|
1169
|
+
if (lookup.entryId) {
|
|
1170
|
+
for (const candidate of candidates) {
|
|
1171
|
+
if (candidate.getAttribute("data-prepr-entry-id") === lookup.entryId) {
|
|
1172
|
+
return candidate;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
for (const path of [lookup.field, lookup.fieldById]) {
|
|
1177
|
+
if (!path) continue;
|
|
1178
|
+
const wanted = normalizeFieldPath(path);
|
|
1179
|
+
for (const candidate of candidates) {
|
|
1180
|
+
const field = candidate.getAttribute("data-prepr-field");
|
|
1181
|
+
if (!field || normalizeFieldPath(field) !== wanted) continue;
|
|
1182
|
+
if (lookup.id && candidate.getAttribute("data-prepr-id") !== lookup.id) {
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
return candidate;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
543
1191
|
// src/core/stega/elements.ts
|
|
544
|
-
var
|
|
1192
|
+
var debug4 = createScopedLogger("stega:elements");
|
|
545
1193
|
var StegaElements = class {
|
|
546
1194
|
constructor(decode = decodeStega) {
|
|
547
1195
|
this.observer = null;
|
|
@@ -586,7 +1234,7 @@ var StegaElements = class {
|
|
|
586
1234
|
walkTextNodes(document.body, (textNode) => {
|
|
587
1235
|
if (this.tagFromTextNode(textNode)) encodedCount++;
|
|
588
1236
|
});
|
|
589
|
-
|
|
1237
|
+
debug4.log("document scan complete, encoded", encodedCount, "elements");
|
|
590
1238
|
this.elements = document.querySelectorAll("[data-prepr-encoded]");
|
|
591
1239
|
}
|
|
592
1240
|
/** `onUpdate` fires after each debounced batch, for refreshing derived state. */
|
|
@@ -629,231 +1277,22 @@ var StegaElements = class {
|
|
|
629
1277
|
attributeFilter: ["class", "style", "hidden", "open", "aria-expanded"]
|
|
630
1278
|
});
|
|
631
1279
|
}
|
|
632
|
-
/**
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
1280
|
+
/**
|
|
1281
|
+
* Stops watching for new content. Tags stay on their elements: the page's
|
|
1282
|
+
* stega characters are stripped once they are read, so a tag removed here
|
|
1283
|
+
* could never be rebuilt when edit mode is turned back on.
|
|
1284
|
+
*/
|
|
1285
|
+
disconnect() {
|
|
637
1286
|
if (this.observer) {
|
|
638
1287
|
this.observer.disconnect();
|
|
639
1288
|
this.observer = null;
|
|
640
1289
|
}
|
|
641
|
-
document.querySelectorAll("[data-prepr-encoded]").forEach((element) => {
|
|
642
|
-
element.removeAttribute("data-prepr-encoded");
|
|
643
|
-
element.removeAttribute("data-prepr-href");
|
|
644
|
-
element.removeAttribute("data-prepr-origin");
|
|
645
|
-
element.classList.remove("prepr-overlay-active");
|
|
646
|
-
});
|
|
647
1290
|
this.elements = void 0;
|
|
648
1291
|
}
|
|
649
1292
|
};
|
|
650
1293
|
|
|
651
|
-
// src/core/stega/overlay.ts
|
|
652
|
-
var debug4 = createScopedLogger("stega:overlay");
|
|
653
|
-
var StegaOverlay = class {
|
|
654
|
-
constructor(onEdit) {
|
|
655
|
-
this.overlay = null;
|
|
656
|
-
this.tooltip = null;
|
|
657
|
-
this.hideTimeout = null;
|
|
658
|
-
this.currentElement = null;
|
|
659
|
-
this.onEdit = onEdit;
|
|
660
|
-
}
|
|
661
|
-
create() {
|
|
662
|
-
const overlay = document.createElement("div");
|
|
663
|
-
overlay.className = "prepr-overlay";
|
|
664
|
-
overlay.style.display = "none";
|
|
665
|
-
const tooltip = document.createElement("div");
|
|
666
|
-
tooltip.className = "prepr-tooltip";
|
|
667
|
-
tooltip.style.display = "none";
|
|
668
|
-
document.body.appendChild(overlay);
|
|
669
|
-
document.body.appendChild(tooltip);
|
|
670
|
-
this.overlay = overlay;
|
|
671
|
-
this.tooltip = tooltip;
|
|
672
|
-
return { overlay, tooltip };
|
|
673
|
-
}
|
|
674
|
-
getTooltip() {
|
|
675
|
-
return this.tooltip;
|
|
676
|
-
}
|
|
677
|
-
hasCurrentElement() {
|
|
678
|
-
return this.currentElement !== null;
|
|
679
|
-
}
|
|
680
|
-
clearHideTimeout() {
|
|
681
|
-
if (this.hideTimeout) {
|
|
682
|
-
clearTimeout(this.hideTimeout);
|
|
683
|
-
this.hideTimeout = null;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
show(element) {
|
|
687
|
-
if (!this.overlay || !this.tooltip) return;
|
|
688
|
-
this.clearHideTimeout();
|
|
689
|
-
if (this.currentElement && this.currentElement !== element) {
|
|
690
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
691
|
-
}
|
|
692
|
-
const rect = element.getBoundingClientRect();
|
|
693
|
-
const href = element.getAttribute("data-prepr-href");
|
|
694
|
-
const origin = element.getAttribute("data-prepr-origin");
|
|
695
|
-
const id = element.getAttribute("data-prepr-id") ?? void 0;
|
|
696
|
-
const field = element.getAttribute("data-prepr-field") ?? void 0;
|
|
697
|
-
debug4.log("showing overlay", { href, origin });
|
|
698
|
-
const overlay = this.overlay;
|
|
699
|
-
overlay.style.display = "block";
|
|
700
|
-
overlay.style.top = `${rect.top + window.scrollY - 2}px`;
|
|
701
|
-
overlay.style.left = `${rect.left + window.scrollX - 4}px`;
|
|
702
|
-
overlay.style.width = `${rect.width + 8}px`;
|
|
703
|
-
overlay.style.height = `${rect.height + 4}px`;
|
|
704
|
-
const tooltip = this.tooltip;
|
|
705
|
-
if (href && origin) {
|
|
706
|
-
const MIN_WIDTH_FOR_TEXT = 80;
|
|
707
|
-
const isCompact = rect.width < MIN_WIDTH_FOR_TEXT;
|
|
708
|
-
tooltip.textContent = isCompact ? "\u2197" : `${origin} \u2197`;
|
|
709
|
-
tooltip.style.display = "block";
|
|
710
|
-
tooltip.style.minWidth = isCompact ? "auto" : "80px";
|
|
711
|
-
requestAnimationFrame(() => {
|
|
712
|
-
let top = rect.top + window.scrollY - tooltip.clientHeight - 2;
|
|
713
|
-
let left = rect.right + 4 - tooltip.clientWidth;
|
|
714
|
-
const minTop = window.scrollY + 4;
|
|
715
|
-
const maxTop = window.scrollY + window.innerHeight - tooltip.clientHeight - 4;
|
|
716
|
-
const minLeft = window.scrollX + 4;
|
|
717
|
-
const maxLeft = window.scrollX + window.innerWidth - tooltip.clientWidth - 4;
|
|
718
|
-
const below = top < minTop;
|
|
719
|
-
if (below) {
|
|
720
|
-
top = rect.bottom + window.scrollY + 2;
|
|
721
|
-
}
|
|
722
|
-
top = Math.max(minTop, Math.min(top, maxTop));
|
|
723
|
-
left = Math.max(minLeft, Math.min(left, maxLeft));
|
|
724
|
-
tooltip.dataset.preprPlacement = below ? "below" : "above";
|
|
725
|
-
tooltip.style.top = `${top}px`;
|
|
726
|
-
tooltip.style.left = `${left}px`;
|
|
727
|
-
});
|
|
728
|
-
tooltip.onclick = () => {
|
|
729
|
-
this.onEdit({ href, origin, id, field });
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
this.currentElement = element;
|
|
733
|
-
element.classList.add("prepr-overlay-active");
|
|
734
|
-
}
|
|
735
|
-
hideImmediate() {
|
|
736
|
-
this.clearHideTimeout();
|
|
737
|
-
if (this.overlay) this.overlay.style.display = "none";
|
|
738
|
-
if (this.tooltip) this.tooltip.style.display = "none";
|
|
739
|
-
if (this.currentElement) {
|
|
740
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
741
|
-
this.currentElement = null;
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
hide() {
|
|
745
|
-
if (!this.overlay || !this.tooltip) return;
|
|
746
|
-
this.clearHideTimeout();
|
|
747
|
-
this.hideTimeout = setTimeout(() => this.hideImmediate(), 100);
|
|
748
|
-
}
|
|
749
|
-
cleanup() {
|
|
750
|
-
this.clearHideTimeout();
|
|
751
|
-
if (this.overlay) {
|
|
752
|
-
this.overlay.style.display = "none";
|
|
753
|
-
this.overlay.parentNode?.removeChild(this.overlay);
|
|
754
|
-
this.overlay = null;
|
|
755
|
-
}
|
|
756
|
-
if (this.tooltip) {
|
|
757
|
-
this.tooltip.style.display = "none";
|
|
758
|
-
this.tooltip.parentNode?.removeChild(this.tooltip);
|
|
759
|
-
this.tooltip = null;
|
|
760
|
-
}
|
|
761
|
-
if (this.currentElement) {
|
|
762
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
763
|
-
this.currentElement = null;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
// src/core/stega/proximity.ts
|
|
769
|
-
var debug5 = createScopedLogger("stega:proximity");
|
|
770
|
-
var StegaProximity = class {
|
|
771
|
-
constructor() {
|
|
772
|
-
this.highlighted = /* @__PURE__ */ new Set();
|
|
773
|
-
this.visible = /* @__PURE__ */ new Set();
|
|
774
|
-
this.observer = null;
|
|
775
|
-
this.getEncodedElements = createElementCache(
|
|
776
|
-
"[data-prepr-encoded]",
|
|
777
|
-
200
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
|
-
/** Call after the encoded element set changes — rebuilds the observer. */
|
|
781
|
-
refreshObserving() {
|
|
782
|
-
try {
|
|
783
|
-
if (typeof IntersectionObserver === "undefined") return;
|
|
784
|
-
if (this.observer) {
|
|
785
|
-
this.observer.disconnect();
|
|
786
|
-
this.observer = null;
|
|
787
|
-
}
|
|
788
|
-
this.visible = /* @__PURE__ */ new Set();
|
|
789
|
-
this.observer = new IntersectionObserver(
|
|
790
|
-
(entries) => {
|
|
791
|
-
entries.forEach((entry) => {
|
|
792
|
-
const el = entry.target;
|
|
793
|
-
if (entry.isIntersecting) {
|
|
794
|
-
this.visible.add(el);
|
|
795
|
-
} else {
|
|
796
|
-
this.visible.delete(el);
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
},
|
|
800
|
-
{ root: null, rootMargin: "0px", threshold: 0 }
|
|
801
|
-
);
|
|
802
|
-
const nodes = this.getEncodedElements();
|
|
803
|
-
nodes.forEach((el) => this.observer.observe(el));
|
|
804
|
-
debug5.log("observing", nodes.length, "encoded elements");
|
|
805
|
-
} catch (error) {
|
|
806
|
-
debug5.log("error setting up IntersectionObserver", error);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
stopObserving() {
|
|
810
|
-
if (this.observer) {
|
|
811
|
-
this.observer.disconnect();
|
|
812
|
-
this.observer = null;
|
|
813
|
-
this.visible.clear();
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
updateElementGradients(cursorX, cursorY) {
|
|
817
|
-
const candidates = this.visible.size > 0 ? Array.from(this.visible) : Array.from(this.getEncodedElements());
|
|
818
|
-
const newHighlighted = /* @__PURE__ */ new Set();
|
|
819
|
-
candidates.forEach((element) => {
|
|
820
|
-
const rect = element.getBoundingClientRect();
|
|
821
|
-
const distance = Math.min(
|
|
822
|
-
Math.abs(cursorX - rect.left),
|
|
823
|
-
Math.abs(cursorX - rect.right),
|
|
824
|
-
Math.abs(cursorY - rect.top),
|
|
825
|
-
Math.abs(cursorY - rect.bottom)
|
|
826
|
-
);
|
|
827
|
-
if (distance < 150) {
|
|
828
|
-
const relativeX = cursorX - rect.left;
|
|
829
|
-
const relativeY = cursorY - rect.top;
|
|
830
|
-
element.style.setProperty("--cursor-x", `${relativeX}px`);
|
|
831
|
-
element.style.setProperty("--cursor-y", `${relativeY}px`);
|
|
832
|
-
const baseGradientSize = Math.max(
|
|
833
|
-
150,
|
|
834
|
-
Math.max(rect.width, rect.height) * 1.1
|
|
835
|
-
);
|
|
836
|
-
const distanceScale = Math.max(0, (400 - distance) / 400);
|
|
837
|
-
const gradientSize = baseGradientSize * distanceScale;
|
|
838
|
-
element.style.setProperty("--gradient-size", `${gradientSize}px`);
|
|
839
|
-
element.classList.add("prepr-proximity-highlight");
|
|
840
|
-
newHighlighted.add(element);
|
|
841
|
-
} else {
|
|
842
|
-
element.classList.remove("prepr-proximity-highlight");
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
this.highlighted = newHighlighted;
|
|
846
|
-
}
|
|
847
|
-
clearAllHighlights() {
|
|
848
|
-
this.highlighted.forEach(
|
|
849
|
-
(element) => element.classList.remove("prepr-proximity-highlight")
|
|
850
|
-
);
|
|
851
|
-
this.highlighted.clear();
|
|
852
|
-
}
|
|
853
|
-
};
|
|
854
|
-
|
|
855
1294
|
// src/core/stega/stega.generated.ts
|
|
856
|
-
var STEGA_STYLES = '
|
|
1295
|
+
var STEGA_STYLES = '@property --prepr-presence{syntax:"<number>";inherits:true;initial-value:1}[data-prepr-encoded]{outline:1px dashed rgb(79 70 229/calc(.3 * var(--prepr-presence,1)));outline-offset:2px}';
|
|
857
1296
|
|
|
858
1297
|
// src/core/stega/styles.ts
|
|
859
1298
|
var STEGA_STYLE_MARKER = "data-prepr-stega";
|
|
@@ -873,45 +1312,80 @@ function removeStegaStyles() {
|
|
|
873
1312
|
}
|
|
874
1313
|
|
|
875
1314
|
// src/core/stega/scan.ts
|
|
876
|
-
var
|
|
1315
|
+
var debug5 = createScopedLogger("stega:scan");
|
|
1316
|
+
var ENCODED_SELECTOR = "[data-prepr-encoded]";
|
|
1317
|
+
var PRESENCE_FADE_MS = 400;
|
|
877
1318
|
var EDITOR_MODE_STYLES = "[data-prepr-encoded]{cursor:pointer}";
|
|
1319
|
+
function fadeInPresence() {
|
|
1320
|
+
const root = document.documentElement;
|
|
1321
|
+
if (typeof root.animate !== "function") return;
|
|
1322
|
+
if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
|
|
1323
|
+
root.animate(
|
|
1324
|
+
{ "--prepr-presence": ["0", "1"] },
|
|
1325
|
+
{ duration: PRESENCE_FADE_MS, easing: "ease-out" }
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
878
1328
|
function createStegaController(opts) {
|
|
879
1329
|
const tooltipEnabled = opts.tooltip ?? true;
|
|
880
|
-
const
|
|
881
|
-
const proximity = new StegaProximity();
|
|
1330
|
+
const locale = opts.locale ?? "en";
|
|
882
1331
|
const elements = new StegaElements();
|
|
1332
|
+
const overlay = new VisualEditorOverlay({
|
|
1333
|
+
onLabelClick: (target) => opts.onEdit(readEditPayload(target))
|
|
1334
|
+
});
|
|
883
1335
|
let initialized = false;
|
|
884
|
-
let
|
|
885
|
-
|
|
886
|
-
|
|
1336
|
+
let pointerFrame = null;
|
|
1337
|
+
let pointerTarget = null;
|
|
1338
|
+
let pointerX = 0;
|
|
1339
|
+
let pointerY = 0;
|
|
1340
|
+
let hitTestPending = false;
|
|
1341
|
+
const updateHover = () => {
|
|
1342
|
+
pointerFrame = null;
|
|
1343
|
+
if (hitTestPending) {
|
|
1344
|
+
hitTestPending = false;
|
|
1345
|
+
pointerTarget = document.elementFromPoint(pointerX, pointerY);
|
|
1346
|
+
}
|
|
1347
|
+
if (overlay.owns(pointerTarget)) return;
|
|
1348
|
+
const encoded = pointerTarget instanceof Element ? pointerTarget.closest(ENCODED_SELECTOR) : null;
|
|
1349
|
+
overlay.setHover(
|
|
1350
|
+
encoded,
|
|
1351
|
+
encoded ? fieldLabel(encoded.getAttribute("data-prepr-field"), locale) : ""
|
|
1352
|
+
);
|
|
887
1353
|
};
|
|
888
|
-
const
|
|
889
|
-
if (
|
|
890
|
-
|
|
1354
|
+
const scheduleHover = () => {
|
|
1355
|
+
if (pointerFrame === null) {
|
|
1356
|
+
pointerFrame = requestAnimationFrame(updateHover);
|
|
891
1357
|
}
|
|
892
1358
|
};
|
|
1359
|
+
const handlePointer = (e2) => {
|
|
1360
|
+
pointerTarget = e2.target;
|
|
1361
|
+
pointerX = e2.clientX;
|
|
1362
|
+
pointerY = e2.clientY;
|
|
1363
|
+
hitTestPending = false;
|
|
1364
|
+
scheduleHover();
|
|
1365
|
+
};
|
|
1366
|
+
const handlePointerLeave = () => {
|
|
1367
|
+
pointerTarget = null;
|
|
1368
|
+
hitTestPending = false;
|
|
1369
|
+
overlay.setHover(null, "");
|
|
1370
|
+
};
|
|
893
1371
|
const handleScroll = () => {
|
|
894
|
-
overlay.
|
|
895
|
-
|
|
1372
|
+
overlay.onScroll();
|
|
1373
|
+
if (pointerTarget === null) return;
|
|
1374
|
+
hitTestPending = true;
|
|
1375
|
+
scheduleHover();
|
|
896
1376
|
};
|
|
1377
|
+
const handleResize = () => overlay.schedule();
|
|
897
1378
|
const handleClick = (e2) => {
|
|
898
1379
|
const target = e2.target;
|
|
899
|
-
const encoded = target?.closest(
|
|
900
|
-
"[data-prepr-encoded]"
|
|
901
|
-
);
|
|
1380
|
+
const encoded = target?.closest(ENCODED_SELECTOR);
|
|
902
1381
|
if (!encoded) return;
|
|
903
1382
|
e2.preventDefault();
|
|
904
1383
|
e2.stopPropagation();
|
|
905
|
-
opts.onEdit(
|
|
906
|
-
href: encoded.getAttribute("data-prepr-href") ?? "",
|
|
907
|
-
origin: encoded.getAttribute("data-prepr-origin") ?? "",
|
|
908
|
-
id: encoded.getAttribute("data-prepr-id") ?? void 0,
|
|
909
|
-
field: encoded.getAttribute("data-prepr-field") ?? void 0
|
|
910
|
-
});
|
|
1384
|
+
opts.onEdit(readEditPayload(encoded));
|
|
911
1385
|
};
|
|
912
1386
|
const start = () => {
|
|
913
1387
|
if (initialized) {
|
|
914
|
-
|
|
1388
|
+
debug5.log("already initialized, skipping setup");
|
|
915
1389
|
return;
|
|
916
1390
|
}
|
|
917
1391
|
if (!tooltipEnabled) {
|
|
@@ -921,71 +1395,94 @@ function createStegaController(opts) {
|
|
|
921
1395
|
});
|
|
922
1396
|
document.addEventListener("click", handleClick, { capture: true });
|
|
923
1397
|
initialized = true;
|
|
924
|
-
|
|
1398
|
+
debug5.log("stega controller started (headless)");
|
|
925
1399
|
return;
|
|
926
1400
|
}
|
|
927
1401
|
injectStegaStyles();
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
tooltip.addEventListener("mouseleave", handleTooltipMouseLeave);
|
|
1402
|
+
fadeInPresence();
|
|
1403
|
+
overlay.mount();
|
|
931
1404
|
elements.scanDocument(true);
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
const target = mouseEvent.target;
|
|
937
|
-
if (!target) return;
|
|
938
|
-
if (target.closest(".prepr-tooltip")) return;
|
|
939
|
-
proximity.updateElementGradients(mouseEvent.clientX, mouseEvent.clientY);
|
|
940
|
-
const encoded = target.closest("[data-prepr-encoded]");
|
|
941
|
-
if (encoded) {
|
|
942
|
-
overlay.show(encoded);
|
|
943
|
-
} else {
|
|
944
|
-
overlay.hide();
|
|
945
|
-
}
|
|
946
|
-
}, 16);
|
|
947
|
-
document.addEventListener("mousemove", throttledMouseMove, {
|
|
948
|
-
passive: true
|
|
1405
|
+
elements.setupMutationObserver(() => {
|
|
1406
|
+
if (pointerTarget === null) return;
|
|
1407
|
+
hitTestPending = true;
|
|
1408
|
+
scheduleHover();
|
|
949
1409
|
});
|
|
1410
|
+
document.addEventListener("pointermove", handlePointer, { passive: true });
|
|
1411
|
+
document.addEventListener("pointerdown", handlePointer, { passive: true });
|
|
1412
|
+
document.documentElement.addEventListener(
|
|
1413
|
+
"pointerleave",
|
|
1414
|
+
handlePointerLeave
|
|
1415
|
+
);
|
|
950
1416
|
window.addEventListener("scroll", handleScroll, {
|
|
951
1417
|
capture: true,
|
|
952
1418
|
passive: true
|
|
953
1419
|
});
|
|
1420
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
954
1421
|
initialized = true;
|
|
955
|
-
|
|
1422
|
+
debug5.log("stega controller started");
|
|
956
1423
|
};
|
|
957
1424
|
const stop = () => {
|
|
958
1425
|
if (!initialized) return;
|
|
1426
|
+
initialized = false;
|
|
959
1427
|
if (!tooltipEnabled) {
|
|
960
1428
|
document.removeEventListener("click", handleClick, { capture: true });
|
|
961
|
-
elements.
|
|
1429
|
+
elements.disconnect();
|
|
962
1430
|
removeStegaStyles();
|
|
963
|
-
|
|
964
|
-
debug6.log("stega controller stopped (editor mode)");
|
|
1431
|
+
debug5.log("stega controller stopped (headless)");
|
|
965
1432
|
return;
|
|
966
1433
|
}
|
|
967
|
-
if (
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1434
|
+
if (pointerFrame !== null) cancelAnimationFrame(pointerFrame);
|
|
1435
|
+
pointerFrame = null;
|
|
1436
|
+
pointerTarget = null;
|
|
1437
|
+
hitTestPending = false;
|
|
1438
|
+
document.removeEventListener("pointermove", handlePointer);
|
|
1439
|
+
document.removeEventListener("pointerdown", handlePointer);
|
|
1440
|
+
document.documentElement.removeEventListener(
|
|
1441
|
+
"pointerleave",
|
|
1442
|
+
handlePointerLeave
|
|
1443
|
+
);
|
|
972
1444
|
window.removeEventListener("scroll", handleScroll, { capture: true });
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
tooltip.removeEventListener("mouseleave", handleTooltipMouseLeave);
|
|
977
|
-
}
|
|
978
|
-
overlay.cleanup();
|
|
979
|
-
proximity.clearAllHighlights();
|
|
980
|
-
proximity.stopObserving();
|
|
981
|
-
elements.cleanup();
|
|
1445
|
+
window.removeEventListener("resize", handleResize);
|
|
1446
|
+
overlay.unmount();
|
|
1447
|
+
elements.disconnect();
|
|
982
1448
|
removeStegaStyles();
|
|
983
|
-
|
|
984
|
-
debug6.log("stega controller stopped");
|
|
1449
|
+
debug5.log("stega controller stopped");
|
|
985
1450
|
};
|
|
986
1451
|
return { start, stop };
|
|
987
1452
|
}
|
|
988
1453
|
|
|
1454
|
+
// src/core/stega/scroll-to-field.ts
|
|
1455
|
+
var debug6 = createScopedLogger("stega:scroll");
|
|
1456
|
+
var FLASH_DURATION_MS = 1200;
|
|
1457
|
+
var flashTimeout = null;
|
|
1458
|
+
function scrollToField(request) {
|
|
1459
|
+
const { field, id } = request;
|
|
1460
|
+
if (!field) return false;
|
|
1461
|
+
const candidates = document.querySelectorAll(
|
|
1462
|
+
"[data-prepr-encoded]"
|
|
1463
|
+
);
|
|
1464
|
+
let target = null;
|
|
1465
|
+
for (const candidate of candidates) {
|
|
1466
|
+
if (candidate.getAttribute("data-prepr-field") !== field) continue;
|
|
1467
|
+
if (id && candidate.getAttribute("data-prepr-id") !== id) continue;
|
|
1468
|
+
target = candidate;
|
|
1469
|
+
break;
|
|
1470
|
+
}
|
|
1471
|
+
if (!target) {
|
|
1472
|
+
debug6.log("no element found for field", field);
|
|
1473
|
+
return false;
|
|
1474
|
+
}
|
|
1475
|
+
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1476
|
+
if (flashTimeout) clearTimeout(flashTimeout);
|
|
1477
|
+
document.querySelectorAll(".prepr-overlay-active").forEach((el) => el.classList.remove("prepr-overlay-active"));
|
|
1478
|
+
target.classList.add("prepr-overlay-active");
|
|
1479
|
+
flashTimeout = setTimeout(() => {
|
|
1480
|
+
target.classList.remove("prepr-overlay-active");
|
|
1481
|
+
flashTimeout = null;
|
|
1482
|
+
}, FLASH_DURATION_MS);
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
989
1486
|
// src/core/store.ts
|
|
990
1487
|
var DEFAULT_STATE = {
|
|
991
1488
|
locale: "en",
|
|
@@ -1120,7 +1617,7 @@ function splitPath(full) {
|
|
|
1120
1617
|
return [full.slice(0, index), full.slice(index + 1)];
|
|
1121
1618
|
}
|
|
1122
1619
|
|
|
1123
|
-
// ../../node_modules/.pnpm/preact@10.29.
|
|
1620
|
+
// ../../node_modules/.pnpm/preact@10.29.8/node_modules/preact/dist/preact.mjs
|
|
1124
1621
|
var n;
|
|
1125
1622
|
var l;
|
|
1126
1623
|
var u;
|
|
@@ -1191,8 +1688,8 @@ function H() {
|
|
|
1191
1688
|
}
|
|
1192
1689
|
}
|
|
1193
1690
|
function L(n2, l2, u3, t3, i2, r2, o2, e2, f3, c2, a2) {
|
|
1194
|
-
var s2, h2, p2, v2, y2, _2, g2
|
|
1195
|
-
for (f3 = T(u3, l2,
|
|
1691
|
+
var s2, h2, p2, v2, y2, _2, g2 = t3 && t3.__k || w, m2 = l2.length;
|
|
1692
|
+
for (f3 = T(u3, l2, g2, f3, m2), s2 = 0; s2 < m2; s2++) null != (p2 = u3.__k[s2]) && (h2 = -1 != p2.__i && g2[p2.__i] || d, p2.__i = s2, _2 = q(n2, p2, h2, i2, r2, o2, e2, f3, c2, a2), v2 = p2.__e, p2.ref && h2.ref != p2.ref && (h2.ref && J(h2.ref, null, p2), a2.push(p2.ref, p2.__c || v2, p2)), null == y2 && null != v2 && (y2 = v2), 4 & p2.__u ? (f3 = j(p2, f3, n2), h2.__e && (h2.__e = null)) : "function" == typeof p2.type && void 0 !== _2 ? f3 = _2 : v2 && (f3 = v2.nextSibling), p2.__u &= -7);
|
|
1196
1693
|
return u3.__e = y2, f3;
|
|
1197
1694
|
}
|
|
1198
1695
|
function T(n2, l2, u3, t3, i2) {
|
|
@@ -1201,13 +1698,13 @@ function T(n2, l2, u3, t3, i2) {
|
|
|
1201
1698
|
if (s2) for (r2 = 0; r2 < a2; r2++) null != (e2 = u3[r2]) && 0 == (2 & e2.__u) && (e2.__e == t3 && (t3 = $(e2)), K(e2, e2));
|
|
1202
1699
|
return t3;
|
|
1203
1700
|
}
|
|
1204
|
-
function j(n2, l2, u3
|
|
1205
|
-
var
|
|
1701
|
+
function j(n2, l2, u3) {
|
|
1702
|
+
var t3, i2;
|
|
1206
1703
|
if ("function" == typeof n2.type) {
|
|
1207
|
-
for (
|
|
1704
|
+
for (t3 = n2.__k, i2 = 0; t3 && i2 < t3.length; i2++) t3[i2] && (t3[i2].__ = n2, l2 = j(t3[i2], l2, u3));
|
|
1208
1705
|
return l2;
|
|
1209
1706
|
}
|
|
1210
|
-
n2.__e != l2 && (
|
|
1707
|
+
n2.__e != l2 && (l2 && n2.type && !l2.parentNode && (l2 = $(n2)), l2 = u3.insertBefore(n2.__e, l2 || null));
|
|
1211
1708
|
do {
|
|
1212
1709
|
l2 = l2 && l2.nextSibling;
|
|
1213
1710
|
} while (null != l2 && 8 == l2.nodeType);
|
|
@@ -1253,35 +1750,35 @@ function V(n2) {
|
|
|
1253
1750
|
};
|
|
1254
1751
|
}
|
|
1255
1752
|
function q(n2, u3, t3, i2, r2, o2, e2, f3, c2, a2) {
|
|
1256
|
-
var s2, h2, p2, v2, y2, d2, _2, k2, x2, M,
|
|
1753
|
+
var s2, h2, p2, v2, y2, d2, _2, k2, x2, M, I2, P2, A2, H2, T2, j2, F = u3.type;
|
|
1257
1754
|
if (void 0 !== u3.constructor) return null;
|
|
1258
1755
|
128 & t3.__u && (c2 = !!(32 & t3.__u), o2 = [f3 = u3.__e = t3.__e]), (s2 = l.__b) && s2(u3);
|
|
1259
|
-
n: if ("function" == typeof
|
|
1756
|
+
n: if ("function" == typeof F) {
|
|
1260
1757
|
h2 = e2.length;
|
|
1261
1758
|
try {
|
|
1262
|
-
if (x2 = u3.props, M =
|
|
1759
|
+
if (x2 = u3.props, M = F.prototype && F.prototype.render, I2 = (s2 = F.contextType) && i2[s2.__c], P2 = s2 ? I2 ? I2.props.value : s2.__ : i2, t3.__c ? k2 = (p2 = u3.__c = t3.__c).__ = p2.__E : (M ? u3.__c = p2 = new F(x2, P2) : (u3.__c = p2 = new C(x2, P2), p2.constructor = F, p2.render = Q), I2 && I2.sub(p2), p2.state || (p2.state = {}), p2.__n = i2, v2 = p2.__d = true, p2.__h = [], p2._sb = []), M && null == p2.__s && (p2.__s = p2.state), M && null != F.getDerivedStateFromProps && (p2.__s == p2.state && (p2.__s = m({}, p2.__s)), m(p2.__s, F.getDerivedStateFromProps(x2, p2.__s))), y2 = p2.props, d2 = p2.state, p2.__v = u3, v2) M && null == F.getDerivedStateFromProps && null != p2.componentWillMount && p2.componentWillMount(), M && null != p2.componentDidMount && p2.__h.push(p2.componentDidMount);
|
|
1263
1760
|
else {
|
|
1264
|
-
if (M && null ==
|
|
1761
|
+
if (M && null == F.getDerivedStateFromProps && x2 !== y2 && null != p2.componentWillReceiveProps && p2.componentWillReceiveProps(x2, P2), u3.__v == t3.__v || !p2.__e && null != p2.shouldComponentUpdate && false === p2.shouldComponentUpdate(x2, p2.__s, P2)) {
|
|
1265
1762
|
u3.__v != t3.__v && (p2.props = x2, p2.state = p2.__s, p2.__d = false), u3.__e = t3.__e, u3.__k = t3.__k, u3.__k.some(function(n3) {
|
|
1266
1763
|
n3 && (n3.__ = u3);
|
|
1267
|
-
}), w.push.apply(p2.__h, p2._sb), p2._sb = [], p2.__h.length && e2.push(p2);
|
|
1764
|
+
}), w.push.apply(p2.__h, p2._sb), p2._sb = [], p2.__h.length && e2.push(p2), f3 = $(t3);
|
|
1268
1765
|
break n;
|
|
1269
1766
|
}
|
|
1270
|
-
null != p2.componentWillUpdate && p2.componentWillUpdate(x2, p2.__s,
|
|
1767
|
+
null != p2.componentWillUpdate && p2.componentWillUpdate(x2, p2.__s, P2), M && null != p2.componentDidUpdate && p2.__h.push(function() {
|
|
1271
1768
|
p2.componentDidUpdate(y2, d2, _2);
|
|
1272
1769
|
});
|
|
1273
1770
|
}
|
|
1274
|
-
if (p2.context =
|
|
1771
|
+
if (p2.context = P2, p2.props = x2, p2.__P = n2, p2.__e = false, A2 = l.__r, H2 = 0, M) p2.state = p2.__s, p2.__d = false, A2 && A2(u3), s2 = p2.render(p2.props, p2.state, p2.context), w.push.apply(p2.__h, p2._sb), p2._sb = [];
|
|
1275
1772
|
else do {
|
|
1276
|
-
p2.__d = false,
|
|
1277
|
-
} while (p2.__d && ++
|
|
1278
|
-
p2.state = p2.__s, null != p2.getChildContext && (i2 = m(m({}, i2), p2.getChildContext())), M && !v2 && null != p2.getSnapshotBeforeUpdate && (_2 = p2.getSnapshotBeforeUpdate(y2, d2)),
|
|
1773
|
+
p2.__d = false, A2 && A2(u3), s2 = p2.render(p2.props, p2.state, p2.context), p2.state = p2.__s;
|
|
1774
|
+
} while (p2.__d && ++H2 < 25);
|
|
1775
|
+
p2.state = p2.__s, null != p2.getChildContext && (i2 = m(m({}, i2), p2.getChildContext())), M && !v2 && null != p2.getSnapshotBeforeUpdate && (_2 = p2.getSnapshotBeforeUpdate(y2, d2)), T2 = null != s2 && s2.type === S && null == s2.key ? E(s2.props.children) : s2, f3 = L(n2, g(T2) ? T2 : [T2], u3, t3, i2, r2, o2, e2, f3, c2, a2), p2.base = u3.__e, u3.__u &= -161, p2.__h.length && e2.push(p2), k2 && (p2.__E = p2.__ = null);
|
|
1279
1776
|
} catch (n3) {
|
|
1280
1777
|
if (e2.length = h2, u3.__v = null, c2 || null != o2) {
|
|
1281
1778
|
if (n3.then) {
|
|
1282
1779
|
for (u3.__u |= c2 ? 160 : 128; f3 && 8 == f3.nodeType && f3.nextSibling; ) f3 = f3.nextSibling;
|
|
1283
1780
|
null != o2 && (o2[o2.indexOf(f3)] = null), u3.__e = f3;
|
|
1284
|
-
} else if (null != o2) for (
|
|
1781
|
+
} else if (null != o2) for (j2 = o2.length; j2--; ) b(o2[j2]);
|
|
1285
1782
|
} else u3.__e = t3.__e;
|
|
1286
1783
|
null == u3.__k && (u3.__k = t3.__k || []), n3.then || B(u3), l.__e(n3, u3, t3);
|
|
1287
1784
|
}
|
|
@@ -1402,9 +1899,9 @@ var CLOSE_ICON_SVG = `<svg width="10" height="10" viewBox="0 0 10 10" fill="none
|
|
|
1402
1899
|
</svg>`;
|
|
1403
1900
|
|
|
1404
1901
|
// src/core/ui/toolbar.generated.ts
|
|
1405
|
-
var TOOLBAR_CSS = ":host{--prepr-primary:#4338ca;--prepr-bg:#eef2ff;--prepr-text:#1f2937;--prepr-radius:8px;--prepr-shadow:0px 0px 40px 0px #1f29373d;--prepr-z-index:10000;--prepr-primary-50:#eef2ff;--prepr-primary-100:#e0e7ff;--prepr-primary-800:#3730a3;--prepr-indigo-600:#4f46e5;--prepr-indigo-50:#eef2ff;--prepr-indigo-700:#4338ca;--prepr-secondary-400:#fb923c;--prepr-secondary-500:#f97316;--prepr-gray-100:#f3f4f6;--prepr-gray-200:#e5e7eb;--prepr-gray-300:#d1d5db;--prepr-gray-400:#9ca3af;--prepr-gray-500:#6b7280;--prepr-gray-700:#374151;--prepr-gray-800:#1f2937;--prepr-gray-900:#111827;--prepr-shadow-sm:0px 0px 12px 0px #c3c3c3cc;color:var(--prepr-text);box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.5}:host *,:host :before,:host :after,*,:before,:after{box-sizing:border-box}button{font-family:inherit;font-size:inherit;margin:0}.prepr-regular-text{font-size:.875rem;font-weight:500;line-height:1.5}.prepr-backdrop{z-index:calc(var(--prepr-z-index) - 1);opacity:0;pointer-events:none;background:#0000001a;transition:opacity .2s ease-in-out;position:fixed;top:0;bottom:0;left:0;right:0}.prepr-backdrop[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-container{z-index:var(--prepr-z-index);justify-content:flex-end;align-items:center;display:flex;position:fixed;top:calc(50% - 64px);right:0}.prepr-toggle-holder{padding:.5rem}.prepr-toggle{cursor:pointer;background:var(--prepr-primary);color:#fff;border:none;border-radius:9999px;justify-content:center;align-items:center;width:2.25rem;height:2.25rem;display:flex}.prepr-toggle>span{display:flex}.prepr-toggle svg{color:#fff}.prepr-panel{z-index:var(--prepr-z-index);opacity:0;pointer-events:none;justify-content:flex-end;padding:.75rem;transition:opacity .2s ease-in-out;display:flex;position:fixed;top:50%;right:0;transform:translateY(-50%)}.prepr-panel[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-content{box-shadow:var(--prepr-shadow);border-radius:var(--prepr-radius);background:var(--prepr-bg);flex-direction:column;gap:2.5rem;width:100%;padding:1.5rem;display:flex}@media (min-width:640px){.prepr-panel{margin-right:4rem;padding:0}.prepr-content{width:502px;padding:2.5rem}}.prepr-header{justify-content:space-between;align-items:center;gap:.5rem;display:flex}.prepr-header-left{align-items:flex-start;gap:.75rem;display:flex}.prepr-badge{background:var(--prepr-primary-100);height:1.5rem;color:var(--prepr-primary);border-radius:4px;align-items:center;padding:.25rem .75rem;font-size:.875rem;display:flex}.prepr-header-close{cursor:pointer;background:var(--prepr-primary-100);width:2rem;height:2rem;color:var(--prepr-gray-700);border:none;border-radius:6px;justify-content:center;align-items:center;display:flex}.prepr-section{flex-direction:column;gap:.5rem;display:flex}.prepr-section-label{color:var(--prepr-gray-400);font-size:.875rem}.prepr-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:.5rem 1.5rem;display:flex}.prepr-row-title{color:var(--prepr-gray-800);margin:0;font-size:1rem;font-weight:600}.prepr-radiogroup{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);background:#fff;align-items:center;gap:.25rem;height:2.5rem;padding:.25rem;display:flex}.prepr-radiogroup[data-disabled=true]{cursor:not-allowed;opacity:.5}.prepr-radiogroup[data-disabled=true] .prepr-radio{pointer-events:none}.prepr-radio{cursor:pointer;white-space:nowrap;text-align:center;height:2rem;color:var(--prepr-gray-900);background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:.5rem 1.125rem;font-size:.875rem;font-weight:500;transition:all .2s ease-in-out;display:flex}.prepr-radio[data-checked=true]{background:var(--prepr-indigo-600);color:#fff;box-shadow:var(--prepr-shadow-sm)}.prepr-radio[data-checked=true][data-off=true]{background:var(--prepr-gray-800);color:#fff}.prepr-radio-variant{width:82px}.prepr-listbox{position:relative}.prepr-segment-button{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);width:240px;height:2.5rem;color:var(--prepr-gray-500);cursor:pointer;background:#fff;flex-shrink:0;align-items:center;gap:.5rem;padding:0 .5rem;font-size:.875rem;font-weight:500;display:flex;overflow:hidden}@media (min-width:768px){.prepr-segment-button{padding:0 1rem}}.prepr-segment-button:disabled{cursor:not-allowed;background:var(--prepr-gray-200);color:var(--prepr-gray-400)}.prepr-segment-button[aria-expanded=true]{border-bottom-color:#fff;border-bottom-right-radius:0;border-bottom-left-radius:0}.prepr-segment-label{white-space:nowrap;text-overflow:ellipsis;text-align:left;width:100%;margin-right:auto;overflow:hidden}.prepr-segment-caret{color:var(--prepr-gray-800);display:flex}.prepr-options{z-index:calc(var(--prepr-z-index) + 1);border-left:1px solid var(--prepr-gray-300);border-right:1px solid var(--prepr-gray-300);border-bottom:1px solid var(--prepr-gray-300);-ms-overflow-style:none;scrollbar-width:none;background:#fff;border-radius:0 0 6px 6px;width:240px;max-height:300px;margin:0;padding:0;list-style:none;position:absolute;top:100%;left:0;overflow-y:auto;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.prepr-options::-webkit-scrollbar{display:none}.prepr-options[hidden]{display:none}.prepr-option-search{background:#fff;padding:.5rem;position
|
|
1902
|
+
var TOOLBAR_CSS = ":host{--prepr-primary:#4338ca;--prepr-bg:#eef2ff;--prepr-text:#1f2937;--prepr-radius:8px;--prepr-shadow:0px 0px 40px 0px #1f29373d;--prepr-z-index:10000;--prepr-primary-50:#eef2ff;--prepr-primary-100:#e0e7ff;--prepr-primary-800:#3730a3;--prepr-indigo-600:#4f46e5;--prepr-indigo-50:#eef2ff;--prepr-indigo-700:#4338ca;--prepr-secondary-400:#fb923c;--prepr-secondary-500:#f97316;--prepr-gray-100:#f3f4f6;--prepr-gray-200:#e5e7eb;--prepr-gray-300:#d1d5db;--prepr-gray-400:#9ca3af;--prepr-gray-500:#6b7280;--prepr-gray-700:#374151;--prepr-gray-800:#1f2937;--prepr-gray-900:#111827;--prepr-shadow-sm:0px 0px 12px 0px #c3c3c3cc;color:var(--prepr-text);box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.5}:host *,:host :before,:host :after,*,:before,:after{box-sizing:border-box}button{font-family:inherit;font-size:inherit;margin:0}.prepr-regular-text{font-size:.875rem;font-weight:500;line-height:1.5}.prepr-backdrop{z-index:calc(var(--prepr-z-index) - 1);opacity:0;pointer-events:none;background:#0000001a;transition:opacity .2s ease-in-out;position:fixed;top:0;bottom:0;left:0;right:0}.prepr-backdrop[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-container{z-index:var(--prepr-z-index);justify-content:flex-end;align-items:center;display:flex;position:fixed;top:calc(50% - 64px);right:0}.prepr-toggle-holder{padding:.5rem}.prepr-toggle{cursor:pointer;background:var(--prepr-primary);color:#fff;border:none;border-radius:9999px;justify-content:center;align-items:center;width:2.25rem;height:2.25rem;display:flex}.prepr-toggle>span{display:flex}.prepr-toggle svg{color:#fff}.prepr-panel{z-index:var(--prepr-z-index);opacity:0;pointer-events:none;justify-content:flex-end;padding:.75rem;transition:opacity .2s ease-in-out;display:flex;position:fixed;top:50%;right:0;transform:translateY(-50%)}.prepr-panel[data-open=true]{opacity:1;pointer-events:auto;animation:.2s ease-in-out prepr-fade-in}.prepr-content{box-shadow:var(--prepr-shadow);border-radius:var(--prepr-radius);background:var(--prepr-bg);flex-direction:column;gap:2.5rem;width:100%;padding:1.5rem;display:flex}@media (min-width:640px){.prepr-panel{margin-right:4rem;padding:0}.prepr-content{width:502px;padding:2.5rem}}.prepr-header{justify-content:space-between;align-items:center;gap:.5rem;display:flex}.prepr-header-left{align-items:flex-start;gap:.75rem;display:flex}.prepr-badge{background:var(--prepr-primary-100);height:1.5rem;color:var(--prepr-primary);border-radius:4px;align-items:center;padding:.25rem .75rem;font-size:.875rem;display:flex}.prepr-header-close{cursor:pointer;background:var(--prepr-primary-100);width:2rem;height:2rem;color:var(--prepr-gray-700);border:none;border-radius:6px;justify-content:center;align-items:center;display:flex}.prepr-section{flex-direction:column;gap:.5rem;display:flex}.prepr-section-label{color:var(--prepr-gray-400);font-size:.875rem}.prepr-row{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:.5rem 1.5rem;display:flex}.prepr-row-title{color:var(--prepr-gray-800);margin:0;font-size:1rem;font-weight:600}.prepr-radiogroup{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);background:#fff;align-items:center;gap:.25rem;height:2.5rem;padding:.25rem;display:flex}.prepr-radiogroup[data-disabled=true]{cursor:not-allowed;opacity:.5}.prepr-radiogroup[data-disabled=true] .prepr-radio{pointer-events:none}.prepr-radio{cursor:pointer;white-space:nowrap;text-align:center;height:2rem;color:var(--prepr-gray-900);background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:.5rem 1.125rem;font-size:.875rem;font-weight:500;transition:all .2s ease-in-out;display:flex}.prepr-radio[data-checked=true]{background:var(--prepr-indigo-600);color:#fff;box-shadow:var(--prepr-shadow-sm)}.prepr-radio[data-checked=true][data-off=true]{background:var(--prepr-gray-800);color:#fff}.prepr-radio-variant{width:82px}.prepr-listbox{position:relative}.prepr-segment-button{border-radius:var(--prepr-radius);border:1px solid var(--prepr-gray-300);width:240px;height:2.5rem;color:var(--prepr-gray-500);cursor:pointer;background:#fff;flex-shrink:0;align-items:center;gap:.5rem;padding:0 .5rem;font-size:.875rem;font-weight:500;display:flex;overflow:hidden}@media (min-width:768px){.prepr-segment-button{padding:0 1rem}}.prepr-segment-button:disabled{cursor:not-allowed;background:var(--prepr-gray-200);color:var(--prepr-gray-400)}.prepr-segment-button[aria-expanded=true]{border-bottom-color:#fff;border-bottom-right-radius:0;border-bottom-left-radius:0}.prepr-segment-label{white-space:nowrap;text-overflow:ellipsis;text-align:left;width:100%;margin-right:auto;overflow:hidden}.prepr-segment-caret{color:var(--prepr-gray-800);display:flex}.prepr-options{z-index:calc(var(--prepr-z-index) + 1);border-left:1px solid var(--prepr-gray-300);border-right:1px solid var(--prepr-gray-300);border-bottom:1px solid var(--prepr-gray-300);-ms-overflow-style:none;scrollbar-width:none;background:#fff;border-radius:0 0 6px 6px;width:240px;max-height:300px;margin:0;padding:0;list-style:none;position:absolute;top:100%;left:0;overflow-y:auto;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.prepr-options::-webkit-scrollbar{display:none}.prepr-options[hidden]{display:none}.prepr-option-search{background:#fff;padding:.5rem;position:sticky;top:0}.prepr-segment-search{border:1px solid var(--prepr-gray-300);border-radius:var(--prepr-radius);width:100%;height:2rem;font:inherit;color:var(--prepr-gray-900);box-sizing:border-box;background:#fff;padding:0 .5rem;font-size:.875rem}@media (pointer:coarse){.prepr-segment-search{font-size:16px}}.prepr-segment-search::placeholder{color:var(--prepr-gray-400)}.prepr-segment-search:focus{border-color:var(--prepr-gray-400);outline:none}.prepr-option{cursor:pointer;width:100%;color:var(--prepr-gray-900);background:#fff;align-items:center;padding:.5rem 1rem;font-size:.875rem;font-weight:500;display:flex}.prepr-option:hover{background:var(--prepr-gray-100)}.prepr-option[aria-selected=true]{background:var(--prepr-indigo-50);color:var(--prepr-indigo-700)}.prepr-option-label{white-space:nowrap;text-overflow:ellipsis;text-align:left;width:100%;margin-right:auto;overflow:hidden}.prepr-reset{background:var(--prepr-secondary-400);color:#fff;cursor:pointer;border:none;border-radius:6px;justify-content:center;align-items:center;gap:.5rem;width:100%;height:2.5rem;padding:.5rem .75rem;font-size:.875rem;font-weight:500;display:flex}.prepr-reset:hover:not(:disabled){background:var(--prepr-secondary-500)}.prepr-reset:disabled{background:var(--prepr-gray-400);color:var(--prepr-gray-500);cursor:not-allowed}@media (min-width:768px){.prepr-reset{width:108px}}.prepr-indicators{z-index:var(--prepr-z-index);gap:.75rem;display:flex;position:fixed;bottom:1.5rem;right:1.5rem}.prepr-status-pill{background:var(--prepr-primary);color:#fff;cursor:pointer;border:none;border-radius:9999px;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.75rem;font-weight:500;transition:background-color .15s;display:flex;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.prepr-status-pill[data-interactive=false]{cursor:default;opacity:1;color:#fff}.prepr-status-pill[data-interactive=true]:hover{background:var(--prepr-primary-800)}.prepr-status-pill[hidden]{display:none}.prepr-status-viewing{color:#fff9;font-size:10px}.prepr-status-segment{text-overflow:ellipsis;white-space:nowrap;max-width:120px;display:inline-block;overflow:hidden}.prepr-status-variant{text-overflow:ellipsis;white-space:nowrap;background:#fff3;border-radius:4px;max-width:80px;padding:0 .5rem;display:inline-block;overflow:hidden}.prepr-status-x{color:#fff;width:.75rem;height:.75rem}.prepr-close-edit-pill{background:var(--prepr-primary-50);color:var(--prepr-gray-800);cursor:pointer;border:none;border-radius:9999px;align-items:center;gap:.5rem;padding:.5rem 1rem;font-size:.75rem;font-weight:500;transition:background-color .15s;display:flex;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.prepr-close-edit-pill:hover{background:var(--prepr-primary-100)}.prepr-close-edit-pill[hidden]{display:none}.prepr-close-edit-x{width:1rem;height:1rem;color:var(--prepr-gray-500);transition:color .15s}.prepr-close-edit-pill:hover .prepr-close-edit-x{color:var(--prepr-gray-700)}.prepr-tip{z-index:calc(var(--prepr-z-index) + 2);overflow-wrap:break-word;background:var(--prepr-gray-900);color:#fff;pointer-events:none;border-radius:6px;width:auto;max-width:min(420px,100vw - 16px);padding:.5rem .75rem;font-size:.75rem;font-weight:500;position:fixed;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.prepr-tip-arrow{border-left:6px solid #0000;border-right:6px solid #0000;border-top:6px solid var(--prepr-gray-900);width:0;height:0;position:absolute;top:100%;transform:translate(-50%)}[hidden]{display:none!important}@keyframes prepr-fade-in{0%{opacity:0}to{opacity:1}}";
|
|
1406
1903
|
|
|
1407
|
-
// ../../node_modules/.pnpm/preact@10.29.
|
|
1904
|
+
// ../../node_modules/.pnpm/preact@10.29.8/node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs
|
|
1408
1905
|
var f2 = 0;
|
|
1409
1906
|
function u2(e2, t3, n2, o2, i2, u3) {
|
|
1410
1907
|
t3 || (t3 = {});
|
|
@@ -2097,8 +2594,742 @@ function definePreprToolbar() {
|
|
|
2097
2594
|
}
|
|
2098
2595
|
}
|
|
2099
2596
|
|
|
2597
|
+
// src/core/visual-editor/asset-images.ts
|
|
2598
|
+
var MAX_URL_LENGTH = 4096;
|
|
2599
|
+
var MIN_KEY_SEGMENTS = 2;
|
|
2600
|
+
var TRANSFORM_OPTION = /^[a-z]{1,3}_[\w.-]*$/i;
|
|
2601
|
+
var FORMAT_PLACEHOLDER = "{format}";
|
|
2602
|
+
var PROXY_PARAMS = ["url", "href", "src", "image"];
|
|
2603
|
+
var ABSOLUTE_HTTP = /^https?:\/\//i;
|
|
2604
|
+
var SOURCE_URL_ATTRIBUTES = ["src", "data-src"];
|
|
2605
|
+
var SOURCE_SET_ATTRIBUTES = ["srcset", "data-srcset"];
|
|
2606
|
+
var ASSET_ATTR = "data-prepr-asset";
|
|
2607
|
+
var ASSET_SOURCE_ATTRIBUTES = [
|
|
2608
|
+
...SOURCE_URL_ATTRIBUTES,
|
|
2609
|
+
...SOURCE_SET_ATTRIBUTES
|
|
2610
|
+
];
|
|
2611
|
+
var TAG_ATTRIBUTES = [
|
|
2612
|
+
"data-prepr-encoded",
|
|
2613
|
+
"data-prepr-id",
|
|
2614
|
+
"data-prepr-field",
|
|
2615
|
+
"data-prepr-entry-id",
|
|
2616
|
+
ASSET_ATTR
|
|
2617
|
+
];
|
|
2618
|
+
function decodeSegment(segment) {
|
|
2619
|
+
try {
|
|
2620
|
+
return decodeURIComponent(segment);
|
|
2621
|
+
} catch {
|
|
2622
|
+
return segment;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
function isTransformSegment(segment) {
|
|
2626
|
+
return segment === FORMAT_PLACEHOLDER || segment.split(",").every((option) => TRANSFORM_OPTION.test(option));
|
|
2627
|
+
}
|
|
2628
|
+
function assetKey(url, depth = 0) {
|
|
2629
|
+
if (!url || url.length > MAX_URL_LENGTH) return null;
|
|
2630
|
+
let parsed;
|
|
2631
|
+
try {
|
|
2632
|
+
parsed = new URL(url, "https://base.invalid");
|
|
2633
|
+
} catch {
|
|
2634
|
+
return null;
|
|
2635
|
+
}
|
|
2636
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
2637
|
+
if (depth === 0) {
|
|
2638
|
+
for (const name of PROXY_PARAMS) {
|
|
2639
|
+
const inner = parsed.searchParams.get(name);
|
|
2640
|
+
if (inner && ABSOLUTE_HTTP.test(inner)) return assetKey(inner, 1);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
const segments = parsed.pathname.split("/").filter(Boolean).map(decodeSegment);
|
|
2644
|
+
const file = segments.pop();
|
|
2645
|
+
if (!file) return null;
|
|
2646
|
+
const kept = segments.filter((segment) => !isTransformSegment(segment));
|
|
2647
|
+
kept.push(file);
|
|
2648
|
+
return kept.length >= MIN_KEY_SEGMENTS ? kept.join("/") : null;
|
|
2649
|
+
}
|
|
2650
|
+
function srcsetUrls(srcset) {
|
|
2651
|
+
const urls = [];
|
|
2652
|
+
let expectUrl = true;
|
|
2653
|
+
for (const token of srcset.split(/\s+/)) {
|
|
2654
|
+
if (!token) continue;
|
|
2655
|
+
if (!expectUrl) {
|
|
2656
|
+
const comma = token.indexOf(",");
|
|
2657
|
+
if (comma === -1) continue;
|
|
2658
|
+
expectUrl = true;
|
|
2659
|
+
const rest = token.slice(comma + 1);
|
|
2660
|
+
if (!rest) continue;
|
|
2661
|
+
urls.push(rest.replace(/,+$/, ""));
|
|
2662
|
+
expectUrl = rest.endsWith(",");
|
|
2663
|
+
continue;
|
|
2664
|
+
}
|
|
2665
|
+
const url = token.replace(/^,+|,+$/g, "");
|
|
2666
|
+
if (url) urls.push(url);
|
|
2667
|
+
expectUrl = token.endsWith(",");
|
|
2668
|
+
}
|
|
2669
|
+
return urls;
|
|
2670
|
+
}
|
|
2671
|
+
function createAssetIndex(assets) {
|
|
2672
|
+
const index = /* @__PURE__ */ new Map();
|
|
2673
|
+
for (const asset of assets) {
|
|
2674
|
+
const key = assetKey(asset.key);
|
|
2675
|
+
if (key && !index.has(key)) index.set(key, asset);
|
|
2676
|
+
}
|
|
2677
|
+
return index;
|
|
2678
|
+
}
|
|
2679
|
+
function urlsOf(img) {
|
|
2680
|
+
const urls = [];
|
|
2681
|
+
const collect = (el) => {
|
|
2682
|
+
for (const name of SOURCE_URL_ATTRIBUTES) {
|
|
2683
|
+
const value = el.getAttribute(name);
|
|
2684
|
+
if (value) urls.push(value);
|
|
2685
|
+
}
|
|
2686
|
+
for (const name of SOURCE_SET_ATTRIBUTES) {
|
|
2687
|
+
const value = el.getAttribute(name);
|
|
2688
|
+
if (value) urls.push(...srcsetUrls(value));
|
|
2689
|
+
}
|
|
2690
|
+
};
|
|
2691
|
+
collect(img);
|
|
2692
|
+
const picture = img.parentElement;
|
|
2693
|
+
if (picture?.tagName === "PICTURE") {
|
|
2694
|
+
for (const source of picture.children) {
|
|
2695
|
+
if (source.tagName === "SOURCE") collect(source);
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
return urls;
|
|
2699
|
+
}
|
|
2700
|
+
function matchAsset(img, index, keys) {
|
|
2701
|
+
for (const url of urlsOf(img)) {
|
|
2702
|
+
let key = keys.get(url);
|
|
2703
|
+
if (key === void 0) {
|
|
2704
|
+
key = assetKey(url);
|
|
2705
|
+
keys.set(url, key);
|
|
2706
|
+
}
|
|
2707
|
+
const asset = key ? index.get(key) : void 0;
|
|
2708
|
+
if (asset) return asset;
|
|
2709
|
+
}
|
|
2710
|
+
return void 0;
|
|
2711
|
+
}
|
|
2712
|
+
function setIfChanged(el, name, value) {
|
|
2713
|
+
if (value === null) {
|
|
2714
|
+
if (el.hasAttribute(name)) el.removeAttribute(name);
|
|
2715
|
+
} else if (el.getAttribute(name) !== value) {
|
|
2716
|
+
el.setAttribute(name, value);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
function untagAssetImage(img) {
|
|
2720
|
+
for (const name of TAG_ATTRIBUTES) img.removeAttribute(name);
|
|
2721
|
+
}
|
|
2722
|
+
var TEXT_FIELD_SELECTOR = `[data-prepr-encoded]:not([${ASSET_ATTR}])`;
|
|
2723
|
+
function besideOtherItem(img, isCurrentItem) {
|
|
2724
|
+
for (let block = img.parentElement; block && block !== document.body; block = block.parentElement) {
|
|
2725
|
+
const texts = block.querySelectorAll(TEXT_FIELD_SELECTOR);
|
|
2726
|
+
if (texts.length === 0) continue;
|
|
2727
|
+
for (const text of texts) {
|
|
2728
|
+
if (isCurrentItem(text)) return false;
|
|
2729
|
+
}
|
|
2730
|
+
return true;
|
|
2731
|
+
}
|
|
2732
|
+
return false;
|
|
2733
|
+
}
|
|
2734
|
+
function tagAssetImages(index, itemId, keys, options = {}) {
|
|
2735
|
+
const { isCurrentItem } = options;
|
|
2736
|
+
for (const img of document.getElementsByTagName("img")) {
|
|
2737
|
+
const ours = img.hasAttribute(ASSET_ATTR);
|
|
2738
|
+
if (!ours && img.hasAttribute("data-prepr-encoded")) continue;
|
|
2739
|
+
const match = index.size ? matchAsset(img, index, keys) : void 0;
|
|
2740
|
+
const asset = match && isCurrentItem && besideOtherItem(img, isCurrentItem) ? void 0 : match;
|
|
2741
|
+
if (!asset) {
|
|
2742
|
+
if (ours) untagAssetImage(img);
|
|
2743
|
+
continue;
|
|
2744
|
+
}
|
|
2745
|
+
setIfChanged(img, "data-prepr-encoded", "");
|
|
2746
|
+
setIfChanged(img, ASSET_ATTR, "");
|
|
2747
|
+
setIfChanged(img, "data-prepr-id", itemId);
|
|
2748
|
+
setIfChanged(img, "data-prepr-field", asset.field);
|
|
2749
|
+
setIfChanged(img, "data-prepr-entry-id", asset.entryId ?? null);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
function untagAssetImages() {
|
|
2753
|
+
document.querySelectorAll(`img[${ASSET_ATTR}]`).forEach(untagAssetImage);
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
// src/core/visual-editor/navigation-guard.ts
|
|
2757
|
+
var debug7 = createScopedLogger("navigation-guard");
|
|
2758
|
+
var trimSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
2759
|
+
function resolve(url) {
|
|
2760
|
+
try {
|
|
2761
|
+
return new URL(url, document.baseURI);
|
|
2762
|
+
} catch {
|
|
2763
|
+
return null;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
function leavesPage(url) {
|
|
2767
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
2768
|
+
const here = window.location;
|
|
2769
|
+
return url.origin !== here.origin || trimSlash(url.pathname) !== trimSlash(here.pathname);
|
|
2770
|
+
}
|
|
2771
|
+
function leavingLinkHref(target) {
|
|
2772
|
+
if (!(target instanceof Element)) return null;
|
|
2773
|
+
const link = target.closest("a[href]");
|
|
2774
|
+
if (!link || link.hasAttribute("download")) return null;
|
|
2775
|
+
const linkTarget = link.getAttribute("target");
|
|
2776
|
+
if (linkTarget && linkTarget !== "_self") return null;
|
|
2777
|
+
const href = link.getAttribute("href") ?? "";
|
|
2778
|
+
if (href.startsWith("#")) return null;
|
|
2779
|
+
const url = resolve(href);
|
|
2780
|
+
return url && leavesPage(url) ? url.href : null;
|
|
2781
|
+
}
|
|
2782
|
+
function submitterAttr(submitter, attr) {
|
|
2783
|
+
if (!submitter?.hasAttribute(attr)) return null;
|
|
2784
|
+
if (attr === "formaction") return submitter.formAction;
|
|
2785
|
+
if (attr === "formmethod") return submitter.formMethod;
|
|
2786
|
+
return submitter.formTarget;
|
|
2787
|
+
}
|
|
2788
|
+
function submissionUrl(form, submitter) {
|
|
2789
|
+
const method = (submitterAttr(submitter, "formmethod") ?? form.method).toLowerCase();
|
|
2790
|
+
if (method === "dialog") return null;
|
|
2791
|
+
const target = submitterAttr(submitter, "formtarget") ?? form.target;
|
|
2792
|
+
if (target && target !== "_self") return null;
|
|
2793
|
+
const url = resolve(
|
|
2794
|
+
submitterAttr(submitter, "formaction") ?? (form.action || location.href)
|
|
2795
|
+
);
|
|
2796
|
+
if (!url) return null;
|
|
2797
|
+
if (method === "get") {
|
|
2798
|
+
let data;
|
|
2799
|
+
try {
|
|
2800
|
+
data = new FormData(form, submitter);
|
|
2801
|
+
} catch {
|
|
2802
|
+
data = new FormData(form);
|
|
2803
|
+
}
|
|
2804
|
+
const query = new URLSearchParams();
|
|
2805
|
+
data.forEach((value, key) => {
|
|
2806
|
+
query.append(key, typeof value === "string" ? value : value.name);
|
|
2807
|
+
});
|
|
2808
|
+
url.search = query.toString();
|
|
2809
|
+
}
|
|
2810
|
+
return url;
|
|
2811
|
+
}
|
|
2812
|
+
function createNavigationGuard(opts) {
|
|
2813
|
+
let active = false;
|
|
2814
|
+
let permitted = null;
|
|
2815
|
+
let pendingForm = null;
|
|
2816
|
+
let originalPush = null;
|
|
2817
|
+
let guardedPush = null;
|
|
2818
|
+
const isPermitted = (url) => permitted === url.href;
|
|
2819
|
+
const guarding = () => active && opts.showsEditedContent();
|
|
2820
|
+
const report = (href) => {
|
|
2821
|
+
debug7.log("blocked navigation to", href);
|
|
2822
|
+
opts.onBlocked(href);
|
|
2823
|
+
};
|
|
2824
|
+
const onSubmit = (e2) => {
|
|
2825
|
+
if (e2.defaultPrevented || !(e2.target instanceof HTMLFormElement)) return;
|
|
2826
|
+
if (!guarding()) return;
|
|
2827
|
+
const submitter = e2.submitter ?? null;
|
|
2828
|
+
const url = submissionUrl(e2.target, submitter);
|
|
2829
|
+
if (!url || !leavesPage(url) || isPermitted(url)) return;
|
|
2830
|
+
e2.preventDefault();
|
|
2831
|
+
pendingForm = { form: e2.target, submitter, href: url.href };
|
|
2832
|
+
report(url.href);
|
|
2833
|
+
};
|
|
2834
|
+
const onNavigate = (event) => {
|
|
2835
|
+
const e2 = event;
|
|
2836
|
+
if (e2.navigationType === "traverse" || e2.navigationType === "reload") {
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
if (!e2.cancelable || e2.hashChange || e2.downloadRequest !== null) return;
|
|
2840
|
+
if (!guarding()) return;
|
|
2841
|
+
if (e2.navigationType === "replace" && e2.destination.sameDocument) return;
|
|
2842
|
+
const url = resolve(e2.destination.url);
|
|
2843
|
+
if (!url || !leavesPage(url) || isPermitted(url)) return;
|
|
2844
|
+
e2.preventDefault();
|
|
2845
|
+
report(url.href);
|
|
2846
|
+
if (e2.destination.sameDocument) window.location.reload();
|
|
2847
|
+
};
|
|
2848
|
+
const navigation = () => window.navigation;
|
|
2849
|
+
function installPushGuard() {
|
|
2850
|
+
const original = history.pushState;
|
|
2851
|
+
const guard = function(data, unused, url) {
|
|
2852
|
+
if (guarding() && guardedPush === guard && url != null) {
|
|
2853
|
+
const target = resolve(url);
|
|
2854
|
+
if (target && leavesPage(target) && !isPermitted(target)) {
|
|
2855
|
+
report(target.href);
|
|
2856
|
+
window.location.reload();
|
|
2857
|
+
return;
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
return original.call(this, data, unused, url);
|
|
2861
|
+
};
|
|
2862
|
+
originalPush = original;
|
|
2863
|
+
guardedPush = guard;
|
|
2864
|
+
history.pushState = guard;
|
|
2865
|
+
}
|
|
2866
|
+
function uninstallPushGuard() {
|
|
2867
|
+
if (history.pushState === guardedPush && originalPush) {
|
|
2868
|
+
history.pushState = originalPush;
|
|
2869
|
+
}
|
|
2870
|
+
originalPush = null;
|
|
2871
|
+
guardedPush = null;
|
|
2872
|
+
}
|
|
2873
|
+
return {
|
|
2874
|
+
start() {
|
|
2875
|
+
if (active) return;
|
|
2876
|
+
active = true;
|
|
2877
|
+
installPushGuard();
|
|
2878
|
+
window.addEventListener("submit", onSubmit);
|
|
2879
|
+
navigation()?.addEventListener("navigate", onNavigate);
|
|
2880
|
+
},
|
|
2881
|
+
stop() {
|
|
2882
|
+
if (!active) return;
|
|
2883
|
+
active = false;
|
|
2884
|
+
uninstallPushGuard();
|
|
2885
|
+
window.removeEventListener("submit", onSubmit);
|
|
2886
|
+
navigation()?.removeEventListener("navigate", onNavigate);
|
|
2887
|
+
pendingForm = null;
|
|
2888
|
+
},
|
|
2889
|
+
isActive: guarding,
|
|
2890
|
+
block(href) {
|
|
2891
|
+
report(href);
|
|
2892
|
+
},
|
|
2893
|
+
permit(href) {
|
|
2894
|
+
permitted = resolve(href)?.href ?? null;
|
|
2895
|
+
},
|
|
2896
|
+
allow(href) {
|
|
2897
|
+
const url = resolve(href);
|
|
2898
|
+
if (!url || url.protocol !== "http:" && url.protocol !== "https:") {
|
|
2899
|
+
debug7.warn("ignored navigation to unsupported URL");
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2902
|
+
permitted = url.href;
|
|
2903
|
+
const form = pendingForm;
|
|
2904
|
+
pendingForm = null;
|
|
2905
|
+
if (form && form.href === url.href && form.form.isConnected) {
|
|
2906
|
+
try {
|
|
2907
|
+
form.form.requestSubmit(form.submitter ?? void 0);
|
|
2908
|
+
} catch {
|
|
2909
|
+
form.form.requestSubmit();
|
|
2910
|
+
}
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
window.location.assign(url.href);
|
|
2914
|
+
}
|
|
2915
|
+
};
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
// src/core/visual-editor/index.ts
|
|
2919
|
+
var debug8 = createScopedLogger("visual-editor");
|
|
2920
|
+
var ENCODED_SELECTOR2 = "[data-prepr-encoded]";
|
|
2921
|
+
var REFERENCE_ATTR = "data-prepr-reference";
|
|
2922
|
+
var REFERENCE_SELECTOR = `[${REFERENCE_ATTR}]`;
|
|
2923
|
+
var CURSOR_STYLE_MARKER = "data-prepr-visual-editor";
|
|
2924
|
+
var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}`;
|
|
2925
|
+
var SUSPENDED_ATTR = "data-prepr-ve-suspended";
|
|
2926
|
+
var FIND_RETRY_MS = 150;
|
|
2927
|
+
var FIND_RETRIES = 6;
|
|
2928
|
+
var MAX_CACHED_KEYS = 2e3;
|
|
2929
|
+
var LAYOUT_MUTATIONS = {
|
|
2930
|
+
childList: true,
|
|
2931
|
+
subtree: true,
|
|
2932
|
+
characterData: true,
|
|
2933
|
+
attributes: true,
|
|
2934
|
+
attributeFilter: [
|
|
2935
|
+
"class",
|
|
2936
|
+
"style",
|
|
2937
|
+
"hidden",
|
|
2938
|
+
"open",
|
|
2939
|
+
"data-prepr-encoded",
|
|
2940
|
+
// An image swapped by a carousel or lazy loader can show another asset.
|
|
2941
|
+
...ASSET_SOURCE_ATTRIBUTES
|
|
2942
|
+
]
|
|
2943
|
+
};
|
|
2944
|
+
function scrollIntoFrame(el) {
|
|
2945
|
+
const rect = el.getBoundingClientRect();
|
|
2946
|
+
const offset = Math.max(0, (window.innerHeight - rect.height) / 2);
|
|
2947
|
+
window.scrollTo({
|
|
2948
|
+
top: Math.max(0, window.scrollY + rect.top - offset),
|
|
2949
|
+
behavior: "smooth"
|
|
2950
|
+
});
|
|
2951
|
+
}
|
|
2952
|
+
var isVisible = (el) => typeof el.checkVisibility === "function" ? el.checkVisibility() : el.getClientRects().length > 0;
|
|
2953
|
+
function createVisualEditor(opts) {
|
|
2954
|
+
const overlay = new VisualEditorOverlay();
|
|
2955
|
+
let running = false;
|
|
2956
|
+
let currentItemId = null;
|
|
2957
|
+
let locale = opts.locale ?? "en";
|
|
2958
|
+
let suspended = false;
|
|
2959
|
+
let pointerFrame = null;
|
|
2960
|
+
let lastPointerTarget = null;
|
|
2961
|
+
let lastPointerX = 0;
|
|
2962
|
+
let lastPointerY = 0;
|
|
2963
|
+
let mutationFrame = null;
|
|
2964
|
+
let mutationObserver = null;
|
|
2965
|
+
let selection = null;
|
|
2966
|
+
let findTimeout = null;
|
|
2967
|
+
let settlePendingFind = null;
|
|
2968
|
+
let guardEnabled = false;
|
|
2969
|
+
let assetIndex = /* @__PURE__ */ new Map();
|
|
2970
|
+
const assetKeys = /* @__PURE__ */ new Map();
|
|
2971
|
+
const guard = createNavigationGuard({
|
|
2972
|
+
onBlocked: (href) => opts.onNavigationBlocked?.(href),
|
|
2973
|
+
showsEditedContent: () => showsEditedContent()
|
|
2974
|
+
});
|
|
2975
|
+
const encodedFrom = (node) => node instanceof Element ? node.closest(ENCODED_SELECTOR2) : null;
|
|
2976
|
+
const labelFor = (el) => fieldLabel(el.getAttribute("data-prepr-field"), locale);
|
|
2977
|
+
let references = null;
|
|
2978
|
+
let ownComponents = /* @__PURE__ */ new Set();
|
|
2979
|
+
const isCurrentItem = (el) => {
|
|
2980
|
+
if (currentItemId === null) return true;
|
|
2981
|
+
const id = el.getAttribute("data-prepr-id");
|
|
2982
|
+
return !id || id === currentItemId || ownComponents.has(id);
|
|
2983
|
+
};
|
|
2984
|
+
const showsEditedContent = () => {
|
|
2985
|
+
for (const el of document.querySelectorAll(ENCODED_SELECTOR2)) {
|
|
2986
|
+
if (isCurrentItem(el) && isVisible(el)) return true;
|
|
2987
|
+
}
|
|
2988
|
+
return false;
|
|
2989
|
+
};
|
|
2990
|
+
const linkedIdOf = (el) => {
|
|
2991
|
+
if (!references) return null;
|
|
2992
|
+
const id = el.getAttribute("data-prepr-id") ?? "";
|
|
2993
|
+
if (references.has(id)) return id;
|
|
2994
|
+
let container = el.closest(REFERENCE_SELECTOR);
|
|
2995
|
+
while (container) {
|
|
2996
|
+
const linked = container.getAttribute(REFERENCE_ATTR) ?? "";
|
|
2997
|
+
if (references.has(linked)) return linked;
|
|
2998
|
+
container = container.parentElement?.closest(REFERENCE_SELECTOR) ?? null;
|
|
2999
|
+
}
|
|
3000
|
+
return null;
|
|
3001
|
+
};
|
|
3002
|
+
const itemKind = (el) => {
|
|
3003
|
+
if (isCurrentItem(el)) return "current";
|
|
3004
|
+
if (!references) return "other";
|
|
3005
|
+
return linkedIdOf(el) ? "referenced" : null;
|
|
3006
|
+
};
|
|
3007
|
+
const referenceFieldOf = (el) => {
|
|
3008
|
+
const linked = linkedIdOf(el);
|
|
3009
|
+
return linked ? references?.get(linked) : void 0;
|
|
3010
|
+
};
|
|
3011
|
+
const hoverLabel = (el, kind) => {
|
|
3012
|
+
if (kind === "other") {
|
|
3013
|
+
return `${labelFor(el)} \xB7 ${t("visualEditor.otherItem", locale)}`;
|
|
3014
|
+
}
|
|
3015
|
+
if (kind === "referenced") {
|
|
3016
|
+
const via = referenceFieldOf(el);
|
|
3017
|
+
const prefix = via ? `${fieldLabel(via, locale)} \u203A ` : "";
|
|
3018
|
+
return `${prefix}${labelFor(el)} \xB7 ${t("visualEditor.linkedItem", locale)}`;
|
|
3019
|
+
}
|
|
3020
|
+
return labelFor(el);
|
|
3021
|
+
};
|
|
3022
|
+
const setSuspended = (value) => {
|
|
3023
|
+
if (suspended === value) return;
|
|
3024
|
+
suspended = value;
|
|
3025
|
+
document.documentElement.toggleAttribute(SUSPENDED_ATTR, value);
|
|
3026
|
+
if (value) overlay.setHover(null, "");
|
|
3027
|
+
};
|
|
3028
|
+
const updateHover = () => {
|
|
3029
|
+
pointerFrame = null;
|
|
3030
|
+
overlay.schedule();
|
|
3031
|
+
if (suspended) return;
|
|
3032
|
+
const encoded = encodedFrom(lastPointerTarget);
|
|
3033
|
+
const target = encoded === overlay.getSelectedTarget() ? null : encoded;
|
|
3034
|
+
if (!target) {
|
|
3035
|
+
overlay.setHover(null, "");
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
const kind = itemKind(target);
|
|
3039
|
+
if (!kind) {
|
|
3040
|
+
overlay.setHover(null, "");
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
overlay.setHover(target, hoverLabel(target, kind), { kind });
|
|
3044
|
+
};
|
|
3045
|
+
const handlePointerMove = (e2) => {
|
|
3046
|
+
setSuspended(e2.altKey);
|
|
3047
|
+
lastPointerTarget = e2.target;
|
|
3048
|
+
lastPointerX = e2.clientX;
|
|
3049
|
+
lastPointerY = e2.clientY;
|
|
3050
|
+
if (pointerFrame === null) {
|
|
3051
|
+
pointerFrame = requestAnimationFrame(updateHover);
|
|
3052
|
+
}
|
|
3053
|
+
};
|
|
3054
|
+
const handlePointerLeave = () => {
|
|
3055
|
+
lastPointerTarget = null;
|
|
3056
|
+
overlay.setHover(null, "");
|
|
3057
|
+
};
|
|
3058
|
+
const handleClick = (e2) => {
|
|
3059
|
+
if (e2.altKey) {
|
|
3060
|
+
const href = leavingLinkHref(e2.target);
|
|
3061
|
+
if (href && guard.isActive()) guard.permit(href);
|
|
3062
|
+
return;
|
|
3063
|
+
}
|
|
3064
|
+
const encoded = encodedFrom(e2.target);
|
|
3065
|
+
const kind = encoded ? itemKind(encoded) : null;
|
|
3066
|
+
if (!encoded || !kind) {
|
|
3067
|
+
clearSelection();
|
|
3068
|
+
if (e2.ctrlKey || e2.metaKey || e2.shiftKey || !showsEditedContent()) return;
|
|
3069
|
+
const href = leavingLinkHref(e2.target);
|
|
3070
|
+
if (!href) return;
|
|
3071
|
+
e2.preventDefault();
|
|
3072
|
+
if (guard.isActive()) {
|
|
3073
|
+
e2.stopPropagation();
|
|
3074
|
+
guard.block(href);
|
|
3075
|
+
}
|
|
3076
|
+
return;
|
|
3077
|
+
}
|
|
3078
|
+
e2.preventDefault();
|
|
3079
|
+
e2.stopPropagation();
|
|
3080
|
+
const item = listItemIn(encoded, e2.target);
|
|
3081
|
+
if (kind === "current")
|
|
3082
|
+
select(item?.element ?? encoded, encoded, item?.index);
|
|
3083
|
+
const referenceField = kind === "referenced" ? referenceFieldOf(encoded) : void 0;
|
|
3084
|
+
const linkedId = kind === "referenced" ? linkedIdOf(encoded) : null;
|
|
3085
|
+
opts.onEdit({
|
|
3086
|
+
...readEditPayload(encoded),
|
|
3087
|
+
...linkedId && { id: linkedId },
|
|
3088
|
+
...item && { block: item.index },
|
|
3089
|
+
...referenceField && { referenceField }
|
|
3090
|
+
});
|
|
3091
|
+
};
|
|
3092
|
+
const handleKey = (e2) => {
|
|
3093
|
+
if (e2.key === "Alt") setSuspended(e2.type === "keydown");
|
|
3094
|
+
if (e2.type === "keydown" && e2.key === "Escape") {
|
|
3095
|
+
clearSelection();
|
|
3096
|
+
}
|
|
3097
|
+
};
|
|
3098
|
+
const handleBlur = () => setSuspended(false);
|
|
3099
|
+
const handleScroll = () => overlay.onScroll();
|
|
3100
|
+
const handleResize = () => overlay.schedule();
|
|
3101
|
+
const handleSettled = () => overlay.schedule();
|
|
3102
|
+
function listItemIn(encoded, target) {
|
|
3103
|
+
if (!(target instanceof Element)) return null;
|
|
3104
|
+
const li = target.closest("li");
|
|
3105
|
+
if (!li || !encoded.contains(li) || !li.parentElement) return null;
|
|
3106
|
+
const items = Array.from(li.parentElement.children).filter(
|
|
3107
|
+
(child) => child.tagName === "LI"
|
|
3108
|
+
);
|
|
3109
|
+
return { element: li, index: items.indexOf(li) };
|
|
3110
|
+
}
|
|
3111
|
+
function nthListItem(encoded, block) {
|
|
3112
|
+
const list = encoded.querySelector("ul, ol");
|
|
3113
|
+
if (!list) return null;
|
|
3114
|
+
const items = Array.from(list.children).filter(
|
|
3115
|
+
(child) => child.tagName === "LI"
|
|
3116
|
+
);
|
|
3117
|
+
return items[block] ?? null;
|
|
3118
|
+
}
|
|
3119
|
+
function select(el, field, block) {
|
|
3120
|
+
const entryId = field.getAttribute("data-prepr-entry-id");
|
|
3121
|
+
selection = {
|
|
3122
|
+
lookup: {
|
|
3123
|
+
field: field.getAttribute("data-prepr-field") ?? "",
|
|
3124
|
+
id: field.getAttribute("data-prepr-id") ?? void 0,
|
|
3125
|
+
...entryId && { entryId },
|
|
3126
|
+
...block !== void 0 && { block }
|
|
3127
|
+
},
|
|
3128
|
+
label: field
|
|
3129
|
+
};
|
|
3130
|
+
overlay.setSelected(el, labelFor(field));
|
|
3131
|
+
if (overlay.getHoverTarget() === el) overlay.setHover(null, "");
|
|
3132
|
+
}
|
|
3133
|
+
function clearSelection() {
|
|
3134
|
+
selection = null;
|
|
3135
|
+
overlay.setSelected(null, "");
|
|
3136
|
+
}
|
|
3137
|
+
function reselect() {
|
|
3138
|
+
if (!selection) return;
|
|
3139
|
+
const current = overlay.getSelectedTarget();
|
|
3140
|
+
if (current?.isConnected && selection.label.isConnected) return;
|
|
3141
|
+
const found = findFieldElement(selection.lookup);
|
|
3142
|
+
if (!found || !isCurrentItem(found)) {
|
|
3143
|
+
overlay.setSelected(null, "");
|
|
3144
|
+
return;
|
|
3145
|
+
}
|
|
3146
|
+
const { block } = selection.lookup;
|
|
3147
|
+
const el = block !== void 0 ? nthListItem(found, block) ?? found : found;
|
|
3148
|
+
select(el, found, block);
|
|
3149
|
+
}
|
|
3150
|
+
function tagAssets() {
|
|
3151
|
+
if (assetKeys.size > MAX_CACHED_KEYS) assetKeys.clear();
|
|
3152
|
+
tagAssetImages(assetIndex, currentItemId, assetKeys, {
|
|
3153
|
+
isCurrentItem: (el) => isCurrentItem(el)
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
const handleLayoutFrame = () => {
|
|
3157
|
+
mutationFrame = null;
|
|
3158
|
+
if (assetIndex.size) tagAssets();
|
|
3159
|
+
reselect();
|
|
3160
|
+
const hovered = overlay.getHoverTarget();
|
|
3161
|
+
if (hovered && !hovered.isConnected) {
|
|
3162
|
+
lastPointerTarget = document.elementFromPoint(lastPointerX, lastPointerY);
|
|
3163
|
+
updateHover();
|
|
3164
|
+
return;
|
|
3165
|
+
}
|
|
3166
|
+
overlay.schedule();
|
|
3167
|
+
};
|
|
3168
|
+
const handleMutations = () => {
|
|
3169
|
+
if (mutationFrame !== null) return;
|
|
3170
|
+
if (!selection && !overlay.getHoverTarget() && !assetIndex.size) return;
|
|
3171
|
+
mutationFrame = requestAnimationFrame(handleLayoutFrame);
|
|
3172
|
+
};
|
|
3173
|
+
function injectCursorStyles() {
|
|
3174
|
+
if (document.querySelector(`style[${CURSOR_STYLE_MARKER}]`)) return;
|
|
3175
|
+
const style = document.createElement("style");
|
|
3176
|
+
style.setAttribute(CURSOR_STYLE_MARKER, "");
|
|
3177
|
+
style.textContent = CURSOR_STYLES;
|
|
3178
|
+
document.head.appendChild(style);
|
|
3179
|
+
}
|
|
3180
|
+
function start() {
|
|
3181
|
+
if (running) return;
|
|
3182
|
+
running = true;
|
|
3183
|
+
overlay.mount();
|
|
3184
|
+
injectCursorStyles();
|
|
3185
|
+
document.addEventListener("pointermove", handlePointerMove, {
|
|
3186
|
+
passive: true
|
|
3187
|
+
});
|
|
3188
|
+
document.documentElement.addEventListener(
|
|
3189
|
+
"pointerleave",
|
|
3190
|
+
handlePointerLeave
|
|
3191
|
+
);
|
|
3192
|
+
document.addEventListener("click", handleClick, { capture: true });
|
|
3193
|
+
window.addEventListener("keydown", handleKey);
|
|
3194
|
+
window.addEventListener("keyup", handleKey);
|
|
3195
|
+
window.addEventListener("blur", handleBlur);
|
|
3196
|
+
window.addEventListener("scroll", handleScroll, {
|
|
3197
|
+
capture: true,
|
|
3198
|
+
passive: true
|
|
3199
|
+
});
|
|
3200
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
3201
|
+
document.addEventListener("transitionend", handleSettled, {
|
|
3202
|
+
capture: true,
|
|
3203
|
+
passive: true
|
|
3204
|
+
});
|
|
3205
|
+
document.addEventListener("animationend", handleSettled, {
|
|
3206
|
+
capture: true,
|
|
3207
|
+
passive: true
|
|
3208
|
+
});
|
|
3209
|
+
mutationObserver = new MutationObserver(handleMutations);
|
|
3210
|
+
mutationObserver.observe(document.body, LAYOUT_MUTATIONS);
|
|
3211
|
+
if (assetIndex.size) tagAssets();
|
|
3212
|
+
syncGuard();
|
|
3213
|
+
debug8.log("visual editor started");
|
|
3214
|
+
}
|
|
3215
|
+
function stop() {
|
|
3216
|
+
if (!running) return;
|
|
3217
|
+
running = false;
|
|
3218
|
+
if (pointerFrame !== null) cancelAnimationFrame(pointerFrame);
|
|
3219
|
+
if (mutationFrame !== null) cancelAnimationFrame(mutationFrame);
|
|
3220
|
+
mutationObserver?.disconnect();
|
|
3221
|
+
mutationObserver = null;
|
|
3222
|
+
cancelFind();
|
|
3223
|
+
pointerFrame = null;
|
|
3224
|
+
mutationFrame = null;
|
|
3225
|
+
selection = null;
|
|
3226
|
+
lastPointerTarget = null;
|
|
3227
|
+
setSuspended(false);
|
|
3228
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
3229
|
+
document.documentElement.removeEventListener(
|
|
3230
|
+
"pointerleave",
|
|
3231
|
+
handlePointerLeave
|
|
3232
|
+
);
|
|
3233
|
+
document.removeEventListener("click", handleClick, { capture: true });
|
|
3234
|
+
window.removeEventListener("keydown", handleKey);
|
|
3235
|
+
window.removeEventListener("keyup", handleKey);
|
|
3236
|
+
window.removeEventListener("blur", handleBlur);
|
|
3237
|
+
window.removeEventListener("scroll", handleScroll, { capture: true });
|
|
3238
|
+
window.removeEventListener("resize", handleResize);
|
|
3239
|
+
document.removeEventListener("transitionend", handleSettled, {
|
|
3240
|
+
capture: true
|
|
3241
|
+
});
|
|
3242
|
+
document.removeEventListener("animationend", handleSettled, {
|
|
3243
|
+
capture: true
|
|
3244
|
+
});
|
|
3245
|
+
document.querySelectorAll(`style[${CURSOR_STYLE_MARKER}]`).forEach((node) => node.remove());
|
|
3246
|
+
overlay.unmount();
|
|
3247
|
+
untagAssetImages();
|
|
3248
|
+
guard.stop();
|
|
3249
|
+
debug8.log("visual editor stopped");
|
|
3250
|
+
}
|
|
3251
|
+
function cancelFind() {
|
|
3252
|
+
if (findTimeout) clearTimeout(findTimeout);
|
|
3253
|
+
findTimeout = null;
|
|
3254
|
+
settlePendingFind?.();
|
|
3255
|
+
settlePendingFind = null;
|
|
3256
|
+
}
|
|
3257
|
+
function focusField(lookup) {
|
|
3258
|
+
cancelFind();
|
|
3259
|
+
return new Promise((resolve2) => {
|
|
3260
|
+
let attempt = 0;
|
|
3261
|
+
const finish = (found) => {
|
|
3262
|
+
settlePendingFind = null;
|
|
3263
|
+
resolve2(found);
|
|
3264
|
+
};
|
|
3265
|
+
settlePendingFind = () => resolve2(false);
|
|
3266
|
+
const tryFind = () => {
|
|
3267
|
+
findTimeout = null;
|
|
3268
|
+
const found = findFieldElement(lookup);
|
|
3269
|
+
if (found) {
|
|
3270
|
+
const el = lookup.block !== void 0 ? nthListItem(found, lookup.block) ?? found : found;
|
|
3271
|
+
scrollIntoFrame(el);
|
|
3272
|
+
if (running && isCurrentItem(found)) select(el, found, lookup.block);
|
|
3273
|
+
finish(true);
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
if (++attempt >= FIND_RETRIES) {
|
|
3277
|
+
debug8.log("no element renders field", lookup.field);
|
|
3278
|
+
finish(false);
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3281
|
+
findTimeout = setTimeout(tryFind, FIND_RETRY_MS);
|
|
3282
|
+
};
|
|
3283
|
+
tryFind();
|
|
3284
|
+
});
|
|
3285
|
+
}
|
|
3286
|
+
function setCurrentItem(itemId, referenceList, componentIds) {
|
|
3287
|
+
currentItemId = itemId;
|
|
3288
|
+
if (referenceList) setReferences(referenceList, componentIds);
|
|
3289
|
+
else if (componentIds) ownComponents = new Set(componentIds);
|
|
3290
|
+
if (running && assetIndex.size) tagAssets();
|
|
3291
|
+
const selected = overlay.getSelectedTarget();
|
|
3292
|
+
const owner = selected ? encodedFrom(selected) : null;
|
|
3293
|
+
if (owner && !isCurrentItem(owner)) clearSelection();
|
|
3294
|
+
}
|
|
3295
|
+
function setAssets(assets) {
|
|
3296
|
+
const hadAssets = assetIndex.size > 0;
|
|
3297
|
+
assetIndex = createAssetIndex(assets);
|
|
3298
|
+
if (running && (hadAssets || assetIndex.size)) tagAssets();
|
|
3299
|
+
}
|
|
3300
|
+
function setReferences(referenceList, componentIds) {
|
|
3301
|
+
if (componentIds) ownComponents = new Set(componentIds);
|
|
3302
|
+
references = /* @__PURE__ */ new Map();
|
|
3303
|
+
for (const { id, field } of referenceList) {
|
|
3304
|
+
if (!references.has(id)) references.set(id, field);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
function setLocale(value) {
|
|
3308
|
+
locale = value;
|
|
3309
|
+
}
|
|
3310
|
+
function syncGuard() {
|
|
3311
|
+
if (running && guardEnabled) guard.start();
|
|
3312
|
+
else guard.stop();
|
|
3313
|
+
}
|
|
3314
|
+
function setNavigationGuard(enabled) {
|
|
3315
|
+
guardEnabled = enabled;
|
|
3316
|
+
syncGuard();
|
|
3317
|
+
}
|
|
3318
|
+
return {
|
|
3319
|
+
start,
|
|
3320
|
+
stop,
|
|
3321
|
+
setCurrentItem,
|
|
3322
|
+
setReferences,
|
|
3323
|
+
setAssets,
|
|
3324
|
+
setLocale,
|
|
3325
|
+
focusField,
|
|
3326
|
+
setNavigationGuard,
|
|
3327
|
+
allowNavigation: (href) => guard.allow(href)
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
|
|
2100
3331
|
// src/core/create-preview.ts
|
|
2101
|
-
var
|
|
3332
|
+
var debug9 = createScopedLogger("create-preview");
|
|
2102
3333
|
var controllerStores = /* @__PURE__ */ new WeakMap();
|
|
2103
3334
|
function defaultNavigation() {
|
|
2104
3335
|
return {
|
|
@@ -2140,7 +3371,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2140
3371
|
const search = new URLSearchParams(window.location.search);
|
|
2141
3372
|
const hideBar = search.get(PARAM_HIDE_BAR) === "true";
|
|
2142
3373
|
if (hideBar) {
|
|
2143
|
-
|
|
3374
|
+
debug9.log(`${PARAM_HIDE_BAR}=true \u2014 no visible toolbar`);
|
|
2144
3375
|
}
|
|
2145
3376
|
const mountUi = (options?.ui ?? true) && !isIframe && !hideBar;
|
|
2146
3377
|
const locale = resolveLocale(options);
|
|
@@ -2169,30 +3400,45 @@ function createPreprPreview(opts = {}) {
|
|
|
2169
3400
|
el.connect(store, (key) => t(key, locale));
|
|
2170
3401
|
}
|
|
2171
3402
|
const editingEnabled = features.editMode || isIframe;
|
|
2172
|
-
const
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
if (isIframe) {
|
|
2186
|
-
sendPreprEvent("field_edit_requested", {
|
|
2187
|
-
href: safeHref ?? void 0,
|
|
2188
|
-
origin,
|
|
2189
|
-
id,
|
|
2190
|
-
field
|
|
2191
|
-
});
|
|
2192
|
-
} else if (safeHref) {
|
|
2193
|
-
window.open(safeHref, "_blank", "noopener,noreferrer");
|
|
2194
|
-
}
|
|
3403
|
+
const onEdit = ({
|
|
3404
|
+
href,
|
|
3405
|
+
id,
|
|
3406
|
+
field,
|
|
3407
|
+
locale: contentLocale,
|
|
3408
|
+
block,
|
|
3409
|
+
entryId,
|
|
3410
|
+
referenceField
|
|
3411
|
+
}) => {
|
|
3412
|
+
const safeHref = href ? safeEditUrl(href) : null;
|
|
3413
|
+
if (href && !safeHref) {
|
|
3414
|
+
debug9.warn("ignored edit request with unsupported href scheme");
|
|
3415
|
+
return;
|
|
2195
3416
|
}
|
|
3417
|
+
if (isIframe) {
|
|
3418
|
+
sendPreprEvent("field_edit_requested", {
|
|
3419
|
+
id,
|
|
3420
|
+
field,
|
|
3421
|
+
locale: contentLocale,
|
|
3422
|
+
...block !== void 0 && { block },
|
|
3423
|
+
...entryId && { entryId },
|
|
3424
|
+
...referenceField && { referenceField }
|
|
3425
|
+
});
|
|
3426
|
+
} else if (safeHref) {
|
|
3427
|
+
window.open(safeHref, "_blank", "noopener,noreferrer");
|
|
3428
|
+
}
|
|
3429
|
+
};
|
|
3430
|
+
const visualEditor = isIframe ? createVisualEditor({
|
|
3431
|
+
onEdit,
|
|
3432
|
+
locale,
|
|
3433
|
+
onNavigationBlocked: (href) => sendPreprEvent("navigation_blocked", { href })
|
|
3434
|
+
}) : null;
|
|
3435
|
+
const stega = visualEditor ?? createStegaController({
|
|
3436
|
+
// The edit-mode marks and hover label are chrome, so they follow the
|
|
3437
|
+
// same decision as the bar: unwanted in a headless preview that asked
|
|
3438
|
+
// for no UI.
|
|
3439
|
+
tooltip: mountUi,
|
|
3440
|
+
onEdit,
|
|
3441
|
+
locale
|
|
2196
3442
|
});
|
|
2197
3443
|
const autoClean = createStegaAutoClean({
|
|
2198
3444
|
enabled: options?.autoClean
|
|
@@ -2214,7 +3460,35 @@ function createPreprPreview(opts = {}) {
|
|
|
2214
3460
|
handleChange(before, state);
|
|
2215
3461
|
});
|
|
2216
3462
|
const bridge = createIframeBridge(store, {
|
|
2217
|
-
allowedEditorOrigins: options?.allowedEditorOrigins
|
|
3463
|
+
allowedEditorOrigins: options?.allowedEditorOrigins,
|
|
3464
|
+
onHandshake: ({
|
|
3465
|
+
itemId,
|
|
3466
|
+
referencedIds,
|
|
3467
|
+
componentIds,
|
|
3468
|
+
uiLocale,
|
|
3469
|
+
navigationGuard,
|
|
3470
|
+
assets
|
|
3471
|
+
}) => {
|
|
3472
|
+
if (uiLocale) visualEditor?.setLocale(uiLocale);
|
|
3473
|
+
visualEditor?.setCurrentItem(itemId ?? null, referencedIds, componentIds);
|
|
3474
|
+
visualEditor?.setNavigationGuard(navigationGuard === true);
|
|
3475
|
+
if (assets) visualEditor?.setAssets(assets);
|
|
3476
|
+
},
|
|
3477
|
+
onAssets: (assets) => visualEditor?.setAssets(assets),
|
|
3478
|
+
onAllowNavigation: (href) => visualEditor?.allowNavigation(href),
|
|
3479
|
+
onReferences: (referencedIds, componentIds) => visualEditor?.setReferences(referencedIds, componentIds),
|
|
3480
|
+
// The editor selecting a field scrolls the matching element into view. The
|
|
3481
|
+
// lookup relies on the attributes the stega scan writes, so it finds
|
|
3482
|
+
// nothing until encoded content has been tagged.
|
|
3483
|
+
onScrollToField: ({ field, id, block, entryId, fieldById }) => {
|
|
3484
|
+
if (visualEditor) {
|
|
3485
|
+
void visualEditor.focusField({ field, id, block, entryId, fieldById });
|
|
3486
|
+
return;
|
|
3487
|
+
}
|
|
3488
|
+
if (!scrollToField({ field, id })) {
|
|
3489
|
+
debug9.log("no element on this page renders field", field);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
2218
3492
|
});
|
|
2219
3493
|
sendPreprEvent(
|
|
2220
3494
|
"getScrollPosition",
|
|
@@ -2234,7 +3508,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2234
3508
|
bridge.stop();
|
|
2235
3509
|
}
|
|
2236
3510
|
el?.remove();
|
|
2237
|
-
|
|
3511
|
+
debug9.log("preview destroyed");
|
|
2238
3512
|
}
|
|
2239
3513
|
const controller = { destroy };
|
|
2240
3514
|
controllerStores.set(controller, store);
|
|
@@ -2242,7 +3516,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2242
3516
|
}
|
|
2243
3517
|
|
|
2244
3518
|
// src/version.ts
|
|
2245
|
-
var VERSION = "0.
|
|
3519
|
+
var VERSION = "0.5.0-rc.10";
|
|
2246
3520
|
|
|
2247
3521
|
// src/core/pixel.ts
|
|
2248
3522
|
var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
|