@primestyleai/tryon 5.10.104 → 5.10.105
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.
|
@@ -21,6 +21,13 @@ export declare function setLastCompletedProduct(productId: string | null, produc
|
|
|
21
21
|
* the size-result view. The cart-hook then fires a SIZE_RECOMMENDATION_ACCEPTED
|
|
22
22
|
* event when this product gets added to cart — a clearer "user committed"
|
|
23
23
|
* signal than "user clicked a pill once".
|
|
24
|
+
*
|
|
25
|
+
* Side-effect: proactively stamp the current cart with `attributes[ps_session]`
|
|
26
|
+
* via `/cart/update.js`. This is the primary attribution path now — works
|
|
27
|
+
* regardless of how the buyer eventually checks out (Ajax cart, Buy It Now,
|
|
28
|
+
* Shop Pay), because the attribute is already on the cart object before
|
|
29
|
+
* the buyer adds anything. The legacy fetch/form-submit interception below
|
|
30
|
+
* stays as a defense-in-depth fallback for older themes.
|
|
24
31
|
*/
|
|
25
32
|
export declare function setLastSizeSelection(input: {
|
|
26
33
|
productId: string | null;
|
|
@@ -1,4 +1,46 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
const TAG$3 = "[primestyle-shadow]";
|
|
3
|
+
const collected = [];
|
|
4
|
+
if (typeof document !== "undefined" && document.head) {
|
|
5
|
+
let shouldCapture = function(node) {
|
|
6
|
+
return node instanceof HTMLStyleElement;
|
|
7
|
+
};
|
|
8
|
+
const origAppend = document.head.appendChild.bind(document.head);
|
|
9
|
+
const origInsertBefore = document.head.insertBefore.bind(document.head);
|
|
10
|
+
document.head.appendChild = function(node) {
|
|
11
|
+
if (shouldCapture(node)) {
|
|
12
|
+
collected.push(node);
|
|
13
|
+
console.log(`${TAG$3} captured style tag (${node.textContent?.length ?? 0} chars)`);
|
|
14
|
+
}
|
|
15
|
+
return origAppend(node);
|
|
16
|
+
};
|
|
17
|
+
document.head.insertBefore = function(node, ref) {
|
|
18
|
+
if (shouldCapture(node)) {
|
|
19
|
+
collected.push(node);
|
|
20
|
+
console.log(`${TAG$3} captured style tag via insertBefore (${node.textContent?.length ?? 0} chars)`);
|
|
21
|
+
}
|
|
22
|
+
return origInsertBefore(node, ref);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function getCollectedCss() {
|
|
26
|
+
const parts = [];
|
|
27
|
+
for (const el2 of collected) {
|
|
28
|
+
const text = el2.textContent;
|
|
29
|
+
if (text) parts.push(text);
|
|
30
|
+
}
|
|
31
|
+
return parts.join("\n\n");
|
|
32
|
+
}
|
|
33
|
+
function injectStylesIntoShadow(shadow) {
|
|
34
|
+
const css = getCollectedCss();
|
|
35
|
+
if (!css) {
|
|
36
|
+
console.warn(`${TAG$3} no SDK styles collected — widget may render unstyled`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const style = document.createElement("style");
|
|
40
|
+
style.setAttribute("data-primestyle", "sdk-styles");
|
|
41
|
+
style.textContent = css;
|
|
42
|
+
shadow.appendChild(style);
|
|
43
|
+
}
|
|
2
44
|
var react = { exports: {} };
|
|
3
45
|
var react_production_min = {};
|
|
4
46
|
/**
|
|
@@ -19418,14 +19460,6 @@ function ProductPhotoCarouselCard({
|
|
|
19418
19460
|
slide.push(photos[(start + slide.length) % photos.length]);
|
|
19419
19461
|
}
|
|
19420
19462
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-photo-strip", role: "group", "aria-label": t2("Product photos"), children: [
|
|
19421
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-photo-strip-head", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ps-tryon-photo-strip-badge", children: [
|
|
19422
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
19423
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
19424
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9", cy: "9", r: "2" }),
|
|
19425
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
|
|
19426
|
-
] }),
|
|
19427
|
-
t2("Gallery")
|
|
19428
|
-
] }) }),
|
|
19429
19463
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-photo-strip-row", children: slide.map((src, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-photo-strip-cell", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src, alt: productTitle || "", draggable: false }) }, `${groupIdx}-${i}`)) }, groupIdx),
|
|
19430
19464
|
totalGroups > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-photo-strip-dots", "aria-hidden": "true", children: Array.from({ length: totalGroups }).map((_, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `ps-tryon-photo-strip-dot${i === groupIdx ? " is-active" : ""}` }, i)) })
|
|
19431
19465
|
] });
|
|
@@ -20066,103 +20100,26 @@ function SectionDetailView({
|
|
|
20066
20100
|
return details.map((m2) => {
|
|
20067
20101
|
if (isFromLength.has(m2.measurement)) {
|
|
20068
20102
|
const userNum2 = userMeasurements[m2.measurement.toLowerCase()] || pNumFn(m2.userValue);
|
|
20069
|
-
|
|
20070
|
-
|
|
20071
|
-
|
|
20072
|
-
|
|
20073
|
-
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
const sizeCol = sec.headers.findIndex((h) => /size|length/i.test(h.trim()));
|
|
20077
|
-
const userIsInches = unitLbl === "in";
|
|
20078
|
-
let targetColIdx = -1;
|
|
20079
|
-
let colIsCm = false;
|
|
20080
|
-
if (isHeight) {
|
|
20081
|
-
const cmColIdx = sec.headers.findIndex((h) => /cm|\(cm\)|height.*cm/i.test(h.toLowerCase()));
|
|
20082
|
-
const genericColIdx = sec.headers.findIndex((h) => /height|altezza|estatura/i.test(h.toLowerCase()) && !/cm/i.test(h));
|
|
20083
|
-
targetColIdx = cmColIdx >= 0 ? cmColIdx : genericColIdx;
|
|
20084
|
-
colIsCm = targetColIdx === cmColIdx;
|
|
20085
|
-
} else {
|
|
20086
|
-
targetColIdx = sec.headers.findIndex((h) => {
|
|
20087
|
-
const hLc = h.toLowerCase().replace(/\s*\(.*?\)\s*/g, "").trim();
|
|
20088
|
-
if (!hLc) return false;
|
|
20089
|
-
return hLc === measLc || hLc.includes(measLc) || measLc.includes(hLc);
|
|
20090
|
-
});
|
|
20091
|
-
colIsCm = targetColIdx >= 0 ? /cm/i.test(sec.headers[targetColIdx] || "") : false;
|
|
20092
|
-
}
|
|
20093
|
-
const sIdx = sizeCol >= 0 ? sizeCol : 0;
|
|
20094
|
-
const hIdx = targetColIdx >= 0 ? targetColIdx : -1;
|
|
20095
|
-
const activeLengthForLookup = (() => {
|
|
20096
|
-
const al2 = (activeLength || "").toLowerCase().trim();
|
|
20097
|
-
if (/big.*tall|tall/.test(al2)) return "Long";
|
|
20098
|
-
if (/^big$/.test(al2)) return "Regular";
|
|
20099
|
-
return activeLength;
|
|
20100
|
-
})();
|
|
20101
|
-
const alLc = (activeLengthForLookup || "").toLowerCase().trim();
|
|
20102
|
-
const matchRow = sec.rows.find((r2) => cellValFn(r2, sIdx, sec.headers[sIdx]) === activeLength) || sec.rows.find((r2) => cellValFn(r2, sIdx, sec.headers[sIdx]).trim().toLowerCase() === alLc) || null;
|
|
20103
|
-
let chartLabel2 = activeLength;
|
|
20104
|
-
let fit2 = "good";
|
|
20105
|
-
if (matchRow && hIdx >= 0) {
|
|
20106
|
-
const rangeStr = cellValFn(matchRow, hIdx, sec.headers[hIdx]);
|
|
20107
|
-
if (rangeStr) {
|
|
20108
|
-
const { min: rMinRaw, max: rMaxRaw } = pRangeFn(rangeStr);
|
|
20109
|
-
if (rMinRaw > 0 && rMaxRaw > 0) {
|
|
20110
|
-
const userInColUnit = colIsCm && userIsInches ? +(userNum2 * 2.54).toFixed(1) : !colIsCm && !userIsInches ? +(userNum2 * 2.54).toFixed(1) : userNum2;
|
|
20111
|
-
const range2 = rMaxRaw - rMinRaw;
|
|
20112
|
-
const threshold2 = range2 > 0 ? range2 * 0.5 : rMinRaw * 0.05 || 3;
|
|
20113
|
-
const tol = colIsCm ? 2.54 : 1;
|
|
20114
|
-
if (userInColUnit > rMinRaw - tol && userInColUnit < rMaxRaw + tol) fit2 = "good";
|
|
20115
|
-
else if (userInColUnit < rMinRaw) {
|
|
20116
|
-
const diff = rMinRaw - userInColUnit;
|
|
20117
|
-
fit2 = diff > threshold2 * 2 ? "too-long" : diff > threshold2 ? "long" : "a-bit-long";
|
|
20118
|
-
} else {
|
|
20119
|
-
const diff = userInColUnit - rMaxRaw;
|
|
20120
|
-
fit2 = diff > threshold2 * 2 ? "too-short" : diff > threshold2 ? "short" : "a-bit-short";
|
|
20121
|
-
}
|
|
20122
|
-
const needsConvert = colIsCm && userIsInches || !colIsCm && !userIsInches;
|
|
20123
|
-
const rMinUser = needsConvert ? colIsCm ? +(rMinRaw / 2.54).toFixed(1) : +(rMinRaw * 2.54).toFixed(1) : rMinRaw;
|
|
20124
|
-
const rMaxUser = needsConvert ? colIsCm ? +(rMaxRaw / 2.54).toFixed(1) : +(rMaxRaw * 2.54).toFixed(1) : rMaxRaw;
|
|
20125
|
-
chartLabel2 = rMinUser === rMaxUser ? `${rMinUser}` : `${rMinUser}-${rMaxUser}`;
|
|
20126
|
-
} else {
|
|
20127
|
-
chartLabel2 = rangeStr;
|
|
20128
|
-
}
|
|
20129
|
-
}
|
|
20130
|
-
}
|
|
20131
|
-
return { area: m2.measurement + " (" + activeLength + ")", userNum: userNum2, chartLabel: cleanNumFn(chartLabel2), fit: fit2, isLength: true };
|
|
20103
|
+
return {
|
|
20104
|
+
area: m2.measurement,
|
|
20105
|
+
userNum: userNum2,
|
|
20106
|
+
chartLabel: cleanNumFn(m2.chartRange),
|
|
20107
|
+
fit: m2.fit || "good",
|
|
20108
|
+
isLength: true
|
|
20109
|
+
};
|
|
20132
20110
|
}
|
|
20133
20111
|
const userNum = userMeasurements[m2.measurement.toLowerCase()] || pNumFn(m2.userValue);
|
|
20134
|
-
let { min: rMin, max: rMax } = pRangeFn(m2.chartRange);
|
|
20135
|
-
let chartLabel = m2.chartRange;
|
|
20136
|
-
const alt = chartRangeFor(m2.measurement, displaySize);
|
|
20137
|
-
if (alt) {
|
|
20138
|
-
chartLabel = alt.range;
|
|
20139
|
-
rMin = alt.min;
|
|
20140
|
-
rMax = alt.max;
|
|
20141
|
-
}
|
|
20142
|
-
const range = rMax - rMin;
|
|
20143
|
-
const threshold = range > 0 ? range * 0.5 : rMin * 0.05 || 3;
|
|
20144
20112
|
const measLower = m2.measurement.toLowerCase();
|
|
20145
20113
|
const isDirectional = /length|inseam|sleeve|hem|rise/.test(measLower);
|
|
20146
|
-
|
|
20147
|
-
|
|
20148
|
-
|
|
20149
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
}
|
|
20153
|
-
const diff = userNum > rMax ? userNum - rMax : rMin - userNum;
|
|
20154
|
-
const bucket = diff > threshold * 2 ? "too-" : diff > threshold ? "" : "a-bit-";
|
|
20155
|
-
fit = bucket + (userNum > rMax ? "short" : "long");
|
|
20156
|
-
} else if (userNum < rMin) {
|
|
20157
|
-
const diff = rMin - userNum;
|
|
20158
|
-
fit = diff > threshold * 2 ? "too-loose" : diff > threshold ? "loose" : "a-bit-loose";
|
|
20159
|
-
} else {
|
|
20160
|
-
const diff = userNum - rMax;
|
|
20161
|
-
fit = diff > threshold * 2 ? "too-tight" : diff > threshold ? "tight" : "a-bit-tight";
|
|
20162
|
-
}
|
|
20163
|
-
return { area: m2.measurement, userNum, chartLabel: cleanNumFn(chartLabel), fit, isLength: isDirectional };
|
|
20114
|
+
return {
|
|
20115
|
+
area: m2.measurement,
|
|
20116
|
+
userNum,
|
|
20117
|
+
chartLabel: cleanNumFn(m2.chartRange),
|
|
20118
|
+
fit: m2.fit || "good",
|
|
20119
|
+
isLength: isDirectional
|
|
20120
|
+
};
|
|
20164
20121
|
});
|
|
20165
|
-
}, [sectionResult, lengthEntry, userMeasurements,
|
|
20122
|
+
}, [sectionResult, lengthEntry, userMeasurements, renderRaw]);
|
|
20166
20123
|
const goodCount = fitRows.filter(
|
|
20167
20124
|
(r2) => r2.fit === "good" || r2.fit === "a-bit-tight" || r2.fit === "a-bit-loose"
|
|
20168
20125
|
).length;
|
|
@@ -30376,10 +30333,29 @@ async function mount(el2) {
|
|
|
30376
30333
|
props.sizeGuideData = fetched;
|
|
30377
30334
|
}
|
|
30378
30335
|
}
|
|
30336
|
+
let shadow;
|
|
30337
|
+
try {
|
|
30338
|
+
shadow = el2.shadowRoot ?? el2.attachShadow({ mode: "open" });
|
|
30339
|
+
injectStylesIntoShadow(shadow);
|
|
30340
|
+
} catch (err) {
|
|
30341
|
+
console.warn(`${TAG} shadow attach failed — falling back to direct mount`, err);
|
|
30342
|
+
shadow = el2;
|
|
30343
|
+
}
|
|
30344
|
+
let mountTarget;
|
|
30345
|
+
if (shadow instanceof ShadowRoot) {
|
|
30346
|
+
mountTarget = shadow.querySelector("[data-primestyle-mount]") ?? (() => {
|
|
30347
|
+
const c = document.createElement("div");
|
|
30348
|
+
c.setAttribute("data-primestyle-mount", "");
|
|
30349
|
+
shadow.appendChild(c);
|
|
30350
|
+
return c;
|
|
30351
|
+
})();
|
|
30352
|
+
} else {
|
|
30353
|
+
mountTarget = shadow;
|
|
30354
|
+
}
|
|
30379
30355
|
if (props.sizeGuideData) {
|
|
30380
30356
|
try {
|
|
30381
30357
|
const buttonStyles = props.buttonStyles;
|
|
30382
|
-
createSizeGuideButton(
|
|
30358
|
+
createSizeGuideButton(mountTarget, props.sizeGuideData, {
|
|
30383
30359
|
accentColor: buttonStyles?.backgroundColor
|
|
30384
30360
|
});
|
|
30385
30361
|
console.log(`${TAG} ✓ size guide button mounted`);
|
|
@@ -30388,10 +30364,10 @@ async function mount(el2) {
|
|
|
30388
30364
|
}
|
|
30389
30365
|
}
|
|
30390
30366
|
try {
|
|
30391
|
-
const root = createRoot(
|
|
30367
|
+
const root = createRoot(mountTarget);
|
|
30392
30368
|
root.render(reactExports.createElement(PrimeStyleTryon, props));
|
|
30393
30369
|
MOUNTED.set(el2, root);
|
|
30394
|
-
console.log(`${TAG} ✓ mounted React component`);
|
|
30370
|
+
console.log(`${TAG} ✓ mounted React component (shadow-isolated)`);
|
|
30395
30371
|
maybeFireProductView(el2);
|
|
30396
30372
|
} catch (err) {
|
|
30397
30373
|
console.error(`${TAG} ✗ React mount failed`, err);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow-DOM style collector.
|
|
3
|
+
*
|
|
4
|
+
* Vite injects all CSS imported by the storefront bundle as <style>
|
|
5
|
+
* tags appended to document.head at module-eval time. Theme app
|
|
6
|
+
* extensions render into the merchant's theme DOM, so those styles
|
|
7
|
+
* coexist with the merchant's theme CSS — and the theme's CSS leaks
|
|
8
|
+
* into our widget (table borders, button resets, font-family, etc).
|
|
9
|
+
*
|
|
10
|
+
* To isolate the widget inside a shadow root we need our SDK styles
|
|
11
|
+
* inside that shadow root, NOT in document.head. The cleanest way to
|
|
12
|
+
* intercept vite's runtime injection is to monkey-patch
|
|
13
|
+
* `document.head.appendChild` before our other imports evaluate, so
|
|
14
|
+
* every <style> tag the SDK emits during module load gets captured
|
|
15
|
+
* here and re-injected into each shadow root we mount later.
|
|
16
|
+
*
|
|
17
|
+
* IMPORTANT: this file MUST be imported as the *first* import of
|
|
18
|
+
* `src/storefront/index.ts`, before React or any CSS-importing module.
|
|
19
|
+
* ES module evaluation order is import-tree depth-first, so a
|
|
20
|
+
* side-effect import at the top of the entry runs before the rest.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Returns CSS text concatenated from every <style> tag captured at
|
|
24
|
+
* module load + any <style> tags that were already in document.head
|
|
25
|
+
* matching SDK selector patterns (defense-in-depth for cases where
|
|
26
|
+
* patching missed an injection).
|
|
27
|
+
*/
|
|
28
|
+
export declare function getCollectedCss(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Inject the SDK's collected styles into a shadow root so the widget
|
|
31
|
+
* looks identical regardless of theme. Idempotent — call once per
|
|
32
|
+
* shadow root after attachShadow().
|
|
33
|
+
*/
|
|
34
|
+
export declare function injectStylesIntoShadow(shadow: ShadowRoot): void;
|