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