@xenia-martech/sdk-js 1.2.0-dev.2 → 1.2.0-dev.4
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 +112 -3
- package/dist/{actions-BcdlokGO.d.cts → actions-C18Kv-4w.d.cts} +50 -2
- package/dist/{actions-BcdlokGO.d.ts → actions-C18Kv-4w.d.ts} +50 -2
- package/dist/{api-CktVQ6Gl.d.ts → api-0IycTYJR.d.cts} +4 -2
- package/dist/{api-BEPW0Uan.d.cts → api-COXWLL5L.d.ts} +4 -2
- package/dist/{chunk-3PYPPIUM.cjs → chunk-2DR5AVPX.cjs} +11 -46
- package/dist/chunk-2DR5AVPX.cjs.map +1 -0
- package/dist/chunk-BQ5PJCZ7.js +179 -0
- package/dist/chunk-BQ5PJCZ7.js.map +1 -0
- package/dist/{chunk-DWFVDNU5.js → chunk-GWRDZIVA.js} +5 -37
- package/dist/chunk-GWRDZIVA.js.map +1 -0
- package/dist/{chunk-7NGJC7CF.js → chunk-LW5AEJ6K.js} +20 -183
- package/dist/chunk-LW5AEJ6K.js.map +1 -0
- package/dist/chunk-N5M4PI7C.cjs +192 -0
- package/dist/chunk-N5M4PI7C.cjs.map +1 -0
- package/dist/chunk-OMMIRSPK.js +336 -0
- package/dist/chunk-OMMIRSPK.js.map +1 -0
- package/dist/chunk-QEYONKWY.js +42 -0
- package/dist/chunk-QEYONKWY.js.map +1 -0
- package/dist/{chunk-IBNIRT7J.cjs → chunk-RWE2OCQL.cjs} +20 -194
- package/dist/chunk-RWE2OCQL.cjs.map +1 -0
- package/dist/chunk-WHGK55JP.cjs +46 -0
- package/dist/chunk-WHGK55JP.cjs.map +1 -0
- package/dist/chunk-YLMMTKQL.cjs +358 -0
- package/dist/chunk-YLMMTKQL.cjs.map +1 -0
- package/dist/client/index.cjs +320 -31
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +15 -4
- package/dist/client/index.d.ts +15 -4
- package/dist/client/index.js +320 -31
- package/dist/client/index.js.map +1 -1
- package/dist/client/react/index.cjs +731 -34
- package/dist/client/react/index.cjs.map +1 -1
- package/dist/client/react/index.d.cts +32 -3
- package/dist/client/react/index.d.ts +32 -3
- package/dist/client/react/index.js +732 -37
- package/dist/client/react/index.js.map +1 -1
- package/dist/cms/contentful.cjs +2 -1
- package/dist/cms/contentful.cjs.map +1 -1
- package/dist/cms/contentful.d.cts +2 -1
- package/dist/cms/contentful.d.ts +2 -1
- package/dist/cms/contentful.js +2 -1
- package/dist/cms/contentful.js.map +1 -1
- package/dist/consent-Cemn91j3.d.ts +248 -0
- package/dist/consent-DgWdjsSy.d.cts +248 -0
- package/dist/{cookie-names-CLqtmGBM.d.ts → cookie-names-BvUaW3uo.d.ts} +1 -1
- package/dist/{cookie-names-BACctagz.d.cts → cookie-names-CnL65Oly.d.cts} +1 -1
- package/dist/edge/index.cjs +280 -0
- package/dist/edge/index.cjs.map +1 -0
- package/dist/edge/index.d.cts +61 -0
- package/dist/edge/index.d.ts +61 -0
- package/dist/edge/index.js +191 -0
- package/dist/edge/index.js.map +1 -0
- package/dist/index.cjs +41 -40
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -1
- package/dist/next/index.cjs +320 -15
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +180 -6
- package/dist/next/index.d.ts +180 -6
- package/dist/next/index.js +309 -9
- package/dist/next/index.js.map +1 -1
- package/dist/personalize-content-c2DDAV6g.d.ts +140 -0
- package/dist/personalize-content-ghZPKlf1.d.cts +140 -0
- package/dist/server/index.cjs +34 -32
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +6 -5
- package/dist/server/index.d.ts +6 -5
- package/dist/server/index.js +5 -3
- package/dist/server/index.js.map +1 -1
- package/dist/{personalize-content-Clc1oMYY.d.ts → types-DgVHS4Qw.d.cts} +7 -127
- package/dist/{personalize-content-Clc1oMYY.d.cts → types-DgVHS4Qw.d.ts} +7 -127
- package/dist/xenia-sdk.iife.js +3 -3
- package/dist/xenia-sdk.iife.js.map +1 -1
- package/dist/{xenia-server-o_wDOcei.d.cts → xenia-server-C3O-cWQT.d.cts} +3 -2
- package/dist/{xenia-server-BWhWD0La.d.ts → xenia-server-DV8Sjmoo.d.ts} +3 -2
- package/package.json +14 -1
- package/dist/chunk-3PYPPIUM.cjs.map +0 -1
- package/dist/chunk-7NGJC7CF.js.map +0 -1
- package/dist/chunk-DWFVDNU5.js.map +0 -1
- package/dist/chunk-IBNIRT7J.cjs.map +0 -1
|
@@ -555,7 +555,7 @@ var XeniaApi = class {
|
|
|
555
555
|
* ({@link personalizeContent}) calls this once per round with the runtime
|
|
556
556
|
* targets it found; callers rarely need it directly.
|
|
557
557
|
*/
|
|
558
|
-
async resolveEntries(entryIds, opts
|
|
558
|
+
async resolveEntries(entryIds, opts) {
|
|
559
559
|
const { data } = await this.http.request({
|
|
560
560
|
method: "POST",
|
|
561
561
|
path: "/api/personalization/resolve-entries",
|
|
@@ -563,7 +563,8 @@ var XeniaApi = class {
|
|
|
563
563
|
body: compact({
|
|
564
564
|
entryIds,
|
|
565
565
|
data: opts.data,
|
|
566
|
-
locale: opts.locale
|
|
566
|
+
locale: opts.locale,
|
|
567
|
+
contentKey: opts.contentKey
|
|
567
568
|
})
|
|
568
569
|
});
|
|
569
570
|
return data;
|
|
@@ -687,7 +688,7 @@ var DEFAULT_TIMEOUT_MS = 300;
|
|
|
687
688
|
var DEFAULT_MAX_ROUNDS = 3;
|
|
688
689
|
var copyOf = (v) => v && typeof v === "object" && typeof structuredClone === "function" ? structuredClone(v) : v;
|
|
689
690
|
async function personalizeContent(payload, options) {
|
|
690
|
-
const
|
|
691
|
+
const cms = scopedAdapter(options.cms, options.contentKey);
|
|
691
692
|
const maxRounds = options.maxRounds ?? DEFAULT_MAX_ROUNDS;
|
|
692
693
|
const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
693
694
|
const applied = [];
|
|
@@ -886,7 +887,8 @@ function withDeadline(promise, deadline) {
|
|
|
886
887
|
})
|
|
887
888
|
]).finally(() => clearTimeout(timer));
|
|
888
889
|
}
|
|
889
|
-
function pendingTargets(payload,
|
|
890
|
+
function pendingTargets(payload, adapter, contentKey) {
|
|
891
|
+
const cms = scopedAdapter(adapter, contentKey);
|
|
890
892
|
try {
|
|
891
893
|
let content = typeof structuredClone === "function" ? structuredClone(payload) : payload;
|
|
892
894
|
content = cms.prepare ? cms.prepare(content) : content;
|
|
@@ -897,6 +899,15 @@ function pendingTargets(payload, cms) {
|
|
|
897
899
|
return [];
|
|
898
900
|
}
|
|
899
901
|
}
|
|
902
|
+
function scopedAdapter(cms, contentKey) {
|
|
903
|
+
return {
|
|
904
|
+
...cms,
|
|
905
|
+
marker(entry) {
|
|
906
|
+
const m = cms.marker(entry);
|
|
907
|
+
return m && contentKey && m.keys?.includes(contentKey) ? m : null;
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
}
|
|
900
911
|
async function personalizePage(ref, options) {
|
|
901
912
|
let page;
|
|
902
913
|
try {
|
|
@@ -1086,17 +1097,22 @@ function parseDataAttribute(value) {
|
|
|
1086
1097
|
return { eventId, interactionType };
|
|
1087
1098
|
}
|
|
1088
1099
|
function readSlotMeta(el) {
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1091
|
-
const contentEntryId =
|
|
1092
|
-
|
|
1093
|
-
const personalized =
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
+
const attr = (name) => el.getAttribute(`data-xenia-${name}`) ?? void 0;
|
|
1101
|
+
const contentKey = attr("content-key");
|
|
1102
|
+
const contentEntryId = attr("content-entry-id");
|
|
1103
|
+
if (!contentKey && !contentEntryId) return null;
|
|
1104
|
+
const personalized = attr("personalized");
|
|
1105
|
+
const meta2 = {};
|
|
1106
|
+
const slot = attr("slot");
|
|
1107
|
+
if (slot) meta2.slot = slot;
|
|
1108
|
+
if (contentKey) meta2.contentKey = contentKey;
|
|
1109
|
+
if (contentEntryId) meta2.contentEntryId = contentEntryId;
|
|
1110
|
+
const baseEntryId = attr("base-entry-id");
|
|
1111
|
+
if (baseEntryId) meta2.baseEntryId = baseEntryId;
|
|
1112
|
+
if (personalized != null) meta2.personalized = personalized === "true";
|
|
1113
|
+
const bucket = attr("bucket");
|
|
1114
|
+
if (bucket) meta2.bucket = bucket;
|
|
1115
|
+
return meta2;
|
|
1100
1116
|
}
|
|
1101
1117
|
var AutoTracker = class {
|
|
1102
1118
|
opts;
|
|
@@ -1157,8 +1173,12 @@ var AutoTracker = class {
|
|
|
1157
1173
|
if (this.opts.fireSlot) {
|
|
1158
1174
|
const slot = target.closest("[data-xenia-slot]");
|
|
1159
1175
|
if (slot) {
|
|
1160
|
-
const
|
|
1161
|
-
|
|
1176
|
+
const meta2 = readSlotMeta(slot);
|
|
1177
|
+
const found = target.closest("a, button");
|
|
1178
|
+
const control = found && slot.contains(found) ? found : null;
|
|
1179
|
+
if (!control && this.opts.slotClicks === "controls") return;
|
|
1180
|
+
const label = control ? (control.textContent ?? "").trim().slice(0, 60) : "";
|
|
1181
|
+
if (meta2 && !this.throttled(slot)) this.opts.fireSlot("click", label ? { ...meta2, label } : meta2);
|
|
1162
1182
|
}
|
|
1163
1183
|
}
|
|
1164
1184
|
};
|
|
@@ -1166,24 +1186,24 @@ var AutoTracker = class {
|
|
|
1166
1186
|
}
|
|
1167
1187
|
observeSlot(el) {
|
|
1168
1188
|
if (!this.opts.fireSlot || this.slotObservers.has(el)) return;
|
|
1169
|
-
const
|
|
1170
|
-
if (!
|
|
1189
|
+
const meta2 = readSlotMeta(el);
|
|
1190
|
+
if (!meta2) return;
|
|
1171
1191
|
const fireSlot = this.opts.fireSlot;
|
|
1172
1192
|
const { threshold, rootMargin } = this.opts.viewport;
|
|
1173
1193
|
const observer = new IntersectionObserver(
|
|
1174
1194
|
(entries) => {
|
|
1175
1195
|
for (const entry of entries) {
|
|
1176
|
-
|
|
1177
|
-
if (this.slotImpressed.has(el)) continue;
|
|
1196
|
+
const inView = entry.isIntersecting && (entry.intersectionRatio >= threshold || entry.intersectionRect.height >= window.innerHeight / 2);
|
|
1197
|
+
if (!inView || this.slotImpressed.has(el)) continue;
|
|
1178
1198
|
this.slotImpressed.add(el);
|
|
1179
|
-
fireSlot("impression",
|
|
1199
|
+
fireSlot("impression", meta2);
|
|
1180
1200
|
observer.disconnect();
|
|
1181
1201
|
this.slotObservers.delete(el);
|
|
1182
1202
|
}
|
|
1183
1203
|
},
|
|
1184
|
-
{ threshold, rootMargin }
|
|
1204
|
+
{ threshold: [.../* @__PURE__ */ new Set([0.1, 0.25, threshold])].sort(), rootMargin }
|
|
1185
1205
|
);
|
|
1186
|
-
observer.observe(el);
|
|
1206
|
+
observer.observe(boxOf(el));
|
|
1187
1207
|
this.slotObservers.set(el, observer);
|
|
1188
1208
|
}
|
|
1189
1209
|
observeViewport(el, eventId) {
|
|
@@ -1237,6 +1257,235 @@ var AutoTracker = class {
|
|
|
1237
1257
|
this.mutationObserver.observe(document.body, { childList: true, subtree: true });
|
|
1238
1258
|
}
|
|
1239
1259
|
};
|
|
1260
|
+
function boxOf(el) {
|
|
1261
|
+
if (typeof getComputedStyle !== "function") return el;
|
|
1262
|
+
return getComputedStyle(el).display === "contents" ? el.firstElementChild ?? el : el;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// src/edge/trail.ts
|
|
1266
|
+
var MAX_TRAIL = 8;
|
|
1267
|
+
var TRAIL_MAX_AGE_S = 30 * 60;
|
|
1268
|
+
function trailCookieName(prefix = DEFAULT_PREFIX) {
|
|
1269
|
+
return `${prefix}_trail`;
|
|
1270
|
+
}
|
|
1271
|
+
var decode = (v) => {
|
|
1272
|
+
try {
|
|
1273
|
+
return decodeURIComponent(v);
|
|
1274
|
+
} catch {
|
|
1275
|
+
return v;
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
function readTrail(value) {
|
|
1279
|
+
if (!value) return [];
|
|
1280
|
+
try {
|
|
1281
|
+
const parsed = JSON.parse(decode(value));
|
|
1282
|
+
if (!Array.isArray(parsed)) return [];
|
|
1283
|
+
return parsed.filter((t) => Array.isArray(t) && typeof t[0] === "string" && typeof t[1] === "number").slice(0, MAX_TRAIL).map(
|
|
1284
|
+
([path, at, seconds]) => typeof seconds === "number" && seconds >= 0 && seconds <= 86400 ? { path, at, seconds } : { path, at }
|
|
1285
|
+
);
|
|
1286
|
+
} catch {
|
|
1287
|
+
return [];
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
function withView(trail, path, now = Date.now()) {
|
|
1291
|
+
if (trail[0]?.path === path) return trail;
|
|
1292
|
+
const earlier = trail.find((t) => t.path === path);
|
|
1293
|
+
return [{ path, at: earlier?.at ?? now }, ...trail.filter((t) => t.path !== path)].slice(0, MAX_TRAIL);
|
|
1294
|
+
}
|
|
1295
|
+
function trailCookieValue(trail) {
|
|
1296
|
+
return JSON.stringify(
|
|
1297
|
+
trail.slice(0, MAX_TRAIL).map((t) => t.seconds === void 0 ? [t.path, t.at] : [t.path, t.at, t.seconds])
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// src/client/page-tracking.ts
|
|
1302
|
+
var SETTLE_MS = 3e3;
|
|
1303
|
+
var POLL_MS = 50;
|
|
1304
|
+
var MIN_VISIT_MS = 250;
|
|
1305
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1306
|
+
var restore = null;
|
|
1307
|
+
function watchNavigation(listener) {
|
|
1308
|
+
listeners.add(listener);
|
|
1309
|
+
if (!restore) {
|
|
1310
|
+
const { pushState, replaceState } = history;
|
|
1311
|
+
const notify = () => listeners.forEach((l) => {
|
|
1312
|
+
try {
|
|
1313
|
+
l();
|
|
1314
|
+
} catch {
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
const ownPush = function(...args) {
|
|
1318
|
+
pushState.apply(this, args);
|
|
1319
|
+
notify();
|
|
1320
|
+
};
|
|
1321
|
+
const ownReplace = function(...args) {
|
|
1322
|
+
replaceState.apply(this, args);
|
|
1323
|
+
notify();
|
|
1324
|
+
};
|
|
1325
|
+
history.pushState = ownPush;
|
|
1326
|
+
history.replaceState = ownReplace;
|
|
1327
|
+
window.addEventListener("popstate", notify);
|
|
1328
|
+
restore = () => {
|
|
1329
|
+
if (history.pushState !== ownPush || history.replaceState !== ownReplace) return false;
|
|
1330
|
+
history.pushState = pushState;
|
|
1331
|
+
history.replaceState = replaceState;
|
|
1332
|
+
window.removeEventListener("popstate", notify);
|
|
1333
|
+
return true;
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
return () => {
|
|
1337
|
+
listeners.delete(listener);
|
|
1338
|
+
if (!listeners.size && restore?.()) restore = null;
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
var meta = (name) => document.querySelector(`meta[name="${name}"]`)?.content || void 0;
|
|
1342
|
+
function startPageTracking(client, opts) {
|
|
1343
|
+
let path = location.pathname;
|
|
1344
|
+
let lastTitle = null;
|
|
1345
|
+
let timer;
|
|
1346
|
+
let view2 = { path, since: visibleNow(), sent: false };
|
|
1347
|
+
function visibleNow() {
|
|
1348
|
+
return document.visibilityState === "visible" ? Date.now() : null;
|
|
1349
|
+
}
|
|
1350
|
+
function sendView() {
|
|
1351
|
+
clearTimeout(timer);
|
|
1352
|
+
const started = Date.now();
|
|
1353
|
+
const tick = () => {
|
|
1354
|
+
const settled = document.title !== "" && document.title !== lastTitle;
|
|
1355
|
+
if (!settled && Date.now() - started < SETTLE_MS) {
|
|
1356
|
+
timer = setTimeout(tick, POLL_MS);
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
const params = new URLSearchParams(location.search);
|
|
1360
|
+
const searchTerm = (params.get("utm_term") ?? params.get("q") ?? "").slice(0, 120);
|
|
1361
|
+
const category = meta("xenia-category");
|
|
1362
|
+
lastTitle = document.title;
|
|
1363
|
+
client.track("pageView", {
|
|
1364
|
+
title: (meta("xenia-title") ?? document.title).slice(0, 160),
|
|
1365
|
+
...searchTerm ? { searchTerm } : {},
|
|
1366
|
+
...category ? { category } : {}
|
|
1367
|
+
});
|
|
1368
|
+
};
|
|
1369
|
+
timer = setTimeout(tick, 0);
|
|
1370
|
+
}
|
|
1371
|
+
function leave() {
|
|
1372
|
+
if (view2.sent || view2.since === null || Date.now() - view2.since < MIN_VISIT_MS) return;
|
|
1373
|
+
view2.sent = true;
|
|
1374
|
+
client.track("page_leave", { path: view2.path, durationMs: Date.now() - view2.since });
|
|
1375
|
+
}
|
|
1376
|
+
const onNavigate = () => {
|
|
1377
|
+
if (location.pathname === path) return;
|
|
1378
|
+
if (opts.pageLeave) leave();
|
|
1379
|
+
path = location.pathname;
|
|
1380
|
+
view2 = { path, since: visibleNow(), sent: false };
|
|
1381
|
+
if (opts.pageViews) sendView();
|
|
1382
|
+
};
|
|
1383
|
+
const onVisibility = () => {
|
|
1384
|
+
if (document.visibilityState === "hidden") leave();
|
|
1385
|
+
else if (view2.since === null) view2.since = Date.now();
|
|
1386
|
+
};
|
|
1387
|
+
const unwatch = watchNavigation(onNavigate);
|
|
1388
|
+
if (opts.pageLeave) document.addEventListener("visibilitychange", onVisibility);
|
|
1389
|
+
if (opts.pageViews) sendView();
|
|
1390
|
+
return () => {
|
|
1391
|
+
clearTimeout(timer);
|
|
1392
|
+
unwatch();
|
|
1393
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// src/client/engagement.ts
|
|
1398
|
+
var rootOf = (opt, fallback) => typeof opt === "object" && opt.root || fallback;
|
|
1399
|
+
function readCookie(name) {
|
|
1400
|
+
return document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`))?.[1];
|
|
1401
|
+
}
|
|
1402
|
+
function writeCookie(name, value) {
|
|
1403
|
+
try {
|
|
1404
|
+
document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${TRAIL_MAX_AGE_S}; samesite=lax`;
|
|
1405
|
+
} catch {
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
function sectionOf(link) {
|
|
1409
|
+
if (!link || link.origin !== location.origin) return void 0;
|
|
1410
|
+
return link.pathname.split("/").filter(Boolean)[0];
|
|
1411
|
+
}
|
|
1412
|
+
function cardImage(target, root) {
|
|
1413
|
+
let el = target;
|
|
1414
|
+
for (let depth = 0; el && depth < 5 && el !== root; depth++, el = el.parentElement) {
|
|
1415
|
+
const imgs = [...el.querySelectorAll("img")].filter((i) => i.alt.trim());
|
|
1416
|
+
if (!imgs.length) continue;
|
|
1417
|
+
return new Set(imgs.map((i) => i.alt.trim())).size === 1 ? imgs[0] : null;
|
|
1418
|
+
}
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1421
|
+
function startEngagement(client, opts) {
|
|
1422
|
+
const trailName = trailCookieName(opts.prefix);
|
|
1423
|
+
const clicksName = `${opts.prefix}_clicks`;
|
|
1424
|
+
const visibleNow = () => document.visibilityState === "visible" ? Date.now() : null;
|
|
1425
|
+
let current = { path: location.pathname, since: visibleNow(), stamped: false };
|
|
1426
|
+
const stamp = () => {
|
|
1427
|
+
if (current.stamped || !opts.trail || !opts.analyticsAllowed() || current.since === null) return;
|
|
1428
|
+
current.stamped = true;
|
|
1429
|
+
const seconds = Math.round((Date.now() - current.since) / 100) / 10;
|
|
1430
|
+
const trail = readTrail(readCookie(trailName));
|
|
1431
|
+
const i = trail.findIndex((t) => t.path === current.path);
|
|
1432
|
+
if (i < 0) return;
|
|
1433
|
+
trail[i] = { ...trail[i], seconds };
|
|
1434
|
+
writeCookie(trailName, trailCookieValue(trail));
|
|
1435
|
+
};
|
|
1436
|
+
const onNavigate = () => {
|
|
1437
|
+
if (location.pathname === current.path) return;
|
|
1438
|
+
stamp();
|
|
1439
|
+
current = { path: location.pathname, since: visibleNow(), stamped: false };
|
|
1440
|
+
if (!opts.trail || !opts.analyticsAllowed()) return;
|
|
1441
|
+
const trail = readTrail(readCookie(trailName));
|
|
1442
|
+
const next = withView(trail, current.path);
|
|
1443
|
+
if (next !== trail) writeCookie(trailName, trailCookieValue(next));
|
|
1444
|
+
};
|
|
1445
|
+
const onVisible = () => {
|
|
1446
|
+
if (document.visibilityState === "visible" && current.since === null) current.since = Date.now();
|
|
1447
|
+
};
|
|
1448
|
+
const onLeaveClick = (e) => {
|
|
1449
|
+
const link = e.target?.closest?.("a[href]");
|
|
1450
|
+
if (link && link.origin === location.origin && link.pathname !== location.pathname) stamp();
|
|
1451
|
+
};
|
|
1452
|
+
const bump = () => {
|
|
1453
|
+
if (!opts.analyticsAllowed()) return;
|
|
1454
|
+
writeCookie(clicksName, String((Number(readCookie(clicksName)) || 0) + 1));
|
|
1455
|
+
};
|
|
1456
|
+
const navRoot = rootOf(opts.navClicks, "header");
|
|
1457
|
+
const contentRoot = rootOf(opts.contentClicks, "main");
|
|
1458
|
+
const onClick = (e) => {
|
|
1459
|
+
const target = e.target;
|
|
1460
|
+
if (!target?.closest) return;
|
|
1461
|
+
const nav = opts.navClicks ? target.closest(`${navRoot} a[href]`) : null;
|
|
1462
|
+
if (nav) {
|
|
1463
|
+
const category2 = sectionOf(nav);
|
|
1464
|
+
if (!category2) return;
|
|
1465
|
+
client.track("nav_click", { category: category2, label: (nav.textContent ?? "").trim().slice(0, 60) });
|
|
1466
|
+
bump();
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
const root = opts.contentClicks ? target.closest(contentRoot) : null;
|
|
1470
|
+
if (!root) return;
|
|
1471
|
+
const img = target.closest("img") ?? cardImage(target, root);
|
|
1472
|
+
const label = (img?.alt ?? "").trim().slice(0, 160);
|
|
1473
|
+
if (!label) return;
|
|
1474
|
+
const category = sectionOf(target.closest("a[href]"));
|
|
1475
|
+
client.track("content_click", category ? { label, category } : { label });
|
|
1476
|
+
bump();
|
|
1477
|
+
};
|
|
1478
|
+
const unwatch = watchNavigation(onNavigate);
|
|
1479
|
+
window.addEventListener("click", onLeaveClick, { capture: true });
|
|
1480
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
1481
|
+
if (opts.contentClicks || opts.navClicks) document.addEventListener("click", onClick, { capture: true });
|
|
1482
|
+
return () => {
|
|
1483
|
+
unwatch();
|
|
1484
|
+
window.removeEventListener("click", onLeaveClick, { capture: true });
|
|
1485
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
1486
|
+
document.removeEventListener("click", onClick, { capture: true });
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1240
1489
|
|
|
1241
1490
|
// src/server/cookies.ts
|
|
1242
1491
|
function parseCookies(header) {
|
|
@@ -1247,7 +1496,12 @@ function parseCookies(header) {
|
|
|
1247
1496
|
if (idx === -1) continue;
|
|
1248
1497
|
const key = part.slice(0, idx).trim();
|
|
1249
1498
|
const value = part.slice(idx + 1).trim();
|
|
1250
|
-
if (key)
|
|
1499
|
+
if (!key) continue;
|
|
1500
|
+
try {
|
|
1501
|
+
out[key] = decodeURIComponent(value);
|
|
1502
|
+
} catch {
|
|
1503
|
+
out[key] = value;
|
|
1504
|
+
}
|
|
1251
1505
|
}
|
|
1252
1506
|
return out;
|
|
1253
1507
|
}
|
|
@@ -1321,6 +1575,14 @@ var Identity = class {
|
|
|
1321
1575
|
const storageKey = this.key("device_id");
|
|
1322
1576
|
try {
|
|
1323
1577
|
const cached = localStorage.getItem(storageKey);
|
|
1578
|
+
const fromServer = this.cookie?.authoritative ? this.readVisitorCookie() : null;
|
|
1579
|
+
const effective = localStorage.getItem(this.key("visitor_id")) ?? cached;
|
|
1580
|
+
if (cached && fromServer && fromServer !== effective) {
|
|
1581
|
+
this.deviceId = fromServer;
|
|
1582
|
+
localStorage.setItem(storageKey, fromServer);
|
|
1583
|
+
if (localStorage.getItem(this.key("visitor_id"))) localStorage.setItem(this.key("visitor_id"), fromServer);
|
|
1584
|
+
return fromServer;
|
|
1585
|
+
}
|
|
1324
1586
|
if (cached) {
|
|
1325
1587
|
this.deviceId = cached;
|
|
1326
1588
|
this.writeVisitorCookie(this.getVisitorId() ?? cached);
|
|
@@ -1500,7 +1762,7 @@ function resolveCookieConfig(opt) {
|
|
|
1500
1762
|
const o = opt === true ? {} : opt;
|
|
1501
1763
|
const sameSite = o.sameSite ?? "Lax";
|
|
1502
1764
|
const secure = sameSite === "None" ? true : o.secure ?? false;
|
|
1503
|
-
return { sameSite, secure, maxAgeDays: o.maxAgeDays ?? 365, domain: o.domain };
|
|
1765
|
+
return { sameSite, secure, maxAgeDays: o.maxAgeDays ?? 365, domain: o.domain, authoritative: o.authoritative ?? false };
|
|
1504
1766
|
}
|
|
1505
1767
|
|
|
1506
1768
|
// src/client/widgets/form.ts
|
|
@@ -2184,6 +2446,8 @@ var XeniaClient = class {
|
|
|
2184
2446
|
renderer;
|
|
2185
2447
|
analyticsQueue;
|
|
2186
2448
|
tracker = null;
|
|
2449
|
+
stopPageTracking = null;
|
|
2450
|
+
stopEngagement = null;
|
|
2187
2451
|
/** The in-flight (or finished) boot; shared so concurrent `init()` calls start one tracker. */
|
|
2188
2452
|
booting = null;
|
|
2189
2453
|
/** Bumped by `destroy()`, so a boot still waiting on the device id knows to stop. */
|
|
@@ -2235,17 +2499,21 @@ var XeniaClient = class {
|
|
|
2235
2499
|
rootMargin: this.options.viewport?.rootMargin,
|
|
2236
2500
|
triggerOnce: this.options.viewport?.triggerOnce ?? true
|
|
2237
2501
|
},
|
|
2502
|
+
slotClicks: this.options.slotClicks,
|
|
2238
2503
|
// Declarative `data-xenia` elements fire journey triggers (immediate).
|
|
2239
2504
|
fire: (eventId, interaction) => this.track(eventId, { interactionType: interaction }, { trigger: true }),
|
|
2240
2505
|
// `[data-xenia-slot]` elements fire analytics-only personalization events.
|
|
2241
|
-
fireSlot: (interaction,
|
|
2506
|
+
fireSlot: (interaction, meta2) => {
|
|
2242
2507
|
this.track(
|
|
2243
2508
|
"customEvent",
|
|
2244
2509
|
{
|
|
2245
2510
|
customEventName: interaction === "impression" ? "personalization_impression" : "personalization_click",
|
|
2246
|
-
contentKey:
|
|
2247
|
-
contentEntryId:
|
|
2248
|
-
baseEntryId:
|
|
2511
|
+
...meta2.contentKey ? { contentKey: meta2.contentKey } : {},
|
|
2512
|
+
contentEntryId: meta2.contentEntryId ?? null,
|
|
2513
|
+
baseEntryId: meta2.baseEntryId ?? null,
|
|
2514
|
+
...meta2.bucket ? { bucket: meta2.bucket } : {},
|
|
2515
|
+
...meta2.slot ? { slot: meta2.slot } : {},
|
|
2516
|
+
...meta2.label ? { label: meta2.label } : {}
|
|
2249
2517
|
},
|
|
2250
2518
|
{ trigger: false }
|
|
2251
2519
|
);
|
|
@@ -2253,11 +2521,32 @@ var XeniaClient = class {
|
|
|
2253
2521
|
});
|
|
2254
2522
|
this.tracker.start();
|
|
2255
2523
|
}
|
|
2524
|
+
const { pageViews = false, pageLeave = false } = this.options;
|
|
2525
|
+
if (pageViews || pageLeave) {
|
|
2526
|
+
this.stopPageTracking = startPageTracking({ track: (name, props) => this.track(name, props) }, { pageViews, pageLeave });
|
|
2527
|
+
}
|
|
2528
|
+
const { trail = false, contentClicks = false, navClicks = false } = this.options;
|
|
2529
|
+
if (trail || contentClicks || navClicks) {
|
|
2530
|
+
this.stopEngagement = startEngagement(
|
|
2531
|
+
{ track: (name, props) => this.track(name, props) },
|
|
2532
|
+
{
|
|
2533
|
+
trail,
|
|
2534
|
+
contentClicks,
|
|
2535
|
+
navClicks,
|
|
2536
|
+
prefix: this.options.prefix ?? "xenia",
|
|
2537
|
+
analyticsAllowed: () => this.analyticsAllowed() && !this.dntBlocked()
|
|
2538
|
+
}
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2256
2541
|
}
|
|
2257
2542
|
/** Tear down listeners, observers, and injected UI. */
|
|
2258
2543
|
destroy() {
|
|
2259
2544
|
this.tracker?.stop();
|
|
2260
2545
|
this.tracker = null;
|
|
2546
|
+
this.stopPageTracking?.();
|
|
2547
|
+
this.stopPageTracking = null;
|
|
2548
|
+
this.stopEngagement?.();
|
|
2549
|
+
this.stopEngagement = null;
|
|
2261
2550
|
this.renderer.destroy();
|
|
2262
2551
|
this.analyticsQueue.stop();
|
|
2263
2552
|
this.booting = null;
|
|
@@ -2408,7 +2697,7 @@ var XeniaClient = class {
|
|
|
2408
2697
|
return personalizePage(ref, { ...opts, resolve: this.entryResolver(opts) });
|
|
2409
2698
|
}
|
|
2410
2699
|
entryResolver(opts) {
|
|
2411
|
-
const resolveOpts = this.withVisitor({ data: opts.data, locale: opts.locale });
|
|
2700
|
+
const resolveOpts = { ...this.withVisitor({ data: opts.data, locale: opts.locale }), contentKey: opts.contentKey };
|
|
2412
2701
|
return async (ids) => (await this.api.resolveEntries(ids, resolveOpts)).decisions ?? [];
|
|
2413
2702
|
}
|
|
2414
2703
|
/** The SDK-owned visitor anchor: the `identify()`/`config.visitorId` id, else the device id. */
|
|
@@ -2825,8 +3114,8 @@ function usePersonalizedContent(page, opts) {
|
|
|
2825
3114
|
const xenia = useXenia();
|
|
2826
3115
|
const hide = opts.render === "hide-until-resolved";
|
|
2827
3116
|
const initialPending = react.useMemo(
|
|
2828
|
-
() => new Set(page == null ? [] : pendingTargets(page, opts.cms)),
|
|
2829
|
-
[page]
|
|
3117
|
+
() => new Set(page == null ? [] : pendingTargets(page, opts.cms, opts.contentKey)),
|
|
3118
|
+
[page, opts.contentKey]
|
|
2830
3119
|
// markers live in `page`; a new adapter object for the same page changes nothing
|
|
2831
3120
|
);
|
|
2832
3121
|
const [state, setState] = react.useState(
|
|
@@ -2834,7 +3123,7 @@ function usePersonalizedContent(page, opts) {
|
|
|
2834
3123
|
);
|
|
2835
3124
|
const optsRef = react.useRef(opts);
|
|
2836
3125
|
optsRef.current = opts;
|
|
2837
|
-
const optsKey = JSON.stringify({ data: opts.data ?? {}, locale: opts.locale });
|
|
3126
|
+
const optsKey = JSON.stringify({ data: opts.data ?? {}, locale: opts.locale, contentKey: opts.contentKey });
|
|
2838
3127
|
react.useEffect(() => {
|
|
2839
3128
|
if (page == null) return;
|
|
2840
3129
|
let active = true;
|
|
@@ -2955,13 +3244,421 @@ function reactMessageComponent(Component) {
|
|
|
2955
3244
|
};
|
|
2956
3245
|
}
|
|
2957
3246
|
|
|
3247
|
+
// src/edge/debug-entries.ts
|
|
3248
|
+
function readDebugEntries(raw) {
|
|
3249
|
+
if (!raw) return {};
|
|
3250
|
+
try {
|
|
3251
|
+
let text = decodeURIComponent(raw);
|
|
3252
|
+
if (text.startsWith("%")) text = decodeURIComponent(text);
|
|
3253
|
+
const parsed = JSON.parse(text);
|
|
3254
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
3255
|
+
} catch {
|
|
3256
|
+
return {};
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
var str = (v) => typeof v === "string" || typeof v === "number" ? String(v) : "";
|
|
3260
|
+
var isObject2 = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
|
|
3261
|
+
var strings = (v) => Array.isArray(v) ? v.map(str).filter(Boolean) : [];
|
|
3262
|
+
function view(e) {
|
|
3263
|
+
const decisions = (Array.isArray(e.decisions) ? e.decisions : []).filter(isObject2).map((d) => ({
|
|
3264
|
+
key: str(d.key),
|
|
3265
|
+
result: str(d.result) || "base",
|
|
3266
|
+
reason: str(d.reason),
|
|
3267
|
+
trace: isObject2(d.trace) ? Object.entries(d.trace).map(([step, outcome]) => [step, str(outcome)]) : []
|
|
3268
|
+
}));
|
|
3269
|
+
const p = isObject2(e.profile) ? e.profile : void 0;
|
|
3270
|
+
const answers = isObject2(p?.answers) ? Object.entries(p.answers).flatMap(
|
|
3271
|
+
([name, a]) => isObject2(a) ? [{
|
|
3272
|
+
name,
|
|
3273
|
+
choice: str(a.choice),
|
|
3274
|
+
source: str(a.source),
|
|
3275
|
+
confidence: typeof a.confidence === "number" ? a.confidence : void 0,
|
|
3276
|
+
model: str(a.model),
|
|
3277
|
+
scores: isObject2(a.scores) ? Object.entries(a.scores).filter((x) => typeof x[1] === "number").sort((x, y) => y[1] - x[1]) : []
|
|
3278
|
+
}] : []
|
|
3279
|
+
) : [];
|
|
3280
|
+
const r = isObject2(e.request) ? e.request : {};
|
|
3281
|
+
const geoOf = (g) => isObject2(g) ? Object.fromEntries(Object.entries(g).map(([k, v]) => [k, str(v)])) : void 0;
|
|
3282
|
+
const consent = isObject2(r.consent) ? r.consent : void 0;
|
|
3283
|
+
const custom = isObject2(e.custom) ? Object.entries(e.custom).flatMap(
|
|
3284
|
+
([label, value]) => Array.isArray(value) ? [{ label, lines: strings(value) }] : str(value) ? [{ label, lines: [str(value)] }] : []
|
|
3285
|
+
) : [];
|
|
3286
|
+
return {
|
|
3287
|
+
page: str(e.page),
|
|
3288
|
+
source: str(e.source),
|
|
3289
|
+
ms: typeof e.ms === "number" ? e.ms : void 0,
|
|
3290
|
+
decisions,
|
|
3291
|
+
profile: p ? { pageViews: typeof p.pageViews === "number" ? p.pageViews : 0, lastSection: str(p.lastSection), campaign: str(p.campaign), answers } : void 0,
|
|
3292
|
+
request: {
|
|
3293
|
+
location: geoOf(r.location),
|
|
3294
|
+
detected: geoOf(r.detected),
|
|
3295
|
+
via: str(r.via),
|
|
3296
|
+
seen: strings(r.seen),
|
|
3297
|
+
device: str(r.device),
|
|
3298
|
+
consent: consent ? { personalization: consent.personalization !== false, analytics: consent.analytics !== false } : void 0
|
|
3299
|
+
},
|
|
3300
|
+
custom
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3303
|
+
var changed = (result) => !/^(base|control|suppressed)\b/.test(result);
|
|
3304
|
+
function debugStatus(entry) {
|
|
3305
|
+
const v = view(entry);
|
|
3306
|
+
if (v.source === "timeout" || v.source === "error") return "attention";
|
|
3307
|
+
return v.decisions.some((d) => changed(d.result)) ? "personalized" : "base";
|
|
3308
|
+
}
|
|
3309
|
+
var STATUS_LABEL = {
|
|
3310
|
+
personalized: "personalized",
|
|
3311
|
+
base: "showing base content",
|
|
3312
|
+
attention: "couldn't decide"
|
|
3313
|
+
};
|
|
3314
|
+
var SOURCE_CHIP = {
|
|
3315
|
+
xenia: "Live",
|
|
3316
|
+
cache: "Cached",
|
|
3317
|
+
timeout: "Timed out",
|
|
3318
|
+
error: "Error",
|
|
3319
|
+
"site page": "Site page",
|
|
3320
|
+
"not targeted": "Not targeted",
|
|
3321
|
+
"no consent": "No consent"
|
|
3322
|
+
};
|
|
3323
|
+
function sourceChip(source) {
|
|
3324
|
+
const known = SOURCE_CHIP[source] ?? Object.entries(SOURCE_CHIP).find(([k]) => source.startsWith(`${k} `))?.[1];
|
|
3325
|
+
if (known) return known;
|
|
3326
|
+
return source ? source[0].toUpperCase() + source.slice(1) : "Unknown";
|
|
3327
|
+
}
|
|
3328
|
+
function readEntry(prefix) {
|
|
3329
|
+
const raw = document.cookie.match(new RegExp(`(?:^|;\\s*)${prefix}_debug=([^;]+)`))?.[1];
|
|
3330
|
+
return readDebugEntries(raw)[location.pathname] ?? null;
|
|
3331
|
+
}
|
|
3332
|
+
var OPEN_KEY = "xenia_debug_open";
|
|
3333
|
+
var TAB_KEY = "xenia_debug_tab";
|
|
3334
|
+
function Mark({ size }) {
|
|
3335
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 32 20", "aria-hidden": "true", focusable: "false", children: [
|
|
3336
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "10", cy: "10", r: "9", fill: "#3EC6FF" }),
|
|
3337
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "22", cy: "10", r: "9", fill: "#97CF49" })
|
|
3338
|
+
] });
|
|
3339
|
+
}
|
|
3340
|
+
function Id({ value }) {
|
|
3341
|
+
return value.length > 14 && /^[0-9a-f-]+$/i.test(value) ? /* @__PURE__ */ jsxRuntime.jsxs("span", { title: value, children: [
|
|
3342
|
+
value.slice(0, 8),
|
|
3343
|
+
"\u2026"
|
|
3344
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: value });
|
|
3345
|
+
}
|
|
3346
|
+
var percent = (n) => `${Math.round(n <= 1 ? n * 100 : n)}%`;
|
|
3347
|
+
function XeniaDebugPanel({ prefix = DEFAULT_PREFIX, position = "bottom-right", renderExtra }) {
|
|
3348
|
+
const [entry, setEntry] = react.useState(null);
|
|
3349
|
+
const [open, setOpen] = react.useState(false);
|
|
3350
|
+
const [tab, setTab] = react.useState("Decision");
|
|
3351
|
+
const root = react.useRef(null);
|
|
3352
|
+
const launcher = react.useRef(null);
|
|
3353
|
+
const card = react.useRef(null);
|
|
3354
|
+
const tabRefs = react.useRef({});
|
|
3355
|
+
const id = react.useId();
|
|
3356
|
+
react.useEffect(() => {
|
|
3357
|
+
let timer;
|
|
3358
|
+
const read = () => setEntry(readEntry(prefix));
|
|
3359
|
+
const onNavigate = () => {
|
|
3360
|
+
clearTimeout(timer);
|
|
3361
|
+
setTimeout(read, 0);
|
|
3362
|
+
timer = setTimeout(read, 300);
|
|
3363
|
+
};
|
|
3364
|
+
read();
|
|
3365
|
+
try {
|
|
3366
|
+
setOpen(sessionStorage.getItem(OPEN_KEY) === "1");
|
|
3367
|
+
const saved = sessionStorage.getItem(TAB_KEY);
|
|
3368
|
+
if (saved === "Decision" || saved === "Visitor" || saved === "Request" || saved === "Site") setTab(saved);
|
|
3369
|
+
} catch {
|
|
3370
|
+
}
|
|
3371
|
+
const unwatch = watchNavigation(onNavigate);
|
|
3372
|
+
return () => {
|
|
3373
|
+
clearTimeout(timer);
|
|
3374
|
+
unwatch();
|
|
3375
|
+
};
|
|
3376
|
+
}, [prefix]);
|
|
3377
|
+
const toggle = react.useCallback((next, focusLauncher = false) => {
|
|
3378
|
+
setOpen(next);
|
|
3379
|
+
if (focusLauncher) launcher.current?.focus();
|
|
3380
|
+
remember(OPEN_KEY, next ? "1" : "0");
|
|
3381
|
+
}, []);
|
|
3382
|
+
react.useEffect(() => {
|
|
3383
|
+
if (!open) return;
|
|
3384
|
+
card.current?.focus();
|
|
3385
|
+
const onKey = (e) => {
|
|
3386
|
+
if (e.key === "Escape" && root.current?.contains(document.activeElement)) toggle(false, true);
|
|
3387
|
+
};
|
|
3388
|
+
const onDown = (e) => {
|
|
3389
|
+
if (root.current && !root.current.contains(e.target)) toggle(false);
|
|
3390
|
+
};
|
|
3391
|
+
document.addEventListener("keydown", onKey);
|
|
3392
|
+
document.addEventListener("mousedown", onDown);
|
|
3393
|
+
return () => {
|
|
3394
|
+
document.removeEventListener("keydown", onKey);
|
|
3395
|
+
document.removeEventListener("mousedown", onDown);
|
|
3396
|
+
};
|
|
3397
|
+
}, [open, toggle]);
|
|
3398
|
+
if (!entry) return null;
|
|
3399
|
+
const status = debugStatus(entry);
|
|
3400
|
+
const v = view(entry);
|
|
3401
|
+
const tabs = ["Decision", "Visitor", "Request", ...v.custom.length || renderExtra ? ["Site"] : []];
|
|
3402
|
+
const current = tabs.includes(tab) ? tab : "Decision";
|
|
3403
|
+
const [vertical, horizontal] = position.split("-");
|
|
3404
|
+
const choose = (t, focus = false) => {
|
|
3405
|
+
setTab(t);
|
|
3406
|
+
remember(TAB_KEY, t);
|
|
3407
|
+
if (focus) tabRefs.current[t]?.focus();
|
|
3408
|
+
};
|
|
3409
|
+
const onTabKey = (e) => {
|
|
3410
|
+
const i = tabs.indexOf(current);
|
|
3411
|
+
const next = e.key === "ArrowRight" ? tabs[(i + 1) % tabs.length] : e.key === "ArrowLeft" ? tabs[(i - 1 + tabs.length) % tabs.length] : null;
|
|
3412
|
+
if (next) {
|
|
3413
|
+
e.preventDefault();
|
|
3414
|
+
choose(next, true);
|
|
3415
|
+
}
|
|
3416
|
+
};
|
|
3417
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: root, className: "xdp", "data-vertical": vertical, "data-horizontal": horizontal, children: [
|
|
3418
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: STYLES2 }),
|
|
3419
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3420
|
+
"button",
|
|
3421
|
+
{
|
|
3422
|
+
ref: launcher,
|
|
3423
|
+
type: "button",
|
|
3424
|
+
className: "xdp-launcher",
|
|
3425
|
+
"data-status": status,
|
|
3426
|
+
"aria-label": `Xenia personalization: ${STATUS_LABEL[status]}`,
|
|
3427
|
+
"aria-expanded": open,
|
|
3428
|
+
"aria-controls": open ? `${id}-card` : void 0,
|
|
3429
|
+
onClick: () => toggle(!open),
|
|
3430
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Mark, { size: 24 })
|
|
3431
|
+
}
|
|
3432
|
+
),
|
|
3433
|
+
open ? /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: card, id: `${id}-card`, role: "dialog", "aria-label": "Xenia personalization", tabIndex: -1, className: "xdp-card", children: [
|
|
3434
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "xdp-head", children: [
|
|
3435
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-head-row", children: [
|
|
3436
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-pill", "data-status": status, children: status === "personalized" ? "Personalized" : "Base content" }),
|
|
3437
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-chip", title: v.source, children: sourceChip(v.source) }),
|
|
3438
|
+
typeof v.ms === "number" && v.source === "xenia" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "xdp-ms", children: [
|
|
3439
|
+
v.ms,
|
|
3440
|
+
" ms"
|
|
3441
|
+
] }) : null
|
|
3442
|
+
] }),
|
|
3443
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { className: "xdp-path", title: v.page, children: v.page }),
|
|
3444
|
+
status === "attention" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "xdp-alert", children: v.source === "timeout" ? "Xenia didn't answer within the time budget." : "The decision failed; the proxy's onError has the cause." }) : null
|
|
3445
|
+
] }),
|
|
3446
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "tablist", "aria-label": "Details", className: "xdp-tabs", children: tabs.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3447
|
+
"button",
|
|
3448
|
+
{
|
|
3449
|
+
ref: (el) => {
|
|
3450
|
+
tabRefs.current[t] = el;
|
|
3451
|
+
},
|
|
3452
|
+
type: "button",
|
|
3453
|
+
role: "tab",
|
|
3454
|
+
id: `${id}-tab-${t}`,
|
|
3455
|
+
"aria-selected": current === t,
|
|
3456
|
+
"aria-controls": `${id}-panel`,
|
|
3457
|
+
tabIndex: current === t ? 0 : -1,
|
|
3458
|
+
className: "xdp-tab",
|
|
3459
|
+
onClick: () => choose(t),
|
|
3460
|
+
onKeyDown: onTabKey,
|
|
3461
|
+
children: t
|
|
3462
|
+
},
|
|
3463
|
+
t
|
|
3464
|
+
)) }),
|
|
3465
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { role: "tabpanel", id: `${id}-panel`, "aria-labelledby": `${id}-tab-${current}`, className: "xdp-panel", children: [
|
|
3466
|
+
current === "Decision" ? /* @__PURE__ */ jsxRuntime.jsx(DecisionTab, { v }) : null,
|
|
3467
|
+
current === "Visitor" ? /* @__PURE__ */ jsxRuntime.jsx(VisitorTab, { v }) : null,
|
|
3468
|
+
current === "Request" ? /* @__PURE__ */ jsxRuntime.jsx(RequestTab, { v }) : null,
|
|
3469
|
+
current === "Site" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3470
|
+
v.custom.map(
|
|
3471
|
+
(c) => c.lines.length === 1 ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: c.label, children: c.lines[0] }, c.label) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block", children: [
|
|
3472
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "xdp-block-title", children: c.label }),
|
|
3473
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "xdp-list", children: c.lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: line }, i)) })
|
|
3474
|
+
] }, c.label)
|
|
3475
|
+
),
|
|
3476
|
+
renderExtra ? renderExtra(entry) : null
|
|
3477
|
+
] }) : null
|
|
3478
|
+
] })
|
|
3479
|
+
] }) : null
|
|
3480
|
+
] });
|
|
3481
|
+
}
|
|
3482
|
+
function remember(key, value) {
|
|
3483
|
+
try {
|
|
3484
|
+
sessionStorage.setItem(key, value);
|
|
3485
|
+
} catch {
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
function Row({ label, children: children2 }) {
|
|
3489
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-row", children: [
|
|
3490
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-label", children: label }),
|
|
3491
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-value", children: children2 })
|
|
3492
|
+
] });
|
|
3493
|
+
}
|
|
3494
|
+
function DecisionTab({ v }) {
|
|
3495
|
+
if (!v.decisions.length) return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "xdp-empty", children: "No journey targets this page." });
|
|
3496
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: v.decisions.map((d) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block", children: [
|
|
3497
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block-head", children: [
|
|
3498
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { className: "xdp-key", children: d.key }),
|
|
3499
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-result", "data-result": changed(d.result) ? "changed" : "base", children: d.result })
|
|
3500
|
+
] }),
|
|
3501
|
+
d.reason ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "xdp-reason", children: d.reason }) : null,
|
|
3502
|
+
d.trace.length ? /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "xdp-steps", "aria-label": "Journey path", children: d.trace.map(([step, outcome]) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
|
|
3503
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: step }),
|
|
3504
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: /* @__PURE__ */ jsxRuntime.jsx(Id, { value: outcome }) })
|
|
3505
|
+
] }, step)) }) : null
|
|
3506
|
+
] }, d.key)) });
|
|
3507
|
+
}
|
|
3508
|
+
var SOURCE_NAME = { jev: "Jev", signals: "Signals" };
|
|
3509
|
+
function VisitorTab({ v }) {
|
|
3510
|
+
const p = v.profile;
|
|
3511
|
+
if (!p) return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "xdp-empty", children: "No visitor profile came with this decision." });
|
|
3512
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3513
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-chips", children: [
|
|
3514
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "xdp-chip", children: [
|
|
3515
|
+
p.pageViews,
|
|
3516
|
+
" page view",
|
|
3517
|
+
p.pageViews === 1 ? "" : "s"
|
|
3518
|
+
] }),
|
|
3519
|
+
p.lastSection ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "xdp-chip", children: [
|
|
3520
|
+
"Last: ",
|
|
3521
|
+
p.lastSection
|
|
3522
|
+
] }) : null,
|
|
3523
|
+
p.campaign ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "xdp-chip", children: [
|
|
3524
|
+
"Campaign: ",
|
|
3525
|
+
p.campaign
|
|
3526
|
+
] }) : null
|
|
3527
|
+
] }),
|
|
3528
|
+
p.answers.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "xdp-empty", children: "No Visitor Intent answers." }) : null,
|
|
3529
|
+
p.answers.map((a) => {
|
|
3530
|
+
const max = a.scores[0]?.[1] || 1;
|
|
3531
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block", children: [
|
|
3532
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block-head", children: [
|
|
3533
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { className: "xdp-key", children: a.name }),
|
|
3534
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-answer", children: a.choice || "no answer" })
|
|
3535
|
+
] }),
|
|
3536
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-meta", children: [
|
|
3537
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-source", "data-source": a.source || "none", children: SOURCE_NAME[a.source] ?? "No source" }),
|
|
3538
|
+
typeof a.confidence === "number" && a.confidence > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "xdp-confidence", children: [
|
|
3539
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-meter", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: percent(Math.min(1, a.confidence)) } }) }),
|
|
3540
|
+
percent(a.confidence),
|
|
3541
|
+
" confident"
|
|
3542
|
+
] }) : null,
|
|
3543
|
+
a.model ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-model", children: a.model }) : null
|
|
3544
|
+
] }),
|
|
3545
|
+
a.scores.length ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "xdp-scores", children: a.scores.map(([option, score]) => /* @__PURE__ */ jsxRuntime.jsxs("li", { "data-score": option, children: [
|
|
3546
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-score-label", title: option, children: option }),
|
|
3547
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-bar", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { width: `${Math.max(2, score / max * 100)}%` } }) }),
|
|
3548
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-score-value", children: Math.round(score * 1e3) / 1e3 })
|
|
3549
|
+
] }, option)) }) : null
|
|
3550
|
+
] }, a.name);
|
|
3551
|
+
})
|
|
3552
|
+
] });
|
|
3553
|
+
}
|
|
3554
|
+
var place = (g) => g ? [g.city, g.regionName || g.region].filter(Boolean).join(", ") : "";
|
|
3555
|
+
function RequestTab({ v }) {
|
|
3556
|
+
const r = v.request;
|
|
3557
|
+
const loc = r.location;
|
|
3558
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3559
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block", children: [
|
|
3560
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block-head", children: [
|
|
3561
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-place", children: place(loc) || (loc?.country ? "Country only" : "Location unknown") }),
|
|
3562
|
+
loc?.country ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "xdp-chip", children: loc.country }) : null
|
|
3563
|
+
] }),
|
|
3564
|
+
loc?.latitude && loc.longitude ? /* @__PURE__ */ jsxRuntime.jsxs(Row, { label: "Coordinates", children: [
|
|
3565
|
+
loc.latitude,
|
|
3566
|
+
", ",
|
|
3567
|
+
loc.longitude
|
|
3568
|
+
] }) : null,
|
|
3569
|
+
loc?.timeZone ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Time zone", children: loc.timeZone }) : null,
|
|
3570
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Resolved via", children: loc?.source === "qa" ? "QA override" : !r.via || r.via === "none" ? "Not resolved" : r.via }),
|
|
3571
|
+
r.detected ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Edge detected", children: [place(r.detected), r.detected.country].filter(Boolean).join(", ") || "nothing" }) : null
|
|
3572
|
+
] }),
|
|
3573
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "xdp-block", children: [
|
|
3574
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "xdp-block-title", children: "Location sources seen" }),
|
|
3575
|
+
r.seen.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "xdp-chips", children: r.seen.map((s) => /* @__PURE__ */ jsxRuntime.jsx("code", { className: "xdp-chip", children: s }, s)) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "xdp-empty", children: "No location header or runtime context reached the proxy." })
|
|
3576
|
+
] }),
|
|
3577
|
+
r.device ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Device", children: r.device }) : null,
|
|
3578
|
+
r.consent ? /* @__PURE__ */ jsxRuntime.jsxs(Row, { label: "Consent", children: [
|
|
3579
|
+
"Personalization ",
|
|
3580
|
+
r.consent.personalization ? "on" : "off",
|
|
3581
|
+
", Analytics ",
|
|
3582
|
+
r.consent.analytics ? "on" : "off"
|
|
3583
|
+
] }) : null
|
|
3584
|
+
] });
|
|
3585
|
+
}
|
|
3586
|
+
var STYLES2 = `
|
|
3587
|
+
.xdp{--xdp-paper:#FFFFFF;--xdp-ink:#0A0F14;--xdp-muted:#424548;--xdp-faint:#5F6469;--xdp-border:#DEE2E5;--xdp-soft:#F2F4F5;--xdp-blue:#005C91;--xdp-blue-strong:#00334F;--xdp-blue-tint:#E6F1F7;--xdp-ok:#316302;--xdp-ok-tint:#EBF4E1;--xdp-warn:#9A5B00;--xdp-warn-tint:#FFF3DF;
|
|
3588
|
+
position:fixed;z-index:2147483000;display:flex;flex-direction:column;gap:8px;font:13px/1.45 ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;color:var(--xdp-ink);font-variant-numeric:tabular-nums;color-scheme:light;-webkit-font-smoothing:antialiased}
|
|
3589
|
+
.xdp *,.xdp *::before,.xdp *::after{box-sizing:border-box}
|
|
3590
|
+
.xdp[data-vertical=bottom]{bottom:16px;flex-direction:column-reverse}.xdp[data-vertical=top]{top:16px}
|
|
3591
|
+
.xdp[data-horizontal=right]{right:16px;align-items:flex-end}.xdp[data-horizontal=left]{left:16px;align-items:flex-start}
|
|
3592
|
+
.xdp button{font:inherit;color:inherit}
|
|
3593
|
+
.xdp code{font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace}
|
|
3594
|
+
.xdp-launcher{width:40px;height:40px;border-radius:50%;border:0;padding:0;background:var(--xdp-blue);display:grid;place-items:center;cursor:pointer;box-shadow:0 0 0 1px rgba(255,255,255,.25) inset,0 6px 18px rgba(0,51,79,.35)}
|
|
3595
|
+
.xdp-launcher:hover{background:var(--xdp-blue-strong)}
|
|
3596
|
+
.xdp-launcher:focus-visible,.xdp-tab:focus-visible{outline:2px solid var(--xdp-blue);outline-offset:2px}
|
|
3597
|
+
.xdp-card{width:360px;max-width:calc(100vw - 32px);background:var(--xdp-paper);border:1px solid var(--xdp-border);border-radius:14px;box-shadow:0 16px 40px rgba(10,15,20,.16),0 2px 6px rgba(10,15,20,.06);overflow:hidden}
|
|
3598
|
+
.xdp-card:focus,.xdp-card:focus-visible{outline:none}
|
|
3599
|
+
.xdp-head{padding:14px 16px 10px}
|
|
3600
|
+
.xdp-head-row{display:flex;align-items:center;gap:8px}
|
|
3601
|
+
.xdp-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;font-weight:600;background:var(--xdp-soft);color:var(--xdp-muted)}
|
|
3602
|
+
.xdp-pill::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor}
|
|
3603
|
+
.xdp-pill[data-status=personalized]{background:var(--xdp-ok-tint);color:var(--xdp-ok)}
|
|
3604
|
+
.xdp-pill[data-status=attention]{background:var(--xdp-warn-tint);color:var(--xdp-warn)}
|
|
3605
|
+
.xdp-chip{display:inline-flex;align-items:center;padding:2px 8px;border-radius:6px;background:var(--xdp-soft);color:var(--xdp-muted);font-size:12px;white-space:nowrap}
|
|
3606
|
+
code.xdp-chip{font-size:11.5px}
|
|
3607
|
+
.xdp-ms{margin-left:auto;color:var(--xdp-faint)}
|
|
3608
|
+
.xdp-path{display:block;margin-top:8px;font-size:13px;color:var(--xdp-ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3609
|
+
.xdp-alert{margin:8px 0 0;padding:8px 10px;border-radius:8px;background:var(--xdp-warn-tint);color:var(--xdp-warn)}
|
|
3610
|
+
.xdp-tabs{display:flex;gap:4px;padding:0 12px;border-bottom:1px solid var(--xdp-border)}
|
|
3611
|
+
.xdp-tab{background:none;border:0;padding:8px 6px 9px;margin-bottom:-1px;border-bottom:2px solid transparent;color:var(--xdp-muted);cursor:pointer;border-radius:4px 4px 0 0}
|
|
3612
|
+
.xdp-tab:hover{color:var(--xdp-ink)}
|
|
3613
|
+
.xdp-tab[aria-selected=true]{color:var(--xdp-blue);border-bottom-color:var(--xdp-blue);font-weight:600}
|
|
3614
|
+
.xdp-panel{padding:12px 16px 16px;display:flex;flex-direction:column;gap:10px}
|
|
3615
|
+
.xdp-block{display:flex;flex-direction:column;gap:6px;padding:10px 12px;border:1px solid var(--xdp-border);border-radius:10px}
|
|
3616
|
+
.xdp-block-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
|
|
3617
|
+
.xdp-block-title{font-weight:600}
|
|
3618
|
+
.xdp-key{color:var(--xdp-ink);overflow-wrap:anywhere}
|
|
3619
|
+
.xdp-result{padding:2px 8px;border-radius:6px;background:var(--xdp-soft);color:var(--xdp-muted);font-weight:600;text-align:right}
|
|
3620
|
+
.xdp-result[data-result=changed]{background:var(--xdp-ok-tint);color:var(--xdp-ok)}
|
|
3621
|
+
.xdp-reason{margin:0;color:var(--xdp-warn)}
|
|
3622
|
+
.xdp-steps{list-style:none;margin:2px 0 0;padding:0 0 0 14px;position:relative;display:flex;flex-direction:column;gap:6px}
|
|
3623
|
+
.xdp-steps::before{content:"";position:absolute;left:4px;top:6px;bottom:6px;width:1px;background:var(--xdp-border)}
|
|
3624
|
+
.xdp-steps li{position:relative;display:flex;justify-content:space-between;gap:12px}
|
|
3625
|
+
.xdp-steps li::before{content:"";position:absolute;left:-13px;top:6px;width:7px;height:7px;border-radius:50%;background:var(--xdp-paper);border:1.5px solid var(--xdp-blue)}
|
|
3626
|
+
.xdp-steps code{color:var(--xdp-muted)}
|
|
3627
|
+
.xdp-steps span{text-align:right;overflow-wrap:anywhere}
|
|
3628
|
+
.xdp-row{display:flex;justify-content:space-between;align-items:baseline;gap:16px}
|
|
3629
|
+
.xdp-label{color:var(--xdp-muted);flex:none}
|
|
3630
|
+
.xdp-value{text-align:right;min-width:0;overflow-wrap:anywhere}
|
|
3631
|
+
.xdp-chips{display:flex;flex-wrap:wrap;gap:6px}
|
|
3632
|
+
.xdp-answer{font-weight:600}
|
|
3633
|
+
.xdp-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;color:var(--xdp-muted);font-size:12px}
|
|
3634
|
+
.xdp-source{padding:1px 7px;border-radius:6px;font-weight:600;background:var(--xdp-soft)}
|
|
3635
|
+
.xdp-source[data-source=signals]{background:var(--xdp-ok-tint);color:var(--xdp-ok)}
|
|
3636
|
+
.xdp-source[data-source=jev]{background:var(--xdp-blue-tint);color:var(--xdp-blue)}
|
|
3637
|
+
.xdp-confidence{display:inline-flex;align-items:center;gap:6px}
|
|
3638
|
+
.xdp-meter{display:inline-block;width:44px;height:5px;border-radius:3px;background:var(--xdp-soft);overflow:hidden}
|
|
3639
|
+
.xdp-meter span,.xdp-bar span{display:block;height:100%;background:var(--xdp-blue);border-radius:3px}
|
|
3640
|
+
.xdp-scores{list-style:none;margin:2px 0 0;padding:0;display:flex;flex-direction:column;gap:4px}
|
|
3641
|
+
.xdp-scores li{display:grid;grid-template-columns:minmax(0,92px) 1fr auto;align-items:center;gap:8px;font-size:12px}
|
|
3642
|
+
.xdp-score-label{color:var(--xdp-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3643
|
+
.xdp-bar{height:6px;border-radius:3px;background:var(--xdp-soft);overflow:hidden}
|
|
3644
|
+
.xdp-scores li:first-child .xdp-bar span{background:var(--xdp-ok)}
|
|
3645
|
+
.xdp-score-value{color:var(--xdp-muted);min-width:38px;text-align:right}
|
|
3646
|
+
.xdp-place{font-weight:600;font-size:14px}
|
|
3647
|
+
.xdp-list{margin:0;padding-left:18px;list-style:disc;display:flex;flex-direction:column;gap:4px}.xdp-list li::marker{color:var(--xdp-blue)}
|
|
3648
|
+
.xdp-empty{margin:0;color:var(--xdp-muted)}
|
|
3649
|
+
.xdp-model{color:var(--xdp-faint)}
|
|
3650
|
+
@media (prefers-reduced-motion:no-preference){.xdp-card{animation:xdp-in .14s ease-out}@keyframes xdp-in{from{opacity:0;transform:translateY(4px)}}}
|
|
3651
|
+
`;
|
|
3652
|
+
|
|
2958
3653
|
exports.XeniaClient = XeniaClient;
|
|
2959
3654
|
exports.XeniaContentProvider = XeniaContentProvider;
|
|
2960
3655
|
exports.XeniaContext = XeniaContext;
|
|
3656
|
+
exports.XeniaDebugPanel = XeniaDebugPanel;
|
|
2961
3657
|
exports.XeniaMessage = XeniaMessage;
|
|
2962
3658
|
exports.XeniaProvider = XeniaProvider;
|
|
2963
3659
|
exports.XeniaTrack = XeniaTrack;
|
|
2964
3660
|
exports.createXeniaClient = createXeniaClient;
|
|
3661
|
+
exports.debugStatus = debugStatus;
|
|
2965
3662
|
exports.extractContentSwaps = extractContentSwaps;
|
|
2966
3663
|
exports.parseActions = parseActions;
|
|
2967
3664
|
exports.pendingTargets = pendingTargets;
|