@preprio/toolkit 0.5.0-rc.2 → 0.5.0-rc.3
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 +207 -10
- package/dist/astro/index.cjs +1174 -1
- package/dist/astro/index.d.cts +47 -1
- package/dist/astro/index.d.ts +47 -1
- package/dist/astro/index.js +1177 -1
- package/dist/chunk-42Z2VW7G.js +1161 -0
- package/dist/chunk-6WY3MWCX.js +246 -0
- package/dist/chunk-MDAV2FLM.js +26 -0
- package/dist/{chunk-FLPNN3CV.js → chunk-PP3O7RB4.js} +1370 -623
- package/dist/chunk-S3VT4UWM.js +75 -0
- package/dist/chunk-SXUDBGLZ.js +184 -0
- package/dist/chunk-URXSDJOU.js +356 -0
- package/dist/index.cjs +3108 -392
- package/dist/index.d.cts +238 -4
- package/dist/index.d.ts +238 -4
- package/dist/index.js +189 -149
- package/dist/live-preview-EADA3ZL4.js +18 -0
- package/dist/nextjs/components.cjs +1522 -359
- 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 +1065 -6
- package/dist/nextjs/index.d.cts +73 -1
- package/dist/nextjs/index.d.ts +73 -1
- package/dist/nextjs/index.js +1059 -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 +1838 -0
- package/dist/nextjs/live.d.cts +52 -0
- package/dist/nextjs/live.d.ts +52 -0
- package/dist/nextjs/live.js +265 -0
- package/dist/nuxt/index.cjs +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/nuxt/live-preview-PTEC3GPC.js +1462 -0
- package/dist/nuxt/live.cjs +1662 -0
- package/dist/nuxt/live.d.cts +43 -0
- package/dist/nuxt/live.d.ts +43 -0
- package/dist/nuxt/live.js +94 -0
- package/dist/{pixel-B3oQNbZ3.d.cts → pixel-CX422dCO.d.cts} +10 -1
- package/dist/{pixel-B3oQNbZ3.d.ts → pixel-CX422dCO.d.ts} +10 -1
- package/dist/react/components.cjs +1522 -359
- package/dist/react/components.d.cts +9 -0
- package/dist/react/components.d.ts +9 -0
- package/dist/react/components.js +1522 -359
- package/dist/sveltekit/index.cjs +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/package.json +12 -1
- package/src/astro/PreprToolbar.astro +7 -1
|
@@ -1,133 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
var ALL_FEATURES_ENABLED = Object.freeze({
|
|
17
|
-
segments: true,
|
|
18
|
-
abTesting: true,
|
|
19
|
-
editMode: true
|
|
20
|
-
});
|
|
1
|
+
import {
|
|
2
|
+
ALL_FEATURES_ENABLED,
|
|
3
|
+
VERSION,
|
|
4
|
+
resolveFeatures
|
|
5
|
+
} from "./chunk-MDAV2FLM.js";
|
|
6
|
+
import {
|
|
7
|
+
createIframeBridge,
|
|
8
|
+
isLocale,
|
|
9
|
+
t
|
|
10
|
+
} from "./chunk-URXSDJOU.js";
|
|
11
|
+
import {
|
|
12
|
+
createScopedLogger,
|
|
13
|
+
initDebugLogger,
|
|
14
|
+
sendPreprEvent
|
|
15
|
+
} from "./chunk-S3VT4UWM.js";
|
|
21
16
|
|
|
22
17
|
// src/core/stega/clean.ts
|
|
23
18
|
import { vercelStegaClean, vercelStegaDecode } from "@vercel/stega";
|
|
24
|
-
|
|
25
|
-
// src/core/utils.ts
|
|
26
|
-
var trustedParentOrigin = null;
|
|
27
|
-
function setTrustedParentOrigin(origin) {
|
|
28
|
-
trustedParentOrigin = origin;
|
|
29
|
-
}
|
|
30
|
-
function sendPreprEvent(event, data, options) {
|
|
31
|
-
if (typeof window !== "undefined") {
|
|
32
|
-
const message = {
|
|
33
|
-
name: "prepr_preview_bar",
|
|
34
|
-
event,
|
|
35
|
-
...data
|
|
36
|
-
};
|
|
37
|
-
window.dispatchEvent(
|
|
38
|
-
new CustomEvent("prepr_preview_bar", { detail: message })
|
|
39
|
-
);
|
|
40
|
-
if (window.parent && window.parent !== window) {
|
|
41
|
-
if (trustedParentOrigin) {
|
|
42
|
-
window.parent.postMessage(message, trustedParentOrigin);
|
|
43
|
-
} else if (options?.allowUntrustedTarget) {
|
|
44
|
-
window.parent.postMessage(message, "*");
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
var DebugLogger = class {
|
|
50
|
-
constructor(options) {
|
|
51
|
-
this.options = {
|
|
52
|
-
prefix: "[Prepr]",
|
|
53
|
-
...options
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
// A local `enabled` wins; otherwise defer to the global logger so scoped
|
|
57
|
-
// loggers created before initDebugLogger() still pick up the setting.
|
|
58
|
-
isEnabled() {
|
|
59
|
-
if (this.options.enabled !== void 0) {
|
|
60
|
-
return this.options.enabled;
|
|
61
|
-
}
|
|
62
|
-
return globalDebugLogger?.options?.enabled ?? false;
|
|
63
|
-
}
|
|
64
|
-
log(message, ...args) {
|
|
65
|
-
if (!this.isEnabled()) return;
|
|
66
|
-
const prefix = this.options.prefix;
|
|
67
|
-
console.log(`${prefix} ${message}`, ...args);
|
|
68
|
-
}
|
|
69
|
-
warn(message, ...args) {
|
|
70
|
-
if (!this.isEnabled()) return;
|
|
71
|
-
const prefix = this.options.prefix;
|
|
72
|
-
console.warn(`${prefix} ${message}`, ...args);
|
|
73
|
-
}
|
|
74
|
-
error(message, ...args) {
|
|
75
|
-
if (!this.isEnabled()) return;
|
|
76
|
-
const prefix = this.options.prefix;
|
|
77
|
-
console.error(`${prefix} ${message}`, ...args);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
var globalDebugLogger = null;
|
|
81
|
-
function initDebugLogger(enabled = false) {
|
|
82
|
-
globalDebugLogger = new DebugLogger({ enabled });
|
|
83
|
-
}
|
|
84
|
-
function createScopedLogger(scopeName) {
|
|
85
|
-
return new DebugLogger({
|
|
86
|
-
prefix: `[Prepr][${scopeName}]`
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
function throttle(func, delay) {
|
|
90
|
-
let timeoutId = null;
|
|
91
|
-
let lastExecTime = 0;
|
|
92
|
-
const throttledFunc = ((...args) => {
|
|
93
|
-
const currentTime = Date.now();
|
|
94
|
-
const timeSinceLastExec = currentTime - lastExecTime;
|
|
95
|
-
if (timeSinceLastExec >= delay) {
|
|
96
|
-
func(...args);
|
|
97
|
-
lastExecTime = currentTime;
|
|
98
|
-
} else {
|
|
99
|
-
if (timeoutId) {
|
|
100
|
-
clearTimeout(timeoutId);
|
|
101
|
-
}
|
|
102
|
-
timeoutId = setTimeout(() => {
|
|
103
|
-
func(...args);
|
|
104
|
-
lastExecTime = Date.now();
|
|
105
|
-
timeoutId = null;
|
|
106
|
-
}, delay - timeSinceLastExec);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
throttledFunc.cancel = () => {
|
|
110
|
-
if (timeoutId) {
|
|
111
|
-
clearTimeout(timeoutId);
|
|
112
|
-
timeoutId = null;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
return throttledFunc;
|
|
116
|
-
}
|
|
117
|
-
function createElementCache(query, ttl = 1e3) {
|
|
118
|
-
let cache = null;
|
|
119
|
-
let lastCacheTime = 0;
|
|
120
|
-
return () => {
|
|
121
|
-
const now = Date.now();
|
|
122
|
-
if (!cache || now - lastCacheTime > ttl) {
|
|
123
|
-
cache = document.querySelectorAll(query);
|
|
124
|
-
lastCacheTime = now;
|
|
125
|
-
}
|
|
126
|
-
return cache;
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// src/core/stega/clean.ts
|
|
131
19
|
var debug = createScopedLogger("stega:clean");
|
|
132
20
|
function stegaClean(text) {
|
|
133
21
|
if (!text) return text;
|
|
@@ -157,6 +45,439 @@ function decodeStega(str) {
|
|
|
157
45
|
return null;
|
|
158
46
|
}
|
|
159
47
|
|
|
48
|
+
// src/core/visual-editor/overlay.ts
|
|
49
|
+
var HOST_TAG = "prepr-visual-editor";
|
|
50
|
+
var SCROLL_SETTLE_MS = 120;
|
|
51
|
+
var LABEL_SPACE_PX = 22;
|
|
52
|
+
var BOX_PADDING_PX = 3;
|
|
53
|
+
var LABEL_MARGIN_PX = 4;
|
|
54
|
+
var PROBE_POINTS = [
|
|
55
|
+
[0.5, 0.5],
|
|
56
|
+
[0.15, 0.2],
|
|
57
|
+
[0.85, 0.2],
|
|
58
|
+
[0.15, 0.8],
|
|
59
|
+
[0.85, 0.8]
|
|
60
|
+
];
|
|
61
|
+
function overlapsBox(area, rect) {
|
|
62
|
+
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;
|
|
63
|
+
}
|
|
64
|
+
function overlaps(a2, b2) {
|
|
65
|
+
return a2.left < b2.right && b2.left < a2.right && a2.top < b2.bottom && b2.top < a2.bottom;
|
|
66
|
+
}
|
|
67
|
+
function coverOf(hit, target, x2, y2) {
|
|
68
|
+
let cover = hit.getBoundingClientRect();
|
|
69
|
+
for (let el = hit.parentElement; el && !el.contains(target) && el !== document.body; el = el.parentElement) {
|
|
70
|
+
const r2 = el.getBoundingClientRect();
|
|
71
|
+
if (x2 < r2.left || x2 >= r2.right || y2 < r2.top || y2 >= r2.bottom) break;
|
|
72
|
+
cover = r2;
|
|
73
|
+
}
|
|
74
|
+
return cover;
|
|
75
|
+
}
|
|
76
|
+
function mergeAreas(areas) {
|
|
77
|
+
const merged = areas.slice();
|
|
78
|
+
for (let i2 = 0; i2 < merged.length; i2++) {
|
|
79
|
+
for (let j2 = i2 + 1; j2 < merged.length; j2++) {
|
|
80
|
+
if (!overlaps(merged[i2], merged[j2])) continue;
|
|
81
|
+
merged[i2] = {
|
|
82
|
+
left: Math.min(merged[i2].left, merged[j2].left),
|
|
83
|
+
top: Math.min(merged[i2].top, merged[j2].top),
|
|
84
|
+
right: Math.max(merged[i2].right, merged[j2].right),
|
|
85
|
+
bottom: Math.max(merged[i2].bottom, merged[j2].bottom)
|
|
86
|
+
};
|
|
87
|
+
merged.splice(j2, 1);
|
|
88
|
+
j2 = i2;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return merged;
|
|
92
|
+
}
|
|
93
|
+
function findCovers(target, rect) {
|
|
94
|
+
const width = window.innerWidth;
|
|
95
|
+
const height = window.innerHeight;
|
|
96
|
+
const covers = [];
|
|
97
|
+
const seen = /* @__PURE__ */ new Set();
|
|
98
|
+
let probed = false;
|
|
99
|
+
let reached = false;
|
|
100
|
+
for (const [fx, fy] of PROBE_POINTS) {
|
|
101
|
+
const x2 = rect.left + rect.width * fx;
|
|
102
|
+
const y2 = rect.top + rect.height * fy;
|
|
103
|
+
if (x2 < 0 || y2 < 0 || x2 >= width || y2 >= height) continue;
|
|
104
|
+
probed = true;
|
|
105
|
+
const hit = document.elementFromPoint(x2, y2);
|
|
106
|
+
if (!hit || hit === target || target.contains(hit)) {
|
|
107
|
+
reached = true;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (seen.has(hit)) continue;
|
|
111
|
+
seen.add(hit);
|
|
112
|
+
if (covers.some(
|
|
113
|
+
(c2) => x2 >= c2.left && x2 < c2.right && y2 >= c2.top && y2 < c2.bottom
|
|
114
|
+
)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
covers.push(coverOf(hit, target, x2, y2));
|
|
118
|
+
}
|
|
119
|
+
if (!probed) return [];
|
|
120
|
+
return reached ? mergeAreas(covers) : null;
|
|
121
|
+
}
|
|
122
|
+
function clipPath(x2, y2, covers) {
|
|
123
|
+
const far = 1e5;
|
|
124
|
+
const points = [
|
|
125
|
+
`${-far}px ${-far}px`,
|
|
126
|
+
`${far}px ${-far}px`,
|
|
127
|
+
`${far}px ${far}px`,
|
|
128
|
+
`${-far}px ${far}px`,
|
|
129
|
+
`${-far}px ${-far}px`
|
|
130
|
+
];
|
|
131
|
+
for (const c2 of covers) {
|
|
132
|
+
const l2 = c2.left - x2;
|
|
133
|
+
const t3 = c2.top - y2;
|
|
134
|
+
const r2 = c2.right - x2;
|
|
135
|
+
const b2 = c2.bottom - y2;
|
|
136
|
+
points.push(
|
|
137
|
+
`${l2}px ${t3}px`,
|
|
138
|
+
`${r2}px ${t3}px`,
|
|
139
|
+
`${r2}px ${b2}px`,
|
|
140
|
+
`${l2}px ${b2}px`,
|
|
141
|
+
`${l2}px ${t3}px`,
|
|
142
|
+
`${-far}px ${-far}px`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return `polygon(evenodd, ${points.join(", ")})`;
|
|
146
|
+
}
|
|
147
|
+
var STYLES = `
|
|
148
|
+
:host {
|
|
149
|
+
all: initial;
|
|
150
|
+
position: fixed;
|
|
151
|
+
inset: 0;
|
|
152
|
+
pointer-events: none;
|
|
153
|
+
z-index: 2147483646;
|
|
154
|
+
contain: strict;
|
|
155
|
+
}
|
|
156
|
+
.box {
|
|
157
|
+
position: absolute;
|
|
158
|
+
top: 0;
|
|
159
|
+
left: 0;
|
|
160
|
+
box-sizing: border-box;
|
|
161
|
+
border-radius: 6px;
|
|
162
|
+
opacity: 0;
|
|
163
|
+
will-change: transform, width, height;
|
|
164
|
+
transition:
|
|
165
|
+
transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
166
|
+
width 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
167
|
+
height 180ms cubic-bezier(0.2, 0.8, 0.2, 1),
|
|
168
|
+
opacity 140ms ease;
|
|
169
|
+
}
|
|
170
|
+
.box[data-visible] { opacity: 1; }
|
|
171
|
+
.box[data-instant], :host([data-scrolling]) .box {
|
|
172
|
+
transition: opacity 140ms ease;
|
|
173
|
+
}
|
|
174
|
+
.hover {
|
|
175
|
+
border: 1.5px solid rgba(79, 70, 229, 0.9);
|
|
176
|
+
background: rgba(79, 70, 229, 0.06);
|
|
177
|
+
}
|
|
178
|
+
.selected {
|
|
179
|
+
border: 2px solid #4f46e5;
|
|
180
|
+
box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.18);
|
|
181
|
+
}
|
|
182
|
+
.label {
|
|
183
|
+
position: absolute;
|
|
184
|
+
left: -2px;
|
|
185
|
+
bottom: 100%;
|
|
186
|
+
margin-bottom: 4px;
|
|
187
|
+
padding: 3px 7px;
|
|
188
|
+
border-radius: 5px;
|
|
189
|
+
background: #4f46e5;
|
|
190
|
+
color: #fff;
|
|
191
|
+
font: 500 11px/1.3 ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
192
|
+
letter-spacing: 0.01em;
|
|
193
|
+
white-space: nowrap;
|
|
194
|
+
display: flex;
|
|
195
|
+
max-width: 240px;
|
|
196
|
+
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.25);
|
|
197
|
+
}
|
|
198
|
+
.text {
|
|
199
|
+
min-width: 0;
|
|
200
|
+
overflow: hidden;
|
|
201
|
+
text-overflow: ellipsis;
|
|
202
|
+
}
|
|
203
|
+
.hover .label { background: rgba(79, 70, 229, 0.92); }
|
|
204
|
+
/* Content of another item than the one open in the editor: still clickable,
|
|
205
|
+
but visibly not part of the item being edited. */
|
|
206
|
+
.hover[data-item-kind='other'] {
|
|
207
|
+
border: 1.5px dashed #a5b4fc;
|
|
208
|
+
background: rgba(100, 116, 139, 0.05);
|
|
209
|
+
}
|
|
210
|
+
.hover[data-item-kind='other'] .label { background: #64748b; }
|
|
211
|
+
.hover[data-item-kind='referenced'] {
|
|
212
|
+
border: 1.5px solid #a5b4fc;
|
|
213
|
+
background: rgba(79, 70, 229, 0.03);
|
|
214
|
+
}
|
|
215
|
+
.hover[data-item-kind='referenced'] .label { background: #818cf8; }
|
|
216
|
+
.box[data-label-covered] .label { visibility: hidden; }
|
|
217
|
+
.box[data-label-inside] .label {
|
|
218
|
+
bottom: auto;
|
|
219
|
+
top: 2px;
|
|
220
|
+
left: 2px;
|
|
221
|
+
margin: 0;
|
|
222
|
+
}
|
|
223
|
+
/* A clickable hover label (edit mode on the live site). Only while shown, so
|
|
224
|
+
a fading box never leaves an invisible click target behind. */
|
|
225
|
+
:host([data-label-action]) .hover[data-visible] .label {
|
|
226
|
+
pointer-events: auto;
|
|
227
|
+
cursor: pointer;
|
|
228
|
+
}
|
|
229
|
+
:host([data-label-action]) .hover .label::after {
|
|
230
|
+
content: '\\2197';
|
|
231
|
+
margin-left: 5px;
|
|
232
|
+
}
|
|
233
|
+
:host([data-label-action]) .hover .label:hover { background: #4338ca; }
|
|
234
|
+
/* Bridges the gap between the label and its element, so the pointer can
|
|
235
|
+
travel from one to the other without the hover ending on the way. */
|
|
236
|
+
:host([data-label-action]) .hover .label::before {
|
|
237
|
+
content: '';
|
|
238
|
+
position: absolute;
|
|
239
|
+
left: 0;
|
|
240
|
+
right: 0;
|
|
241
|
+
top: 100%;
|
|
242
|
+
height: ${LABEL_MARGIN_PX + BOX_PADDING_PX + 2}px;
|
|
243
|
+
}
|
|
244
|
+
@media (prefers-reduced-motion: reduce) {
|
|
245
|
+
.box { transition: opacity 80ms linear; }
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
var VisualEditorOverlay = class {
|
|
249
|
+
constructor(options = {}) {
|
|
250
|
+
this.host = null;
|
|
251
|
+
this.hoverBox = null;
|
|
252
|
+
this.selectedBox = null;
|
|
253
|
+
this.frame = null;
|
|
254
|
+
this.scrollTimeout = null;
|
|
255
|
+
this.resizeObserver = null;
|
|
256
|
+
this.onLabelClick = options.onLabelClick;
|
|
257
|
+
}
|
|
258
|
+
mount() {
|
|
259
|
+
if (this.host) return;
|
|
260
|
+
const host = document.createElement(HOST_TAG);
|
|
261
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
262
|
+
const style = document.createElement("style");
|
|
263
|
+
style.textContent = STYLES;
|
|
264
|
+
shadow.appendChild(style);
|
|
265
|
+
this.hoverBox = this.createBox(shadow, "hover");
|
|
266
|
+
this.selectedBox = this.createBox(shadow, "selected");
|
|
267
|
+
const onLabelClick = this.onLabelClick;
|
|
268
|
+
if (onLabelClick) {
|
|
269
|
+
host.setAttribute("data-label-action", "");
|
|
270
|
+
const hoverBox = this.hoverBox;
|
|
271
|
+
hoverBox.label.addEventListener("click", () => {
|
|
272
|
+
if (hoverBox.target) onLabelClick(hoverBox.target);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
document.documentElement.appendChild(host);
|
|
276
|
+
this.host = host;
|
|
277
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
278
|
+
this.resizeObserver = new ResizeObserver(() => this.schedule());
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
unmount() {
|
|
282
|
+
if (this.frame !== null) cancelAnimationFrame(this.frame);
|
|
283
|
+
if (this.scrollTimeout) clearTimeout(this.scrollTimeout);
|
|
284
|
+
this.frame = null;
|
|
285
|
+
this.scrollTimeout = null;
|
|
286
|
+
this.resizeObserver?.disconnect();
|
|
287
|
+
this.resizeObserver = null;
|
|
288
|
+
this.host?.remove();
|
|
289
|
+
this.host = null;
|
|
290
|
+
this.hoverBox = null;
|
|
291
|
+
this.selectedBox = null;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Whether a pointer event target is this chrome: events from inside the
|
|
295
|
+
* shadow root reach the page retargeted to its host.
|
|
296
|
+
*/
|
|
297
|
+
owns(node) {
|
|
298
|
+
return node !== null && node === this.host;
|
|
299
|
+
}
|
|
300
|
+
getHoverTarget() {
|
|
301
|
+
return this.hoverBox?.target ?? null;
|
|
302
|
+
}
|
|
303
|
+
getSelectedTarget() {
|
|
304
|
+
return this.selectedBox?.target ?? null;
|
|
305
|
+
}
|
|
306
|
+
setHover(target, label, options = {}) {
|
|
307
|
+
if (target) {
|
|
308
|
+
this.hoverBox?.root.setAttribute(
|
|
309
|
+
"data-item-kind",
|
|
310
|
+
options.kind ?? "current"
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
this.retarget(this.hoverBox, target, label);
|
|
314
|
+
}
|
|
315
|
+
setSelected(target, label) {
|
|
316
|
+
this.retarget(this.selectedBox, target, label);
|
|
317
|
+
}
|
|
318
|
+
/** Call on every scroll; positions follow in the next frame, untransitioned. */
|
|
319
|
+
onScroll() {
|
|
320
|
+
if (!this.host) return;
|
|
321
|
+
this.host.setAttribute("data-scrolling", "");
|
|
322
|
+
if (this.scrollTimeout) clearTimeout(this.scrollTimeout);
|
|
323
|
+
this.scrollTimeout = setTimeout(() => {
|
|
324
|
+
this.host?.removeAttribute("data-scrolling");
|
|
325
|
+
this.scrollTimeout = null;
|
|
326
|
+
}, SCROLL_SETTLE_MS);
|
|
327
|
+
this.schedule();
|
|
328
|
+
}
|
|
329
|
+
/** Re-measure both boxes in the next animation frame. */
|
|
330
|
+
schedule() {
|
|
331
|
+
if (this.frame !== null || !this.host) return;
|
|
332
|
+
this.frame = requestAnimationFrame(() => {
|
|
333
|
+
this.frame = null;
|
|
334
|
+
this.render();
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
createBox(shadow, kind) {
|
|
338
|
+
const root = document.createElement("div");
|
|
339
|
+
root.className = `box ${kind}`;
|
|
340
|
+
const label = document.createElement("div");
|
|
341
|
+
label.className = "label";
|
|
342
|
+
const text = document.createElement("span");
|
|
343
|
+
text.className = "text";
|
|
344
|
+
label.appendChild(text);
|
|
345
|
+
root.appendChild(label);
|
|
346
|
+
shadow.appendChild(root);
|
|
347
|
+
return { root, label, text, target: null };
|
|
348
|
+
}
|
|
349
|
+
retarget(box, target, label) {
|
|
350
|
+
if (!box || box.target === target) return;
|
|
351
|
+
const previous = box.target;
|
|
352
|
+
if (previous) this.resizeObserver?.unobserve(previous);
|
|
353
|
+
box.target = target;
|
|
354
|
+
if (target) {
|
|
355
|
+
this.resizeObserver?.observe(target);
|
|
356
|
+
box.text.textContent = label;
|
|
357
|
+
if (!box.root.hasAttribute("data-visible")) {
|
|
358
|
+
box.root.setAttribute("data-instant", "");
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
this.schedule();
|
|
362
|
+
}
|
|
363
|
+
render() {
|
|
364
|
+
const hover = this.hoverBox ? this.measure(this.hoverBox) : null;
|
|
365
|
+
const selected = this.selectedBox ? this.measure(this.selectedBox) : null;
|
|
366
|
+
if (this.hoverBox) this.paint(this.hoverBox, hover, selected);
|
|
367
|
+
if (this.selectedBox) this.paint(this.selectedBox, selected, hover);
|
|
368
|
+
}
|
|
369
|
+
measure(box) {
|
|
370
|
+
const target = box.target;
|
|
371
|
+
if (!target) return null;
|
|
372
|
+
if (!target.isConnected) {
|
|
373
|
+
this.resizeObserver?.unobserve(target);
|
|
374
|
+
box.target = null;
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
const rect = target.getBoundingClientRect();
|
|
378
|
+
if (rect.width === 0 && rect.height === 0) return null;
|
|
379
|
+
const covers = findCovers(target, rect);
|
|
380
|
+
if (!covers) return null;
|
|
381
|
+
return {
|
|
382
|
+
rect,
|
|
383
|
+
covers,
|
|
384
|
+
labelWidth: covers.length ? box.label.offsetWidth : 0,
|
|
385
|
+
labelHeight: covers.length ? box.label.offsetHeight : 0
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
paint(box, measurement, other = null) {
|
|
389
|
+
const root = box.root;
|
|
390
|
+
if (!measurement) {
|
|
391
|
+
root.removeAttribute("data-visible");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const { rect, covers } = measurement;
|
|
395
|
+
const x2 = rect.left - BOX_PADDING_PX;
|
|
396
|
+
const y2 = rect.top - BOX_PADDING_PX;
|
|
397
|
+
const labelAbove = {
|
|
398
|
+
left: x2 - 2,
|
|
399
|
+
top: y2 - LABEL_SPACE_PX,
|
|
400
|
+
right: x2 - 2 + (measurement.labelWidth || Math.min(240, rect.width)),
|
|
401
|
+
bottom: y2
|
|
402
|
+
};
|
|
403
|
+
const labelInside = y2 < LABEL_SPACE_PX || other !== null && other.rect !== rect && overlapsBox(labelAbove, other.rect);
|
|
404
|
+
root.style.transform = `translate3d(${x2}px, ${y2}px, 0)`;
|
|
405
|
+
root.style.width = `${rect.width + BOX_PADDING_PX * 2}px`;
|
|
406
|
+
root.style.height = `${rect.height + BOX_PADDING_PX * 2}px`;
|
|
407
|
+
root.toggleAttribute("data-label-inside", labelInside);
|
|
408
|
+
root.style.clipPath = covers.length ? clipPath(x2, y2, covers) : "";
|
|
409
|
+
const labelLeft = labelInside ? x2 + 2 : x2 - 2;
|
|
410
|
+
const labelTop = labelInside ? y2 + 2 : y2 - LABEL_MARGIN_PX - measurement.labelHeight;
|
|
411
|
+
const label = {
|
|
412
|
+
left: labelLeft,
|
|
413
|
+
top: labelTop,
|
|
414
|
+
right: labelLeft + measurement.labelWidth,
|
|
415
|
+
bottom: labelTop + measurement.labelHeight
|
|
416
|
+
};
|
|
417
|
+
root.toggleAttribute(
|
|
418
|
+
"data-label-covered",
|
|
419
|
+
covers.some((c2) => overlaps(c2, label))
|
|
420
|
+
);
|
|
421
|
+
if (root.hasAttribute("data-instant")) {
|
|
422
|
+
root.setAttribute("data-visible", "");
|
|
423
|
+
requestAnimationFrame(() => root.removeAttribute("data-instant"));
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
root.setAttribute("data-visible", "");
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/core/visual-editor/field-path.ts
|
|
431
|
+
var INDEX_SEGMENT = /^\d+$/;
|
|
432
|
+
var WORD_SEPARATORS = /[_-]+/g;
|
|
433
|
+
function normalizeFieldPath(path) {
|
|
434
|
+
return path.split(".").map(
|
|
435
|
+
(segment) => INDEX_SEGMENT.test(segment) ? String(Number.parseInt(segment, 10)) : segment
|
|
436
|
+
).join(".");
|
|
437
|
+
}
|
|
438
|
+
var GENERIC_LEAVES = /* @__PURE__ */ new Set(["value", "text", "body", "content", "label"]);
|
|
439
|
+
function humanize(name) {
|
|
440
|
+
const words = name.replace(WORD_SEPARATORS, " ").trim();
|
|
441
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
442
|
+
}
|
|
443
|
+
function fieldLabel(path, locale = "en") {
|
|
444
|
+
if (!path) return t("visualEditor.edit", locale);
|
|
445
|
+
const segments = path.split(".").filter((s2) => s2 && s2 !== "items" && !INDEX_SEGMENT.test(s2));
|
|
446
|
+
const name = segments[segments.length - 1];
|
|
447
|
+
if (!name) return t("visualEditor.edit", locale);
|
|
448
|
+
const parent = segments[segments.length - 2];
|
|
449
|
+
if (parent && GENERIC_LEAVES.has(name)) {
|
|
450
|
+
return `${humanize(parent)} \u203A ${humanize(name)}`;
|
|
451
|
+
}
|
|
452
|
+
return humanize(name);
|
|
453
|
+
}
|
|
454
|
+
function findFieldElement(lookup) {
|
|
455
|
+
if (!lookup.field) return null;
|
|
456
|
+
const candidates = document.querySelectorAll(
|
|
457
|
+
"[data-prepr-encoded]"
|
|
458
|
+
);
|
|
459
|
+
if (lookup.entryId) {
|
|
460
|
+
for (const candidate of candidates) {
|
|
461
|
+
if (candidate.getAttribute("data-prepr-entry-id") === lookup.entryId) {
|
|
462
|
+
return candidate;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
for (const path of [lookup.field, lookup.fieldById]) {
|
|
467
|
+
if (!path) continue;
|
|
468
|
+
const wanted = normalizeFieldPath(path);
|
|
469
|
+
for (const candidate of candidates) {
|
|
470
|
+
const field = candidate.getAttribute("data-prepr-field");
|
|
471
|
+
if (!field || normalizeFieldPath(field) !== wanted) continue;
|
|
472
|
+
if (lookup.id && candidate.getAttribute("data-prepr-id") !== lookup.id) {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
return candidate;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
160
481
|
// src/core/stega/dom.ts
|
|
161
482
|
var IGNORED_ANCESTORS = "script, style, noscript";
|
|
162
483
|
function isIgnoredTextNode(node) {
|
|
@@ -211,9 +532,20 @@ function tagEncodedElement(element, decoded) {
|
|
|
211
532
|
element.setAttribute("data-prepr-origin", decoded.origin);
|
|
212
533
|
const { id, field, locale } = parseEditHref(decoded.href);
|
|
213
534
|
setOrRemove(element, "data-prepr-id", id);
|
|
214
|
-
|
|
535
|
+
const fieldHint = element.getAttribute("data-prepr-edit-target")?.trim() || void 0;
|
|
536
|
+
setOrRemove(element, "data-prepr-field", field ?? fieldHint);
|
|
215
537
|
setOrRemove(element, "data-prepr-locale", locale);
|
|
216
538
|
}
|
|
539
|
+
function readEditPayload(element) {
|
|
540
|
+
return {
|
|
541
|
+
href: element.getAttribute("data-prepr-href") ?? "",
|
|
542
|
+
origin: element.getAttribute("data-prepr-origin") ?? "",
|
|
543
|
+
id: element.getAttribute("data-prepr-id") ?? void 0,
|
|
544
|
+
field: element.getAttribute("data-prepr-field") ?? void 0,
|
|
545
|
+
locale: element.getAttribute("data-prepr-locale") ?? void 0,
|
|
546
|
+
entryId: element.getAttribute("data-prepr-entry-id") || void 0
|
|
547
|
+
};
|
|
548
|
+
}
|
|
217
549
|
|
|
218
550
|
// src/core/stega/elements.ts
|
|
219
551
|
var debug2 = createScopedLogger("stega:elements");
|
|
@@ -304,235 +636,22 @@ var StegaElements = class {
|
|
|
304
636
|
attributeFilter: ["class", "style", "hidden", "open", "aria-expanded"]
|
|
305
637
|
});
|
|
306
638
|
}
|
|
307
|
-
/**
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
639
|
+
/**
|
|
640
|
+
* Stops watching for new content. Tags stay on their elements: the page's
|
|
641
|
+
* stega characters are stripped once they are read, so a tag removed here
|
|
642
|
+
* could never be rebuilt when edit mode is turned back on.
|
|
643
|
+
*/
|
|
644
|
+
disconnect() {
|
|
312
645
|
if (this.observer) {
|
|
313
646
|
this.observer.disconnect();
|
|
314
647
|
this.observer = null;
|
|
315
648
|
}
|
|
316
|
-
document.querySelectorAll("[data-prepr-encoded]").forEach((element) => {
|
|
317
|
-
element.removeAttribute("data-prepr-encoded");
|
|
318
|
-
element.removeAttribute("data-prepr-href");
|
|
319
|
-
element.removeAttribute("data-prepr-origin");
|
|
320
|
-
element.removeAttribute("data-prepr-id");
|
|
321
|
-
element.removeAttribute("data-prepr-field");
|
|
322
|
-
element.removeAttribute("data-prepr-locale");
|
|
323
|
-
element.classList.remove("prepr-overlay-active");
|
|
324
|
-
});
|
|
325
649
|
this.elements = void 0;
|
|
326
650
|
}
|
|
327
651
|
};
|
|
328
652
|
|
|
329
|
-
// src/core/stega/overlay.ts
|
|
330
|
-
var debug3 = createScopedLogger("stega:overlay");
|
|
331
|
-
var StegaOverlay = class {
|
|
332
|
-
constructor(onEdit) {
|
|
333
|
-
this.overlay = null;
|
|
334
|
-
this.tooltip = null;
|
|
335
|
-
this.hideTimeout = null;
|
|
336
|
-
this.currentElement = null;
|
|
337
|
-
this.onEdit = onEdit;
|
|
338
|
-
}
|
|
339
|
-
create() {
|
|
340
|
-
const overlay = document.createElement("div");
|
|
341
|
-
overlay.className = "prepr-overlay";
|
|
342
|
-
overlay.style.display = "none";
|
|
343
|
-
const tooltip = document.createElement("div");
|
|
344
|
-
tooltip.className = "prepr-tooltip";
|
|
345
|
-
tooltip.style.display = "none";
|
|
346
|
-
document.body.appendChild(overlay);
|
|
347
|
-
document.body.appendChild(tooltip);
|
|
348
|
-
this.overlay = overlay;
|
|
349
|
-
this.tooltip = tooltip;
|
|
350
|
-
return { overlay, tooltip };
|
|
351
|
-
}
|
|
352
|
-
getTooltip() {
|
|
353
|
-
return this.tooltip;
|
|
354
|
-
}
|
|
355
|
-
hasCurrentElement() {
|
|
356
|
-
return this.currentElement !== null;
|
|
357
|
-
}
|
|
358
|
-
clearHideTimeout() {
|
|
359
|
-
if (this.hideTimeout) {
|
|
360
|
-
clearTimeout(this.hideTimeout);
|
|
361
|
-
this.hideTimeout = null;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
show(element) {
|
|
365
|
-
if (!this.overlay || !this.tooltip) return;
|
|
366
|
-
this.clearHideTimeout();
|
|
367
|
-
if (this.currentElement && this.currentElement !== element) {
|
|
368
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
369
|
-
}
|
|
370
|
-
const rect = element.getBoundingClientRect();
|
|
371
|
-
const href = element.getAttribute("data-prepr-href");
|
|
372
|
-
const origin = element.getAttribute("data-prepr-origin");
|
|
373
|
-
const id = element.getAttribute("data-prepr-id") ?? void 0;
|
|
374
|
-
const field = element.getAttribute("data-prepr-field") ?? void 0;
|
|
375
|
-
const locale = element.getAttribute("data-prepr-locale") ?? void 0;
|
|
376
|
-
debug3.log("showing overlay", { href, origin });
|
|
377
|
-
const overlay = this.overlay;
|
|
378
|
-
overlay.style.display = "block";
|
|
379
|
-
overlay.style.top = `${rect.top + window.scrollY - 2}px`;
|
|
380
|
-
overlay.style.left = `${rect.left + window.scrollX - 4}px`;
|
|
381
|
-
overlay.style.width = `${rect.width + 8}px`;
|
|
382
|
-
overlay.style.height = `${rect.height + 4}px`;
|
|
383
|
-
const tooltip = this.tooltip;
|
|
384
|
-
if (href && origin) {
|
|
385
|
-
const MIN_WIDTH_FOR_TEXT = 80;
|
|
386
|
-
const isCompact = rect.width < MIN_WIDTH_FOR_TEXT;
|
|
387
|
-
tooltip.textContent = isCompact ? "\u2197" : `${origin} \u2197`;
|
|
388
|
-
tooltip.style.display = "block";
|
|
389
|
-
tooltip.style.minWidth = isCompact ? "auto" : "80px";
|
|
390
|
-
requestAnimationFrame(() => {
|
|
391
|
-
let top = rect.top + window.scrollY - tooltip.clientHeight - 2;
|
|
392
|
-
let left = rect.right + 4 - tooltip.clientWidth;
|
|
393
|
-
const minTop = window.scrollY + 4;
|
|
394
|
-
const maxTop = window.scrollY + window.innerHeight - tooltip.clientHeight - 4;
|
|
395
|
-
const minLeft = window.scrollX + 4;
|
|
396
|
-
const maxLeft = window.scrollX + window.innerWidth - tooltip.clientWidth - 4;
|
|
397
|
-
const below = top < minTop;
|
|
398
|
-
if (below) {
|
|
399
|
-
top = rect.bottom + window.scrollY + 2;
|
|
400
|
-
}
|
|
401
|
-
top = Math.max(minTop, Math.min(top, maxTop));
|
|
402
|
-
left = Math.max(minLeft, Math.min(left, maxLeft));
|
|
403
|
-
tooltip.dataset.preprPlacement = below ? "below" : "above";
|
|
404
|
-
tooltip.style.top = `${top}px`;
|
|
405
|
-
tooltip.style.left = `${left}px`;
|
|
406
|
-
});
|
|
407
|
-
tooltip.onclick = () => {
|
|
408
|
-
this.onEdit({ href, origin, id, field, locale });
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
this.currentElement = element;
|
|
412
|
-
element.classList.add("prepr-overlay-active");
|
|
413
|
-
}
|
|
414
|
-
hideImmediate() {
|
|
415
|
-
this.clearHideTimeout();
|
|
416
|
-
if (this.overlay) this.overlay.style.display = "none";
|
|
417
|
-
if (this.tooltip) this.tooltip.style.display = "none";
|
|
418
|
-
if (this.currentElement) {
|
|
419
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
420
|
-
this.currentElement = null;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
hide() {
|
|
424
|
-
if (!this.overlay || !this.tooltip) return;
|
|
425
|
-
this.clearHideTimeout();
|
|
426
|
-
this.hideTimeout = setTimeout(() => this.hideImmediate(), 100);
|
|
427
|
-
}
|
|
428
|
-
cleanup() {
|
|
429
|
-
this.clearHideTimeout();
|
|
430
|
-
if (this.overlay) {
|
|
431
|
-
this.overlay.style.display = "none";
|
|
432
|
-
this.overlay.parentNode?.removeChild(this.overlay);
|
|
433
|
-
this.overlay = null;
|
|
434
|
-
}
|
|
435
|
-
if (this.tooltip) {
|
|
436
|
-
this.tooltip.style.display = "none";
|
|
437
|
-
this.tooltip.parentNode?.removeChild(this.tooltip);
|
|
438
|
-
this.tooltip = null;
|
|
439
|
-
}
|
|
440
|
-
if (this.currentElement) {
|
|
441
|
-
this.currentElement.classList.remove("prepr-overlay-active");
|
|
442
|
-
this.currentElement = null;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
// src/core/stega/proximity.ts
|
|
448
|
-
var debug4 = createScopedLogger("stega:proximity");
|
|
449
|
-
var StegaProximity = class {
|
|
450
|
-
constructor() {
|
|
451
|
-
this.highlighted = /* @__PURE__ */ new Set();
|
|
452
|
-
this.visible = /* @__PURE__ */ new Set();
|
|
453
|
-
this.observer = null;
|
|
454
|
-
this.getEncodedElements = createElementCache(
|
|
455
|
-
"[data-prepr-encoded]",
|
|
456
|
-
200
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
/** Call after the encoded element set changes — rebuilds the observer. */
|
|
460
|
-
refreshObserving() {
|
|
461
|
-
try {
|
|
462
|
-
if (typeof IntersectionObserver === "undefined") return;
|
|
463
|
-
if (this.observer) {
|
|
464
|
-
this.observer.disconnect();
|
|
465
|
-
this.observer = null;
|
|
466
|
-
}
|
|
467
|
-
this.visible = /* @__PURE__ */ new Set();
|
|
468
|
-
this.observer = new IntersectionObserver(
|
|
469
|
-
(entries) => {
|
|
470
|
-
entries.forEach((entry) => {
|
|
471
|
-
const el = entry.target;
|
|
472
|
-
if (entry.isIntersecting) {
|
|
473
|
-
this.visible.add(el);
|
|
474
|
-
} else {
|
|
475
|
-
this.visible.delete(el);
|
|
476
|
-
}
|
|
477
|
-
});
|
|
478
|
-
},
|
|
479
|
-
{ root: null, rootMargin: "0px", threshold: 0 }
|
|
480
|
-
);
|
|
481
|
-
const nodes = this.getEncodedElements();
|
|
482
|
-
nodes.forEach((el) => this.observer.observe(el));
|
|
483
|
-
debug4.log("observing", nodes.length, "encoded elements");
|
|
484
|
-
} catch (error) {
|
|
485
|
-
debug4.log("error setting up IntersectionObserver", error);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
stopObserving() {
|
|
489
|
-
if (this.observer) {
|
|
490
|
-
this.observer.disconnect();
|
|
491
|
-
this.observer = null;
|
|
492
|
-
this.visible.clear();
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
updateElementGradients(cursorX, cursorY) {
|
|
496
|
-
const candidates = this.visible.size > 0 ? Array.from(this.visible) : Array.from(this.getEncodedElements());
|
|
497
|
-
const newHighlighted = /* @__PURE__ */ new Set();
|
|
498
|
-
candidates.forEach((element) => {
|
|
499
|
-
const rect = element.getBoundingClientRect();
|
|
500
|
-
const distance = Math.min(
|
|
501
|
-
Math.abs(cursorX - rect.left),
|
|
502
|
-
Math.abs(cursorX - rect.right),
|
|
503
|
-
Math.abs(cursorY - rect.top),
|
|
504
|
-
Math.abs(cursorY - rect.bottom)
|
|
505
|
-
);
|
|
506
|
-
if (distance < 150) {
|
|
507
|
-
const relativeX = cursorX - rect.left;
|
|
508
|
-
const relativeY = cursorY - rect.top;
|
|
509
|
-
element.style.setProperty("--cursor-x", `${relativeX}px`);
|
|
510
|
-
element.style.setProperty("--cursor-y", `${relativeY}px`);
|
|
511
|
-
const baseGradientSize = Math.max(
|
|
512
|
-
150,
|
|
513
|
-
Math.max(rect.width, rect.height) * 1.1
|
|
514
|
-
);
|
|
515
|
-
const distanceScale = Math.max(0, (400 - distance) / 400);
|
|
516
|
-
const gradientSize = baseGradientSize * distanceScale;
|
|
517
|
-
element.style.setProperty("--gradient-size", `${gradientSize}px`);
|
|
518
|
-
element.classList.add("prepr-proximity-highlight");
|
|
519
|
-
newHighlighted.add(element);
|
|
520
|
-
} else {
|
|
521
|
-
element.classList.remove("prepr-proximity-highlight");
|
|
522
|
-
}
|
|
523
|
-
});
|
|
524
|
-
this.highlighted = newHighlighted;
|
|
525
|
-
}
|
|
526
|
-
clearAllHighlights() {
|
|
527
|
-
this.highlighted.forEach(
|
|
528
|
-
(element) => element.classList.remove("prepr-proximity-highlight")
|
|
529
|
-
);
|
|
530
|
-
this.highlighted.clear();
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
|
|
534
653
|
// src/core/stega/stega.generated.ts
|
|
535
|
-
var STEGA_STYLES = '
|
|
654
|
+
var STEGA_STYLES = '@property --prepr-presence{syntax:"<number>";inherits:true;initial-value:1}[data-prepr-encoded]{outline:1px dashed rgba(79,70,229,calc(.3 * var(--prepr-presence,1)));outline-offset:2px}';
|
|
536
655
|
|
|
537
656
|
// src/core/stega/styles.ts
|
|
538
657
|
var STEGA_STYLE_MARKER = "data-prepr-stega";
|
|
@@ -552,46 +671,80 @@ function removeStegaStyles() {
|
|
|
552
671
|
}
|
|
553
672
|
|
|
554
673
|
// src/core/stega/scan.ts
|
|
555
|
-
var
|
|
674
|
+
var debug3 = createScopedLogger("stega:scan");
|
|
675
|
+
var ENCODED_SELECTOR = "[data-prepr-encoded]";
|
|
676
|
+
var PRESENCE_FADE_MS = 400;
|
|
556
677
|
var EDITOR_MODE_STYLES = "[data-prepr-encoded]{cursor:pointer}";
|
|
678
|
+
function fadeInPresence() {
|
|
679
|
+
const root = document.documentElement;
|
|
680
|
+
if (typeof root.animate !== "function") return;
|
|
681
|
+
if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
|
|
682
|
+
root.animate(
|
|
683
|
+
{ "--prepr-presence": ["0", "1"] },
|
|
684
|
+
{ duration: PRESENCE_FADE_MS, easing: "ease-out" }
|
|
685
|
+
);
|
|
686
|
+
}
|
|
557
687
|
function createStegaController(opts) {
|
|
558
688
|
const tooltipEnabled = opts.tooltip ?? true;
|
|
559
|
-
const
|
|
560
|
-
const proximity = new StegaProximity();
|
|
689
|
+
const locale = opts.locale ?? "en";
|
|
561
690
|
const elements = new StegaElements();
|
|
691
|
+
const overlay = new VisualEditorOverlay({
|
|
692
|
+
onLabelClick: (target) => opts.onEdit(readEditPayload(target))
|
|
693
|
+
});
|
|
562
694
|
let initialized = false;
|
|
563
|
-
let
|
|
564
|
-
|
|
565
|
-
|
|
695
|
+
let pointerFrame = null;
|
|
696
|
+
let pointerTarget = null;
|
|
697
|
+
let pointerX = 0;
|
|
698
|
+
let pointerY = 0;
|
|
699
|
+
let hitTestPending = false;
|
|
700
|
+
const updateHover = () => {
|
|
701
|
+
pointerFrame = null;
|
|
702
|
+
if (hitTestPending) {
|
|
703
|
+
hitTestPending = false;
|
|
704
|
+
pointerTarget = document.elementFromPoint(pointerX, pointerY);
|
|
705
|
+
}
|
|
706
|
+
if (overlay.owns(pointerTarget)) return;
|
|
707
|
+
const encoded = pointerTarget instanceof Element ? pointerTarget.closest(ENCODED_SELECTOR) : null;
|
|
708
|
+
overlay.setHover(
|
|
709
|
+
encoded,
|
|
710
|
+
encoded ? fieldLabel(encoded.getAttribute("data-prepr-field"), locale) : ""
|
|
711
|
+
);
|
|
566
712
|
};
|
|
567
|
-
const
|
|
568
|
-
if (
|
|
569
|
-
|
|
713
|
+
const scheduleHover = () => {
|
|
714
|
+
if (pointerFrame === null) {
|
|
715
|
+
pointerFrame = requestAnimationFrame(updateHover);
|
|
570
716
|
}
|
|
571
717
|
};
|
|
718
|
+
const handlePointer = (e2) => {
|
|
719
|
+
pointerTarget = e2.target;
|
|
720
|
+
pointerX = e2.clientX;
|
|
721
|
+
pointerY = e2.clientY;
|
|
722
|
+
hitTestPending = false;
|
|
723
|
+
scheduleHover();
|
|
724
|
+
};
|
|
725
|
+
const handlePointerLeave = () => {
|
|
726
|
+
pointerTarget = null;
|
|
727
|
+
hitTestPending = false;
|
|
728
|
+
overlay.setHover(null, "");
|
|
729
|
+
};
|
|
572
730
|
const handleScroll = () => {
|
|
573
|
-
overlay.
|
|
574
|
-
|
|
731
|
+
overlay.onScroll();
|
|
732
|
+
if (pointerTarget === null) return;
|
|
733
|
+
hitTestPending = true;
|
|
734
|
+
scheduleHover();
|
|
575
735
|
};
|
|
736
|
+
const handleResize = () => overlay.schedule();
|
|
576
737
|
const handleClick = (e2) => {
|
|
577
738
|
const target = e2.target;
|
|
578
|
-
const encoded = target?.closest(
|
|
579
|
-
"[data-prepr-encoded]"
|
|
580
|
-
);
|
|
739
|
+
const encoded = target?.closest(ENCODED_SELECTOR);
|
|
581
740
|
if (!encoded) return;
|
|
582
741
|
e2.preventDefault();
|
|
583
742
|
e2.stopPropagation();
|
|
584
|
-
opts.onEdit(
|
|
585
|
-
href: encoded.getAttribute("data-prepr-href") ?? "",
|
|
586
|
-
origin: encoded.getAttribute("data-prepr-origin") ?? "",
|
|
587
|
-
id: encoded.getAttribute("data-prepr-id") ?? void 0,
|
|
588
|
-
field: encoded.getAttribute("data-prepr-field") ?? void 0,
|
|
589
|
-
locale: encoded.getAttribute("data-prepr-locale") ?? void 0
|
|
590
|
-
});
|
|
743
|
+
opts.onEdit(readEditPayload(encoded));
|
|
591
744
|
};
|
|
592
745
|
const start = () => {
|
|
593
746
|
if (initialized) {
|
|
594
|
-
|
|
747
|
+
debug3.log("already initialized, skipping setup");
|
|
595
748
|
return;
|
|
596
749
|
}
|
|
597
750
|
if (!tooltipEnabled) {
|
|
@@ -601,91 +754,85 @@ function createStegaController(opts) {
|
|
|
601
754
|
});
|
|
602
755
|
document.addEventListener("click", handleClick, { capture: true });
|
|
603
756
|
initialized = true;
|
|
604
|
-
|
|
757
|
+
debug3.log("stega controller started (headless)");
|
|
605
758
|
return;
|
|
606
759
|
}
|
|
607
760
|
injectStegaStyles();
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
tooltip.addEventListener("mouseleave", handleTooltipMouseLeave);
|
|
761
|
+
fadeInPresence();
|
|
762
|
+
overlay.mount();
|
|
611
763
|
elements.scanDocument(true);
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
const target = mouseEvent.target;
|
|
617
|
-
if (!target) return;
|
|
618
|
-
if (target.closest(".prepr-tooltip")) return;
|
|
619
|
-
proximity.updateElementGradients(mouseEvent.clientX, mouseEvent.clientY);
|
|
620
|
-
const encoded = target.closest("[data-prepr-encoded]");
|
|
621
|
-
if (encoded) {
|
|
622
|
-
overlay.show(encoded);
|
|
623
|
-
} else {
|
|
624
|
-
overlay.hide();
|
|
625
|
-
}
|
|
626
|
-
}, 16);
|
|
627
|
-
document.addEventListener("mousemove", throttledMouseMove, {
|
|
628
|
-
passive: true
|
|
764
|
+
elements.setupMutationObserver(() => {
|
|
765
|
+
if (pointerTarget === null) return;
|
|
766
|
+
hitTestPending = true;
|
|
767
|
+
scheduleHover();
|
|
629
768
|
});
|
|
769
|
+
document.addEventListener("pointermove", handlePointer, { passive: true });
|
|
770
|
+
document.addEventListener("pointerdown", handlePointer, { passive: true });
|
|
771
|
+
document.documentElement.addEventListener(
|
|
772
|
+
"pointerleave",
|
|
773
|
+
handlePointerLeave
|
|
774
|
+
);
|
|
630
775
|
window.addEventListener("scroll", handleScroll, {
|
|
631
776
|
capture: true,
|
|
632
777
|
passive: true
|
|
633
778
|
});
|
|
779
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
634
780
|
initialized = true;
|
|
635
|
-
|
|
781
|
+
debug3.log("stega controller started");
|
|
636
782
|
};
|
|
637
783
|
const stop = () => {
|
|
638
784
|
if (!initialized) return;
|
|
785
|
+
initialized = false;
|
|
639
786
|
if (!tooltipEnabled) {
|
|
640
787
|
document.removeEventListener("click", handleClick, { capture: true });
|
|
641
|
-
elements.
|
|
788
|
+
elements.disconnect();
|
|
642
789
|
removeStegaStyles();
|
|
643
|
-
|
|
644
|
-
debug5.log("stega controller stopped (editor mode)");
|
|
790
|
+
debug3.log("stega controller stopped (headless)");
|
|
645
791
|
return;
|
|
646
792
|
}
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
793
|
+
if (pointerFrame !== null) cancelAnimationFrame(pointerFrame);
|
|
794
|
+
pointerFrame = null;
|
|
795
|
+
pointerTarget = null;
|
|
796
|
+
hitTestPending = false;
|
|
797
|
+
document.removeEventListener("pointermove", handlePointer);
|
|
798
|
+
document.removeEventListener("pointerdown", handlePointer);
|
|
799
|
+
document.documentElement.removeEventListener(
|
|
800
|
+
"pointerleave",
|
|
801
|
+
handlePointerLeave
|
|
802
|
+
);
|
|
652
803
|
window.removeEventListener("scroll", handleScroll, { capture: true });
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
tooltip.removeEventListener("mouseleave", handleTooltipMouseLeave);
|
|
657
|
-
}
|
|
658
|
-
overlay.cleanup();
|
|
659
|
-
proximity.clearAllHighlights();
|
|
660
|
-
proximity.stopObserving();
|
|
661
|
-
elements.cleanup();
|
|
804
|
+
window.removeEventListener("resize", handleResize);
|
|
805
|
+
overlay.unmount();
|
|
806
|
+
elements.disconnect();
|
|
662
807
|
removeStegaStyles();
|
|
663
|
-
|
|
664
|
-
debug5.log("stega controller stopped");
|
|
808
|
+
debug3.log("stega controller stopped");
|
|
665
809
|
};
|
|
666
810
|
return { start, stop };
|
|
667
811
|
}
|
|
668
812
|
|
|
669
813
|
// src/core/stega/auto-clean.ts
|
|
670
814
|
import { vercelStegaSplit } from "@vercel/stega";
|
|
671
|
-
var
|
|
815
|
+
var debug4 = createScopedLogger("stega:auto-clean");
|
|
816
|
+
var cleanedNodes = /* @__PURE__ */ new WeakMap();
|
|
672
817
|
function createStegaAutoClean(options) {
|
|
673
818
|
const stripText = options?.enabled ?? true;
|
|
674
819
|
let observer = null;
|
|
675
820
|
let processing = false;
|
|
676
|
-
let cleaned = /* @__PURE__ */ new WeakMap();
|
|
677
821
|
let pendingHidden = /* @__PURE__ */ new Set();
|
|
678
822
|
let pendingDirty = /* @__PURE__ */ new Set();
|
|
679
823
|
let debounceTimeout = null;
|
|
680
824
|
function cleanTextNode(textNode) {
|
|
681
825
|
if (isIgnoredTextNode(textNode)) return false;
|
|
682
826
|
const textContent = textNode.textContent;
|
|
683
|
-
const info =
|
|
827
|
+
const info = cleanedNodes.get(textNode);
|
|
684
828
|
const settled = stripText ? info?.cleaned : info?.original;
|
|
685
829
|
if (info && textContent === settled) {
|
|
686
830
|
if (!info.decoded) return false;
|
|
687
831
|
const shown = resolveEditTarget(textNode);
|
|
688
|
-
if (!shown)
|
|
832
|
+
if (!shown) {
|
|
833
|
+
pendingHidden.add(textNode);
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
689
836
|
pendingHidden.delete(textNode);
|
|
690
837
|
if (shown.hasAttribute("data-prepr-encoded")) return false;
|
|
691
838
|
tagEncodedElement(shown, info.decoded);
|
|
@@ -696,7 +843,7 @@ function createStegaAutoClean(options) {
|
|
|
696
843
|
if (!encoded || stripped === textContent) return false;
|
|
697
844
|
const decoded = decodeStega(textContent);
|
|
698
845
|
if (!decoded?.href && !stripText) return false;
|
|
699
|
-
|
|
846
|
+
cleanedNodes.set(textNode, {
|
|
700
847
|
original: textContent,
|
|
701
848
|
cleaned: stripped,
|
|
702
849
|
decoded: decoded?.href ? decoded : null
|
|
@@ -711,7 +858,7 @@ function createStegaAutoClean(options) {
|
|
|
711
858
|
}
|
|
712
859
|
pendingHidden.add(textNode);
|
|
713
860
|
} catch (error) {
|
|
714
|
-
|
|
861
|
+
debug4.log("error cleaning text node", error);
|
|
715
862
|
}
|
|
716
863
|
return false;
|
|
717
864
|
}
|
|
@@ -738,7 +885,7 @@ function createStegaAutoClean(options) {
|
|
|
738
885
|
}
|
|
739
886
|
if (mutation.type === "characterData" && mutation.target.nodeType === Node.TEXT_NODE) {
|
|
740
887
|
const textNode = mutation.target;
|
|
741
|
-
const info =
|
|
888
|
+
const info = cleanedNodes.get(textNode);
|
|
742
889
|
const text = textNode.textContent ?? "";
|
|
743
890
|
const settled = stripText ? info?.cleaned : info?.original;
|
|
744
891
|
if (text !== settled && vercelStegaSplit(text).encoded) {
|
|
@@ -781,7 +928,16 @@ function createStegaAutoClean(options) {
|
|
|
781
928
|
retryPendingHidden();
|
|
782
929
|
}, 50);
|
|
783
930
|
}
|
|
931
|
+
let resizeTimeout = null;
|
|
932
|
+
function handleResize() {
|
|
933
|
+
if (pendingHidden.size === 0 || resizeTimeout) return;
|
|
934
|
+
resizeTimeout = setTimeout(() => {
|
|
935
|
+
resizeTimeout = null;
|
|
936
|
+
retryPendingHidden();
|
|
937
|
+
}, 100);
|
|
938
|
+
}
|
|
784
939
|
function setupObserver() {
|
|
940
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
785
941
|
observer = new MutationObserver(handleMutations);
|
|
786
942
|
observer.observe(document.body, {
|
|
787
943
|
childList: true,
|
|
@@ -797,27 +953,31 @@ function createStegaAutoClean(options) {
|
|
|
797
953
|
if (observer || typeof window === "undefined") return;
|
|
798
954
|
scanAndClean();
|
|
799
955
|
setupObserver();
|
|
800
|
-
|
|
956
|
+
debug4.log("stega auto-clean started");
|
|
801
957
|
}
|
|
802
958
|
function stop() {
|
|
803
959
|
if (debounceTimeout) {
|
|
804
960
|
clearTimeout(debounceTimeout);
|
|
805
961
|
debounceTimeout = null;
|
|
806
962
|
}
|
|
963
|
+
if (resizeTimeout) {
|
|
964
|
+
clearTimeout(resizeTimeout);
|
|
965
|
+
resizeTimeout = null;
|
|
966
|
+
}
|
|
807
967
|
if (observer) {
|
|
808
968
|
observer.disconnect();
|
|
809
969
|
observer = null;
|
|
970
|
+
window.removeEventListener("resize", handleResize);
|
|
810
971
|
}
|
|
811
|
-
cleaned = /* @__PURE__ */ new WeakMap();
|
|
812
972
|
pendingHidden = /* @__PURE__ */ new Set();
|
|
813
973
|
pendingDirty = /* @__PURE__ */ new Set();
|
|
814
|
-
|
|
974
|
+
debug4.log("stega auto-clean stopped");
|
|
815
975
|
}
|
|
816
976
|
return { start, stop };
|
|
817
977
|
}
|
|
818
978
|
|
|
819
979
|
// src/core/stega/scroll-to-field.ts
|
|
820
|
-
var
|
|
980
|
+
var debug5 = createScopedLogger("stega:scroll");
|
|
821
981
|
var FLASH_DURATION_MS = 1200;
|
|
822
982
|
var flashTimeout = null;
|
|
823
983
|
function scrollToField(request) {
|
|
@@ -834,7 +994,7 @@ function scrollToField(request) {
|
|
|
834
994
|
break;
|
|
835
995
|
}
|
|
836
996
|
if (!target) {
|
|
837
|
-
|
|
997
|
+
debug5.log("no element found for field", field);
|
|
838
998
|
return false;
|
|
839
999
|
}
|
|
840
1000
|
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -888,7 +1048,7 @@ function createToolbarStore(initial = {}) {
|
|
|
888
1048
|
var n;
|
|
889
1049
|
var l;
|
|
890
1050
|
var u;
|
|
891
|
-
var
|
|
1051
|
+
var t2;
|
|
892
1052
|
var i;
|
|
893
1053
|
var r;
|
|
894
1054
|
var o;
|
|
@@ -1130,7 +1290,7 @@ n = w.slice, l = { __e: function(n2, l2, u3, t3) {
|
|
|
1130
1290
|
n2 = l3;
|
|
1131
1291
|
}
|
|
1132
1292
|
throw n2;
|
|
1133
|
-
} }, u = 0,
|
|
1293
|
+
} }, u = 0, t2 = function(n2) {
|
|
1134
1294
|
return null != n2 && void 0 === n2.constructor;
|
|
1135
1295
|
}, C.prototype.setState = function(n2, l2) {
|
|
1136
1296
|
var u3;
|
|
@@ -1166,7 +1326,7 @@ var CLOSE_ICON_SVG = `<svg width="10" height="10" viewBox="0 0 10 10" fill="none
|
|
|
1166
1326
|
</svg>`;
|
|
1167
1327
|
|
|
1168
1328
|
// src/core/ui/toolbar.generated.ts
|
|
1169
|
-
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:-webkit-sticky;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}.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}}";
|
|
1329
|
+
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:-webkit-sticky;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}}";
|
|
1170
1330
|
|
|
1171
1331
|
// ../../node_modules/.pnpm/preact@10.29.7/node_modules/preact/jsx-runtime/dist/jsxRuntime.mjs
|
|
1172
1332
|
var f2 = 0;
|
|
@@ -1872,6 +2032,7 @@ var PARAM_SEGMENT = "prepr_preview_segment";
|
|
|
1872
2032
|
var PARAM_VARIANT = "prepr_preview_ab";
|
|
1873
2033
|
var PARAM_HIDE_BAR = "prepr_hide_bar";
|
|
1874
2034
|
var ONE_YEAR_SECONDS = 60 * 60 * 24 * 365;
|
|
2035
|
+
var HEADER_LIVE_RENDER = "x-prepr-live";
|
|
1875
2036
|
|
|
1876
2037
|
// src/core/cookies.ts
|
|
1877
2038
|
function getCookie(name) {
|
|
@@ -1913,173 +2074,6 @@ function crossSiteCookieOptions() {
|
|
|
1913
2074
|
return { sameSite: "None", secure: true };
|
|
1914
2075
|
}
|
|
1915
2076
|
|
|
1916
|
-
// src/core/locales/en.json
|
|
1917
|
-
var en_default = {
|
|
1918
|
-
common: {
|
|
1919
|
-
viewingAs: "Viewing as:",
|
|
1920
|
-
user: "User",
|
|
1921
|
-
off: "Off",
|
|
1922
|
-
on: "On",
|
|
1923
|
-
reset: "Reset",
|
|
1924
|
-
toolbar: "Toolbar"
|
|
1925
|
-
},
|
|
1926
|
-
adaptiveContent: {
|
|
1927
|
-
adaptiveContent: "Adaptive content",
|
|
1928
|
-
enablePreview: "Preview mode",
|
|
1929
|
-
segment: "Segment",
|
|
1930
|
-
ABVariant: "A/B variant",
|
|
1931
|
-
chooseSegment: "Choose segment",
|
|
1932
|
-
searchSegments: "Search segments",
|
|
1933
|
-
none: "No segments",
|
|
1934
|
-
allOtherUsers: "All other users",
|
|
1935
|
-
offDescription: "View the site as a real user (use cookies)",
|
|
1936
|
-
onDescription: "Use the toolbar to simulate segments and A/B variants"
|
|
1937
|
-
},
|
|
1938
|
-
editingTools: {
|
|
1939
|
-
editingTools: "Editing tools",
|
|
1940
|
-
editMode: "Edit mode",
|
|
1941
|
-
ariaCloseEditMode: "Close edit mode"
|
|
1942
|
-
}
|
|
1943
|
-
};
|
|
1944
|
-
|
|
1945
|
-
// src/core/locales/nl.json
|
|
1946
|
-
var nl_default = {
|
|
1947
|
-
common: {
|
|
1948
|
-
viewingAs: "Bekijken als:",
|
|
1949
|
-
user: "Gebruiker",
|
|
1950
|
-
off: "Uit",
|
|
1951
|
-
on: "Aan",
|
|
1952
|
-
reset: "Resetten",
|
|
1953
|
-
toolbar: "Toolbar"
|
|
1954
|
-
},
|
|
1955
|
-
adaptiveContent: {
|
|
1956
|
-
adaptiveContent: "Adaptieve inhoud",
|
|
1957
|
-
enablePreview: "Preview modus",
|
|
1958
|
-
segment: "Segment",
|
|
1959
|
-
ABVariant: "A/B-variant",
|
|
1960
|
-
chooseSegment: "Kies segment",
|
|
1961
|
-
searchSegments: "Zoek segmenten",
|
|
1962
|
-
none: "Geen segmenten",
|
|
1963
|
-
allOtherUsers: "Alle andere gebruikers",
|
|
1964
|
-
offDescription: "Bekijk de site als een echte gebruiker (gebruik cookies)",
|
|
1965
|
-
onDescription: "Gebruik de werkbalk om segmenten en A/B-varianten te simuleren"
|
|
1966
|
-
},
|
|
1967
|
-
editingTools: {
|
|
1968
|
-
editingTools: "Editing Tools",
|
|
1969
|
-
editMode: "Edit mode",
|
|
1970
|
-
ariaCloseEditMode: "Edit mode sluiten"
|
|
1971
|
-
}
|
|
1972
|
-
};
|
|
1973
|
-
|
|
1974
|
-
// src/core/i18n.ts
|
|
1975
|
-
var dictionaries = {
|
|
1976
|
-
en: en_default,
|
|
1977
|
-
nl: nl_default
|
|
1978
|
-
};
|
|
1979
|
-
var LOCALES = Object.keys(dictionaries);
|
|
1980
|
-
function isLocale(value) {
|
|
1981
|
-
return typeof value === "string" && value in dictionaries;
|
|
1982
|
-
}
|
|
1983
|
-
function getDict(locale) {
|
|
1984
|
-
return isLocale(locale) ? dictionaries[locale] : dictionaries.en;
|
|
1985
|
-
}
|
|
1986
|
-
function getFromPath(obj, path) {
|
|
1987
|
-
return path.split(".").reduce(
|
|
1988
|
-
(acc, key) => acc && typeof acc === "object" ? acc[key] : void 0,
|
|
1989
|
-
obj
|
|
1990
|
-
);
|
|
1991
|
-
}
|
|
1992
|
-
function format(message, vars) {
|
|
1993
|
-
if (!vars) return message;
|
|
1994
|
-
return Object.keys(vars).reduce(
|
|
1995
|
-
(acc, k2) => acc.replace(new RegExp(`\\{${k2}\\}`, "g"), String(vars[k2])),
|
|
1996
|
-
message
|
|
1997
|
-
);
|
|
1998
|
-
}
|
|
1999
|
-
function t2(key, locale, vars) {
|
|
2000
|
-
const dict = getDict(locale);
|
|
2001
|
-
const msg = getFromPath(dict, key);
|
|
2002
|
-
if (typeof msg === "string") return format(msg, vars);
|
|
2003
|
-
return key;
|
|
2004
|
-
}
|
|
2005
|
-
|
|
2006
|
-
// src/core/iframe-bridge.ts
|
|
2007
|
-
var debug8 = createScopedLogger("iframe-bridge");
|
|
2008
|
-
var EDITOR_BASE_DOMAIN = "prepr.io";
|
|
2009
|
-
function isAllowedEditorOrigin(origin, allowed) {
|
|
2010
|
-
if (allowed) {
|
|
2011
|
-
if (allowed.includes("*")) return origin !== "null";
|
|
2012
|
-
return allowed.includes(origin);
|
|
2013
|
-
}
|
|
2014
|
-
let url;
|
|
2015
|
-
try {
|
|
2016
|
-
url = new URL(origin);
|
|
2017
|
-
} catch {
|
|
2018
|
-
return false;
|
|
2019
|
-
}
|
|
2020
|
-
if (url.protocol !== "https:" || url.port !== "") return false;
|
|
2021
|
-
const suffix = `.${EDITOR_BASE_DOMAIN}`;
|
|
2022
|
-
if (!url.hostname.endsWith(suffix)) return false;
|
|
2023
|
-
const label = url.hostname.slice(0, -suffix.length);
|
|
2024
|
-
return label.length > 0 && !label.includes(".");
|
|
2025
|
-
}
|
|
2026
|
-
function createIframeBridge(store, options = {}) {
|
|
2027
|
-
const allowedOrigins = options.allowedEditorOrigins;
|
|
2028
|
-
const onScrollToField = options.onScrollToField;
|
|
2029
|
-
let parentOrigin = null;
|
|
2030
|
-
const onKeyDown = (event) => {
|
|
2031
|
-
const key = event.key.toLowerCase();
|
|
2032
|
-
const blocked = (event.ctrlKey || event.metaKey) && ["s", "p", "l"].includes(key);
|
|
2033
|
-
if (blocked) event.preventDefault();
|
|
2034
|
-
};
|
|
2035
|
-
const onMessage = (evt) => {
|
|
2036
|
-
const data = evt?.data;
|
|
2037
|
-
if (data?.event === "prepr:initVE" && !parentOrigin) {
|
|
2038
|
-
if (!isAllowedEditorOrigin(evt.origin, allowedOrigins)) {
|
|
2039
|
-
debug8.warn("ignored editor handshake from origin", evt.origin);
|
|
2040
|
-
return;
|
|
2041
|
-
}
|
|
2042
|
-
parentOrigin = evt.origin;
|
|
2043
|
-
setTrustedParentOrigin(parentOrigin);
|
|
2044
|
-
if (data.scrollPosition != null) {
|
|
2045
|
-
const top = data.scrollPosition;
|
|
2046
|
-
setTimeout(() => window.scrollTo(0, top), 1);
|
|
2047
|
-
}
|
|
2048
|
-
store?.set({ previewMode: true, editMode: data.editMode ?? true });
|
|
2049
|
-
}
|
|
2050
|
-
if (!parentOrigin || evt.origin !== parentOrigin) return;
|
|
2051
|
-
if (data?.event === "prepr:getScrollPosition") {
|
|
2052
|
-
const currentScrollY = window.scrollY || document.documentElement.scrollTop;
|
|
2053
|
-
sendPreprEvent("getScrollPosition", { value: currentScrollY });
|
|
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
|
-
}
|
|
2064
|
-
};
|
|
2065
|
-
return {
|
|
2066
|
-
start() {
|
|
2067
|
-
const features = store?.get().features;
|
|
2068
|
-
sendPreprEvent("loaded", features ? { features } : void 0, {
|
|
2069
|
-
allowUntrustedTarget: true
|
|
2070
|
-
});
|
|
2071
|
-
window.addEventListener("keydown", onKeyDown);
|
|
2072
|
-
window.addEventListener("message", onMessage);
|
|
2073
|
-
},
|
|
2074
|
-
stop() {
|
|
2075
|
-
window.removeEventListener("keydown", onKeyDown);
|
|
2076
|
-
window.removeEventListener("message", onMessage);
|
|
2077
|
-
parentOrigin = null;
|
|
2078
|
-
setTrustedParentOrigin(null);
|
|
2079
|
-
}
|
|
2080
|
-
};
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
2077
|
// src/core/toolbar-change-handler.ts
|
|
2084
2078
|
function cookieOpts() {
|
|
2085
2079
|
return {
|
|
@@ -2178,8 +2172,726 @@ function splitPath(full) {
|
|
|
2178
2172
|
return [full.slice(0, index), full.slice(index + 1)];
|
|
2179
2173
|
}
|
|
2180
2174
|
|
|
2175
|
+
// src/core/visual-editor/asset-images.ts
|
|
2176
|
+
var MAX_URL_LENGTH = 4096;
|
|
2177
|
+
var MIN_KEY_SEGMENTS = 2;
|
|
2178
|
+
var TRANSFORM_OPTION = /^[a-z]{1,3}_[\w.-]*$/i;
|
|
2179
|
+
var FORMAT_PLACEHOLDER = "{format}";
|
|
2180
|
+
var PROXY_PARAMS = ["url", "href", "src", "image"];
|
|
2181
|
+
var ABSOLUTE_HTTP = /^https?:\/\//i;
|
|
2182
|
+
var SOURCE_URL_ATTRIBUTES = ["src", "data-src"];
|
|
2183
|
+
var SOURCE_SET_ATTRIBUTES = ["srcset", "data-srcset"];
|
|
2184
|
+
var ASSET_ATTR = "data-prepr-asset";
|
|
2185
|
+
var ASSET_SOURCE_ATTRIBUTES = [
|
|
2186
|
+
...SOURCE_URL_ATTRIBUTES,
|
|
2187
|
+
...SOURCE_SET_ATTRIBUTES
|
|
2188
|
+
];
|
|
2189
|
+
var TAG_ATTRIBUTES = [
|
|
2190
|
+
"data-prepr-encoded",
|
|
2191
|
+
"data-prepr-id",
|
|
2192
|
+
"data-prepr-field",
|
|
2193
|
+
"data-prepr-entry-id",
|
|
2194
|
+
ASSET_ATTR
|
|
2195
|
+
];
|
|
2196
|
+
function decodeSegment(segment) {
|
|
2197
|
+
try {
|
|
2198
|
+
return decodeURIComponent(segment);
|
|
2199
|
+
} catch {
|
|
2200
|
+
return segment;
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
function isTransformSegment(segment) {
|
|
2204
|
+
return segment === FORMAT_PLACEHOLDER || segment.split(",").every((option) => TRANSFORM_OPTION.test(option));
|
|
2205
|
+
}
|
|
2206
|
+
function assetKey(url, depth = 0) {
|
|
2207
|
+
if (!url || url.length > MAX_URL_LENGTH) return null;
|
|
2208
|
+
let parsed;
|
|
2209
|
+
try {
|
|
2210
|
+
parsed = new URL(url, "https://base.invalid");
|
|
2211
|
+
} catch {
|
|
2212
|
+
return null;
|
|
2213
|
+
}
|
|
2214
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
|
|
2215
|
+
if (depth === 0) {
|
|
2216
|
+
for (const name of PROXY_PARAMS) {
|
|
2217
|
+
const inner = parsed.searchParams.get(name);
|
|
2218
|
+
if (inner && ABSOLUTE_HTTP.test(inner)) return assetKey(inner, 1);
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
const segments = parsed.pathname.split("/").filter(Boolean).map(decodeSegment);
|
|
2222
|
+
const file = segments.pop();
|
|
2223
|
+
if (!file) return null;
|
|
2224
|
+
const kept = segments.filter((segment) => !isTransformSegment(segment));
|
|
2225
|
+
kept.push(file);
|
|
2226
|
+
return kept.length >= MIN_KEY_SEGMENTS ? kept.join("/") : null;
|
|
2227
|
+
}
|
|
2228
|
+
function srcsetUrls(srcset) {
|
|
2229
|
+
const urls = [];
|
|
2230
|
+
let expectUrl = true;
|
|
2231
|
+
for (const token of srcset.split(/\s+/)) {
|
|
2232
|
+
if (!token) continue;
|
|
2233
|
+
if (!expectUrl) {
|
|
2234
|
+
const comma = token.indexOf(",");
|
|
2235
|
+
if (comma === -1) continue;
|
|
2236
|
+
expectUrl = true;
|
|
2237
|
+
const rest = token.slice(comma + 1);
|
|
2238
|
+
if (!rest) continue;
|
|
2239
|
+
urls.push(rest.replace(/,+$/, ""));
|
|
2240
|
+
expectUrl = rest.endsWith(",");
|
|
2241
|
+
continue;
|
|
2242
|
+
}
|
|
2243
|
+
const url = token.replace(/^,+|,+$/g, "");
|
|
2244
|
+
if (url) urls.push(url);
|
|
2245
|
+
expectUrl = token.endsWith(",");
|
|
2246
|
+
}
|
|
2247
|
+
return urls;
|
|
2248
|
+
}
|
|
2249
|
+
function createAssetIndex(assets) {
|
|
2250
|
+
const index = /* @__PURE__ */ new Map();
|
|
2251
|
+
for (const asset of assets) {
|
|
2252
|
+
const key = assetKey(asset.key);
|
|
2253
|
+
if (key && !index.has(key)) index.set(key, asset);
|
|
2254
|
+
}
|
|
2255
|
+
return index;
|
|
2256
|
+
}
|
|
2257
|
+
function urlsOf(img) {
|
|
2258
|
+
const urls = [];
|
|
2259
|
+
const collect = (el) => {
|
|
2260
|
+
for (const name of SOURCE_URL_ATTRIBUTES) {
|
|
2261
|
+
const value = el.getAttribute(name);
|
|
2262
|
+
if (value) urls.push(value);
|
|
2263
|
+
}
|
|
2264
|
+
for (const name of SOURCE_SET_ATTRIBUTES) {
|
|
2265
|
+
const value = el.getAttribute(name);
|
|
2266
|
+
if (value) urls.push(...srcsetUrls(value));
|
|
2267
|
+
}
|
|
2268
|
+
};
|
|
2269
|
+
collect(img);
|
|
2270
|
+
const picture = img.parentElement;
|
|
2271
|
+
if (picture?.tagName === "PICTURE") {
|
|
2272
|
+
for (const source of picture.children) {
|
|
2273
|
+
if (source.tagName === "SOURCE") collect(source);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
return urls;
|
|
2277
|
+
}
|
|
2278
|
+
function matchAsset(img, index, keys) {
|
|
2279
|
+
for (const url of urlsOf(img)) {
|
|
2280
|
+
let key = keys.get(url);
|
|
2281
|
+
if (key === void 0) {
|
|
2282
|
+
key = assetKey(url);
|
|
2283
|
+
keys.set(url, key);
|
|
2284
|
+
}
|
|
2285
|
+
const asset = key ? index.get(key) : void 0;
|
|
2286
|
+
if (asset) return asset;
|
|
2287
|
+
}
|
|
2288
|
+
return void 0;
|
|
2289
|
+
}
|
|
2290
|
+
function setIfChanged(el, name, value) {
|
|
2291
|
+
if (value === null) {
|
|
2292
|
+
if (el.hasAttribute(name)) el.removeAttribute(name);
|
|
2293
|
+
} else if (el.getAttribute(name) !== value) {
|
|
2294
|
+
el.setAttribute(name, value);
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
function untagAssetImage(img) {
|
|
2298
|
+
for (const name of TAG_ATTRIBUTES) img.removeAttribute(name);
|
|
2299
|
+
}
|
|
2300
|
+
function tagAssetImages(index, itemId, keys) {
|
|
2301
|
+
for (const img of document.getElementsByTagName("img")) {
|
|
2302
|
+
const ours = img.hasAttribute(ASSET_ATTR);
|
|
2303
|
+
if (!ours && img.hasAttribute("data-prepr-encoded")) continue;
|
|
2304
|
+
const asset = index.size ? matchAsset(img, index, keys) : void 0;
|
|
2305
|
+
if (!asset) {
|
|
2306
|
+
if (ours) untagAssetImage(img);
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
setIfChanged(img, "data-prepr-encoded", "");
|
|
2310
|
+
setIfChanged(img, ASSET_ATTR, "");
|
|
2311
|
+
setIfChanged(img, "data-prepr-id", itemId);
|
|
2312
|
+
setIfChanged(img, "data-prepr-field", asset.field);
|
|
2313
|
+
setIfChanged(img, "data-prepr-entry-id", asset.entryId ?? null);
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
function untagAssetImages() {
|
|
2317
|
+
document.querySelectorAll(`img[${ASSET_ATTR}]`).forEach(untagAssetImage);
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
// src/core/visual-editor/navigation-guard.ts
|
|
2321
|
+
var debug6 = createScopedLogger("navigation-guard");
|
|
2322
|
+
var trimSlash = (path) => path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
2323
|
+
function resolve(url) {
|
|
2324
|
+
try {
|
|
2325
|
+
return new URL(url, document.baseURI);
|
|
2326
|
+
} catch {
|
|
2327
|
+
return null;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
function leavesPage(url) {
|
|
2331
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
2332
|
+
const here = window.location;
|
|
2333
|
+
return url.origin !== here.origin || trimSlash(url.pathname) !== trimSlash(here.pathname);
|
|
2334
|
+
}
|
|
2335
|
+
function leavingLinkHref(target) {
|
|
2336
|
+
if (!(target instanceof Element)) return null;
|
|
2337
|
+
const link = target.closest("a[href]");
|
|
2338
|
+
if (!link || link.hasAttribute("download")) return null;
|
|
2339
|
+
const linkTarget = link.getAttribute("target");
|
|
2340
|
+
if (linkTarget && linkTarget !== "_self") return null;
|
|
2341
|
+
const href = link.getAttribute("href") ?? "";
|
|
2342
|
+
if (href.startsWith("#")) return null;
|
|
2343
|
+
const url = resolve(href);
|
|
2344
|
+
return url && leavesPage(url) ? url.href : null;
|
|
2345
|
+
}
|
|
2346
|
+
function submitterAttr(submitter, attr) {
|
|
2347
|
+
if (!submitter?.hasAttribute(attr)) return null;
|
|
2348
|
+
if (attr === "formaction") return submitter.formAction;
|
|
2349
|
+
if (attr === "formmethod") return submitter.formMethod;
|
|
2350
|
+
return submitter.formTarget;
|
|
2351
|
+
}
|
|
2352
|
+
function submissionUrl(form, submitter) {
|
|
2353
|
+
const method = (submitterAttr(submitter, "formmethod") ?? form.method).toLowerCase();
|
|
2354
|
+
if (method === "dialog") return null;
|
|
2355
|
+
const target = submitterAttr(submitter, "formtarget") ?? form.target;
|
|
2356
|
+
if (target && target !== "_self") return null;
|
|
2357
|
+
const url = resolve(
|
|
2358
|
+
submitterAttr(submitter, "formaction") ?? (form.action || location.href)
|
|
2359
|
+
);
|
|
2360
|
+
if (!url) return null;
|
|
2361
|
+
if (method === "get") {
|
|
2362
|
+
let data;
|
|
2363
|
+
try {
|
|
2364
|
+
data = new FormData(form, submitter);
|
|
2365
|
+
} catch {
|
|
2366
|
+
data = new FormData(form);
|
|
2367
|
+
}
|
|
2368
|
+
const query = new URLSearchParams();
|
|
2369
|
+
data.forEach((value, key) => {
|
|
2370
|
+
query.append(key, typeof value === "string" ? value : value.name);
|
|
2371
|
+
});
|
|
2372
|
+
url.search = query.toString();
|
|
2373
|
+
}
|
|
2374
|
+
return url;
|
|
2375
|
+
}
|
|
2376
|
+
function createNavigationGuard(opts) {
|
|
2377
|
+
let active = false;
|
|
2378
|
+
let permitted = null;
|
|
2379
|
+
let pendingForm = null;
|
|
2380
|
+
let originalPush = null;
|
|
2381
|
+
let guardedPush = null;
|
|
2382
|
+
const isPermitted = (url) => permitted === url.href;
|
|
2383
|
+
const guarding = () => active && opts.showsEditedContent();
|
|
2384
|
+
const report = (href) => {
|
|
2385
|
+
debug6.log("blocked navigation to", href);
|
|
2386
|
+
opts.onBlocked(href);
|
|
2387
|
+
};
|
|
2388
|
+
const onSubmit = (e2) => {
|
|
2389
|
+
if (e2.defaultPrevented || !(e2.target instanceof HTMLFormElement)) return;
|
|
2390
|
+
if (!guarding()) return;
|
|
2391
|
+
const submitter = e2.submitter ?? null;
|
|
2392
|
+
const url = submissionUrl(e2.target, submitter);
|
|
2393
|
+
if (!url || !leavesPage(url) || isPermitted(url)) return;
|
|
2394
|
+
e2.preventDefault();
|
|
2395
|
+
pendingForm = { form: e2.target, submitter, href: url.href };
|
|
2396
|
+
report(url.href);
|
|
2397
|
+
};
|
|
2398
|
+
const onNavigate = (event) => {
|
|
2399
|
+
const e2 = event;
|
|
2400
|
+
if (e2.navigationType === "traverse" || e2.navigationType === "reload") {
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
if (!e2.cancelable || e2.hashChange || e2.downloadRequest !== null) return;
|
|
2404
|
+
if (!guarding()) return;
|
|
2405
|
+
if (e2.navigationType === "replace" && e2.destination.sameDocument) return;
|
|
2406
|
+
const url = resolve(e2.destination.url);
|
|
2407
|
+
if (!url || !leavesPage(url) || isPermitted(url)) return;
|
|
2408
|
+
e2.preventDefault();
|
|
2409
|
+
report(url.href);
|
|
2410
|
+
if (e2.destination.sameDocument) window.location.reload();
|
|
2411
|
+
};
|
|
2412
|
+
const navigation = () => window.navigation;
|
|
2413
|
+
function installPushGuard() {
|
|
2414
|
+
const original = history.pushState;
|
|
2415
|
+
const guard = function(data, unused, url) {
|
|
2416
|
+
if (guarding() && guardedPush === guard && url != null) {
|
|
2417
|
+
const target = resolve(url);
|
|
2418
|
+
if (target && leavesPage(target) && !isPermitted(target)) {
|
|
2419
|
+
report(target.href);
|
|
2420
|
+
window.location.reload();
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
return original.call(this, data, unused, url);
|
|
2425
|
+
};
|
|
2426
|
+
originalPush = original;
|
|
2427
|
+
guardedPush = guard;
|
|
2428
|
+
history.pushState = guard;
|
|
2429
|
+
}
|
|
2430
|
+
function uninstallPushGuard() {
|
|
2431
|
+
if (history.pushState === guardedPush && originalPush) {
|
|
2432
|
+
history.pushState = originalPush;
|
|
2433
|
+
}
|
|
2434
|
+
originalPush = null;
|
|
2435
|
+
guardedPush = null;
|
|
2436
|
+
}
|
|
2437
|
+
return {
|
|
2438
|
+
start() {
|
|
2439
|
+
if (active) return;
|
|
2440
|
+
active = true;
|
|
2441
|
+
installPushGuard();
|
|
2442
|
+
window.addEventListener("submit", onSubmit);
|
|
2443
|
+
navigation()?.addEventListener("navigate", onNavigate);
|
|
2444
|
+
},
|
|
2445
|
+
stop() {
|
|
2446
|
+
if (!active) return;
|
|
2447
|
+
active = false;
|
|
2448
|
+
uninstallPushGuard();
|
|
2449
|
+
window.removeEventListener("submit", onSubmit);
|
|
2450
|
+
navigation()?.removeEventListener("navigate", onNavigate);
|
|
2451
|
+
pendingForm = null;
|
|
2452
|
+
},
|
|
2453
|
+
isActive: guarding,
|
|
2454
|
+
block(href) {
|
|
2455
|
+
report(href);
|
|
2456
|
+
},
|
|
2457
|
+
permit(href) {
|
|
2458
|
+
permitted = resolve(href)?.href ?? null;
|
|
2459
|
+
},
|
|
2460
|
+
allow(href) {
|
|
2461
|
+
const url = resolve(href);
|
|
2462
|
+
if (!url || url.protocol !== "http:" && url.protocol !== "https:") {
|
|
2463
|
+
debug6.warn("ignored navigation to unsupported URL");
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
permitted = url.href;
|
|
2467
|
+
const form = pendingForm;
|
|
2468
|
+
pendingForm = null;
|
|
2469
|
+
if (form && form.href === url.href && form.form.isConnected) {
|
|
2470
|
+
try {
|
|
2471
|
+
form.form.requestSubmit(form.submitter ?? void 0);
|
|
2472
|
+
} catch {
|
|
2473
|
+
form.form.requestSubmit();
|
|
2474
|
+
}
|
|
2475
|
+
return;
|
|
2476
|
+
}
|
|
2477
|
+
window.location.assign(url.href);
|
|
2478
|
+
}
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
// src/core/visual-editor/index.ts
|
|
2483
|
+
var debug7 = createScopedLogger("visual-editor");
|
|
2484
|
+
var ENCODED_SELECTOR2 = "[data-prepr-encoded]";
|
|
2485
|
+
var REFERENCE_ATTR = "data-prepr-reference";
|
|
2486
|
+
var REFERENCE_SELECTOR = `[${REFERENCE_ATTR}]`;
|
|
2487
|
+
var CURSOR_STYLE_MARKER = "data-prepr-visual-editor";
|
|
2488
|
+
var CURSOR_STYLES = `${ENCODED_SELECTOR2}{cursor:pointer}html[data-prepr-ve-suspended] ${ENCODED_SELECTOR2}{cursor:auto}`;
|
|
2489
|
+
var SUSPENDED_ATTR = "data-prepr-ve-suspended";
|
|
2490
|
+
var FIND_RETRY_MS = 150;
|
|
2491
|
+
var FIND_RETRIES = 6;
|
|
2492
|
+
var MAX_CACHED_KEYS = 2e3;
|
|
2493
|
+
var LAYOUT_MUTATIONS = {
|
|
2494
|
+
childList: true,
|
|
2495
|
+
subtree: true,
|
|
2496
|
+
characterData: true,
|
|
2497
|
+
attributes: true,
|
|
2498
|
+
attributeFilter: [
|
|
2499
|
+
"class",
|
|
2500
|
+
"style",
|
|
2501
|
+
"hidden",
|
|
2502
|
+
"open",
|
|
2503
|
+
"data-prepr-encoded",
|
|
2504
|
+
// An image swapped by a carousel or lazy loader can show another asset.
|
|
2505
|
+
...ASSET_SOURCE_ATTRIBUTES
|
|
2506
|
+
]
|
|
2507
|
+
};
|
|
2508
|
+
function scrollIntoFrame(el) {
|
|
2509
|
+
const rect = el.getBoundingClientRect();
|
|
2510
|
+
const offset = Math.max(0, (window.innerHeight - rect.height) / 2);
|
|
2511
|
+
window.scrollTo({
|
|
2512
|
+
top: Math.max(0, window.scrollY + rect.top - offset),
|
|
2513
|
+
behavior: "smooth"
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
var isVisible = (el) => typeof el.checkVisibility === "function" ? el.checkVisibility() : el.getClientRects().length > 0;
|
|
2517
|
+
function createVisualEditor(opts) {
|
|
2518
|
+
const overlay = new VisualEditorOverlay();
|
|
2519
|
+
let running = false;
|
|
2520
|
+
let currentItemId = null;
|
|
2521
|
+
let locale = opts.locale ?? "en";
|
|
2522
|
+
let suspended = false;
|
|
2523
|
+
let pointerFrame = null;
|
|
2524
|
+
let lastPointerTarget = null;
|
|
2525
|
+
let lastPointerX = 0;
|
|
2526
|
+
let lastPointerY = 0;
|
|
2527
|
+
let mutationFrame = null;
|
|
2528
|
+
let mutationObserver = null;
|
|
2529
|
+
let selection = null;
|
|
2530
|
+
let findTimeout = null;
|
|
2531
|
+
let settlePendingFind = null;
|
|
2532
|
+
let guardEnabled = false;
|
|
2533
|
+
let assetIndex = /* @__PURE__ */ new Map();
|
|
2534
|
+
const assetKeys = /* @__PURE__ */ new Map();
|
|
2535
|
+
const guard = createNavigationGuard({
|
|
2536
|
+
onBlocked: (href) => opts.onNavigationBlocked?.(href),
|
|
2537
|
+
showsEditedContent: () => showsEditedContent()
|
|
2538
|
+
});
|
|
2539
|
+
const encodedFrom = (node) => node instanceof Element ? node.closest(ENCODED_SELECTOR2) : null;
|
|
2540
|
+
const labelFor = (el) => fieldLabel(el.getAttribute("data-prepr-field"), locale);
|
|
2541
|
+
let references = null;
|
|
2542
|
+
let ownComponents = /* @__PURE__ */ new Set();
|
|
2543
|
+
const isCurrentItem = (el) => {
|
|
2544
|
+
if (currentItemId === null) return true;
|
|
2545
|
+
const id = el.getAttribute("data-prepr-id");
|
|
2546
|
+
return !id || id === currentItemId || ownComponents.has(id);
|
|
2547
|
+
};
|
|
2548
|
+
const showsEditedContent = () => {
|
|
2549
|
+
for (const el of document.querySelectorAll(ENCODED_SELECTOR2)) {
|
|
2550
|
+
if (isCurrentItem(el) && isVisible(el)) return true;
|
|
2551
|
+
}
|
|
2552
|
+
return false;
|
|
2553
|
+
};
|
|
2554
|
+
const linkedIdOf = (el) => {
|
|
2555
|
+
if (!references) return null;
|
|
2556
|
+
const id = el.getAttribute("data-prepr-id") ?? "";
|
|
2557
|
+
if (references.has(id)) return id;
|
|
2558
|
+
let container = el.closest(REFERENCE_SELECTOR);
|
|
2559
|
+
while (container) {
|
|
2560
|
+
const linked = container.getAttribute(REFERENCE_ATTR) ?? "";
|
|
2561
|
+
if (references.has(linked)) return linked;
|
|
2562
|
+
container = container.parentElement?.closest(REFERENCE_SELECTOR) ?? null;
|
|
2563
|
+
}
|
|
2564
|
+
return null;
|
|
2565
|
+
};
|
|
2566
|
+
const itemKind = (el) => {
|
|
2567
|
+
if (isCurrentItem(el)) return "current";
|
|
2568
|
+
if (!references) return "other";
|
|
2569
|
+
return linkedIdOf(el) ? "referenced" : null;
|
|
2570
|
+
};
|
|
2571
|
+
const referenceFieldOf = (el) => {
|
|
2572
|
+
const linked = linkedIdOf(el);
|
|
2573
|
+
return linked ? references?.get(linked) : void 0;
|
|
2574
|
+
};
|
|
2575
|
+
const hoverLabel = (el, kind) => {
|
|
2576
|
+
if (kind === "other") {
|
|
2577
|
+
return `${labelFor(el)} \xB7 ${t("visualEditor.otherItem", locale)}`;
|
|
2578
|
+
}
|
|
2579
|
+
if (kind === "referenced") {
|
|
2580
|
+
const via = referenceFieldOf(el);
|
|
2581
|
+
const prefix = via ? `${fieldLabel(via, locale)} \u203A ` : "";
|
|
2582
|
+
return `${prefix}${labelFor(el)} \xB7 ${t("visualEditor.linkedItem", locale)}`;
|
|
2583
|
+
}
|
|
2584
|
+
return labelFor(el);
|
|
2585
|
+
};
|
|
2586
|
+
const setSuspended = (value) => {
|
|
2587
|
+
if (suspended === value) return;
|
|
2588
|
+
suspended = value;
|
|
2589
|
+
document.documentElement.toggleAttribute(SUSPENDED_ATTR, value);
|
|
2590
|
+
if (value) overlay.setHover(null, "");
|
|
2591
|
+
};
|
|
2592
|
+
const updateHover = () => {
|
|
2593
|
+
pointerFrame = null;
|
|
2594
|
+
overlay.schedule();
|
|
2595
|
+
if (suspended) return;
|
|
2596
|
+
const encoded = encodedFrom(lastPointerTarget);
|
|
2597
|
+
const target = encoded === overlay.getSelectedTarget() ? null : encoded;
|
|
2598
|
+
if (!target) {
|
|
2599
|
+
overlay.setHover(null, "");
|
|
2600
|
+
return;
|
|
2601
|
+
}
|
|
2602
|
+
const kind = itemKind(target);
|
|
2603
|
+
if (!kind) {
|
|
2604
|
+
overlay.setHover(null, "");
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2607
|
+
overlay.setHover(target, hoverLabel(target, kind), { kind });
|
|
2608
|
+
};
|
|
2609
|
+
const handlePointerMove = (e2) => {
|
|
2610
|
+
setSuspended(e2.altKey);
|
|
2611
|
+
lastPointerTarget = e2.target;
|
|
2612
|
+
lastPointerX = e2.clientX;
|
|
2613
|
+
lastPointerY = e2.clientY;
|
|
2614
|
+
if (pointerFrame === null) {
|
|
2615
|
+
pointerFrame = requestAnimationFrame(updateHover);
|
|
2616
|
+
}
|
|
2617
|
+
};
|
|
2618
|
+
const handlePointerLeave = () => {
|
|
2619
|
+
lastPointerTarget = null;
|
|
2620
|
+
overlay.setHover(null, "");
|
|
2621
|
+
};
|
|
2622
|
+
const handleClick = (e2) => {
|
|
2623
|
+
if (e2.altKey) {
|
|
2624
|
+
const href = leavingLinkHref(e2.target);
|
|
2625
|
+
if (href && guard.isActive()) guard.permit(href);
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
const encoded = encodedFrom(e2.target);
|
|
2629
|
+
const kind = encoded ? itemKind(encoded) : null;
|
|
2630
|
+
if (!encoded || !kind) {
|
|
2631
|
+
clearSelection();
|
|
2632
|
+
if (e2.ctrlKey || e2.metaKey || e2.shiftKey || !showsEditedContent()) return;
|
|
2633
|
+
const href = leavingLinkHref(e2.target);
|
|
2634
|
+
if (!href) return;
|
|
2635
|
+
e2.preventDefault();
|
|
2636
|
+
if (guard.isActive()) {
|
|
2637
|
+
e2.stopPropagation();
|
|
2638
|
+
guard.block(href);
|
|
2639
|
+
}
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
e2.preventDefault();
|
|
2643
|
+
e2.stopPropagation();
|
|
2644
|
+
const item = listItemIn(encoded, e2.target);
|
|
2645
|
+
if (kind === "current")
|
|
2646
|
+
select(item?.element ?? encoded, encoded, item?.index);
|
|
2647
|
+
const referenceField = kind === "referenced" ? referenceFieldOf(encoded) : void 0;
|
|
2648
|
+
const linkedId = kind === "referenced" ? linkedIdOf(encoded) : null;
|
|
2649
|
+
opts.onEdit({
|
|
2650
|
+
...readEditPayload(encoded),
|
|
2651
|
+
...linkedId && { id: linkedId },
|
|
2652
|
+
...item && { block: item.index },
|
|
2653
|
+
...referenceField && { referenceField }
|
|
2654
|
+
});
|
|
2655
|
+
};
|
|
2656
|
+
const handleKey = (e2) => {
|
|
2657
|
+
if (e2.key === "Alt") setSuspended(e2.type === "keydown");
|
|
2658
|
+
if (e2.type === "keydown" && e2.key === "Escape") {
|
|
2659
|
+
clearSelection();
|
|
2660
|
+
}
|
|
2661
|
+
};
|
|
2662
|
+
const handleBlur = () => setSuspended(false);
|
|
2663
|
+
const handleScroll = () => overlay.onScroll();
|
|
2664
|
+
const handleResize = () => overlay.schedule();
|
|
2665
|
+
const handleSettled = () => overlay.schedule();
|
|
2666
|
+
function listItemIn(encoded, target) {
|
|
2667
|
+
if (!(target instanceof Element)) return null;
|
|
2668
|
+
const li = target.closest("li");
|
|
2669
|
+
if (!li || !encoded.contains(li) || !li.parentElement) return null;
|
|
2670
|
+
const items = Array.from(li.parentElement.children).filter(
|
|
2671
|
+
(child) => child.tagName === "LI"
|
|
2672
|
+
);
|
|
2673
|
+
return { element: li, index: items.indexOf(li) };
|
|
2674
|
+
}
|
|
2675
|
+
function nthListItem(encoded, block) {
|
|
2676
|
+
const list = encoded.querySelector("ul, ol");
|
|
2677
|
+
if (!list) return null;
|
|
2678
|
+
const items = Array.from(list.children).filter(
|
|
2679
|
+
(child) => child.tagName === "LI"
|
|
2680
|
+
);
|
|
2681
|
+
return items[block] ?? null;
|
|
2682
|
+
}
|
|
2683
|
+
function select(el, field, block) {
|
|
2684
|
+
const entryId = field.getAttribute("data-prepr-entry-id");
|
|
2685
|
+
selection = {
|
|
2686
|
+
lookup: {
|
|
2687
|
+
field: field.getAttribute("data-prepr-field") ?? "",
|
|
2688
|
+
id: field.getAttribute("data-prepr-id") ?? void 0,
|
|
2689
|
+
...entryId && { entryId },
|
|
2690
|
+
...block !== void 0 && { block }
|
|
2691
|
+
},
|
|
2692
|
+
label: field
|
|
2693
|
+
};
|
|
2694
|
+
overlay.setSelected(el, labelFor(field));
|
|
2695
|
+
if (overlay.getHoverTarget() === el) overlay.setHover(null, "");
|
|
2696
|
+
}
|
|
2697
|
+
function clearSelection() {
|
|
2698
|
+
selection = null;
|
|
2699
|
+
overlay.setSelected(null, "");
|
|
2700
|
+
}
|
|
2701
|
+
function reselect() {
|
|
2702
|
+
if (!selection) return;
|
|
2703
|
+
const current = overlay.getSelectedTarget();
|
|
2704
|
+
if (current?.isConnected && selection.label.isConnected) return;
|
|
2705
|
+
const found = findFieldElement(selection.lookup);
|
|
2706
|
+
if (!found || !isCurrentItem(found)) {
|
|
2707
|
+
overlay.setSelected(null, "");
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2710
|
+
const { block } = selection.lookup;
|
|
2711
|
+
const el = block !== void 0 ? nthListItem(found, block) ?? found : found;
|
|
2712
|
+
select(el, found, block);
|
|
2713
|
+
}
|
|
2714
|
+
function tagAssets() {
|
|
2715
|
+
if (assetKeys.size > MAX_CACHED_KEYS) assetKeys.clear();
|
|
2716
|
+
tagAssetImages(assetIndex, currentItemId, assetKeys);
|
|
2717
|
+
}
|
|
2718
|
+
const handleLayoutFrame = () => {
|
|
2719
|
+
mutationFrame = null;
|
|
2720
|
+
if (assetIndex.size) tagAssets();
|
|
2721
|
+
reselect();
|
|
2722
|
+
const hovered = overlay.getHoverTarget();
|
|
2723
|
+
if (hovered && !hovered.isConnected) {
|
|
2724
|
+
lastPointerTarget = document.elementFromPoint(lastPointerX, lastPointerY);
|
|
2725
|
+
updateHover();
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
overlay.schedule();
|
|
2729
|
+
};
|
|
2730
|
+
const handleMutations = () => {
|
|
2731
|
+
if (mutationFrame !== null) return;
|
|
2732
|
+
if (!selection && !overlay.getHoverTarget() && !assetIndex.size) return;
|
|
2733
|
+
mutationFrame = requestAnimationFrame(handleLayoutFrame);
|
|
2734
|
+
};
|
|
2735
|
+
function injectCursorStyles() {
|
|
2736
|
+
if (document.querySelector(`style[${CURSOR_STYLE_MARKER}]`)) return;
|
|
2737
|
+
const style = document.createElement("style");
|
|
2738
|
+
style.setAttribute(CURSOR_STYLE_MARKER, "");
|
|
2739
|
+
style.textContent = CURSOR_STYLES;
|
|
2740
|
+
document.head.appendChild(style);
|
|
2741
|
+
}
|
|
2742
|
+
function start() {
|
|
2743
|
+
if (running) return;
|
|
2744
|
+
running = true;
|
|
2745
|
+
overlay.mount();
|
|
2746
|
+
injectCursorStyles();
|
|
2747
|
+
document.addEventListener("pointermove", handlePointerMove, {
|
|
2748
|
+
passive: true
|
|
2749
|
+
});
|
|
2750
|
+
document.documentElement.addEventListener(
|
|
2751
|
+
"pointerleave",
|
|
2752
|
+
handlePointerLeave
|
|
2753
|
+
);
|
|
2754
|
+
document.addEventListener("click", handleClick, { capture: true });
|
|
2755
|
+
window.addEventListener("keydown", handleKey);
|
|
2756
|
+
window.addEventListener("keyup", handleKey);
|
|
2757
|
+
window.addEventListener("blur", handleBlur);
|
|
2758
|
+
window.addEventListener("scroll", handleScroll, {
|
|
2759
|
+
capture: true,
|
|
2760
|
+
passive: true
|
|
2761
|
+
});
|
|
2762
|
+
window.addEventListener("resize", handleResize, { passive: true });
|
|
2763
|
+
document.addEventListener("transitionend", handleSettled, {
|
|
2764
|
+
capture: true,
|
|
2765
|
+
passive: true
|
|
2766
|
+
});
|
|
2767
|
+
document.addEventListener("animationend", handleSettled, {
|
|
2768
|
+
capture: true,
|
|
2769
|
+
passive: true
|
|
2770
|
+
});
|
|
2771
|
+
mutationObserver = new MutationObserver(handleMutations);
|
|
2772
|
+
mutationObserver.observe(document.body, LAYOUT_MUTATIONS);
|
|
2773
|
+
if (assetIndex.size) tagAssets();
|
|
2774
|
+
syncGuard();
|
|
2775
|
+
debug7.log("visual editor started");
|
|
2776
|
+
}
|
|
2777
|
+
function stop() {
|
|
2778
|
+
if (!running) return;
|
|
2779
|
+
running = false;
|
|
2780
|
+
if (pointerFrame !== null) cancelAnimationFrame(pointerFrame);
|
|
2781
|
+
if (mutationFrame !== null) cancelAnimationFrame(mutationFrame);
|
|
2782
|
+
mutationObserver?.disconnect();
|
|
2783
|
+
mutationObserver = null;
|
|
2784
|
+
cancelFind();
|
|
2785
|
+
pointerFrame = null;
|
|
2786
|
+
mutationFrame = null;
|
|
2787
|
+
selection = null;
|
|
2788
|
+
lastPointerTarget = null;
|
|
2789
|
+
setSuspended(false);
|
|
2790
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
2791
|
+
document.documentElement.removeEventListener(
|
|
2792
|
+
"pointerleave",
|
|
2793
|
+
handlePointerLeave
|
|
2794
|
+
);
|
|
2795
|
+
document.removeEventListener("click", handleClick, { capture: true });
|
|
2796
|
+
window.removeEventListener("keydown", handleKey);
|
|
2797
|
+
window.removeEventListener("keyup", handleKey);
|
|
2798
|
+
window.removeEventListener("blur", handleBlur);
|
|
2799
|
+
window.removeEventListener("scroll", handleScroll, { capture: true });
|
|
2800
|
+
window.removeEventListener("resize", handleResize);
|
|
2801
|
+
document.removeEventListener("transitionend", handleSettled, {
|
|
2802
|
+
capture: true
|
|
2803
|
+
});
|
|
2804
|
+
document.removeEventListener("animationend", handleSettled, {
|
|
2805
|
+
capture: true
|
|
2806
|
+
});
|
|
2807
|
+
document.querySelectorAll(`style[${CURSOR_STYLE_MARKER}]`).forEach((node) => node.remove());
|
|
2808
|
+
overlay.unmount();
|
|
2809
|
+
untagAssetImages();
|
|
2810
|
+
guard.stop();
|
|
2811
|
+
debug7.log("visual editor stopped");
|
|
2812
|
+
}
|
|
2813
|
+
function cancelFind() {
|
|
2814
|
+
if (findTimeout) clearTimeout(findTimeout);
|
|
2815
|
+
findTimeout = null;
|
|
2816
|
+
settlePendingFind?.();
|
|
2817
|
+
settlePendingFind = null;
|
|
2818
|
+
}
|
|
2819
|
+
function focusField(lookup) {
|
|
2820
|
+
cancelFind();
|
|
2821
|
+
return new Promise((resolve2) => {
|
|
2822
|
+
let attempt = 0;
|
|
2823
|
+
const finish = (found) => {
|
|
2824
|
+
settlePendingFind = null;
|
|
2825
|
+
resolve2(found);
|
|
2826
|
+
};
|
|
2827
|
+
settlePendingFind = () => resolve2(false);
|
|
2828
|
+
const tryFind = () => {
|
|
2829
|
+
findTimeout = null;
|
|
2830
|
+
const found = findFieldElement(lookup);
|
|
2831
|
+
if (found) {
|
|
2832
|
+
const el = lookup.block !== void 0 ? nthListItem(found, lookup.block) ?? found : found;
|
|
2833
|
+
scrollIntoFrame(el);
|
|
2834
|
+
if (running && isCurrentItem(found)) select(el, found, lookup.block);
|
|
2835
|
+
finish(true);
|
|
2836
|
+
return;
|
|
2837
|
+
}
|
|
2838
|
+
if (++attempt >= FIND_RETRIES) {
|
|
2839
|
+
debug7.log("no element renders field", lookup.field);
|
|
2840
|
+
finish(false);
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
findTimeout = setTimeout(tryFind, FIND_RETRY_MS);
|
|
2844
|
+
};
|
|
2845
|
+
tryFind();
|
|
2846
|
+
});
|
|
2847
|
+
}
|
|
2848
|
+
function setCurrentItem(itemId, referenceList, componentIds) {
|
|
2849
|
+
currentItemId = itemId;
|
|
2850
|
+
if (referenceList) setReferences(referenceList, componentIds);
|
|
2851
|
+
else if (componentIds) ownComponents = new Set(componentIds);
|
|
2852
|
+
if (running && assetIndex.size) tagAssets();
|
|
2853
|
+
const selected = overlay.getSelectedTarget();
|
|
2854
|
+
const owner = selected ? encodedFrom(selected) : null;
|
|
2855
|
+
if (owner && !isCurrentItem(owner)) clearSelection();
|
|
2856
|
+
}
|
|
2857
|
+
function setAssets(assets) {
|
|
2858
|
+
const hadAssets = assetIndex.size > 0;
|
|
2859
|
+
assetIndex = createAssetIndex(assets);
|
|
2860
|
+
if (running && (hadAssets || assetIndex.size)) tagAssets();
|
|
2861
|
+
}
|
|
2862
|
+
function setReferences(referenceList, componentIds) {
|
|
2863
|
+
if (componentIds) ownComponents = new Set(componentIds);
|
|
2864
|
+
references = /* @__PURE__ */ new Map();
|
|
2865
|
+
for (const { id, field } of referenceList) {
|
|
2866
|
+
if (!references.has(id)) references.set(id, field);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
function setLocale(value) {
|
|
2870
|
+
locale = value;
|
|
2871
|
+
}
|
|
2872
|
+
function syncGuard() {
|
|
2873
|
+
if (running && guardEnabled) guard.start();
|
|
2874
|
+
else guard.stop();
|
|
2875
|
+
}
|
|
2876
|
+
function setNavigationGuard(enabled) {
|
|
2877
|
+
guardEnabled = enabled;
|
|
2878
|
+
syncGuard();
|
|
2879
|
+
}
|
|
2880
|
+
return {
|
|
2881
|
+
start,
|
|
2882
|
+
stop,
|
|
2883
|
+
setCurrentItem,
|
|
2884
|
+
setReferences,
|
|
2885
|
+
setAssets,
|
|
2886
|
+
setLocale,
|
|
2887
|
+
focusField,
|
|
2888
|
+
setNavigationGuard,
|
|
2889
|
+
allowNavigation: (href) => guard.allow(href)
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2181
2893
|
// src/core/create-preview.ts
|
|
2182
|
-
var
|
|
2894
|
+
var debug8 = createScopedLogger("create-preview");
|
|
2183
2895
|
var controllerStores = /* @__PURE__ */ new WeakMap();
|
|
2184
2896
|
function defaultNavigation() {
|
|
2185
2897
|
return {
|
|
@@ -2221,7 +2933,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2221
2933
|
const search = new URLSearchParams(window.location.search);
|
|
2222
2934
|
const hideBar = search.get(PARAM_HIDE_BAR) === "true";
|
|
2223
2935
|
if (hideBar) {
|
|
2224
|
-
|
|
2936
|
+
debug8.log(`${PARAM_HIDE_BAR}=true \u2014 no visible toolbar`);
|
|
2225
2937
|
}
|
|
2226
2938
|
const mountUi = (options?.ui ?? true) && !isIframe && !hideBar;
|
|
2227
2939
|
const locale = resolveLocale(options);
|
|
@@ -2247,32 +2959,48 @@ function createPreprPreview(opts = {}) {
|
|
|
2247
2959
|
definePreprToolbar();
|
|
2248
2960
|
el = document.createElement("prepr-toolbar");
|
|
2249
2961
|
document.body.appendChild(el);
|
|
2250
|
-
el.connect(store, (key) =>
|
|
2962
|
+
el.connect(store, (key) => t(key, locale));
|
|
2251
2963
|
}
|
|
2252
2964
|
const editingEnabled = features.editMode || isIframe;
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2965
|
+
const onEdit = ({
|
|
2966
|
+
href,
|
|
2967
|
+
id,
|
|
2968
|
+
field,
|
|
2969
|
+
locale: contentLocale,
|
|
2970
|
+
block,
|
|
2971
|
+
entryId,
|
|
2972
|
+
referenceField
|
|
2973
|
+
}) => {
|
|
2974
|
+
const safeHref = href ? safeEditUrl(href) : null;
|
|
2975
|
+
if (href && !safeHref) {
|
|
2976
|
+
debug8.warn("ignored edit request with unsupported href scheme");
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
if (isIframe) {
|
|
2980
|
+
sendPreprEvent("field_edit_requested", {
|
|
2981
|
+
id,
|
|
2982
|
+
field,
|
|
2983
|
+
locale: contentLocale,
|
|
2984
|
+
...block !== void 0 && { block },
|
|
2985
|
+
...entryId && { entryId },
|
|
2986
|
+
...referenceField && { referenceField }
|
|
2987
|
+
});
|
|
2988
|
+
} else if (safeHref) {
|
|
2989
|
+
window.open(safeHref, "_blank", "noopener,noreferrer");
|
|
2275
2990
|
}
|
|
2991
|
+
};
|
|
2992
|
+
const visualEditor = isIframe ? createVisualEditor({
|
|
2993
|
+
onEdit,
|
|
2994
|
+
locale,
|
|
2995
|
+
onNavigationBlocked: (href) => sendPreprEvent("navigation_blocked", { href })
|
|
2996
|
+
}) : null;
|
|
2997
|
+
const stega = visualEditor ?? createStegaController({
|
|
2998
|
+
// The edit-mode marks and hover label are chrome, so they follow the
|
|
2999
|
+
// same decision as the bar: unwanted in a headless preview that asked
|
|
3000
|
+
// for no UI.
|
|
3001
|
+
tooltip: mountUi,
|
|
3002
|
+
onEdit,
|
|
3003
|
+
locale
|
|
2276
3004
|
});
|
|
2277
3005
|
const autoClean = createStegaAutoClean({
|
|
2278
3006
|
enabled: options?.autoClean
|
|
@@ -2295,12 +3023,32 @@ function createPreprPreview(opts = {}) {
|
|
|
2295
3023
|
});
|
|
2296
3024
|
const bridge = createIframeBridge(store, {
|
|
2297
3025
|
allowedEditorOrigins: options?.allowedEditorOrigins,
|
|
3026
|
+
onHandshake: ({
|
|
3027
|
+
itemId,
|
|
3028
|
+
referencedIds,
|
|
3029
|
+
componentIds,
|
|
3030
|
+
uiLocale,
|
|
3031
|
+
navigationGuard,
|
|
3032
|
+
assets
|
|
3033
|
+
}) => {
|
|
3034
|
+
if (uiLocale) visualEditor?.setLocale(uiLocale);
|
|
3035
|
+
visualEditor?.setCurrentItem(itemId ?? null, referencedIds, componentIds);
|
|
3036
|
+
visualEditor?.setNavigationGuard(navigationGuard === true);
|
|
3037
|
+
if (assets) visualEditor?.setAssets(assets);
|
|
3038
|
+
},
|
|
3039
|
+
onAssets: (assets) => visualEditor?.setAssets(assets),
|
|
3040
|
+
onAllowNavigation: (href) => visualEditor?.allowNavigation(href),
|
|
3041
|
+
onReferences: (referencedIds, componentIds) => visualEditor?.setReferences(referencedIds, componentIds),
|
|
2298
3042
|
// The editor selecting a field scrolls the matching element into view. The
|
|
2299
3043
|
// lookup relies on the attributes the stega scan writes, so it finds
|
|
2300
3044
|
// nothing until encoded content has been tagged.
|
|
2301
|
-
onScrollToField: ({ field, id }) => {
|
|
3045
|
+
onScrollToField: ({ field, id, block, entryId, fieldById }) => {
|
|
3046
|
+
if (visualEditor) {
|
|
3047
|
+
void visualEditor.focusField({ field, id, block, entryId, fieldById });
|
|
3048
|
+
return;
|
|
3049
|
+
}
|
|
2302
3050
|
if (!scrollToField({ field, id })) {
|
|
2303
|
-
|
|
3051
|
+
debug8.log("no element on this page renders field", field);
|
|
2304
3052
|
}
|
|
2305
3053
|
}
|
|
2306
3054
|
});
|
|
@@ -2322,7 +3070,7 @@ function createPreprPreview(opts = {}) {
|
|
|
2322
3070
|
bridge.stop();
|
|
2323
3071
|
}
|
|
2324
3072
|
el?.remove();
|
|
2325
|
-
|
|
3073
|
+
debug8.log("preview destroyed");
|
|
2326
3074
|
}
|
|
2327
3075
|
const controller = { destroy };
|
|
2328
3076
|
controllerStores.set(controller, store);
|
|
@@ -2395,8 +3143,6 @@ function setTrackingParam(key, value) {
|
|
|
2395
3143
|
}
|
|
2396
3144
|
|
|
2397
3145
|
export {
|
|
2398
|
-
VERSION,
|
|
2399
|
-
resolveFeatures,
|
|
2400
3146
|
COOKIE_PREVIEW_MODE,
|
|
2401
3147
|
COOKIE_SEGMENT,
|
|
2402
3148
|
COOKIE_VARIANT,
|
|
@@ -2406,6 +3152,7 @@ export {
|
|
|
2406
3152
|
PARAM_VARIANT,
|
|
2407
3153
|
PARAM_HIDE_BAR,
|
|
2408
3154
|
ONE_YEAR_SECONDS,
|
|
3155
|
+
HEADER_LIVE_RENDER,
|
|
2409
3156
|
stegaClean,
|
|
2410
3157
|
createToolbarStore,
|
|
2411
3158
|
PreprToolbarElement,
|