@preprio/toolkit 0.1.0-beta.6 → 0.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -12
- package/dist/astro/index.cjs +37 -15
- package/dist/astro/index.d.cts +38 -8
- package/dist/astro/index.d.ts +38 -8
- package/dist/astro/index.js +37 -15
- package/dist/{chunk-NLQIRN5O.js → chunk-6OOGNVJ4.js} +83 -41
- package/dist/index.cjs +102 -55
- package/dist/index.d.cts +59 -16
- package/dist/index.d.ts +59 -16
- package/dist/index.js +22 -16
- package/dist/nextjs/components.cjs +87 -42
- package/dist/nextjs/components.d.cts +3 -3
- package/dist/nextjs/components.d.ts +3 -3
- package/dist/nextjs/components.js +8 -4
- package/dist/nextjs/index.cjs +37 -15
- package/dist/nextjs/index.d.cts +46 -16
- package/dist/nextjs/index.d.ts +46 -16
- package/dist/nextjs/index.js +37 -15
- package/dist/nuxt/index.cjs +37 -15
- package/dist/nuxt/index.d.cts +62 -32
- package/dist/nuxt/index.d.ts +62 -32
- package/dist/nuxt/index.js +37 -15
- package/dist/{pixel-DVSJQC0K.d.cts → pixel-763p7m6A.d.cts} +64 -1
- package/dist/{pixel-DVSJQC0K.d.ts → pixel-763p7m6A.d.ts} +64 -1
- package/dist/sveltekit/index.cjs +37 -15
- package/dist/sveltekit/index.d.cts +62 -32
- package/dist/sveltekit/index.d.ts +62 -32
- package/dist/sveltekit/index.js +37 -15
- package/package.json +1 -1
- package/src/astro/PreprToolbar.astro +16 -10
- package/src/nuxt/PreprToolbar.vue +10 -8
- package/src/sveltekit/PreprToolbar.svelte +11 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { L as Locale,
|
|
2
|
-
export {
|
|
1
|
+
import { P as PreprFeatures, R as ResolvedPreprFeatures, L as Locale, a as PreprSegment, b as PreprVariant, c as PreprToolbarProps, d as PreprPreviewOptions, e as PreprErrorCode, f as PreprHeaders } from './pixel-763p7m6A.js';
|
|
2
|
+
export { g as PreprEnvironment, h as PreprEventType, i as PreprFeatureConfig, j as PreprHeaderName, k as PreprPixelConfig, l as PreprToolbarComponentProps, m as PreprToolbarOptions, n as loadTrackingPixel, s as setTrackingParam, t as trackEvent } from './pixel-763p7m6A.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Package version; must match package.json. Lives in its own leaf module so
|
|
6
6
|
* core can import it without cycling through the barrel.
|
|
7
7
|
*/
|
|
8
|
-
declare const VERSION = "0.
|
|
8
|
+
declare const VERSION = "0.2.0-beta.2";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a `PreprFeatures` bag to plain booleans, defaulting every omitted key
|
|
12
|
+
* to enabled. Shared by the toolbar and the middleware so both sides agree on
|
|
13
|
+
* what a given config means.
|
|
14
|
+
*/
|
|
15
|
+
declare function resolveFeatures(features?: PreprFeatures): ResolvedPreprFeatures;
|
|
9
16
|
|
|
10
17
|
interface ToolbarState {
|
|
11
18
|
locale: Locale;
|
|
@@ -16,6 +23,11 @@ interface ToolbarState {
|
|
|
16
23
|
previewMode: boolean;
|
|
17
24
|
toolbarOpen: boolean;
|
|
18
25
|
isIframe: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Set once at construction, never patched. Lives here so the panel and the
|
|
28
|
+
* element read it the same way they read everything else.
|
|
29
|
+
*/
|
|
30
|
+
features: ResolvedPreprFeatures;
|
|
19
31
|
}
|
|
20
32
|
interface ToolbarStore {
|
|
21
33
|
get(): ToolbarState;
|
|
@@ -25,7 +37,7 @@ interface ToolbarStore {
|
|
|
25
37
|
/**
|
|
26
38
|
* Framework-free store for toolbar state. Deliberately pure: it holds state and
|
|
27
39
|
* notifies subscribers, nothing else. Cookies, postMessage and reloads are side
|
|
28
|
-
* effects owned by the mount controller (create-
|
|
40
|
+
* effects owned by the mount controller (create-preview.ts).
|
|
29
41
|
*/
|
|
30
42
|
declare function createToolbarStore(initial?: Partial<ToolbarState>): ToolbarStore;
|
|
31
43
|
|
|
@@ -45,21 +57,32 @@ interface PreprNavigationAdapter {
|
|
|
45
57
|
*/
|
|
46
58
|
reload?(): void;
|
|
47
59
|
}
|
|
48
|
-
interface
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
interface CreatePreprPreviewOptions {
|
|
61
|
+
/**
|
|
62
|
+
* Toolbar data. Optional: a headless preview (`ui: false`) that only wants
|
|
63
|
+
* click-to-edit or scroll restore has no segment list to pass.
|
|
64
|
+
*/
|
|
65
|
+
props?: PreprToolbarProps;
|
|
66
|
+
options?: PreprPreviewOptions;
|
|
51
67
|
navigation?: PreprNavigationAdapter;
|
|
52
68
|
}
|
|
53
|
-
interface
|
|
69
|
+
interface PreprPreviewController {
|
|
54
70
|
destroy(): void;
|
|
55
71
|
}
|
|
56
72
|
/**
|
|
57
|
-
* Composition root: hydrates state from props +
|
|
58
|
-
* `<prepr-toolbar>`, and wires the collaborators —
|
|
59
|
-
* (toolbar-change-handler.ts), the stega click-to-edit
|
|
60
|
-
* auto-clean pass and the editor bridge (iframe-bridge.ts).
|
|
73
|
+
* Composition root for the Prepr preview runtime: hydrates state from props +
|
|
74
|
+
* cookies, optionally mounts `<prepr-toolbar>`, and wires the collaborators —
|
|
75
|
+
* the change handler (toolbar-change-handler.ts), the stega click-to-edit
|
|
76
|
+
* controller, the auto-clean pass and the editor bridge (iframe-bridge.ts).
|
|
77
|
+
*
|
|
78
|
+
* Two independent axes control what runs:
|
|
79
|
+
* - `options.features` — which features are active at all (segments, A/B,
|
|
80
|
+
* click-to-edit).
|
|
81
|
+
* - `options.ui` — whether the visible toolbar is mounted. `ui: false` keeps
|
|
82
|
+
* every non-visual side effect wired, which is how a headless preview gets
|
|
83
|
+
* click-to-edit or editor scroll restore with no chrome of its own.
|
|
61
84
|
*/
|
|
62
|
-
declare function
|
|
85
|
+
declare function createPreprPreview(opts?: CreatePreprPreviewOptions): PreprPreviewController;
|
|
63
86
|
|
|
64
87
|
type Translate = (key: string) => string;
|
|
65
88
|
declare const HTMLElementBase: typeof HTMLElement;
|
|
@@ -69,7 +92,7 @@ declare const HTMLElementBase: typeof HTMLElement;
|
|
|
69
92
|
*
|
|
70
93
|
* This element is the controller: it owns interaction state (listbox open,
|
|
71
94
|
* keyboard nav, tooltip positioning, outside-click) and writes back ONLY via
|
|
72
|
-
* `store.set(...)`. Cookies/postMessage/navigation belong to create-
|
|
95
|
+
* `store.set(...)`. Cookies/postMessage/navigation belong to create-preview.ts,
|
|
73
96
|
* which subscribes to the same store.
|
|
74
97
|
*
|
|
75
98
|
* State reaches `<Panel>` as a prop rather than internal Preact state, so every
|
|
@@ -87,6 +110,8 @@ declare class PreprToolbarElement extends HTMLElementBase {
|
|
|
87
110
|
disconnectedCallback(): void;
|
|
88
111
|
private get state();
|
|
89
112
|
private set;
|
|
113
|
+
/** Reset patch covering only the enabled personalization features. */
|
|
114
|
+
private resetPatch;
|
|
90
115
|
private renderPanel;
|
|
91
116
|
private tip;
|
|
92
117
|
/**
|
|
@@ -112,6 +137,14 @@ declare class PreprToolbarElement extends HTMLElementBase {
|
|
|
112
137
|
/** Idempotent, including across bundles that each try to define the element. */
|
|
113
138
|
declare function definePreprToolbar(): void;
|
|
114
139
|
|
|
140
|
+
interface IframeBridgeOptions {
|
|
141
|
+
/**
|
|
142
|
+
* Replace the `*.prepr.io` wildcard with an exact origin list. Intended for
|
|
143
|
+
* self-hosted editors; when set, the wildcard no longer applies.
|
|
144
|
+
*/
|
|
145
|
+
allowedEditorOrigins?: string[];
|
|
146
|
+
}
|
|
147
|
+
|
|
115
148
|
/** Error thrown by the token helpers and the segments fetch. */
|
|
116
149
|
declare class PreprError extends Error {
|
|
117
150
|
readonly code: PreprErrorCode;
|
|
@@ -152,8 +185,12 @@ declare function getPreprEnvironmentSegments(token: string): Promise<PreprSegmen
|
|
|
152
185
|
* Never throws: a failed segments fetch yields an empty list so the toolbar
|
|
153
186
|
* still renders. The list is returned under both `segments` and the deprecated
|
|
154
187
|
* `data` alias.
|
|
188
|
+
*
|
|
189
|
+
* Pass the same `features` object you give the toolbar and the middleware: with
|
|
190
|
+
* segments disabled the `_Segments` request is skipped entirely, saving a
|
|
191
|
+
* round-trip per page.
|
|
155
192
|
*/
|
|
156
|
-
declare function getToolbarPropsFromHeaders(headers: Headers, token: string): Promise<PreprToolbarProps>;
|
|
193
|
+
declare function getToolbarPropsFromHeaders(headers: Headers, token: string, features?: PreprFeatures): Promise<PreprToolbarProps>;
|
|
157
194
|
|
|
158
195
|
interface CookieSpec {
|
|
159
196
|
name: string;
|
|
@@ -177,6 +214,12 @@ interface PreprMiddlewareResult {
|
|
|
177
214
|
interface PreprMiddlewareOptions {
|
|
178
215
|
/** Enable preview-mode headers (segment/AB overrides, preview bar). */
|
|
179
216
|
preview?: boolean;
|
|
217
|
+
/**
|
|
218
|
+
* Disable features app-wide. Pass the same object you give the toolbar: a
|
|
219
|
+
* disabled feature injects no header and persists no cookie, whatever the
|
|
220
|
+
* request carries.
|
|
221
|
+
*/
|
|
222
|
+
features?: PreprFeatures;
|
|
180
223
|
/** Override the version reported in Prepr-Package (mainly for tests). */
|
|
181
224
|
version?: string;
|
|
182
225
|
}
|
|
@@ -187,4 +230,4 @@ interface PreprMiddlewareOptions {
|
|
|
187
230
|
*/
|
|
188
231
|
declare function processPreprRequest(request: Request, opts?: PreprMiddlewareOptions): PreprMiddlewareResult;
|
|
189
232
|
|
|
190
|
-
export { type CookieSpec, type
|
|
233
|
+
export { type CookieSpec, type CreatePreprPreviewOptions, type IframeBridgeOptions, PreprError, PreprErrorCode, PreprFeatures, PreprHeaders, type PreprMiddlewareOptions, type PreprMiddlewareResult, type PreprNavigationAdapter, type PreprPreviewController, PreprPreviewOptions, PreprSegment, PreprToolbarElement, PreprToolbarProps, PreprVariant, ResolvedPreprFeatures, type ToolbarState, type ToolbarStore, VERSION, createPreprPreview, createToolbarStore, definePreprToolbar, extractAccessToken, getActiveSegmentFromHeaders, getActiveVariantFromHeaders, getPreprEnvironmentSegments, getPreprHeadersFromHeaders, getPreprUUIDFromHeaders, getToolbarPropsFromHeaders, processPreprRequest, resolveFeatures, stegaClean, validatePreprToken };
|
package/dist/index.js
CHANGED
|
@@ -9,17 +9,18 @@ import {
|
|
|
9
9
|
PARAM_SEGMENT,
|
|
10
10
|
PARAM_VARIANT,
|
|
11
11
|
PreprToolbarElement,
|
|
12
|
-
|
|
12
|
+
createPreprPreview,
|
|
13
13
|
createToolbarStore,
|
|
14
14
|
definePreprToolbar,
|
|
15
15
|
loadTrackingPixel,
|
|
16
|
+
resolveFeatures,
|
|
16
17
|
setTrackingParam,
|
|
17
18
|
stegaClean,
|
|
18
19
|
trackEvent
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-6OOGNVJ4.js";
|
|
20
21
|
|
|
21
22
|
// src/version.ts
|
|
22
|
-
var VERSION = "0.
|
|
23
|
+
var VERSION = "0.2.0-beta.2";
|
|
23
24
|
|
|
24
25
|
// src/core/server.ts
|
|
25
26
|
var PreprError = class extends Error {
|
|
@@ -157,16 +158,19 @@ async function getPreprEnvironmentSegments(token) {
|
|
|
157
158
|
);
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
|
-
async function getToolbarPropsFromHeaders(headers, token) {
|
|
161
|
+
async function getToolbarPropsFromHeaders(headers, token, features) {
|
|
162
|
+
const resolved = resolveFeatures(features);
|
|
161
163
|
let segments = [];
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
if (resolved.segments) {
|
|
165
|
+
try {
|
|
166
|
+
segments = await getPreprEnvironmentSegments(token);
|
|
167
|
+
} catch {
|
|
168
|
+
segments = [];
|
|
169
|
+
}
|
|
166
170
|
}
|
|
167
171
|
return {
|
|
168
|
-
activeSegment: getActiveSegmentFromHeaders(headers),
|
|
169
|
-
activeVariant: getActiveVariantFromHeaders(headers),
|
|
172
|
+
activeSegment: resolved.segments ? getActiveSegmentFromHeaders(headers) : null,
|
|
173
|
+
activeVariant: resolved.abTesting ? getActiveVariantFromHeaders(headers) : null,
|
|
170
174
|
segments,
|
|
171
175
|
data: segments
|
|
172
176
|
};
|
|
@@ -241,7 +245,8 @@ function processPreprRequest(request, opts) {
|
|
|
241
245
|
if (!opts?.preview) {
|
|
242
246
|
return { requestHeaders, responseCookies };
|
|
243
247
|
}
|
|
244
|
-
const
|
|
248
|
+
const features = resolveFeatures(opts?.features);
|
|
249
|
+
const hasPreviewParams = features.segments && searchParams.has(PARAM_SEGMENT) || features.abTesting && searchParams.has(PARAM_VARIANT);
|
|
245
250
|
if (!hasPreviewParams) {
|
|
246
251
|
const previewModeCookie = requestCookies.get(COOKIE_PREVIEW_MODE);
|
|
247
252
|
const previewModeEnabled = previewModeCookie !== "false";
|
|
@@ -261,16 +266,16 @@ function processPreprRequest(request, opts) {
|
|
|
261
266
|
});
|
|
262
267
|
}
|
|
263
268
|
if (!isLivePreview) {
|
|
264
|
-
const segmentCookie = requestCookies.get(COOKIE_SEGMENT);
|
|
269
|
+
const segmentCookie = features.segments ? requestCookies.get(COOKIE_SEGMENT) : void 0;
|
|
265
270
|
if (segmentCookie) {
|
|
266
271
|
requestHeaders.set("Prepr-Segments", segmentCookie);
|
|
267
272
|
}
|
|
268
|
-
const abCookie = requestCookies.get(COOKIE_VARIANT);
|
|
273
|
+
const abCookie = features.abTesting ? requestCookies.get(COOKIE_VARIANT) : void 0;
|
|
269
274
|
if (abCookie) {
|
|
270
275
|
requestHeaders.set("Prepr-ABtesting", abCookie);
|
|
271
276
|
}
|
|
272
277
|
}
|
|
273
|
-
const previewAb = searchParams.get(PARAM_VARIANT);
|
|
278
|
+
const previewAb = features.abTesting ? searchParams.get(PARAM_VARIANT) : null;
|
|
274
279
|
if (previewAb !== null) {
|
|
275
280
|
requestHeaders.set("Prepr-ABtesting", previewAb);
|
|
276
281
|
if (!isLivePreview) {
|
|
@@ -283,7 +288,7 @@ function processPreprRequest(request, opts) {
|
|
|
283
288
|
});
|
|
284
289
|
}
|
|
285
290
|
}
|
|
286
|
-
const previewSegment = searchParams.get(PARAM_SEGMENT);
|
|
291
|
+
const previewSegment = features.segments ? searchParams.get(PARAM_SEGMENT) : null;
|
|
287
292
|
if (previewSegment !== null) {
|
|
288
293
|
requestHeaders.set("Prepr-Segments", previewSegment);
|
|
289
294
|
if (!isLivePreview) {
|
|
@@ -302,7 +307,7 @@ export {
|
|
|
302
307
|
PreprError,
|
|
303
308
|
PreprToolbarElement,
|
|
304
309
|
VERSION,
|
|
305
|
-
|
|
310
|
+
createPreprPreview,
|
|
306
311
|
createToolbarStore,
|
|
307
312
|
definePreprToolbar,
|
|
308
313
|
extractAccessToken,
|
|
@@ -314,6 +319,7 @@ export {
|
|
|
314
319
|
getToolbarPropsFromHeaders,
|
|
315
320
|
loadTrackingPixel,
|
|
316
321
|
processPreprRequest,
|
|
322
|
+
resolveFeatures,
|
|
317
323
|
setTrackingParam,
|
|
318
324
|
stegaClean,
|
|
319
325
|
trackEvent,
|
|
@@ -78,6 +78,24 @@ function crossSiteCookieOptions() {
|
|
|
78
78
|
return { sameSite: "None", secure: true };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// src/core/features.ts
|
|
82
|
+
function isEnabled(config) {
|
|
83
|
+
if (typeof config === "boolean") return config;
|
|
84
|
+
return config?.enabled ?? true;
|
|
85
|
+
}
|
|
86
|
+
function resolveFeatures(features) {
|
|
87
|
+
return {
|
|
88
|
+
segments: isEnabled(features?.segments),
|
|
89
|
+
abTesting: isEnabled(features?.abTesting),
|
|
90
|
+
editMode: isEnabled(features?.editMode)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
var ALL_FEATURES_ENABLED = Object.freeze({
|
|
94
|
+
segments: true,
|
|
95
|
+
abTesting: true,
|
|
96
|
+
editMode: true
|
|
97
|
+
});
|
|
98
|
+
|
|
81
99
|
// src/core/locales/en.json
|
|
82
100
|
var en_default = {
|
|
83
101
|
common: {
|
|
@@ -307,7 +325,7 @@ function createIframeBridge(store, options = {}) {
|
|
|
307
325
|
const top = data.scrollPosition;
|
|
308
326
|
setTimeout(() => window.scrollTo(0, top), 1);
|
|
309
327
|
}
|
|
310
|
-
store
|
|
328
|
+
store?.set({ previewMode: true, editMode: data.editMode ?? true });
|
|
311
329
|
}
|
|
312
330
|
if (!parentOrigin || evt.origin !== parentOrigin) return;
|
|
313
331
|
if (data?.event === "prepr:getScrollPosition") {
|
|
@@ -929,7 +947,8 @@ var DEFAULT_STATE = {
|
|
|
929
947
|
editMode: false,
|
|
930
948
|
previewMode: false,
|
|
931
949
|
toolbarOpen: false,
|
|
932
|
-
isIframe: false
|
|
950
|
+
isIframe: false,
|
|
951
|
+
features: ALL_FEATURES_ENABLED
|
|
933
952
|
};
|
|
934
953
|
function createToolbarStore(initial = {}) {
|
|
935
954
|
let state = { ...DEFAULT_STATE, ...initial };
|
|
@@ -964,7 +983,7 @@ function cookieOpts() {
|
|
|
964
983
|
};
|
|
965
984
|
}
|
|
966
985
|
function createChangeHandler(deps) {
|
|
967
|
-
const { store, stega, syncAutoClean } = deps;
|
|
986
|
+
const { store, stega, syncAutoClean, features, editingEnabled } = deps;
|
|
968
987
|
function updateParams(patch) {
|
|
969
988
|
const [path, existing] = splitPath(deps.currentPath());
|
|
970
989
|
const params = new URLSearchParams(existing);
|
|
@@ -980,7 +999,7 @@ function createChangeHandler(deps) {
|
|
|
980
999
|
}
|
|
981
1000
|
return function handleChange(before, after) {
|
|
982
1001
|
const paramPatch = {};
|
|
983
|
-
if (before.selectedSegment !== after.selectedSegment) {
|
|
1002
|
+
if (features.segments && before.selectedSegment !== after.selectedSegment) {
|
|
984
1003
|
if (after.selectedSegment === null) {
|
|
985
1004
|
removeCookie(COOKIE_SEGMENT, "/", crossSiteCookieOptions());
|
|
986
1005
|
} else {
|
|
@@ -991,7 +1010,7 @@ function createChangeHandler(deps) {
|
|
|
991
1010
|
segment: after.selectedSegment ?? void 0
|
|
992
1011
|
});
|
|
993
1012
|
}
|
|
994
|
-
if (before.selectedVariant !== after.selectedVariant) {
|
|
1013
|
+
if (features.abTesting && before.selectedVariant !== after.selectedVariant) {
|
|
995
1014
|
if (after.selectedVariant === null) {
|
|
996
1015
|
removeCookie(COOKIE_VARIANT, "/", crossSiteCookieOptions());
|
|
997
1016
|
} else {
|
|
@@ -1006,7 +1025,7 @@ function createChangeHandler(deps) {
|
|
|
1006
1025
|
updateParams(paramPatch);
|
|
1007
1026
|
}
|
|
1008
1027
|
if (before.editMode !== after.editMode) {
|
|
1009
|
-
if (after.editMode) {
|
|
1028
|
+
if (after.editMode && editingEnabled) {
|
|
1010
1029
|
stega.start();
|
|
1011
1030
|
} else {
|
|
1012
1031
|
stega.stop();
|
|
@@ -1577,9 +1596,10 @@ function StatusPill({
|
|
|
1577
1596
|
t: t3,
|
|
1578
1597
|
handlers
|
|
1579
1598
|
}) {
|
|
1599
|
+
const { features } = state;
|
|
1580
1600
|
const defaultSegmentName = state.segments.find((s2) => s2._id === "all_other_users")?.name ?? t3("adaptiveContent.allOtherUsers");
|
|
1581
1601
|
let segmentLabel;
|
|
1582
|
-
if (!state.previewMode) {
|
|
1602
|
+
if (!state.previewMode || !features.segments) {
|
|
1583
1603
|
segmentLabel = t3("common.user");
|
|
1584
1604
|
} else if (state.selectedSegment !== null) {
|
|
1585
1605
|
const seg = state.segments.find((s2) => s2._id === state.selectedSegment);
|
|
@@ -1603,7 +1623,7 @@ function StatusPill({
|
|
|
1603
1623
|
{
|
|
1604
1624
|
class: "prepr-status-variant",
|
|
1605
1625
|
"data-prepr": "status-variant",
|
|
1606
|
-
hidden: !state.previewMode,
|
|
1626
|
+
hidden: !features.abTesting || !state.previewMode,
|
|
1607
1627
|
children: state.selectedVariant === "B" ? "B" : "A"
|
|
1608
1628
|
}
|
|
1609
1629
|
),
|
|
@@ -1612,7 +1632,7 @@ function StatusPill({
|
|
|
1612
1632
|
{
|
|
1613
1633
|
class: "prepr-status-x",
|
|
1614
1634
|
"data-prepr": "status-x",
|
|
1615
|
-
hidden: !state.previewMode || state.selectedSegment === null,
|
|
1635
|
+
hidden: !features.segments || !state.previewMode || state.selectedSegment === null,
|
|
1616
1636
|
children: /* @__PURE__ */ u2(RawSvg, { svg: XMARK_ICON_SVG })
|
|
1617
1637
|
}
|
|
1618
1638
|
)
|
|
@@ -1632,7 +1652,7 @@ function CloseEditPill({
|
|
|
1632
1652
|
class: "prepr-close-edit-pill",
|
|
1633
1653
|
"data-prepr": "close-edit-pill",
|
|
1634
1654
|
"aria-label": t3("editingTools.ariaCloseEditMode"),
|
|
1635
|
-
hidden: !state.editMode || state.isIframe,
|
|
1655
|
+
hidden: !state.features.editMode || !state.editMode || state.isIframe,
|
|
1636
1656
|
onClick: () => handlers.onCloseEditPill(),
|
|
1637
1657
|
children: [
|
|
1638
1658
|
/* @__PURE__ */ u2("span", { "data-prepr": "close-edit-label", children: t3("editingTools.editMode") }),
|
|
@@ -1648,7 +1668,9 @@ function Panel({
|
|
|
1648
1668
|
segmentFilter,
|
|
1649
1669
|
handlers
|
|
1650
1670
|
}) {
|
|
1651
|
-
const
|
|
1671
|
+
const { features } = state;
|
|
1672
|
+
const hasPersonalization = features.segments && state.selectedSegment !== null || features.abTesting && state.selectedVariant !== null;
|
|
1673
|
+
const showAdaptive = features.segments || features.abTesting;
|
|
1652
1674
|
return /* @__PURE__ */ u2(S, { children: [
|
|
1653
1675
|
/* @__PURE__ */ u2(
|
|
1654
1676
|
"style",
|
|
@@ -1691,12 +1713,12 @@ function Panel({
|
|
|
1691
1713
|
children: [
|
|
1692
1714
|
/* @__PURE__ */ u2(Header, { t: t3, onClose: handlers.onClose }),
|
|
1693
1715
|
/* @__PURE__ */ u2("div", { class: "prepr-section", "data-prepr": "section-adaptive", children: [
|
|
1694
|
-
/* @__PURE__ */ u2("span", { class: "prepr-section-label", "data-prepr": "adaptive-label", children: t3("adaptiveContent.adaptiveContent") }),
|
|
1716
|
+
/* @__PURE__ */ u2("span", { class: "prepr-section-label", "data-prepr": "adaptive-label", children: showAdaptive ? t3("adaptiveContent.adaptiveContent") : t3("adaptiveContent.enablePreview") }),
|
|
1695
1717
|
/* @__PURE__ */ u2("div", { class: "prepr-row", children: [
|
|
1696
1718
|
/* @__PURE__ */ u2("h2", { class: "prepr-row-title", "data-prepr": "preview-label", children: t3("adaptiveContent.enablePreview") }),
|
|
1697
1719
|
/* @__PURE__ */ u2(PreviewSelector, { state, t: t3, handlers })
|
|
1698
1720
|
] }),
|
|
1699
|
-
/* @__PURE__ */ u2("div", { class: "prepr-row", children: [
|
|
1721
|
+
features.segments && /* @__PURE__ */ u2("div", { class: "prepr-row", children: [
|
|
1700
1722
|
/* @__PURE__ */ u2("h2", { class: "prepr-row-title", "data-prepr": "segment-label", children: t3("adaptiveContent.segment") }),
|
|
1701
1723
|
/* @__PURE__ */ u2(
|
|
1702
1724
|
SegmentListbox,
|
|
@@ -1709,12 +1731,12 @@ function Panel({
|
|
|
1709
1731
|
}
|
|
1710
1732
|
)
|
|
1711
1733
|
] }),
|
|
1712
|
-
/* @__PURE__ */ u2("div", { class: "prepr-row", children: [
|
|
1734
|
+
features.abTesting && /* @__PURE__ */ u2("div", { class: "prepr-row", children: [
|
|
1713
1735
|
/* @__PURE__ */ u2("h2", { class: "prepr-row-title", "data-prepr": "variant-label", children: t3("adaptiveContent.ABVariant") }),
|
|
1714
1736
|
/* @__PURE__ */ u2(VariantSelector, { state, handlers })
|
|
1715
1737
|
] })
|
|
1716
1738
|
] }),
|
|
1717
|
-
/* @__PURE__ */ u2("div", { class: "prepr-section", "data-prepr": "section-editing", children: [
|
|
1739
|
+
features.editMode && /* @__PURE__ */ u2("div", { class: "prepr-section", "data-prepr": "section-editing", children: [
|
|
1718
1740
|
/* @__PURE__ */ u2("span", { class: "prepr-section-label", "data-prepr": "editing-label", children: t3("editingTools.editingTools") }),
|
|
1719
1741
|
/* @__PURE__ */ u2("div", { class: "prepr-row", children: [
|
|
1720
1742
|
/* @__PURE__ */ u2("h2", { class: "prepr-row-title", "data-prepr": "edit-label", children: t3("editingTools.editMode") }),
|
|
@@ -1767,26 +1789,28 @@ var PreprToolbarElement = class extends HTMLElementBase {
|
|
|
1767
1789
|
onToggle: () => this.set({ toolbarOpen: !this.state.toolbarOpen }),
|
|
1768
1790
|
onClose: () => this.set({ toolbarOpen: false }),
|
|
1769
1791
|
onPreviewMode: (value) => this.set({ previewMode: value }),
|
|
1770
|
-
onEditMode: (value) =>
|
|
1792
|
+
onEditMode: (value) => {
|
|
1793
|
+
if (!this.state.features.editMode) return;
|
|
1794
|
+
this.set({ editMode: value });
|
|
1795
|
+
},
|
|
1771
1796
|
onVariant: (value) => {
|
|
1797
|
+
if (!this.state.features.abTesting) return;
|
|
1772
1798
|
if (!this.state.previewMode) return;
|
|
1773
1799
|
this.set({ selectedVariant: value });
|
|
1774
1800
|
},
|
|
1775
1801
|
// Reset contract: segment to none, variant to 'A' — NOT null, since the
|
|
1776
1802
|
// resulting `variant_changed` must carry 'A' on the wire — edit mode off.
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
editMode: false
|
|
1781
|
-
}),
|
|
1803
|
+
// Disabled features are left untouched: their state is already inert and
|
|
1804
|
+
// writing it would emit events for a feature the consumer turned off.
|
|
1805
|
+
onReset: () => this.set(this.resetPatch({ editMode: false })),
|
|
1782
1806
|
onStatusPill: () => {
|
|
1783
1807
|
if (!this.state.previewMode) {
|
|
1784
1808
|
this.set({ previewMode: true });
|
|
1785
1809
|
return;
|
|
1786
1810
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1811
|
+
const patch = this.resetPatch();
|
|
1812
|
+
const dirty = "selectedSegment" in patch && this.state.selectedSegment !== null || "selectedVariant" in patch && this.state.selectedVariant !== "A";
|
|
1813
|
+
if (dirty) this.set(patch);
|
|
1790
1814
|
},
|
|
1791
1815
|
onCloseEditPill: () => this.set({ editMode: false }),
|
|
1792
1816
|
onSegmentButtonClick: () => this.toggleListbox(!this.listboxOpen),
|
|
@@ -1836,6 +1860,14 @@ var PreprToolbarElement = class extends HTMLElementBase {
|
|
|
1836
1860
|
set(patch) {
|
|
1837
1861
|
this.store?.set(patch);
|
|
1838
1862
|
}
|
|
1863
|
+
/** Reset patch covering only the enabled personalization features. */
|
|
1864
|
+
resetPatch(base = {}) {
|
|
1865
|
+
const { features } = this.state;
|
|
1866
|
+
const patch = { ...base };
|
|
1867
|
+
if (features.segments) patch.selectedSegment = null;
|
|
1868
|
+
if (features.abTesting) patch.selectedVariant = "A";
|
|
1869
|
+
return patch;
|
|
1870
|
+
}
|
|
1839
1871
|
// Synchronous: Preact's top-level render() mutates the DOM before returning.
|
|
1840
1872
|
renderPanel(state) {
|
|
1841
1873
|
const root = this.shadowRoot;
|
|
@@ -1994,6 +2026,7 @@ var PreprToolbarElement = class extends HTMLElementBase {
|
|
|
1994
2026
|
) ?? null;
|
|
1995
2027
|
}
|
|
1996
2028
|
chooseSegment(id) {
|
|
2029
|
+
if (!this.state.features.segments) return;
|
|
1997
2030
|
this.listboxOpen = false;
|
|
1998
2031
|
this.segmentFilter = "";
|
|
1999
2032
|
this.set({ selectedSegment: id });
|
|
@@ -2008,8 +2041,8 @@ function definePreprToolbar() {
|
|
|
2008
2041
|
}
|
|
2009
2042
|
}
|
|
2010
2043
|
|
|
2011
|
-
// src/core/create-
|
|
2012
|
-
var debug7 = createScopedLogger("create-
|
|
2044
|
+
// src/core/create-preview.ts
|
|
2045
|
+
var debug7 = createScopedLogger("create-preview");
|
|
2013
2046
|
var controllerStores = /* @__PURE__ */ new WeakMap();
|
|
2014
2047
|
function defaultNavigation() {
|
|
2015
2048
|
return {
|
|
@@ -2030,7 +2063,7 @@ function resolveLocale(options) {
|
|
|
2030
2063
|
}
|
|
2031
2064
|
return "en";
|
|
2032
2065
|
}
|
|
2033
|
-
function
|
|
2066
|
+
function createPreprPreview(opts = {}) {
|
|
2034
2067
|
const { props, options } = opts;
|
|
2035
2068
|
const navigation = opts.navigation ?? defaultNavigation();
|
|
2036
2069
|
initDebugLogger(options?.debug ?? false);
|
|
@@ -2043,36 +2076,40 @@ function createPreprToolbar(opts) {
|
|
|
2043
2076
|
const search = new URLSearchParams(window.location.search);
|
|
2044
2077
|
const hideBar = search.get(PARAM_HIDE_BAR) === "true";
|
|
2045
2078
|
if (hideBar) {
|
|
2046
|
-
debug7.log(`${PARAM_HIDE_BAR}=true \u2014
|
|
2047
|
-
return noop();
|
|
2079
|
+
debug7.log(`${PARAM_HIDE_BAR}=true \u2014 no visible toolbar`);
|
|
2048
2080
|
}
|
|
2081
|
+
const mountUi = (options?.ui ?? true) && !isIframe && !hideBar;
|
|
2049
2082
|
const locale = resolveLocale(options);
|
|
2083
|
+
const features = resolveFeatures(options?.features);
|
|
2050
2084
|
const previewMode = getCookie(COOKIE_PREVIEW_MODE) === "true";
|
|
2051
2085
|
const toolbarOpen = getCookie(COOKIE_TOOLBAR_OPEN) === "true";
|
|
2052
|
-
const cookieSegment = getCookie(COOKIE_SEGMENT);
|
|
2053
|
-
const cookieVariant = getCookie(COOKIE_VARIANT);
|
|
2054
|
-
const rawVariant = props
|
|
2086
|
+
const cookieSegment = features.segments ? getCookie(COOKIE_SEGMENT) : null;
|
|
2087
|
+
const cookieVariant = features.abTesting ? getCookie(COOKIE_VARIANT) : null;
|
|
2088
|
+
const rawVariant = features.abTesting ? props?.activeVariant ?? cookieVariant : null;
|
|
2055
2089
|
const selectedVariant = rawVariant === "A" || rawVariant === "B" ? rawVariant : null;
|
|
2056
2090
|
const store = createToolbarStore({
|
|
2057
2091
|
locale,
|
|
2058
|
-
|
|
2059
|
-
|
|
2092
|
+
features,
|
|
2093
|
+
segments: features.segments ? buildSegments(props?.segments ?? props?.data ?? []) : [],
|
|
2094
|
+
selectedSegment: features.segments ? props?.activeSegment ?? cookieSegment ?? null : null,
|
|
2060
2095
|
selectedVariant,
|
|
2061
2096
|
previewMode,
|
|
2062
2097
|
toolbarOpen,
|
|
2063
2098
|
isIframe
|
|
2064
2099
|
});
|
|
2065
2100
|
let el = null;
|
|
2066
|
-
if (
|
|
2101
|
+
if (mountUi) {
|
|
2067
2102
|
definePreprToolbar();
|
|
2068
2103
|
el = document.createElement("prepr-toolbar");
|
|
2069
2104
|
document.body.appendChild(el);
|
|
2070
2105
|
el.connect(store, (key) => t(key, locale));
|
|
2071
2106
|
}
|
|
2107
|
+
const editingEnabled = features.editMode || isIframe;
|
|
2072
2108
|
const stega = createStegaController({
|
|
2073
|
-
// The CMS deep-link tooltip is
|
|
2074
|
-
//
|
|
2075
|
-
|
|
2109
|
+
// The CMS deep-link tooltip is chrome, so it follows the same decision as
|
|
2110
|
+
// the bar: noise inside the editor (clicking the element itself requests
|
|
2111
|
+
// the edit there), and unwanted in a headless preview that asked for no UI.
|
|
2112
|
+
tooltip: mountUi,
|
|
2076
2113
|
// In the editor, ask the parent to focus the field instead of opening a new
|
|
2077
2114
|
// tab. Standalone previews keep the window.open behaviour.
|
|
2078
2115
|
onEdit: ({ href, origin, id, field }) => {
|
|
@@ -2096,6 +2133,8 @@ function createPreprToolbar(opts) {
|
|
|
2096
2133
|
}
|
|
2097
2134
|
const handleChange = createChangeHandler({
|
|
2098
2135
|
store,
|
|
2136
|
+
features,
|
|
2137
|
+
editingEnabled,
|
|
2099
2138
|
navigate: (url) => navigation.navigate(url),
|
|
2100
2139
|
currentPath: () => navigation.currentPath(),
|
|
2101
2140
|
reload: navigation.reload ?? (() => window.location.reload()),
|
|
@@ -2109,7 +2148,9 @@ function createPreprToolbar(opts) {
|
|
|
2109
2148
|
handleChange(before, state);
|
|
2110
2149
|
});
|
|
2111
2150
|
syncAutoClean(store.get().previewMode);
|
|
2112
|
-
const bridge = createIframeBridge(store
|
|
2151
|
+
const bridge = createIframeBridge(store, {
|
|
2152
|
+
allowedEditorOrigins: options?.allowedEditorOrigins
|
|
2153
|
+
});
|
|
2113
2154
|
sendPreprEvent("getScrollPosition", { value: 0 }, {
|
|
2114
2155
|
allowUntrustedTarget: true
|
|
2115
2156
|
});
|
|
@@ -2124,7 +2165,7 @@ function createPreprToolbar(opts) {
|
|
|
2124
2165
|
bridge.stop();
|
|
2125
2166
|
}
|
|
2126
2167
|
el?.remove();
|
|
2127
|
-
debug7.log("
|
|
2168
|
+
debug7.log("preview destroyed");
|
|
2128
2169
|
}
|
|
2129
2170
|
const controller = { destroy };
|
|
2130
2171
|
controllerStores.set(controller, store);
|
|
@@ -2170,12 +2211,16 @@ function loadTrackingPixel(trackingId, config) {
|
|
|
2170
2211
|
}
|
|
2171
2212
|
|
|
2172
2213
|
// src/nextjs/components.ts
|
|
2173
|
-
function PreprToolbar(
|
|
2214
|
+
function PreprToolbar({
|
|
2215
|
+
options,
|
|
2216
|
+
...props
|
|
2217
|
+
}) {
|
|
2174
2218
|
const router = (0, import_navigation.useRouter)();
|
|
2175
2219
|
const pathname = (0, import_navigation.usePathname)();
|
|
2176
2220
|
(0, import_react.useEffect)(() => {
|
|
2177
|
-
const toolbar =
|
|
2221
|
+
const toolbar = createPreprPreview({
|
|
2178
2222
|
props,
|
|
2223
|
+
options,
|
|
2179
2224
|
navigation: {
|
|
2180
2225
|
navigate: (url) => router.push(url),
|
|
2181
2226
|
currentPath: () => pathname + window.location.search
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as PreprToolbarComponentProps, k as PreprPixelConfig } from '../pixel-763p7m6A.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Mounts the Prepr toolbar and wires it to the Next.js router so segment/variant
|
|
5
5
|
* switches navigate. Renders nothing — the toolbar UI is a custom element that
|
|
6
|
-
* `
|
|
6
|
+
* `createPreprPreview` mounts imperatively.
|
|
7
7
|
*/
|
|
8
|
-
declare function PreprToolbar(props:
|
|
8
|
+
declare function PreprToolbar({ options, ...props }: PreprToolbarComponentProps): null;
|
|
9
9
|
interface PreprTrackingPixelProps {
|
|
10
10
|
/** Prepr tracking/access token. */
|
|
11
11
|
id: string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as PreprToolbarComponentProps, k as PreprPixelConfig } from '../pixel-763p7m6A.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Mounts the Prepr toolbar and wires it to the Next.js router so segment/variant
|
|
5
5
|
* switches navigate. Renders nothing — the toolbar UI is a custom element that
|
|
6
|
-
* `
|
|
6
|
+
* `createPreprPreview` mounts imperatively.
|
|
7
7
|
*/
|
|
8
|
-
declare function PreprToolbar(props:
|
|
8
|
+
declare function PreprToolbar({ options, ...props }: PreprToolbarComponentProps): null;
|
|
9
9
|
interface PreprTrackingPixelProps {
|
|
10
10
|
/** Prepr tracking/access token. */
|
|
11
11
|
id: string;
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
createPreprPreview,
|
|
4
4
|
loadTrackingPixel
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-6OOGNVJ4.js";
|
|
6
6
|
|
|
7
7
|
// src/nextjs/components.ts
|
|
8
8
|
import { usePathname, useRouter } from "next/navigation";
|
|
9
9
|
import { useEffect } from "react";
|
|
10
|
-
function PreprToolbar(
|
|
10
|
+
function PreprToolbar({
|
|
11
|
+
options,
|
|
12
|
+
...props
|
|
13
|
+
}) {
|
|
11
14
|
const router = useRouter();
|
|
12
15
|
const pathname = usePathname();
|
|
13
16
|
useEffect(() => {
|
|
14
|
-
const toolbar =
|
|
17
|
+
const toolbar = createPreprPreview({
|
|
15
18
|
props,
|
|
19
|
+
options,
|
|
16
20
|
navigation: {
|
|
17
21
|
navigate: (url) => router.push(url),
|
|
18
22
|
currentPath: () => pathname + window.location.search
|