@preprio/toolkit 0.4.3 → 0.5.0-rc.2
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 +8 -8
- package/dist/astro/index.cjs +1 -1
- package/dist/astro/index.js +1 -1
- package/dist/{chunk-H7VA7NGY.js → chunk-FLPNN3CV.js} +98 -14
- package/dist/index.cjs +118 -34
- package/dist/index.d.cts +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.js +1 -1
- package/dist/nextjs/components.cjs +118 -34
- package/dist/nextjs/components.d.cts +1 -1
- package/dist/nextjs/components.d.ts +1 -1
- package/dist/nextjs/components.js +1 -1
- package/dist/nextjs/index.cjs +1 -1
- package/dist/nextjs/index.d.cts +8 -2
- package/dist/nextjs/index.d.ts +8 -2
- package/dist/nextjs/index.js +1 -1
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/{pixel-e1fUWl2Y.d.cts → pixel-B3oQNbZ3.d.cts} +8 -2
- package/dist/{pixel-e1fUWl2Y.d.ts → pixel-B3oQNbZ3.d.ts} +8 -2
- package/dist/react/components.cjs +118 -34
- package/dist/react/components.d.cts +8 -2
- package/dist/react/components.d.ts +8 -2
- package/dist/react/components.js +118 -34
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -694,14 +694,14 @@ createPreprMiddleware(request, response, { preview });
|
|
|
694
694
|
createPreprPreview({ props, options: { debug: true, locale: 'nl' } });
|
|
695
695
|
```
|
|
696
696
|
|
|
697
|
-
| Option | Type | Default | Description
|
|
698
|
-
| ---------------------- | --------------- | ------------- |
|
|
699
|
-
| `debug` | `boolean` | `false` | Enable debug logging.
|
|
700
|
-
| `locale` | `'en' \| 'nl'` | auto-detected | UI language. Falls back to the first supported browser language, then `en`.
|
|
701
|
-
| `features` | `PreprFeatures` | all enabled | Which features run. See [Feature flags](#feature-flags).
|
|
702
|
-
| `ui` | `boolean` | `true` | Mount the visible toolbar. See [Headless preview](#headless-preview-no-toolbar-ui).
|
|
703
|
-
| `allowedEditorOrigins` | `string[]` | `*.prepr.io` | Exact editor origins allowed to drive this preview, for self-hosted editors. Replaces the wildcard.
|
|
704
|
-
| `autoClean` | `boolean` | `true` | Strip the invisible stega characters from visible text. See [Stega auto-cleaning](#stega-auto-cleaning).
|
|
697
|
+
| Option | Type | Default | Description |
|
|
698
|
+
| ---------------------- | --------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
699
|
+
| `debug` | `boolean` | `false` | Enable debug logging. |
|
|
700
|
+
| `locale` | `'en' \| 'nl'` | auto-detected | UI language. Falls back to the first supported browser language, then `en`. |
|
|
701
|
+
| `features` | `PreprFeatures` | all enabled | Which features run. See [Feature flags](#feature-flags). |
|
|
702
|
+
| `ui` | `boolean` | `true` | Mount the visible toolbar. See [Headless preview](#headless-preview-no-toolbar-ui). |
|
|
703
|
+
| `allowedEditorOrigins` | `string[]` | `*.prepr.io` | Exact editor origins allowed to drive this preview, for self-hosted or local editors. Replaces the wildcard. `['*']` accepts any origin — development only. |
|
|
704
|
+
| `autoClean` | `boolean` | `true` | Strip the invisible stega characters from visible text. See [Stega auto-cleaning](#stega-auto-cleaning). |
|
|
705
705
|
|
|
706
706
|
`features` and `ui` are independent: `features` decides _what runs_, `ui` decides _whether the toolbar is visible_.
|
|
707
707
|
|
package/dist/astro/index.cjs
CHANGED
package/dist/astro/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var VERSION = "0.
|
|
2
|
+
var VERSION = "0.5.0-rc.2";
|
|
3
3
|
|
|
4
4
|
// src/core/features.ts
|
|
5
5
|
function isEnabled(config) {
|
|
@@ -186,10 +186,33 @@ function isRenderedElement(element) {
|
|
|
186
186
|
if (!style) return true;
|
|
187
187
|
return style.visibility !== "hidden" && style.display !== "none";
|
|
188
188
|
}
|
|
189
|
+
function parseEditHref(href) {
|
|
190
|
+
try {
|
|
191
|
+
const url = new URL(href, window.location.origin);
|
|
192
|
+
return {
|
|
193
|
+
id: url.pathname.split("/").pop() || void 0,
|
|
194
|
+
field: url.searchParams.get("field") || void 0,
|
|
195
|
+
locale: url.searchParams.get("locale") || void 0
|
|
196
|
+
};
|
|
197
|
+
} catch {
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function setOrRemove(element, name, value) {
|
|
202
|
+
if (value) {
|
|
203
|
+
element.setAttribute(name, value);
|
|
204
|
+
} else {
|
|
205
|
+
element.removeAttribute(name);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
189
208
|
function tagEncodedElement(element, decoded) {
|
|
190
209
|
element.setAttribute("data-prepr-encoded", "");
|
|
191
210
|
element.setAttribute("data-prepr-href", decoded.href);
|
|
192
211
|
element.setAttribute("data-prepr-origin", decoded.origin);
|
|
212
|
+
const { id, field, locale } = parseEditHref(decoded.href);
|
|
213
|
+
setOrRemove(element, "data-prepr-id", id);
|
|
214
|
+
setOrRemove(element, "data-prepr-field", field);
|
|
215
|
+
setOrRemove(element, "data-prepr-locale", locale);
|
|
193
216
|
}
|
|
194
217
|
|
|
195
218
|
// src/core/stega/elements.ts
|
|
@@ -294,6 +317,9 @@ var StegaElements = class {
|
|
|
294
317
|
element.removeAttribute("data-prepr-encoded");
|
|
295
318
|
element.removeAttribute("data-prepr-href");
|
|
296
319
|
element.removeAttribute("data-prepr-origin");
|
|
320
|
+
element.removeAttribute("data-prepr-id");
|
|
321
|
+
element.removeAttribute("data-prepr-field");
|
|
322
|
+
element.removeAttribute("data-prepr-locale");
|
|
297
323
|
element.classList.remove("prepr-overlay-active");
|
|
298
324
|
});
|
|
299
325
|
this.elements = void 0;
|
|
@@ -346,6 +372,7 @@ var StegaOverlay = class {
|
|
|
346
372
|
const origin = element.getAttribute("data-prepr-origin");
|
|
347
373
|
const id = element.getAttribute("data-prepr-id") ?? void 0;
|
|
348
374
|
const field = element.getAttribute("data-prepr-field") ?? void 0;
|
|
375
|
+
const locale = element.getAttribute("data-prepr-locale") ?? void 0;
|
|
349
376
|
debug3.log("showing overlay", { href, origin });
|
|
350
377
|
const overlay = this.overlay;
|
|
351
378
|
overlay.style.display = "block";
|
|
@@ -378,7 +405,7 @@ var StegaOverlay = class {
|
|
|
378
405
|
tooltip.style.left = `${left}px`;
|
|
379
406
|
});
|
|
380
407
|
tooltip.onclick = () => {
|
|
381
|
-
this.onEdit({ href, origin, id, field });
|
|
408
|
+
this.onEdit({ href, origin, id, field, locale });
|
|
382
409
|
};
|
|
383
410
|
}
|
|
384
411
|
this.currentElement = element;
|
|
@@ -558,7 +585,8 @@ function createStegaController(opts) {
|
|
|
558
585
|
href: encoded.getAttribute("data-prepr-href") ?? "",
|
|
559
586
|
origin: encoded.getAttribute("data-prepr-origin") ?? "",
|
|
560
587
|
id: encoded.getAttribute("data-prepr-id") ?? void 0,
|
|
561
|
-
field: encoded.getAttribute("data-prepr-field") ?? void 0
|
|
588
|
+
field: encoded.getAttribute("data-prepr-field") ?? void 0,
|
|
589
|
+
locale: encoded.getAttribute("data-prepr-locale") ?? void 0
|
|
562
590
|
});
|
|
563
591
|
};
|
|
564
592
|
const start = () => {
|
|
@@ -788,6 +816,38 @@ function createStegaAutoClean(options) {
|
|
|
788
816
|
return { start, stop };
|
|
789
817
|
}
|
|
790
818
|
|
|
819
|
+
// src/core/stega/scroll-to-field.ts
|
|
820
|
+
var debug7 = createScopedLogger("stega:scroll");
|
|
821
|
+
var FLASH_DURATION_MS = 1200;
|
|
822
|
+
var flashTimeout = null;
|
|
823
|
+
function scrollToField(request) {
|
|
824
|
+
const { field, id } = request;
|
|
825
|
+
if (!field) return false;
|
|
826
|
+
const candidates = document.querySelectorAll(
|
|
827
|
+
"[data-prepr-encoded]"
|
|
828
|
+
);
|
|
829
|
+
let target = null;
|
|
830
|
+
for (const candidate of candidates) {
|
|
831
|
+
if (candidate.getAttribute("data-prepr-field") !== field) continue;
|
|
832
|
+
if (id && candidate.getAttribute("data-prepr-id") !== id) continue;
|
|
833
|
+
target = candidate;
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
if (!target) {
|
|
837
|
+
debug7.log("no element found for field", field);
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
841
|
+
if (flashTimeout) clearTimeout(flashTimeout);
|
|
842
|
+
document.querySelectorAll(".prepr-overlay-active").forEach((el) => el.classList.remove("prepr-overlay-active"));
|
|
843
|
+
target.classList.add("prepr-overlay-active");
|
|
844
|
+
flashTimeout = setTimeout(() => {
|
|
845
|
+
target.classList.remove("prepr-overlay-active");
|
|
846
|
+
flashTimeout = null;
|
|
847
|
+
}, FLASH_DURATION_MS);
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
|
|
791
851
|
// src/core/store.ts
|
|
792
852
|
var DEFAULT_STATE = {
|
|
793
853
|
locale: "en",
|
|
@@ -1944,9 +2004,13 @@ function t2(key, locale, vars) {
|
|
|
1944
2004
|
}
|
|
1945
2005
|
|
|
1946
2006
|
// src/core/iframe-bridge.ts
|
|
2007
|
+
var debug8 = createScopedLogger("iframe-bridge");
|
|
1947
2008
|
var EDITOR_BASE_DOMAIN = "prepr.io";
|
|
1948
2009
|
function isAllowedEditorOrigin(origin, allowed) {
|
|
1949
|
-
if (allowed)
|
|
2010
|
+
if (allowed) {
|
|
2011
|
+
if (allowed.includes("*")) return origin !== "null";
|
|
2012
|
+
return allowed.includes(origin);
|
|
2013
|
+
}
|
|
1950
2014
|
let url;
|
|
1951
2015
|
try {
|
|
1952
2016
|
url = new URL(origin);
|
|
@@ -1961,6 +2025,7 @@ function isAllowedEditorOrigin(origin, allowed) {
|
|
|
1961
2025
|
}
|
|
1962
2026
|
function createIframeBridge(store, options = {}) {
|
|
1963
2027
|
const allowedOrigins = options.allowedEditorOrigins;
|
|
2028
|
+
const onScrollToField = options.onScrollToField;
|
|
1964
2029
|
let parentOrigin = null;
|
|
1965
2030
|
const onKeyDown = (event) => {
|
|
1966
2031
|
const key = event.key.toLowerCase();
|
|
@@ -1970,7 +2035,10 @@ function createIframeBridge(store, options = {}) {
|
|
|
1970
2035
|
const onMessage = (evt) => {
|
|
1971
2036
|
const data = evt?.data;
|
|
1972
2037
|
if (data?.event === "prepr:initVE" && !parentOrigin) {
|
|
1973
|
-
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins))
|
|
2038
|
+
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins)) {
|
|
2039
|
+
debug8.warn("ignored editor handshake from origin", evt.origin);
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
1974
2042
|
parentOrigin = evt.origin;
|
|
1975
2043
|
setTrustedParentOrigin(parentOrigin);
|
|
1976
2044
|
if (data.scrollPosition != null) {
|
|
@@ -1984,6 +2052,15 @@ function createIframeBridge(store, options = {}) {
|
|
|
1984
2052
|
const currentScrollY = window.scrollY || document.documentElement.scrollTop;
|
|
1985
2053
|
sendPreprEvent("getScrollPosition", { value: currentScrollY });
|
|
1986
2054
|
}
|
|
2055
|
+
if (data?.event === "prepr:scrollToField") {
|
|
2056
|
+
if (typeof data.field !== "string" || !data.field) return;
|
|
2057
|
+
if (data.id !== void 0 && typeof data.id !== "string") return;
|
|
2058
|
+
try {
|
|
2059
|
+
onScrollToField?.({ field: data.field, id: data.id });
|
|
2060
|
+
} catch (error) {
|
|
2061
|
+
debug8.warn("scrollToField handler failed", error);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
1987
2064
|
};
|
|
1988
2065
|
return {
|
|
1989
2066
|
start() {
|
|
@@ -2102,7 +2179,7 @@ function splitPath(full) {
|
|
|
2102
2179
|
}
|
|
2103
2180
|
|
|
2104
2181
|
// src/core/create-preview.ts
|
|
2105
|
-
var
|
|
2182
|
+
var debug9 = createScopedLogger("create-preview");
|
|
2106
2183
|
var controllerStores = /* @__PURE__ */ new WeakMap();
|
|
2107
2184
|
function defaultNavigation() {
|
|
2108
2185
|
return {
|
|
@@ -2144,7 +2221,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2144
2221
|
const search = new URLSearchParams(window.location.search);
|
|
2145
2222
|
const hideBar = search.get(PARAM_HIDE_BAR) === "true";
|
|
2146
2223
|
if (hideBar) {
|
|
2147
|
-
|
|
2224
|
+
debug9.log(`${PARAM_HIDE_BAR}=true \u2014 no visible toolbar`);
|
|
2148
2225
|
}
|
|
2149
2226
|
const mountUi = (options?.ui ?? true) && !isIframe && !hideBar;
|
|
2150
2227
|
const locale = resolveLocale(options);
|
|
@@ -2180,18 +2257,17 @@ function createPreprPreview(opts = {}) {
|
|
|
2180
2257
|
tooltip: mountUi,
|
|
2181
2258
|
// In the editor, ask the parent to focus the field instead of opening a new
|
|
2182
2259
|
// tab. Standalone previews keep the window.open behaviour.
|
|
2183
|
-
onEdit: ({ href,
|
|
2260
|
+
onEdit: ({ href, id, field, locale: contentLocale }) => {
|
|
2184
2261
|
const safeHref = href ? safeEditUrl(href) : null;
|
|
2185
2262
|
if (href && !safeHref) {
|
|
2186
|
-
|
|
2263
|
+
debug9.warn("ignored edit request with unsupported href scheme");
|
|
2187
2264
|
return;
|
|
2188
2265
|
}
|
|
2189
2266
|
if (isIframe) {
|
|
2190
2267
|
sendPreprEvent("field_edit_requested", {
|
|
2191
|
-
href: safeHref ?? void 0,
|
|
2192
|
-
origin,
|
|
2193
2268
|
id,
|
|
2194
|
-
field
|
|
2269
|
+
field,
|
|
2270
|
+
locale: contentLocale
|
|
2195
2271
|
});
|
|
2196
2272
|
} else if (safeHref) {
|
|
2197
2273
|
window.open(safeHref, "_blank", "noopener,noreferrer");
|
|
@@ -2218,7 +2294,15 @@ function createPreprPreview(opts = {}) {
|
|
|
2218
2294
|
handleChange(before, state);
|
|
2219
2295
|
});
|
|
2220
2296
|
const bridge = createIframeBridge(store, {
|
|
2221
|
-
allowedEditorOrigins: options?.allowedEditorOrigins
|
|
2297
|
+
allowedEditorOrigins: options?.allowedEditorOrigins,
|
|
2298
|
+
// The editor selecting a field scrolls the matching element into view. The
|
|
2299
|
+
// lookup relies on the attributes the stega scan writes, so it finds
|
|
2300
|
+
// nothing until encoded content has been tagged.
|
|
2301
|
+
onScrollToField: ({ field, id }) => {
|
|
2302
|
+
if (!scrollToField({ field, id })) {
|
|
2303
|
+
debug9.log("no element on this page renders field", field);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2222
2306
|
});
|
|
2223
2307
|
sendPreprEvent(
|
|
2224
2308
|
"getScrollPosition",
|
|
@@ -2238,7 +2322,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2238
2322
|
bridge.stop();
|
|
2239
2323
|
}
|
|
2240
2324
|
el?.remove();
|
|
2241
|
-
|
|
2325
|
+
debug9.log("preview destroyed");
|
|
2242
2326
|
}
|
|
2243
2327
|
const controller = { destroy };
|
|
2244
2328
|
controllerStores.set(controller, store);
|
package/dist/index.cjs
CHANGED
|
@@ -44,7 +44,7 @@ __export(src_exports, {
|
|
|
44
44
|
module.exports = __toCommonJS(src_exports);
|
|
45
45
|
|
|
46
46
|
// src/version.ts
|
|
47
|
-
var VERSION = "0.
|
|
47
|
+
var VERSION = "0.5.0-rc.2";
|
|
48
48
|
|
|
49
49
|
// src/core/features.ts
|
|
50
50
|
function isEnabled(config) {
|
|
@@ -312,9 +312,13 @@ function createElementCache(query, ttl = 1e3) {
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// src/core/iframe-bridge.ts
|
|
315
|
+
var debug = createScopedLogger("iframe-bridge");
|
|
315
316
|
var EDITOR_BASE_DOMAIN = "prepr.io";
|
|
316
317
|
function isAllowedEditorOrigin(origin, allowed) {
|
|
317
|
-
if (allowed)
|
|
318
|
+
if (allowed) {
|
|
319
|
+
if (allowed.includes("*")) return origin !== "null";
|
|
320
|
+
return allowed.includes(origin);
|
|
321
|
+
}
|
|
318
322
|
let url;
|
|
319
323
|
try {
|
|
320
324
|
url = new URL(origin);
|
|
@@ -329,6 +333,7 @@ function isAllowedEditorOrigin(origin, allowed) {
|
|
|
329
333
|
}
|
|
330
334
|
function createIframeBridge(store, options = {}) {
|
|
331
335
|
const allowedOrigins = options.allowedEditorOrigins;
|
|
336
|
+
const onScrollToField = options.onScrollToField;
|
|
332
337
|
let parentOrigin = null;
|
|
333
338
|
const onKeyDown = (event) => {
|
|
334
339
|
const key = event.key.toLowerCase();
|
|
@@ -338,7 +343,10 @@ function createIframeBridge(store, options = {}) {
|
|
|
338
343
|
const onMessage = (evt) => {
|
|
339
344
|
const data = evt?.data;
|
|
340
345
|
if (data?.event === "prepr:initVE" && !parentOrigin) {
|
|
341
|
-
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins))
|
|
346
|
+
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins)) {
|
|
347
|
+
debug.warn("ignored editor handshake from origin", evt.origin);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
342
350
|
parentOrigin = evt.origin;
|
|
343
351
|
setTrustedParentOrigin(parentOrigin);
|
|
344
352
|
if (data.scrollPosition != null) {
|
|
@@ -352,6 +360,15 @@ function createIframeBridge(store, options = {}) {
|
|
|
352
360
|
const currentScrollY = window.scrollY || document.documentElement.scrollTop;
|
|
353
361
|
sendPreprEvent("getScrollPosition", { value: currentScrollY });
|
|
354
362
|
}
|
|
363
|
+
if (data?.event === "prepr:scrollToField") {
|
|
364
|
+
if (typeof data.field !== "string" || !data.field) return;
|
|
365
|
+
if (data.id !== void 0 && typeof data.id !== "string") return;
|
|
366
|
+
try {
|
|
367
|
+
onScrollToField?.({ field: data.field, id: data.id });
|
|
368
|
+
} catch (error) {
|
|
369
|
+
debug.warn("scrollToField handler failed", error);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
355
372
|
};
|
|
356
373
|
return {
|
|
357
374
|
start() {
|
|
@@ -376,7 +393,7 @@ var import_stega2 = require("@vercel/stega");
|
|
|
376
393
|
|
|
377
394
|
// src/core/stega/clean.ts
|
|
378
395
|
var import_stega = require("@vercel/stega");
|
|
379
|
-
var
|
|
396
|
+
var debug2 = createScopedLogger("stega:clean");
|
|
380
397
|
function stegaClean(text) {
|
|
381
398
|
if (!text) return text;
|
|
382
399
|
return (0, import_stega.vercelStegaClean)(text);
|
|
@@ -389,7 +406,7 @@ function decodeStega(str) {
|
|
|
389
406
|
return decoded;
|
|
390
407
|
}
|
|
391
408
|
} catch (error) {
|
|
392
|
-
|
|
409
|
+
debug2.log("error decoding stega data", error);
|
|
393
410
|
const match = str.match(/{"origin.*?}/);
|
|
394
411
|
if (match) {
|
|
395
412
|
try {
|
|
@@ -398,7 +415,7 @@ function decodeStega(str) {
|
|
|
398
415
|
return decodedMatch;
|
|
399
416
|
}
|
|
400
417
|
} catch (innerError) {
|
|
401
|
-
|
|
418
|
+
debug2.log("error decoding stega regex match", innerError);
|
|
402
419
|
}
|
|
403
420
|
}
|
|
404
421
|
}
|
|
@@ -434,14 +451,37 @@ function isRenderedElement(element) {
|
|
|
434
451
|
if (!style) return true;
|
|
435
452
|
return style.visibility !== "hidden" && style.display !== "none";
|
|
436
453
|
}
|
|
454
|
+
function parseEditHref(href) {
|
|
455
|
+
try {
|
|
456
|
+
const url = new URL(href, window.location.origin);
|
|
457
|
+
return {
|
|
458
|
+
id: url.pathname.split("/").pop() || void 0,
|
|
459
|
+
field: url.searchParams.get("field") || void 0,
|
|
460
|
+
locale: url.searchParams.get("locale") || void 0
|
|
461
|
+
};
|
|
462
|
+
} catch {
|
|
463
|
+
return {};
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function setOrRemove(element, name, value) {
|
|
467
|
+
if (value) {
|
|
468
|
+
element.setAttribute(name, value);
|
|
469
|
+
} else {
|
|
470
|
+
element.removeAttribute(name);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
437
473
|
function tagEncodedElement(element, decoded) {
|
|
438
474
|
element.setAttribute("data-prepr-encoded", "");
|
|
439
475
|
element.setAttribute("data-prepr-href", decoded.href);
|
|
440
476
|
element.setAttribute("data-prepr-origin", decoded.origin);
|
|
477
|
+
const { id, field, locale } = parseEditHref(decoded.href);
|
|
478
|
+
setOrRemove(element, "data-prepr-id", id);
|
|
479
|
+
setOrRemove(element, "data-prepr-field", field);
|
|
480
|
+
setOrRemove(element, "data-prepr-locale", locale);
|
|
441
481
|
}
|
|
442
482
|
|
|
443
483
|
// src/core/stega/auto-clean.ts
|
|
444
|
-
var
|
|
484
|
+
var debug3 = createScopedLogger("stega:auto-clean");
|
|
445
485
|
function createStegaAutoClean(options) {
|
|
446
486
|
const stripText = options?.enabled ?? true;
|
|
447
487
|
let observer = null;
|
|
@@ -484,7 +524,7 @@ function createStegaAutoClean(options) {
|
|
|
484
524
|
}
|
|
485
525
|
pendingHidden.add(textNode);
|
|
486
526
|
} catch (error) {
|
|
487
|
-
|
|
527
|
+
debug3.log("error cleaning text node", error);
|
|
488
528
|
}
|
|
489
529
|
return false;
|
|
490
530
|
}
|
|
@@ -570,7 +610,7 @@ function createStegaAutoClean(options) {
|
|
|
570
610
|
if (observer || typeof window === "undefined") return;
|
|
571
611
|
scanAndClean();
|
|
572
612
|
setupObserver();
|
|
573
|
-
|
|
613
|
+
debug3.log("stega auto-clean started");
|
|
574
614
|
}
|
|
575
615
|
function stop() {
|
|
576
616
|
if (debounceTimeout) {
|
|
@@ -584,13 +624,13 @@ function createStegaAutoClean(options) {
|
|
|
584
624
|
cleaned = /* @__PURE__ */ new WeakMap();
|
|
585
625
|
pendingHidden = /* @__PURE__ */ new Set();
|
|
586
626
|
pendingDirty = /* @__PURE__ */ new Set();
|
|
587
|
-
|
|
627
|
+
debug3.log("stega auto-clean stopped");
|
|
588
628
|
}
|
|
589
629
|
return { start, stop };
|
|
590
630
|
}
|
|
591
631
|
|
|
592
632
|
// src/core/stega/elements.ts
|
|
593
|
-
var
|
|
633
|
+
var debug4 = createScopedLogger("stega:elements");
|
|
594
634
|
var StegaElements = class {
|
|
595
635
|
constructor(decode = decodeStega) {
|
|
596
636
|
this.observer = null;
|
|
@@ -635,7 +675,7 @@ var StegaElements = class {
|
|
|
635
675
|
walkTextNodes(document.body, (textNode) => {
|
|
636
676
|
if (this.tagFromTextNode(textNode)) encodedCount++;
|
|
637
677
|
});
|
|
638
|
-
|
|
678
|
+
debug4.log("document scan complete, encoded", encodedCount, "elements");
|
|
639
679
|
this.elements = document.querySelectorAll("[data-prepr-encoded]");
|
|
640
680
|
}
|
|
641
681
|
/** `onUpdate` fires after each debounced batch, for refreshing derived state. */
|
|
@@ -691,6 +731,9 @@ var StegaElements = class {
|
|
|
691
731
|
element.removeAttribute("data-prepr-encoded");
|
|
692
732
|
element.removeAttribute("data-prepr-href");
|
|
693
733
|
element.removeAttribute("data-prepr-origin");
|
|
734
|
+
element.removeAttribute("data-prepr-id");
|
|
735
|
+
element.removeAttribute("data-prepr-field");
|
|
736
|
+
element.removeAttribute("data-prepr-locale");
|
|
694
737
|
element.classList.remove("prepr-overlay-active");
|
|
695
738
|
});
|
|
696
739
|
this.elements = void 0;
|
|
@@ -698,7 +741,7 @@ var StegaElements = class {
|
|
|
698
741
|
};
|
|
699
742
|
|
|
700
743
|
// src/core/stega/overlay.ts
|
|
701
|
-
var
|
|
744
|
+
var debug5 = createScopedLogger("stega:overlay");
|
|
702
745
|
var StegaOverlay = class {
|
|
703
746
|
constructor(onEdit) {
|
|
704
747
|
this.overlay = null;
|
|
@@ -743,7 +786,8 @@ var StegaOverlay = class {
|
|
|
743
786
|
const origin = element.getAttribute("data-prepr-origin");
|
|
744
787
|
const id = element.getAttribute("data-prepr-id") ?? void 0;
|
|
745
788
|
const field = element.getAttribute("data-prepr-field") ?? void 0;
|
|
746
|
-
|
|
789
|
+
const locale = element.getAttribute("data-prepr-locale") ?? void 0;
|
|
790
|
+
debug5.log("showing overlay", { href, origin });
|
|
747
791
|
const overlay = this.overlay;
|
|
748
792
|
overlay.style.display = "block";
|
|
749
793
|
overlay.style.top = `${rect.top + window.scrollY - 2}px`;
|
|
@@ -775,7 +819,7 @@ var StegaOverlay = class {
|
|
|
775
819
|
tooltip.style.left = `${left}px`;
|
|
776
820
|
});
|
|
777
821
|
tooltip.onclick = () => {
|
|
778
|
-
this.onEdit({ href, origin, id, field });
|
|
822
|
+
this.onEdit({ href, origin, id, field, locale });
|
|
779
823
|
};
|
|
780
824
|
}
|
|
781
825
|
this.currentElement = element;
|
|
@@ -815,7 +859,7 @@ var StegaOverlay = class {
|
|
|
815
859
|
};
|
|
816
860
|
|
|
817
861
|
// src/core/stega/proximity.ts
|
|
818
|
-
var
|
|
862
|
+
var debug6 = createScopedLogger("stega:proximity");
|
|
819
863
|
var StegaProximity = class {
|
|
820
864
|
constructor() {
|
|
821
865
|
this.highlighted = /* @__PURE__ */ new Set();
|
|
@@ -850,9 +894,9 @@ var StegaProximity = class {
|
|
|
850
894
|
);
|
|
851
895
|
const nodes = this.getEncodedElements();
|
|
852
896
|
nodes.forEach((el) => this.observer.observe(el));
|
|
853
|
-
|
|
897
|
+
debug6.log("observing", nodes.length, "encoded elements");
|
|
854
898
|
} catch (error) {
|
|
855
|
-
|
|
899
|
+
debug6.log("error setting up IntersectionObserver", error);
|
|
856
900
|
}
|
|
857
901
|
}
|
|
858
902
|
stopObserving() {
|
|
@@ -922,7 +966,7 @@ function removeStegaStyles() {
|
|
|
922
966
|
}
|
|
923
967
|
|
|
924
968
|
// src/core/stega/scan.ts
|
|
925
|
-
var
|
|
969
|
+
var debug7 = createScopedLogger("stega:scan");
|
|
926
970
|
var EDITOR_MODE_STYLES = "[data-prepr-encoded]{cursor:pointer}";
|
|
927
971
|
function createStegaController(opts) {
|
|
928
972
|
const tooltipEnabled = opts.tooltip ?? true;
|
|
@@ -955,12 +999,13 @@ function createStegaController(opts) {
|
|
|
955
999
|
href: encoded.getAttribute("data-prepr-href") ?? "",
|
|
956
1000
|
origin: encoded.getAttribute("data-prepr-origin") ?? "",
|
|
957
1001
|
id: encoded.getAttribute("data-prepr-id") ?? void 0,
|
|
958
|
-
field: encoded.getAttribute("data-prepr-field") ?? void 0
|
|
1002
|
+
field: encoded.getAttribute("data-prepr-field") ?? void 0,
|
|
1003
|
+
locale: encoded.getAttribute("data-prepr-locale") ?? void 0
|
|
959
1004
|
});
|
|
960
1005
|
};
|
|
961
1006
|
const start = () => {
|
|
962
1007
|
if (initialized) {
|
|
963
|
-
|
|
1008
|
+
debug7.log("already initialized, skipping setup");
|
|
964
1009
|
return;
|
|
965
1010
|
}
|
|
966
1011
|
if (!tooltipEnabled) {
|
|
@@ -970,7 +1015,7 @@ function createStegaController(opts) {
|
|
|
970
1015
|
});
|
|
971
1016
|
document.addEventListener("click", handleClick, { capture: true });
|
|
972
1017
|
initialized = true;
|
|
973
|
-
|
|
1018
|
+
debug7.log("stega controller started (editor mode)");
|
|
974
1019
|
return;
|
|
975
1020
|
}
|
|
976
1021
|
injectStegaStyles();
|
|
@@ -1001,7 +1046,7 @@ function createStegaController(opts) {
|
|
|
1001
1046
|
passive: true
|
|
1002
1047
|
});
|
|
1003
1048
|
initialized = true;
|
|
1004
|
-
|
|
1049
|
+
debug7.log("stega controller started");
|
|
1005
1050
|
};
|
|
1006
1051
|
const stop = () => {
|
|
1007
1052
|
if (!initialized) return;
|
|
@@ -1010,7 +1055,7 @@ function createStegaController(opts) {
|
|
|
1010
1055
|
elements.cleanup();
|
|
1011
1056
|
removeStegaStyles();
|
|
1012
1057
|
initialized = false;
|
|
1013
|
-
|
|
1058
|
+
debug7.log("stega controller stopped (editor mode)");
|
|
1014
1059
|
return;
|
|
1015
1060
|
}
|
|
1016
1061
|
if (throttledMouseMove) {
|
|
@@ -1030,11 +1075,43 @@ function createStegaController(opts) {
|
|
|
1030
1075
|
elements.cleanup();
|
|
1031
1076
|
removeStegaStyles();
|
|
1032
1077
|
initialized = false;
|
|
1033
|
-
|
|
1078
|
+
debug7.log("stega controller stopped");
|
|
1034
1079
|
};
|
|
1035
1080
|
return { start, stop };
|
|
1036
1081
|
}
|
|
1037
1082
|
|
|
1083
|
+
// src/core/stega/scroll-to-field.ts
|
|
1084
|
+
var debug8 = createScopedLogger("stega:scroll");
|
|
1085
|
+
var FLASH_DURATION_MS = 1200;
|
|
1086
|
+
var flashTimeout = null;
|
|
1087
|
+
function scrollToField(request) {
|
|
1088
|
+
const { field, id } = request;
|
|
1089
|
+
if (!field) return false;
|
|
1090
|
+
const candidates = document.querySelectorAll(
|
|
1091
|
+
"[data-prepr-encoded]"
|
|
1092
|
+
);
|
|
1093
|
+
let target = null;
|
|
1094
|
+
for (const candidate of candidates) {
|
|
1095
|
+
if (candidate.getAttribute("data-prepr-field") !== field) continue;
|
|
1096
|
+
if (id && candidate.getAttribute("data-prepr-id") !== id) continue;
|
|
1097
|
+
target = candidate;
|
|
1098
|
+
break;
|
|
1099
|
+
}
|
|
1100
|
+
if (!target) {
|
|
1101
|
+
debug8.log("no element found for field", field);
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1105
|
+
if (flashTimeout) clearTimeout(flashTimeout);
|
|
1106
|
+
document.querySelectorAll(".prepr-overlay-active").forEach((el) => el.classList.remove("prepr-overlay-active"));
|
|
1107
|
+
target.classList.add("prepr-overlay-active");
|
|
1108
|
+
flashTimeout = setTimeout(() => {
|
|
1109
|
+
target.classList.remove("prepr-overlay-active");
|
|
1110
|
+
flashTimeout = null;
|
|
1111
|
+
}, FLASH_DURATION_MS);
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1038
1115
|
// src/core/store.ts
|
|
1039
1116
|
var DEFAULT_STATE = {
|
|
1040
1117
|
locale: "en",
|
|
@@ -2147,7 +2224,7 @@ function definePreprToolbar() {
|
|
|
2147
2224
|
}
|
|
2148
2225
|
|
|
2149
2226
|
// src/core/create-preview.ts
|
|
2150
|
-
var
|
|
2227
|
+
var debug9 = createScopedLogger("create-preview");
|
|
2151
2228
|
var controllerStores = /* @__PURE__ */ new WeakMap();
|
|
2152
2229
|
function defaultNavigation() {
|
|
2153
2230
|
return {
|
|
@@ -2189,7 +2266,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2189
2266
|
const search = new URLSearchParams(window.location.search);
|
|
2190
2267
|
const hideBar = search.get(PARAM_HIDE_BAR) === "true";
|
|
2191
2268
|
if (hideBar) {
|
|
2192
|
-
|
|
2269
|
+
debug9.log(`${PARAM_HIDE_BAR}=true \u2014 no visible toolbar`);
|
|
2193
2270
|
}
|
|
2194
2271
|
const mountUi = (options?.ui ?? true) && !isIframe && !hideBar;
|
|
2195
2272
|
const locale = resolveLocale(options);
|
|
@@ -2225,18 +2302,17 @@ function createPreprPreview(opts = {}) {
|
|
|
2225
2302
|
tooltip: mountUi,
|
|
2226
2303
|
// In the editor, ask the parent to focus the field instead of opening a new
|
|
2227
2304
|
// tab. Standalone previews keep the window.open behaviour.
|
|
2228
|
-
onEdit: ({ href,
|
|
2305
|
+
onEdit: ({ href, id, field, locale: contentLocale }) => {
|
|
2229
2306
|
const safeHref = href ? safeEditUrl(href) : null;
|
|
2230
2307
|
if (href && !safeHref) {
|
|
2231
|
-
|
|
2308
|
+
debug9.warn("ignored edit request with unsupported href scheme");
|
|
2232
2309
|
return;
|
|
2233
2310
|
}
|
|
2234
2311
|
if (isIframe) {
|
|
2235
2312
|
sendPreprEvent("field_edit_requested", {
|
|
2236
|
-
href: safeHref ?? void 0,
|
|
2237
|
-
origin,
|
|
2238
2313
|
id,
|
|
2239
|
-
field
|
|
2314
|
+
field,
|
|
2315
|
+
locale: contentLocale
|
|
2240
2316
|
});
|
|
2241
2317
|
} else if (safeHref) {
|
|
2242
2318
|
window.open(safeHref, "_blank", "noopener,noreferrer");
|
|
@@ -2263,7 +2339,15 @@ function createPreprPreview(opts = {}) {
|
|
|
2263
2339
|
handleChange(before, state);
|
|
2264
2340
|
});
|
|
2265
2341
|
const bridge = createIframeBridge(store, {
|
|
2266
|
-
allowedEditorOrigins: options?.allowedEditorOrigins
|
|
2342
|
+
allowedEditorOrigins: options?.allowedEditorOrigins,
|
|
2343
|
+
// The editor selecting a field scrolls the matching element into view. The
|
|
2344
|
+
// lookup relies on the attributes the stega scan writes, so it finds
|
|
2345
|
+
// nothing until encoded content has been tagged.
|
|
2346
|
+
onScrollToField: ({ field, id }) => {
|
|
2347
|
+
if (!scrollToField({ field, id })) {
|
|
2348
|
+
debug9.log("no element on this page renders field", field);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2267
2351
|
});
|
|
2268
2352
|
sendPreprEvent(
|
|
2269
2353
|
"getScrollPosition",
|
|
@@ -2283,7 +2367,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2283
2367
|
bridge.stop();
|
|
2284
2368
|
}
|
|
2285
2369
|
el?.remove();
|
|
2286
|
-
|
|
2370
|
+
debug9.log("preview destroyed");
|
|
2287
2371
|
}
|
|
2288
2372
|
const controller = { destroy };
|
|
2289
2373
|
controllerStores.set(controller, store);
|