@preprio/toolkit 0.5.0-rc.1 → 0.5.0-rc.11
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 +430 -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-4JNKYIEV.js → chunk-6423Q744.js} +1449 -642
- package/dist/chunk-AOHA5INA.js +26 -0
- package/dist/chunk-IPMB32AZ.js +246 -0
- package/dist/chunk-KD3GK6TO.js +1221 -0
- package/dist/chunk-QSXIHRH4.js +184 -0
- package/dist/chunk-S3VT4UWM.js +75 -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 +3367 -416
- package/dist/index.d.cts +276 -5
- package/dist/index.d.ts +276 -5
- package/dist/index.js +307 -144
- package/dist/live-preview-DLXGXRE5.js +18 -0
- package/dist/nextjs/components.cjs +1608 -386
- 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 +1608 -386
- package/dist/react/components.d.cts +17 -2
- package/dist/react/components.d.ts +17 -2
- package/dist/react/components.js +1608 -386
- 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
|
@@ -95,6 +95,40 @@ var ALL_FEATURES_ENABLED = Object.freeze({
|
|
|
95
95
|
editMode: true
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
+
// src/core/locales/de.json
|
|
99
|
+
var de_default = {
|
|
100
|
+
common: {
|
|
101
|
+
viewingAs: "Ansicht als:",
|
|
102
|
+
user: "Benutzer",
|
|
103
|
+
off: "Aus",
|
|
104
|
+
on: "An",
|
|
105
|
+
reset: "Zur\xFCcksetzen",
|
|
106
|
+
toolbar: "Symbolleiste"
|
|
107
|
+
},
|
|
108
|
+
adaptiveContent: {
|
|
109
|
+
adaptiveContent: "Adaptive Inhalte",
|
|
110
|
+
enablePreview: "Vorschaumodus",
|
|
111
|
+
segment: "Segment",
|
|
112
|
+
ABVariant: "A/B-Variante",
|
|
113
|
+
chooseSegment: "Segment w\xE4hlen",
|
|
114
|
+
searchSegments: "Segmente suchen",
|
|
115
|
+
none: "Keine Segmente",
|
|
116
|
+
allOtherUsers: "Alle anderen Benutzer",
|
|
117
|
+
offDescription: "Die Website als echter Benutzer ansehen (Cookies verwenden)",
|
|
118
|
+
onDescription: "Mit der Symbolleiste Segmente und A/B-Varianten simulieren"
|
|
119
|
+
},
|
|
120
|
+
editingTools: {
|
|
121
|
+
editingTools: "Bearbeitungswerkzeuge",
|
|
122
|
+
editMode: "Bearbeitungsmodus",
|
|
123
|
+
ariaCloseEditMode: "Bearbeitungsmodus schlie\xDFen"
|
|
124
|
+
},
|
|
125
|
+
visualEditor: {
|
|
126
|
+
edit: "Bearbeiten",
|
|
127
|
+
otherItem: "anderes Element",
|
|
128
|
+
linkedItem: "verkn\xFCpftes Element"
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
98
132
|
// src/core/locales/en.json
|
|
99
133
|
var en_default = {
|
|
100
134
|
common: {
|
|
@@ -121,6 +155,45 @@ var en_default = {
|
|
|
121
155
|
editingTools: "Editing tools",
|
|
122
156
|
editMode: "Edit mode",
|
|
123
157
|
ariaCloseEditMode: "Close edit mode"
|
|
158
|
+
},
|
|
159
|
+
visualEditor: {
|
|
160
|
+
edit: "Edit",
|
|
161
|
+
otherItem: "other item",
|
|
162
|
+
linkedItem: "linked item"
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// src/core/locales/fr.json
|
|
167
|
+
var fr_default = {
|
|
168
|
+
common: {
|
|
169
|
+
viewingAs: "Affichage en tant que :",
|
|
170
|
+
user: "Utilisateur",
|
|
171
|
+
off: "D\xE9sactiv\xE9",
|
|
172
|
+
on: "Activ\xE9",
|
|
173
|
+
reset: "R\xE9initialiser",
|
|
174
|
+
toolbar: "Barre d'outils"
|
|
175
|
+
},
|
|
176
|
+
adaptiveContent: {
|
|
177
|
+
adaptiveContent: "Contenu adaptatif",
|
|
178
|
+
enablePreview: "Mode aper\xE7u",
|
|
179
|
+
segment: "Segment",
|
|
180
|
+
ABVariant: "Variante A/B",
|
|
181
|
+
chooseSegment: "Choisir un segment",
|
|
182
|
+
searchSegments: "Rechercher des segments",
|
|
183
|
+
none: "Aucun segment",
|
|
184
|
+
allOtherUsers: "Tous les autres utilisateurs",
|
|
185
|
+
offDescription: "Voir le site comme un vrai utilisateur (utiliser les cookies)",
|
|
186
|
+
onDescription: "Utilisez la barre d'outils pour simuler des segments et des variantes A/B"
|
|
187
|
+
},
|
|
188
|
+
editingTools: {
|
|
189
|
+
editingTools: "Outils d'\xE9dition",
|
|
190
|
+
editMode: "Mode \xE9dition",
|
|
191
|
+
ariaCloseEditMode: "Fermer le mode \xE9dition"
|
|
192
|
+
},
|
|
193
|
+
visualEditor: {
|
|
194
|
+
edit: "Modifier",
|
|
195
|
+
otherItem: "autre \xE9l\xE9ment",
|
|
196
|
+
linkedItem: "\xE9l\xE9ment li\xE9"
|
|
124
197
|
}
|
|
125
198
|
};
|
|
126
199
|
|
|
@@ -150,13 +223,20 @@ var nl_default = {
|
|
|
150
223
|
editingTools: "Editing Tools",
|
|
151
224
|
editMode: "Edit mode",
|
|
152
225
|
ariaCloseEditMode: "Edit mode sluiten"
|
|
226
|
+
},
|
|
227
|
+
visualEditor: {
|
|
228
|
+
edit: "Bewerken",
|
|
229
|
+
otherItem: "ander item",
|
|
230
|
+
linkedItem: "gekoppeld item"
|
|
153
231
|
}
|
|
154
232
|
};
|
|
155
233
|
|
|
156
234
|
// src/core/i18n.ts
|
|
157
235
|
var dictionaries = {
|
|
158
236
|
en: en_default,
|
|
159
|
-
nl: nl_default
|
|
237
|
+
nl: nl_default,
|
|
238
|
+
fr: fr_default,
|
|
239
|
+
de: de_default
|
|
160
240
|
};
|
|
161
241
|
var LOCALES = Object.keys(dictionaries);
|
|
162
242
|
function isLocale(value) {
|
|
@@ -249,52 +329,53 @@ function createScopedLogger(scopeName) {
|
|
|
249
329
|
prefix: `[Prepr][${scopeName}]`
|
|
250
330
|
});
|
|
251
331
|
}
|
|
252
|
-
function throttle(func, delay) {
|
|
253
|
-
let timeoutId = null;
|
|
254
|
-
let lastExecTime = 0;
|
|
255
|
-
const throttledFunc = ((...args) => {
|
|
256
|
-
const currentTime = Date.now();
|
|
257
|
-
const timeSinceLastExec = currentTime - lastExecTime;
|
|
258
|
-
if (timeSinceLastExec >= delay) {
|
|
259
|
-
func(...args);
|
|
260
|
-
lastExecTime = currentTime;
|
|
261
|
-
} else {
|
|
262
|
-
if (timeoutId) {
|
|
263
|
-
clearTimeout(timeoutId);
|
|
264
|
-
}
|
|
265
|
-
timeoutId = setTimeout(() => {
|
|
266
|
-
func(...args);
|
|
267
|
-
lastExecTime = Date.now();
|
|
268
|
-
timeoutId = null;
|
|
269
|
-
}, delay - timeSinceLastExec);
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
throttledFunc.cancel = () => {
|
|
273
|
-
if (timeoutId) {
|
|
274
|
-
clearTimeout(timeoutId);
|
|
275
|
-
timeoutId = null;
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
return throttledFunc;
|
|
279
|
-
}
|
|
280
|
-
function createElementCache(query, ttl = 1e3) {
|
|
281
|
-
let cache = null;
|
|
282
|
-
let lastCacheTime = 0;
|
|
283
|
-
return () => {
|
|
284
|
-
const now = Date.now();
|
|
285
|
-
if (!cache || now - lastCacheTime > ttl) {
|
|
286
|
-
cache = document.querySelectorAll(query);
|
|
287
|
-
lastCacheTime = now;
|
|
288
|
-
}
|
|
289
|
-
return cache;
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
332
|
|
|
293
333
|
// src/core/iframe-bridge.ts
|
|
294
334
|
var debug = createScopedLogger("iframe-bridge");
|
|
335
|
+
var MAX_REFERENCES = 500;
|
|
336
|
+
var MAX_HREF_LENGTH = 8192;
|
|
337
|
+
var MAX_ASSETS = 500;
|
|
338
|
+
var MAX_ASSET_KEY_LENGTH = 2048;
|
|
339
|
+
var MAX_ASSET_FIELD_LENGTH = 512;
|
|
340
|
+
function parseIds(value) {
|
|
341
|
+
if (!Array.isArray(value)) return void 0;
|
|
342
|
+
return value.slice(0, MAX_REFERENCES).filter((id) => typeof id === "string" && id.length > 0);
|
|
343
|
+
}
|
|
344
|
+
function parseReferences(value) {
|
|
345
|
+
if (!Array.isArray(value)) return void 0;
|
|
346
|
+
const list = [];
|
|
347
|
+
for (const entry of value.slice(0, MAX_REFERENCES)) {
|
|
348
|
+
const id = entry?.id;
|
|
349
|
+
const field = entry?.field;
|
|
350
|
+
if (typeof id === "string" && id && typeof field === "string" && field) {
|
|
351
|
+
list.push({ id, field });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return list;
|
|
355
|
+
}
|
|
356
|
+
function boundedString(value, max) {
|
|
357
|
+
return typeof value === "string" && value && value.length <= max ? value : void 0;
|
|
358
|
+
}
|
|
359
|
+
function parseAssets(value) {
|
|
360
|
+
if (!Array.isArray(value)) return void 0;
|
|
361
|
+
const list = [];
|
|
362
|
+
for (const entry of value.slice(0, MAX_ASSETS)) {
|
|
363
|
+
if (!entry || typeof entry !== "object") continue;
|
|
364
|
+
const raw = entry;
|
|
365
|
+
const key = boundedString(raw.key, MAX_ASSET_KEY_LENGTH);
|
|
366
|
+
const field = boundedString(raw.field, MAX_ASSET_FIELD_LENGTH);
|
|
367
|
+
if (!key || !field) continue;
|
|
368
|
+
const entryId = boundedString(raw.entryId, MAX_ASSET_FIELD_LENGTH);
|
|
369
|
+
list.push(entryId ? { key, field, entryId } : { key, field });
|
|
370
|
+
}
|
|
371
|
+
return list;
|
|
372
|
+
}
|
|
295
373
|
var EDITOR_BASE_DOMAIN = "prepr.io";
|
|
296
374
|
function isAllowedEditorOrigin(origin, allowed) {
|
|
297
|
-
if (allowed)
|
|
375
|
+
if (allowed) {
|
|
376
|
+
if (allowed.includes("*")) return origin !== "null";
|
|
377
|
+
return allowed.includes(origin);
|
|
378
|
+
}
|
|
298
379
|
let url;
|
|
299
380
|
try {
|
|
300
381
|
url = new URL(origin);
|
|
@@ -310,6 +391,10 @@ function isAllowedEditorOrigin(origin, allowed) {
|
|
|
310
391
|
function createIframeBridge(store, options = {}) {
|
|
311
392
|
const allowedOrigins = options.allowedEditorOrigins;
|
|
312
393
|
const onScrollToField = options.onScrollToField;
|
|
394
|
+
const onHandshake = options.onHandshake;
|
|
395
|
+
const onReferences = options.onReferences;
|
|
396
|
+
const onAssets = options.onAssets;
|
|
397
|
+
const onAllowNavigation = options.onAllowNavigation;
|
|
313
398
|
let parentOrigin = null;
|
|
314
399
|
const onKeyDown = (event) => {
|
|
315
400
|
const key = event.key.toLowerCase();
|
|
@@ -319,7 +404,10 @@ function createIframeBridge(store, options = {}) {
|
|
|
319
404
|
const onMessage = (evt) => {
|
|
320
405
|
const data = evt?.data;
|
|
321
406
|
if (data?.event === "prepr:initVE" && !parentOrigin) {
|
|
322
|
-
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins))
|
|
407
|
+
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins)) {
|
|
408
|
+
debug.warn("ignored editor handshake from origin", evt.origin);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
323
411
|
parentOrigin = evt.origin;
|
|
324
412
|
setTrustedParentOrigin(parentOrigin);
|
|
325
413
|
if (data.scrollPosition != null) {
|
|
@@ -327,8 +415,43 @@ function createIframeBridge(store, options = {}) {
|
|
|
327
415
|
setTimeout(() => window.scrollTo(0, top), 1);
|
|
328
416
|
}
|
|
329
417
|
store?.set({ previewMode: true, editMode: data.editMode ?? true });
|
|
418
|
+
const itemId = typeof data.itemId === "string" && data.itemId ? data.itemId : void 0;
|
|
419
|
+
const referencedIds = parseReferences(data.referencedIds);
|
|
420
|
+
const componentIds = parseIds(data.componentIds);
|
|
421
|
+
const uiLocale = isLocale(data.uiLocale) ? data.uiLocale : void 0;
|
|
422
|
+
const navigationGuard = data.navigationGuard === true;
|
|
423
|
+
const assets = parseAssets(data.assets);
|
|
424
|
+
try {
|
|
425
|
+
onHandshake?.({
|
|
426
|
+
...itemId && { itemId },
|
|
427
|
+
...referencedIds && { referencedIds },
|
|
428
|
+
...componentIds && { componentIds },
|
|
429
|
+
...uiLocale && { uiLocale },
|
|
430
|
+
...navigationGuard && { navigationGuard },
|
|
431
|
+
...assets && { assets }
|
|
432
|
+
});
|
|
433
|
+
} catch (error) {
|
|
434
|
+
debug.warn("handshake handler failed", error);
|
|
435
|
+
}
|
|
330
436
|
}
|
|
331
437
|
if (!parentOrigin || evt.origin !== parentOrigin) return;
|
|
438
|
+
if (data?.event === "prepr:references") {
|
|
439
|
+
const assets = parseAssets(data.assets);
|
|
440
|
+
if (assets) {
|
|
441
|
+
try {
|
|
442
|
+
onAssets?.(assets);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
debug.warn("assets handler failed", error);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const referencedIds = parseReferences(data.referencedIds);
|
|
448
|
+
if (!referencedIds) return;
|
|
449
|
+
try {
|
|
450
|
+
onReferences?.(referencedIds, parseIds(data.componentIds));
|
|
451
|
+
} catch (error) {
|
|
452
|
+
debug.warn("references handler failed", error);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
332
455
|
if (data?.event === "prepr:getScrollPosition") {
|
|
333
456
|
const currentScrollY = window.scrollY || document.documentElement.scrollTop;
|
|
334
457
|
sendPreprEvent("getScrollPosition", { value: currentScrollY });
|
|
@@ -336,12 +459,32 @@ function createIframeBridge(store, options = {}) {
|
|
|
336
459
|
if (data?.event === "prepr:scrollToField") {
|
|
337
460
|
if (typeof data.field !== "string" || !data.field) return;
|
|
338
461
|
if (data.id !== void 0 && typeof data.id !== "string") return;
|
|
462
|
+
const block = typeof data.block === "number" && Number.isInteger(data.block) ? data.block : void 0;
|
|
463
|
+
const entryId = typeof data.entryId === "string" && data.entryId ? data.entryId : void 0;
|
|
464
|
+
const fieldById = typeof data.fieldById === "string" && data.fieldById ? data.fieldById : void 0;
|
|
339
465
|
try {
|
|
340
|
-
onScrollToField?.({
|
|
466
|
+
onScrollToField?.({
|
|
467
|
+
field: data.field,
|
|
468
|
+
id: data.id,
|
|
469
|
+
block,
|
|
470
|
+
entryId,
|
|
471
|
+
fieldById
|
|
472
|
+
});
|
|
341
473
|
} catch (error) {
|
|
342
474
|
debug.warn("scrollToField handler failed", error);
|
|
343
475
|
}
|
|
344
476
|
}
|
|
477
|
+
if (data?.event === "prepr:allowNavigation") {
|
|
478
|
+
const href = data.href;
|
|
479
|
+
if (typeof href !== "string" || !href || href.length > MAX_HREF_LENGTH) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
onAllowNavigation?.(href);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
debug.warn("allowNavigation handler failed", error);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
345
488
|
};
|
|
346
489
|
return {
|
|
347
490
|
start() {
|
|
@@ -393,6 +536,7 @@ function decodeStega(str) {
|
|
|
393
536
|
|
|
394
537
|
// src/core/stega/dom.ts
|
|
395
538
|
var IGNORED_ANCESTORS = "script, style, noscript";
|
|
539
|
+
var AUTO_ENTRY_ATTR = "data-prepr-entry-auto";
|
|
396
540
|
function isIgnoredTextNode(node) {
|
|
397
541
|
if (node.parentElement?.closest(IGNORED_ANCESTORS)) return true;
|
|
398
542
|
return !node.textContent?.trim();
|
|
@@ -426,7 +570,9 @@ function parseEditHref(href) {
|
|
|
426
570
|
return {
|
|
427
571
|
id: url.pathname.split("/").pop() || void 0,
|
|
428
572
|
field: url.searchParams.get("field") || void 0,
|
|
429
|
-
locale: url.searchParams.get("locale") || void 0
|
|
573
|
+
locale: url.searchParams.get("locale") || void 0,
|
|
574
|
+
// Added by the toolkit's `annotateEditLinks`, not by Prepr.
|
|
575
|
+
entry: url.searchParams.get("entry") || void 0
|
|
430
576
|
};
|
|
431
577
|
} catch {
|
|
432
578
|
return {};
|
|
@@ -440,45 +586,68 @@ function setOrRemove(element, name, value) {
|
|
|
440
586
|
}
|
|
441
587
|
}
|
|
442
588
|
function tagEncodedElement(element, decoded) {
|
|
589
|
+
if (isSiteTagged(element)) return false;
|
|
443
590
|
element.setAttribute("data-prepr-encoded", "");
|
|
444
591
|
element.setAttribute("data-prepr-href", decoded.href);
|
|
445
592
|
element.setAttribute("data-prepr-origin", decoded.origin);
|
|
446
|
-
const { id, field, locale } = parseEditHref(decoded.href);
|
|
593
|
+
const { id, field, locale, entry } = parseEditHref(decoded.href);
|
|
447
594
|
setOrRemove(element, "data-prepr-id", id);
|
|
448
|
-
|
|
595
|
+
const fieldHint = element.getAttribute("data-prepr-edit-target")?.trim() || void 0;
|
|
596
|
+
setOrRemove(element, "data-prepr-field", field ?? fieldHint);
|
|
449
597
|
setOrRemove(element, "data-prepr-locale", locale);
|
|
598
|
+
const siteEntry = element.hasAttribute("data-prepr-entry-id") && !element.hasAttribute(AUTO_ENTRY_ATTR);
|
|
599
|
+
if (!siteEntry) {
|
|
600
|
+
setOrRemove(element, "data-prepr-entry-id", entry);
|
|
601
|
+
setOrRemove(element, AUTO_ENTRY_ATTR, entry && "true");
|
|
602
|
+
}
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
function isSiteTagged(element) {
|
|
606
|
+
return element.hasAttribute("data-prepr-encoded") && !element.hasAttribute("data-prepr-href");
|
|
607
|
+
}
|
|
608
|
+
function readEditPayload(element) {
|
|
609
|
+
return {
|
|
610
|
+
href: element.getAttribute("data-prepr-href") ?? "",
|
|
611
|
+
origin: element.getAttribute("data-prepr-origin") ?? "",
|
|
612
|
+
id: element.getAttribute("data-prepr-id") ?? void 0,
|
|
613
|
+
field: element.getAttribute("data-prepr-field") ?? void 0,
|
|
614
|
+
locale: element.getAttribute("data-prepr-locale") ?? void 0,
|
|
615
|
+
entryId: element.getAttribute("data-prepr-entry-id") || void 0
|
|
616
|
+
};
|
|
450
617
|
}
|
|
451
618
|
|
|
452
619
|
// src/core/stega/auto-clean.ts
|
|
453
620
|
var debug3 = createScopedLogger("stega:auto-clean");
|
|
621
|
+
var cleanedNodes = /* @__PURE__ */ new WeakMap();
|
|
454
622
|
function createStegaAutoClean(options) {
|
|
455
623
|
const stripText = options?.enabled ?? true;
|
|
456
624
|
let observer = null;
|
|
457
625
|
let processing = false;
|
|
458
|
-
let cleaned = /* @__PURE__ */ new WeakMap();
|
|
459
626
|
let pendingHidden = /* @__PURE__ */ new Set();
|
|
460
627
|
let pendingDirty = /* @__PURE__ */ new Set();
|
|
461
628
|
let debounceTimeout = null;
|
|
462
629
|
function cleanTextNode(textNode) {
|
|
463
630
|
if (isIgnoredTextNode(textNode)) return false;
|
|
464
631
|
const textContent = textNode.textContent;
|
|
465
|
-
const info =
|
|
632
|
+
const info = cleanedNodes.get(textNode);
|
|
466
633
|
const settled = stripText ? info?.cleaned : info?.original;
|
|
467
634
|
if (info && textContent === settled) {
|
|
468
635
|
if (!info.decoded) return false;
|
|
469
636
|
const shown = resolveEditTarget(textNode);
|
|
470
|
-
if (!shown)
|
|
637
|
+
if (!shown) {
|
|
638
|
+
pendingHidden.add(textNode);
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
471
641
|
pendingHidden.delete(textNode);
|
|
472
642
|
if (shown.hasAttribute("data-prepr-encoded")) return false;
|
|
473
|
-
tagEncodedElement(shown, info.decoded);
|
|
474
|
-
return true;
|
|
643
|
+
return tagEncodedElement(shown, info.decoded);
|
|
475
644
|
}
|
|
476
645
|
try {
|
|
477
646
|
const { cleaned: stripped, encoded } = (0, import_stega2.vercelStegaSplit)(textContent);
|
|
478
647
|
if (!encoded || stripped === textContent) return false;
|
|
479
648
|
const decoded = decodeStega(textContent);
|
|
480
649
|
if (!decoded?.href && !stripText) return false;
|
|
481
|
-
|
|
650
|
+
cleanedNodes.set(textNode, {
|
|
482
651
|
original: textContent,
|
|
483
652
|
cleaned: stripped,
|
|
484
653
|
decoded: decoded?.href ? decoded : null
|
|
@@ -488,8 +657,7 @@ function createStegaAutoClean(options) {
|
|
|
488
657
|
const target = resolveEditTarget(textNode);
|
|
489
658
|
if (target) {
|
|
490
659
|
pendingHidden.delete(textNode);
|
|
491
|
-
tagEncodedElement(target, decoded);
|
|
492
|
-
return true;
|
|
660
|
+
return tagEncodedElement(target, decoded);
|
|
493
661
|
}
|
|
494
662
|
pendingHidden.add(textNode);
|
|
495
663
|
} catch (error) {
|
|
@@ -520,7 +688,7 @@ function createStegaAutoClean(options) {
|
|
|
520
688
|
}
|
|
521
689
|
if (mutation.type === "characterData" && mutation.target.nodeType === Node.TEXT_NODE) {
|
|
522
690
|
const textNode = mutation.target;
|
|
523
|
-
const info =
|
|
691
|
+
const info = cleanedNodes.get(textNode);
|
|
524
692
|
const text = textNode.textContent ?? "";
|
|
525
693
|
const settled = stripText ? info?.cleaned : info?.original;
|
|
526
694
|
if (text !== settled && (0, import_stega2.vercelStegaSplit)(text).encoded) {
|
|
@@ -563,7 +731,16 @@ function createStegaAutoClean(options) {
|
|
|
563
731
|
retryPendingHidden();
|
|
564
732
|
}, 50);
|
|
565
733
|
}
|
|
734
|
+
let resizeTimeout = null;
|
|
735
|
+
function handleResize() {
|
|
736
|
+
if (pendingHidden.size === 0 || resizeTimeout) return;
|
|
737
|
+
resizeTimeout = setTimeout(() => {
|
|
738
|
+
resizeTimeout = null;
|
|
739
|
+
retryPendingHidden();
|
|
740
|
+
}, 100);
|
|
741
|
+
}
|
|
566
742
|
function setupObserver() {
|
|
743
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
567
744
|
observer = new MutationObserver(handleMutations);
|
|
568
745
|
observer.observe(document.body, {
|
|
569
746
|
childList: true,
|
|
@@ -586,11 +763,15 @@ function createStegaAutoClean(options) {
|
|
|
586
763
|
clearTimeout(debounceTimeout);
|
|
587
764
|
debounceTimeout = null;
|
|
588
765
|
}
|
|
766
|
+
if (resizeTimeout) {
|
|
767
|
+
clearTimeout(resizeTimeout);
|
|
768
|
+
resizeTimeout = null;
|
|
769
|
+
}
|
|
589
770
|
if (observer) {
|
|
590
771
|
observer.disconnect();
|
|
591
772
|
observer = null;
|
|
773
|
+
window.removeEventListener("resize", handleResize);
|
|
592
774
|
}
|
|
593
|
-
cleaned = /* @__PURE__ */ new WeakMap();
|
|
594
775
|
pendingHidden = /* @__PURE__ */ new Set();
|
|
595
776
|
pendingDirty = /* @__PURE__ */ new Set();
|
|
596
777
|
debug3.log("stega auto-clean stopped");
|
|
@@ -598,6 +779,439 @@ function createStegaAutoClean(options) {
|
|
|
598
779
|
return { start, stop };
|
|
599
780
|
}
|
|
600
781
|
|
|
782
|
+
// src/core/visual-editor/overlay.ts
|
|
783
|
+
var HOST_TAG = "prepr-visual-editor";
|
|
784
|
+
var SCROLL_SETTLE_MS = 120;
|
|
785
|
+
var LABEL_SPACE_PX = 22;
|
|
786
|
+
var BOX_PADDING_PX = 3;
|
|
787
|
+
var LABEL_MARGIN_PX = 4;
|
|
788
|
+
var PROBE_POINTS = [
|
|
789
|
+
[0.5, 0.5],
|
|
790
|
+
[0.15, 0.2],
|
|
791
|
+
[0.85, 0.2],
|
|
792
|
+
[0.15, 0.8],
|
|
793
|
+
[0.85, 0.8]
|
|
794
|
+
];
|
|
795
|
+
function overlapsBox(area, rect) {
|
|
796
|
+
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;
|
|
797
|
+
}
|
|
798
|
+
function overlaps(a2, b2) {
|
|
799
|
+
return a2.left < b2.right && b2.left < a2.right && a2.top < b2.bottom && b2.top < a2.bottom;
|
|
800
|
+
}
|
|
801
|
+
function coverOf(hit, target, x2, y2) {
|
|
802
|
+
let cover = hit.getBoundingClientRect();
|
|
803
|
+
for (let el = hit.parentElement; el && !el.contains(target) && el !== document.body; el = el.parentElement) {
|
|
804
|
+
const r2 = el.getBoundingClientRect();
|
|
805
|
+
if (x2 < r2.left || x2 >= r2.right || y2 < r2.top || y2 >= r2.bottom) break;
|
|
806
|
+
cover = r2;
|
|
807
|
+
}
|
|
808
|
+
return cover;
|
|
809
|
+
}
|
|
810
|
+
function mergeAreas(areas) {
|
|
811
|
+
const merged = areas.slice();
|
|
812
|
+
for (let i2 = 0; i2 < merged.length; i2++) {
|
|
813
|
+
for (let j2 = i2 + 1; j2 < merged.length; j2++) {
|
|
814
|
+
if (!overlaps(merged[i2], merged[j2])) continue;
|
|
815
|
+
merged[i2] = {
|
|
816
|
+
left: Math.min(merged[i2].left, merged[j2].left),
|
|
817
|
+
top: Math.min(merged[i2].top, merged[j2].top),
|
|
818
|
+
right: Math.max(merged[i2].right, merged[j2].right),
|
|
819
|
+
bottom: Math.max(merged[i2].bottom, merged[j2].bottom)
|
|
820
|
+
};
|
|
821
|
+
merged.splice(j2, 1);
|
|
822
|
+
j2 = i2;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return merged;
|
|
826
|
+
}
|
|
827
|
+
function findCovers(target, rect) {
|
|
828
|
+
const width = window.innerWidth;
|
|
829
|
+
const height = window.innerHeight;
|
|
830
|
+
const covers = [];
|
|
831
|
+
const seen = /* @__PURE__ */ new Set();
|
|
832
|
+
let probed = false;
|
|
833
|
+
let reached = false;
|
|
834
|
+
for (const [fx, fy] of PROBE_POINTS) {
|
|
835
|
+
const x2 = rect.left + rect.width * fx;
|
|
836
|
+
const y2 = rect.top + rect.height * fy;
|
|
837
|
+
if (x2 < 0 || y2 < 0 || x2 >= width || y2 >= height) continue;
|
|
838
|
+
probed = true;
|
|
839
|
+
const hit = document.elementFromPoint(x2, y2);
|
|
840
|
+
if (!hit || hit === target || target.contains(hit)) {
|
|
841
|
+
reached = true;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (seen.has(hit)) continue;
|
|
845
|
+
seen.add(hit);
|
|
846
|
+
if (covers.some(
|
|
847
|
+
(c2) => x2 >= c2.left && x2 < c2.right && y2 >= c2.top && y2 < c2.bottom
|
|
848
|
+
)) {
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
covers.push(coverOf(hit, target, x2, y2));
|
|
852
|
+
}
|
|
853
|
+
if (!probed) return [];
|
|
854
|
+
return reached ? mergeAreas(covers) : null;
|
|
855
|
+
}
|
|
856
|
+
function clipPath(x2, y2, covers) {
|
|
857
|
+
const far = 1e5;
|
|
858
|
+
const points = [
|
|
859
|
+
`${-far}px ${-far}px`,
|
|
860
|
+
`${far}px ${-far}px`,
|
|
861
|
+
`${far}px ${far}px`,
|
|
862
|
+
`${-far}px ${far}px`,
|
|
863
|
+
`${-far}px ${-far}px`
|
|
864
|
+
];
|
|
865
|
+
for (const c2 of covers) {
|
|
866
|
+
const l2 = c2.left - x2;
|
|
867
|
+
const t3 = c2.top - y2;
|
|
868
|
+
const r2 = c2.right - x2;
|
|
869
|
+
const b2 = c2.bottom - y2;
|
|
870
|
+
points.push(
|
|
871
|
+
`${l2}px ${t3}px`,
|
|
872
|
+
`${r2}px ${t3}px`,
|
|
873
|
+
`${r2}px ${b2}px`,
|
|
874
|
+
`${l2}px ${b2}px`,
|
|
875
|
+
`${l2}px ${t3}px`,
|
|
876
|
+
`${-far}px ${-far}px`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
return `polygon(evenodd, ${points.join(", ")})`;
|
|
880
|
+
}
|
|
881
|
+
var STYLES = `
|
|
882
|
+
:host {
|
|
883
|
+
all: initial;
|
|
884
|
+
position: fixed;
|
|
885
|
+
inset: 0;
|
|
886
|
+
pointer-events: none;
|
|
887
|
+
z-index: 2147483646;
|
|
888
|
+
contain: strict;
|
|
889
|
+
}
|
|
890
|
+
.box {
|
|
891
|
+
position: absolute;
|
|
892
|
+
top: 0;
|
|
893
|
+
left: 0;
|
|
894
|
+
box-sizing: border-box;
|
|
895
|
+
border-radius: 6px;
|
|
896
|
+
opacity: 0;
|
|
897
|
+
will-change: transform, width, height;
|
|
898
|
+
transition:
|
|
899
|
+
transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
900
|
+
width 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
901
|
+
height 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
902
|
+
opacity 140ms ease;
|
|
903
|
+
}
|
|
904
|
+
.box[data-visible] { opacity: 1; }
|
|
905
|
+
.box[data-instant], :host([data-scrolling]) .box {
|
|
906
|
+
transition: opacity 140ms ease;
|
|
907
|
+
}
|
|
908
|
+
.hover {
|
|
909
|
+
border: 1.5px solid rgba(79, 70, 229, 0.9);
|
|
910
|
+
background: rgba(79, 70, 229, 0.06);
|
|
911
|
+
}
|
|
912
|
+
.selected {
|
|
913
|
+
border: 2px solid #4f46e5;
|
|
914
|
+
box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.18);
|
|
915
|
+
}
|
|
916
|
+
.label {
|
|
917
|
+
position: absolute;
|
|
918
|
+
left: -2px;
|
|
919
|
+
bottom: 100%;
|
|
920
|
+
margin-bottom: 4px;
|
|
921
|
+
padding: 3px 7px;
|
|
922
|
+
border-radius: 5px;
|
|
923
|
+
background: #4f46e5;
|
|
924
|
+
color: #fff;
|
|
925
|
+
font: 500 11px/1.3 ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
926
|
+
letter-spacing: 0.01em;
|
|
927
|
+
white-space: nowrap;
|
|
928
|
+
display: flex;
|
|
929
|
+
max-width: 240px;
|
|
930
|
+
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.25);
|
|
931
|
+
}
|
|
932
|
+
.text {
|
|
933
|
+
min-width: 0;
|
|
934
|
+
overflow: hidden;
|
|
935
|
+
text-overflow: ellipsis;
|
|
936
|
+
}
|
|
937
|
+
.hover .label { background: rgba(79, 70, 229, 0.92); }
|
|
938
|
+
/* Content of another item than the one open in the editor: still clickable,
|
|
939
|
+
but visibly not part of the item being edited. */
|
|
940
|
+
.hover[data-item-kind='other'] {
|
|
941
|
+
border: 1.5px dashed #a5b4fc;
|
|
942
|
+
background: rgba(100, 116, 139, 0.05);
|
|
943
|
+
}
|
|
944
|
+
.hover[data-item-kind='other'] .label { background: #64748b; }
|
|
945
|
+
.hover[data-item-kind='referenced'] {
|
|
946
|
+
border: 1.5px solid #a5b4fc;
|
|
947
|
+
background: rgba(79, 70, 229, 0.03);
|
|
948
|
+
}
|
|
949
|
+
.hover[data-item-kind='referenced'] .label { background: #818cf8; }
|
|
950
|
+
.box[data-label-covered] .label { visibility: hidden; }
|
|
951
|
+
.box[data-label-inside] .label {
|
|
952
|
+
bottom: auto;
|
|
953
|
+
top: 2px;
|
|
954
|
+
left: 2px;
|
|
955
|
+
margin: 0;
|
|
956
|
+
}
|
|
957
|
+
/* A clickable hover label (edit mode on the live site). Only while shown, so
|
|
958
|
+
a fading box never leaves an invisible click target behind. */
|
|
959
|
+
:host([data-label-action]) .hover[data-visible] .label {
|
|
960
|
+
pointer-events: auto;
|
|
961
|
+
cursor: pointer;
|
|
962
|
+
}
|
|
963
|
+
:host([data-label-action]) .hover .label::after {
|
|
964
|
+
content: '\\2197';
|
|
965
|
+
margin-left: 5px;
|
|
966
|
+
}
|
|
967
|
+
:host([data-label-action]) .hover .label:hover { background: #4338ca; }
|
|
968
|
+
/* Bridges the gap between the label and its element, so the pointer can
|
|
969
|
+
travel from one to the other without the hover ending on the way. */
|
|
970
|
+
:host([data-label-action]) .hover .label::before {
|
|
971
|
+
content: '';
|
|
972
|
+
position: absolute;
|
|
973
|
+
left: 0;
|
|
974
|
+
right: 0;
|
|
975
|
+
top: 100%;
|
|
976
|
+
height: ${LABEL_MARGIN_PX + BOX_PADDING_PX + 2}px;
|
|
977
|
+
}
|
|
978
|
+
@media (prefers-reduced-motion: reduce) {
|
|
979
|
+
.box { transition: opacity 80ms linear; }
|
|
980
|
+
}
|
|
981
|
+
`;
|
|
982
|
+
var VisualEditorOverlay = class {
|
|
983
|
+
constructor(options = {}) {
|
|
984
|
+
this.host = null;
|
|
985
|
+
this.hoverBox = null;
|
|
986
|
+
this.selectedBox = null;
|
|
987
|
+
this.frame = null;
|
|
988
|
+
this.scrollTimeout = null;
|
|
989
|
+
this.resizeObserver = null;
|
|
990
|
+
this.onLabelClick = options.onLabelClick;
|
|
991
|
+
}
|
|
992
|
+
mount() {
|
|
993
|
+
if (this.host) return;
|
|
994
|
+
const host = document.createElement(HOST_TAG);
|
|
995
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
996
|
+
const style = document.createElement("style");
|
|
997
|
+
style.textContent = STYLES;
|
|
998
|
+
shadow.appendChild(style);
|
|
999
|
+
this.hoverBox = this.createBox(shadow, "hover");
|
|
1000
|
+
this.selectedBox = this.createBox(shadow, "selected");
|
|
1001
|
+
const onLabelClick = this.onLabelClick;
|
|
1002
|
+
if (onLabelClick) {
|
|
1003
|
+
host.setAttribute("data-label-action", "");
|
|
1004
|
+
const hoverBox = this.hoverBox;
|
|
1005
|
+
hoverBox.label.addEventListener("click", () => {
|
|
1006
|
+
if (hoverBox.target) onLabelClick(hoverBox.target);
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
document.documentElement.appendChild(host);
|
|
1010
|
+
this.host = host;
|
|
1011
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
1012
|
+
this.resizeObserver = new ResizeObserver(() => this.schedule());
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
unmount() {
|
|
1016
|
+
if (this.frame !== null) cancelAnimationFrame(this.frame);
|
|
1017
|
+
if (this.scrollTimeout) clearTimeout(this.scrollTimeout);
|
|
1018
|
+
this.frame = null;
|
|
1019
|
+
this.scrollTimeout = null;
|
|
1020
|
+
this.resizeObserver?.disconnect();
|
|
1021
|
+
this.resizeObserver = null;
|
|
1022
|
+
this.host?.remove();
|
|
1023
|
+
this.host = null;
|
|
1024
|
+
this.hoverBox = null;
|
|
1025
|
+
this.selectedBox = null;
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Whether a pointer event target is this chrome: events from inside the
|
|
1029
|
+
* shadow root reach the page retargeted to its host.
|
|
1030
|
+
*/
|
|
1031
|
+
owns(node) {
|
|
1032
|
+
return node !== null && node === this.host;
|
|
1033
|
+
}
|
|
1034
|
+
getHoverTarget() {
|
|
1035
|
+
return this.hoverBox?.target ?? null;
|
|
1036
|
+
}
|
|
1037
|
+
getSelectedTarget() {
|
|
1038
|
+
return this.selectedBox?.target ?? null;
|
|
1039
|
+
}
|
|
1040
|
+
setHover(target, label, options = {}) {
|
|
1041
|
+
if (target) {
|
|
1042
|
+
this.hoverBox?.root.setAttribute(
|
|
1043
|
+
"data-item-kind",
|
|
1044
|
+
options.kind ?? "current"
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
this.retarget(this.hoverBox, target, label);
|
|
1048
|
+
}
|
|
1049
|
+
setSelected(target, label) {
|
|
1050
|
+
this.retarget(this.selectedBox, target, label);
|
|
1051
|
+
}
|
|
1052
|
+
/** Call on every scroll; positions follow in the next frame, untransitioned. */
|
|
1053
|
+
onScroll() {
|
|
1054
|
+
if (!this.host) return;
|
|
1055
|
+
this.host.setAttribute("data-scrolling", "");
|
|
1056
|
+
if (this.scrollTimeout) clearTimeout(this.scrollTimeout);
|
|
1057
|
+
this.scrollTimeout = setTimeout(() => {
|
|
1058
|
+
this.host?.removeAttribute("data-scrolling");
|
|
1059
|
+
this.scrollTimeout = null;
|
|
1060
|
+
}, SCROLL_SETTLE_MS);
|
|
1061
|
+
this.schedule();
|
|
1062
|
+
}
|
|
1063
|
+
/** Re-measure both boxes in the next animation frame. */
|
|
1064
|
+
schedule() {
|
|
1065
|
+
if (this.frame !== null || !this.host) return;
|
|
1066
|
+
this.frame = requestAnimationFrame(() => {
|
|
1067
|
+
this.frame = null;
|
|
1068
|
+
this.render();
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
createBox(shadow, kind) {
|
|
1072
|
+
const root = document.createElement("div");
|
|
1073
|
+
root.className = `box ${kind}`;
|
|
1074
|
+
const label = document.createElement("div");
|
|
1075
|
+
label.className = "label";
|
|
1076
|
+
const text = document.createElement("span");
|
|
1077
|
+
text.className = "text";
|
|
1078
|
+
label.appendChild(text);
|
|
1079
|
+
root.appendChild(label);
|
|
1080
|
+
shadow.appendChild(root);
|
|
1081
|
+
return { root, label, text, target: null };
|
|
1082
|
+
}
|
|
1083
|
+
retarget(box, target, label) {
|
|
1084
|
+
if (!box || box.target === target) return;
|
|
1085
|
+
const previous = box.target;
|
|
1086
|
+
if (previous) this.resizeObserver?.unobserve(previous);
|
|
1087
|
+
box.target = target;
|
|
1088
|
+
if (target) {
|
|
1089
|
+
this.resizeObserver?.observe(target);
|
|
1090
|
+
box.text.textContent = label;
|
|
1091
|
+
if (!box.root.hasAttribute("data-visible")) {
|
|
1092
|
+
box.root.setAttribute("data-instant", "");
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
this.schedule();
|
|
1096
|
+
}
|
|
1097
|
+
render() {
|
|
1098
|
+
const hover = this.hoverBox ? this.measure(this.hoverBox) : null;
|
|
1099
|
+
const selected = this.selectedBox ? this.measure(this.selectedBox) : null;
|
|
1100
|
+
if (this.hoverBox) this.paint(this.hoverBox, hover, selected);
|
|
1101
|
+
if (this.selectedBox) this.paint(this.selectedBox, selected, hover);
|
|
1102
|
+
}
|
|
1103
|
+
measure(box) {
|
|
1104
|
+
const target = box.target;
|
|
1105
|
+
if (!target) return null;
|
|
1106
|
+
if (!target.isConnected) {
|
|
1107
|
+
this.resizeObserver?.unobserve(target);
|
|
1108
|
+
box.target = null;
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
const rect = target.getBoundingClientRect();
|
|
1112
|
+
if (rect.width === 0 && rect.height === 0) return null;
|
|
1113
|
+
const covers = findCovers(target, rect);
|
|
1114
|
+
if (!covers) return null;
|
|
1115
|
+
return {
|
|
1116
|
+
rect,
|
|
1117
|
+
covers,
|
|
1118
|
+
labelWidth: covers.length ? box.label.offsetWidth : 0,
|
|
1119
|
+
labelHeight: covers.length ? box.label.offsetHeight : 0
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
paint(box, measurement, other = null) {
|
|
1123
|
+
const root = box.root;
|
|
1124
|
+
if (!measurement) {
|
|
1125
|
+
root.removeAttribute("data-visible");
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
const { rect, covers } = measurement;
|
|
1129
|
+
const x2 = rect.left - BOX_PADDING_PX;
|
|
1130
|
+
const y2 = rect.top - BOX_PADDING_PX;
|
|
1131
|
+
const labelAbove = {
|
|
1132
|
+
left: x2 - 2,
|
|
1133
|
+
top: y2 - LABEL_SPACE_PX,
|
|
1134
|
+
right: x2 - 2 + (measurement.labelWidth || Math.min(240, rect.width)),
|
|
1135
|
+
bottom: y2
|
|
1136
|
+
};
|
|
1137
|
+
const labelInside = y2 < LABEL_SPACE_PX || other !== null && other.rect !== rect && overlapsBox(labelAbove, other.rect);
|
|
1138
|
+
root.style.transform = `translate3d(${x2}px, ${y2}px, 0)`;
|
|
1139
|
+
root.style.width = `${rect.width + BOX_PADDING_PX * 2}px`;
|
|
1140
|
+
root.style.height = `${rect.height + BOX_PADDING_PX * 2}px`;
|
|
1141
|
+
root.toggleAttribute("data-label-inside", labelInside);
|
|
1142
|
+
root.style.clipPath = covers.length ? clipPath(x2, y2, covers) : "";
|
|
1143
|
+
const labelLeft = labelInside ? x2 + 2 : x2 - 2;
|
|
1144
|
+
const labelTop = labelInside ? y2 + 2 : y2 - LABEL_MARGIN_PX - measurement.labelHeight;
|
|
1145
|
+
const label = {
|
|
1146
|
+
left: labelLeft,
|
|
1147
|
+
top: labelTop,
|
|
1148
|
+
right: labelLeft + measurement.labelWidth,
|
|
1149
|
+
bottom: labelTop + measurement.labelHeight
|
|
1150
|
+
};
|
|
1151
|
+
root.toggleAttribute(
|
|
1152
|
+
"data-label-covered",
|
|
1153
|
+
covers.some((c2) => overlaps(c2, label))
|
|
1154
|
+
);
|
|
1155
|
+
if (root.hasAttribute("data-instant")) {
|
|
1156
|
+
root.setAttribute("data-visible", "");
|
|
1157
|
+
requestAnimationFrame(() => root.removeAttribute("data-instant"));
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
root.setAttribute("data-visible", "");
|
|
1161
|
+
}
|
|
1162
|
+
};
|
|
1163
|
+
|
|
1164
|
+
// src/core/visual-editor/field-path.ts
|
|
1165
|
+
var INDEX_SEGMENT = /^\d+$/;
|
|
1166
|
+
var WORD_SEPARATORS = /[_-]+/g;
|
|
1167
|
+
function normalizeFieldPath(path) {
|
|
1168
|
+
return path.split(".").map(
|
|
1169
|
+
(segment) => INDEX_SEGMENT.test(segment) ? String(Number.parseInt(segment, 10)) : segment
|
|
1170
|
+
).join(".");
|
|
1171
|
+
}
|
|
1172
|
+
var GENERIC_LEAVES = /* @__PURE__ */ new Set(["value", "text", "body", "content", "label"]);
|
|
1173
|
+
function humanize(name) {
|
|
1174
|
+
const words = name.replace(WORD_SEPARATORS, " ").trim();
|
|
1175
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
1176
|
+
}
|
|
1177
|
+
function fieldLabel(path, locale = "en") {
|
|
1178
|
+
if (!path) return t("visualEditor.edit", locale);
|
|
1179
|
+
const segments = path.split(".").filter((s2) => s2 && s2 !== "items" && !INDEX_SEGMENT.test(s2));
|
|
1180
|
+
const name = segments[segments.length - 1];
|
|
1181
|
+
if (!name) return t("visualEditor.edit", locale);
|
|
1182
|
+
const parent = segments[segments.length - 2];
|
|
1183
|
+
if (parent && GENERIC_LEAVES.has(name)) {
|
|
1184
|
+
return `${humanize(parent)} \u203A ${humanize(name)}`;
|
|
1185
|
+
}
|
|
1186
|
+
return humanize(name);
|
|
1187
|
+
}
|
|
1188
|
+
function findFieldElement(lookup) {
|
|
1189
|
+
if (!lookup.field) return null;
|
|
1190
|
+
const candidates = document.querySelectorAll(
|
|
1191
|
+
"[data-prepr-encoded]"
|
|
1192
|
+
);
|
|
1193
|
+
if (lookup.entryId) {
|
|
1194
|
+
for (const candidate of candidates) {
|
|
1195
|
+
if (candidate.getAttribute("data-prepr-entry-id") === lookup.entryId) {
|
|
1196
|
+
return candidate;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
for (const path of [lookup.field, lookup.fieldById]) {
|
|
1201
|
+
if (!path) continue;
|
|
1202
|
+
const wanted = normalizeFieldPath(path);
|
|
1203
|
+
for (const candidate of candidates) {
|
|
1204
|
+
const field = candidate.getAttribute("data-prepr-field");
|
|
1205
|
+
if (!field || normalizeFieldPath(field) !== wanted) continue;
|
|
1206
|
+
if (lookup.id && candidate.getAttribute("data-prepr-id") !== lookup.id) {
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
return candidate;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return null;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
601
1215
|
// src/core/stega/elements.ts
|
|
602
1216
|
var debug4 = createScopedLogger("stega:elements");
|
|
603
1217
|
var StegaElements = class {
|
|
@@ -687,235 +1301,22 @@ var StegaElements = class {
|
|
|
687
1301
|
attributeFilter: ["class", "style", "hidden", "open", "aria-expanded"]
|
|
688
1302
|
});
|
|
689
1303
|
}
|
|
690
|
-
/**
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1304
|
+
/**
|
|
1305
|
+
* Stops watching for new content. Tags stay on their elements: the page's
|
|
1306
|
+
* stega characters are stripped once they are read, so a tag removed here
|
|
1307
|
+
* could never be rebuilt when edit mode is turned back on.
|
|
1308
|
+
*/
|
|
1309
|
+
disconnect() {
|
|
695
1310
|
if (this.observer) {
|
|
696
1311
|
this.observer.disconnect();
|
|
697
1312
|
this.observer = null;
|
|
698
1313
|
}
|
|
699
|
-
document.querySelectorAll("[data-prepr-encoded]").forEach((element) => {
|
|
700
|
-
element.removeAttribute("data-prepr-encoded");
|
|
701
|
-
element.removeAttribute("data-prepr-href");
|
|
702
|
-
element.removeAttribute("data-prepr-origin");
|
|
703
|
-
element.removeAttribute("data-prepr-id");
|
|
704
|
-
element.removeAttribute("data-prepr-field");
|
|
705
|
-
element.removeAttribute("data-prepr-locale");
|
|
706
|
-
element.classList.remove("prepr-overlay-active");
|
|
707
|
-
});
|
|
708
1314
|
this.elements = void 0;
|
|
709
1315
|
}
|
|
710
1316
|
};
|
|
711
1317
|
|
|
712
|
-
// src/core/stega/overlay.ts
|
|
713
|
-
var debug5 = createScopedLogger("stega:overlay");
|
|
714
|
-
var StegaOverlay = class {
|
|
715
|
-
constructor(onEdit) {
|
|
716
|
-
this.overlay = null;
|
|
717
|
-
this.tooltip = null;
|
|
718
|
-
this.hideTimeout = null;
|
|
719
|
-
this.currentElement = null;
|
|
720
|
-
this.onEdit = onEdit;
|
|
721
|
-
}
|
|
722
|
-
create() {
|
|
723
|
-
const overlay = document.createElement("div");
|
|
724
|
-
overlay.className = "prepr-overlay";
|
|
725
|
-
overlay.style.display = "none";
|
|
726
|
-
const tooltip = document.createElement("div");
|
|
727
|
-
tooltip.className = "prepr-tooltip";
|
|
728
|
-
tooltip.style.display = "none";
|
|
729
|
-
document.body.appendChild(overlay);
|
|
730
|
-
document.body.appendChild(tooltip);
|
|
731
|
-
this.overlay = overlay;
|
|
732
|
-
this.tooltip = tooltip;
|
|
733
|
-
return { overlay, tooltip };
|
|
734
|
-
}
|
|
735
|
-
getTooltip() {
|
|
736
|
-
return this.tooltip;
|
|
737
|
-
}
|
|
738
|
-
hasCurrentElement() {
|
|
739
|
-
return this.currentElement !== null;
|
|
740
|
-
}
|
|
741
|
-
clearHideTimeout() {
|
|
742
|
-
if (this.hideTimeout) {
|
|
743
|
-
clearTimeout(this.hideTimeout);
|
|
744
|
-
this.hideTimeout = null;
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
show(element) {
|
|
748
|
-
if (!this.overlay || !this.tooltip) return;
|
|
749
|
-
this.clearHideTimeout();
|
|
750
|
-
if (this.currentElement && this.currentElement !== element) {
|
|
751
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
752
|
-
}
|
|
753
|
-
const rect = element.getBoundingClientRect();
|
|
754
|
-
const href = element.getAttribute("data-prepr-href");
|
|
755
|
-
const origin = element.getAttribute("data-prepr-origin");
|
|
756
|
-
const id = element.getAttribute("data-prepr-id") ?? void 0;
|
|
757
|
-
const field = element.getAttribute("data-prepr-field") ?? void 0;
|
|
758
|
-
const locale = element.getAttribute("data-prepr-locale") ?? void 0;
|
|
759
|
-
debug5.log("showing overlay", { href, origin });
|
|
760
|
-
const overlay = this.overlay;
|
|
761
|
-
overlay.style.display = "block";
|
|
762
|
-
overlay.style.top = `${rect.top + window.scrollY - 2}px`;
|
|
763
|
-
overlay.style.left = `${rect.left + window.scrollX - 4}px`;
|
|
764
|
-
overlay.style.width = `${rect.width + 8}px`;
|
|
765
|
-
overlay.style.height = `${rect.height + 4}px`;
|
|
766
|
-
const tooltip = this.tooltip;
|
|
767
|
-
if (href && origin) {
|
|
768
|
-
const MIN_WIDTH_FOR_TEXT = 80;
|
|
769
|
-
const isCompact = rect.width < MIN_WIDTH_FOR_TEXT;
|
|
770
|
-
tooltip.textContent = isCompact ? "\u2197" : `${origin} \u2197`;
|
|
771
|
-
tooltip.style.display = "block";
|
|
772
|
-
tooltip.style.minWidth = isCompact ? "auto" : "80px";
|
|
773
|
-
requestAnimationFrame(() => {
|
|
774
|
-
let top = rect.top + window.scrollY - tooltip.clientHeight - 2;
|
|
775
|
-
let left = rect.right + 4 - tooltip.clientWidth;
|
|
776
|
-
const minTop = window.scrollY + 4;
|
|
777
|
-
const maxTop = window.scrollY + window.innerHeight - tooltip.clientHeight - 4;
|
|
778
|
-
const minLeft = window.scrollX + 4;
|
|
779
|
-
const maxLeft = window.scrollX + window.innerWidth - tooltip.clientWidth - 4;
|
|
780
|
-
const below = top < minTop;
|
|
781
|
-
if (below) {
|
|
782
|
-
top = rect.bottom + window.scrollY + 2;
|
|
783
|
-
}
|
|
784
|
-
top = Math.max(minTop, Math.min(top, maxTop));
|
|
785
|
-
left = Math.max(minLeft, Math.min(left, maxLeft));
|
|
786
|
-
tooltip.dataset.preprPlacement = below ? "below" : "above";
|
|
787
|
-
tooltip.style.top = `${top}px`;
|
|
788
|
-
tooltip.style.left = `${left}px`;
|
|
789
|
-
});
|
|
790
|
-
tooltip.onclick = () => {
|
|
791
|
-
this.onEdit({ href, origin, id, field, locale });
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
this.currentElement = element;
|
|
795
|
-
element.classList.add("prepr-overlay-active");
|
|
796
|
-
}
|
|
797
|
-
hideImmediate() {
|
|
798
|
-
this.clearHideTimeout();
|
|
799
|
-
if (this.overlay) this.overlay.style.display = "none";
|
|
800
|
-
if (this.tooltip) this.tooltip.style.display = "none";
|
|
801
|
-
if (this.currentElement) {
|
|
802
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
803
|
-
this.currentElement = null;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
hide() {
|
|
807
|
-
if (!this.overlay || !this.tooltip) return;
|
|
808
|
-
this.clearHideTimeout();
|
|
809
|
-
this.hideTimeout = setTimeout(() => this.hideImmediate(), 100);
|
|
810
|
-
}
|
|
811
|
-
cleanup() {
|
|
812
|
-
this.clearHideTimeout();
|
|
813
|
-
if (this.overlay) {
|
|
814
|
-
this.overlay.style.display = "none";
|
|
815
|
-
this.overlay.parentNode?.removeChild(this.overlay);
|
|
816
|
-
this.overlay = null;
|
|
817
|
-
}
|
|
818
|
-
if (this.tooltip) {
|
|
819
|
-
this.tooltip.style.display = "none";
|
|
820
|
-
this.tooltip.parentNode?.removeChild(this.tooltip);
|
|
821
|
-
this.tooltip = null;
|
|
822
|
-
}
|
|
823
|
-
if (this.currentElement) {
|
|
824
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
825
|
-
this.currentElement = null;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
};
|
|
829
|
-
|
|
830
|
-
// src/core/stega/proximity.ts
|
|
831
|
-
var debug6 = createScopedLogger("stega:proximity");
|
|
832
|
-
var StegaProximity = class {
|
|
833
|
-
constructor() {
|
|
834
|
-
this.highlighted = /* @__PURE__ */ new Set();
|
|
835
|
-
this.visible = /* @__PURE__ */ new Set();
|
|
836
|
-
this.observer = null;
|
|
837
|
-
this.getEncodedElements = createElementCache(
|
|
838
|
-
"[data-prepr-encoded]",
|
|
839
|
-
200
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
/** Call after the encoded element set changes — rebuilds the observer. */
|
|
843
|
-
refreshObserving() {
|
|
844
|
-
try {
|
|
845
|
-
if (typeof IntersectionObserver === "undefined") return;
|
|
846
|
-
if (this.observer) {
|
|
847
|
-
this.observer.disconnect();
|
|
848
|
-
this.observer = null;
|
|
849
|
-
}
|
|
850
|
-
this.visible = /* @__PURE__ */ new Set();
|
|
851
|
-
this.observer = new IntersectionObserver(
|
|
852
|
-
(entries) => {
|
|
853
|
-
entries.forEach((entry) => {
|
|
854
|
-
const el = entry.target;
|
|
855
|
-
if (entry.isIntersecting) {
|
|
856
|
-
this.visible.add(el);
|
|
857
|
-
} else {
|
|
858
|
-
this.visible.delete(el);
|
|
859
|
-
}
|
|
860
|
-
});
|
|
861
|
-
},
|
|
862
|
-
{ root: null, rootMargin: "0px", threshold: 0 }
|
|
863
|
-
);
|
|
864
|
-
const nodes = this.getEncodedElements();
|
|
865
|
-
nodes.forEach((el) => this.observer.observe(el));
|
|
866
|
-
debug6.log("observing", nodes.length, "encoded elements");
|
|
867
|
-
} catch (error) {
|
|
868
|
-
debug6.log("error setting up IntersectionObserver", error);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
stopObserving() {
|
|
872
|
-
if (this.observer) {
|
|
873
|
-
this.observer.disconnect();
|
|
874
|
-
this.observer = null;
|
|
875
|
-
this.visible.clear();
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
updateElementGradients(cursorX, cursorY) {
|
|
879
|
-
const candidates = this.visible.size > 0 ? Array.from(this.visible) : Array.from(this.getEncodedElements());
|
|
880
|
-
const newHighlighted = /* @__PURE__ */ new Set();
|
|
881
|
-
candidates.forEach((element) => {
|
|
882
|
-
const rect = element.getBoundingClientRect();
|
|
883
|
-
const distance = Math.min(
|
|
884
|
-
Math.abs(cursorX - rect.left),
|
|
885
|
-
Math.abs(cursorX - rect.right),
|
|
886
|
-
Math.abs(cursorY - rect.top),
|
|
887
|
-
Math.abs(cursorY - rect.bottom)
|
|
888
|
-
);
|
|
889
|
-
if (distance < 150) {
|
|
890
|
-
const relativeX = cursorX - rect.left;
|
|
891
|
-
const relativeY = cursorY - rect.top;
|
|
892
|
-
element.style.setProperty("--cursor-x", `${relativeX}px`);
|
|
893
|
-
element.style.setProperty("--cursor-y", `${relativeY}px`);
|
|
894
|
-
const baseGradientSize = Math.max(
|
|
895
|
-
150,
|
|
896
|
-
Math.max(rect.width, rect.height) * 1.1
|
|
897
|
-
);
|
|
898
|
-
const distanceScale = Math.max(0, (400 - distance) / 400);
|
|
899
|
-
const gradientSize = baseGradientSize * distanceScale;
|
|
900
|
-
element.style.setProperty("--gradient-size", `${gradientSize}px`);
|
|
901
|
-
element.classList.add("prepr-proximity-highlight");
|
|
902
|
-
newHighlighted.add(element);
|
|
903
|
-
} else {
|
|
904
|
-
element.classList.remove("prepr-proximity-highlight");
|
|
905
|
-
}
|
|
906
|
-
});
|
|
907
|
-
this.highlighted = newHighlighted;
|
|
908
|
-
}
|
|
909
|
-
clearAllHighlights() {
|
|
910
|
-
this.highlighted.forEach(
|
|
911
|
-
(element) => element.classList.remove("prepr-proximity-highlight")
|
|
912
|
-
);
|
|
913
|
-
this.highlighted.clear();
|
|
914
|
-
}
|
|
915
|
-
};
|
|
916
|
-
|
|
917
1318
|
// src/core/stega/stega.generated.ts
|
|
918
|
-
var STEGA_STYLES = '
|
|
1319
|
+
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}';
|
|
919
1320
|
|
|
920
1321
|
// src/core/stega/styles.ts
|
|
921
1322
|
var STEGA_STYLE_MARKER = "data-prepr-stega";
|
|
@@ -935,46 +1336,80 @@ function removeStegaStyles() {
|
|
|
935
1336
|
}
|
|
936
1337
|
|
|
937
1338
|
// src/core/stega/scan.ts
|
|
938
|
-
var
|
|
1339
|
+
var debug5 = createScopedLogger("stega:scan");
|
|
1340
|
+
var ENCODED_SELECTOR = "[data-prepr-encoded]";
|
|
1341
|
+
var PRESENCE_FADE_MS = 400;
|
|
939
1342
|
var EDITOR_MODE_STYLES = "[data-prepr-encoded]{cursor:pointer}";
|
|
1343
|
+
function fadeInPresence() {
|
|
1344
|
+
const root = document.documentElement;
|
|
1345
|
+
if (typeof root.animate !== "function") return;
|
|
1346
|
+
if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
|
|
1347
|
+
root.animate(
|
|
1348
|
+
{ "--prepr-presence": ["0", "1"] },
|
|
1349
|
+
{ duration: PRESENCE_FADE_MS, easing: "ease-out" }
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
940
1352
|
function createStegaController(opts) {
|
|
941
1353
|
const tooltipEnabled = opts.tooltip ?? true;
|
|
942
|
-
const
|
|
943
|
-
const proximity = new StegaProximity();
|
|
1354
|
+
const locale = opts.locale ?? "en";
|
|
944
1355
|
const elements = new StegaElements();
|
|
1356
|
+
const overlay = new VisualEditorOverlay({
|
|
1357
|
+
onLabelClick: (target) => opts.onEdit(readEditPayload(target))
|
|
1358
|
+
});
|
|
945
1359
|
let initialized = false;
|
|
946
|
-
let
|
|
947
|
-
|
|
948
|
-
|
|
1360
|
+
let pointerFrame = null;
|
|
1361
|
+
let pointerTarget = null;
|
|
1362
|
+
let pointerX = 0;
|
|
1363
|
+
let pointerY = 0;
|
|
1364
|
+
let hitTestPending = false;
|
|
1365
|
+
const updateHover = () => {
|
|
1366
|
+
pointerFrame = null;
|
|
1367
|
+
if (hitTestPending) {
|
|
1368
|
+
hitTestPending = false;
|
|
1369
|
+
pointerTarget = document.elementFromPoint(pointerX, pointerY);
|
|
1370
|
+
}
|
|
1371
|
+
if (overlay.owns(pointerTarget)) return;
|
|
1372
|
+
const encoded = pointerTarget instanceof Element ? pointerTarget.closest(ENCODED_SELECTOR) : null;
|
|
1373
|
+
overlay.setHover(
|
|
1374
|
+
encoded,
|
|
1375
|
+
encoded ? fieldLabel(encoded.getAttribute("data-prepr-field"), locale) : ""
|
|
1376
|
+
);
|
|
949
1377
|
};
|
|
950
|
-
const
|
|
951
|
-
if (
|
|
952
|
-
|
|
1378
|
+
const scheduleHover = () => {
|
|
1379
|
+
if (pointerFrame === null) {
|
|
1380
|
+
pointerFrame = requestAnimationFrame(updateHover);
|
|
953
1381
|
}
|
|
954
1382
|
};
|
|
1383
|
+
const handlePointer = (e2) => {
|
|
1384
|
+
pointerTarget = e2.target;
|
|
1385
|
+
pointerX = e2.clientX;
|
|
1386
|
+
pointerY = e2.clientY;
|
|
1387
|
+
hitTestPending = false;
|
|
1388
|
+
scheduleHover();
|
|
1389
|
+
};
|
|
1390
|
+
const handlePointerLeave = () => {
|
|
1391
|
+
pointerTarget = null;
|
|
1392
|
+
hitTestPending = false;
|
|
1393
|
+
overlay.setHover(null, "");
|
|
1394
|
+
};
|
|
955
1395
|
const handleScroll = () => {
|
|
956
|
-
overlay.
|
|
957
|
-
|
|
1396
|
+
overlay.onScroll();
|
|
1397
|
+
if (pointerTarget === null) return;
|
|
1398
|
+
hitTestPending = true;
|
|
1399
|
+
scheduleHover();
|
|
958
1400
|
};
|
|
1401
|
+
const handleResize = () => overlay.schedule();
|
|
959
1402
|
const handleClick = (e2) => {
|
|
960
1403
|
const target = e2.target;
|
|
961
|
-
const encoded = target?.closest(
|
|
962
|
-
"[data-prepr-encoded]"
|
|
963
|
-
);
|
|
1404
|
+
const encoded = target?.closest(ENCODED_SELECTOR);
|
|
964
1405
|
if (!encoded) return;
|
|
965
1406
|
e2.preventDefault();
|
|
966
1407
|
e2.stopPropagation();
|
|
967
|
-
opts.onEdit(
|
|
968
|
-
href: encoded.getAttribute("data-prepr-href") ?? "",
|
|
969
|
-
origin: encoded.getAttribute("data-prepr-origin") ?? "",
|
|
970
|
-
id: encoded.getAttribute("data-prepr-id") ?? void 0,
|
|
971
|
-
field: encoded.getAttribute("data-prepr-field") ?? void 0,
|
|
972
|
-
locale: encoded.getAttribute("data-prepr-locale") ?? void 0
|
|
973
|
-
});
|
|
1408
|
+
opts.onEdit(readEditPayload(encoded));
|
|
974
1409
|
};
|
|
975
1410
|
const start = () => {
|
|
976
1411
|
if (initialized) {
|
|
977
|
-
|
|
1412
|
+
debug5.log("already initialized, skipping setup");
|
|
978
1413
|
return;
|
|
979
1414
|
}
|
|
980
1415
|
if (!tooltipEnabled) {
|
|
@@ -984,73 +1419,64 @@ function createStegaController(opts) {
|
|
|
984
1419
|
});
|
|
985
1420
|
document.addEventListener("click", handleClick, { capture: true });
|
|
986
1421
|
initialized = true;
|
|
987
|
-
|
|
1422
|
+
debug5.log("stega controller started (headless)");
|
|
988
1423
|
return;
|
|
989
1424
|
}
|
|
990
1425
|
injectStegaStyles();
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
tooltip.addEventListener("mouseleave", handleTooltipMouseLeave);
|
|
1426
|
+
fadeInPresence();
|
|
1427
|
+
overlay.mount();
|
|
994
1428
|
elements.scanDocument(true);
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
const target = mouseEvent.target;
|
|
1000
|
-
if (!target) return;
|
|
1001
|
-
if (target.closest(".prepr-tooltip")) return;
|
|
1002
|
-
proximity.updateElementGradients(mouseEvent.clientX, mouseEvent.clientY);
|
|
1003
|
-
const encoded = target.closest("[data-prepr-encoded]");
|
|
1004
|
-
if (encoded) {
|
|
1005
|
-
overlay.show(encoded);
|
|
1006
|
-
} else {
|
|
1007
|
-
overlay.hide();
|
|
1008
|
-
}
|
|
1009
|
-
}, 16);
|
|
1010
|
-
document.addEventListener("mousemove", throttledMouseMove, {
|
|
1011
|
-
passive: true
|
|
1429
|
+
elements.setupMutationObserver(() => {
|
|
1430
|
+
if (pointerTarget === null) return;
|
|
1431
|
+
hitTestPending = true;
|
|
1432
|
+
scheduleHover();
|
|
1012
1433
|
});
|
|
1434
|
+
document.addEventListener("pointermove", handlePointer, { passive: true });
|
|
1435
|
+
document.addEventListener("pointerdown", handlePointer, { passive: true });
|
|
1436
|
+
document.documentElement.addEventListener(
|
|
1437
|
+
"pointerleave",
|
|
1438
|
+
handlePointerLeave
|
|
1439
|
+
);
|
|
1013
1440
|
window.addEventListener("scroll", handleScroll, {
|
|
1014
1441
|
capture: true,
|
|
1015
1442
|
passive: true
|
|
1016
1443
|
});
|
|
1444
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
1017
1445
|
initialized = true;
|
|
1018
|
-
|
|
1446
|
+
debug5.log("stega controller started");
|
|
1019
1447
|
};
|
|
1020
1448
|
const stop = () => {
|
|
1021
1449
|
if (!initialized) return;
|
|
1450
|
+
initialized = false;
|
|
1022
1451
|
if (!tooltipEnabled) {
|
|
1023
1452
|
document.removeEventListener("click", handleClick, { capture: true });
|
|
1024
|
-
elements.
|
|
1453
|
+
elements.disconnect();
|
|
1025
1454
|
removeStegaStyles();
|
|
1026
|
-
|
|
1027
|
-
debug7.log("stega controller stopped (editor mode)");
|
|
1455
|
+
debug5.log("stega controller stopped (headless)");
|
|
1028
1456
|
return;
|
|
1029
1457
|
}
|
|
1030
|
-
if (
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1458
|
+
if (pointerFrame !== null) cancelAnimationFrame(pointerFrame);
|
|
1459
|
+
pointerFrame = null;
|
|
1460
|
+
pointerTarget = null;
|
|
1461
|
+
hitTestPending = false;
|
|
1462
|
+
document.removeEventListener("pointermove", handlePointer);
|
|
1463
|
+
document.removeEventListener("pointerdown", handlePointer);
|
|
1464
|
+
document.documentElement.removeEventListener(
|
|
1465
|
+
"pointerleave",
|
|
1466
|
+
handlePointerLeave
|
|
1467
|
+
);
|
|
1035
1468
|
window.removeEventListener("scroll", handleScroll, { capture: true });
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
tooltip.removeEventListener("mouseleave", handleTooltipMouseLeave);
|
|
1040
|
-
}
|
|
1041
|
-
overlay.cleanup();
|
|
1042
|
-
proximity.clearAllHighlights();
|
|
1043
|
-
proximity.stopObserving();
|
|
1044
|
-
elements.cleanup();
|
|
1469
|
+
window.removeEventListener("resize", handleResize);
|
|
1470
|
+
overlay.unmount();
|
|
1471
|
+
elements.disconnect();
|
|
1045
1472
|
removeStegaStyles();
|
|
1046
|
-
|
|
1047
|
-
debug7.log("stega controller stopped");
|
|
1473
|
+
debug5.log("stega controller stopped");
|
|
1048
1474
|
};
|
|
1049
1475
|
return { start, stop };
|
|
1050
1476
|
}
|
|
1051
1477
|
|
|
1052
1478
|
// src/core/stega/scroll-to-field.ts
|
|
1053
|
-
var
|
|
1479
|
+
var debug6 = createScopedLogger("stega:scroll");
|
|
1054
1480
|
var FLASH_DURATION_MS = 1200;
|
|
1055
1481
|
var flashTimeout = null;
|
|
1056
1482
|
function scrollToField(request) {
|
|
@@ -1067,7 +1493,7 @@ function scrollToField(request) {
|
|
|
1067
1493
|
break;
|
|
1068
1494
|
}
|
|
1069
1495
|
if (!target) {
|
|
1070
|
-
|
|
1496
|
+
debug6.log("no element found for field", field);
|
|
1071
1497
|
return false;
|
|
1072
1498
|
}
|
|
1073
1499
|
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -1215,7 +1641,7 @@ function splitPath(full) {
|
|
|
1215
1641
|
return [full.slice(0, index), full.slice(index + 1)];
|
|
1216
1642
|
}
|
|
1217
1643
|
|
|
1218
|
-
// ../../node_modules/.pnpm/preact@10.29.
|
|
1644
|
+
// ../../node_modules/.pnpm/preact@10.29.8/node_modules/preact/dist/preact.mjs
|
|
1219
1645
|
var n;
|
|
1220
1646
|
var l;
|
|
1221
1647
|
var u;
|
|
@@ -1286,8 +1712,8 @@ function H() {
|
|
|
1286
1712
|
}
|
|
1287
1713
|
}
|
|
1288
1714
|
function L(n2, l2, u3, t3, i2, r2, o2, e2, f3, c2, a2) {
|
|
1289
|
-
var s2, h2, p2, v2, y2, _2, g2
|
|
1290
|
-
for (f3 = T(u3, l2,
|
|
1715
|
+
var s2, h2, p2, v2, y2, _2, g2 = t3 && t3.__k || w, m2 = l2.length;
|
|
1716
|
+
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);
|
|
1291
1717
|
return u3.__e = y2, f3;
|
|
1292
1718
|
}
|
|
1293
1719
|
function T(n2, l2, u3, t3, i2) {
|
|
@@ -1296,13 +1722,13 @@ function T(n2, l2, u3, t3, i2) {
|
|
|
1296
1722
|
if (s2) for (r2 = 0; r2 < a2; r2++) null != (e2 = u3[r2]) && 0 == (2 & e2.__u) && (e2.__e == t3 && (t3 = $(e2)), K(e2, e2));
|
|
1297
1723
|
return t3;
|
|
1298
1724
|
}
|
|
1299
|
-
function j(n2, l2, u3
|
|
1300
|
-
var
|
|
1725
|
+
function j(n2, l2, u3) {
|
|
1726
|
+
var t3, i2;
|
|
1301
1727
|
if ("function" == typeof n2.type) {
|
|
1302
|
-
for (
|
|
1728
|
+
for (t3 = n2.__k, i2 = 0; t3 && i2 < t3.length; i2++) t3[i2] && (t3[i2].__ = n2, l2 = j(t3[i2], l2, u3));
|
|
1303
1729
|
return l2;
|
|
1304
1730
|
}
|
|
1305
|
-
n2.__e != l2 && (
|
|
1731
|
+
n2.__e != l2 && (l2 && n2.type && !l2.parentNode && (l2 = $(n2)), l2 = u3.insertBefore(n2.__e, l2 || null));
|
|
1306
1732
|
do {
|
|
1307
1733
|
l2 = l2 && l2.nextSibling;
|
|
1308
1734
|
} while (null != l2 && 8 == l2.nodeType);
|
|
@@ -1348,35 +1774,35 @@ function V(n2) {
|
|
|
1348
1774
|
};
|
|
1349
1775
|
}
|
|
1350
1776
|
function q(n2, u3, t3, i2, r2, o2, e2, f3, c2, a2) {
|
|
1351
|
-
var s2, h2, p2, v2, y2, d2, _2, k2, x2, M,
|
|
1777
|
+
var s2, h2, p2, v2, y2, d2, _2, k2, x2, M, I2, P2, A2, H2, T2, j2, F = u3.type;
|
|
1352
1778
|
if (void 0 !== u3.constructor) return null;
|
|
1353
1779
|
128 & t3.__u && (c2 = !!(32 & t3.__u), o2 = [f3 = u3.__e = t3.__e]), (s2 = l.__b) && s2(u3);
|
|
1354
|
-
n: if ("function" == typeof
|
|
1780
|
+
n: if ("function" == typeof F) {
|
|
1355
1781
|
h2 = e2.length;
|
|
1356
1782
|
try {
|
|
1357
|
-
if (x2 = u3.props, M =
|
|
1783
|
+
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);
|
|
1358
1784
|
else {
|
|
1359
|
-
if (M && null ==
|
|
1785
|
+
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)) {
|
|
1360
1786
|
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) {
|
|
1361
1787
|
n3 && (n3.__ = u3);
|
|
1362
|
-
}), w.push.apply(p2.__h, p2._sb), p2._sb = [], p2.__h.length && e2.push(p2);
|
|
1788
|
+
}), w.push.apply(p2.__h, p2._sb), p2._sb = [], p2.__h.length && e2.push(p2), f3 = $(t3);
|
|
1363
1789
|
break n;
|
|
1364
1790
|
}
|
|
1365
|
-
null != p2.componentWillUpdate && p2.componentWillUpdate(x2, p2.__s,
|
|
1791
|
+
null != p2.componentWillUpdate && p2.componentWillUpdate(x2, p2.__s, P2), M && null != p2.componentDidUpdate && p2.__h.push(function() {
|
|
1366
1792
|
p2.componentDidUpdate(y2, d2, _2);
|
|
1367
1793
|
});
|
|
1368
1794
|
}
|
|
1369
|
-
if (p2.context =
|
|
1795
|
+
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 = [];
|
|
1370
1796
|
else do {
|
|
1371
|
-
p2.__d = false,
|
|
1372
|
-
} while (p2.__d && ++
|
|
1373
|
-
p2.state = p2.__s, null != p2.getChildContext && (i2 = m(m({}, i2), p2.getChildContext())), M && !v2 && null != p2.getSnapshotBeforeUpdate && (_2 = p2.getSnapshotBeforeUpdate(y2, d2)),
|
|
1797
|
+
p2.__d = false, A2 && A2(u3), s2 = p2.render(p2.props, p2.state, p2.context), p2.state = p2.__s;
|
|
1798
|
+
} while (p2.__d && ++H2 < 25);
|
|
1799
|
+
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);
|
|
1374
1800
|
} catch (n3) {
|
|
1375
1801
|
if (e2.length = h2, u3.__v = null, c2 || null != o2) {
|
|
1376
1802
|
if (n3.then) {
|
|
1377
1803
|
for (u3.__u |= c2 ? 160 : 128; f3 && 8 == f3.nodeType && f3.nextSibling; ) f3 = f3.nextSibling;
|
|
1378
1804
|
null != o2 && (o2[o2.indexOf(f3)] = null), u3.__e = f3;
|
|
1379
|
-
} else if (null != o2) for (
|
|
1805
|
+
} else if (null != o2) for (j2 = o2.length; j2--; ) b(o2[j2]);
|
|
1380
1806
|
} else u3.__e = t3.__e;
|
|
1381
1807
|
null == u3.__k && (u3.__k = t3.__k || []), n3.then || B(u3), l.__e(n3, u3, t3);
|
|
1382
1808
|
}
|
|
@@ -1497,9 +1923,9 @@ var CLOSE_ICON_SVG = `<svg width="10" height="10" viewBox="0 0 10 10" fill="none
|
|
|
1497
1923
|
</svg>`;
|
|
1498
1924
|
|
|
1499
1925
|
// src/core/ui/toolbar.generated.ts
|
|
1500
|
-
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
|
|
1926
|
+
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}}";
|
|
1501
1927
|
|
|
1502
|
-
// ../../node_modules/.pnpm/preact@10.29.
|
|
1928
|
+
// ../../node_modules/.pnpm/preact@10.29.8/node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs
|
|
1503
1929
|
var f2 = 0;
|
|
1504
1930
|
function u2(e2, t3, n2, o2, i2, u3) {
|
|
1505
1931
|
t3 || (t3 = {});
|
|
@@ -2192,6 +2618,764 @@ function definePreprToolbar() {
|
|
|
2192
2618
|
}
|
|
2193
2619
|
}
|
|
2194
2620
|
|
|
2621
|
+
// src/core/visual-editor/asset-images.ts
|
|
2622
|
+
var MAX_URL_LENGTH = 4096;
|
|
2623
|
+
var MIN_KEY_SEGMENTS = 2;
|
|
2624
|
+
var TRANSFORM_OPTION = /^[a-z]{1,3}_[\w.-]*$/i;
|
|
2625
|
+
var FORMAT_PLACEHOLDER = "{format}";
|
|
2626
|
+
var PROXY_PARAMS = ["url", "href", "src", "image"];
|
|
2627
|
+
var ABSOLUTE_HTTP = /^https?:\/\//i;
|
|
2628
|
+
var SOURCE_URL_ATTRIBUTES = ["src", "data-src"];
|
|
2629
|
+
var SOURCE_SET_ATTRIBUTES = ["srcset", "data-srcset"];
|
|
2630
|
+
var ASSET_ATTR = "data-prepr-asset";
|
|
2631
|
+
var ASSET_SOURCE_ATTRIBUTES = [
|
|
2632
|
+
...SOURCE_URL_ATTRIBUTES,
|
|
2633
|
+
...SOURCE_SET_ATTRIBUTES
|
|
2634
|
+
];
|
|
2635
|
+
var TAG_ATTRIBUTES = [
|
|
2636
|
+
"data-prepr-encoded",
|
|
2637
|
+
"data-prepr-id",
|
|
2638
|
+
"data-prepr-field",
|
|
2639
|
+
"data-prepr-entry-id",
|
|
2640
|
+
ASSET_ATTR
|
|
2641
|
+
];
|
|
2642
|
+
function decodeSegment(segment) {
|
|
2643
|
+
try {
|
|
2644
|
+
return decodeURIComponent(segment);
|
|
2645
|
+
} catch {
|
|
2646
|
+
return segment;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
function isTransformSegment(segment) {
|
|
2650
|
+
return segment === FORMAT_PLACEHOLDER || segment.split(",").every((option) => TRANSFORM_OPTION.test(option));
|
|
2651
|
+
}
|
|
2652
|
+
function assetKey(url, depth = 0) {
|
|
2653
|
+
if (!url || url.length > MAX_URL_LENGTH) return null;
|
|
2654
|
+
let parsed;
|
|
2655
|
+
try {
|
|
2656
|
+
parsed = new URL(url, "https://base.invalid");
|
|
2657
|
+
} catch {
|
|
2658
|
+
return null;
|
|
2659
|
+
}
|
|
2660
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
2661
|
+
if (depth === 0) {
|
|
2662
|
+
for (const name of PROXY_PARAMS) {
|
|
2663
|
+
const inner = parsed.searchParams.get(name);
|
|
2664
|
+
if (inner && ABSOLUTE_HTTP.test(inner)) return assetKey(inner, 1);
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
const segments = parsed.pathname.split("/").filter(Boolean).map(decodeSegment);
|
|
2668
|
+
const file = segments.pop();
|
|
2669
|
+
if (!file) return null;
|
|
2670
|
+
const kept = segments.filter((segment) => !isTransformSegment(segment));
|
|
2671
|
+
kept.push(file);
|
|
2672
|
+
return kept.length >= MIN_KEY_SEGMENTS ? kept.join("/") : null;
|
|
2673
|
+
}
|
|
2674
|
+
function srcsetUrls(srcset) {
|
|
2675
|
+
const urls = [];
|
|
2676
|
+
let expectUrl = true;
|
|
2677
|
+
for (const token of srcset.split(/\s+/)) {
|
|
2678
|
+
if (!token) continue;
|
|
2679
|
+
if (!expectUrl) {
|
|
2680
|
+
const comma = token.indexOf(",");
|
|
2681
|
+
if (comma === -1) continue;
|
|
2682
|
+
expectUrl = true;
|
|
2683
|
+
const rest = token.slice(comma + 1);
|
|
2684
|
+
if (!rest) continue;
|
|
2685
|
+
urls.push(rest.replace(/,+$/, ""));
|
|
2686
|
+
expectUrl = rest.endsWith(",");
|
|
2687
|
+
continue;
|
|
2688
|
+
}
|
|
2689
|
+
const url = token.replace(/^,+|,+$/g, "");
|
|
2690
|
+
if (url) urls.push(url);
|
|
2691
|
+
expectUrl = token.endsWith(",");
|
|
2692
|
+
}
|
|
2693
|
+
return urls;
|
|
2694
|
+
}
|
|
2695
|
+
function createAssetIndex(assets) {
|
|
2696
|
+
const index = /* @__PURE__ */ new Map();
|
|
2697
|
+
for (const asset of assets) {
|
|
2698
|
+
const key = assetKey(asset.key);
|
|
2699
|
+
if (key && !index.has(key)) index.set(key, asset);
|
|
2700
|
+
}
|
|
2701
|
+
return index;
|
|
2702
|
+
}
|
|
2703
|
+
function urlsOf(img) {
|
|
2704
|
+
const urls = [];
|
|
2705
|
+
const collect = (el) => {
|
|
2706
|
+
for (const name of SOURCE_URL_ATTRIBUTES) {
|
|
2707
|
+
const value = el.getAttribute(name);
|
|
2708
|
+
if (value) urls.push(value);
|
|
2709
|
+
}
|
|
2710
|
+
for (const name of SOURCE_SET_ATTRIBUTES) {
|
|
2711
|
+
const value = el.getAttribute(name);
|
|
2712
|
+
if (value) urls.push(...srcsetUrls(value));
|
|
2713
|
+
}
|
|
2714
|
+
};
|
|
2715
|
+
collect(img);
|
|
2716
|
+
const picture = img.parentElement;
|
|
2717
|
+
if (picture?.tagName === "PICTURE") {
|
|
2718
|
+
for (const source of picture.children) {
|
|
2719
|
+
if (source.tagName === "SOURCE") collect(source);
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
return urls;
|
|
2723
|
+
}
|
|
2724
|
+
function matchAsset(img, index, keys) {
|
|
2725
|
+
for (const url of urlsOf(img)) {
|
|
2726
|
+
let key = keys.get(url);
|
|
2727
|
+
if (key === void 0) {
|
|
2728
|
+
key = assetKey(url);
|
|
2729
|
+
keys.set(url, key);
|
|
2730
|
+
}
|
|
2731
|
+
const asset = key ? index.get(key) : void 0;
|
|
2732
|
+
if (asset) return asset;
|
|
2733
|
+
}
|
|
2734
|
+
return void 0;
|
|
2735
|
+
}
|
|
2736
|
+
function setIfChanged(el, name, value) {
|
|
2737
|
+
if (value === null) {
|
|
2738
|
+
if (el.hasAttribute(name)) el.removeAttribute(name);
|
|
2739
|
+
} else if (el.getAttribute(name) !== value) {
|
|
2740
|
+
el.setAttribute(name, value);
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
function untagAssetImage(img) {
|
|
2744
|
+
for (const name of TAG_ATTRIBUTES) img.removeAttribute(name);
|
|
2745
|
+
}
|
|
2746
|
+
var TEXT_FIELD_SELECTOR = `[data-prepr-encoded]:not([${ASSET_ATTR}])`;
|
|
2747
|
+
function besideOtherItem(img, isCurrentItem) {
|
|
2748
|
+
for (let block = img.parentElement; block && block !== document.body; block = block.parentElement) {
|
|
2749
|
+
const texts = block.querySelectorAll(TEXT_FIELD_SELECTOR);
|
|
2750
|
+
if (texts.length === 0) continue;
|
|
2751
|
+
for (const text of texts) {
|
|
2752
|
+
if (isCurrentItem(text)) return false;
|
|
2753
|
+
}
|
|
2754
|
+
return true;
|
|
2755
|
+
}
|
|
2756
|
+
return false;
|
|
2757
|
+
}
|
|
2758
|
+
function tagAssetImages(index, itemId, keys, options = {}) {
|
|
2759
|
+
const { isCurrentItem } = options;
|
|
2760
|
+
for (const img of document.getElementsByTagName("img")) {
|
|
2761
|
+
const ours = img.hasAttribute(ASSET_ATTR);
|
|
2762
|
+
if (!ours && img.hasAttribute("data-prepr-encoded")) continue;
|
|
2763
|
+
const match = index.size ? matchAsset(img, index, keys) : void 0;
|
|
2764
|
+
const asset = match && isCurrentItem && besideOtherItem(img, isCurrentItem) ? void 0 : match;
|
|
2765
|
+
if (!asset) {
|
|
2766
|
+
if (ours) untagAssetImage(img);
|
|
2767
|
+
continue;
|
|
2768
|
+
}
|
|
2769
|
+
setIfChanged(img, "data-prepr-encoded", "");
|
|
2770
|
+
setIfChanged(img, ASSET_ATTR, "");
|
|
2771
|
+
setIfChanged(img, "data-prepr-id", itemId);
|
|
2772
|
+
setIfChanged(img, "data-prepr-field", asset.field);
|
|
2773
|
+
setIfChanged(img, "data-prepr-entry-id", asset.entryId ?? null);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
function untagAssetImages() {
|
|
2777
|
+
document.querySelectorAll(`img[${ASSET_ATTR}]`).forEach(untagAssetImage);
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
// src/core/visual-editor/navigation-guard.ts
|
|
2781
|
+
var debug7 = createScopedLogger("navigation-guard");
|
|
2782
|
+
var trimSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
2783
|
+
function resolve(url) {
|
|
2784
|
+
try {
|
|
2785
|
+
return new URL(url, document.baseURI);
|
|
2786
|
+
} catch {
|
|
2787
|
+
return null;
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
function leavesPage(url) {
|
|
2791
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
2792
|
+
const here = window.location;
|
|
2793
|
+
return url.origin !== here.origin || trimSlash(url.pathname) !== trimSlash(here.pathname);
|
|
2794
|
+
}
|
|
2795
|
+
function leavingLinkHref(target) {
|
|
2796
|
+
if (!(target instanceof Element)) return null;
|
|
2797
|
+
const link = target.closest("a[href]");
|
|
2798
|
+
if (!link || link.hasAttribute("download")) return null;
|
|
2799
|
+
const linkTarget = link.getAttribute("target");
|
|
2800
|
+
if (linkTarget && linkTarget !== "_self") return null;
|
|
2801
|
+
const href = link.getAttribute("href") ?? "";
|
|
2802
|
+
if (href.startsWith("#")) return null;
|
|
2803
|
+
const url = resolve(href);
|
|
2804
|
+
return url && leavesPage(url) ? url.href : null;
|
|
2805
|
+
}
|
|
2806
|
+
function submitterAttr(submitter, attr) {
|
|
2807
|
+
if (!submitter?.hasAttribute(attr)) return null;
|
|
2808
|
+
if (attr === "formaction") return submitter.formAction;
|
|
2809
|
+
if (attr === "formmethod") return submitter.formMethod;
|
|
2810
|
+
return submitter.formTarget;
|
|
2811
|
+
}
|
|
2812
|
+
function submissionUrl(form, submitter) {
|
|
2813
|
+
const method = (submitterAttr(submitter, "formmethod") ?? form.method).toLowerCase();
|
|
2814
|
+
if (method === "dialog") return null;
|
|
2815
|
+
const target = submitterAttr(submitter, "formtarget") ?? form.target;
|
|
2816
|
+
if (target && target !== "_self") return null;
|
|
2817
|
+
const url = resolve(
|
|
2818
|
+
submitterAttr(submitter, "formaction") ?? (form.action || location.href)
|
|
2819
|
+
);
|
|
2820
|
+
if (!url) return null;
|
|
2821
|
+
if (method === "get") {
|
|
2822
|
+
let data;
|
|
2823
|
+
try {
|
|
2824
|
+
data = new FormData(form, submitter);
|
|
2825
|
+
} catch {
|
|
2826
|
+
data = new FormData(form);
|
|
2827
|
+
}
|
|
2828
|
+
const query = new URLSearchParams();
|
|
2829
|
+
data.forEach((value, key) => {
|
|
2830
|
+
query.append(key, typeof value === "string" ? value : value.name);
|
|
2831
|
+
});
|
|
2832
|
+
url.search = query.toString();
|
|
2833
|
+
}
|
|
2834
|
+
return url;
|
|
2835
|
+
}
|
|
2836
|
+
function createNavigationGuard(opts) {
|
|
2837
|
+
let active = false;
|
|
2838
|
+
let permitted = null;
|
|
2839
|
+
let pendingForm = null;
|
|
2840
|
+
let originalPush = null;
|
|
2841
|
+
let guardedPush = null;
|
|
2842
|
+
const isPermitted = (url) => permitted === url.href;
|
|
2843
|
+
const guarding = () => active && opts.showsEditedContent();
|
|
2844
|
+
const report = (href) => {
|
|
2845
|
+
debug7.log("blocked navigation to", href);
|
|
2846
|
+
opts.onBlocked(href);
|
|
2847
|
+
};
|
|
2848
|
+
const onSubmit = (e2) => {
|
|
2849
|
+
if (e2.defaultPrevented || !(e2.target instanceof HTMLFormElement)) return;
|
|
2850
|
+
if (!guarding()) return;
|
|
2851
|
+
const submitter = e2.submitter ?? null;
|
|
2852
|
+
const url = submissionUrl(e2.target, submitter);
|
|
2853
|
+
if (!url || !leavesPage(url) || isPermitted(url)) return;
|
|
2854
|
+
e2.preventDefault();
|
|
2855
|
+
pendingForm = { form: e2.target, submitter, href: url.href };
|
|
2856
|
+
report(url.href);
|
|
2857
|
+
};
|
|
2858
|
+
const onNavigate = (event) => {
|
|
2859
|
+
const e2 = event;
|
|
2860
|
+
if (e2.navigationType === "traverse" || e2.navigationType === "reload") {
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
if (!e2.cancelable || e2.hashChange || e2.downloadRequest !== null) return;
|
|
2864
|
+
if (!guarding()) return;
|
|
2865
|
+
if (e2.navigationType === "replace" && e2.destination.sameDocument) return;
|
|
2866
|
+
const url = resolve(e2.destination.url);
|
|
2867
|
+
if (!url || !leavesPage(url) || isPermitted(url)) return;
|
|
2868
|
+
e2.preventDefault();
|
|
2869
|
+
report(url.href);
|
|
2870
|
+
if (e2.destination.sameDocument) window.location.reload();
|
|
2871
|
+
};
|
|
2872
|
+
const navigation = () => window.navigation;
|
|
2873
|
+
function installPushGuard() {
|
|
2874
|
+
const original = history.pushState;
|
|
2875
|
+
const guard = function(data, unused, url) {
|
|
2876
|
+
if (guarding() && guardedPush === guard && url != null) {
|
|
2877
|
+
const target = resolve(url);
|
|
2878
|
+
if (target && leavesPage(target) && !isPermitted(target)) {
|
|
2879
|
+
report(target.href);
|
|
2880
|
+
window.location.reload();
|
|
2881
|
+
return;
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
return original.call(this, data, unused, url);
|
|
2885
|
+
};
|
|
2886
|
+
originalPush = original;
|
|
2887
|
+
guardedPush = guard;
|
|
2888
|
+
history.pushState = guard;
|
|
2889
|
+
}
|
|
2890
|
+
function uninstallPushGuard() {
|
|
2891
|
+
if (history.pushState === guardedPush && originalPush) {
|
|
2892
|
+
history.pushState = originalPush;
|
|
2893
|
+
}
|
|
2894
|
+
originalPush = null;
|
|
2895
|
+
guardedPush = null;
|
|
2896
|
+
}
|
|
2897
|
+
return {
|
|
2898
|
+
start() {
|
|
2899
|
+
if (active) return;
|
|
2900
|
+
active = true;
|
|
2901
|
+
installPushGuard();
|
|
2902
|
+
window.addEventListener("submit", onSubmit);
|
|
2903
|
+
navigation()?.addEventListener("navigate", onNavigate);
|
|
2904
|
+
},
|
|
2905
|
+
stop() {
|
|
2906
|
+
if (!active) return;
|
|
2907
|
+
active = false;
|
|
2908
|
+
uninstallPushGuard();
|
|
2909
|
+
window.removeEventListener("submit", onSubmit);
|
|
2910
|
+
navigation()?.removeEventListener("navigate", onNavigate);
|
|
2911
|
+
pendingForm = null;
|
|
2912
|
+
},
|
|
2913
|
+
isActive: guarding,
|
|
2914
|
+
block(href) {
|
|
2915
|
+
report(href);
|
|
2916
|
+
},
|
|
2917
|
+
permit(href) {
|
|
2918
|
+
permitted = resolve(href)?.href ?? null;
|
|
2919
|
+
},
|
|
2920
|
+
allow(href) {
|
|
2921
|
+
const url = resolve(href);
|
|
2922
|
+
if (!url || url.protocol !== "http:" && url.protocol !== "https:") {
|
|
2923
|
+
debug7.warn("ignored navigation to unsupported URL");
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
permitted = url.href;
|
|
2927
|
+
const form = pendingForm;
|
|
2928
|
+
pendingForm = null;
|
|
2929
|
+
if (form && form.href === url.href && form.form.isConnected) {
|
|
2930
|
+
try {
|
|
2931
|
+
form.form.requestSubmit(form.submitter ?? void 0);
|
|
2932
|
+
} catch {
|
|
2933
|
+
form.form.requestSubmit();
|
|
2934
|
+
}
|
|
2935
|
+
return;
|
|
2936
|
+
}
|
|
2937
|
+
window.location.assign(url.href);
|
|
2938
|
+
}
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
// src/core/visual-editor/index.ts
|
|
2943
|
+
var debug8 = createScopedLogger("visual-editor");
|
|
2944
|
+
var ENCODED_SELECTOR2 = "[data-prepr-encoded]";
|
|
2945
|
+
var REFERENCE_ATTR = "data-prepr-reference";
|
|
2946
|
+
var REFERENCE_SELECTOR = `[${REFERENCE_ATTR}]`;
|
|
2947
|
+
var OTHER_ITEM_ATTR = "data-prepr-other-item";
|
|
2948
|
+
var OTHER_ITEM_SELECTOR = `[${OTHER_ITEM_ATTR}="on"],[${OTHER_ITEM_ATTR}="off"]`;
|
|
2949
|
+
var CHROME_SELECTOR = "header, footer, nav, [role=banner], [role=contentinfo], [role=navigation]";
|
|
2950
|
+
var SECTIONING_SELECTOR = "article, aside, main, nav, section";
|
|
2951
|
+
var PLAIN_HEADER_FOOTER = "header:not([role]), footer:not([role])";
|
|
2952
|
+
var CURSOR_STYLE_MARKER = "data-prepr-visual-editor";
|
|
2953
|
+
var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}`;
|
|
2954
|
+
var SUSPENDED_ATTR = "data-prepr-ve-suspended";
|
|
2955
|
+
var FIND_RETRY_MS = 150;
|
|
2956
|
+
var FIND_RETRIES = 6;
|
|
2957
|
+
var MAX_CACHED_KEYS = 2e3;
|
|
2958
|
+
var LAYOUT_MUTATIONS = {
|
|
2959
|
+
childList: true,
|
|
2960
|
+
subtree: true,
|
|
2961
|
+
characterData: true,
|
|
2962
|
+
attributes: true,
|
|
2963
|
+
attributeFilter: [
|
|
2964
|
+
"class",
|
|
2965
|
+
"style",
|
|
2966
|
+
"hidden",
|
|
2967
|
+
"open",
|
|
2968
|
+
"data-prepr-encoded",
|
|
2969
|
+
// An image swapped by a carousel or lazy loader can show another asset.
|
|
2970
|
+
...ASSET_SOURCE_ATTRIBUTES
|
|
2971
|
+
]
|
|
2972
|
+
};
|
|
2973
|
+
function scrollIntoFrame(el) {
|
|
2974
|
+
const rect = el.getBoundingClientRect();
|
|
2975
|
+
const offset = Math.max(0, (window.innerHeight - rect.height) / 2);
|
|
2976
|
+
window.scrollTo({
|
|
2977
|
+
top: Math.max(0, window.scrollY + rect.top - offset),
|
|
2978
|
+
behavior: "smooth"
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
var isVisible = (el) => typeof el.checkVisibility === "function" ? el.checkVisibility() : el.getClientRects().length > 0;
|
|
2982
|
+
function createVisualEditor(opts) {
|
|
2983
|
+
const overlay = new VisualEditorOverlay();
|
|
2984
|
+
let running = false;
|
|
2985
|
+
let currentItemId = null;
|
|
2986
|
+
let locale = opts.locale ?? "en";
|
|
2987
|
+
let suspended = false;
|
|
2988
|
+
let pointerFrame = null;
|
|
2989
|
+
let lastPointerTarget = null;
|
|
2990
|
+
let lastPointerX = 0;
|
|
2991
|
+
let lastPointerY = 0;
|
|
2992
|
+
let mutationFrame = null;
|
|
2993
|
+
let mutationObserver = null;
|
|
2994
|
+
let selection = null;
|
|
2995
|
+
let findTimeout = null;
|
|
2996
|
+
let settlePendingFind = null;
|
|
2997
|
+
let guardEnabled = false;
|
|
2998
|
+
let assetIndex = /* @__PURE__ */ new Map();
|
|
2999
|
+
const assetKeys = /* @__PURE__ */ new Map();
|
|
3000
|
+
const guard = createNavigationGuard({
|
|
3001
|
+
onBlocked: (href) => opts.onNavigationBlocked?.(href),
|
|
3002
|
+
showsEditedContent: () => showsEditedContent()
|
|
3003
|
+
});
|
|
3004
|
+
const encodedFrom = (node) => node instanceof Element ? node.closest(ENCODED_SELECTOR2) : null;
|
|
3005
|
+
const labelFor = (el) => fieldLabel(el.getAttribute("data-prepr-field"), locale);
|
|
3006
|
+
let references = null;
|
|
3007
|
+
let ownComponents = /* @__PURE__ */ new Set();
|
|
3008
|
+
const isCurrentItem = (el) => {
|
|
3009
|
+
if (currentItemId === null) return true;
|
|
3010
|
+
const id = el.getAttribute("data-prepr-id");
|
|
3011
|
+
return !id || id === currentItemId || ownComponents.has(id);
|
|
3012
|
+
};
|
|
3013
|
+
const showsEditedContent = () => {
|
|
3014
|
+
for (const el of document.querySelectorAll(ENCODED_SELECTOR2)) {
|
|
3015
|
+
if (isCurrentItem(el) && isVisible(el)) return true;
|
|
3016
|
+
}
|
|
3017
|
+
return false;
|
|
3018
|
+
};
|
|
3019
|
+
const linkedIdOf = (el) => {
|
|
3020
|
+
if (!references) return null;
|
|
3021
|
+
const id = el.getAttribute("data-prepr-id") ?? "";
|
|
3022
|
+
if (references.has(id)) return id;
|
|
3023
|
+
let container = el.closest(REFERENCE_SELECTOR);
|
|
3024
|
+
while (container) {
|
|
3025
|
+
const linked = container.getAttribute(REFERENCE_ATTR) ?? "";
|
|
3026
|
+
if (references.has(linked)) return linked;
|
|
3027
|
+
container = container.parentElement?.closest(REFERENCE_SELECTOR) ?? null;
|
|
3028
|
+
}
|
|
3029
|
+
return null;
|
|
3030
|
+
};
|
|
3031
|
+
const inSiteChrome = (el) => {
|
|
3032
|
+
let chrome = el.closest(CHROME_SELECTOR);
|
|
3033
|
+
while (chrome) {
|
|
3034
|
+
if (!chrome.matches(PLAIN_HEADER_FOOTER) || !chrome.parentElement?.closest(SECTIONING_SELECTOR)) {
|
|
3035
|
+
return true;
|
|
3036
|
+
}
|
|
3037
|
+
chrome = chrome.parentElement?.closest(CHROME_SELECTOR) ?? null;
|
|
3038
|
+
}
|
|
3039
|
+
return false;
|
|
3040
|
+
};
|
|
3041
|
+
const showsOtherItem = (el) => {
|
|
3042
|
+
const choice = el.closest(OTHER_ITEM_SELECTOR);
|
|
3043
|
+
if (choice) return choice.getAttribute(OTHER_ITEM_ATTR) === "on";
|
|
3044
|
+
return !inSiteChrome(el);
|
|
3045
|
+
};
|
|
3046
|
+
const itemKind = (el) => {
|
|
3047
|
+
if (isCurrentItem(el)) return "current";
|
|
3048
|
+
if (!references) return "other";
|
|
3049
|
+
if (linkedIdOf(el)) return "referenced";
|
|
3050
|
+
return showsOtherItem(el) ? "other" : null;
|
|
3051
|
+
};
|
|
3052
|
+
const referenceFieldOf = (el) => {
|
|
3053
|
+
const linked = linkedIdOf(el);
|
|
3054
|
+
return linked ? references?.get(linked) : void 0;
|
|
3055
|
+
};
|
|
3056
|
+
const hoverLabel = (el, kind) => {
|
|
3057
|
+
if (kind === "other") {
|
|
3058
|
+
return `${labelFor(el)} \xB7 ${t("visualEditor.otherItem", locale)}`;
|
|
3059
|
+
}
|
|
3060
|
+
if (kind === "referenced") {
|
|
3061
|
+
const via = referenceFieldOf(el);
|
|
3062
|
+
const prefix = via ? `${fieldLabel(via, locale)} \u203A ` : "";
|
|
3063
|
+
return `${prefix}${labelFor(el)} \xB7 ${t("visualEditor.linkedItem", locale)}`;
|
|
3064
|
+
}
|
|
3065
|
+
return labelFor(el);
|
|
3066
|
+
};
|
|
3067
|
+
const setSuspended = (value) => {
|
|
3068
|
+
if (suspended === value) return;
|
|
3069
|
+
suspended = value;
|
|
3070
|
+
document.documentElement.toggleAttribute(SUSPENDED_ATTR, value);
|
|
3071
|
+
if (value) overlay.setHover(null, "");
|
|
3072
|
+
};
|
|
3073
|
+
const updateHover = () => {
|
|
3074
|
+
pointerFrame = null;
|
|
3075
|
+
overlay.schedule();
|
|
3076
|
+
if (suspended) return;
|
|
3077
|
+
const encoded = encodedFrom(lastPointerTarget);
|
|
3078
|
+
const target = encoded === overlay.getSelectedTarget() ? null : encoded;
|
|
3079
|
+
if (!target) {
|
|
3080
|
+
overlay.setHover(null, "");
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
const kind = itemKind(target);
|
|
3084
|
+
if (!kind) {
|
|
3085
|
+
overlay.setHover(null, "");
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
overlay.setHover(target, hoverLabel(target, kind), { kind });
|
|
3089
|
+
};
|
|
3090
|
+
const handlePointerMove = (e2) => {
|
|
3091
|
+
setSuspended(e2.altKey);
|
|
3092
|
+
lastPointerTarget = e2.target;
|
|
3093
|
+
lastPointerX = e2.clientX;
|
|
3094
|
+
lastPointerY = e2.clientY;
|
|
3095
|
+
if (pointerFrame === null) {
|
|
3096
|
+
pointerFrame = requestAnimationFrame(updateHover);
|
|
3097
|
+
}
|
|
3098
|
+
};
|
|
3099
|
+
const handlePointerLeave = () => {
|
|
3100
|
+
lastPointerTarget = null;
|
|
3101
|
+
overlay.setHover(null, "");
|
|
3102
|
+
};
|
|
3103
|
+
const handleClick = (e2) => {
|
|
3104
|
+
if (e2.altKey) {
|
|
3105
|
+
const href = leavingLinkHref(e2.target);
|
|
3106
|
+
if (href && guard.isActive()) guard.permit(href);
|
|
3107
|
+
return;
|
|
3108
|
+
}
|
|
3109
|
+
const encoded = encodedFrom(e2.target);
|
|
3110
|
+
const kind = encoded ? itemKind(encoded) : null;
|
|
3111
|
+
if (!encoded || !kind) {
|
|
3112
|
+
clearSelection();
|
|
3113
|
+
if (e2.ctrlKey || e2.metaKey || e2.shiftKey || !showsEditedContent()) return;
|
|
3114
|
+
const href = leavingLinkHref(e2.target);
|
|
3115
|
+
if (!href) return;
|
|
3116
|
+
e2.preventDefault();
|
|
3117
|
+
if (guard.isActive()) {
|
|
3118
|
+
e2.stopPropagation();
|
|
3119
|
+
guard.block(href);
|
|
3120
|
+
}
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
if (kind === "other" && (e2.ctrlKey || e2.metaKey || e2.shiftKey)) return;
|
|
3124
|
+
e2.preventDefault();
|
|
3125
|
+
e2.stopPropagation();
|
|
3126
|
+
const item = listItemIn(encoded, e2.target);
|
|
3127
|
+
if (kind === "current")
|
|
3128
|
+
select(item?.element ?? encoded, encoded, item?.index);
|
|
3129
|
+
const referenceField = kind === "referenced" ? referenceFieldOf(encoded) : void 0;
|
|
3130
|
+
const linkedId = kind === "referenced" ? linkedIdOf(encoded) : null;
|
|
3131
|
+
const linkHref = kind === "other" ? leavingLinkHref(e2.target) : null;
|
|
3132
|
+
opts.onEdit({
|
|
3133
|
+
...readEditPayload(encoded),
|
|
3134
|
+
...linkedId && { id: linkedId },
|
|
3135
|
+
...item && { block: item.index },
|
|
3136
|
+
...referenceField && { referenceField },
|
|
3137
|
+
...linkHref && { linkHref }
|
|
3138
|
+
});
|
|
3139
|
+
};
|
|
3140
|
+
const handleKey = (e2) => {
|
|
3141
|
+
if (e2.key === "Alt") setSuspended(e2.type === "keydown");
|
|
3142
|
+
if (e2.type === "keydown" && e2.key === "Escape") {
|
|
3143
|
+
clearSelection();
|
|
3144
|
+
}
|
|
3145
|
+
};
|
|
3146
|
+
const handleBlur = () => setSuspended(false);
|
|
3147
|
+
const handleScroll = () => overlay.onScroll();
|
|
3148
|
+
const handleResize = () => overlay.schedule();
|
|
3149
|
+
const handleSettled = () => overlay.schedule();
|
|
3150
|
+
function listItemIn(encoded, target) {
|
|
3151
|
+
if (!(target instanceof Element)) return null;
|
|
3152
|
+
const li = target.closest("li");
|
|
3153
|
+
if (!li || !encoded.contains(li) || !li.parentElement) return null;
|
|
3154
|
+
const items = Array.from(li.parentElement.children).filter(
|
|
3155
|
+
(child) => child.tagName === "LI"
|
|
3156
|
+
);
|
|
3157
|
+
return { element: li, index: items.indexOf(li) };
|
|
3158
|
+
}
|
|
3159
|
+
function nthListItem(encoded, block) {
|
|
3160
|
+
const list = encoded.querySelector("ul, ol");
|
|
3161
|
+
if (!list) return null;
|
|
3162
|
+
const items = Array.from(list.children).filter(
|
|
3163
|
+
(child) => child.tagName === "LI"
|
|
3164
|
+
);
|
|
3165
|
+
return items[block] ?? null;
|
|
3166
|
+
}
|
|
3167
|
+
function select(el, field, block) {
|
|
3168
|
+
const entryId = field.getAttribute("data-prepr-entry-id");
|
|
3169
|
+
selection = {
|
|
3170
|
+
lookup: {
|
|
3171
|
+
field: field.getAttribute("data-prepr-field") ?? "",
|
|
3172
|
+
id: field.getAttribute("data-prepr-id") ?? void 0,
|
|
3173
|
+
...entryId && { entryId },
|
|
3174
|
+
...block !== void 0 && { block }
|
|
3175
|
+
},
|
|
3176
|
+
label: field
|
|
3177
|
+
};
|
|
3178
|
+
overlay.setSelected(el, labelFor(field));
|
|
3179
|
+
if (overlay.getHoverTarget() === el) overlay.setHover(null, "");
|
|
3180
|
+
}
|
|
3181
|
+
function clearSelection() {
|
|
3182
|
+
selection = null;
|
|
3183
|
+
overlay.setSelected(null, "");
|
|
3184
|
+
}
|
|
3185
|
+
function reselect() {
|
|
3186
|
+
if (!selection) return;
|
|
3187
|
+
const current = overlay.getSelectedTarget();
|
|
3188
|
+
if (current?.isConnected && selection.label.isConnected) return;
|
|
3189
|
+
const found = findFieldElement(selection.lookup);
|
|
3190
|
+
if (!found || !isCurrentItem(found)) {
|
|
3191
|
+
overlay.setSelected(null, "");
|
|
3192
|
+
return;
|
|
3193
|
+
}
|
|
3194
|
+
const { block } = selection.lookup;
|
|
3195
|
+
const el = block !== void 0 ? nthListItem(found, block) ?? found : found;
|
|
3196
|
+
select(el, found, block);
|
|
3197
|
+
}
|
|
3198
|
+
function tagAssets() {
|
|
3199
|
+
if (assetKeys.size > MAX_CACHED_KEYS) assetKeys.clear();
|
|
3200
|
+
tagAssetImages(assetIndex, currentItemId, assetKeys, {
|
|
3201
|
+
isCurrentItem: (el) => isCurrentItem(el)
|
|
3202
|
+
});
|
|
3203
|
+
}
|
|
3204
|
+
const handleLayoutFrame = () => {
|
|
3205
|
+
mutationFrame = null;
|
|
3206
|
+
if (assetIndex.size) tagAssets();
|
|
3207
|
+
reselect();
|
|
3208
|
+
const hovered = overlay.getHoverTarget();
|
|
3209
|
+
if (hovered && !hovered.isConnected) {
|
|
3210
|
+
lastPointerTarget = document.elementFromPoint(lastPointerX, lastPointerY);
|
|
3211
|
+
updateHover();
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
overlay.schedule();
|
|
3215
|
+
};
|
|
3216
|
+
const handleMutations = () => {
|
|
3217
|
+
if (mutationFrame !== null) return;
|
|
3218
|
+
if (!selection && !overlay.getHoverTarget() && !assetIndex.size) return;
|
|
3219
|
+
mutationFrame = requestAnimationFrame(handleLayoutFrame);
|
|
3220
|
+
};
|
|
3221
|
+
function injectCursorStyles() {
|
|
3222
|
+
if (document.querySelector(`style[${CURSOR_STYLE_MARKER}]`)) return;
|
|
3223
|
+
const style = document.createElement("style");
|
|
3224
|
+
style.setAttribute(CURSOR_STYLE_MARKER, "");
|
|
3225
|
+
style.textContent = CURSOR_STYLES;
|
|
3226
|
+
document.head.appendChild(style);
|
|
3227
|
+
}
|
|
3228
|
+
function start() {
|
|
3229
|
+
if (running) return;
|
|
3230
|
+
running = true;
|
|
3231
|
+
overlay.mount();
|
|
3232
|
+
injectCursorStyles();
|
|
3233
|
+
document.addEventListener("pointermove", handlePointerMove, {
|
|
3234
|
+
passive: true
|
|
3235
|
+
});
|
|
3236
|
+
document.documentElement.addEventListener(
|
|
3237
|
+
"pointerleave",
|
|
3238
|
+
handlePointerLeave
|
|
3239
|
+
);
|
|
3240
|
+
document.addEventListener("click", handleClick, { capture: true });
|
|
3241
|
+
window.addEventListener("keydown", handleKey);
|
|
3242
|
+
window.addEventListener("keyup", handleKey);
|
|
3243
|
+
window.addEventListener("blur", handleBlur);
|
|
3244
|
+
window.addEventListener("scroll", handleScroll, {
|
|
3245
|
+
capture: true,
|
|
3246
|
+
passive: true
|
|
3247
|
+
});
|
|
3248
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
3249
|
+
document.addEventListener("transitionend", handleSettled, {
|
|
3250
|
+
capture: true,
|
|
3251
|
+
passive: true
|
|
3252
|
+
});
|
|
3253
|
+
document.addEventListener("animationend", handleSettled, {
|
|
3254
|
+
capture: true,
|
|
3255
|
+
passive: true
|
|
3256
|
+
});
|
|
3257
|
+
mutationObserver = new MutationObserver(handleMutations);
|
|
3258
|
+
mutationObserver.observe(document.body, LAYOUT_MUTATIONS);
|
|
3259
|
+
if (assetIndex.size) tagAssets();
|
|
3260
|
+
syncGuard();
|
|
3261
|
+
debug8.log("visual editor started");
|
|
3262
|
+
}
|
|
3263
|
+
function stop() {
|
|
3264
|
+
if (!running) return;
|
|
3265
|
+
running = false;
|
|
3266
|
+
if (pointerFrame !== null) cancelAnimationFrame(pointerFrame);
|
|
3267
|
+
if (mutationFrame !== null) cancelAnimationFrame(mutationFrame);
|
|
3268
|
+
mutationObserver?.disconnect();
|
|
3269
|
+
mutationObserver = null;
|
|
3270
|
+
cancelFind();
|
|
3271
|
+
pointerFrame = null;
|
|
3272
|
+
mutationFrame = null;
|
|
3273
|
+
selection = null;
|
|
3274
|
+
lastPointerTarget = null;
|
|
3275
|
+
setSuspended(false);
|
|
3276
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
3277
|
+
document.documentElement.removeEventListener(
|
|
3278
|
+
"pointerleave",
|
|
3279
|
+
handlePointerLeave
|
|
3280
|
+
);
|
|
3281
|
+
document.removeEventListener("click", handleClick, { capture: true });
|
|
3282
|
+
window.removeEventListener("keydown", handleKey);
|
|
3283
|
+
window.removeEventListener("keyup", handleKey);
|
|
3284
|
+
window.removeEventListener("blur", handleBlur);
|
|
3285
|
+
window.removeEventListener("scroll", handleScroll, { capture: true });
|
|
3286
|
+
window.removeEventListener("resize", handleResize);
|
|
3287
|
+
document.removeEventListener("transitionend", handleSettled, {
|
|
3288
|
+
capture: true
|
|
3289
|
+
});
|
|
3290
|
+
document.removeEventListener("animationend", handleSettled, {
|
|
3291
|
+
capture: true
|
|
3292
|
+
});
|
|
3293
|
+
document.querySelectorAll(`style[${CURSOR_STYLE_MARKER}]`).forEach((node) => node.remove());
|
|
3294
|
+
overlay.unmount();
|
|
3295
|
+
untagAssetImages();
|
|
3296
|
+
guard.stop();
|
|
3297
|
+
debug8.log("visual editor stopped");
|
|
3298
|
+
}
|
|
3299
|
+
function cancelFind() {
|
|
3300
|
+
if (findTimeout) clearTimeout(findTimeout);
|
|
3301
|
+
findTimeout = null;
|
|
3302
|
+
settlePendingFind?.();
|
|
3303
|
+
settlePendingFind = null;
|
|
3304
|
+
}
|
|
3305
|
+
function focusField(lookup) {
|
|
3306
|
+
cancelFind();
|
|
3307
|
+
return new Promise((resolve2) => {
|
|
3308
|
+
let attempt = 0;
|
|
3309
|
+
const finish = (found) => {
|
|
3310
|
+
settlePendingFind = null;
|
|
3311
|
+
resolve2(found);
|
|
3312
|
+
};
|
|
3313
|
+
settlePendingFind = () => resolve2(false);
|
|
3314
|
+
const tryFind = () => {
|
|
3315
|
+
findTimeout = null;
|
|
3316
|
+
const found = findFieldElement(lookup);
|
|
3317
|
+
if (found) {
|
|
3318
|
+
const el = lookup.block !== void 0 ? nthListItem(found, lookup.block) ?? found : found;
|
|
3319
|
+
scrollIntoFrame(el);
|
|
3320
|
+
if (running && isCurrentItem(found)) select(el, found, lookup.block);
|
|
3321
|
+
finish(true);
|
|
3322
|
+
return;
|
|
3323
|
+
}
|
|
3324
|
+
if (++attempt >= FIND_RETRIES) {
|
|
3325
|
+
debug8.log("no element renders field", lookup.field);
|
|
3326
|
+
finish(false);
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
findTimeout = setTimeout(tryFind, FIND_RETRY_MS);
|
|
3330
|
+
};
|
|
3331
|
+
tryFind();
|
|
3332
|
+
});
|
|
3333
|
+
}
|
|
3334
|
+
function setCurrentItem(itemId, referenceList, componentIds) {
|
|
3335
|
+
currentItemId = itemId;
|
|
3336
|
+
if (referenceList) setReferences(referenceList, componentIds);
|
|
3337
|
+
else if (componentIds) ownComponents = new Set(componentIds);
|
|
3338
|
+
if (running && assetIndex.size) tagAssets();
|
|
3339
|
+
const selected = overlay.getSelectedTarget();
|
|
3340
|
+
const owner = selected ? encodedFrom(selected) : null;
|
|
3341
|
+
if (owner && !isCurrentItem(owner)) clearSelection();
|
|
3342
|
+
}
|
|
3343
|
+
function setAssets(assets) {
|
|
3344
|
+
const hadAssets = assetIndex.size > 0;
|
|
3345
|
+
assetIndex = createAssetIndex(assets);
|
|
3346
|
+
if (running && (hadAssets || assetIndex.size)) tagAssets();
|
|
3347
|
+
}
|
|
3348
|
+
function setReferences(referenceList, componentIds) {
|
|
3349
|
+
if (componentIds) ownComponents = new Set(componentIds);
|
|
3350
|
+
references = /* @__PURE__ */ new Map();
|
|
3351
|
+
for (const { id, field } of referenceList) {
|
|
3352
|
+
if (!references.has(id)) references.set(id, field);
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
function setLocale(value) {
|
|
3356
|
+
locale = value;
|
|
3357
|
+
}
|
|
3358
|
+
function syncGuard() {
|
|
3359
|
+
if (running && guardEnabled) guard.start();
|
|
3360
|
+
else guard.stop();
|
|
3361
|
+
}
|
|
3362
|
+
function setNavigationGuard(enabled) {
|
|
3363
|
+
guardEnabled = enabled;
|
|
3364
|
+
syncGuard();
|
|
3365
|
+
}
|
|
3366
|
+
return {
|
|
3367
|
+
start,
|
|
3368
|
+
stop,
|
|
3369
|
+
setCurrentItem,
|
|
3370
|
+
setReferences,
|
|
3371
|
+
setAssets,
|
|
3372
|
+
setLocale,
|
|
3373
|
+
focusField,
|
|
3374
|
+
setNavigationGuard,
|
|
3375
|
+
allowNavigation: (href) => guard.allow(href)
|
|
3376
|
+
};
|
|
3377
|
+
}
|
|
3378
|
+
|
|
2195
3379
|
// src/core/create-preview.ts
|
|
2196
3380
|
var debug9 = createScopedLogger("create-preview");
|
|
2197
3381
|
var controllerStores = /* @__PURE__ */ new WeakMap();
|
|
@@ -2264,29 +3448,47 @@ function createPreprPreview(opts = {}) {
|
|
|
2264
3448
|
el.connect(store, (key) => t(key, locale));
|
|
2265
3449
|
}
|
|
2266
3450
|
const editingEnabled = features.editMode || isIframe;
|
|
2267
|
-
const
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
sendPreprEvent("field_edit_requested", {
|
|
2282
|
-
id,
|
|
2283
|
-
field,
|
|
2284
|
-
locale: contentLocale
|
|
2285
|
-
});
|
|
2286
|
-
} else if (safeHref) {
|
|
2287
|
-
window.open(safeHref, "_blank", "noopener,noreferrer");
|
|
2288
|
-
}
|
|
3451
|
+
const onEdit = ({
|
|
3452
|
+
href,
|
|
3453
|
+
id,
|
|
3454
|
+
field,
|
|
3455
|
+
locale: contentLocale,
|
|
3456
|
+
block,
|
|
3457
|
+
entryId,
|
|
3458
|
+
referenceField,
|
|
3459
|
+
linkHref
|
|
3460
|
+
}) => {
|
|
3461
|
+
const safeHref = href ? safeEditUrl(href) : null;
|
|
3462
|
+
if (href && !safeHref) {
|
|
3463
|
+
debug9.warn("ignored edit request with unsupported href scheme");
|
|
3464
|
+
return;
|
|
2289
3465
|
}
|
|
3466
|
+
if (isIframe) {
|
|
3467
|
+
sendPreprEvent("field_edit_requested", {
|
|
3468
|
+
id,
|
|
3469
|
+
field,
|
|
3470
|
+
locale: contentLocale,
|
|
3471
|
+
...block !== void 0 && { block },
|
|
3472
|
+
...entryId && { entryId },
|
|
3473
|
+
...referenceField && { referenceField },
|
|
3474
|
+
...linkHref && { linkHref }
|
|
3475
|
+
});
|
|
3476
|
+
} else if (safeHref) {
|
|
3477
|
+
window.open(safeHref, "_blank", "noopener,noreferrer");
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
const visualEditor = isIframe ? createVisualEditor({
|
|
3481
|
+
onEdit,
|
|
3482
|
+
locale,
|
|
3483
|
+
onNavigationBlocked: (href) => sendPreprEvent("navigation_blocked", { href })
|
|
3484
|
+
}) : null;
|
|
3485
|
+
const stega = visualEditor ?? createStegaController({
|
|
3486
|
+
// The edit-mode marks and hover label are chrome, so they follow the
|
|
3487
|
+
// same decision as the bar: unwanted in a headless preview that asked
|
|
3488
|
+
// for no UI.
|
|
3489
|
+
tooltip: mountUi,
|
|
3490
|
+
onEdit,
|
|
3491
|
+
locale
|
|
2290
3492
|
});
|
|
2291
3493
|
const autoClean = createStegaAutoClean({
|
|
2292
3494
|
enabled: options?.autoClean
|
|
@@ -2309,10 +3511,30 @@ function createPreprPreview(opts = {}) {
|
|
|
2309
3511
|
});
|
|
2310
3512
|
const bridge = createIframeBridge(store, {
|
|
2311
3513
|
allowedEditorOrigins: options?.allowedEditorOrigins,
|
|
3514
|
+
onHandshake: ({
|
|
3515
|
+
itemId,
|
|
3516
|
+
referencedIds,
|
|
3517
|
+
componentIds,
|
|
3518
|
+
uiLocale,
|
|
3519
|
+
navigationGuard,
|
|
3520
|
+
assets
|
|
3521
|
+
}) => {
|
|
3522
|
+
if (uiLocale) visualEditor?.setLocale(uiLocale);
|
|
3523
|
+
visualEditor?.setCurrentItem(itemId ?? null, referencedIds, componentIds);
|
|
3524
|
+
visualEditor?.setNavigationGuard(navigationGuard === true);
|
|
3525
|
+
if (assets) visualEditor?.setAssets(assets);
|
|
3526
|
+
},
|
|
3527
|
+
onAssets: (assets) => visualEditor?.setAssets(assets),
|
|
3528
|
+
onAllowNavigation: (href) => visualEditor?.allowNavigation(href),
|
|
3529
|
+
onReferences: (referencedIds, componentIds) => visualEditor?.setReferences(referencedIds, componentIds),
|
|
2312
3530
|
// The editor selecting a field scrolls the matching element into view. The
|
|
2313
3531
|
// lookup relies on the attributes the stega scan writes, so it finds
|
|
2314
3532
|
// nothing until encoded content has been tagged.
|
|
2315
|
-
onScrollToField: ({ field, id }) => {
|
|
3533
|
+
onScrollToField: ({ field, id, block, entryId, fieldById }) => {
|
|
3534
|
+
if (visualEditor) {
|
|
3535
|
+
void visualEditor.focusField({ field, id, block, entryId, fieldById });
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
2316
3538
|
if (!scrollToField({ field, id })) {
|
|
2317
3539
|
debug9.log("no element on this page renders field", field);
|
|
2318
3540
|
}
|
|
@@ -2344,7 +3566,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2344
3566
|
}
|
|
2345
3567
|
|
|
2346
3568
|
// src/version.ts
|
|
2347
|
-
var VERSION = "0.5.0-rc.
|
|
3569
|
+
var VERSION = "0.5.0-rc.11";
|
|
2348
3570
|
|
|
2349
3571
|
// src/core/pixel.ts
|
|
2350
3572
|
var PIXEL_SCRIPT_URL = "https://cdn.tracking.prepr.io/js/prepr.min.js";
|