@syntrologie/adapt-product 2.55.1 → 2.57.0
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/dist/bind/controller.d.ts +22 -1
- package/dist/bind/controller.d.ts.map +1 -1
- package/dist/cdn.js +2 -2
- package/dist/chunk-E2PP64GL.js +5215 -0
- package/dist/chunk-E2PP64GL.js.map +7 -0
- package/dist/{chunk-QXR5X22R.js → chunk-YEW72FVW.js} +1038 -1214
- package/dist/chunk-YEW72FVW.js.map +7 -0
- package/dist/decision/card.d.ts +4 -0
- package/dist/decision/card.d.ts.map +1 -1
- package/dist/platformAdapter.d.ts +31 -0
- package/dist/platformAdapter.d.ts.map +1 -1
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +4 -2
- package/dist/sanitizer.d.ts +4 -0
- package/dist/sanitizer.d.ts.map +1 -1
- package/dist/schema.d.ts +32 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +1 -1
- package/dist/storefront-urls.d.ts +9 -0
- package/dist/storefront-urls.d.ts.map +1 -0
- package/dist/widgets/OtherProductTileLit.d.ts +20 -2
- package/dist/widgets/OtherProductTileLit.d.ts.map +1 -1
- package/dist/widgets/PdpChartBarStackedLit.d.ts.map +1 -1
- package/dist/widgets/PdpChartLit.d.ts.map +1 -1
- package/dist/widgets/PdpChartMetricPullLit.d.ts.map +1 -1
- package/dist/widgets/PdpOtherProductsCarouselLit.d.ts.map +1 -1
- package/dist/widgets/PdpOtherProductsGridLit.d.ts.map +1 -1
- package/dist/widgets/PdpPeerFeedCarouselLit.d.ts.map +1 -1
- package/dist/widgets/PdpPeerFeedLit.d.ts.map +1 -1
- package/dist/widgets/PdpPeerFeedMasonryLit.d.ts.map +1 -1
- package/dist/widgets/PdpQaAccordionLit.d.ts.map +1 -1
- package/dist/widgets/PdpQaLit.d.ts.map +1 -1
- package/dist/widgets/PdpQaSideBySideLit.d.ts.map +1 -1
- package/dist/widgets/PdpRegionalFullBleedLit.d.ts.map +1 -1
- package/dist/widgets/PdpRegionalInlineNoteLit.d.ts.map +1 -1
- package/dist/widgets/PdpRegionalLit.d.ts.map +1 -1
- package/dist/widgets/PdpRibbonLit.d.ts.map +1 -1
- package/dist/widgets/PdpSectionHeaderLit.d.ts.map +1 -1
- package/dist/widgets/PdpSlotLit.d.ts.map +1 -1
- package/dist/widgets/PdpTrendingNewsHeroLit.d.ts.map +1 -1
- package/dist/widgets/PdpTrendingNewsLit.d.ts.map +1 -1
- package/dist/widgets/PdpTrendingNewsMagazineLit.d.ts.map +1 -1
- package/dist/widgets/ProductCardLit.d.ts +3 -8
- package/dist/widgets/ProductCardLit.d.ts.map +1 -1
- package/dist/widgets/ProductComparisonLit.d.ts +1 -5
- package/dist/widgets/ProductComparisonLit.d.ts.map +1 -1
- package/dist/widgets/ProductDetailsLit.d.ts +31 -0
- package/dist/widgets/ProductDetailsLit.d.ts.map +1 -0
- package/dist/widgets/ProductGridLit.d.ts +2 -4
- package/dist/widgets/ProductGridLit.d.ts.map +1 -1
- package/dist/widgets/ProductHeroLit.d.ts +2 -4
- package/dist/widgets/ProductHeroLit.d.ts.map +1 -1
- package/dist/widgets/ProductRecoCarouselLit.d.ts +7 -0
- package/dist/widgets/ProductRecoCarouselLit.d.ts.map +1 -1
- package/dist/widgets/SyntroPdpLit.d.ts +12 -3
- package/dist/widgets/SyntroPdpLit.d.ts.map +1 -1
- package/dist/widgets/VariantPanelLit.d.ts +9 -0
- package/dist/widgets/VariantPanelLit.d.ts.map +1 -1
- package/dist/widgets/imageStandin.d.ts +2 -1
- package/dist/widgets/imageStandin.d.ts.map +1 -1
- package/dist/widgets/presentationContext.d.ts +11 -0
- package/dist/widgets/presentationContext.d.ts.map +1 -0
- package/package.json +4 -7
- package/dist/chunk-D2W6UYLT.js +0 -370
- package/dist/chunk-D2W6UYLT.js.map +0 -7
- package/dist/chunk-QXR5X22R.js.map +0 -7
|
@@ -1,546 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
+
IMAGE_STANDIN_BACKGROUND,
|
|
2
3
|
VISIBLE_FACT_KEYS,
|
|
4
|
+
ViewportClassController,
|
|
3
5
|
__privateAdd,
|
|
4
6
|
__privateGet,
|
|
5
7
|
__privateMethod,
|
|
6
8
|
__privateSet,
|
|
7
9
|
cardSchema,
|
|
8
10
|
comparisonSchema,
|
|
11
|
+
dispatchDiveDeeper,
|
|
12
|
+
external_exports,
|
|
13
|
+
fetchLivePrice,
|
|
9
14
|
getProductPlatformAdapter,
|
|
10
15
|
gridSchema,
|
|
11
16
|
heroSchema,
|
|
12
17
|
recoSchema,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return n2;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// ../../sdk-contracts/dist/canvas-context.js
|
|
22
|
-
var canvasRuntimeContext = n("syntrologie:canvas-runtime");
|
|
23
|
-
|
|
24
|
-
// ../../sdk-contracts/dist/detector-events.js
|
|
25
|
-
var DETECTOR_EVENT_NAMES = [
|
|
26
|
-
"ui.hover",
|
|
27
|
-
"ui.idle",
|
|
28
|
-
"ui.hesitate",
|
|
29
|
-
"ui.rage_click",
|
|
30
|
-
"ui.scroll_thrash",
|
|
31
|
-
"ui.focus_bounce"
|
|
32
|
-
];
|
|
33
|
-
var CANONICAL_BUS_EVENT_NAMES = [
|
|
34
|
-
...DETECTOR_EVENT_NAMES,
|
|
35
|
-
"nav.section_viewed",
|
|
36
|
-
"nav.scroll_depth"
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
// ../../sdk-contracts/dist/dive-deeper.js
|
|
40
|
-
var DIVE_DEEPER_EVENT = "syntro:chat:dive-deeper";
|
|
41
|
-
function dispatchDiveDeeper(detail) {
|
|
42
|
-
if (typeof window === "undefined")
|
|
43
|
-
return;
|
|
44
|
-
window.dispatchEvent(new CustomEvent(DIVE_DEEPER_EVENT, { detail }));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ../../sdk-contracts/dist/mount-plumbing.js
|
|
48
|
-
var MOUNT_PLUMBING_KEYS = ["instanceId", "runtime", "tileId"];
|
|
49
|
-
function stripMountPlumbing(config) {
|
|
50
|
-
if (!config || typeof config !== "object") {
|
|
51
|
-
return {};
|
|
52
|
-
}
|
|
53
|
-
const out = { ...config };
|
|
54
|
-
for (const key of MOUNT_PLUMBING_KEYS) {
|
|
55
|
-
delete out[key];
|
|
56
|
-
}
|
|
57
|
-
return out;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ../../sdk-contracts/dist/routes.js
|
|
61
|
-
var RESERVED_BYTES = /* @__PURE__ */ new Set([
|
|
62
|
-
33,
|
|
63
|
-
// !
|
|
64
|
-
35,
|
|
65
|
-
// #
|
|
66
|
-
36,
|
|
67
|
-
// $
|
|
68
|
-
38,
|
|
69
|
-
// &
|
|
70
|
-
39,
|
|
71
|
-
// '
|
|
72
|
-
40,
|
|
73
|
-
// (
|
|
74
|
-
41,
|
|
75
|
-
// )
|
|
76
|
-
42,
|
|
77
|
-
// *
|
|
78
|
-
43,
|
|
79
|
-
// +
|
|
80
|
-
44,
|
|
81
|
-
// ,
|
|
82
|
-
47,
|
|
83
|
-
// /
|
|
84
|
-
58,
|
|
85
|
-
// :
|
|
86
|
-
59,
|
|
87
|
-
// ;
|
|
88
|
-
61,
|
|
89
|
-
// =
|
|
90
|
-
63,
|
|
91
|
-
// ?
|
|
92
|
-
64,
|
|
93
|
-
// @
|
|
94
|
-
91,
|
|
95
|
-
// [
|
|
96
|
-
93
|
|
97
|
-
// ]
|
|
98
|
-
]);
|
|
99
|
-
var utf8Decoder = new TextDecoder("utf-8", { fatal: false });
|
|
100
|
-
function decodeUnreservedOnly(input) {
|
|
101
|
-
let out = "";
|
|
102
|
-
let pending2 = [];
|
|
103
|
-
const flushPending = () => {
|
|
104
|
-
if (pending2.length === 0)
|
|
105
|
-
return;
|
|
106
|
-
const bytes = new Uint8Array(pending2);
|
|
107
|
-
out += utf8Decoder.decode(bytes);
|
|
108
|
-
pending2 = [];
|
|
109
|
-
};
|
|
110
|
-
let i2 = 0;
|
|
111
|
-
while (i2 < input.length) {
|
|
112
|
-
const ch = input[i2];
|
|
113
|
-
if (ch === "%" && i2 + 2 < input.length && isHex(input[i2 + 1]) && isHex(input[i2 + 2])) {
|
|
114
|
-
const byte = parseInt(input.slice(i2 + 1, i2 + 3), 16);
|
|
115
|
-
if (RESERVED_BYTES.has(byte)) {
|
|
116
|
-
flushPending();
|
|
117
|
-
out += `%${input.slice(i2 + 1, i2 + 3).toUpperCase()}`;
|
|
118
|
-
i2 += 3;
|
|
119
|
-
} else {
|
|
120
|
-
pending2.push(byte);
|
|
121
|
-
i2 += 3;
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
flushPending();
|
|
125
|
-
out += ch;
|
|
126
|
-
i2 += 1;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
flushPending();
|
|
130
|
-
return out;
|
|
131
|
-
}
|
|
132
|
-
function isHex(c2) {
|
|
133
|
-
return c2 >= "0" && c2 <= "9" || c2 >= "a" && c2 <= "f" || c2 >= "A" && c2 <= "F";
|
|
134
|
-
}
|
|
135
|
-
function stripQueryAndHash(s4) {
|
|
136
|
-
const q = s4.indexOf("?");
|
|
137
|
-
if (q !== -1)
|
|
138
|
-
s4 = s4.slice(0, q);
|
|
139
|
-
const h = s4.indexOf("#");
|
|
140
|
-
if (h !== -1)
|
|
141
|
-
s4 = s4.slice(0, h);
|
|
142
|
-
return s4;
|
|
143
|
-
}
|
|
144
|
-
function normalizeRoute(path) {
|
|
145
|
-
if (typeof path !== "string" || path.length === 0) {
|
|
146
|
-
throw new TypeError("normalizeRoute: input must be a non-empty string");
|
|
147
|
-
}
|
|
148
|
-
if (!path.startsWith("/")) {
|
|
149
|
-
throw new TypeError(`normalizeRoute: input must be absolute (start with '/'); got ${JSON.stringify(path)}`);
|
|
150
|
-
}
|
|
151
|
-
let s4 = stripQueryAndHash(path);
|
|
152
|
-
s4 = decodeUnreservedOnly(s4);
|
|
153
|
-
s4 = s4.replace(/\/+/g, "/");
|
|
154
|
-
if (s4.length > 1 && s4.endsWith("/"))
|
|
155
|
-
s4 = s4.slice(0, -1);
|
|
156
|
-
return s4;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// ../../sdk-contracts/dist/schemas.js
|
|
160
|
-
import { z } from "zod";
|
|
161
|
-
var NO_CSS_BREAKOUT_PATTERN = /^[^{}]*$/;
|
|
162
|
-
var AnchorIdZ = z.object({
|
|
163
|
-
selector: z.string().regex(NO_CSS_BREAKOUT_PATTERN, {
|
|
164
|
-
message: 'selector must not contain "{" or "}" \u2014 not valid CSS selector syntax, and content:hideBySelector injects this value directly into a <style> element where these characters would break out of the generated rule.'
|
|
165
|
-
}).describe("CSS selector for the target element"),
|
|
166
|
-
route: z.union([z.string(), z.array(z.string())]).superRefine((value, ctx) => {
|
|
167
|
-
for (const route of Array.isArray(value) ? value : [value]) {
|
|
168
|
-
let canonical;
|
|
169
|
-
try {
|
|
170
|
-
canonical = normalizeRoute(route);
|
|
171
|
-
} catch (err) {
|
|
172
|
-
ctx.addIssue({
|
|
173
|
-
code: z.ZodIssueCode.custom,
|
|
174
|
-
message: `route must be an absolute path starting with "/" (got ${JSON.stringify(route)}): ${err instanceof Error ? err.message : String(err)}`
|
|
175
|
-
});
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
if (canonical !== route) {
|
|
179
|
-
ctx.addIssue({
|
|
180
|
-
code: z.ZodIssueCode.custom,
|
|
181
|
-
message: `route ${JSON.stringify(route)} is not canonical \u2014 use ${JSON.stringify(canonical)} (this must match what the backend's RouteCanonicalityCheck accepts)`
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}).describe("URL path(s) where this element exists")
|
|
186
|
-
}).strict().describe("DOM element target. selector = CSS selector, route = URL path(s) where the element exists.");
|
|
187
|
-
var AuthoringFieldsZ = {
|
|
188
|
-
id: z.string().optional().describe('Stable action identifier (e.g. "act_3db6a14d2ab0").'),
|
|
189
|
-
title: z.string().max(200).optional().describe("Authoring-only: short label shown on the action plan dashboard. Stripped before serving to the runtime SDK."),
|
|
190
|
-
description: z.string().max(1e3).optional().describe("Authoring-only: one-sentence explanation of what this action does and why. Stripped before serving to the runtime SDK."),
|
|
191
|
-
validation: z.array(z.string().max(500)).max(10).optional().describe("Authoring-only: ordered steps a reviewer can follow to trigger this action and visually confirm it works. Each entry is one step. Stripped before serving to the runtime SDK.")
|
|
192
|
-
};
|
|
193
|
-
var COUNTABLE_EVENTS = [
|
|
194
|
-
// User interactions (from PostHog autocapture normalization)
|
|
195
|
-
"ui.click",
|
|
196
|
-
"ui.scroll",
|
|
197
|
-
"ui.input",
|
|
198
|
-
"ui.change",
|
|
199
|
-
"ui.submit",
|
|
200
|
-
// Behavioral detectors (from event-processor)
|
|
201
|
-
"ui.hover",
|
|
202
|
-
"ui.idle",
|
|
203
|
-
"ui.scroll_thrash",
|
|
204
|
-
"ui.focus_bounce",
|
|
205
|
-
"ui.hesitate",
|
|
206
|
-
"ui.rage_click",
|
|
207
|
-
// Navigation
|
|
208
|
-
"nav.page_view",
|
|
209
|
-
"nav.page_leave"
|
|
210
|
-
];
|
|
211
|
-
var CountableEventZ = z.enum(COUNTABLE_EVENTS).describe("Event name to count. ui.* = user interactions and behavioral detectors (hesitate, rage_click, scroll_thrash, focus_bounce, idle, hover); nav.* = page navigation.");
|
|
212
|
-
var SESSION_METRIC_KEYS = ["time_on_page", "page_views", "scroll_depth"];
|
|
213
|
-
var SessionMetricKeyZ = z.enum(SESSION_METRIC_KEYS).describe("Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage.");
|
|
214
|
-
var PageUrlConditionZ = z.object({
|
|
215
|
-
type: z.literal("page_url"),
|
|
216
|
-
url: z.string().describe('URL path to match (e.g. "/pricing", "/dashboard")')
|
|
217
|
-
}).describe('Fires when the current page URL matches. Use for page-specific actions. Example: {"type": "page_url", "url": "/pricing"}');
|
|
218
|
-
var RouteConditionZ = z.object({
|
|
219
|
-
type: z.literal("route"),
|
|
220
|
-
routeId: z.string().describe("Named route ID from the route filter")
|
|
221
|
-
}).describe("Fires when the current route matches a named route ID.");
|
|
222
|
-
var AnchorVisibleConditionZ = z.object({
|
|
223
|
-
type: z.literal("anchor_visible"),
|
|
224
|
-
anchorId: z.string().describe("CSS selector of the anchor element"),
|
|
225
|
-
state: z.enum(["visible", "present", "absent"]).describe('"visible" = in viewport, "present" = in DOM, "absent" = not in DOM')
|
|
226
|
-
}).describe(`Fires based on a DOM element's visibility state. Example: {"type": "anchor_visible", "anchorId": "#cta-button", "state": "visible"}`);
|
|
227
|
-
var EventOccurredConditionZ = z.object({
|
|
228
|
-
type: z.literal("event_occurred"),
|
|
229
|
-
eventName: z.string().describe('Event name (e.g. "ui.click", "$pageview")'),
|
|
230
|
-
withinMs: z.number().optional().describe("Time window in ms. Omit = any time this session.")
|
|
231
|
-
}).describe('Fires when a specific event has occurred during this session. Example: {"type": "event_occurred", "eventName": "ui.click", "withinMs": 5000}');
|
|
232
|
-
var StateEqualsConditionZ = z.object({
|
|
233
|
-
type: z.literal("state_equals"),
|
|
234
|
-
key: z.string().describe("Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set()."),
|
|
235
|
-
value: z.unknown().describe("Expected value to match against")
|
|
236
|
-
}).describe("Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() \u2014 NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type.");
|
|
237
|
-
var ViewportConditionZ = z.object({
|
|
238
|
-
type: z.literal("viewport"),
|
|
239
|
-
minWidth: z.number().optional().describe("Minimum viewport width in pixels"),
|
|
240
|
-
maxWidth: z.number().optional().describe("Maximum viewport width in pixels"),
|
|
241
|
-
minHeight: z.number().optional().describe("Minimum viewport height in pixels"),
|
|
242
|
-
maxHeight: z.number().optional().describe("Maximum viewport height in pixels")
|
|
243
|
-
}).describe('Fires based on viewport (screen) size. Use for responsive behavior. Example: {"type": "viewport", "minWidth": 768} \u2014 fires on tablet and larger.');
|
|
244
|
-
var SessionMetricConditionZ = z.object({
|
|
245
|
-
type: z.literal("session_metric"),
|
|
246
|
-
key: SessionMetricKeyZ,
|
|
247
|
-
operator: z.enum(["gte", "lte", "eq", "gt", "lt"]),
|
|
248
|
-
threshold: z.number().describe("Numeric threshold to compare against")
|
|
249
|
-
}).describe('Fires when a session metric crosses a threshold. Valid keys: "time_on_page" (seconds), "page_views" (count), "scroll_depth" (0-100). Example: {"type": "session_metric", "key": "time_on_page", "operator": "gte", "threshold": 30}');
|
|
250
|
-
var DismissedConditionZ = z.object({
|
|
251
|
-
type: z.literal("dismissed"),
|
|
252
|
-
key: z.string().describe("Dismissal key (usually a tile or action ID)"),
|
|
253
|
-
inverted: z.boolean().optional().describe("When true, fires if NOT dismissed (default behavior)")
|
|
254
|
-
}).describe("Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed.");
|
|
255
|
-
var CooldownActiveConditionZ = z.object({
|
|
256
|
-
type: z.literal("cooldown_active"),
|
|
257
|
-
key: z.string().describe("Cooldown key"),
|
|
258
|
-
inverted: z.boolean().optional().describe("When true, fires if cooldown is NOT active")
|
|
259
|
-
}).describe("Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently.");
|
|
260
|
-
var FrequencyLimitConditionZ = z.object({
|
|
261
|
-
type: z.literal("frequency_limit"),
|
|
262
|
-
key: z.string().describe("Frequency counter key"),
|
|
263
|
-
limit: z.number().describe("Maximum allowed count"),
|
|
264
|
-
inverted: z.boolean().optional().describe("When true, fires if limit NOT reached")
|
|
265
|
-
}).describe("Checks if a frequency limit has been reached. Use to cap how many times an action fires per session.");
|
|
266
|
-
var MatchOpZ = z.object({
|
|
267
|
-
equals: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
268
|
-
contains: z.string().optional()
|
|
269
|
-
}).refine((operator) => Number(operator.equals !== void 0) + Number(operator.contains !== void 0) === 1, {
|
|
270
|
-
message: "Exactly one of equals or contains must be specified."
|
|
271
|
-
}).describe("Match operator for counter filters. Exactly one of equals or contains must be specified.");
|
|
272
|
-
var CounterDefZ = z.object({
|
|
273
|
-
events: z.array(CountableEventZ).min(1).describe("Event names to count. Use values from the countable events enum."),
|
|
274
|
-
match: z.record(z.string(), MatchOpZ).optional().describe("Property filters. Keys are event prop names or element-chain fields (tag_name, $el_text, attr__*). All entries AND together.")
|
|
275
|
-
}).describe("Defines what events to count. Registered as an accumulator predicate at config-load time.");
|
|
276
|
-
var EventCountConditionZ = z.object({
|
|
277
|
-
type: z.literal("event_count"),
|
|
278
|
-
key: z.string().describe("Unique key for this counter (used for accumulator registration)"),
|
|
279
|
-
operator: z.enum(["gte", "lte", "eq", "gt", "lt"]),
|
|
280
|
-
count: z.number().int().min(0).describe("Target count threshold"),
|
|
281
|
-
withinMs: z.number().positive().optional().describe("Time window in ms. Omit = count across entire session."),
|
|
282
|
-
counter: CounterDefZ.optional().describe("Inline counter definition. Defines what events to count.")
|
|
283
|
-
}).describe('Fires when accumulated event count crosses a threshold. Most powerful trigger type. Example: {"type": "event_count", "key": "pricing-clicks", "operator": "gte", "count": 3, "counter": {"events": ["ui.click"], "match": {"attr__data-cta": {"contains": "pricing"}}}}');
|
|
284
|
-
var ConditionZ = z.discriminatedUnion("type", [
|
|
285
|
-
PageUrlConditionZ,
|
|
286
|
-
RouteConditionZ,
|
|
287
|
-
AnchorVisibleConditionZ,
|
|
288
|
-
EventOccurredConditionZ,
|
|
289
|
-
StateEqualsConditionZ,
|
|
290
|
-
ViewportConditionZ,
|
|
291
|
-
SessionMetricConditionZ,
|
|
292
|
-
DismissedConditionZ,
|
|
293
|
-
CooldownActiveConditionZ,
|
|
294
|
-
FrequencyLimitConditionZ,
|
|
295
|
-
EventCountConditionZ
|
|
296
|
-
]);
|
|
297
|
-
var RuleZ = z.object({
|
|
298
|
-
conditions: z.array(ConditionZ).describe("Array of conditions \u2014 ALL must match (AND logic) for this rule to fire."),
|
|
299
|
-
value: z.unknown().describe("Value returned when all conditions match. For triggerWhen: true = fire the action.")
|
|
300
|
-
}).describe("A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated top-to-bottom \u2014 first rule where all conditions match wins and returns its value.");
|
|
301
|
-
var RuleStrategyZ = z.object({
|
|
302
|
-
type: z.literal("rules"),
|
|
303
|
-
rules: z.array(RuleZ).describe("Ordered list of rules. Evaluated top-to-bottom \u2014 first match wins."),
|
|
304
|
-
default: z.unknown().describe("Fallback value when no rule matches. For triggerWhen: false = do not fire by default.")
|
|
305
|
-
}).describe("Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match returns its value. If no rule matches, returns default. For triggerWhen: set value=true on matching rules, default=false.");
|
|
306
|
-
var ScoreStrategyZ = z.object({
|
|
307
|
-
type: z.literal("score"),
|
|
308
|
-
field: z.string(),
|
|
309
|
-
threshold: z.number(),
|
|
310
|
-
above: z.unknown(),
|
|
311
|
-
below: z.unknown()
|
|
312
|
-
}).describe("Score-based strategy. Compares a field value against a threshold.");
|
|
313
|
-
var ModelStrategyZ = z.object({
|
|
314
|
-
type: z.literal("model"),
|
|
315
|
-
modelId: z.string(),
|
|
316
|
-
inputs: z.array(z.string()),
|
|
317
|
-
outputMapping: z.record(z.string(), z.unknown()),
|
|
318
|
-
default: z.unknown()
|
|
319
|
-
}).describe("ML model strategy. Sends inputs to a model and maps outputs.");
|
|
320
|
-
var ExternalStrategyZ = z.object({
|
|
321
|
-
type: z.literal("external"),
|
|
322
|
-
endpoint: z.string(),
|
|
323
|
-
method: z.enum(["GET", "POST"]).optional(),
|
|
324
|
-
default: z.unknown(),
|
|
325
|
-
timeoutMs: z.number().optional()
|
|
326
|
-
}).describe("External API strategy. Calls an endpoint to determine the value.");
|
|
327
|
-
var DecisionStrategyZ = z.discriminatedUnion("type", [
|
|
328
|
-
RuleStrategyZ,
|
|
329
|
-
ScoreStrategyZ,
|
|
330
|
-
ModelStrategyZ,
|
|
331
|
-
ExternalStrategyZ
|
|
332
|
-
]);
|
|
333
|
-
var TriggerWhenZ = DecisionStrategyZ.nullable().optional();
|
|
334
|
-
var EventScopeZ = z.object({
|
|
335
|
-
events: z.array(z.string()),
|
|
336
|
-
urlContains: z.string().optional(),
|
|
337
|
-
props: z.record(z.union([z.string(), z.number(), z.boolean()])).optional()
|
|
338
|
-
});
|
|
339
|
-
var NotifyZ = z.object({
|
|
340
|
-
title: z.string().optional().describe("Notification title"),
|
|
341
|
-
body: z.string().optional().describe("Notification body text"),
|
|
342
|
-
icon: z.string().optional().describe("Notification icon (emoji or URL)")
|
|
343
|
-
}).describe("Optional toast notification shown when this action triggers.").nullable().optional();
|
|
344
|
-
|
|
345
|
-
// ../../sdk-contracts/dist/telemetry-events.js
|
|
346
|
-
var entry = (e2) => e2;
|
|
347
|
-
var TELEMETRY_EVENTS = {
|
|
348
|
-
// --- intervention metrics (April 2026) — wire names unchanged, all plan-scope
|
|
349
|
-
syntro_config_served: entry({
|
|
350
|
-
subject: "config",
|
|
351
|
-
verb: "served",
|
|
352
|
-
scope: "plan",
|
|
353
|
-
props: ["tiles", "actions"]
|
|
354
|
-
}),
|
|
355
|
-
syntro_intervention_seen: entry({
|
|
356
|
-
subject: "intervention",
|
|
357
|
-
verb: "seen",
|
|
358
|
-
scope: "plan",
|
|
359
|
-
props: ["intervention_id", "intervention_kind"]
|
|
360
|
-
}),
|
|
361
|
-
syntro_intervention_triggered: entry({
|
|
362
|
-
subject: "intervention",
|
|
363
|
-
verb: "triggered",
|
|
364
|
-
scope: "plan",
|
|
365
|
-
props: ["intervention_id", "intervention_kind"]
|
|
366
|
-
}),
|
|
367
|
-
syntro_intervention_interacted: entry({
|
|
368
|
-
subject: "intervention",
|
|
369
|
-
verb: "interacted",
|
|
370
|
-
scope: "plan",
|
|
371
|
-
props: ["intervention_id", "intervention_kind", "interaction_type"]
|
|
372
|
-
}),
|
|
373
|
-
// --- concierge (FEAT-1789150184) ---------------------------------------
|
|
374
|
-
syntro_concierge_opened: entry({
|
|
375
|
-
subject: "concierge",
|
|
376
|
-
verb: "opened",
|
|
377
|
-
scope: "open",
|
|
378
|
-
props: ["trigger", "page_path"]
|
|
379
|
-
// trigger: ConciergeOpenTrigger
|
|
380
|
-
}),
|
|
381
|
-
syntro_concierge_closed: entry({
|
|
382
|
-
subject: "concierge",
|
|
383
|
-
verb: "closed",
|
|
384
|
-
scope: "open",
|
|
385
|
-
props: ["duration_ms", "reason", "messages_sent", "tiles_dismissed", "pages_viewed"]
|
|
386
|
-
// reason: ConciergeCloseReason
|
|
387
|
-
}),
|
|
388
|
-
syntro_deck_paged: entry({
|
|
389
|
-
subject: "deck",
|
|
390
|
-
verb: "paged",
|
|
391
|
-
scope: "open",
|
|
392
|
-
props: ["from_index", "to_index", "method"]
|
|
393
|
-
// method: DeckPageMethod
|
|
394
|
-
}),
|
|
395
|
-
syntro_tile_dismissed: entry({
|
|
396
|
-
subject: "tile",
|
|
397
|
-
verb: "dismissed",
|
|
398
|
-
scope: "open",
|
|
399
|
-
props: ["tile_id", "tile_kind", "visible_ms"]
|
|
400
|
-
}),
|
|
401
|
-
// The three chat atoms are OPEN-scope: they carry `surface` / `open_id` and
|
|
402
|
-
// only make sense inside a concierge open. An INLINE chat bar (no concierge)
|
|
403
|
-
// still dispatches them on every turn, and the core drops each one and
|
|
404
|
-
// increments the `telemetry_dropped` health counter. That is EXPECTED on an
|
|
405
|
-
// inline-chat host page, not a bug — `telemetry_dropped` there mixes "real
|
|
406
|
-
// defect" with "inline chat has no open scope", so do not alert on it alone.
|
|
407
|
-
syntro_chat_message_sent: entry({
|
|
408
|
-
subject: "chat_message",
|
|
409
|
-
verb: "sent",
|
|
410
|
-
scope: "open",
|
|
411
|
-
props: ["turn", "chars", "via"]
|
|
412
|
-
// via: ChatSendVia
|
|
413
|
-
}),
|
|
414
|
-
syntro_chat_reply_received: entry({
|
|
415
|
-
subject: "chat_reply",
|
|
416
|
-
verb: "received",
|
|
417
|
-
scope: "open",
|
|
418
|
-
props: ["turn", "ttft_ms", "total_ms", "mounts", "cards"]
|
|
419
|
-
}),
|
|
420
|
-
syntro_chat_interrupted: entry({
|
|
421
|
-
subject: "chat",
|
|
422
|
-
verb: "interrupted",
|
|
423
|
-
scope: "open",
|
|
424
|
-
props: ["turn", "after_ms"]
|
|
425
|
-
}),
|
|
426
|
-
syntro_chat_maximize_toggled: entry({
|
|
427
|
-
subject: "chat_maximize",
|
|
428
|
-
verb: "toggled",
|
|
429
|
-
scope: "open",
|
|
430
|
-
props: ["maximized"]
|
|
431
|
-
}),
|
|
432
|
-
syntro_chat_takeover_triggered: entry({
|
|
433
|
-
subject: "chat_takeover",
|
|
434
|
-
verb: "triggered",
|
|
435
|
-
scope: "open",
|
|
436
|
-
props: ["turn"]
|
|
437
|
-
})
|
|
438
|
-
};
|
|
439
|
-
var TELEMETRY_EVENT_NAMES = Object.keys(TELEMETRY_EVENTS);
|
|
440
|
-
|
|
441
|
-
// ../../sdk-contracts/dist/viewport.js
|
|
442
|
-
var VIEWPORT_CLASS_ATTR = "data-viewport";
|
|
443
|
-
var DESKTOP_MIN_WIDTH_PX = 720;
|
|
444
|
-
function isCoarsePointer() {
|
|
445
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function")
|
|
446
|
-
return true;
|
|
447
|
-
return window.matchMedia("(pointer: coarse)").matches;
|
|
448
|
-
}
|
|
449
|
-
function classifyPointer() {
|
|
450
|
-
return isCoarsePointer() ? "coarse" : "fine";
|
|
451
|
-
}
|
|
452
|
-
function hasAnyFinePointer() {
|
|
453
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function")
|
|
454
|
-
return true;
|
|
455
|
-
return window.matchMedia("(any-pointer: fine)").matches;
|
|
456
|
-
}
|
|
457
|
-
function observePointer(cb) {
|
|
458
|
-
const read = () => ({ primary: classifyPointer(), anyFine: hasAnyFinePointer() });
|
|
459
|
-
let current = read();
|
|
460
|
-
cb(current);
|
|
461
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function")
|
|
462
|
-
return () => {
|
|
463
|
-
};
|
|
464
|
-
const coarse = window.matchMedia("(pointer: coarse)");
|
|
465
|
-
const anyFine = window.matchMedia("(any-pointer: fine)");
|
|
466
|
-
const onChange = () => {
|
|
467
|
-
const next = read();
|
|
468
|
-
if (next.primary !== current.primary || next.anyFine !== current.anyFine) {
|
|
469
|
-
current = next;
|
|
470
|
-
cb(next);
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
const sub = (mq) => {
|
|
474
|
-
if (mq.addEventListener)
|
|
475
|
-
mq.addEventListener("change", onChange);
|
|
476
|
-
else
|
|
477
|
-
mq.addListener?.(onChange);
|
|
478
|
-
};
|
|
479
|
-
const unsub = (mq) => {
|
|
480
|
-
if (mq.removeEventListener)
|
|
481
|
-
mq.removeEventListener("change", onChange);
|
|
482
|
-
else
|
|
483
|
-
mq.removeListener?.(onChange);
|
|
484
|
-
};
|
|
485
|
-
sub(coarse);
|
|
486
|
-
sub(anyFine);
|
|
487
|
-
return () => {
|
|
488
|
-
unsub(coarse);
|
|
489
|
-
unsub(anyFine);
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
var POINTER_CLASS_ATTR = "data-pointer";
|
|
493
|
-
function classifyViewport() {
|
|
494
|
-
if (typeof window === "undefined")
|
|
495
|
-
return "mobile";
|
|
496
|
-
return window.innerWidth >= DESKTOP_MIN_WIDTH_PX ? "desktop" : "mobile";
|
|
497
|
-
}
|
|
498
|
-
function observeViewportClass(cb) {
|
|
499
|
-
let current = classifyViewport();
|
|
500
|
-
cb(current);
|
|
501
|
-
if (typeof window === "undefined")
|
|
502
|
-
return () => {
|
|
503
|
-
};
|
|
504
|
-
const onResize = () => {
|
|
505
|
-
const next = classifyViewport();
|
|
506
|
-
if (next !== current) {
|
|
507
|
-
current = next;
|
|
508
|
-
cb(next);
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
window.addEventListener("resize", onResize, { passive: true });
|
|
512
|
-
return () => window.removeEventListener("resize", onResize);
|
|
513
|
-
}
|
|
514
|
-
var ViewportClassController = class {
|
|
515
|
-
constructor(host) {
|
|
516
|
-
this.value = classifyViewport();
|
|
517
|
-
this.pointer = classifyPointer();
|
|
518
|
-
this.anyFinePointer = hasAnyFinePointer();
|
|
519
|
-
this._unsubscribes = [];
|
|
520
|
-
this._host = host;
|
|
521
|
-
host.addController(this);
|
|
522
|
-
}
|
|
523
|
-
hostConnected() {
|
|
524
|
-
this._unsubscribes = [
|
|
525
|
-
observeViewportClass((c2) => {
|
|
526
|
-
this.value = c2;
|
|
527
|
-
this._host.setAttribute(VIEWPORT_CLASS_ATTR, c2);
|
|
528
|
-
this._host.requestUpdate();
|
|
529
|
-
}),
|
|
530
|
-
observePointer(({ primary, anyFine }) => {
|
|
531
|
-
this.pointer = primary;
|
|
532
|
-
this.anyFinePointer = anyFine;
|
|
533
|
-
this._host.setAttribute(POINTER_CLASS_ATTR, primary);
|
|
534
|
-
this._host.requestUpdate();
|
|
535
|
-
})
|
|
536
|
-
];
|
|
537
|
-
}
|
|
538
|
-
hostDisconnected() {
|
|
539
|
-
for (const off of this._unsubscribes)
|
|
540
|
-
off();
|
|
541
|
-
this._unsubscribes = [];
|
|
542
|
-
}
|
|
543
|
-
};
|
|
18
|
+
standinInitial,
|
|
19
|
+
stripMountPlumbing,
|
|
20
|
+
takeoverPlanSchema,
|
|
21
|
+
watchLivePrice
|
|
22
|
+
} from "./chunk-E2PP64GL.js";
|
|
544
23
|
|
|
545
24
|
// src/presets-store.ts
|
|
546
25
|
function presetsWindow() {
|
|
@@ -577,9 +56,9 @@ function selectPoolItems(items, decision, max) {
|
|
|
577
56
|
if (items.length <= max) return [...items];
|
|
578
57
|
const sharp = decision && decision !== UNKNOWN_DECISION;
|
|
579
58
|
if (sharp) {
|
|
580
|
-
const matched = items.filter((
|
|
581
|
-
const general = items.filter((
|
|
582
|
-
const rest = items.filter((
|
|
59
|
+
const matched = items.filter((i) => tagsOf(i).includes(decision));
|
|
60
|
+
const general = items.filter((i) => tagsOf(i).length === 0);
|
|
61
|
+
const rest = items.filter((i) => !matched.includes(i) && !general.includes(i));
|
|
583
62
|
return [...matched, ...general, ...rest].slice(0, max);
|
|
584
63
|
}
|
|
585
64
|
const picked = [];
|
|
@@ -587,9 +66,9 @@ function selectPoolItems(items, decision, max) {
|
|
|
587
66
|
for (const item of items) {
|
|
588
67
|
const tags = tagsOf(item);
|
|
589
68
|
if (tags.length === 0) continue;
|
|
590
|
-
if (tags.some((
|
|
69
|
+
if (tags.some((t) => covered.has(t))) continue;
|
|
591
70
|
picked.push(item);
|
|
592
|
-
for (const
|
|
71
|
+
for (const t of tags) covered.add(t);
|
|
593
72
|
if (picked.length >= max) return picked.slice(0, max);
|
|
594
73
|
}
|
|
595
74
|
for (const item of items) {
|
|
@@ -1052,8 +531,8 @@ async function resolveScoredTemplate(opts) {
|
|
|
1052
531
|
value = await opts.score(opts.scoreSpec);
|
|
1053
532
|
}
|
|
1054
533
|
const variant = opts.presets[value] ?? opts.presets[opts.fallbackValue ?? "unknown"] ?? {};
|
|
1055
|
-
const setSlots = opts.slotIds.filter((
|
|
1056
|
-
const unsetSlots = opts.slotIds.filter((
|
|
534
|
+
const setSlots = opts.slotIds.filter((s) => s in variant);
|
|
535
|
+
const unsetSlots = opts.slotIds.filter((s) => !(s in variant));
|
|
1057
536
|
const pageOrder = opts.orderSlots ? opts.orderSlots([...opts.slotIds], (slot) => slot in variant) : opts.slotIds;
|
|
1058
537
|
if (pageOrder.length > 0) {
|
|
1059
538
|
opts.emitPlan(opts.buildPlan(pageOrder.map((slot) => ({ slot, preset: variant[slot] }))));
|
|
@@ -1068,6 +547,62 @@ async function resolveScoredTemplate(opts) {
|
|
|
1068
547
|
return { value, decisionValue, setSlots, unsetSlots };
|
|
1069
548
|
}
|
|
1070
549
|
|
|
550
|
+
// src/storefront-urls.ts
|
|
551
|
+
function storefrontBase(runtime2) {
|
|
552
|
+
if (!runtime2 || typeof runtime2 !== "object" || !("storefrontUrl" in runtime2)) return void 0;
|
|
553
|
+
if (typeof runtime2.storefrontUrl !== "string") return void 0;
|
|
554
|
+
try {
|
|
555
|
+
const base = new URL(runtime2.storefrontUrl);
|
|
556
|
+
return base.protocol === "https:" || base.protocol === "http:" ? base : void 0;
|
|
557
|
+
} catch {
|
|
558
|
+
return void 0;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function resolveAgainst(value, base) {
|
|
562
|
+
if (typeof value !== "string" || !value || /^(?:[a-z][a-z\d+.-]*:|\/\/|[?#])/i.test(value))
|
|
563
|
+
return value;
|
|
564
|
+
try {
|
|
565
|
+
const resolved = new URL(value, base);
|
|
566
|
+
return resolved.protocol === "https:" || resolved.protocol === "http:" ? resolved.href : value;
|
|
567
|
+
} catch {
|
|
568
|
+
return value;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
function resolveStorePath(value, runtime2) {
|
|
572
|
+
const base = storefrontBase(runtime2);
|
|
573
|
+
return base ? resolveAgainst(value, base) : value;
|
|
574
|
+
}
|
|
575
|
+
function resolveStorefrontProps(props, runtime2) {
|
|
576
|
+
const base = storefrontBase(runtime2);
|
|
577
|
+
if (!base) return props;
|
|
578
|
+
const resolve = (value) => resolveAgainst(value, base);
|
|
579
|
+
const product = (value) => ({
|
|
580
|
+
...value,
|
|
581
|
+
...value.image ? { image: { ...value.image, src: resolve(value.image.src) } } : {},
|
|
582
|
+
...Array.isArray(value.ctas) ? {
|
|
583
|
+
ctas: value.ctas.map((cta) => ({
|
|
584
|
+
...cta,
|
|
585
|
+
...cta.href ? { href: resolve(cta.href) } : {}
|
|
586
|
+
}))
|
|
587
|
+
} : {}
|
|
588
|
+
});
|
|
589
|
+
const content = props;
|
|
590
|
+
return {
|
|
591
|
+
...props,
|
|
592
|
+
...content.product ? { product: product(content.product) } : {},
|
|
593
|
+
...Array.isArray(content.products) ? { products: content.products.map((value) => value ? product(value) : value) } : {},
|
|
594
|
+
...Array.isArray(content.items) ? {
|
|
595
|
+
items: content.items.map(
|
|
596
|
+
(item) => item ? {
|
|
597
|
+
...item,
|
|
598
|
+
...item.image_url ? { image_url: resolve(item.image_url) } : {},
|
|
599
|
+
pdp_href: resolve(item.pdp_href)
|
|
600
|
+
} : item
|
|
601
|
+
)
|
|
602
|
+
} : {}
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
|
|
1071
606
|
// src/takeover/resurrectionGuard.ts
|
|
1072
607
|
function startResurrectionGuard(opts) {
|
|
1073
608
|
const maxRetries = opts.maxRetries ?? 3;
|
|
@@ -1096,8 +631,8 @@ function startResurrectionGuard(opts) {
|
|
|
1096
631
|
opts.onBudgetExhausted(exhausted.sectionId, exhausted.el);
|
|
1097
632
|
return;
|
|
1098
633
|
}
|
|
1099
|
-
for (const { sectionId, el, n
|
|
1100
|
-
counts.set(sectionId,
|
|
634
|
+
for (const { sectionId, el, n } of present) {
|
|
635
|
+
counts.set(sectionId, n);
|
|
1101
636
|
opts.onResurrected(sectionId, el);
|
|
1102
637
|
}
|
|
1103
638
|
};
|
|
@@ -1154,7 +689,7 @@ var NOOP_DISPOSE = () => {
|
|
|
1154
689
|
};
|
|
1155
690
|
async function runTakeover(config, ports, opts = {}) {
|
|
1156
691
|
if (!ports.hasSignals()) {
|
|
1157
|
-
const declaredRoles = new Set(config.hostSections.map((
|
|
692
|
+
const declaredRoles = new Set(config.hostSections.map((s) => s.role));
|
|
1158
693
|
void Promise.resolve().then(() => ports.fetchPlan()).then(
|
|
1159
694
|
(response2) => ports.renderModules(
|
|
1160
695
|
{
|
|
@@ -1164,9 +699,9 @@ async function runTakeover(config, ports, opts = {}) {
|
|
|
1164
699
|
// 1. host-derived entries (kind:sectionId) — review F2;
|
|
1165
700
|
// 2. native kinds superseding a DECLARED host role (two-Q&As
|
|
1166
701
|
// bug: our qa next to their attached FAQ).
|
|
1167
|
-
order: response2.plan.order.filter((
|
|
1168
|
-
if (
|
|
1169
|
-
const superseded = KIND_SUPERSEDES_ROLE[
|
|
702
|
+
order: response2.plan.order.filter((entry) => {
|
|
703
|
+
if (entry.includes(":")) return false;
|
|
704
|
+
const superseded = KIND_SUPERSEDES_ROLE[entry];
|
|
1170
705
|
return !(superseded && declaredRoles.has(superseded));
|
|
1171
706
|
}),
|
|
1172
707
|
suppress: []
|
|
@@ -1200,7 +735,7 @@ async function runTakeover(config, ports, opts = {}) {
|
|
|
1200
735
|
return { outcome: "aborted-precommit", dispose: NOOP_DISPOSE };
|
|
1201
736
|
}
|
|
1202
737
|
const plan = response.plan;
|
|
1203
|
-
const sectionsById = new Map(config.hostSections.map((
|
|
738
|
+
const sectionsById = new Map(config.hostSections.map((s) => [s.id, s]));
|
|
1204
739
|
const toSuppress = [];
|
|
1205
740
|
let reservedPx = 0;
|
|
1206
741
|
for (const id of plan.suppress) {
|
|
@@ -1272,9 +807,8 @@ async function runTakeover(config, ports, opts = {}) {
|
|
|
1272
807
|
}
|
|
1273
808
|
|
|
1274
809
|
// src/takeover/fetchPlan.ts
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
decisionArchetype: z2.string().min(1),
|
|
810
|
+
var takeoverPlanResponseSchema = external_exports.object({
|
|
811
|
+
decisionArchetype: external_exports.string().min(1),
|
|
1278
812
|
plan: takeoverPlanSchema
|
|
1279
813
|
}).strict();
|
|
1280
814
|
function createTakeoverPlanFetcher(deps) {
|
|
@@ -1343,7 +877,7 @@ function applyRegionCurtain(region, opts = {}) {
|
|
|
1343
877
|
return lift;
|
|
1344
878
|
}
|
|
1345
879
|
function applyCurtainToTargets(targets, opts = {}) {
|
|
1346
|
-
const lifts = targets.map((
|
|
880
|
+
const lifts = targets.map((t) => applyRegionCurtain(t, opts));
|
|
1347
881
|
return () => {
|
|
1348
882
|
for (const lift of lifts) lift();
|
|
1349
883
|
};
|
|
@@ -1398,14 +932,20 @@ function attachLifecycleDispose(result) {
|
|
|
1398
932
|
|
|
1399
933
|
// src/widgets/PdpSectionHeaderLit.ts
|
|
1400
934
|
import { html, LitElement, nothing } from "lit";
|
|
1401
|
-
|
|
935
|
+
|
|
936
|
+
// src/widgets/ensureStylesInRoot.ts
|
|
937
|
+
function ensureStylesInRoot(el, id, css) {
|
|
1402
938
|
if (typeof document === "undefined") return;
|
|
1403
|
-
|
|
1404
|
-
const
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
939
|
+
const root = el.getRootNode();
|
|
940
|
+
const target = root instanceof ShadowRoot ? root : document.head;
|
|
941
|
+
if (target.querySelector(`#${id}`)) return;
|
|
942
|
+
const style = document.createElement("style");
|
|
943
|
+
style.id = id;
|
|
944
|
+
style.textContent = css;
|
|
945
|
+
target.appendChild(style);
|
|
1408
946
|
}
|
|
947
|
+
|
|
948
|
+
// src/widgets/PdpSectionHeaderLit.ts
|
|
1409
949
|
var SECTION_HEADER_CSS = `
|
|
1410
950
|
/* The header renders at the FULL-WIDTH section (band) level so the eyebrow
|
|
1411
951
|
can center across the whole horizontal slice, while the headline below
|
|
@@ -1530,7 +1070,7 @@ var PdpSectionHeaderLit = class extends LitElement {
|
|
|
1530
1070
|
}
|
|
1531
1071
|
connectedCallback() {
|
|
1532
1072
|
super.connectedCallback();
|
|
1533
|
-
|
|
1073
|
+
ensureStylesInRoot(this, "syntro-pdp-section-header-style", SECTION_HEADER_CSS);
|
|
1534
1074
|
}
|
|
1535
1075
|
render() {
|
|
1536
1076
|
const split = this.accent ? splitTitleAt(this.title, this.accent) : null;
|
|
@@ -1562,14 +1102,6 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-sec
|
|
|
1562
1102
|
|
|
1563
1103
|
// src/widgets/PdpSlotLit.ts
|
|
1564
1104
|
import { html as html2, LitElement as LitElement2, nothing as nothing2 } from "lit";
|
|
1565
|
-
function ensureSlotStyles() {
|
|
1566
|
-
if (typeof document === "undefined") return;
|
|
1567
|
-
if (document.getElementById("syntro-pdp-slot-style")) return;
|
|
1568
|
-
const s4 = document.createElement("style");
|
|
1569
|
-
s4.id = "syntro-pdp-slot-style";
|
|
1570
|
-
s4.textContent = SLOT_CSS;
|
|
1571
|
-
document.head.appendChild(s4);
|
|
1572
|
-
}
|
|
1573
1105
|
var SLOT_CSS = `
|
|
1574
1106
|
.syntro-pdp-slot {
|
|
1575
1107
|
--sl-primary: var(--syntro-pdp-primary, hsl(150 32% 38%));
|
|
@@ -1731,7 +1263,7 @@ var PdpSlotLit = class extends LitElement2 {
|
|
|
1731
1263
|
}
|
|
1732
1264
|
connectedCallback() {
|
|
1733
1265
|
super.connectedCallback();
|
|
1734
|
-
|
|
1266
|
+
ensureStylesInRoot(this, "syntro-pdp-slot-style", SLOT_CSS);
|
|
1735
1267
|
}
|
|
1736
1268
|
willUpdate() {
|
|
1737
1269
|
this.dataset.state = this.state;
|
|
@@ -1774,23 +1306,23 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-slo
|
|
|
1774
1306
|
}
|
|
1775
1307
|
|
|
1776
1308
|
// src/takeover/renderModules.ts
|
|
1777
|
-
function parsePlanEntry(
|
|
1778
|
-
const
|
|
1779
|
-
if (
|
|
1780
|
-
return { kind:
|
|
1309
|
+
function parsePlanEntry(entry) {
|
|
1310
|
+
const i = entry.indexOf(":");
|
|
1311
|
+
if (i === -1) return { kind: entry, hostSectionId: null };
|
|
1312
|
+
return { kind: entry.slice(0, i), hostSectionId: entry.slice(i + 1) || null };
|
|
1781
1313
|
}
|
|
1782
1314
|
function createTakeoverModuleRenderer(deps) {
|
|
1783
1315
|
return async (plan, onModuleFailed) => {
|
|
1784
1316
|
const decisionArchetype = deps.getDecisionArchetype();
|
|
1785
1317
|
const row = deps.getPresetRow(decisionArchetype) ?? {};
|
|
1786
1318
|
const bakedWork = [];
|
|
1787
|
-
plan.order.forEach((
|
|
1788
|
-
const { kind, hostSectionId } = parsePlanEntry(
|
|
1319
|
+
plan.order.forEach((entry, index) => {
|
|
1320
|
+
const { kind, hostSectionId } = parsePlanEntry(entry);
|
|
1789
1321
|
const slotId = `pdp-slot-${kind}`;
|
|
1790
1322
|
const preset = row[slotId];
|
|
1791
1323
|
const tag = deps.resolveTag(kind, preset?.variant ?? null);
|
|
1792
1324
|
if (!tag) {
|
|
1793
|
-
deps.telemetry("takeover.module_kind_unknown", { entry
|
|
1325
|
+
deps.telemetry("takeover.module_kind_unknown", { entry, kind });
|
|
1794
1326
|
onModuleFailed(hostSectionId);
|
|
1795
1327
|
return;
|
|
1796
1328
|
}
|
|
@@ -1799,7 +1331,7 @@ function createTakeoverModuleRenderer(deps) {
|
|
|
1799
1331
|
const section = document.createElement("section");
|
|
1800
1332
|
section.toggleAttribute("data-syntro-pdp-section", true);
|
|
1801
1333
|
section.setAttribute("data-slot", slotId);
|
|
1802
|
-
section.setAttribute("data-syntro-takeover-entry",
|
|
1334
|
+
section.setAttribute("data-syntro-takeover-entry", entry);
|
|
1803
1335
|
const header = document.createElement("syntro-pdp-section-header");
|
|
1804
1336
|
header.eyebrow = task.header.eyebrow ?? "";
|
|
1805
1337
|
header.title = task.header.title ?? "";
|
|
@@ -1820,7 +1352,7 @@ function createTakeoverModuleRenderer(deps) {
|
|
|
1820
1352
|
} else {
|
|
1821
1353
|
slotEl.state = "failed";
|
|
1822
1354
|
}
|
|
1823
|
-
deps.telemetry("takeover.module_failed", { entry
|
|
1355
|
+
deps.telemetry("takeover.module_failed", { entry, kind });
|
|
1824
1356
|
onModuleFailed(hostSectionId);
|
|
1825
1357
|
};
|
|
1826
1358
|
const mountPayload = async (payload) => {
|
|
@@ -1857,14 +1389,6 @@ var RELATIONSHIP_LABELS = {
|
|
|
1857
1389
|
complement: "Pairs with this",
|
|
1858
1390
|
alternative: "Alternative"
|
|
1859
1391
|
};
|
|
1860
|
-
function ensureOtherProductTileStyles() {
|
|
1861
|
-
if (typeof document === "undefined") return;
|
|
1862
|
-
if (document.getElementById("syntro-other-product-tile-style")) return;
|
|
1863
|
-
const s4 = document.createElement("style");
|
|
1864
|
-
s4.id = "syntro-other-product-tile-style";
|
|
1865
|
-
s4.textContent = OTHER_PRODUCT_TILE_CSS;
|
|
1866
|
-
document.head.appendChild(s4);
|
|
1867
|
-
}
|
|
1868
1392
|
var OTHER_PRODUCT_TILE_CSS = `
|
|
1869
1393
|
.opt-card {
|
|
1870
1394
|
--opt-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -1909,7 +1433,7 @@ var OTHER_PRODUCT_TILE_CSS = `
|
|
|
1909
1433
|
font-size: 10px;
|
|
1910
1434
|
text-transform: uppercase;
|
|
1911
1435
|
letter-spacing: 0.14em;
|
|
1912
|
-
color: var(--opt-primary);
|
|
1436
|
+
color: var(--syntro-pdp-primary-ink, var(--opt-primary));
|
|
1913
1437
|
font-weight: 600;
|
|
1914
1438
|
display: flex;
|
|
1915
1439
|
align-items: center;
|
|
@@ -1994,64 +1518,107 @@ var OTHER_PRODUCT_TILE_CSS = `
|
|
|
1994
1518
|
background: color-mix(in srgb, var(--opt-primary) 10%, transparent);
|
|
1995
1519
|
}
|
|
1996
1520
|
`;
|
|
1521
|
+
var _unwatch, _OtherProductTileLit_instances, watchPrice_fn;
|
|
1997
1522
|
var OtherProductTileLit = class extends LitElement3 {
|
|
1998
1523
|
constructor() {
|
|
1999
1524
|
super(...arguments);
|
|
1525
|
+
__privateAdd(this, _OtherProductTileLit_instances);
|
|
2000
1526
|
this.tile = null;
|
|
1527
|
+
/** The store's live display price, painted over the catalog price. */
|
|
1528
|
+
this._livePrice = null;
|
|
1529
|
+
__privateAdd(this, _unwatch, null);
|
|
1530
|
+
/**
|
|
1531
|
+
* View asks whoever holds the page to open this product. The event crosses
|
|
1532
|
+
* shadow roots, so a canvas that mounts the page can answer it; on a page
|
|
1533
|
+
* where nothing listens, the button does nothing.
|
|
1534
|
+
*/
|
|
1535
|
+
this._onView = () => {
|
|
1536
|
+
if (!this.tile) return;
|
|
1537
|
+
const { recommended_product_id: productId, name, image_url, price_cents } = this.tile;
|
|
1538
|
+
const price = this._livePrice ?? this.tile.price;
|
|
1539
|
+
this.dispatchEvent(
|
|
1540
|
+
new CustomEvent("syntro:view-product", {
|
|
1541
|
+
bubbles: true,
|
|
1542
|
+
composed: true,
|
|
1543
|
+
detail: { productId, name, image_url, price, price_cents }
|
|
1544
|
+
})
|
|
1545
|
+
);
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
willUpdate(changed) {
|
|
1549
|
+
if (!changed.has("tile")) return;
|
|
1550
|
+
this._livePrice = null;
|
|
1551
|
+
if (this.isConnected) __privateMethod(this, _OtherProductTileLit_instances, watchPrice_fn).call(this);
|
|
1552
|
+
}
|
|
1553
|
+
disconnectedCallback() {
|
|
1554
|
+
var _a;
|
|
1555
|
+
super.disconnectedCallback();
|
|
1556
|
+
(_a = __privateGet(this, _unwatch)) == null ? void 0 : _a.call(this);
|
|
1557
|
+
__privateSet(this, _unwatch, null);
|
|
2001
1558
|
}
|
|
2002
1559
|
createRenderRoot() {
|
|
2003
1560
|
return this;
|
|
2004
1561
|
}
|
|
2005
1562
|
connectedCallback() {
|
|
2006
1563
|
super.connectedCallback();
|
|
2007
|
-
|
|
1564
|
+
ensureStylesInRoot(this, "syntro-other-product-tile-style", OTHER_PRODUCT_TILE_CSS);
|
|
1565
|
+
__privateMethod(this, _OtherProductTileLit_instances, watchPrice_fn).call(this);
|
|
2008
1566
|
}
|
|
2009
1567
|
render() {
|
|
2010
|
-
const
|
|
2011
|
-
if (!
|
|
1568
|
+
const t = this.tile;
|
|
1569
|
+
if (!t) {
|
|
2012
1570
|
return html3``;
|
|
2013
1571
|
}
|
|
2014
|
-
const relationshipLabel = RELATIONSHIP_LABELS[
|
|
2015
|
-
const price =
|
|
1572
|
+
const relationshipLabel = RELATIONSHIP_LABELS[t.relationship];
|
|
1573
|
+
const price = this._livePrice ?? t.price ?? "";
|
|
2016
1574
|
return html3`
|
|
2017
|
-
<article class="opt-card" data-relationship="${
|
|
2018
|
-
${
|
|
2019
|
-
<img src="${
|
|
1575
|
+
<article class="opt-card" data-relationship="${t.relationship}">
|
|
1576
|
+
${t.image_url ? html3`<div class="opt-image">
|
|
1577
|
+
<img src="${t.image_url}" alt="${t.name}" loading="lazy" />
|
|
2020
1578
|
</div>` : null}
|
|
2021
|
-
<div class="opt-relationship" data-relationship="${
|
|
1579
|
+
<div class="opt-relationship" data-relationship="${t.relationship}">
|
|
2022
1580
|
${relationshipLabel ? html3`${relationshipLabel}` : null}
|
|
2023
|
-
<span class="opt-relationship-type">${
|
|
1581
|
+
<span class="opt-relationship-type">${t.relationship}</span>
|
|
2024
1582
|
</div>
|
|
2025
|
-
<h3 class="opt-name">${
|
|
2026
|
-
${
|
|
2027
|
-
${
|
|
2028
|
-
${
|
|
2029
|
-
${
|
|
1583
|
+
<h3 class="opt-name">${t.name}</h3>
|
|
1584
|
+
${t.tagline ? html3`<p class="opt-tagline">${t.tagline}</p>` : null}
|
|
1585
|
+
${t.why_blurb ? html3`<p class="opt-why">${t.why_blurb}</p>` : null}
|
|
1586
|
+
${t.evidence_chips.length ? html3`<div class="opt-chips">
|
|
1587
|
+
${t.evidence_chips.map((c) => html3`<span class="opt-chip">${c.display}</span>`)}
|
|
2030
1588
|
</div>` : null}
|
|
2031
1589
|
<div class="opt-footer">
|
|
2032
1590
|
<span class="opt-price">${price}</span>
|
|
2033
|
-
<button type="button" class="opt-cta" data-action="view-product">
|
|
1591
|
+
<button type="button" class="opt-cta" data-action="view-product" @click=${this._onView}>
|
|
1592
|
+
View
|
|
1593
|
+
</button>
|
|
2034
1594
|
</div>
|
|
2035
1595
|
</article>
|
|
2036
1596
|
`;
|
|
2037
1597
|
}
|
|
2038
1598
|
};
|
|
1599
|
+
_unwatch = new WeakMap();
|
|
1600
|
+
_OtherProductTileLit_instances = new WeakSet();
|
|
1601
|
+
/** The store's live price, now and whenever the runtime publishes a
|
|
1602
|
+
* commerce adapter; no answer keeps the catalog price the server sent. */
|
|
1603
|
+
watchPrice_fn = function() {
|
|
1604
|
+
var _a;
|
|
1605
|
+
(_a = __privateGet(this, _unwatch)) == null ? void 0 : _a.call(this);
|
|
1606
|
+
__privateSet(this, _unwatch, null);
|
|
1607
|
+
const id = this.tile?.recommended_product_id;
|
|
1608
|
+
if (!id || !this.tile?.price) return;
|
|
1609
|
+
__privateSet(this, _unwatch, watchLivePrice(id, (live) => {
|
|
1610
|
+
if (this.tile?.recommended_product_id === id) this._livePrice = live.price;
|
|
1611
|
+
}));
|
|
1612
|
+
};
|
|
2039
1613
|
OtherProductTileLit.properties = {
|
|
2040
|
-
tile: { attribute: false }
|
|
1614
|
+
tile: { attribute: false },
|
|
1615
|
+
_livePrice: { state: true }
|
|
2041
1616
|
};
|
|
2042
1617
|
if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-other-product-tile")) {
|
|
2043
1618
|
customElements.define("syntro-pdp-other-product-tile", OtherProductTileLit);
|
|
2044
1619
|
}
|
|
2045
1620
|
|
|
2046
1621
|
// src/widgets/PdpOtherProductsCarouselLit.ts
|
|
2047
|
-
function ensureCarouselStyles() {
|
|
2048
|
-
if (typeof document === "undefined") return;
|
|
2049
|
-
if (document.getElementById("syntro-pdp-other-products-carousel-style")) return;
|
|
2050
|
-
const s4 = document.createElement("style");
|
|
2051
|
-
s4.id = "syntro-pdp-other-products-carousel-style";
|
|
2052
|
-
s4.textContent = CAROUSEL_CSS;
|
|
2053
|
-
document.head.appendChild(s4);
|
|
2054
|
-
}
|
|
2055
1622
|
var CAROUSEL_CSS = `
|
|
2056
1623
|
/* Host containment \u2014 must not bust the PDP container width. */
|
|
2057
1624
|
syntro-pdp-other-products-carousel {
|
|
@@ -2125,7 +1692,7 @@ var PdpOtherProductsCarouselLit = class extends LitElement4 {
|
|
|
2125
1692
|
}
|
|
2126
1693
|
connectedCallback() {
|
|
2127
1694
|
super.connectedCallback();
|
|
2128
|
-
|
|
1695
|
+
ensureStylesInRoot(this, "syntro-pdp-other-products-carousel-style", CAROUSEL_CSS);
|
|
2129
1696
|
}
|
|
2130
1697
|
render() {
|
|
2131
1698
|
if (!this.data) return html4``;
|
|
@@ -2153,14 +1720,6 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-oth
|
|
|
2153
1720
|
|
|
2154
1721
|
// src/widgets/PdpOtherProductsGridLit.ts
|
|
2155
1722
|
import { html as html5, LitElement as LitElement5 } from "lit";
|
|
2156
|
-
function ensureGridStyles() {
|
|
2157
|
-
if (typeof document === "undefined") return;
|
|
2158
|
-
if (document.getElementById("syntro-pdp-other-products-grid-style")) return;
|
|
2159
|
-
const s4 = document.createElement("style");
|
|
2160
|
-
s4.id = "syntro-pdp-other-products-grid-style";
|
|
2161
|
-
s4.textContent = GRID_CSS;
|
|
2162
|
-
document.head.appendChild(s4);
|
|
2163
|
-
}
|
|
2164
1723
|
var GRID_CSS = `
|
|
2165
1724
|
syntro-pdp-other-products-grid {
|
|
2166
1725
|
display: block;
|
|
@@ -2197,7 +1756,7 @@ var PdpOtherProductsGridLit = class extends LitElement5 {
|
|
|
2197
1756
|
}
|
|
2198
1757
|
connectedCallback() {
|
|
2199
1758
|
super.connectedCallback();
|
|
2200
|
-
|
|
1759
|
+
ensureStylesInRoot(this, "syntro-pdp-other-products-grid-style", GRID_CSS);
|
|
2201
1760
|
}
|
|
2202
1761
|
render() {
|
|
2203
1762
|
if (!this.data) return html5``;
|
|
@@ -2235,7 +1794,7 @@ function _readPdpDecisionArchetypes() {
|
|
|
2235
1794
|
if (typeof window === "undefined") return void 0;
|
|
2236
1795
|
return window.__SYNTRO_PDP_DECISION_ARCHETYPES__;
|
|
2237
1796
|
}
|
|
2238
|
-
async function composeModule(authedFetch, args) {
|
|
1797
|
+
async function composeModule(authedFetch, args, signal) {
|
|
2239
1798
|
try {
|
|
2240
1799
|
const requestBody = {
|
|
2241
1800
|
product_name: args.productName,
|
|
@@ -2254,7 +1813,8 @@ async function composeModule(authedFetch, args) {
|
|
|
2254
1813
|
};
|
|
2255
1814
|
const resp = await authedFetch("/api/pdp/module", {
|
|
2256
1815
|
method: "POST",
|
|
2257
|
-
body: JSON.stringify(requestBody)
|
|
1816
|
+
body: JSON.stringify(requestBody),
|
|
1817
|
+
signal
|
|
2258
1818
|
});
|
|
2259
1819
|
if (!resp.ok) return null;
|
|
2260
1820
|
const data = await resp.json();
|
|
@@ -2292,33 +1852,33 @@ function _titleCase(slug) {
|
|
|
2292
1852
|
return slug.split(/[-_\s]+/).map((w) => w ? w[0].toUpperCase() + w.slice(1) : "").join(" ");
|
|
2293
1853
|
}
|
|
2294
1854
|
function _countSignals(signals) {
|
|
2295
|
-
let
|
|
1855
|
+
let n = 0;
|
|
2296
1856
|
const walk = (v) => {
|
|
2297
1857
|
if (v && typeof v === "object") {
|
|
2298
1858
|
for (const x of Object.values(v)) walk(x);
|
|
2299
1859
|
} else if (v !== "" && v != null) {
|
|
2300
|
-
|
|
1860
|
+
n++;
|
|
2301
1861
|
}
|
|
2302
1862
|
};
|
|
2303
1863
|
walk(signals);
|
|
2304
|
-
return
|
|
1864
|
+
return n;
|
|
2305
1865
|
}
|
|
2306
1866
|
function _signalDimensions(signals) {
|
|
2307
|
-
let
|
|
1867
|
+
let n = 0;
|
|
2308
1868
|
for (const v of Object.values(signals)) {
|
|
2309
1869
|
if (v && typeof v === "object") {
|
|
2310
|
-
if (Object.keys(v).length)
|
|
1870
|
+
if (Object.keys(v).length) n++;
|
|
2311
1871
|
} else if (v !== "" && v != null && v !== false) {
|
|
2312
|
-
|
|
1872
|
+
n++;
|
|
2313
1873
|
}
|
|
2314
1874
|
}
|
|
2315
|
-
return
|
|
1875
|
+
return n;
|
|
2316
1876
|
}
|
|
2317
1877
|
function _fingerprint(signals) {
|
|
2318
|
-
const
|
|
1878
|
+
const s = JSON.stringify(signals);
|
|
2319
1879
|
let h = 2166136261;
|
|
2320
|
-
for (let
|
|
2321
|
-
h ^=
|
|
1880
|
+
for (let i = 0; i < s.length; i++) {
|
|
1881
|
+
h ^= s.charCodeAt(i);
|
|
2322
1882
|
h = Math.imul(h, 16777619);
|
|
2323
1883
|
}
|
|
2324
1884
|
return (h >>> 0).toString(16).padStart(8, "0");
|
|
@@ -2416,9 +1976,9 @@ function buildExplain(opts) {
|
|
|
2416
1976
|
dossier
|
|
2417
1977
|
};
|
|
2418
1978
|
}
|
|
2419
|
-
async function fetchDossier(authedFetch) {
|
|
1979
|
+
async function fetchDossier(authedFetch, signal) {
|
|
2420
1980
|
try {
|
|
2421
|
-
const resp = await authedFetch("/api/pdp/dossier", { method: "POST" });
|
|
1981
|
+
const resp = await authedFetch("/api/pdp/dossier", { method: "POST", signal });
|
|
2422
1982
|
if (!resp.ok) return [];
|
|
2423
1983
|
const data = await resp.json();
|
|
2424
1984
|
const rows = data.metrics;
|
|
@@ -2558,8 +2118,8 @@ var _PAGE_TRAIL_MAX_CHARS = 600;
|
|
|
2558
2118
|
function _renderPageTrail(history) {
|
|
2559
2119
|
const paths = [];
|
|
2560
2120
|
let length = 0;
|
|
2561
|
-
for (let
|
|
2562
|
-
let path = history[
|
|
2121
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
2122
|
+
let path = history[i].url;
|
|
2563
2123
|
try {
|
|
2564
2124
|
const u = new URL(path);
|
|
2565
2125
|
path = `${u.pathname}${u.search}`;
|
|
@@ -2572,14 +2132,6 @@ function _renderPageTrail(history) {
|
|
|
2572
2132
|
}
|
|
2573
2133
|
return paths.join(" \u2192 ");
|
|
2574
2134
|
}
|
|
2575
|
-
function ensureSectionLayoutStyles() {
|
|
2576
|
-
if (typeof document === "undefined") return;
|
|
2577
|
-
if (document.getElementById("syntro-pdp-layout-style")) return;
|
|
2578
|
-
const s4 = document.createElement("style");
|
|
2579
|
-
s4.id = "syntro-pdp-layout-style";
|
|
2580
|
-
s4.textContent = SECTION_LAYOUT_CSS;
|
|
2581
|
-
document.head.appendChild(s4);
|
|
2582
|
-
}
|
|
2583
2135
|
var SECTION_LAYOUT_CSS = `
|
|
2584
2136
|
[data-syntro-pdp-root] {
|
|
2585
2137
|
--sc-pdp-max-w: var(--syntro-pdp-max-width, 1080px);
|
|
@@ -2760,6 +2312,7 @@ function resolveTagForMount(inst) {
|
|
|
2760
2312
|
}
|
|
2761
2313
|
return null;
|
|
2762
2314
|
}
|
|
2315
|
+
var _abort;
|
|
2763
2316
|
var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
2764
2317
|
constructor() {
|
|
2765
2318
|
super(...arguments);
|
|
@@ -2840,6 +2393,15 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2840
2393
|
* after reconnect must undo that before the engine renders again. */
|
|
2841
2394
|
this._takeoverRanBefore = false;
|
|
2842
2395
|
this._takeoverLateWarned = false;
|
|
2396
|
+
/**
|
|
2397
|
+
* Cancels in-flight /api/pdp/module + /api/pdp/dossier calls when the
|
|
2398
|
+
* element is removed mid-request — a canvas card can be torn down while a
|
|
2399
|
+
* fetch is still pending, and an unaborted response landing after teardown
|
|
2400
|
+
* would apply a mutation/mount to a disconnected element. Reset to a fresh
|
|
2401
|
+
* controller on (re)connect so a re-added element gets a live signal
|
|
2402
|
+
* instead of one that's already aborted from the previous lifecycle.
|
|
2403
|
+
*/
|
|
2404
|
+
__privateAdd(this, _abort, new AbortController());
|
|
2843
2405
|
/**
|
|
2844
2406
|
* bfcache re-entry (review F4). pagehide disposes the run
|
|
2845
2407
|
* (attachLifecycleDispose) but a bfcache restore does NOT re-fire
|
|
@@ -2872,8 +2434,8 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2872
2434
|
warn: (code, props) => this._warnDecision(code, props),
|
|
2873
2435
|
cleanup: () => this._cleanupDecisionEffects()
|
|
2874
2436
|
});
|
|
2875
|
-
this._onDossierToggle = (
|
|
2876
|
-
const open =
|
|
2437
|
+
this._onDossierToggle = (e) => {
|
|
2438
|
+
const open = e.target.open;
|
|
2877
2439
|
this._dispatchDecision({ type: "dossier-toggled", open });
|
|
2878
2440
|
};
|
|
2879
2441
|
/** Stamps data-viewport on this tag (central classification,
|
|
@@ -2909,6 +2471,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2909
2471
|
return this;
|
|
2910
2472
|
}
|
|
2911
2473
|
connectedCallback() {
|
|
2474
|
+
if (__privateGet(this, _abort).signal.aborted) __privateSet(this, _abort, new AbortController());
|
|
2912
2475
|
if (!this._takeoverModeLatched) {
|
|
2913
2476
|
this._takeoverModeLatched = true;
|
|
2914
2477
|
this._takeoverMode = Boolean(this.takeover);
|
|
@@ -2920,7 +2483,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2920
2483
|
}
|
|
2921
2484
|
if (this._takeoverMode) {
|
|
2922
2485
|
super.connectedCallback();
|
|
2923
|
-
|
|
2486
|
+
ensureStylesInRoot(this, "syntro-pdp-layout-style", SECTION_LAYOUT_CSS);
|
|
2924
2487
|
if (typeof window !== "undefined") {
|
|
2925
2488
|
window.removeEventListener("pageshow", this._onTakeoverPageShow);
|
|
2926
2489
|
window.addEventListener("pageshow", this._onTakeoverPageShow);
|
|
@@ -2930,7 +2493,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2930
2493
|
}
|
|
2931
2494
|
const reconnecting = this._decisionState.connection === "disconnected";
|
|
2932
2495
|
super.connectedCallback();
|
|
2933
|
-
|
|
2496
|
+
ensureStylesInRoot(this, "syntro-pdp-layout-style", SECTION_LAYOUT_CSS);
|
|
2934
2497
|
if (!this.productId) this._selfIdentify = true;
|
|
2935
2498
|
this._refreshNativeSlots();
|
|
2936
2499
|
this._syncDecisionRuntime(reconnecting);
|
|
@@ -2944,6 +2507,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2944
2507
|
}
|
|
2945
2508
|
}
|
|
2946
2509
|
disconnectedCallback() {
|
|
2510
|
+
__privateGet(this, _abort).abort();
|
|
2947
2511
|
if (this._takeoverMode) {
|
|
2948
2512
|
super.disconnectedCallback();
|
|
2949
2513
|
if (typeof window !== "undefined") {
|
|
@@ -3145,24 +2709,28 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3145
2709
|
}),
|
|
3146
2710
|
requestLiveModule: async (kind, decisionArchetype) => {
|
|
3147
2711
|
if (!authedFetch) return null;
|
|
3148
|
-
return composeModule(
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
slug:
|
|
3161
|
-
|
|
2712
|
+
return composeModule(
|
|
2713
|
+
authedFetch,
|
|
2714
|
+
{
|
|
2715
|
+
productName: this.productName,
|
|
2716
|
+
// Guaranteed non-empty: _startTakeover gates on a missing product id.
|
|
2717
|
+
productId: this.productId,
|
|
2718
|
+
kind,
|
|
2719
|
+
variant: null,
|
|
2720
|
+
// The takeover round trip scores only the decision axis (the server
|
|
2721
|
+
// resolves the plan from it); the sport-archetype axis is unscored
|
|
2722
|
+
// on this path, so it is sent as an explicit 'unknown' — never a
|
|
2723
|
+
// silently-invented value.
|
|
2724
|
+
archetype: { slug: "unknown", description: "" },
|
|
2725
|
+
decisionArchetype: decisionArchetype === "unknown" ? null : {
|
|
2726
|
+
slug: decisionArchetype,
|
|
2727
|
+
description: decisionDescriptions[decisionArchetype] ?? ""
|
|
2728
|
+
},
|
|
2729
|
+
behaviorSummary: "",
|
|
2730
|
+
signals
|
|
3162
2731
|
},
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
});
|
|
2732
|
+
__privateGet(this, _abort).signal
|
|
2733
|
+
);
|
|
3166
2734
|
},
|
|
3167
2735
|
telemetry: telemetry2,
|
|
3168
2736
|
getDecisionArchetype: () => scored.decisionArchetype
|
|
@@ -3329,13 +2897,35 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3329
2897
|
}
|
|
3330
2898
|
container.innerHTML = "";
|
|
3331
2899
|
const el = document.createElement(tag);
|
|
3332
|
-
el.data = inst.content;
|
|
2900
|
+
el.data = this._withStoreImages(tag, inst.content);
|
|
3333
2901
|
container.appendChild(el);
|
|
3334
2902
|
this._mountDurations.set(slotId, Math.round(performance.now() - slotStartedAt));
|
|
3335
2903
|
outcomes.push({ status: "mounted", slot: slotId });
|
|
3336
2904
|
}
|
|
3337
2905
|
return outcomes;
|
|
3338
2906
|
}
|
|
2907
|
+
/**
|
|
2908
|
+
* The cross-sell tiles' images are store-relative catalog paths. Inside an
|
|
2909
|
+
* app host's card the document is the host's sandbox and <base> is blocked,
|
|
2910
|
+
* so each is resolved against the runtime's storefront before the carousel
|
|
2911
|
+
* or grid gets it. On a storefront page there is no storefrontUrl and
|
|
2912
|
+
* nothing changes.
|
|
2913
|
+
*/
|
|
2914
|
+
_withStoreImages(tag, content) {
|
|
2915
|
+
if (tag !== "syntro-pdp-other-products-carousel" && tag !== "syntro-pdp-other-products-grid")
|
|
2916
|
+
return content;
|
|
2917
|
+
const tiles = content?.tiles;
|
|
2918
|
+
if (!Array.isArray(tiles)) return content;
|
|
2919
|
+
return {
|
|
2920
|
+
...content,
|
|
2921
|
+
tiles: tiles.map(
|
|
2922
|
+
(tile) => tile && typeof tile === "object" && "image_url" in tile ? {
|
|
2923
|
+
...tile,
|
|
2924
|
+
image_url: resolveStorePath(tile.image_url, this.runtimeRef)
|
|
2925
|
+
} : tile
|
|
2926
|
+
)
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
3339
2929
|
/** Render from config: score the archetype → mount the preset set-values. */
|
|
3340
2930
|
async _fireFromConfig(productPresets, score, authedFetch, generation, run, scoreMulti) {
|
|
3341
2931
|
const startedAt = performance.now();
|
|
@@ -3353,7 +2943,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3353
2943
|
let scoredArgs = null;
|
|
3354
2944
|
const composeAdapter = createPdpComposeAdapter({
|
|
3355
2945
|
scoreSummary: () => authedFetch ? score({ model_id: "summary", name: "behavior_summary", type: "string", signals }) : Promise.resolve("unknown"),
|
|
3356
|
-
requestDossier: () => authedFetch ? fetchDossier(authedFetch) : Promise.resolve([]),
|
|
2946
|
+
requestDossier: () => authedFetch ? fetchDossier(authedFetch, __privateGet(this, _abort).signal) : Promise.resolve([]),
|
|
3357
2947
|
resolveTemplate: (_currentGeneration, composeUnset) => resolveScoredTemplate({
|
|
3358
2948
|
// Wrap the scorer so the resolved SPORT archetype lands on the
|
|
3359
2949
|
// element the instant it scores — the native-takeover check needs it
|
|
@@ -3441,22 +3031,26 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3441
3031
|
requestModule: async ({ slot, archetype, decisionArchetype, behaviorSummary }) => {
|
|
3442
3032
|
if (!authedFetch || !run.isCurrent()) return;
|
|
3443
3033
|
const kind = slot.replace("pdp-slot-", "");
|
|
3444
|
-
const payload = await composeModule(
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3034
|
+
const payload = await composeModule(
|
|
3035
|
+
authedFetch,
|
|
3036
|
+
{
|
|
3037
|
+
productName: this.productName,
|
|
3038
|
+
productId: this.productId || null,
|
|
3039
|
+
kind,
|
|
3040
|
+
variant: null,
|
|
3041
|
+
archetype: {
|
|
3042
|
+
slug: archetype,
|
|
3043
|
+
description: archetypeDescriptions[archetype] ?? ""
|
|
3044
|
+
},
|
|
3045
|
+
decisionArchetype: decisionArchetype === "unknown" ? null : {
|
|
3046
|
+
slug: decisionArchetype,
|
|
3047
|
+
description: decisionDescriptions[decisionArchetype] ?? ""
|
|
3048
|
+
},
|
|
3049
|
+
behaviorSummary,
|
|
3050
|
+
signals
|
|
3452
3051
|
},
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
description: decisionDescriptions[decisionArchetype] ?? ""
|
|
3456
|
-
},
|
|
3457
|
-
behaviorSummary,
|
|
3458
|
-
signals
|
|
3459
|
-
});
|
|
3052
|
+
__privateGet(this, _abort).signal
|
|
3053
|
+
);
|
|
3460
3054
|
if (!run.isCurrent()) return;
|
|
3461
3055
|
if (!payload) {
|
|
3462
3056
|
run.dispatch({ type: "module-failed", generation, slot });
|
|
@@ -3582,7 +3176,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3582
3176
|
_renderWhyBox(view) {
|
|
3583
3177
|
if (view.status === "hidden") return "";
|
|
3584
3178
|
if (view.status === "loading") return this._renderWhyBoxLoading();
|
|
3585
|
-
const
|
|
3179
|
+
const e = view.explanation;
|
|
3586
3180
|
return html6`
|
|
3587
3181
|
<style>
|
|
3588
3182
|
/* Token bridge: every color resolves from a --syntro-pdp-* var the
|
|
@@ -3624,7 +3218,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3624
3218
|
[data-syntro-pdp-why][open] > summary { margin-bottom: 6px; }
|
|
3625
3219
|
[data-syntro-pdp-why-note] { font-size: 11px; color: var(--syntro-pdp-muted, #666); margin: 0 0 4px; }
|
|
3626
3220
|
[data-syntro-pdp-why-archetype] { font-size: 13px; margin: 3px 0; color: var(--syntro-pdp-fg, #1a1a1a); }
|
|
3627
|
-
[data-syntro-pdp-why-archetype] strong { color: var(--syntro-pdp-primary, #1a1a1a); }
|
|
3221
|
+
[data-syntro-pdp-why-archetype] strong { color: var(--syntro-pdp-primary-ink, var(--syntro-pdp-primary, #1a1a1a)); }
|
|
3628
3222
|
[data-syntro-pdp-why-summary] {
|
|
3629
3223
|
font-size: 12.5px; font-style: italic; color: var(--syntro-pdp-muted, #666); margin: 6px 0 8px;
|
|
3630
3224
|
border-left: 2px solid var(--syntro-pdp-accent, #555); padding-left: 10px;
|
|
@@ -3670,14 +3264,14 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3670
3264
|
Shown in demo mode only — a peek under the hood at the live personalization.
|
|
3671
3265
|
</div>
|
|
3672
3266
|
<div data-syntro-pdp-why-archetype>
|
|
3673
|
-
Shopping as: <strong>${
|
|
3267
|
+
Shopping as: <strong>${e.archetypeLabel}</strong>${e.archetypeDesc ? html6` — ${e.archetypeDesc}` : ""}
|
|
3674
3268
|
</div>
|
|
3675
|
-
${
|
|
3676
|
-
What they're weighing: <strong>${
|
|
3269
|
+
${e.decisionLabel ? html6`<div data-syntro-pdp-why-archetype>
|
|
3270
|
+
What they're weighing: <strong>${e.decisionLabel}</strong>${e.decisionDesc ? html6` — “${e.decisionDesc}”` : ""}
|
|
3677
3271
|
</div>` : ""}
|
|
3678
|
-
${
|
|
3272
|
+
${e.summary ? html6`<div data-syntro-pdp-why-summary>"${e.summary}"</div>` : ""}
|
|
3679
3273
|
<div data-syntro-pdp-why-metrics>
|
|
3680
|
-
${
|
|
3274
|
+
${e.metrics.map(
|
|
3681
3275
|
(m) => html6`<div data-syntro-pdp-why-metric>
|
|
3682
3276
|
<span data-syntro-pdp-why-metric-value>${m.value}</span>
|
|
3683
3277
|
<span data-syntro-pdp-why-metric-label>${m.label}</span>
|
|
@@ -3760,10 +3354,10 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3760
3354
|
${skeletonStyles}
|
|
3761
3355
|
<div data-syntro-pdp-root data-state="pending" data-syntro-pdp-skeleton>
|
|
3762
3356
|
${[0, 1, 2].map(
|
|
3763
|
-
(
|
|
3357
|
+
(i) => html6`
|
|
3764
3358
|
<section
|
|
3765
3359
|
data-syntro-pdp-skeleton-section
|
|
3766
|
-
style="animation-delay: ${
|
|
3360
|
+
style="animation-delay: ${i * 0.15}s"
|
|
3767
3361
|
>
|
|
3768
3362
|
<div data-syntro-pdp-skeleton-header>
|
|
3769
3363
|
<div data-syntro-pdp-skeleton-eyebrow></div>
|
|
@@ -3820,6 +3414,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3820
3414
|
`;
|
|
3821
3415
|
}
|
|
3822
3416
|
};
|
|
3417
|
+
_abort = new WeakMap();
|
|
3823
3418
|
_SyntroPdpLit.properties = {
|
|
3824
3419
|
productId: { type: String, attribute: "product-id" },
|
|
3825
3420
|
productName: { type: String, attribute: "product-name" },
|
|
@@ -3833,9 +3428,9 @@ _SyntroPdpLit.properties = {
|
|
|
3833
3428
|
/**
|
|
3834
3429
|
* Self-contained skeleton (shimmer) CSS. Rendered inside the component
|
|
3835
3430
|
* during the pre-plan / streaming phase. The global section-layout +
|
|
3836
|
-
* per-slot accent rules live in
|
|
3837
|
-
*
|
|
3838
|
-
*
|
|
3431
|
+
* per-slot accent rules live in `SECTION_LAYOUT_CSS`, injected once per
|
|
3432
|
+
* root via `ensureStylesInRoot`, not here, so they're emitted once per
|
|
3433
|
+
* root rather than once per `<syntro-pdp>` instance.
|
|
3839
3434
|
*/
|
|
3840
3435
|
_SyntroPdpLit._skeletonStyles = html6`
|
|
3841
3436
|
<style>
|
|
@@ -4258,14 +3853,14 @@ function collectFromNode(node) {
|
|
|
4258
3853
|
const containers = [];
|
|
4259
3854
|
if (node.matches(`[${NATIVE_SLOT_ATTR}]`)) containers.push(node);
|
|
4260
3855
|
containers.push(...node.querySelectorAll(`[${NATIVE_SLOT_ATTR}]`));
|
|
4261
|
-
for (const
|
|
4262
|
-
if (
|
|
4263
|
-
const pid = nativeProductId(
|
|
3856
|
+
for (const c of containers) {
|
|
3857
|
+
if (c.closest("syntro-pdp")) continue;
|
|
3858
|
+
const pid = nativeProductId(c);
|
|
4264
3859
|
if (pid) pending.add(pid);
|
|
4265
3860
|
}
|
|
4266
3861
|
}
|
|
4267
|
-
var onDemoToggle = (
|
|
4268
|
-
const detail =
|
|
3862
|
+
var onDemoToggle = (e) => {
|
|
3863
|
+
const detail = e.detail;
|
|
4269
3864
|
if (!detail || typeof detail.on !== "boolean") return;
|
|
4270
3865
|
toggleOn = detail.on;
|
|
4271
3866
|
renderSession();
|
|
@@ -4341,9 +3936,9 @@ async function resolveDomSelectorImage(input, _signal) {
|
|
|
4341
3936
|
// src/bind/jsonld.ts
|
|
4342
3937
|
function isProduct(node) {
|
|
4343
3938
|
if (typeof node !== "object" || node === null) return false;
|
|
4344
|
-
const
|
|
4345
|
-
if (typeof
|
|
4346
|
-
if (Array.isArray(
|
|
3939
|
+
const t = node["@type"];
|
|
3940
|
+
if (typeof t === "string") return t === "Product";
|
|
3941
|
+
if (Array.isArray(t)) return t.includes("Product");
|
|
4347
3942
|
return false;
|
|
4348
3943
|
}
|
|
4349
3944
|
function parseScripts(scripts) {
|
|
@@ -4358,7 +3953,7 @@ function parseScripts(scripts) {
|
|
|
4358
3953
|
while (stack.length) {
|
|
4359
3954
|
const cur = stack.pop();
|
|
4360
3955
|
if (Array.isArray(cur)) {
|
|
4361
|
-
for (const
|
|
3956
|
+
for (const c of cur) stack.push(c);
|
|
4362
3957
|
continue;
|
|
4363
3958
|
}
|
|
4364
3959
|
if (isProduct(cur)) return cur;
|
|
@@ -4563,14 +4158,15 @@ async function resolveImageBind(input, signal) {
|
|
|
4563
4158
|
}
|
|
4564
4159
|
|
|
4565
4160
|
// src/bind/controller.ts
|
|
4566
|
-
var
|
|
4161
|
+
var _abort2, _pending, _unwatch2, _destroyed, _BindController_instances, resolveImage_fn, resolvePrice_fn;
|
|
4567
4162
|
var BindController = class {
|
|
4568
4163
|
constructor(products, callback) {
|
|
4569
4164
|
this.products = products;
|
|
4570
4165
|
this.callback = callback;
|
|
4571
4166
|
__privateAdd(this, _BindController_instances);
|
|
4572
|
-
__privateAdd(this,
|
|
4167
|
+
__privateAdd(this, _abort2, new AbortController());
|
|
4573
4168
|
__privateAdd(this, _pending, []);
|
|
4169
|
+
__privateAdd(this, _unwatch2, []);
|
|
4574
4170
|
__privateAdd(this, _destroyed, false);
|
|
4575
4171
|
}
|
|
4576
4172
|
start() {
|
|
@@ -4580,6 +4176,15 @@ var BindController = class {
|
|
|
4580
4176
|
}
|
|
4581
4177
|
if (product.price?.bind) {
|
|
4582
4178
|
__privateGet(this, _pending).push(__privateMethod(this, _BindController_instances, resolvePrice_fn).call(this, product.id, product.price.bind));
|
|
4179
|
+
} else if (product.price || product.salePrice) {
|
|
4180
|
+
__privateGet(this, _unwatch2).push(
|
|
4181
|
+
watchLivePrice(product.id, (live) => {
|
|
4182
|
+
if (__privateGet(this, _destroyed)) return;
|
|
4183
|
+
this.callback(product.id, "price", live.price);
|
|
4184
|
+
this.callback(product.id, "compare_at", live.compareAt);
|
|
4185
|
+
})
|
|
4186
|
+
);
|
|
4187
|
+
__privateGet(this, _pending).push(fetchLivePrice(product.id).then(() => void 0));
|
|
4583
4188
|
}
|
|
4584
4189
|
}
|
|
4585
4190
|
}
|
|
@@ -4589,23 +4194,42 @@ var BindController = class {
|
|
|
4589
4194
|
destroy() {
|
|
4590
4195
|
if (__privateGet(this, _destroyed)) return;
|
|
4591
4196
|
__privateSet(this, _destroyed, true);
|
|
4592
|
-
__privateGet(this,
|
|
4197
|
+
__privateGet(this, _abort2).abort();
|
|
4198
|
+
for (const stop of __privateGet(this, _unwatch2)) stop();
|
|
4199
|
+
__privateSet(this, _unwatch2, []);
|
|
4593
4200
|
}
|
|
4594
4201
|
};
|
|
4595
|
-
|
|
4202
|
+
_abort2 = new WeakMap();
|
|
4596
4203
|
_pending = new WeakMap();
|
|
4204
|
+
_unwatch2 = new WeakMap();
|
|
4597
4205
|
_destroyed = new WeakMap();
|
|
4598
4206
|
_BindController_instances = new WeakSet();
|
|
4599
4207
|
resolveImage_fn = async function(productId, bind) {
|
|
4600
|
-
const value = await resolveImageBind(bind, __privateGet(this,
|
|
4208
|
+
const value = await resolveImageBind(bind, __privateGet(this, _abort2).signal);
|
|
4601
4209
|
if (__privateGet(this, _destroyed)) return;
|
|
4602
4210
|
this.callback(productId, "image", value);
|
|
4603
4211
|
};
|
|
4604
4212
|
resolvePrice_fn = async function(productId, bind) {
|
|
4605
|
-
const value = await resolvePriceBind(bind, __privateGet(this,
|
|
4213
|
+
const value = await resolvePriceBind(bind, __privateGet(this, _abort2).signal);
|
|
4606
4214
|
if (__privateGet(this, _destroyed)) return;
|
|
4607
4215
|
this.callback(productId, "price", value);
|
|
4608
4216
|
};
|
|
4217
|
+
function mergeResolved(resolved, productId, kind, value) {
|
|
4218
|
+
if (kind === "compare_at") {
|
|
4219
|
+
return { ...resolved, [productId]: { ...resolved[productId], compareAt: value } };
|
|
4220
|
+
}
|
|
4221
|
+
if (value == null) return resolved;
|
|
4222
|
+
return { ...resolved, [productId]: { ...resolved[productId], [kind]: value } };
|
|
4223
|
+
}
|
|
4224
|
+
function keepResolved(resolved, products) {
|
|
4225
|
+
const kept = {};
|
|
4226
|
+
for (const product of products) {
|
|
4227
|
+
const entry = resolved[product.id];
|
|
4228
|
+
if (!entry || product.price?.bind || !("compareAt" in entry)) continue;
|
|
4229
|
+
kept[product.id] = { price: entry.price, compareAt: entry.compareAt };
|
|
4230
|
+
}
|
|
4231
|
+
return kept;
|
|
4232
|
+
}
|
|
4609
4233
|
|
|
4610
4234
|
// src/decision/card.ts
|
|
4611
4235
|
var VARIANT_ERROR_COPY = {
|
|
@@ -4627,9 +4251,11 @@ function createInitialCardState(generation = 0) {
|
|
|
4627
4251
|
variants: { status: "idle" },
|
|
4628
4252
|
cart: { status: "idle" },
|
|
4629
4253
|
selection: {},
|
|
4630
|
-
activation: { status: "idle" }
|
|
4254
|
+
activation: { status: "idle" },
|
|
4255
|
+
optionsPinned: false
|
|
4631
4256
|
};
|
|
4632
4257
|
}
|
|
4258
|
+
var optionsOpen = (s) => s.face === "back" || s.optionsPinned;
|
|
4633
4259
|
function selectCardView(state) {
|
|
4634
4260
|
const primaryCta = state.cart.status === "added" ? {
|
|
4635
4261
|
mode: "view-cart",
|
|
@@ -4639,7 +4265,7 @@ function selectCardView(state) {
|
|
|
4639
4265
|
disabled: state.connection !== "connected" || state.commerce !== "available" || state.product.status !== "valid"
|
|
4640
4266
|
};
|
|
4641
4267
|
let panel = { status: "hidden", payload: null, error: null, retryable: false };
|
|
4642
|
-
if (state
|
|
4268
|
+
if (optionsOpen(state)) {
|
|
4643
4269
|
switch (state.variants.status) {
|
|
4644
4270
|
case "ready":
|
|
4645
4271
|
panel = { status: "ready", payload: state.variants.payload, error: null, retryable: false };
|
|
@@ -4684,7 +4310,11 @@ function replaceProps(state, event) {
|
|
|
4684
4310
|
return { state, effects: effects2 };
|
|
4685
4311
|
}
|
|
4686
4312
|
const generation = state.generation + 1;
|
|
4687
|
-
const next = {
|
|
4313
|
+
const next = {
|
|
4314
|
+
...createInitialCardState(generation),
|
|
4315
|
+
commerce: state.commerce,
|
|
4316
|
+
optionsPinned: state.optionsPinned
|
|
4317
|
+
};
|
|
4688
4318
|
const effects = [{ type: "cancel-flip-settle" }];
|
|
4689
4319
|
if (!event.valid || event.productId === null) {
|
|
4690
4320
|
return {
|
|
@@ -4717,7 +4347,13 @@ function activatePrimary(state, event) {
|
|
|
4717
4347
|
// CTA is a plain navigating <a>, never dispatched here) — but the state
|
|
4718
4348
|
// machine itself must also refuse to start the Woo/Shopify variant-
|
|
4719
4349
|
// lookup flow for a product that has none.
|
|
4720
|
-
!state.product.commerceCapable || state.face !== "front")
|
|
4350
|
+
!state.product.commerceCapable || state.face !== "front" || // A pinned (double-wide) card has no primary CTA to activate — Add to
|
|
4351
|
+
// cart lives in the options panel beside the front face. Without this
|
|
4352
|
+
// guard a stray primary-activated dispatch after an add (cart status
|
|
4353
|
+
// 'added', face still 'front') would fall into the view-cart branch below
|
|
4354
|
+
// and emit close-sdk/open-cart/view_cart_clicked, which a pinned card must
|
|
4355
|
+
// never do (Task 1 keeps the card pinned open across the add).
|
|
4356
|
+
state.optionsPinned) {
|
|
4721
4357
|
return unchanged2(state);
|
|
4722
4358
|
}
|
|
4723
4359
|
const productId = state.product.id;
|
|
@@ -4785,7 +4421,7 @@ function serializeSelection(selection) {
|
|
|
4785
4421
|
};
|
|
4786
4422
|
}
|
|
4787
4423
|
function activateVariantAdd(state, event) {
|
|
4788
|
-
if (state.connection !== "connected" || state.commerce !== "available" || state.product.status !== "valid" || state
|
|
4424
|
+
if (state.connection !== "connected" || state.commerce !== "available" || state.product.status !== "valid" || !optionsOpen(state) || state.variants.status !== "ready" || state.cart.status === "pending" || state.cart.status === "added") {
|
|
4789
4425
|
return unchanged2(state);
|
|
4790
4426
|
}
|
|
4791
4427
|
const variant = state.variants.payload.variants.find(
|
|
@@ -4844,6 +4480,9 @@ function transitionCard(state, event) {
|
|
|
4844
4480
|
const commerce = event.available ? "available" : "unavailable";
|
|
4845
4481
|
return commerce === state.commerce ? unchanged2(state) : { state: { ...state, commerce }, effects: [] };
|
|
4846
4482
|
}
|
|
4483
|
+
if (event.type === "options-pinned") {
|
|
4484
|
+
return event.pinned === state.optionsPinned ? unchanged2(state) : { state: { ...state, optionsPinned: event.pinned }, effects: [] };
|
|
4485
|
+
}
|
|
4847
4486
|
if ((event.type === "variants-resolved" || event.type === "variants-failed" || event.type === "add-succeeded" || event.type === "add-failed") && (event.generation !== state.generation || state.connection !== "connected" || state.product.status !== "valid")) {
|
|
4848
4487
|
return unchanged2(state);
|
|
4849
4488
|
}
|
|
@@ -4884,7 +4523,7 @@ function transitionCard(state, event) {
|
|
|
4884
4523
|
return { state: resumed.state, effects: [failureEffect, ...resumed.effects] };
|
|
4885
4524
|
}
|
|
4886
4525
|
case "selection-changed":
|
|
4887
|
-
if (state.commerce !== "available" || state
|
|
4526
|
+
if (state.commerce !== "available" || !optionsOpen(state) || state.variants.status !== "ready") {
|
|
4888
4527
|
return unchanged2(state);
|
|
4889
4528
|
}
|
|
4890
4529
|
return {
|
|
@@ -5109,9 +4748,9 @@ function createCardEffectExecutor(ports) {
|
|
|
5109
4748
|
var VIEW_DETAIL_ACTION_ID = "view_detail";
|
|
5110
4749
|
function pickDetailCta(ctas) {
|
|
5111
4750
|
if (!ctas?.length) return void 0;
|
|
5112
|
-
const navigates = ctas.filter((
|
|
4751
|
+
const navigates = ctas.filter((c) => !!c.href);
|
|
5113
4752
|
if (!navigates.length) return void 0;
|
|
5114
|
-
return navigates.find((
|
|
4753
|
+
return navigates.find((c) => c.actionId === VIEW_DETAIL_ACTION_ID) ?? navigates.find((c) => c.variant && c.variant !== "primary") ?? navigates[0];
|
|
5115
4754
|
}
|
|
5116
4755
|
|
|
5117
4756
|
// src/sanitizer.ts
|
|
@@ -5157,12 +4796,12 @@ function isDangerousUrlAttr(name, value) {
|
|
|
5157
4796
|
const normalized = normalizeUrlAttr(value);
|
|
5158
4797
|
return normalized.startsWith("javascript:") || normalized.startsWith("vbscript:") || normalized.startsWith("data:");
|
|
5159
4798
|
}
|
|
5160
|
-
function sanitizeHtml(
|
|
4799
|
+
function sanitizeHtml(html32) {
|
|
5161
4800
|
const hasNative = typeof window.Sanitizer === "function";
|
|
5162
4801
|
if (hasNative) {
|
|
5163
4802
|
try {
|
|
5164
|
-
const
|
|
5165
|
-
const frag =
|
|
4803
|
+
const s = new window.Sanitizer({});
|
|
4804
|
+
const frag = s.sanitizeToFragment(html32);
|
|
5166
4805
|
const div = document.createElement("div");
|
|
5167
4806
|
div.append(frag);
|
|
5168
4807
|
return div.innerHTML;
|
|
@@ -5170,7 +4809,7 @@ function sanitizeHtml(html31) {
|
|
|
5170
4809
|
}
|
|
5171
4810
|
}
|
|
5172
4811
|
const tpl = document.createElement("template");
|
|
5173
|
-
tpl.innerHTML =
|
|
4812
|
+
tpl.innerHTML = html32;
|
|
5174
4813
|
const root = tpl.content;
|
|
5175
4814
|
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);
|
|
5176
4815
|
const toRemove = [];
|
|
@@ -5197,24 +4836,71 @@ function sanitizeHtml(html31) {
|
|
|
5197
4836
|
}
|
|
5198
4837
|
return tpl.innerHTML;
|
|
5199
4838
|
}
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
4839
|
+
var DESCRIPTION_TAGS = /* @__PURE__ */ new Set([
|
|
4840
|
+
"h3",
|
|
4841
|
+
"h4",
|
|
4842
|
+
"h5",
|
|
4843
|
+
"h6",
|
|
4844
|
+
"p",
|
|
4845
|
+
"br",
|
|
4846
|
+
"ul",
|
|
4847
|
+
"ol",
|
|
4848
|
+
"li",
|
|
4849
|
+
"strong",
|
|
4850
|
+
"b",
|
|
4851
|
+
"em",
|
|
4852
|
+
"i",
|
|
4853
|
+
"u",
|
|
4854
|
+
"small",
|
|
4855
|
+
"sup",
|
|
4856
|
+
"sub",
|
|
4857
|
+
"table",
|
|
4858
|
+
"thead",
|
|
4859
|
+
"tbody",
|
|
4860
|
+
"tr",
|
|
4861
|
+
"th",
|
|
4862
|
+
"td",
|
|
4863
|
+
"blockquote"
|
|
4864
|
+
]);
|
|
4865
|
+
var DEMOTED_HEADING = {
|
|
4866
|
+
h1: "h3",
|
|
4867
|
+
h2: "h3",
|
|
4868
|
+
h3: "h4",
|
|
4869
|
+
h4: "h5",
|
|
4870
|
+
h5: "h6",
|
|
4871
|
+
h6: "h6"
|
|
4872
|
+
};
|
|
4873
|
+
function sanitizeProductDescription(html32) {
|
|
4874
|
+
if (typeof html32 !== "string" || html32 === "") return "";
|
|
4875
|
+
const doc = new DOMParser().parseFromString(html32, "text/html");
|
|
4876
|
+
const out = document.createElement("div");
|
|
4877
|
+
const copy = (from, to) => {
|
|
4878
|
+
for (const child of Array.from(from.childNodes)) {
|
|
4879
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
4880
|
+
to.appendChild(document.createTextNode(child.textContent ?? ""));
|
|
4881
|
+
continue;
|
|
4882
|
+
}
|
|
4883
|
+
if (!(child instanceof Element)) continue;
|
|
4884
|
+
const tag = child.tagName.toLowerCase();
|
|
4885
|
+
if (tag === "script" || tag === "style" || tag === "template" || tag === "iframe") continue;
|
|
4886
|
+
const kept = DEMOTED_HEADING[tag] ?? tag;
|
|
4887
|
+
if (DESCRIPTION_TAGS.has(kept)) {
|
|
4888
|
+
const el = document.createElement(kept);
|
|
4889
|
+
copy(child, el);
|
|
4890
|
+
to.appendChild(el);
|
|
4891
|
+
} else {
|
|
4892
|
+
copy(child, to);
|
|
4893
|
+
}
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
copy(doc.body, out);
|
|
4897
|
+
return out.innerHTML;
|
|
5211
4898
|
}
|
|
5212
4899
|
|
|
5213
4900
|
// src/widgets/imageStandin.ts
|
|
5214
4901
|
import { html as html7 } from "lit";
|
|
5215
|
-
var IMAGE_STANDIN_BACKGROUND = "linear-gradient(135deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.02) 45%, rgba(0, 0, 0, 0.22)), var(--sc-color-primary, #3a3a3a)";
|
|
5216
4902
|
function standinMonogram(name) {
|
|
5217
|
-
const initial = (
|
|
4903
|
+
const initial = standinInitial(name) ?? "?";
|
|
5218
4904
|
return html7`<span
|
|
5219
4905
|
data-standin-monogram
|
|
5220
4906
|
style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font:700 2.4rem/1 var(--sc-font-family, inherit);color:var(--sc-launcher-color, #ffffff);opacity:0.85;letter-spacing:0.02em;user-select:none;"
|
|
@@ -5222,12 +4908,25 @@ function standinMonogram(name) {
|
|
|
5222
4908
|
>`;
|
|
5223
4909
|
}
|
|
5224
4910
|
|
|
4911
|
+
// src/widgets/presentationContext.ts
|
|
4912
|
+
function presentationFromContext(el) {
|
|
4913
|
+
let node = el;
|
|
4914
|
+
while (node) {
|
|
4915
|
+
if (node instanceof Element && node.getAttribute("data-product-presentation") === "double") {
|
|
4916
|
+
return "double";
|
|
4917
|
+
}
|
|
4918
|
+
const parent = node.parentNode;
|
|
4919
|
+
node = parent instanceof ShadowRoot ? parent.host : parent;
|
|
4920
|
+
}
|
|
4921
|
+
return void 0;
|
|
4922
|
+
}
|
|
4923
|
+
|
|
5225
4924
|
// src/widgets/renderHealth.ts
|
|
5226
4925
|
var BLANK_MIN_AREA = 60 * 60;
|
|
5227
4926
|
function elementPath(el, stopAt) {
|
|
5228
|
-
const seg = (
|
|
5229
|
-
const tag =
|
|
5230
|
-
const cls = typeof
|
|
4927
|
+
const seg = (e) => {
|
|
4928
|
+
const tag = e.tagName.toLowerCase();
|
|
4929
|
+
const cls = typeof e.className === "string" && e.className.trim() ? `.${e.className.trim().split(/\s+/)[0]}` : "";
|
|
5231
4930
|
return `${tag}${cls}`;
|
|
5232
4931
|
};
|
|
5233
4932
|
const parts = [seg(el)];
|
|
@@ -5276,7 +4975,7 @@ function measureRenderHealth(card) {
|
|
|
5276
4975
|
const r = el.getBoundingClientRect();
|
|
5277
4976
|
if (r.width === 0 || r.height === 0) continue;
|
|
5278
4977
|
const ownText = Array.from(el.childNodes).some(
|
|
5279
|
-
(
|
|
4978
|
+
(n) => n.nodeType === 3 && (n.textContent ?? "").trim().length > 0
|
|
5280
4979
|
);
|
|
5281
4980
|
if (ownText) hasText = true;
|
|
5282
4981
|
if (el.tagName === "IMG") {
|
|
@@ -5310,8 +5009,8 @@ function measureRenderHealth(card) {
|
|
|
5310
5009
|
Math.round(ar.top - r.top)
|
|
5311
5010
|
);
|
|
5312
5011
|
if (esc > 1 && !hasClipOkAncestor(el, card) && !hasClipOkAncestor(a, card)) {
|
|
5313
|
-
const
|
|
5314
|
-
if (
|
|
5012
|
+
const t = (el.textContent ?? "").trim();
|
|
5013
|
+
if (t || ["IMG", "BUTTON", "A"].includes(el.tagName)) {
|
|
5315
5014
|
push({ path: elementPath(el, card), over_px: esc });
|
|
5316
5015
|
}
|
|
5317
5016
|
}
|
|
@@ -5345,9 +5044,9 @@ function reportRenderHealth(root, opts) {
|
|
|
5345
5044
|
synEmit(`app:${opts.category}:blank`, base);
|
|
5346
5045
|
return health;
|
|
5347
5046
|
}
|
|
5348
|
-
const paths = health.clips.map((
|
|
5349
|
-
const over_px = health.clips.reduce((m,
|
|
5350
|
-
const identityTruncated = health.clips.some((
|
|
5047
|
+
const paths = health.clips.map((c) => c.path).sort();
|
|
5048
|
+
const over_px = health.clips.reduce((m, c) => Math.max(m, c.over_px), 0);
|
|
5049
|
+
const identityTruncated = health.clips.some((c) => c.reason === "identity-truncated");
|
|
5351
5050
|
synEmit(`app:${opts.category}:clipped`, {
|
|
5352
5051
|
paths: paths.join(","),
|
|
5353
5052
|
over_px,
|
|
@@ -5648,6 +5347,12 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5648
5347
|
__privateAdd(this, _VariantPanelLit_instances);
|
|
5649
5348
|
this.payload = null;
|
|
5650
5349
|
this.productTitle = "";
|
|
5350
|
+
/** Set by a double-wide card presentation: the panel sits BESIDE the front
|
|
5351
|
+
* face rather than flipped to. There is no Back — the front already shows
|
|
5352
|
+
* the identity, so the panel's own title element is skipped too (the
|
|
5353
|
+
* "Showing options for …" live-region status line still speaks it, so
|
|
5354
|
+
* screen reader users get the same announcement either way). */
|
|
5355
|
+
this.pinned = false;
|
|
5651
5356
|
/** Product-level hero image, passed from the card. Shown on the back face and
|
|
5652
5357
|
* used as the fallback when the selected variant carries no featured_image. */
|
|
5653
5358
|
this.productImage = null;
|
|
@@ -5671,8 +5376,8 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5671
5376
|
this._expandedAxes = /* @__PURE__ */ new Set();
|
|
5672
5377
|
/** @internal — Tier-C per-axis filter text. */
|
|
5673
5378
|
this._axisFilter = {};
|
|
5674
|
-
this._onKeydown = (
|
|
5675
|
-
if (
|
|
5379
|
+
this._onKeydown = (e) => {
|
|
5380
|
+
if (e.key === "Escape") {
|
|
5676
5381
|
this.dispatchEvent(new CustomEvent("variant-panel-back", { bubbles: true, composed: true }));
|
|
5677
5382
|
}
|
|
5678
5383
|
};
|
|
@@ -5794,7 +5499,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5794
5499
|
/>` : nothing3}
|
|
5795
5500
|
</div>
|
|
5796
5501
|
<div class="svp-body" data-panel-body>
|
|
5797
|
-
${this.productTitle ? html8`<div class="svp-product-title" data-product-title data-critical-text>${this.productTitle}</div>` : nothing3}
|
|
5502
|
+
${this.productTitle && !this.pinned ? html8`<div class="svp-product-title" data-product-title data-critical-text>${this.productTitle}</div>` : nothing3}
|
|
5798
5503
|
<div class="svp-attributes-scroll" data-attributes-scroll data-clip-ok>
|
|
5799
5504
|
${singleVariant ? html8`<div class="svp-single-variant-note" data-single-variant-note>
|
|
5800
5505
|
Just one option — ready to add.
|
|
@@ -5813,7 +5518,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5813
5518
|
// so Back fits beside it on the same row).
|
|
5814
5519
|
ctaPrice != null ? html8`Add ${compareAt ? html8`<span class="svp-compare">$${compareAt.toFixed(2)}</span>` : nothing3}· $${ctaPrice.toFixed(2)}` : html8`Add to cart`}
|
|
5815
5520
|
</button>
|
|
5816
|
-
${backBtn}
|
|
5521
|
+
${this.pinned ? nothing3 : backBtn}
|
|
5817
5522
|
</div>
|
|
5818
5523
|
<div class="svp-live" data-live data-clip-ok aria-live="polite">
|
|
5819
5524
|
${this.productTitle ? `Showing options for ${this.productTitle}` : nothing3}
|
|
@@ -5907,8 +5612,8 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5907
5612
|
)?.focus();
|
|
5908
5613
|
});
|
|
5909
5614
|
}
|
|
5910
|
-
_onAxisFilterInput(attrKey,
|
|
5911
|
-
this._axisFilter = { ...this._axisFilter, [attrKey]:
|
|
5615
|
+
_onAxisFilterInput(attrKey, e) {
|
|
5616
|
+
this._axisFilter = { ...this._axisFilter, [attrKey]: e.target.value };
|
|
5912
5617
|
}
|
|
5913
5618
|
/** Density Tier C — select-style trigger + bounded scrollable listbox.
|
|
5914
5619
|
* ONLY this axis collapses; siblings keep their own tier. Filter appears
|
|
@@ -5926,9 +5631,9 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5926
5631
|
class="svp-collapse"
|
|
5927
5632
|
data-collapsed-axis
|
|
5928
5633
|
data-attribute-key=${attr.key}
|
|
5929
|
-
@keydown=${(
|
|
5930
|
-
if (
|
|
5931
|
-
|
|
5634
|
+
@keydown=${(e) => {
|
|
5635
|
+
if (e.key === "Escape" && expanded) {
|
|
5636
|
+
e.stopPropagation();
|
|
5932
5637
|
this._toggleAxis(attr.key);
|
|
5933
5638
|
void this.updateComplete.then(() => {
|
|
5934
5639
|
this.querySelector(
|
|
@@ -5965,7 +5670,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5965
5670
|
type="text"
|
|
5966
5671
|
placeholder=${`Filter ${attr.label.toLowerCase()}\u2026`}
|
|
5967
5672
|
.value=${this._axisFilter[attr.key] ?? ""}
|
|
5968
|
-
@input=${(
|
|
5673
|
+
@input=${(e) => this._onAxisFilterInput(attr.key, e)}
|
|
5969
5674
|
/>` : nothing3}
|
|
5970
5675
|
${filtered.map((v) => {
|
|
5971
5676
|
const disabled2 = this._isValueDisabled(attr.key, v.id);
|
|
@@ -6007,6 +5712,7 @@ _VariantPanelLit.properties = {
|
|
|
6007
5712
|
state: { type: String },
|
|
6008
5713
|
error: { type: String },
|
|
6009
5714
|
retryable: { type: Boolean },
|
|
5715
|
+
pinned: { type: Boolean },
|
|
6010
5716
|
_selection: { state: true },
|
|
6011
5717
|
_touched: { state: true },
|
|
6012
5718
|
_expandedAxes: { state: true },
|
|
@@ -6127,7 +5833,7 @@ var CARD_FLIP_CSS = `
|
|
|
6127
5833
|
.sc-product-card [data-card-flip] { animation: none; }
|
|
6128
5834
|
}
|
|
6129
5835
|
`;
|
|
6130
|
-
var _effectExecutor, _controller, _unsubDevice, _lastFailureFingerprint, _reportedHealthStates, _healthRaf, _ProductCardLit_instances, scheduleHealthCheck_fn, measureAndReportHealth_fn, cleanupHostResources_fn, dispatchCard_fn, _onFlipAnimationEnd, startBind_fn, readProductObservation_fn, syncDecisionInputsBeforeIntent_fn;
|
|
5836
|
+
var _effectExecutor, _controller, _unsubDevice, _double, _lastFailureFingerprint, _reportedHealthStates, _healthRaf, _ProductCardLit_instances, scheduleHealthCheck_fn, measureAndReportHealth_fn, cleanupHostResources_fn, dispatchCard_fn, _onFlipAnimationEnd, startBind_fn, renderVariantPanel_fn, readProductObservation_fn, syncDecisionInputsBeforeIntent_fn;
|
|
6131
5837
|
var ProductCardLit = class extends LitElement8 {
|
|
6132
5838
|
constructor() {
|
|
6133
5839
|
super(...arguments);
|
|
@@ -6151,6 +5857,12 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6151
5857
|
}));
|
|
6152
5858
|
__privateAdd(this, _controller);
|
|
6153
5859
|
__privateAdd(this, _unsubDevice);
|
|
5860
|
+
/** Set once at connect from the canvas DOM around the card
|
|
5861
|
+
* (`data-product-presentation="double"`) — never from `props`, so the
|
|
5862
|
+
* agent that writes card props has no path to setting it. Drives the
|
|
5863
|
+
* double-wide render branch: front face and options side by side, options
|
|
5864
|
+
* pinned open. */
|
|
5865
|
+
__privateAdd(this, _double, false);
|
|
6154
5866
|
/** Fingerprint of the last reported schema-validation failure (sorted
|
|
6155
5867
|
* invalid paths). Guards `render_failed` telemetry against re-render spam:
|
|
6156
5868
|
* the same bad props re-rendering must not re-emit. Reset on a successful
|
|
@@ -6164,25 +5876,25 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6164
5876
|
* the next scheduled pass or on disconnect. */
|
|
6165
5877
|
__privateAdd(this, _healthRaf);
|
|
6166
5878
|
/** animationend on the flip container → the turn is done, render flat. */
|
|
6167
|
-
__privateAdd(this, _onFlipAnimationEnd, (
|
|
6168
|
-
if (
|
|
5879
|
+
__privateAdd(this, _onFlipAnimationEnd, (e) => {
|
|
5880
|
+
if (e.animationName === "sc-card-flip-in") __privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "animation-settled" });
|
|
6169
5881
|
});
|
|
6170
5882
|
this._onVariantBack = () => {
|
|
6171
5883
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6172
5884
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "back-activated" });
|
|
6173
5885
|
};
|
|
6174
|
-
this._onVariantSelectionChange = (
|
|
5886
|
+
this._onVariantSelectionChange = (e) => {
|
|
6175
5887
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6176
|
-
const detail =
|
|
5888
|
+
const detail = e.detail;
|
|
6177
5889
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, {
|
|
6178
5890
|
type: "selection-changed",
|
|
6179
5891
|
attributeKey: detail.attribute_key,
|
|
6180
5892
|
valueId: detail.value_id
|
|
6181
5893
|
});
|
|
6182
5894
|
};
|
|
6183
|
-
this._onVariantAdd = (
|
|
5895
|
+
this._onVariantAdd = (e) => {
|
|
6184
5896
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6185
|
-
const detail =
|
|
5897
|
+
const detail = e.detail;
|
|
6186
5898
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "variant-add-activated", variantId: detail.variant.id });
|
|
6187
5899
|
};
|
|
6188
5900
|
this._onVariantRetry = () => {
|
|
@@ -6195,6 +5907,8 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6195
5907
|
}
|
|
6196
5908
|
connectedCallback() {
|
|
6197
5909
|
super.connectedCallback();
|
|
5910
|
+
__privateSet(this, _double, presentationFromContext(this) === "double");
|
|
5911
|
+
if (__privateGet(this, _double)) __privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "options-pinned", pinned: true });
|
|
6198
5912
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "reconnected" });
|
|
6199
5913
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, {
|
|
6200
5914
|
type: "commerce-updated",
|
|
@@ -6204,8 +5918,8 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6204
5918
|
this.style.color = "var(--sc-tile-text-color, rgba(20, 20, 20, 0.92))";
|
|
6205
5919
|
this._device = _readDevice();
|
|
6206
5920
|
const ctx = synWindow().SynOS?.runtime?.context;
|
|
6207
|
-
__privateSet(this, _unsubDevice, ctx?.subscribe?.((
|
|
6208
|
-
const d =
|
|
5921
|
+
__privateSet(this, _unsubDevice, ctx?.subscribe?.((c) => {
|
|
5922
|
+
const d = c?.surface?.device;
|
|
6209
5923
|
const next = d === "mobile" || d === "tablet" ? d : "desktop";
|
|
6210
5924
|
if (next !== this._device) this._device = next;
|
|
6211
5925
|
}));
|
|
@@ -6238,18 +5952,37 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6238
5952
|
const resolved = this.resolved[product.id];
|
|
6239
5953
|
const view = selectCardView(this._decisionState);
|
|
6240
5954
|
const articleStyles = _buildArticleStyles(density, view.face);
|
|
6241
|
-
const onPrimaryCtaClick = (
|
|
6242
|
-
|
|
5955
|
+
const onPrimaryCtaClick = (e) => {
|
|
5956
|
+
e.preventDefault();
|
|
6243
5957
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6244
5958
|
const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
6245
5959
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "primary-activated", reducedMotion });
|
|
6246
5960
|
};
|
|
6247
|
-
const ctasWithMorphedLabel = product.ctas.map((
|
|
6248
|
-
if (
|
|
6249
|
-
return { ...
|
|
5961
|
+
const ctasWithMorphedLabel = product.ctas.map((c, i) => {
|
|
5962
|
+
if (i === 0 && view.primaryCta.mode === "view-cart") {
|
|
5963
|
+
return { ...c, label: "View cart \u2192" };
|
|
6250
5964
|
}
|
|
6251
|
-
return
|
|
5965
|
+
return c;
|
|
6252
5966
|
});
|
|
5967
|
+
if (__privateGet(this, _double)) {
|
|
5968
|
+
return html9`<article
|
|
5969
|
+
class="sc-product-card"
|
|
5970
|
+
data-presentation="double"
|
|
5971
|
+
data-density=${density}
|
|
5972
|
+
style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));gap:16px;align-items:start;"
|
|
5973
|
+
>
|
|
5974
|
+
<div data-face="front">
|
|
5975
|
+
${renderProductCardInner(product, density, resolved, visibleFacts, [], void 0, true)}
|
|
5976
|
+
</div>
|
|
5977
|
+
<div data-face="back">
|
|
5978
|
+
${__privateMethod(this, _ProductCardLit_instances, renderVariantPanel_fn).call(this, view, product, {
|
|
5979
|
+
pinned: true,
|
|
5980
|
+
productImage: null,
|
|
5981
|
+
wireBack: false
|
|
5982
|
+
})}
|
|
5983
|
+
</div>
|
|
5984
|
+
</article>`;
|
|
5985
|
+
}
|
|
6253
5986
|
const frontContent = renderProductCardInner(
|
|
6254
5987
|
product,
|
|
6255
5988
|
density,
|
|
@@ -6259,19 +5992,11 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6259
5992
|
onPrimaryCtaClick,
|
|
6260
5993
|
!view.primaryCta.disabled
|
|
6261
5994
|
);
|
|
6262
|
-
const backFace =
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
.error=${view.panel.error}
|
|
6268
|
-
.retryable=${view.panel.retryable}
|
|
6269
|
-
@variant-panel-back=${this._onVariantBack}
|
|
6270
|
-
@variant-panel-add=${this._onVariantAdd}
|
|
6271
|
-
@variant-panel-retry=${this._onVariantRetry}
|
|
6272
|
-
@variant-panel-selection-change=${this._onVariantSelectionChange}
|
|
6273
|
-
></syntro-variant-panel>
|
|
6274
|
-
${view.addError ? html9`<div data-add-to-cart-error>${view.addError}</div>` : nothing4}`;
|
|
5995
|
+
const backFace = __privateMethod(this, _ProductCardLit_instances, renderVariantPanel_fn).call(this, view, product, {
|
|
5996
|
+
pinned: false,
|
|
5997
|
+
productImage: product.image ? { src: product.image.src, alt: product.image.alt } : null,
|
|
5998
|
+
wireBack: true
|
|
5999
|
+
});
|
|
6275
6000
|
const backView = view.face === "back" && view.flipAnimating ? (
|
|
6276
6001
|
// Mid-flip: both faces coexist inside a preserve-3d container. The
|
|
6277
6002
|
// front turns away (inert + aria-hidden) while the back turns in.
|
|
@@ -6302,6 +6027,7 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6302
6027
|
_effectExecutor = new WeakMap();
|
|
6303
6028
|
_controller = new WeakMap();
|
|
6304
6029
|
_unsubDevice = new WeakMap();
|
|
6030
|
+
_double = new WeakMap();
|
|
6305
6031
|
_lastFailureFingerprint = new WeakMap();
|
|
6306
6032
|
_reportedHealthStates = new WeakMap();
|
|
6307
6033
|
_healthRaf = new WeakMap();
|
|
@@ -6366,6 +6092,7 @@ dispatchCard_fn = function(event) {
|
|
|
6366
6092
|
_onFlipAnimationEnd = new WeakMap();
|
|
6367
6093
|
startBind_fn = function() {
|
|
6368
6094
|
__privateGet(this, _controller)?.destroy();
|
|
6095
|
+
const previous = this.resolved;
|
|
6369
6096
|
this.resolved = {};
|
|
6370
6097
|
if (!this.props) {
|
|
6371
6098
|
this.validationError = false;
|
|
@@ -6392,15 +6119,41 @@ startBind_fn = function() {
|
|
|
6392
6119
|
__privateSet(this, _lastFailureFingerprint, null);
|
|
6393
6120
|
this.validationError = false;
|
|
6394
6121
|
const parsed = result.data;
|
|
6122
|
+
this.resolved = keepResolved(previous, [parsed.product]);
|
|
6395
6123
|
__privateSet(this, _controller, new BindController([parsed.product], (productId, kind, value) => {
|
|
6396
|
-
|
|
6397
|
-
this.resolved = {
|
|
6398
|
-
...this.resolved,
|
|
6399
|
-
[productId]: { ...this.resolved[productId], [kind]: value }
|
|
6400
|
-
};
|
|
6124
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
6401
6125
|
}));
|
|
6402
6126
|
__privateGet(this, _controller).start();
|
|
6403
6127
|
};
|
|
6128
|
+
/**
|
|
6129
|
+
* The `<syntro-variant-panel>` markup, shared by the flip card's back face
|
|
6130
|
+
* and the double-wide presentation's side panel — everything (payload,
|
|
6131
|
+
* title, state, error, retryable, the add/retry/selection-change handlers,
|
|
6132
|
+
* and the addError block) is identical between the two callers. Only three
|
|
6133
|
+
* things differ, so they're the only knobs: `pinned` (double-wide has no
|
|
6134
|
+
* Back and no title element — see VariantPanelLit), `productImage` (the
|
|
6135
|
+
* flip back face shows the product's hero image; the double-wide panel
|
|
6136
|
+
* shows none, since the front face right beside it already carries the
|
|
6137
|
+
* image), and `wireBack` (the double-wide panel has no Back control to
|
|
6138
|
+
* fire it and the card never flips there, so wiring the handler would be
|
|
6139
|
+
* live but pointless — omitted to match the panel's own no-Back contract).
|
|
6140
|
+
*/
|
|
6141
|
+
renderVariantPanel_fn = function(view, product, options) {
|
|
6142
|
+
return html9`<syntro-variant-panel
|
|
6143
|
+
?pinned=${options.pinned}
|
|
6144
|
+
.payload=${view.panel.payload}
|
|
6145
|
+
.productTitle=${product.name ?? ""}
|
|
6146
|
+
.productImage=${options.productImage}
|
|
6147
|
+
.state=${view.panel.status === "error" ? "error" : view.panel.status === "ready" ? "idle" : "loading"}
|
|
6148
|
+
.error=${view.panel.error}
|
|
6149
|
+
.retryable=${view.panel.retryable}
|
|
6150
|
+
@variant-panel-back=${options.wireBack ? this._onVariantBack : void 0}
|
|
6151
|
+
@variant-panel-add=${this._onVariantAdd}
|
|
6152
|
+
@variant-panel-retry=${this._onVariantRetry}
|
|
6153
|
+
@variant-panel-selection-change=${this._onVariantSelectionChange}
|
|
6154
|
+
></syntro-variant-panel>
|
|
6155
|
+
${view.addError ? html9`<div data-add-to-cart-error>${view.addError}</div>` : nothing4}`;
|
|
6156
|
+
};
|
|
6404
6157
|
readProductObservation_fn = function() {
|
|
6405
6158
|
const parsed = this.props ? cardSchema.safeParse(this.props) : null;
|
|
6406
6159
|
const rawProductId = this.props?.product?.id;
|
|
@@ -6527,7 +6280,9 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
6527
6280
|
const factOrder = Array.from(
|
|
6528
6281
|
/* @__PURE__ */ new Set([...CORE_FACTS, ...visibleFacts ?? VISIBLE_FACT_KEYS])
|
|
6529
6282
|
);
|
|
6530
|
-
const
|
|
6283
|
+
const storeAnswered = resolved !== void 0 && "compareAt" in resolved;
|
|
6284
|
+
const hasSalePrice = storeAnswered ? Boolean(resolved?.compareAt) : product.salePrice !== void 0;
|
|
6285
|
+
const originalPriceText = storeAnswered ? resolved?.compareAt : product.price?.amount;
|
|
6531
6286
|
const activePriceText = resolved?.price ?? product.salePrice?.amount ?? product.price?.amount;
|
|
6532
6287
|
const activeCadence = hasSalePrice ? product.salePrice?.cadence : product.price?.cadence;
|
|
6533
6288
|
const overlay = density === "compact";
|
|
@@ -6830,10 +6585,15 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
6830
6585
|
borderRadius: "9999px",
|
|
6831
6586
|
border: "0",
|
|
6832
6587
|
background: "transparent",
|
|
6833
|
-
|
|
6588
|
+
// Accent as ink on `transparent` is ink on the HOST's ground inside a
|
|
6589
|
+
// card, where vela's #723b32 measures 1.3:1. The host's own ink wins
|
|
6590
|
+
// there; the merchant's accent everywhere else.
|
|
6591
|
+
color: "var(--sc-host-link-color, var(--sc-color-primary, #3d8a5e))",
|
|
6834
6592
|
fontWeight: "600",
|
|
6835
6593
|
fontSize: "0.82rem",
|
|
6836
|
-
|
|
6594
|
+
// Once the link is the same colour as the text around it, colour alone no
|
|
6595
|
+
// longer says it is a link (WCAG 1.4.1), so the host card underlines it.
|
|
6596
|
+
textDecoration: "var(--sc-host-link-decoration, none)",
|
|
6837
6597
|
textAlign: "center",
|
|
6838
6598
|
lineHeight: "1.15",
|
|
6839
6599
|
// Same terminal policy as the primary pill: shrink + wrap, never overflow.
|
|
@@ -6843,41 +6603,41 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
6843
6603
|
maxWidth: "100%",
|
|
6844
6604
|
flexShrink: "1"
|
|
6845
6605
|
};
|
|
6846
|
-
const ctaRow = html9`<div class="sc-product-card__ctas" style=${styleMap(ctasStyles)}>
|
|
6847
|
-
${ctas.map((
|
|
6848
|
-
const disabled2 =
|
|
6606
|
+
const ctaRow = ctas.length === 0 ? nothing4 : html9`<div class="sc-product-card__ctas" style=${styleMap(ctasStyles)}>
|
|
6607
|
+
${ctas.map((c, i) => {
|
|
6608
|
+
const disabled2 = c.actionId === "add_to_cart" && !commerceAvailable;
|
|
6849
6609
|
const ctaStyles = {
|
|
6850
|
-
...
|
|
6610
|
+
...i === 0 ? primaryCtaStyles : secondaryCtaStyles,
|
|
6851
6611
|
...disabled2 ? { opacity: "0.5", "pointer-events": "none", cursor: "not-allowed" } : {}
|
|
6852
6612
|
};
|
|
6853
|
-
const ctaLabel =
|
|
6613
|
+
const ctaLabel = c.label;
|
|
6854
6614
|
return html9`<a
|
|
6855
6615
|
class="sc-product-card__cta"
|
|
6856
|
-
data-product-cta=${
|
|
6616
|
+
data-product-cta=${i === 0 ? "primary" : "secondary"}
|
|
6857
6617
|
data-critical-text
|
|
6858
|
-
data-variant=${
|
|
6618
|
+
data-variant=${c.variant}
|
|
6859
6619
|
data-disabled=${disabled2 ? "true" : nothing4}
|
|
6860
6620
|
aria-disabled=${disabled2 ? "true" : nothing4}
|
|
6861
|
-
href=${
|
|
6862
|
-
target=${
|
|
6863
|
-
rel=${
|
|
6864
|
-
data-action-id=${
|
|
6621
|
+
href=${c.actionId === "add_to_cart" ? "#" : c.href ?? "#"}
|
|
6622
|
+
target=${c.target}
|
|
6623
|
+
rel=${c.target === "_blank" ? "noopener noreferrer" : ""}
|
|
6624
|
+
data-action-id=${c.actionId ?? ""}
|
|
6865
6625
|
style=${styleMap(ctaStyles)}
|
|
6866
|
-
@click=${disabled2 ? (
|
|
6867
|
-
|
|
6626
|
+
@click=${disabled2 ? (e) => {
|
|
6627
|
+
e.preventDefault();
|
|
6868
6628
|
} : (
|
|
6869
6629
|
// The commerce (Add-to-cart / open-picker) intent wires ONLY to
|
|
6870
6630
|
// an actual add_to_cart CTA. A plain navigating CTA — e.g. a
|
|
6871
6631
|
// booking-fulfilment product's "Book a consult" href, which
|
|
6872
6632
|
// sits at i===0 too — must fall through to its own <a href>
|
|
6873
6633
|
// and never enter the Woo/Shopify variant-lookup flow.
|
|
6874
|
-
|
|
6875
|
-
|
|
6634
|
+
i === 0 && c.actionId === "add_to_cart" && onPrimaryCtaClick ? onPrimaryCtaClick : c.actionId === "add_to_cart" ? (e) => {
|
|
6635
|
+
e.preventDefault();
|
|
6876
6636
|
} : void 0
|
|
6877
6637
|
)}
|
|
6878
6638
|
>${// Secondary "Product Page" link gets a trailing chevron (panel's
|
|
6879
6639
|
// quiet-secondary affordance) unless the author already added one.
|
|
6880
|
-
|
|
6640
|
+
i !== 0 && !/[›>→]\s*$/.test(ctaLabel) ? `${ctaLabel} \u203A` : ctaLabel}</a
|
|
6881
6641
|
>`;
|
|
6882
6642
|
})}
|
|
6883
6643
|
</div>`;
|
|
@@ -7080,11 +6840,11 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
7080
6840
|
data-product-price
|
|
7081
6841
|
style=${styleMap(priceBlockStyles)}
|
|
7082
6842
|
>
|
|
7083
|
-
${hasSalePrice &&
|
|
6843
|
+
${hasSalePrice && originalPriceText ? html9`<span
|
|
7084
6844
|
class="sc-product-card__amount-original"
|
|
7085
6845
|
data-product-price-original
|
|
7086
6846
|
style=${styleMap(priceOriginalStyles)}
|
|
7087
|
-
>${
|
|
6847
|
+
>${originalPriceText}</span
|
|
7088
6848
|
>` : nothing4}
|
|
7089
6849
|
<span
|
|
7090
6850
|
class="sc-product-card__amount"
|
|
@@ -7246,8 +7006,8 @@ function renderProductCard(product, density, resolved, visibleFacts) {
|
|
|
7246
7006
|
</article>
|
|
7247
7007
|
`;
|
|
7248
7008
|
}
|
|
7249
|
-
function onImageError(
|
|
7250
|
-
const img =
|
|
7009
|
+
function onImageError(e) {
|
|
7010
|
+
const img = e.currentTarget;
|
|
7251
7011
|
img.dataset.imageError = "true";
|
|
7252
7012
|
img.removeAttribute("src");
|
|
7253
7013
|
}
|
|
@@ -7422,6 +7182,7 @@ syncScrollCue_fn = function() {
|
|
|
7422
7182
|
};
|
|
7423
7183
|
startBind_fn2 = function() {
|
|
7424
7184
|
__privateGet(this, _controller2)?.destroy();
|
|
7185
|
+
const previous = this.resolved;
|
|
7425
7186
|
this.resolved = {};
|
|
7426
7187
|
if (!this.props) {
|
|
7427
7188
|
this.validationError = false;
|
|
@@ -7437,12 +7198,9 @@ startBind_fn2 = function() {
|
|
|
7437
7198
|
}
|
|
7438
7199
|
this.validationError = false;
|
|
7439
7200
|
__privateSet(this, _parsed, result.data);
|
|
7201
|
+
this.resolved = keepResolved(previous, result.data.products);
|
|
7440
7202
|
__privateSet(this, _controller2, new BindController(result.data.products, (productId, kind, value) => {
|
|
7441
|
-
|
|
7442
|
-
this.resolved = {
|
|
7443
|
-
...this.resolved,
|
|
7444
|
-
[productId]: { ...this.resolved[productId], [kind]: value }
|
|
7445
|
-
};
|
|
7203
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
7446
7204
|
}));
|
|
7447
7205
|
__privateGet(this, _controller2).start();
|
|
7448
7206
|
};
|
|
@@ -7457,7 +7215,7 @@ startBind_fn2 = function() {
|
|
|
7457
7215
|
*/
|
|
7458
7216
|
renderDiveDeeper_fn = function(parsed) {
|
|
7459
7217
|
if (parsed.products.length < 2) return nothing5;
|
|
7460
|
-
const chipStyle = "flex:0 0 auto;align-self:flex-end;margin-top:6px;padding:4px 10px;border-radius:9999px;border:1px solid var(--sc-color-border,rgba(0,0,0,0.14));background:transparent;cursor:pointer;font:600 12px/1.2 var(--sc-font-family,inherit);color:var(--sc-color-primary,#3d8a5e);white-space:nowrap;";
|
|
7218
|
+
const chipStyle = "flex:0 0 auto;align-self:flex-end;margin-top:6px;padding:4px 10px;border-radius:9999px;border:1px solid var(--sc-color-border,rgba(0,0,0,0.14));background:transparent;cursor:pointer;font:600 12px/1.2 var(--sc-font-family,inherit);color:var(--sc-host-link-color,var(--sc-color-primary,#3d8a5e));white-space:nowrap;";
|
|
7461
7219
|
return html10`<button
|
|
7462
7220
|
type="button"
|
|
7463
7221
|
class="sc-product-comparison__dive-deeper"
|
|
@@ -7502,8 +7260,8 @@ rowComplete_fn = function(row, productCount) {
|
|
|
7502
7260
|
maxFields_fn = function() {
|
|
7503
7261
|
const raw = getComputedStyle(this).getPropertyValue("--sc-comparison-max-fields").trim();
|
|
7504
7262
|
if (!raw) return null;
|
|
7505
|
-
const
|
|
7506
|
-
return Number.isFinite(
|
|
7263
|
+
const n = Number.parseInt(raw, 10);
|
|
7264
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
7507
7265
|
};
|
|
7508
7266
|
/**
|
|
7509
7267
|
* Build comparison rows from the products' own `attributes` when the assistant
|
|
@@ -7520,10 +7278,10 @@ synthesizeRows_fn = function(products) {
|
|
|
7520
7278
|
}
|
|
7521
7279
|
}
|
|
7522
7280
|
return labels.map((label) => {
|
|
7523
|
-
const [a, b,
|
|
7281
|
+
const [a, b, c, d] = products.map(
|
|
7524
7282
|
(p) => (p.attributes ?? []).find((x) => x.label === label)?.value ?? "\u2014"
|
|
7525
7283
|
);
|
|
7526
|
-
return { label, valueA: a ?? "\u2014", valueB: b ?? "\u2014", valueC:
|
|
7284
|
+
return { label, valueA: a ?? "\u2014", valueB: b ?? "\u2014", valueC: c, valueD: d };
|
|
7527
7285
|
});
|
|
7528
7286
|
};
|
|
7529
7287
|
/**
|
|
@@ -7535,8 +7293,8 @@ renderMatrix_fn = function(products, rows) {
|
|
|
7535
7293
|
const cols = `minmax(62px, 0.55fr) repeat(${products.length}, minmax(0, 1fr))`;
|
|
7536
7294
|
const compactHeaders = products.length >= 3;
|
|
7537
7295
|
const colHeadStyle = "display:block;padding:6px 3px;text-align:left;font-weight:600;min-width:0;";
|
|
7538
|
-
const labelStyle = "padding:8px 4px 8px 2px;font-weight:600;font-size:0.75rem;opacity:0.75;border-top:1px solid var(--sc-
|
|
7539
|
-
const cellStyle = "padding:8px 6px;border-top:1px solid var(--sc-
|
|
7296
|
+
const labelStyle = "padding:8px 4px 8px 2px;font-weight:600;font-size:0.75rem;opacity:0.75;border-top:1px solid var(--sc-content-divider-color,rgba(0,0,0,0.08));align-self:stretch;min-width:0;overflow-wrap:break-word;hyphens:auto;";
|
|
7297
|
+
const cellStyle = "padding:8px 6px;border-top:1px solid var(--sc-content-divider-color,rgba(0,0,0,0.08));align-self:stretch;min-width:0;overflow-wrap:anywhere;";
|
|
7540
7298
|
return html10`
|
|
7541
7299
|
<div
|
|
7542
7300
|
class="sc-product-comparison__matrix"
|
|
@@ -7638,8 +7396,8 @@ renderMatrix_fn = function(products, rows) {
|
|
|
7638
7396
|
>${row.label}</span
|
|
7639
7397
|
>
|
|
7640
7398
|
${products.map(
|
|
7641
|
-
(_p,
|
|
7642
|
-
>${cells[
|
|
7399
|
+
(_p, i) => html10`<span class="sc-product-comparison__matrix-cell" role="cell" style=${cellStyle}
|
|
7400
|
+
>${cells[i] ?? "\u2014"}</span
|
|
7643
7401
|
>`
|
|
7644
7402
|
)}
|
|
7645
7403
|
</div>
|
|
@@ -7655,10 +7413,167 @@ ProductComparisonLit.properties = {
|
|
|
7655
7413
|
scrollFade: { state: true }
|
|
7656
7414
|
};
|
|
7657
7415
|
|
|
7658
|
-
// src/widgets/
|
|
7416
|
+
// src/widgets/ProductDetailsLit.ts
|
|
7659
7417
|
import { html as html11, LitElement as LitElement10, nothing as nothing6 } from "lit";
|
|
7418
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
7419
|
+
var EMPTY_MARKER = html11`<span data-sc-empty hidden></span>`;
|
|
7420
|
+
var DETAILS_CSS = `
|
|
7421
|
+
.pd {
|
|
7422
|
+
max-width: 1040px;
|
|
7423
|
+
margin: 0 auto 16px;
|
|
7424
|
+
display: grid;
|
|
7425
|
+
gap: 16px;
|
|
7426
|
+
}
|
|
7427
|
+
.pd-lead p {
|
|
7428
|
+
margin: 0 0 6px;
|
|
7429
|
+
}
|
|
7430
|
+
.pd-eyebrow {
|
|
7431
|
+
font-size: 0.75rem;
|
|
7432
|
+
letter-spacing: 0.06em;
|
|
7433
|
+
text-transform: uppercase;
|
|
7434
|
+
opacity: 0.75;
|
|
7435
|
+
}
|
|
7436
|
+
.pd-badge {
|
|
7437
|
+
margin-left: 6px;
|
|
7438
|
+
padding: 1px 8px;
|
|
7439
|
+
border-radius: 9999px;
|
|
7440
|
+
box-shadow: inset 0 0 0 1px currentColor;
|
|
7441
|
+
text-transform: none;
|
|
7442
|
+
letter-spacing: 0;
|
|
7443
|
+
}
|
|
7444
|
+
.pd-rating {
|
|
7445
|
+
font-size: 0.875rem;
|
|
7446
|
+
}
|
|
7447
|
+
.pd-desc {
|
|
7448
|
+
line-height: 1.5;
|
|
7449
|
+
max-width: 70ch;
|
|
7450
|
+
}
|
|
7451
|
+
.pd-benefits {
|
|
7452
|
+
margin: 4px 0 0;
|
|
7453
|
+
padding-left: 18px;
|
|
7454
|
+
}
|
|
7455
|
+
.pd-cols {
|
|
7456
|
+
display: grid;
|
|
7457
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
|
|
7458
|
+
gap: 16px;
|
|
7459
|
+
padding: 14px 0;
|
|
7460
|
+
border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
|
7461
|
+
border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
|
7462
|
+
font-size: 0.875rem;
|
|
7463
|
+
}
|
|
7464
|
+
.pd-cols h3 {
|
|
7465
|
+
margin: 0 0 6px;
|
|
7466
|
+
font-size: 0.8125rem;
|
|
7467
|
+
letter-spacing: 0.04em;
|
|
7468
|
+
text-transform: uppercase;
|
|
7469
|
+
opacity: 0.8;
|
|
7470
|
+
}
|
|
7471
|
+
.pd-cols p,
|
|
7472
|
+
.pd-cols ul {
|
|
7473
|
+
margin: 0 0 4px;
|
|
7474
|
+
}
|
|
7475
|
+
.pd-cols ul {
|
|
7476
|
+
padding-left: 18px;
|
|
7477
|
+
}
|
|
7478
|
+
`;
|
|
7479
|
+
var ProductDetailsLit = class extends LitElement10 {
|
|
7480
|
+
constructor() {
|
|
7481
|
+
super(...arguments);
|
|
7482
|
+
this._productId = "";
|
|
7483
|
+
this._details = null;
|
|
7484
|
+
}
|
|
7485
|
+
createRenderRoot() {
|
|
7486
|
+
return this;
|
|
7487
|
+
}
|
|
7488
|
+
connectedCallback() {
|
|
7489
|
+
super.connectedCallback();
|
|
7490
|
+
ensureStylesInRoot(this, "syntro-product-details-style", DETAILS_CSS);
|
|
7491
|
+
}
|
|
7492
|
+
/**
|
|
7493
|
+
* Widget props (the `adaptive-product:details` mountable sets `el.props`
|
|
7494
|
+
* before appending). Starts the details fetch immediately — reading
|
|
7495
|
+
* `productId` off the adapter, not off any attribute — and keys the
|
|
7496
|
+
* in-flight request by productId so a stale answer arriving after a later
|
|
7497
|
+
* `props` set (e.g. the card swapped to a different product) is dropped.
|
|
7498
|
+
*/
|
|
7499
|
+
set props(value) {
|
|
7500
|
+
const id = value?.productId ?? "";
|
|
7501
|
+
this._productId = id;
|
|
7502
|
+
this._details = null;
|
|
7503
|
+
if (!id) return;
|
|
7504
|
+
const adapter = getProductPlatformAdapter();
|
|
7505
|
+
if (typeof adapter?.fetchProductDetails !== "function") return;
|
|
7506
|
+
adapter.fetchProductDetails(id).then((details) => {
|
|
7507
|
+
if (this._productId !== id) return;
|
|
7508
|
+
this._details = details;
|
|
7509
|
+
}).catch(() => {
|
|
7510
|
+
});
|
|
7511
|
+
}
|
|
7512
|
+
get props() {
|
|
7513
|
+
return this._productId ? { productId: this._productId } : void 0;
|
|
7514
|
+
}
|
|
7515
|
+
render() {
|
|
7516
|
+
const details = this._details;
|
|
7517
|
+
if (!details) return EMPTY_MARKER;
|
|
7518
|
+
const d = details.details;
|
|
7519
|
+
const eyebrow = [d?.category, d?.gender].filter(Boolean).join(" \xB7 ");
|
|
7520
|
+
const badges = d?.badges ?? [];
|
|
7521
|
+
const benefits = d?.benefits ?? [];
|
|
7522
|
+
const moreBenefits = benefits.slice(3);
|
|
7523
|
+
const hasFabric = Boolean(d?.fabric || moreBenefits.length);
|
|
7524
|
+
const hasCols = Boolean(
|
|
7525
|
+
d && (hasFabric || (d.care?.length ?? 0) > 0 || d.shipping || d.returns)
|
|
7526
|
+
);
|
|
7527
|
+
const hasLead = Boolean(
|
|
7528
|
+
eyebrow || badges.length || details.rating || details.description || benefits.length
|
|
7529
|
+
);
|
|
7530
|
+
if (!hasLead && !hasCols) return EMPTY_MARKER;
|
|
7531
|
+
return html11`
|
|
7532
|
+
<section class="pd" data-product-details aria-label="Product details">
|
|
7533
|
+
<div class="pd-lead">
|
|
7534
|
+
${eyebrow || badges.length ? html11`<p class="pd-eyebrow">
|
|
7535
|
+
${eyebrow}${badges.map((b) => html11` <span class="pd-badge">${b}</span>`)}
|
|
7536
|
+
</p>` : nothing6}
|
|
7537
|
+
${details.rating ? html11`<p class="pd-rating">
|
|
7538
|
+
★ ${details.rating.value} · ${details.rating.count.toLocaleString("en-US")} reviews
|
|
7539
|
+
</p>` : nothing6}
|
|
7540
|
+
${details.description ? html11`<div class="pd-desc">${unsafeHTML(sanitizeProductDescription(details.description))}</div>` : nothing6}
|
|
7541
|
+
${benefits.length ? html11`<ul class="pd-benefits">
|
|
7542
|
+
${benefits.slice(0, 3).map((b) => html11`<li>${b}</li>`)}
|
|
7543
|
+
</ul>` : nothing6}
|
|
7544
|
+
</div>
|
|
7545
|
+
${hasCols ? html11`<div class="pd-cols">
|
|
7546
|
+
${hasFabric ? html11`<div>
|
|
7547
|
+
<h3>Fabric & benefits</h3>
|
|
7548
|
+
${d?.fabric ? html11`<p>${d.fabric}</p>` : nothing6}
|
|
7549
|
+
${moreBenefits.length ? html11`<ul>
|
|
7550
|
+
${moreBenefits.map((b) => html11`<li>${b}</li>`)}
|
|
7551
|
+
</ul>` : nothing6}
|
|
7552
|
+
</div>` : nothing6}
|
|
7553
|
+
<div>
|
|
7554
|
+
<h3>Shipping & returns</h3>
|
|
7555
|
+
${d?.shipping ? html11`<p>${d.shipping}</p>` : nothing6}
|
|
7556
|
+
${d?.returns ? html11`<p>${d.returns}</p>` : nothing6}
|
|
7557
|
+
</div>
|
|
7558
|
+
<div>
|
|
7559
|
+
<h3>Care</h3>
|
|
7560
|
+
${d?.care?.length ? html11`<ul>
|
|
7561
|
+
${d.care.map((c) => html11`<li>${c}</li>`)}
|
|
7562
|
+
</ul>` : nothing6}
|
|
7563
|
+
</div>
|
|
7564
|
+
</div>` : nothing6}
|
|
7565
|
+
</section>
|
|
7566
|
+
`;
|
|
7567
|
+
}
|
|
7568
|
+
};
|
|
7569
|
+
ProductDetailsLit.properties = {
|
|
7570
|
+
_details: { state: true }
|
|
7571
|
+
};
|
|
7572
|
+
|
|
7573
|
+
// src/widgets/ProductGridLit.ts
|
|
7574
|
+
import { html as html12, LitElement as LitElement11, nothing as nothing7 } from "lit";
|
|
7660
7575
|
var _controller3, _ProductGridLit_instances, startBind_fn3;
|
|
7661
|
-
var ProductGridLit = class extends
|
|
7576
|
+
var ProductGridLit = class extends LitElement11 {
|
|
7662
7577
|
constructor() {
|
|
7663
7578
|
super(...arguments);
|
|
7664
7579
|
__privateAdd(this, _ProductGridLit_instances);
|
|
@@ -7684,20 +7599,20 @@ var ProductGridLit = class extends LitElement10 {
|
|
|
7684
7599
|
__privateGet(this, _controller3)?.destroy();
|
|
7685
7600
|
}
|
|
7686
7601
|
render() {
|
|
7687
|
-
if (this.validationError || !this.props) return
|
|
7602
|
+
if (this.validationError || !this.props) return html12``;
|
|
7688
7603
|
const parsed = gridSchema.safeParse(this.props);
|
|
7689
|
-
if (!parsed.success) return
|
|
7604
|
+
if (!parsed.success) return html12``;
|
|
7690
7605
|
const { products, desktopColumns, heading } = parsed.data;
|
|
7691
|
-
return
|
|
7606
|
+
return html12`
|
|
7692
7607
|
<section class="sc-product-grid">
|
|
7693
|
-
${heading ?
|
|
7608
|
+
${heading ? html12`<h2 data-grid-heading class="sc-product-grid__heading">${heading}</h2>` : nothing7}
|
|
7694
7609
|
<div
|
|
7695
7610
|
data-grid-root
|
|
7696
7611
|
class="sc-product-grid__cells"
|
|
7697
7612
|
style=${`--sc-product-grid-cols:${desktopColumns}`}
|
|
7698
7613
|
>
|
|
7699
7614
|
${products.map(
|
|
7700
|
-
(p) =>
|
|
7615
|
+
(p) => html12`<div data-grid-cell class="sc-product-grid__cell">
|
|
7701
7616
|
${renderProductCard(p, "standard", this.resolved[p.id])}
|
|
7702
7617
|
</div>`
|
|
7703
7618
|
)}
|
|
@@ -7710,6 +7625,7 @@ _controller3 = new WeakMap();
|
|
|
7710
7625
|
_ProductGridLit_instances = new WeakSet();
|
|
7711
7626
|
startBind_fn3 = function() {
|
|
7712
7627
|
__privateGet(this, _controller3)?.destroy();
|
|
7628
|
+
const previous = this.resolved;
|
|
7713
7629
|
this.resolved = {};
|
|
7714
7630
|
if (!this.props) {
|
|
7715
7631
|
this.validationError = false;
|
|
@@ -7722,12 +7638,9 @@ startBind_fn3 = function() {
|
|
|
7722
7638
|
return;
|
|
7723
7639
|
}
|
|
7724
7640
|
this.validationError = false;
|
|
7641
|
+
this.resolved = keepResolved(previous, result.data.products);
|
|
7725
7642
|
__privateSet(this, _controller3, new BindController(result.data.products, (productId, kind, value) => {
|
|
7726
|
-
|
|
7727
|
-
this.resolved = {
|
|
7728
|
-
...this.resolved,
|
|
7729
|
-
[productId]: { ...this.resolved[productId], [kind]: value }
|
|
7730
|
-
};
|
|
7643
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
7731
7644
|
}));
|
|
7732
7645
|
__privateGet(this, _controller3).start();
|
|
7733
7646
|
};
|
|
@@ -7738,9 +7651,9 @@ ProductGridLit.properties = {
|
|
|
7738
7651
|
};
|
|
7739
7652
|
|
|
7740
7653
|
// src/widgets/ProductHeroLit.ts
|
|
7741
|
-
import { html as
|
|
7654
|
+
import { html as html13, LitElement as LitElement12, nothing as nothing8 } from "lit";
|
|
7742
7655
|
var _controller4, _ProductHeroLit_instances, startBind_fn4;
|
|
7743
|
-
var ProductHeroLit = class extends
|
|
7656
|
+
var ProductHeroLit = class extends LitElement12 {
|
|
7744
7657
|
constructor() {
|
|
7745
7658
|
super(...arguments);
|
|
7746
7659
|
__privateAdd(this, _ProductHeroLit_instances);
|
|
@@ -7766,9 +7679,9 @@ var ProductHeroLit = class extends LitElement11 {
|
|
|
7766
7679
|
__privateGet(this, _controller4)?.destroy();
|
|
7767
7680
|
}
|
|
7768
7681
|
render() {
|
|
7769
|
-
if (this.validationError || !this.props) return
|
|
7682
|
+
if (this.validationError || !this.props) return html13``;
|
|
7770
7683
|
const parsed = heroSchema.safeParse(this.props);
|
|
7771
|
-
if (!parsed.success) return
|
|
7684
|
+
if (!parsed.success) return html13``;
|
|
7772
7685
|
const { product, layout, longCopy } = parsed.data;
|
|
7773
7686
|
const resolved = this.resolved[product.id];
|
|
7774
7687
|
const imgSrc = resolved?.image ?? product.image?.src;
|
|
@@ -7777,15 +7690,15 @@ var ProductHeroLit = class extends LitElement11 {
|
|
|
7777
7690
|
const sectionStyles = isTopLayout ? "display:block;" : `display:flex;flex-direction:${layout === "image-right" ? "row-reverse" : "row"};gap:1rem;align-items:flex-start;`;
|
|
7778
7691
|
const imageStyles = isTopLayout ? "width:100%;max-height:160px;object-fit:cover;border-radius:8px;display:block;margin-bottom:0.75rem;" : "width:128px;height:128px;object-fit:cover;border-radius:8px;flex-shrink:0;";
|
|
7779
7692
|
const contentStyles = isTopLayout ? "" : "flex:1;min-width:0;";
|
|
7780
|
-
return
|
|
7693
|
+
return html13`
|
|
7781
7694
|
<section class="sc-product-hero" data-hero-layout=${layout} style=${sectionStyles}>
|
|
7782
|
-
${imgSrc ?
|
|
7695
|
+
${imgSrc ? html13`<img
|
|
7783
7696
|
class="sc-product-hero__image"
|
|
7784
7697
|
src=${imgSrc}
|
|
7785
7698
|
alt=${product.image?.alt ?? product.name}
|
|
7786
7699
|
loading="lazy"
|
|
7787
7700
|
style=${imageStyles}
|
|
7788
|
-
/>` :
|
|
7701
|
+
/>` : html13`<div
|
|
7789
7702
|
class="sc-product-hero__image"
|
|
7790
7703
|
data-product-image-standin
|
|
7791
7704
|
aria-hidden="true"
|
|
@@ -7793,25 +7706,25 @@ var ProductHeroLit = class extends LitElement11 {
|
|
|
7793
7706
|
>${standinMonogram(product.name)}</div>`}
|
|
7794
7707
|
<div class="sc-product-hero__content" style=${contentStyles}>
|
|
7795
7708
|
<h2 class="sc-product-hero__name">${product.name}</h2>
|
|
7796
|
-
${product.tagline ?
|
|
7797
|
-
${product.price ?
|
|
7798
|
-
${priceText}${product.price.cadence ?
|
|
7799
|
-
</p>` :
|
|
7800
|
-
${longCopy ?
|
|
7709
|
+
${product.tagline ? html13`<p class="sc-product-hero__tagline">${product.tagline}</p>` : nothing8}
|
|
7710
|
+
${product.price ? html13`<p class="sc-product-hero__price">
|
|
7711
|
+
${priceText}${product.price.cadence ? html13` <small>${product.price.cadence}</small>` : nothing8}
|
|
7712
|
+
</p>` : nothing8}
|
|
7713
|
+
${longCopy ? html13`<div
|
|
7801
7714
|
data-hero-longcopy
|
|
7802
7715
|
class="sc-product-hero__longcopy"
|
|
7803
7716
|
.innerHTML=${sanitizeHtml(longCopy)}
|
|
7804
|
-
></div>` :
|
|
7717
|
+
></div>` : nothing8}
|
|
7805
7718
|
<div class="sc-product-hero__ctas">
|
|
7806
7719
|
${product.ctas.map(
|
|
7807
|
-
(
|
|
7720
|
+
(c, i) => html13`<a
|
|
7808
7721
|
class="sc-product-hero__cta"
|
|
7809
|
-
data-product-cta=${
|
|
7810
|
-
data-variant=${
|
|
7811
|
-
href=${
|
|
7812
|
-
target=${
|
|
7813
|
-
rel=${
|
|
7814
|
-
>${
|
|
7722
|
+
data-product-cta=${i === 0 ? "primary" : "secondary"}
|
|
7723
|
+
data-variant=${c.variant}
|
|
7724
|
+
href=${c.href ?? "#"}
|
|
7725
|
+
target=${c.target}
|
|
7726
|
+
rel=${c.target === "_blank" ? "noopener noreferrer" : ""}
|
|
7727
|
+
>${c.label}</a
|
|
7815
7728
|
>`
|
|
7816
7729
|
)}
|
|
7817
7730
|
</div>
|
|
@@ -7824,6 +7737,7 @@ _controller4 = new WeakMap();
|
|
|
7824
7737
|
_ProductHeroLit_instances = new WeakSet();
|
|
7825
7738
|
startBind_fn4 = function() {
|
|
7826
7739
|
__privateGet(this, _controller4)?.destroy();
|
|
7740
|
+
const previous = this.resolved;
|
|
7827
7741
|
this.resolved = {};
|
|
7828
7742
|
if (!this.props) {
|
|
7829
7743
|
this.validationError = false;
|
|
@@ -7836,12 +7750,9 @@ startBind_fn4 = function() {
|
|
|
7836
7750
|
return;
|
|
7837
7751
|
}
|
|
7838
7752
|
this.validationError = false;
|
|
7753
|
+
this.resolved = keepResolved(previous, [result.data.product]);
|
|
7839
7754
|
__privateSet(this, _controller4, new BindController([result.data.product], (productId, kind, value) => {
|
|
7840
|
-
|
|
7841
|
-
this.resolved = {
|
|
7842
|
-
...this.resolved,
|
|
7843
|
-
[productId]: { ...this.resolved[productId], [kind]: value }
|
|
7844
|
-
};
|
|
7755
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
7845
7756
|
}));
|
|
7846
7757
|
__privateGet(this, _controller4).start();
|
|
7847
7758
|
};
|
|
@@ -7852,15 +7763,17 @@ ProductHeroLit.properties = {
|
|
|
7852
7763
|
};
|
|
7853
7764
|
|
|
7854
7765
|
// src/widgets/ProductRecoCarouselLit.ts
|
|
7855
|
-
import { html as
|
|
7766
|
+
import { html as html14, LitElement as LitElement13, nothing as nothing9 } from "lit";
|
|
7856
7767
|
import { styleMap as styleMap2 } from "lit/directives/style-map.js";
|
|
7857
|
-
var _ProductRecoCarouselLit_instances, healthProbe_fn3;
|
|
7858
|
-
var ProductRecoCarouselLit = class extends
|
|
7768
|
+
var _unwatch3, _ProductRecoCarouselLit_instances, healthProbe_fn3, watchPrices_fn, stopWatching_fn;
|
|
7769
|
+
var ProductRecoCarouselLit = class extends LitElement13 {
|
|
7859
7770
|
constructor() {
|
|
7860
7771
|
super();
|
|
7861
7772
|
__privateAdd(this, _ProductRecoCarouselLit_instances);
|
|
7773
|
+
__privateAdd(this, _unwatch3, []);
|
|
7862
7774
|
this.props = void 0;
|
|
7863
7775
|
this._parsed = void 0;
|
|
7776
|
+
this._live = {};
|
|
7864
7777
|
new RenderHealthController(this, () => __privateMethod(this, _ProductRecoCarouselLit_instances, healthProbe_fn3).call(this));
|
|
7865
7778
|
}
|
|
7866
7779
|
createRenderRoot() {
|
|
@@ -7870,32 +7783,54 @@ var ProductRecoCarouselLit = class extends LitElement12 {
|
|
|
7870
7783
|
if (changed.has("props")) {
|
|
7871
7784
|
const r = this.props ? recoSchema.safeParse(this.props) : void 0;
|
|
7872
7785
|
this._parsed = r?.success ? r.data : void 0;
|
|
7786
|
+
this._live = {};
|
|
7787
|
+
if (this.isConnected) __privateMethod(this, _ProductRecoCarouselLit_instances, watchPrices_fn).call(this);
|
|
7873
7788
|
}
|
|
7874
7789
|
}
|
|
7790
|
+
connectedCallback() {
|
|
7791
|
+
super.connectedCallback();
|
|
7792
|
+
__privateMethod(this, _ProductRecoCarouselLit_instances, watchPrices_fn).call(this);
|
|
7793
|
+
}
|
|
7794
|
+
disconnectedCallback() {
|
|
7795
|
+
super.disconnectedCallback();
|
|
7796
|
+
__privateMethod(this, _ProductRecoCarouselLit_instances, stopWatching_fn).call(this);
|
|
7797
|
+
}
|
|
7875
7798
|
render() {
|
|
7876
7799
|
const items = this._parsed?.items ?? [];
|
|
7877
|
-
if (items.length === 0) return
|
|
7878
|
-
return
|
|
7800
|
+
if (items.length === 0) return html14``;
|
|
7801
|
+
return html14`
|
|
7879
7802
|
<section class="sc-reco" style="box-sizing:border-box;display:flex;flex-direction:column;height:100%;padding:2px var(--sc-reco-inset,12px) var(--sc-reco-inset,12px);">
|
|
7880
7803
|
<div class="sc-reco__carousel" role="list" style="display:flex;align-items:stretch;gap:8px;flex:1 1 auto;min-height:0;overflow-x:auto;scroll-snap-type:x mandatory;">
|
|
7881
|
-
${items.map(
|
|
7882
|
-
|
|
7804
|
+
${items.map((it) => {
|
|
7805
|
+
const shownPrice = it.product_id && this._live[it.product_id] || it.price;
|
|
7806
|
+
const label = [it.name, shownPrice, it.hook].filter(Boolean).join(", ");
|
|
7807
|
+
return html14`
|
|
7883
7808
|
<div role="listitem" style="display:contents;">
|
|
7884
7809
|
<a data-reco-item data-product-cta="primary" href=${it.pdp_href}
|
|
7810
|
+
aria-label=${label || nothing9}
|
|
7885
7811
|
style=${styleMap2({ position: "relative", flex: "0 0 auto", aspectRatio: "4 / 5", minHeight: "var(--sc-reco-item-min-h, 162px)", maxHeight: "var(--sc-reco-item-max-h, 340px)", borderRadius: "12px", overflow: "hidden", scrollSnapAlign: "start", textDecoration: "none", display: "block", background: "#e5e7eb" })}>
|
|
7886
|
-
${it.image_url ?
|
|
7812
|
+
${it.image_url ? html14`<img src=${it.image_url} alt=${it.hook ?? "Recommended product"} loading="lazy" style="position:absolute;inset:0;width:100%;height:100%;object-fit:cover;">` : (
|
|
7813
|
+
// No photo from the store: the same branded stand-in the
|
|
7814
|
+
// card and comparison paint (imageStandin.ts), never a
|
|
7815
|
+
// flat grey box. Decorative: the product name is shown as
|
|
7816
|
+
// text below it. Nothing to name it by -> no monogram (a
|
|
7817
|
+
// '?' reads as broken).
|
|
7818
|
+
html14`<div data-product-image-standin aria-hidden="true" style="position:absolute;inset:0;background:${IMAGE_STANDIN_BACKGROUND};">${it.name || it.hook ? standinMonogram(it.name || it.hook) : nothing9}</div>`
|
|
7819
|
+
)}
|
|
7887
7820
|
<span style="position:absolute;inset:auto 0 0 0;height:60%;background:linear-gradient(transparent,rgba(0,0,0,.62));"></span>
|
|
7888
7821
|
<span style="position:absolute;left:8px;right:8px;bottom:8px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.5);">
|
|
7889
|
-
${it.
|
|
7890
|
-
${
|
|
7822
|
+
${!it.image_url && it.name ? html14`<span data-reco-name style="display:block;font-weight:700;font-size:0.82rem;line-height:1.2;margin-bottom:2px;">${it.name}</span>` : nothing9}
|
|
7823
|
+
${shownPrice ? html14`<span style="display:block;font-weight:700;font-size:0.9rem;">${shownPrice}</span>` : nothing9}
|
|
7824
|
+
${it.hook ? html14`<span style="display:block;font-size:0.72rem;line-height:1.2;opacity:.95;">${it.hook}</span>` : nothing9}
|
|
7891
7825
|
</span>
|
|
7892
7826
|
</a>
|
|
7893
|
-
</div
|
|
7894
|
-
)}
|
|
7827
|
+
</div>`;
|
|
7828
|
+
})}
|
|
7895
7829
|
</div>
|
|
7896
7830
|
</section>`;
|
|
7897
7831
|
}
|
|
7898
7832
|
};
|
|
7833
|
+
_unwatch3 = new WeakMap();
|
|
7899
7834
|
_ProductRecoCarouselLit_instances = new WeakSet();
|
|
7900
7835
|
/** Measure only the SETTLED carousel (items rendered). An empty carousel
|
|
7901
7836
|
* renders nothing and is never "blank". NON-PII stateKey. */
|
|
@@ -7911,7 +7846,30 @@ healthProbe_fn3 = function() {
|
|
|
7911
7846
|
widgetKind: "adaptive-product:reco"
|
|
7912
7847
|
};
|
|
7913
7848
|
};
|
|
7914
|
-
|
|
7849
|
+
/** The store's live price for each item, now and whenever the runtime
|
|
7850
|
+
* publishes a commerce adapter; an item the store cannot price keeps the
|
|
7851
|
+
* catalog price the server sent. */
|
|
7852
|
+
watchPrices_fn = function() {
|
|
7853
|
+
__privateMethod(this, _ProductRecoCarouselLit_instances, stopWatching_fn).call(this);
|
|
7854
|
+
for (const item of this._parsed?.items ?? []) {
|
|
7855
|
+
const id = item.product_id;
|
|
7856
|
+
if (!id || !item.price) continue;
|
|
7857
|
+
__privateGet(this, _unwatch3).push(
|
|
7858
|
+
watchLivePrice(id, (live) => {
|
|
7859
|
+
this._live = { ...this._live, [id]: live.price };
|
|
7860
|
+
})
|
|
7861
|
+
);
|
|
7862
|
+
}
|
|
7863
|
+
};
|
|
7864
|
+
stopWatching_fn = function() {
|
|
7865
|
+
for (const stop of __privateGet(this, _unwatch3)) stop();
|
|
7866
|
+
__privateSet(this, _unwatch3, []);
|
|
7867
|
+
};
|
|
7868
|
+
ProductRecoCarouselLit.properties = {
|
|
7869
|
+
props: { attribute: false },
|
|
7870
|
+
_parsed: { state: true },
|
|
7871
|
+
_live: { state: true }
|
|
7872
|
+
};
|
|
7915
7873
|
ProductRecoCarouselLit._testing = {
|
|
7916
7874
|
healthProbe: (host) => {
|
|
7917
7875
|
var _a;
|
|
@@ -7922,7 +7880,7 @@ if (!customElements.get("syntro-product-reco-carousel"))
|
|
|
7922
7880
|
customElements.define("syntro-product-reco-carousel", ProductRecoCarouselLit);
|
|
7923
7881
|
|
|
7924
7882
|
// src/widgets/PdpTrendingNewsLit.ts
|
|
7925
|
-
import { html as
|
|
7883
|
+
import { html as html15, LitElement as LitElement14 } from "lit";
|
|
7926
7884
|
|
|
7927
7885
|
// src/widgets/entrance.ts
|
|
7928
7886
|
function armEntranceSettle(onSettle, container, settleMs, fastPath = false) {
|
|
@@ -7950,14 +7908,6 @@ function armEntranceSettle(onSettle, container, settleMs, fastPath = false) {
|
|
|
7950
7908
|
}
|
|
7951
7909
|
|
|
7952
7910
|
// src/widgets/PdpTrendingNewsLit.ts
|
|
7953
|
-
function ensureTrendingNewsStyles() {
|
|
7954
|
-
if (typeof document === "undefined") return;
|
|
7955
|
-
if (document.getElementById("syntro-pdp-trending-news-style")) return;
|
|
7956
|
-
const style = document.createElement("style");
|
|
7957
|
-
style.id = "syntro-pdp-trending-news-style";
|
|
7958
|
-
style.textContent = TRENDING_NEWS_CSS;
|
|
7959
|
-
document.head.appendChild(style);
|
|
7960
|
-
}
|
|
7961
7911
|
var TRENDING_NEWS_CSS = `
|
|
7962
7912
|
.pdp-trending-news {
|
|
7963
7913
|
--tn-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8163,11 +8113,12 @@ var TRENDING_NEWS_CSS = `
|
|
|
8163
8113
|
outline: none;
|
|
8164
8114
|
}
|
|
8165
8115
|
.pdp-tn-headline-link::after {
|
|
8166
|
-
|
|
8116
|
+
/* Decoration: empty alternative text keeps it out of the link's name. */
|
|
8117
|
+
content: ' \\2197' / '';
|
|
8167
8118
|
font-family: var(--tn-sans);
|
|
8168
8119
|
font-size: 0.65em;
|
|
8169
8120
|
letter-spacing: 0;
|
|
8170
|
-
color: var(--tn-primary);
|
|
8121
|
+
color: var(--syntro-pdp-primary-ink, var(--tn-primary));
|
|
8171
8122
|
opacity: 0.55;
|
|
8172
8123
|
vertical-align: 0.18em;
|
|
8173
8124
|
margin-left: 0.2em;
|
|
@@ -8186,7 +8137,7 @@ var TRENDING_NEWS_CSS = `
|
|
|
8186
8137
|
font-style: italic;
|
|
8187
8138
|
font-size: 11px;
|
|
8188
8139
|
letter-spacing: 0.06em;
|
|
8189
|
-
color: color-mix(in srgb, var(--tn-primary) 70%, transparent);
|
|
8140
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--tn-primary) 70%, transparent));
|
|
8190
8141
|
padding: 3px 9px 3px 8px;
|
|
8191
8142
|
border-radius: 999px;
|
|
8192
8143
|
background: color-mix(in srgb, var(--tn-primary) 12%, transparent);
|
|
@@ -8269,7 +8220,7 @@ var TRENDING_NEWS_CSS = `
|
|
|
8269
8220
|
}
|
|
8270
8221
|
}
|
|
8271
8222
|
`;
|
|
8272
|
-
var PdpTrendingNewsLit = class extends
|
|
8223
|
+
var PdpTrendingNewsLit = class extends LitElement14 {
|
|
8273
8224
|
constructor() {
|
|
8274
8225
|
super(...arguments);
|
|
8275
8226
|
this.data = void 0;
|
|
@@ -8282,7 +8233,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8282
8233
|
}
|
|
8283
8234
|
connectedCallback() {
|
|
8284
8235
|
super.connectedCallback();
|
|
8285
|
-
|
|
8236
|
+
ensureStylesInRoot(this, "syntro-pdp-trending-news-style", TRENDING_NEWS_CSS);
|
|
8286
8237
|
if (this._entering) this.requestUpdate();
|
|
8287
8238
|
}
|
|
8288
8239
|
disconnectedCallback() {
|
|
@@ -8305,7 +8256,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8305
8256
|
}
|
|
8306
8257
|
render() {
|
|
8307
8258
|
if (!this.data) {
|
|
8308
|
-
return
|
|
8259
|
+
return html15`<div data-skeleton class="pdp-trending-news-skeleton">
|
|
8309
8260
|
<div class="pdp-skeleton-bar" style="width:60%"></div>
|
|
8310
8261
|
<div class="pdp-skeleton-bar" style="width:90%"></div>
|
|
8311
8262
|
<div class="pdp-skeleton-bar" style="width:80%"></div>
|
|
@@ -8315,7 +8266,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8315
8266
|
const items = this.data.items;
|
|
8316
8267
|
const featured = items[0];
|
|
8317
8268
|
const rest = items.slice(1);
|
|
8318
|
-
return
|
|
8269
|
+
return html15`
|
|
8319
8270
|
<section
|
|
8320
8271
|
class="${this._entering ? "pdp-trending-news is-entering" : "pdp-trending-news"}"
|
|
8321
8272
|
aria-label="Trending news"
|
|
@@ -8332,7 +8283,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8332
8283
|
<p class="pdp-tn-blurb">${featured.blurb}</p>
|
|
8333
8284
|
</article>
|
|
8334
8285
|
${rest.map(
|
|
8335
|
-
(item) =>
|
|
8286
|
+
(item) => html15`
|
|
8336
8287
|
<article class="pdp-tn-card">
|
|
8337
8288
|
<div class="pdp-tn-meta">
|
|
8338
8289
|
<span class="pdp-tn-dot" aria-hidden="true"></span>
|
|
@@ -8354,8 +8305,8 @@ PdpTrendingNewsLit.properties = {
|
|
|
8354
8305
|
_entering: { state: true }
|
|
8355
8306
|
};
|
|
8356
8307
|
function renderHeadlineLink(item) {
|
|
8357
|
-
if (!item.url) return
|
|
8358
|
-
return
|
|
8308
|
+
if (!item.url) return html15`${item.headline}`;
|
|
8309
|
+
return html15`<a
|
|
8359
8310
|
class="pdp-tn-headline-link"
|
|
8360
8311
|
href=${item.url}
|
|
8361
8312
|
target="_blank"
|
|
@@ -8368,15 +8319,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
8368
8319
|
}
|
|
8369
8320
|
|
|
8370
8321
|
// src/widgets/PdpTrendingNewsMagazineLit.ts
|
|
8371
|
-
import { html as
|
|
8372
|
-
function ensureMagazineStyles() {
|
|
8373
|
-
if (typeof document === "undefined") return;
|
|
8374
|
-
if (document.getElementById("syntro-pdp-trending-news-magazine-style")) return;
|
|
8375
|
-
const style = document.createElement("style");
|
|
8376
|
-
style.id = "syntro-pdp-trending-news-magazine-style";
|
|
8377
|
-
style.textContent = MAGAZINE_CSS;
|
|
8378
|
-
document.head.appendChild(style);
|
|
8379
|
-
}
|
|
8322
|
+
import { html as html16, LitElement as LitElement15 } from "lit";
|
|
8380
8323
|
var MAGAZINE_CSS = `
|
|
8381
8324
|
.pdp-tn-mag {
|
|
8382
8325
|
--tn-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8548,7 +8491,7 @@ var MAGAZINE_CSS = `
|
|
|
8548
8491
|
}
|
|
8549
8492
|
}
|
|
8550
8493
|
`;
|
|
8551
|
-
var PdpTrendingNewsMagazineLit = class extends
|
|
8494
|
+
var PdpTrendingNewsMagazineLit = class extends LitElement15 {
|
|
8552
8495
|
constructor() {
|
|
8553
8496
|
super(...arguments);
|
|
8554
8497
|
this.data = void 0;
|
|
@@ -8558,17 +8501,17 @@ var PdpTrendingNewsMagazineLit = class extends LitElement14 {
|
|
|
8558
8501
|
}
|
|
8559
8502
|
connectedCallback() {
|
|
8560
8503
|
super.connectedCallback();
|
|
8561
|
-
|
|
8504
|
+
ensureStylesInRoot(this, "syntro-pdp-trending-news-magazine-style", MAGAZINE_CSS);
|
|
8562
8505
|
}
|
|
8563
8506
|
render() {
|
|
8564
8507
|
if (!this.data) {
|
|
8565
|
-
return
|
|
8508
|
+
return html16`<div aria-busy="true" class="pdp-tn-mag" style="opacity:0.5;">…</div>`;
|
|
8566
8509
|
}
|
|
8567
8510
|
const items = this.data.items;
|
|
8568
8511
|
const [lead, ...rest] = items;
|
|
8569
|
-
return
|
|
8512
|
+
return html16`
|
|
8570
8513
|
<section class="pdp-tn-mag" aria-label="Trending news">
|
|
8571
|
-
${lead ?
|
|
8514
|
+
${lead ? html16`<article class="pdp-tn-mag__lead">
|
|
8572
8515
|
<div class="pdp-tn-mag__meta">
|
|
8573
8516
|
<span class="pdp-tn-mag__meta-source">${lead.source}</span>
|
|
8574
8517
|
· ${lead.published_relative}
|
|
@@ -8577,7 +8520,7 @@ var PdpTrendingNewsMagazineLit = class extends LitElement14 {
|
|
|
8577
8520
|
<p class="pdp-tn-mag__blurb">${lead.blurb}</p>
|
|
8578
8521
|
</article>` : ""}
|
|
8579
8522
|
${rest.map(
|
|
8580
|
-
(it) =>
|
|
8523
|
+
(it) => html16`<article class="pdp-tn-mag__item">
|
|
8581
8524
|
<div class="pdp-tn-mag__meta">
|
|
8582
8525
|
<span class="pdp-tn-mag__meta-source">${it.source}</span>
|
|
8583
8526
|
· ${it.published_relative}
|
|
@@ -8594,8 +8537,8 @@ PdpTrendingNewsMagazineLit.properties = {
|
|
|
8594
8537
|
data: { attribute: false }
|
|
8595
8538
|
};
|
|
8596
8539
|
function renderHeadlineLink2(item) {
|
|
8597
|
-
if (!item.url) return
|
|
8598
|
-
return
|
|
8540
|
+
if (!item.url) return html16`${item.headline}`;
|
|
8541
|
+
return html16`<a
|
|
8599
8542
|
class="pdp-tn-mag__headline-link"
|
|
8600
8543
|
href=${item.url}
|
|
8601
8544
|
target="_blank"
|
|
@@ -8608,15 +8551,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
8608
8551
|
}
|
|
8609
8552
|
|
|
8610
8553
|
// src/widgets/PdpTrendingNewsHeroLit.ts
|
|
8611
|
-
import { html as
|
|
8612
|
-
function ensureHeroStyles() {
|
|
8613
|
-
if (typeof document === "undefined") return;
|
|
8614
|
-
if (document.getElementById("syntro-pdp-trending-news-hero-style")) return;
|
|
8615
|
-
const style = document.createElement("style");
|
|
8616
|
-
style.id = "syntro-pdp-trending-news-hero-style";
|
|
8617
|
-
style.textContent = HERO_CSS;
|
|
8618
|
-
document.head.appendChild(style);
|
|
8619
|
-
}
|
|
8554
|
+
import { html as html17, LitElement as LitElement16 } from "lit";
|
|
8620
8555
|
var HERO_CSS = `
|
|
8621
8556
|
.pdp-tn-hero {
|
|
8622
8557
|
--tn-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8799,7 +8734,7 @@ var HERO_CSS = `
|
|
|
8799
8734
|
}
|
|
8800
8735
|
}
|
|
8801
8736
|
`;
|
|
8802
|
-
var PdpTrendingNewsHeroLit = class extends
|
|
8737
|
+
var PdpTrendingNewsHeroLit = class extends LitElement16 {
|
|
8803
8738
|
constructor() {
|
|
8804
8739
|
super(...arguments);
|
|
8805
8740
|
this.data = void 0;
|
|
@@ -8809,15 +8744,15 @@ var PdpTrendingNewsHeroLit = class extends LitElement15 {
|
|
|
8809
8744
|
}
|
|
8810
8745
|
connectedCallback() {
|
|
8811
8746
|
super.connectedCallback();
|
|
8812
|
-
|
|
8747
|
+
ensureStylesInRoot(this, "syntro-pdp-trending-news-hero-style", HERO_CSS);
|
|
8813
8748
|
}
|
|
8814
8749
|
render() {
|
|
8815
8750
|
if (!this.data) {
|
|
8816
|
-
return
|
|
8751
|
+
return html17`<div aria-busy="true" class="pdp-tn-hero" style="opacity:0.5;">…</div>`;
|
|
8817
8752
|
}
|
|
8818
8753
|
const [lead, ...rest] = this.data.items;
|
|
8819
|
-
if (!lead) return
|
|
8820
|
-
return
|
|
8754
|
+
if (!lead) return html17``;
|
|
8755
|
+
return html17`
|
|
8821
8756
|
<section class="pdp-tn-hero" aria-label="Trending news">
|
|
8822
8757
|
<article class="pdp-tn-hero__panel">
|
|
8823
8758
|
<blockquote class="pdp-tn-hero__quote">${lead.blurb}</blockquote>
|
|
@@ -8828,9 +8763,9 @@ var PdpTrendingNewsHeroLit = class extends LitElement15 {
|
|
|
8828
8763
|
</footer>
|
|
8829
8764
|
<p class="pdp-tn-hero__headline">${renderHeadlineLink3(lead)}</p>
|
|
8830
8765
|
</article>
|
|
8831
|
-
${rest.length ?
|
|
8766
|
+
${rest.length ? html17`<div class="pdp-tn-hero__followon">
|
|
8832
8767
|
${rest.map(
|
|
8833
|
-
(it) =>
|
|
8768
|
+
(it) => html17`<div class="pdp-tn-hero__row">
|
|
8834
8769
|
<p class="pdp-tn-hero__row-headline">${renderHeadlineLink3(it)}</p>
|
|
8835
8770
|
<span class="pdp-tn-hero__row-meta">${it.source}</span>
|
|
8836
8771
|
</div>`
|
|
@@ -8844,8 +8779,8 @@ PdpTrendingNewsHeroLit.properties = {
|
|
|
8844
8779
|
data: { attribute: false }
|
|
8845
8780
|
};
|
|
8846
8781
|
function renderHeadlineLink3(item) {
|
|
8847
|
-
if (!item.url) return
|
|
8848
|
-
return
|
|
8782
|
+
if (!item.url) return html17`${item.headline}`;
|
|
8783
|
+
return html17`<a
|
|
8849
8784
|
class="pdp-tn-hero__headline-link"
|
|
8850
8785
|
href=${item.url}
|
|
8851
8786
|
target="_blank"
|
|
@@ -8858,15 +8793,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
8858
8793
|
}
|
|
8859
8794
|
|
|
8860
8795
|
// src/widgets/PdpPeerFeedMasonryLit.ts
|
|
8861
|
-
import { html as
|
|
8862
|
-
function ensureMasonryStyles() {
|
|
8863
|
-
if (typeof document === "undefined") return;
|
|
8864
|
-
if (document.getElementById("syntro-pdp-peer-feed-masonry-style")) return;
|
|
8865
|
-
const style = document.createElement("style");
|
|
8866
|
-
style.id = "syntro-pdp-peer-feed-masonry-style";
|
|
8867
|
-
style.textContent = MASONRY_CSS;
|
|
8868
|
-
document.head.appendChild(style);
|
|
8869
|
-
}
|
|
8796
|
+
import { html as html18, LitElement as LitElement17 } from "lit";
|
|
8870
8797
|
var MASONRY_CSS = `
|
|
8871
8798
|
.pdp-pf-mas {
|
|
8872
8799
|
--pf-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8997,12 +8924,12 @@ var MASONRY_CSS = `
|
|
|
8997
8924
|
padding-top: 10px;
|
|
8998
8925
|
}
|
|
8999
8926
|
`;
|
|
9000
|
-
function personaHue(
|
|
8927
|
+
function personaHue(s) {
|
|
9001
8928
|
let h = 0;
|
|
9002
|
-
for (let
|
|
8929
|
+
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
|
|
9003
8930
|
return Math.abs(h) % 360;
|
|
9004
8931
|
}
|
|
9005
|
-
var PdpPeerFeedMasonryLit = class extends
|
|
8932
|
+
var PdpPeerFeedMasonryLit = class extends LitElement17 {
|
|
9006
8933
|
constructor() {
|
|
9007
8934
|
super(...arguments);
|
|
9008
8935
|
this.data = void 0;
|
|
@@ -9012,17 +8939,17 @@ var PdpPeerFeedMasonryLit = class extends LitElement16 {
|
|
|
9012
8939
|
}
|
|
9013
8940
|
connectedCallback() {
|
|
9014
8941
|
super.connectedCallback();
|
|
9015
|
-
|
|
8942
|
+
ensureStylesInRoot(this, "syntro-pdp-peer-feed-masonry-style", MASONRY_CSS);
|
|
9016
8943
|
}
|
|
9017
8944
|
render() {
|
|
9018
8945
|
if (!this.data) {
|
|
9019
|
-
return
|
|
8946
|
+
return html18`<div aria-busy="true" class="pdp-pf-mas" style="opacity:0.5;">…</div>`;
|
|
9020
8947
|
}
|
|
9021
|
-
return
|
|
8948
|
+
return html18`
|
|
9022
8949
|
<section class="pdp-pf-mas" aria-label="Peer voices">
|
|
9023
8950
|
<div class="pdp-pf-mas__grid">
|
|
9024
8951
|
${this.data.peers.map(
|
|
9025
|
-
(p) =>
|
|
8952
|
+
(p) => html18`<article
|
|
9026
8953
|
class="pdp-pf-mas__card"
|
|
9027
8954
|
style=${`--pf-avatar-hue: ${personaHue(p.persona)};`}
|
|
9028
8955
|
>
|
|
@@ -9050,15 +8977,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-pee
|
|
|
9050
8977
|
}
|
|
9051
8978
|
|
|
9052
8979
|
// src/widgets/PdpPeerFeedCarouselLit.ts
|
|
9053
|
-
import { html as
|
|
9054
|
-
function ensureCarouselStyles2() {
|
|
9055
|
-
if (typeof document === "undefined") return;
|
|
9056
|
-
if (document.getElementById("syntro-pdp-peer-feed-carousel-style")) return;
|
|
9057
|
-
const style = document.createElement("style");
|
|
9058
|
-
style.id = "syntro-pdp-peer-feed-carousel-style";
|
|
9059
|
-
style.textContent = CAROUSEL_CSS2;
|
|
9060
|
-
document.head.appendChild(style);
|
|
9061
|
-
}
|
|
8980
|
+
import { html as html19, LitElement as LitElement18, svg } from "lit";
|
|
9062
8981
|
var CAROUSEL_CSS2 = `
|
|
9063
8982
|
/* The custom-element tag itself + its slot-mount parent must declare
|
|
9064
8983
|
display:block AND min-width:0 so they participate in the grid cell's
|
|
@@ -9227,12 +9146,12 @@ syntro-pdp-peer-feed-carousel {
|
|
|
9227
9146
|
var GLYPHS = ["waveform", "gauge", "leaf", "spark"];
|
|
9228
9147
|
function glyphFor(persona) {
|
|
9229
9148
|
let h = 0;
|
|
9230
|
-
for (let
|
|
9149
|
+
for (let i = 0; i < persona.length; i++) h = h * 31 + persona.charCodeAt(i) | 0;
|
|
9231
9150
|
return GLYPHS[Math.abs(h) % GLYPHS.length];
|
|
9232
9151
|
}
|
|
9233
9152
|
function glyphHue(persona) {
|
|
9234
9153
|
let h = 0;
|
|
9235
|
-
for (let
|
|
9154
|
+
for (let i = 0; i < persona.length; i++) h = h * 17 + persona.charCodeAt(i) | 0;
|
|
9236
9155
|
return Math.abs(h) % 360;
|
|
9237
9156
|
}
|
|
9238
9157
|
function renderGlyph(kind) {
|
|
@@ -9266,7 +9185,7 @@ function renderGlyph(kind) {
|
|
|
9266
9185
|
<path d="M30 6 L34 26 L54 30 L34 34 L30 54 L26 34 L6 30 L26 26 Z" stroke="currentColor" stroke-width="1.6" fill="none" stroke-linejoin="round"/>
|
|
9267
9186
|
</svg>`;
|
|
9268
9187
|
}
|
|
9269
|
-
var PdpPeerFeedCarouselLit = class extends
|
|
9188
|
+
var PdpPeerFeedCarouselLit = class extends LitElement18 {
|
|
9270
9189
|
constructor() {
|
|
9271
9190
|
super(...arguments);
|
|
9272
9191
|
this.data = void 0;
|
|
@@ -9276,19 +9195,19 @@ var PdpPeerFeedCarouselLit = class extends LitElement17 {
|
|
|
9276
9195
|
}
|
|
9277
9196
|
connectedCallback() {
|
|
9278
9197
|
super.connectedCallback();
|
|
9279
|
-
|
|
9198
|
+
ensureStylesInRoot(this, "syntro-pdp-peer-feed-carousel-style", CAROUSEL_CSS2);
|
|
9280
9199
|
}
|
|
9281
9200
|
render() {
|
|
9282
9201
|
if (!this.data) {
|
|
9283
|
-
return
|
|
9202
|
+
return html19`<div aria-busy="true" class="pdp-pf-car" style="opacity:0.5;">…</div>`;
|
|
9284
9203
|
}
|
|
9285
|
-
return
|
|
9204
|
+
return html19`
|
|
9286
9205
|
<section class="pdp-pf-car" aria-label="Peer voices">
|
|
9287
9206
|
<div class="pdp-pf-car__track">
|
|
9288
9207
|
${this.data.peers.map((p) => {
|
|
9289
9208
|
const kind = glyphFor(p.persona);
|
|
9290
9209
|
const hue = glyphHue(p.persona);
|
|
9291
|
-
return
|
|
9210
|
+
return html19`<article class="pdp-pf-car__card" style=${`--pf-glyph-hue: ${hue};`}>
|
|
9292
9211
|
<span class="pdp-pf-car__glyph" aria-hidden="true">${renderGlyph(kind)}</span>
|
|
9293
9212
|
<span class="pdp-pf-car__persona">${p.persona}</span>
|
|
9294
9213
|
<blockquote class="pdp-pf-car__quote">${p.quote}</blockquote>
|
|
@@ -9313,7 +9232,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-pee
|
|
|
9313
9232
|
}
|
|
9314
9233
|
|
|
9315
9234
|
// src/widgets/PdpChartLit.ts
|
|
9316
|
-
import { html as
|
|
9235
|
+
import { html as html20, LitElement as LitElement19, nothing as nothing10, svg as svg2 } from "lit";
|
|
9317
9236
|
function formatNum(v) {
|
|
9318
9237
|
if (v === void 0 || !Number.isFinite(v)) return "\u2014";
|
|
9319
9238
|
if (Number.isInteger(v)) return v.toLocaleString("en-US");
|
|
@@ -9323,7 +9242,7 @@ function formatNum(v) {
|
|
|
9323
9242
|
return decPart ? `${formatted}.${decPart}` : formatted;
|
|
9324
9243
|
}
|
|
9325
9244
|
function buildComparisonMatrix(data) {
|
|
9326
|
-
const colLabels = data.series.map((
|
|
9245
|
+
const colLabels = data.series.map((s) => s.name);
|
|
9327
9246
|
const seenRows = /* @__PURE__ */ new Map();
|
|
9328
9247
|
const rowLabels = [];
|
|
9329
9248
|
for (const series of data.series) {
|
|
@@ -9391,7 +9310,7 @@ function niceTicks(yMax) {
|
|
|
9391
9310
|
const rawStep = yMax / targetCount;
|
|
9392
9311
|
const mag = 10 ** Math.floor(Math.log10(rawStep));
|
|
9393
9312
|
const niceSteps = [1, 2, 2.5, 5, 10];
|
|
9394
|
-
const step = niceSteps.find((
|
|
9313
|
+
const step = niceSteps.find((s) => s * mag >= rawStep);
|
|
9395
9314
|
const niceStep = step * mag;
|
|
9396
9315
|
const ticks = [];
|
|
9397
9316
|
for (let v = 0; v <= yMax + niceStep * 1e-3; v += niceStep) {
|
|
@@ -9402,8 +9321,8 @@ function niceTicks(yMax) {
|
|
|
9402
9321
|
function buildLinePaths(data) {
|
|
9403
9322
|
const seenX = /* @__PURE__ */ new Map();
|
|
9404
9323
|
const xLabels = [];
|
|
9405
|
-
for (const
|
|
9406
|
-
for (const p of
|
|
9324
|
+
for (const s of data.series) {
|
|
9325
|
+
for (const p of s.points) {
|
|
9407
9326
|
if (!seenX.has(p.x)) {
|
|
9408
9327
|
seenX.set(p.x, xLabels.length);
|
|
9409
9328
|
xLabels.push(p.x);
|
|
@@ -9412,16 +9331,16 @@ function buildLinePaths(data) {
|
|
|
9412
9331
|
}
|
|
9413
9332
|
const denom = Math.max(1, xLabels.length - 1);
|
|
9414
9333
|
let yMax = 0;
|
|
9415
|
-
for (const
|
|
9416
|
-
for (const p of
|
|
9334
|
+
for (const s of data.series) {
|
|
9335
|
+
for (const p of s.points) {
|
|
9417
9336
|
if (p.y > yMax) yMax = p.y;
|
|
9418
9337
|
}
|
|
9419
9338
|
}
|
|
9420
9339
|
if (yMax === 0) yMax = 1;
|
|
9421
|
-
const seriesOut = data.series.map((
|
|
9422
|
-
name:
|
|
9340
|
+
const seriesOut = data.series.map((s, sIdx) => ({
|
|
9341
|
+
name: s.name,
|
|
9423
9342
|
seriesIdx: sIdx,
|
|
9424
|
-
points:
|
|
9343
|
+
points: s.points.map((p) => {
|
|
9425
9344
|
const xIdx = seenX.get(p.x);
|
|
9426
9345
|
return {
|
|
9427
9346
|
cx: xIdx / denom * 100,
|
|
@@ -9433,14 +9352,6 @@ function buildLinePaths(data) {
|
|
|
9433
9352
|
}));
|
|
9434
9353
|
return { series: seriesOut, xLabels, yMax };
|
|
9435
9354
|
}
|
|
9436
|
-
function ensureChartStyles() {
|
|
9437
|
-
if (typeof document === "undefined") return;
|
|
9438
|
-
if (document.getElementById("syntro-pdp-chart-style")) return;
|
|
9439
|
-
const style = document.createElement("style");
|
|
9440
|
-
style.id = "syntro-pdp-chart-style";
|
|
9441
|
-
style.textContent = CHART_CSS;
|
|
9442
|
-
document.head.appendChild(style);
|
|
9443
|
-
}
|
|
9444
9355
|
var CHART_CSS = `
|
|
9445
9356
|
.pdp-chart-frame {
|
|
9446
9357
|
--pc-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -9808,12 +9719,12 @@ var CHART_CSS = `
|
|
|
9808
9719
|
background: linear-gradient(90deg, color-mix(in srgb, var(--pc-primary) 12%, transparent), transparent 80%);
|
|
9809
9720
|
}
|
|
9810
9721
|
.pdp-chart-table tbody tr.is-winning td:first-child {
|
|
9811
|
-
color: color-mix(in srgb, var(--pc-primary) 70%, white 30%);
|
|
9722
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pc-primary) 70%, white 30%));
|
|
9812
9723
|
}
|
|
9813
9724
|
.pdp-chart-table tbody tr.is-winning td:first-child::before {
|
|
9814
9725
|
content: '\\2726 ';
|
|
9815
9726
|
font-size: 11px;
|
|
9816
|
-
color: color-mix(in srgb, var(--pc-primary) 60%, white 40%);
|
|
9727
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pc-primary) 60%, white 40%));
|
|
9817
9728
|
vertical-align: 1px;
|
|
9818
9729
|
margin-right: 2px;
|
|
9819
9730
|
}
|
|
@@ -9936,7 +9847,7 @@ function colorVar(seriesIdx) {
|
|
|
9936
9847
|
return `var(--pc-color-${seriesIdx % PALETTE_SIZE})`;
|
|
9937
9848
|
}
|
|
9938
9849
|
var _PdpChartLit_instances, renderLegend_fn, renderTable_fn, renderBars_fn, renderLine_fn;
|
|
9939
|
-
var PdpChartLit = class extends
|
|
9850
|
+
var PdpChartLit = class extends LitElement19 {
|
|
9940
9851
|
constructor() {
|
|
9941
9852
|
super(...arguments);
|
|
9942
9853
|
__privateAdd(this, _PdpChartLit_instances);
|
|
@@ -9949,7 +9860,7 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
9949
9860
|
}
|
|
9950
9861
|
connectedCallback() {
|
|
9951
9862
|
super.connectedCallback();
|
|
9952
|
-
|
|
9863
|
+
ensureStylesInRoot(this, "syntro-pdp-chart-style", CHART_CSS);
|
|
9953
9864
|
if (this._entering) this.requestUpdate();
|
|
9954
9865
|
}
|
|
9955
9866
|
disconnectedCallback() {
|
|
@@ -9972,16 +9883,16 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
9972
9883
|
}
|
|
9973
9884
|
render() {
|
|
9974
9885
|
if (!this.data) {
|
|
9975
|
-
return
|
|
9886
|
+
return html20`<div data-skeleton class="pdp-chart-skeleton">
|
|
9976
9887
|
<div class="pdp-skeleton-bar" style="width:50%"></div>
|
|
9977
9888
|
<div class="pdp-skeleton-rect" style="height:200px"></div>
|
|
9978
9889
|
</div>`;
|
|
9979
9890
|
}
|
|
9980
9891
|
const isTable = this.data.viz_kind === "table";
|
|
9981
|
-
const allSeriesOnePoint = this.data.series.every((
|
|
9892
|
+
const allSeriesOnePoint = this.data.series.every((s) => s.points.length <= 1);
|
|
9982
9893
|
const effectiveKind = isTable ? "table" : this.data.viz_kind === "line" && !allSeriesOnePoint ? "line" : "bar";
|
|
9983
9894
|
const isMultiSeries = this.data.series.length > 1;
|
|
9984
|
-
return
|
|
9895
|
+
return html20`
|
|
9985
9896
|
<section
|
|
9986
9897
|
class="${this._entering ? "pdp-chart pdp-chart-frame is-entering" : "pdp-chart pdp-chart-frame"}"
|
|
9987
9898
|
aria-label="${this.data.title}"
|
|
@@ -9992,11 +9903,11 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
9992
9903
|
</div>
|
|
9993
9904
|
<h3 class="pdp-chart-title">${this.data.title}</h3>
|
|
9994
9905
|
|
|
9995
|
-
${effectiveKind === "table" ? __privateMethod(this, _PdpChartLit_instances, renderTable_fn).call(this) :
|
|
9996
|
-
${effectiveKind === "bar" ? __privateMethod(this, _PdpChartLit_instances, renderBars_fn).call(this) :
|
|
9997
|
-
${effectiveKind === "line" ? __privateMethod(this, _PdpChartLit_instances, renderLine_fn).call(this) :
|
|
9998
|
-
${effectiveKind !== "table" && isMultiSeries ? __privateMethod(this, _PdpChartLit_instances, renderLegend_fn).call(this) :
|
|
9999
|
-
${this.data.rationale ?
|
|
9906
|
+
${effectiveKind === "table" ? __privateMethod(this, _PdpChartLit_instances, renderTable_fn).call(this) : nothing10}
|
|
9907
|
+
${effectiveKind === "bar" ? __privateMethod(this, _PdpChartLit_instances, renderBars_fn).call(this) : nothing10}
|
|
9908
|
+
${effectiveKind === "line" ? __privateMethod(this, _PdpChartLit_instances, renderLine_fn).call(this) : nothing10}
|
|
9909
|
+
${effectiveKind !== "table" && isMultiSeries ? __privateMethod(this, _PdpChartLit_instances, renderLegend_fn).call(this) : nothing10}
|
|
9910
|
+
${this.data.rationale ? html20`<p class="pdp-chart-rationale">${this.data.rationale}</p>` : nothing10}
|
|
10000
9911
|
</section>
|
|
10001
9912
|
`;
|
|
10002
9913
|
}
|
|
@@ -10004,17 +9915,17 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
10004
9915
|
_PdpChartLit_instances = new WeakSet();
|
|
10005
9916
|
renderLegend_fn = function() {
|
|
10006
9917
|
const data = this.data;
|
|
10007
|
-
return
|
|
9918
|
+
return html20`
|
|
10008
9919
|
<ol class="pdp-chart-legend" aria-label="Legend">
|
|
10009
9920
|
${data.series.map(
|
|
10010
|
-
(
|
|
9921
|
+
(s, idx) => html20`
|
|
10011
9922
|
<li class="pdp-chart-legend-item">
|
|
10012
9923
|
<span
|
|
10013
9924
|
class="pdp-chart-legend-swatch"
|
|
10014
9925
|
style="background:${colorVar(idx)}"
|
|
10015
9926
|
aria-hidden="true"
|
|
10016
9927
|
></span>
|
|
10017
|
-
<span>${
|
|
9928
|
+
<span>${s.name}</span>
|
|
10018
9929
|
</li>
|
|
10019
9930
|
`
|
|
10020
9931
|
)}
|
|
@@ -10023,22 +9934,22 @@ renderLegend_fn = function() {
|
|
|
10023
9934
|
};
|
|
10024
9935
|
renderTable_fn = function() {
|
|
10025
9936
|
const matrix = buildComparisonMatrix(this.data);
|
|
10026
|
-
return
|
|
9937
|
+
return html20`
|
|
10027
9938
|
<div class="pdp-chart-table-wrap">
|
|
10028
9939
|
<table class="pdp-chart-table">
|
|
10029
9940
|
<thead>
|
|
10030
9941
|
<tr>
|
|
10031
9942
|
<th></th>
|
|
10032
|
-
${matrix.colLabels.map((label) =>
|
|
9943
|
+
${matrix.colLabels.map((label) => html20`<th>${label}</th>`)}
|
|
10033
9944
|
</tr>
|
|
10034
9945
|
</thead>
|
|
10035
9946
|
<tbody>
|
|
10036
9947
|
${matrix.rowLabels.map(
|
|
10037
|
-
(row, rowIdx) =>
|
|
9948
|
+
(row, rowIdx) => html20`
|
|
10038
9949
|
<tr class=${rowIdx === matrix.winningRow ? "is-winning" : ""}>
|
|
10039
9950
|
<td>${row}</td>
|
|
10040
9951
|
${matrix.cells[rowIdx].map(
|
|
10041
|
-
(cell) =>
|
|
9952
|
+
(cell) => html20`<td class="pdp-chart-numeric-cell">${formatNum(cell)}</td>`
|
|
10042
9953
|
)}
|
|
10043
9954
|
</tr>
|
|
10044
9955
|
`
|
|
@@ -10053,7 +9964,7 @@ renderBars_fn = function() {
|
|
|
10053
9964
|
const { buckets, yMax } = buildBarBuckets(data);
|
|
10054
9965
|
const ticks = niceTicks(yMax);
|
|
10055
9966
|
const scaleMax = Math.max(yMax, ticks[ticks.length - 1]);
|
|
10056
|
-
return
|
|
9967
|
+
return html20`
|
|
10057
9968
|
<div
|
|
10058
9969
|
class="pdp-chart-bars"
|
|
10059
9970
|
role="figure"
|
|
@@ -10062,33 +9973,33 @@ renderBars_fn = function() {
|
|
|
10062
9973
|
>
|
|
10063
9974
|
<div class="pdp-chart-yaxis" aria-hidden="true">
|
|
10064
9975
|
${ticks.map(
|
|
10065
|
-
(
|
|
9976
|
+
(t) => html20`
|
|
10066
9977
|
<span
|
|
10067
9978
|
class="pdp-chart-yaxis-tick"
|
|
10068
|
-
style="bottom:${
|
|
9979
|
+
style="bottom:${t / scaleMax * 100}%"
|
|
10069
9980
|
>
|
|
10070
|
-
${formatNum(
|
|
9981
|
+
${formatNum(t)}
|
|
10071
9982
|
</span>
|
|
10072
9983
|
`
|
|
10073
9984
|
)}
|
|
10074
9985
|
</div>
|
|
10075
9986
|
<div class="pdp-chart-bars-area">
|
|
10076
9987
|
${ticks.map(
|
|
10077
|
-
(
|
|
9988
|
+
(t) => html20`
|
|
10078
9989
|
<div
|
|
10079
9990
|
class="pdp-chart-bars-gridline"
|
|
10080
|
-
style="bottom:${
|
|
9991
|
+
style="bottom:${t / scaleMax * 100}%"
|
|
10081
9992
|
aria-hidden="true"
|
|
10082
9993
|
></div>
|
|
10083
9994
|
`
|
|
10084
9995
|
)}
|
|
10085
9996
|
<div class="pdp-chart-bars-baseline" aria-hidden="true"></div>
|
|
10086
9997
|
${buckets.map(
|
|
10087
|
-
(bucket, bIdx) =>
|
|
9998
|
+
(bucket, bIdx) => html20`
|
|
10088
9999
|
<div class="pdp-chart-bars-bucket">
|
|
10089
10000
|
<div class="pdp-chart-bars-stack">
|
|
10090
10001
|
${bucket.bars.map(
|
|
10091
|
-
(bar, barIdxInBucket) =>
|
|
10002
|
+
(bar, barIdxInBucket) => html20`
|
|
10092
10003
|
<div
|
|
10093
10004
|
class="pdp-chart-bar"
|
|
10094
10005
|
style="
|
|
@@ -10099,9 +10010,9 @@ renderBars_fn = function() {
|
|
|
10099
10010
|
role="img"
|
|
10100
10011
|
aria-label="${bucket.label} — ${bar.seriesName}: ${formatNum(bar.value)}"
|
|
10101
10012
|
>
|
|
10102
|
-
${bucket.bars.length === 1 ?
|
|
10013
|
+
${bucket.bars.length === 1 ? html20`<span class="pdp-chart-bar-value"
|
|
10103
10014
|
>${formatNum(bar.value)}</span
|
|
10104
|
-
>` :
|
|
10015
|
+
>` : nothing10}
|
|
10105
10016
|
</div>
|
|
10106
10017
|
`
|
|
10107
10018
|
)}
|
|
@@ -10119,7 +10030,7 @@ renderLine_fn = function() {
|
|
|
10119
10030
|
const { series, xLabels, yMax } = buildLinePaths(data);
|
|
10120
10031
|
const ticks = niceTicks(yMax);
|
|
10121
10032
|
const scaleMax = Math.max(yMax, ticks[ticks.length - 1]);
|
|
10122
|
-
return
|
|
10033
|
+
return html20`
|
|
10123
10034
|
<div
|
|
10124
10035
|
class="pdp-chart-line"
|
|
10125
10036
|
role="figure"
|
|
@@ -10129,12 +10040,12 @@ renderLine_fn = function() {
|
|
|
10129
10040
|
>
|
|
10130
10041
|
<div class="pdp-chart-line-yaxis" aria-hidden="true">
|
|
10131
10042
|
${ticks.map(
|
|
10132
|
-
(
|
|
10043
|
+
(t) => html20`
|
|
10133
10044
|
<span
|
|
10134
10045
|
class="pdp-chart-yaxis-tick"
|
|
10135
|
-
style="bottom:${
|
|
10046
|
+
style="bottom:${t / scaleMax * 100}%"
|
|
10136
10047
|
>
|
|
10137
|
-
${formatNum(
|
|
10048
|
+
${formatNum(t)}
|
|
10138
10049
|
</span>
|
|
10139
10050
|
`
|
|
10140
10051
|
)}
|
|
@@ -10146,25 +10057,25 @@ renderLine_fn = function() {
|
|
|
10146
10057
|
preserveAspectRatio="none"
|
|
10147
10058
|
aria-hidden="true"
|
|
10148
10059
|
>
|
|
10149
|
-
${ticks.map((
|
|
10150
|
-
const y = 60 -
|
|
10060
|
+
${ticks.map((t) => {
|
|
10061
|
+
const y = 60 - t / scaleMax * 56 - 2;
|
|
10151
10062
|
return svg2`<line class="pdp-chart-line-gridline" x1="0" y1=${y} x2="100" y2=${y} />`;
|
|
10152
10063
|
})}
|
|
10153
10064
|
${series.map(
|
|
10154
|
-
(
|
|
10065
|
+
(s) => svg2`
|
|
10155
10066
|
<polyline
|
|
10156
10067
|
class="pdp-chart-line-stroke"
|
|
10157
|
-
style=${`--line-color:${colorVar(
|
|
10158
|
-
points=${
|
|
10068
|
+
style=${`--line-color:${colorVar(s.seriesIdx)}; --delay:${s.seriesIdx * 200}ms`}
|
|
10069
|
+
points=${s.points.map((p) => `${p.cx},${p.cy}`).join(" ")}
|
|
10159
10070
|
/>
|
|
10160
|
-
${
|
|
10071
|
+
${s.points.map(
|
|
10161
10072
|
(p, idx) => svg2`
|
|
10162
10073
|
<circle
|
|
10163
10074
|
class="pdp-chart-line-dot"
|
|
10164
10075
|
cx=${p.cx}
|
|
10165
10076
|
cy=${p.cy}
|
|
10166
10077
|
r="1.4"
|
|
10167
|
-
style=${`--line-color:${colorVar(
|
|
10078
|
+
style=${`--line-color:${colorVar(s.seriesIdx)}; --delay:${s.seriesIdx * 200}ms; --dot-delay:${800 + idx * 60}ms`}
|
|
10168
10079
|
/>
|
|
10169
10080
|
`
|
|
10170
10081
|
)}
|
|
@@ -10178,7 +10089,7 @@ renderLine_fn = function() {
|
|
|
10178
10089
|
style="--col-count:${xLabels.length}"
|
|
10179
10090
|
aria-hidden="true"
|
|
10180
10091
|
>
|
|
10181
|
-
${xLabels.map((label) =>
|
|
10092
|
+
${xLabels.map((label) => html20`<span>${label}</span>`)}
|
|
10182
10093
|
</div>
|
|
10183
10094
|
`;
|
|
10184
10095
|
};
|
|
@@ -10191,15 +10102,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
10191
10102
|
}
|
|
10192
10103
|
|
|
10193
10104
|
// src/widgets/PdpChartBarStackedLit.ts
|
|
10194
|
-
import { html as
|
|
10195
|
-
function ensureStyles() {
|
|
10196
|
-
if (typeof document === "undefined") return;
|
|
10197
|
-
if (document.getElementById("syntro-pdp-chart-bar-style")) return;
|
|
10198
|
-
const s4 = document.createElement("style");
|
|
10199
|
-
s4.id = "syntro-pdp-chart-bar-style";
|
|
10200
|
-
s4.textContent = CSS;
|
|
10201
|
-
document.head.appendChild(s4);
|
|
10202
|
-
}
|
|
10105
|
+
import { html as html21, LitElement as LitElement20 } from "lit";
|
|
10203
10106
|
var CSS = `
|
|
10204
10107
|
.pdp-chart-bar {
|
|
10205
10108
|
--c-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10279,7 +10182,7 @@ var CSS = `
|
|
|
10279
10182
|
}
|
|
10280
10183
|
`;
|
|
10281
10184
|
var PALETTE = ["hsl(150 32% 50%)", "hsl(40 75% 60%)", "hsl(208 50% 55%)", "hsl(320 38% 60%)"];
|
|
10282
|
-
var PdpChartBarStackedLit = class extends
|
|
10185
|
+
var PdpChartBarStackedLit = class extends LitElement20 {
|
|
10283
10186
|
constructor() {
|
|
10284
10187
|
super(...arguments);
|
|
10285
10188
|
this.data = void 0;
|
|
@@ -10289,39 +10192,39 @@ var PdpChartBarStackedLit = class extends LitElement19 {
|
|
|
10289
10192
|
}
|
|
10290
10193
|
connectedCallback() {
|
|
10291
10194
|
super.connectedCallback();
|
|
10292
|
-
|
|
10195
|
+
ensureStylesInRoot(this, "syntro-pdp-chart-bar-style", CSS);
|
|
10293
10196
|
}
|
|
10294
10197
|
render() {
|
|
10295
|
-
if (!this.data) return
|
|
10198
|
+
if (!this.data) return html21`<div class="pdp-chart-bar" style="opacity:0.5;">…</div>`;
|
|
10296
10199
|
const { title, series } = this.data;
|
|
10297
|
-
const allX = Array.from(new Set(series.flatMap((
|
|
10298
|
-
const max = Math.max(...series.flatMap((
|
|
10299
|
-
return
|
|
10200
|
+
const allX = Array.from(new Set(series.flatMap((s) => s.points.map((p) => p.x))));
|
|
10201
|
+
const max = Math.max(...series.flatMap((s) => s.points.map((p) => p.y)), 1);
|
|
10202
|
+
return html21`
|
|
10300
10203
|
<section class="pdp-chart-bar" aria-label=${title}>
|
|
10301
10204
|
<h3 class="pdp-chart-bar__title">${title}</h3>
|
|
10302
|
-
${series.length > 1 ?
|
|
10205
|
+
${series.length > 1 ? html21`<div class="pdp-chart-bar__legend">
|
|
10303
10206
|
${series.map(
|
|
10304
|
-
(
|
|
10207
|
+
(s, i) => html21`<span
|
|
10305
10208
|
><span
|
|
10306
10209
|
class="pdp-chart-bar__legend-dot"
|
|
10307
|
-
style=${`background: ${PALETTE[
|
|
10210
|
+
style=${`background: ${PALETTE[i % PALETTE.length]};`}
|
|
10308
10211
|
></span
|
|
10309
|
-
>${
|
|
10212
|
+
>${s.name}</span
|
|
10310
10213
|
>`
|
|
10311
10214
|
)}
|
|
10312
10215
|
</div>` : ""}
|
|
10313
10216
|
${allX.map(
|
|
10314
|
-
(x) =>
|
|
10217
|
+
(x) => html21`<div class="pdp-chart-bar__row">
|
|
10315
10218
|
<div class="pdp-chart-bar__row-label">${x}</div>
|
|
10316
10219
|
<div class="pdp-chart-bar__row-bars">
|
|
10317
|
-
${series.map((
|
|
10318
|
-
const point =
|
|
10220
|
+
${series.map((s, i) => {
|
|
10221
|
+
const point = s.points.find((p) => p.x === x);
|
|
10319
10222
|
if (!point) return "";
|
|
10320
10223
|
const pct = point.y / max * 100;
|
|
10321
|
-
return
|
|
10224
|
+
return html21`<div class="pdp-chart-bar__bar-track">
|
|
10322
10225
|
<div
|
|
10323
10226
|
class="pdp-chart-bar__bar-fill"
|
|
10324
|
-
style=${`width: ${pct.toFixed(1)}%; background: ${PALETTE[
|
|
10227
|
+
style=${`width: ${pct.toFixed(1)}%; background: ${PALETTE[i % PALETTE.length]};`}
|
|
10325
10228
|
></div>
|
|
10326
10229
|
<span class="pdp-chart-bar__bar-value">${point.y}</span>
|
|
10327
10230
|
</div>`;
|
|
@@ -10341,15 +10244,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
10341
10244
|
}
|
|
10342
10245
|
|
|
10343
10246
|
// src/widgets/PdpChartMetricPullLit.ts
|
|
10344
|
-
import { html as
|
|
10345
|
-
function ensureStyles2() {
|
|
10346
|
-
if (typeof document === "undefined") return;
|
|
10347
|
-
if (document.getElementById("syntro-pdp-chart-metric-style")) return;
|
|
10348
|
-
const s4 = document.createElement("style");
|
|
10349
|
-
s4.id = "syntro-pdp-chart-metric-style";
|
|
10350
|
-
s4.textContent = CSS2;
|
|
10351
|
-
document.head.appendChild(s4);
|
|
10352
|
-
}
|
|
10247
|
+
import { html as html22, LitElement as LitElement21, svg as svg3 } from "lit";
|
|
10353
10248
|
var CSS2 = `
|
|
10354
10249
|
.pdp-chart-met {
|
|
10355
10250
|
--c-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10443,17 +10338,17 @@ var CSS2 = `
|
|
|
10443
10338
|
function buildSparkline(points) {
|
|
10444
10339
|
const w = 200;
|
|
10445
10340
|
const h = 38;
|
|
10446
|
-
const xs = points.map((_,
|
|
10341
|
+
const xs = points.map((_, i) => i / Math.max(points.length - 1, 1) * (w - 4) + 2);
|
|
10447
10342
|
const min = Math.min(...points.map((p) => p.y));
|
|
10448
10343
|
const max = Math.max(...points.map((p) => p.y));
|
|
10449
10344
|
const range = max - min || 1;
|
|
10450
10345
|
const ys = points.map((p) => h - 4 - (p.y - min) / range * (h - 8));
|
|
10451
|
-
const line = xs.map((x,
|
|
10346
|
+
const line = xs.map((x, i) => `${i === 0 ? "M" : "L"} ${x.toFixed(1)} ${ys[i].toFixed(1)}`).join(" ");
|
|
10452
10347
|
const area = `${line} L ${xs[xs.length - 1].toFixed(1)} ${h} L ${xs[0].toFixed(1)} ${h} Z`;
|
|
10453
|
-
const peakIndex = points.reduce((best, p,
|
|
10348
|
+
const peakIndex = points.reduce((best, p, i) => p.y > points[best].y ? i : best, 0);
|
|
10454
10349
|
return { linePath: line, areaPath: area, peakIndex };
|
|
10455
10350
|
}
|
|
10456
|
-
var PdpChartMetricPullLit = class extends
|
|
10351
|
+
var PdpChartMetricPullLit = class extends LitElement21 {
|
|
10457
10352
|
constructor() {
|
|
10458
10353
|
super(...arguments);
|
|
10459
10354
|
this.data = void 0;
|
|
@@ -10463,16 +10358,16 @@ var PdpChartMetricPullLit = class extends LitElement20 {
|
|
|
10463
10358
|
}
|
|
10464
10359
|
connectedCallback() {
|
|
10465
10360
|
super.connectedCallback();
|
|
10466
|
-
|
|
10361
|
+
ensureStylesInRoot(this, "syntro-pdp-chart-metric-style", CSS2);
|
|
10467
10362
|
}
|
|
10468
10363
|
render() {
|
|
10469
|
-
if (!this.data) return
|
|
10364
|
+
if (!this.data) return html22`<div class="pdp-chart-met" style="opacity:0.5;">…</div>`;
|
|
10470
10365
|
const { title, series } = this.data;
|
|
10471
10366
|
const first = series[0];
|
|
10472
|
-
if (!first || first.points.length === 0) return
|
|
10367
|
+
if (!first || first.points.length === 0) return html22``;
|
|
10473
10368
|
const points = first.points;
|
|
10474
10369
|
const peak = points.reduce((b, p) => p.y > b.y ? p : b, points[0]);
|
|
10475
|
-
const mean = points.reduce((
|
|
10370
|
+
const mean = points.reduce((s, p) => s + p.y, 0) / points.length;
|
|
10476
10371
|
const delta = (peak.y - mean) / Math.max(mean, 1) * 100;
|
|
10477
10372
|
const spark = buildSparkline(points);
|
|
10478
10373
|
const w = 200;
|
|
@@ -10482,7 +10377,7 @@ var PdpChartMetricPullLit = class extends LitElement20 {
|
|
|
10482
10377
|
const max = Math.max(...points.map((p) => p.y));
|
|
10483
10378
|
const range = max - min || 1;
|
|
10484
10379
|
const py = h - 4 - (peak.y - min) / range * (h - 8);
|
|
10485
|
-
return
|
|
10380
|
+
return html22`
|
|
10486
10381
|
<section class="pdp-chart-met" aria-label=${title}>
|
|
10487
10382
|
<div class="pdp-chart-met__metric">
|
|
10488
10383
|
<span class="pdp-chart-met__big">${peak.y}</span>
|
|
@@ -10514,15 +10409,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
10514
10409
|
}
|
|
10515
10410
|
|
|
10516
10411
|
// src/widgets/PdpQaAccordionLit.ts
|
|
10517
|
-
import { html as
|
|
10518
|
-
function ensureStyles3() {
|
|
10519
|
-
if (typeof document === "undefined") return;
|
|
10520
|
-
if (document.getElementById("syntro-pdp-qa-accordion-style")) return;
|
|
10521
|
-
const s4 = document.createElement("style");
|
|
10522
|
-
s4.id = "syntro-pdp-qa-accordion-style";
|
|
10523
|
-
s4.textContent = CSS3;
|
|
10524
|
-
document.head.appendChild(s4);
|
|
10525
|
-
}
|
|
10412
|
+
import { html as html23, LitElement as LitElement22 } from "lit";
|
|
10526
10413
|
var CSS3 = `
|
|
10527
10414
|
.pdp-qa-acc {
|
|
10528
10415
|
--qa-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10596,7 +10483,7 @@ var CSS3 = `
|
|
|
10596
10483
|
max-width: 56ch;
|
|
10597
10484
|
}
|
|
10598
10485
|
`;
|
|
10599
|
-
var PdpQaAccordionLit = class extends
|
|
10486
|
+
var PdpQaAccordionLit = class extends LitElement22 {
|
|
10600
10487
|
constructor() {
|
|
10601
10488
|
super(...arguments);
|
|
10602
10489
|
this.data = void 0;
|
|
@@ -10606,14 +10493,14 @@ var PdpQaAccordionLit = class extends LitElement21 {
|
|
|
10606
10493
|
}
|
|
10607
10494
|
connectedCallback() {
|
|
10608
10495
|
super.connectedCallback();
|
|
10609
|
-
|
|
10496
|
+
ensureStylesInRoot(this, "syntro-pdp-qa-accordion-style", CSS3);
|
|
10610
10497
|
}
|
|
10611
10498
|
render() {
|
|
10612
|
-
if (!this.data) return
|
|
10613
|
-
return
|
|
10499
|
+
if (!this.data) return html23`<div class="pdp-qa-acc" style="opacity:0.5;">…</div>`;
|
|
10500
|
+
return html23`
|
|
10614
10501
|
<section class="pdp-qa-acc" aria-label="Frequently asked questions">
|
|
10615
10502
|
${this.data.items.map(
|
|
10616
|
-
(it) =>
|
|
10503
|
+
(it) => html23`<details class="pdp-qa-acc__item">
|
|
10617
10504
|
<summary>
|
|
10618
10505
|
<span class="pdp-qa-acc__chev" aria-hidden="true"></span>
|
|
10619
10506
|
<span>${it.question}</span>
|
|
@@ -10633,15 +10520,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa-
|
|
|
10633
10520
|
}
|
|
10634
10521
|
|
|
10635
10522
|
// src/widgets/PdpQaSideBySideLit.ts
|
|
10636
|
-
import { html as
|
|
10637
|
-
function ensureStyles4() {
|
|
10638
|
-
if (typeof document === "undefined") return;
|
|
10639
|
-
if (document.getElementById("syntro-pdp-qa-side-style")) return;
|
|
10640
|
-
const s4 = document.createElement("style");
|
|
10641
|
-
s4.id = "syntro-pdp-qa-side-style";
|
|
10642
|
-
s4.textContent = CSS4;
|
|
10643
|
-
document.head.appendChild(s4);
|
|
10644
|
-
}
|
|
10523
|
+
import { html as html24, LitElement as LitElement23 } from "lit";
|
|
10645
10524
|
var CSS4 = `
|
|
10646
10525
|
.pdp-qa-side {
|
|
10647
10526
|
--qa-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10707,7 +10586,7 @@ var CSS4 = `
|
|
|
10707
10586
|
margin: 0;
|
|
10708
10587
|
}
|
|
10709
10588
|
`;
|
|
10710
|
-
var PdpQaSideBySideLit = class extends
|
|
10589
|
+
var PdpQaSideBySideLit = class extends LitElement23 {
|
|
10711
10590
|
constructor() {
|
|
10712
10591
|
super(...arguments);
|
|
10713
10592
|
this.data = void 0;
|
|
@@ -10717,14 +10596,14 @@ var PdpQaSideBySideLit = class extends LitElement22 {
|
|
|
10717
10596
|
}
|
|
10718
10597
|
connectedCallback() {
|
|
10719
10598
|
super.connectedCallback();
|
|
10720
|
-
|
|
10599
|
+
ensureStylesInRoot(this, "syntro-pdp-qa-side-style", CSS4);
|
|
10721
10600
|
}
|
|
10722
10601
|
render() {
|
|
10723
|
-
if (!this.data) return
|
|
10724
|
-
return
|
|
10602
|
+
if (!this.data) return html24`<div class="pdp-qa-side" style="opacity:0.5;">…</div>`;
|
|
10603
|
+
return html24`
|
|
10725
10604
|
<section class="pdp-qa-side" aria-label="Q&A">
|
|
10726
10605
|
${this.data.items.map(
|
|
10727
|
-
(it) =>
|
|
10606
|
+
(it) => html24`<div class="pdp-qa-side__item">
|
|
10728
10607
|
<h4 class="pdp-qa-side__q">${it.question}</h4>
|
|
10729
10608
|
<p class="pdp-qa-side__a">${it.answer}</p>
|
|
10730
10609
|
</div>`
|
|
@@ -10741,15 +10620,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa-
|
|
|
10741
10620
|
}
|
|
10742
10621
|
|
|
10743
10622
|
// src/widgets/PdpRegionalFullBleedLit.ts
|
|
10744
|
-
import { html as
|
|
10745
|
-
function ensureStyles5() {
|
|
10746
|
-
if (typeof document === "undefined") return;
|
|
10747
|
-
if (document.getElementById("syntro-pdp-regional-fullbleed-style")) return;
|
|
10748
|
-
const s4 = document.createElement("style");
|
|
10749
|
-
s4.id = "syntro-pdp-regional-fullbleed-style";
|
|
10750
|
-
s4.textContent = CSS5;
|
|
10751
|
-
document.head.appendChild(s4);
|
|
10752
|
-
}
|
|
10623
|
+
import { html as html25, LitElement as LitElement24 } from "lit";
|
|
10753
10624
|
var CSS5 = `
|
|
10754
10625
|
.pdp-reg-fb {
|
|
10755
10626
|
--r-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10832,7 +10703,7 @@ var CSS5 = `
|
|
|
10832
10703
|
font-style: italic;
|
|
10833
10704
|
}
|
|
10834
10705
|
`;
|
|
10835
|
-
var PdpRegionalFullBleedLit = class extends
|
|
10706
|
+
var PdpRegionalFullBleedLit = class extends LitElement24 {
|
|
10836
10707
|
constructor() {
|
|
10837
10708
|
super(...arguments);
|
|
10838
10709
|
this.data = void 0;
|
|
@@ -10842,12 +10713,12 @@ var PdpRegionalFullBleedLit = class extends LitElement23 {
|
|
|
10842
10713
|
}
|
|
10843
10714
|
connectedCallback() {
|
|
10844
10715
|
super.connectedCallback();
|
|
10845
|
-
|
|
10716
|
+
ensureStylesInRoot(this, "syntro-pdp-regional-fullbleed-style", CSS5);
|
|
10846
10717
|
}
|
|
10847
10718
|
render() {
|
|
10848
|
-
if (!this.data) return
|
|
10719
|
+
if (!this.data) return html25`<div class="pdp-reg-fb" style="opacity:0.5;">…</div>`;
|
|
10849
10720
|
const d = this.data;
|
|
10850
|
-
return
|
|
10721
|
+
return html25`
|
|
10851
10722
|
<section class="pdp-reg-fb" aria-label="Where you are">
|
|
10852
10723
|
<article class="pdp-reg-fb__panel">
|
|
10853
10724
|
<svg class="pdp-reg-fb__glyph" viewBox="0 0 56 56" aria-hidden="true">
|
|
@@ -10890,15 +10761,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
10890
10761
|
}
|
|
10891
10762
|
|
|
10892
10763
|
// src/widgets/PdpRegionalInlineNoteLit.ts
|
|
10893
|
-
import { html as
|
|
10894
|
-
function ensureStyles6() {
|
|
10895
|
-
if (typeof document === "undefined") return;
|
|
10896
|
-
if (document.getElementById("syntro-pdp-regional-inline-style")) return;
|
|
10897
|
-
const s4 = document.createElement("style");
|
|
10898
|
-
s4.id = "syntro-pdp-regional-inline-style";
|
|
10899
|
-
s4.textContent = CSS6;
|
|
10900
|
-
document.head.appendChild(s4);
|
|
10901
|
-
}
|
|
10764
|
+
import { html as html26, LitElement as LitElement25 } from "lit";
|
|
10902
10765
|
var CSS6 = `
|
|
10903
10766
|
.pdp-reg-in {
|
|
10904
10767
|
--r-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10951,7 +10814,7 @@ var CSS6 = `
|
|
|
10951
10814
|
font-style: italic;
|
|
10952
10815
|
}
|
|
10953
10816
|
`;
|
|
10954
|
-
var PdpRegionalInlineNoteLit = class extends
|
|
10817
|
+
var PdpRegionalInlineNoteLit = class extends LitElement25 {
|
|
10955
10818
|
constructor() {
|
|
10956
10819
|
super(...arguments);
|
|
10957
10820
|
this.data = void 0;
|
|
@@ -10961,12 +10824,12 @@ var PdpRegionalInlineNoteLit = class extends LitElement24 {
|
|
|
10961
10824
|
}
|
|
10962
10825
|
connectedCallback() {
|
|
10963
10826
|
super.connectedCallback();
|
|
10964
|
-
|
|
10827
|
+
ensureStylesInRoot(this, "syntro-pdp-regional-inline-style", CSS6);
|
|
10965
10828
|
}
|
|
10966
10829
|
render() {
|
|
10967
|
-
if (!this.data) return
|
|
10830
|
+
if (!this.data) return html26`<div class="pdp-reg-in" style="opacity:0.5;">…</div>`;
|
|
10968
10831
|
const d = this.data;
|
|
10969
|
-
return
|
|
10832
|
+
return html26`
|
|
10970
10833
|
<aside class="pdp-reg-in" aria-label="Regional note">
|
|
10971
10834
|
<svg class="pdp-reg-in__icon" viewBox="0 0 22 22" aria-hidden="true">
|
|
10972
10835
|
<circle cx="11" cy="11" r="9" fill="none" stroke="currentColor" stroke-width="1.6"/>
|
|
@@ -10994,8 +10857,8 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
10994
10857
|
}
|
|
10995
10858
|
|
|
10996
10859
|
// src/widgets/PdpSlotPlaceholderLit.ts
|
|
10997
|
-
import { html as
|
|
10998
|
-
var PdpSlotPlaceholderLit = class extends
|
|
10860
|
+
import { html as html27, LitElement as LitElement26 } from "lit";
|
|
10861
|
+
var PdpSlotPlaceholderLit = class extends LitElement26 {
|
|
10999
10862
|
constructor() {
|
|
11000
10863
|
super(...arguments);
|
|
11001
10864
|
this.label = "More personalized content coming soon";
|
|
@@ -11004,7 +10867,7 @@ var PdpSlotPlaceholderLit = class extends LitElement25 {
|
|
|
11004
10867
|
return this;
|
|
11005
10868
|
}
|
|
11006
10869
|
render() {
|
|
11007
|
-
return
|
|
10870
|
+
return html27`
|
|
11008
10871
|
<div class="pdp-slot-placeholder" data-placeholder>
|
|
11009
10872
|
<p>${this.label}</p>
|
|
11010
10873
|
<small>Ask in chat to explore this product further.</small>
|
|
@@ -11020,15 +10883,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-slo
|
|
|
11020
10883
|
}
|
|
11021
10884
|
|
|
11022
10885
|
// src/widgets/PdpRibbonLit.ts
|
|
11023
|
-
import { html as
|
|
11024
|
-
function ensureRibbonStyles() {
|
|
11025
|
-
if (typeof document === "undefined") return;
|
|
11026
|
-
if (document.getElementById("syntro-pdp-ribbon-style")) return;
|
|
11027
|
-
const s4 = document.createElement("style");
|
|
11028
|
-
s4.id = "syntro-pdp-ribbon-style";
|
|
11029
|
-
s4.textContent = RIBBON_CSS;
|
|
11030
|
-
document.head.appendChild(s4);
|
|
11031
|
-
}
|
|
10886
|
+
import { html as html28, LitElement as LitElement27, nothing as nothing11 } from "lit";
|
|
11032
10887
|
var RIBBON_CSS = `
|
|
11033
10888
|
.syntro-pdp-ribbon {
|
|
11034
10889
|
--rb-primary: var(--syntro-pdp-primary, hsl(150 32% 38%));
|
|
@@ -11087,7 +10942,7 @@ var RIBBON_CSS = `
|
|
|
11087
10942
|
width: 16px;
|
|
11088
10943
|
height: 16px;
|
|
11089
10944
|
display: inline-block;
|
|
11090
|
-
color: var(--rb-primary);
|
|
10945
|
+
color: var(--syntro-pdp-primary-ink, var(--rb-primary));
|
|
11091
10946
|
}
|
|
11092
10947
|
.syntro-pdp-ribbon-copy {
|
|
11093
10948
|
font-family: var(--rb-display);
|
|
@@ -11104,7 +10959,7 @@ var RIBBON_CSS = `
|
|
|
11104
10959
|
font-style: normal;
|
|
11105
10960
|
font-family: var(--syntro-pdp-sans, inherit);
|
|
11106
10961
|
font-weight: 500;
|
|
11107
|
-
color: var(--rb-primary);
|
|
10962
|
+
color: var(--syntro-pdp-primary-ink, var(--rb-primary));
|
|
11108
10963
|
font-size: 14.5px;
|
|
11109
10964
|
letter-spacing: 0;
|
|
11110
10965
|
}
|
|
@@ -11149,7 +11004,7 @@ var RIBBON_CSS = `
|
|
|
11149
11004
|
100% { transform: translateX(110%); }
|
|
11150
11005
|
}
|
|
11151
11006
|
`;
|
|
11152
|
-
var PdpRibbonLit = class extends
|
|
11007
|
+
var PdpRibbonLit = class extends LitElement27 {
|
|
11153
11008
|
constructor() {
|
|
11154
11009
|
super(...arguments);
|
|
11155
11010
|
this.topic = "";
|
|
@@ -11163,7 +11018,7 @@ var PdpRibbonLit = class extends LitElement26 {
|
|
|
11163
11018
|
}
|
|
11164
11019
|
connectedCallback() {
|
|
11165
11020
|
super.connectedCallback();
|
|
11166
|
-
|
|
11021
|
+
ensureStylesInRoot(this, "syntro-pdp-ribbon-style", RIBBON_CSS);
|
|
11167
11022
|
if (this._entering) this.requestUpdate();
|
|
11168
11023
|
}
|
|
11169
11024
|
disconnectedCallback() {
|
|
@@ -11187,7 +11042,7 @@ var PdpRibbonLit = class extends LitElement26 {
|
|
|
11187
11042
|
}
|
|
11188
11043
|
render() {
|
|
11189
11044
|
const cls = this._entering ? "syntro-pdp-ribbon is-entering" : "syntro-pdp-ribbon";
|
|
11190
|
-
return
|
|
11045
|
+
return html28`
|
|
11191
11046
|
<div class="${cls}" role="status" aria-live="polite">
|
|
11192
11047
|
<span class="syntro-pdp-ribbon-sweep" aria-hidden="true"></span>
|
|
11193
11048
|
<svg
|
|
@@ -11207,7 +11062,7 @@ var PdpRibbonLit = class extends LitElement26 {
|
|
|
11207
11062
|
</svg>
|
|
11208
11063
|
<p class="syntro-pdp-ribbon-copy">
|
|
11209
11064
|
Tailored to your conversation about
|
|
11210
|
-
${this.topic ?
|
|
11065
|
+
${this.topic ? html28` <span class="syntro-pdp-ribbon-topic">${this.topic}</span>.` : nothing11}
|
|
11211
11066
|
<span class="syntro-pdp-ribbon-trail"> ${this.subtitle}</span>
|
|
11212
11067
|
</p>
|
|
11213
11068
|
</div>
|
|
@@ -11224,15 +11079,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-rib
|
|
|
11224
11079
|
}
|
|
11225
11080
|
|
|
11226
11081
|
// src/widgets/PdpQaLit.ts
|
|
11227
|
-
import { html as
|
|
11228
|
-
function ensureQaStyles() {
|
|
11229
|
-
if (typeof document === "undefined") return;
|
|
11230
|
-
if (document.getElementById("syntro-pdp-qa-style")) return;
|
|
11231
|
-
const s4 = document.createElement("style");
|
|
11232
|
-
s4.id = "syntro-pdp-qa-style";
|
|
11233
|
-
s4.textContent = QA_CSS;
|
|
11234
|
-
document.head.appendChild(s4);
|
|
11235
|
-
}
|
|
11082
|
+
import { html as html29, LitElement as LitElement28, nothing as nothing12 } from "lit";
|
|
11236
11083
|
var QA_CSS = `
|
|
11237
11084
|
.pdp-qa {
|
|
11238
11085
|
--qa-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -11314,7 +11161,7 @@ var QA_CSS = `
|
|
|
11314
11161
|
background: color-mix(in srgb, var(--qa-primary) 14%, transparent);
|
|
11315
11162
|
display: grid;
|
|
11316
11163
|
place-items: center;
|
|
11317
|
-
color: color-mix(in srgb, var(--qa-primary) 80%, white 20%);
|
|
11164
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--qa-primary) 80%, white 20%));
|
|
11318
11165
|
transition: background 200ms ease, transform 200ms ease;
|
|
11319
11166
|
}
|
|
11320
11167
|
.pdp-qa-item[open] .pdp-qa-toggle {
|
|
@@ -11386,7 +11233,7 @@ var QA_CSS = `
|
|
|
11386
11233
|
}
|
|
11387
11234
|
}
|
|
11388
11235
|
`;
|
|
11389
|
-
var PdpQaLit = class extends
|
|
11236
|
+
var PdpQaLit = class extends LitElement28 {
|
|
11390
11237
|
constructor() {
|
|
11391
11238
|
super(...arguments);
|
|
11392
11239
|
this.data = void 0;
|
|
@@ -11398,7 +11245,7 @@ var PdpQaLit = class extends LitElement27 {
|
|
|
11398
11245
|
}
|
|
11399
11246
|
connectedCallback() {
|
|
11400
11247
|
super.connectedCallback();
|
|
11401
|
-
|
|
11248
|
+
ensureStylesInRoot(this, "syntro-pdp-qa-style", QA_CSS);
|
|
11402
11249
|
if (this._entering) this.requestUpdate();
|
|
11403
11250
|
}
|
|
11404
11251
|
disconnectedCallback() {
|
|
@@ -11421,19 +11268,19 @@ var PdpQaLit = class extends LitElement27 {
|
|
|
11421
11268
|
}
|
|
11422
11269
|
render() {
|
|
11423
11270
|
if (!this.data) {
|
|
11424
|
-
return
|
|
11271
|
+
return html29`<div data-skeleton class="pdp-qa-skeleton">
|
|
11425
11272
|
<div class="pdp-qa-skeleton-row"></div>
|
|
11426
11273
|
<div class="pdp-qa-skeleton-row"></div>
|
|
11427
11274
|
<div class="pdp-qa-skeleton-row"></div>
|
|
11428
11275
|
</div>`;
|
|
11429
11276
|
}
|
|
11430
|
-
return
|
|
11277
|
+
return html29`
|
|
11431
11278
|
<section
|
|
11432
11279
|
class="${this._entering ? "pdp-qa is-entering" : "pdp-qa"}"
|
|
11433
11280
|
aria-label="Frequently asked questions"
|
|
11434
11281
|
>
|
|
11435
11282
|
${this.data.items.map(
|
|
11436
|
-
(item, idx) =>
|
|
11283
|
+
(item, idx) => html29`
|
|
11437
11284
|
<details class="pdp-qa-item" ?open=${idx === 0}>
|
|
11438
11285
|
<summary class="pdp-qa-question">
|
|
11439
11286
|
<span>${item.question}</span>
|
|
@@ -11448,7 +11295,7 @@ var PdpQaLit = class extends LitElement27 {
|
|
|
11448
11295
|
</details>
|
|
11449
11296
|
`
|
|
11450
11297
|
)}
|
|
11451
|
-
${
|
|
11298
|
+
${nothing12}
|
|
11452
11299
|
</section>
|
|
11453
11300
|
`;
|
|
11454
11301
|
}
|
|
@@ -11462,15 +11309,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa"
|
|
|
11462
11309
|
}
|
|
11463
11310
|
|
|
11464
11311
|
// src/widgets/PdpRegionalLit.ts
|
|
11465
|
-
import { html as
|
|
11466
|
-
function ensureRegionalStyles() {
|
|
11467
|
-
if (typeof document === "undefined") return;
|
|
11468
|
-
if (document.getElementById("syntro-pdp-regional-style")) return;
|
|
11469
|
-
const s4 = document.createElement("style");
|
|
11470
|
-
s4.id = "syntro-pdp-regional-style";
|
|
11471
|
-
s4.textContent = REGIONAL_CSS;
|
|
11472
|
-
document.head.appendChild(s4);
|
|
11473
|
-
}
|
|
11312
|
+
import { html as html30, LitElement as LitElement29 } from "lit";
|
|
11474
11313
|
var REGIONAL_CSS = `
|
|
11475
11314
|
.pdp-regional {
|
|
11476
11315
|
--rg-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -11548,7 +11387,7 @@ var REGIONAL_CSS = `
|
|
|
11548
11387
|
font-size: 11px;
|
|
11549
11388
|
letter-spacing: 0.12em;
|
|
11550
11389
|
text-transform: uppercase;
|
|
11551
|
-
color: color-mix(in srgb, var(--rg-primary) 75%, white 25%);
|
|
11390
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--rg-primary) 75%, white 25%));
|
|
11552
11391
|
font-weight: 500;
|
|
11553
11392
|
margin-bottom: 22px;
|
|
11554
11393
|
}
|
|
@@ -11580,7 +11419,7 @@ var REGIONAL_CSS = `
|
|
|
11580
11419
|
.pdp-regional-headline em {
|
|
11581
11420
|
font-style: italic;
|
|
11582
11421
|
font-variation-settings: 'SOFT' 100, 'WONK' 1;
|
|
11583
|
-
color: var(--rg-primary);
|
|
11422
|
+
color: var(--syntro-pdp-primary-ink, var(--rg-primary));
|
|
11584
11423
|
}
|
|
11585
11424
|
|
|
11586
11425
|
.pdp-regional-body {
|
|
@@ -11604,7 +11443,7 @@ var REGIONAL_CSS = `
|
|
|
11604
11443
|
font-family: var(--rg-display);
|
|
11605
11444
|
font-size: 14.5px;
|
|
11606
11445
|
font-variation-settings: 'SOFT' 70;
|
|
11607
|
-
color: color-mix(in srgb, var(--rg-primary) 65%, white 35%);
|
|
11446
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--rg-primary) 65%, white 35%));
|
|
11608
11447
|
line-height: 1.5;
|
|
11609
11448
|
}
|
|
11610
11449
|
.pdp-regional-footer-spark {
|
|
@@ -11650,7 +11489,7 @@ var REGIONAL_CSS = `
|
|
|
11650
11489
|
to { opacity: 1; transform: translateY(0); }
|
|
11651
11490
|
}
|
|
11652
11491
|
`;
|
|
11653
|
-
var PdpRegionalLit = class extends
|
|
11492
|
+
var PdpRegionalLit = class extends LitElement29 {
|
|
11654
11493
|
constructor() {
|
|
11655
11494
|
super(...arguments);
|
|
11656
11495
|
this.data = void 0;
|
|
@@ -11662,7 +11501,7 @@ var PdpRegionalLit = class extends LitElement28 {
|
|
|
11662
11501
|
}
|
|
11663
11502
|
connectedCallback() {
|
|
11664
11503
|
super.connectedCallback();
|
|
11665
|
-
|
|
11504
|
+
ensureStylesInRoot(this, "syntro-pdp-regional-style", REGIONAL_CSS);
|
|
11666
11505
|
if (this._entering) this.requestUpdate();
|
|
11667
11506
|
}
|
|
11668
11507
|
disconnectedCallback() {
|
|
@@ -11686,11 +11525,11 @@ var PdpRegionalLit = class extends LitElement28 {
|
|
|
11686
11525
|
}
|
|
11687
11526
|
render() {
|
|
11688
11527
|
if (!this.data) {
|
|
11689
|
-
return
|
|
11528
|
+
return html30`<div data-skeleton class="pdp-qa-skeleton" style="height:200px;">
|
|
11690
11529
|
<div class="pdp-qa-skeleton-row" style="height:200px;border-radius:24px;"></div>
|
|
11691
11530
|
</div>`;
|
|
11692
11531
|
}
|
|
11693
|
-
return
|
|
11532
|
+
return html30`
|
|
11694
11533
|
<section
|
|
11695
11534
|
class="${this._entering ? "pdp-regional is-entering" : "pdp-regional"}"
|
|
11696
11535
|
aria-label="Regional and temporal context"
|
|
@@ -11735,15 +11574,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
11735
11574
|
}
|
|
11736
11575
|
|
|
11737
11576
|
// src/widgets/PdpPeerFeedLit.ts
|
|
11738
|
-
import { html as
|
|
11739
|
-
function ensurePeerFeedStyles() {
|
|
11740
|
-
if (typeof document === "undefined") return;
|
|
11741
|
-
if (document.getElementById("syntro-pdp-peer-feed-style")) return;
|
|
11742
|
-
const s4 = document.createElement("style");
|
|
11743
|
-
s4.id = "syntro-pdp-peer-feed-style";
|
|
11744
|
-
s4.textContent = PEER_FEED_CSS;
|
|
11745
|
-
document.head.appendChild(s4);
|
|
11746
|
-
}
|
|
11577
|
+
import { html as html31, LitElement as LitElement30 } from "lit";
|
|
11747
11578
|
var PEER_FEED_CSS = `
|
|
11748
11579
|
.pdp-peer-feed {
|
|
11749
11580
|
--pf-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -11859,14 +11690,14 @@ var PEER_FEED_CSS = `
|
|
|
11859
11690
|
}
|
|
11860
11691
|
.pdp-peer-quote::before {
|
|
11861
11692
|
content: '\u201C ';
|
|
11862
|
-
color: color-mix(in srgb, var(--pf-primary) 60%, transparent);
|
|
11693
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pf-primary) 60%, transparent));
|
|
11863
11694
|
font-size: 1.4em;
|
|
11864
11695
|
vertical-align: -0.2em;
|
|
11865
11696
|
margin-right: -2px;
|
|
11866
11697
|
}
|
|
11867
11698
|
.pdp-peer-quote::after {
|
|
11868
11699
|
content: ' \u201D';
|
|
11869
|
-
color: color-mix(in srgb, var(--pf-primary) 60%, transparent);
|
|
11700
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pf-primary) 60%, transparent));
|
|
11870
11701
|
font-size: 1.4em;
|
|
11871
11702
|
vertical-align: -0.2em;
|
|
11872
11703
|
margin-left: -2px;
|
|
@@ -11877,7 +11708,7 @@ var PEER_FEED_CSS = `
|
|
|
11877
11708
|
align-items: center;
|
|
11878
11709
|
gap: 7px;
|
|
11879
11710
|
font-size: 12.5px;
|
|
11880
|
-
color: color-mix(in srgb, var(--pf-primary) 75%, white 25%);
|
|
11711
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pf-primary) 75%, white 25%));
|
|
11881
11712
|
font-style: italic;
|
|
11882
11713
|
font-family: var(--pf-display);
|
|
11883
11714
|
margin-top: 2px;
|
|
@@ -11947,7 +11778,7 @@ var PEER_FEED_CSS = `
|
|
|
11947
11778
|
}
|
|
11948
11779
|
}
|
|
11949
11780
|
`;
|
|
11950
|
-
var PdpPeerFeedLit = class extends
|
|
11781
|
+
var PdpPeerFeedLit = class extends LitElement30 {
|
|
11951
11782
|
constructor() {
|
|
11952
11783
|
super(...arguments);
|
|
11953
11784
|
this.data = void 0;
|
|
@@ -11959,7 +11790,7 @@ var PdpPeerFeedLit = class extends LitElement29 {
|
|
|
11959
11790
|
}
|
|
11960
11791
|
connectedCallback() {
|
|
11961
11792
|
super.connectedCallback();
|
|
11962
|
-
|
|
11793
|
+
ensureStylesInRoot(this, "syntro-pdp-peer-feed-style", PEER_FEED_CSS);
|
|
11963
11794
|
if (this._entering) this.requestUpdate();
|
|
11964
11795
|
}
|
|
11965
11796
|
disconnectedCallback() {
|
|
@@ -11982,19 +11813,19 @@ var PdpPeerFeedLit = class extends LitElement29 {
|
|
|
11982
11813
|
}
|
|
11983
11814
|
render() {
|
|
11984
11815
|
if (!this.data) {
|
|
11985
|
-
return
|
|
11816
|
+
return html31`<div data-skeleton class="pdp-peer-skeleton">
|
|
11986
11817
|
<div class="pdp-peer-skeleton-row"></div>
|
|
11987
11818
|
<div class="pdp-peer-skeleton-row"></div>
|
|
11988
11819
|
<div class="pdp-peer-skeleton-row"></div>
|
|
11989
11820
|
</div>`;
|
|
11990
11821
|
}
|
|
11991
|
-
return
|
|
11822
|
+
return html31`
|
|
11992
11823
|
<section
|
|
11993
11824
|
class="${this._entering ? "pdp-peer-feed is-entering" : "pdp-peer-feed"}"
|
|
11994
11825
|
aria-label="Users like you"
|
|
11995
11826
|
>
|
|
11996
11827
|
${this.data.peers.map(
|
|
11997
|
-
(peer) =>
|
|
11828
|
+
(peer) => html31`
|
|
11998
11829
|
<article class="pdp-peer-post">
|
|
11999
11830
|
<div class="pdp-peer-avatar" aria-hidden="true">${peer.initials}</div>
|
|
12000
11831
|
<div class="pdp-peer-body">
|
|
@@ -12031,7 +11862,8 @@ var TAGS = {
|
|
|
12031
11862
|
hero: "syntro-product-hero",
|
|
12032
11863
|
grid: "syntro-product-grid",
|
|
12033
11864
|
pdp: "syntro-pdp",
|
|
12034
|
-
reco: "syntro-product-reco-carousel"
|
|
11865
|
+
reco: "syntro-product-reco-carousel",
|
|
11866
|
+
details: "syntro-product-details"
|
|
12035
11867
|
};
|
|
12036
11868
|
if (typeof customElements !== "undefined") {
|
|
12037
11869
|
if (!customElements.get(TAGS.card)) customElements.define(TAGS.card, ProductCardLit);
|
|
@@ -12041,6 +11873,7 @@ if (typeof customElements !== "undefined") {
|
|
|
12041
11873
|
if (!customElements.get(TAGS.grid)) customElements.define(TAGS.grid, ProductGridLit);
|
|
12042
11874
|
if (!customElements.get(TAGS.pdp)) customElements.define(TAGS.pdp, SyntroPdpLit);
|
|
12043
11875
|
if (!customElements.get(TAGS.reco)) customElements.define(TAGS.reco, ProductRecoCarouselLit);
|
|
11876
|
+
if (!customElements.get(TAGS.details)) customElements.define(TAGS.details, ProductDetailsLit);
|
|
12044
11877
|
}
|
|
12045
11878
|
if (typeof document !== "undefined") startNativeSlotWatcher();
|
|
12046
11879
|
var _WIRED_RUNTIMES = /* @__PURE__ */ new WeakSet();
|
|
@@ -12081,17 +11914,21 @@ function onActivate(runtime2) {
|
|
|
12081
11914
|
const observer2 = wirePdpElements(runtime2, document);
|
|
12082
11915
|
return () => {
|
|
12083
11916
|
observer2.disconnect();
|
|
12084
|
-
const
|
|
12085
|
-
if (
|
|
11917
|
+
const i = _ACTIVE_OBSERVERS.indexOf(observer2);
|
|
11918
|
+
if (i >= 0) _ACTIVE_OBSERVERS.splice(i, 1);
|
|
12086
11919
|
_WIRED_RUNTIMES.delete(runtime2);
|
|
12087
11920
|
};
|
|
12088
11921
|
}
|
|
12089
|
-
function makeMountable(tag) {
|
|
11922
|
+
function makeMountable(tag, hooks) {
|
|
12090
11923
|
return {
|
|
12091
11924
|
mount(container, config) {
|
|
12092
|
-
const widgetProps =
|
|
11925
|
+
const widgetProps = resolveStorefrontProps(
|
|
11926
|
+
stripMountPlumbing(config ?? null),
|
|
11927
|
+
config?.runtime
|
|
11928
|
+
);
|
|
12093
11929
|
const el = document.createElement(tag);
|
|
12094
11930
|
el.props = widgetProps;
|
|
11931
|
+
hooks?.afterCreate?.(el, config);
|
|
12095
11932
|
container.appendChild(el);
|
|
12096
11933
|
return () => el.remove();
|
|
12097
11934
|
}
|
|
@@ -12101,8 +11938,13 @@ var ProductCardMountable = makeMountable(TAGS.card);
|
|
|
12101
11938
|
var ProductComparisonMountable = makeMountable(TAGS.comparison);
|
|
12102
11939
|
var ProductHeroMountable = makeMountable(TAGS.hero);
|
|
12103
11940
|
var ProductGridMountable = makeMountable(TAGS.grid);
|
|
12104
|
-
var SyntroPdpMountable = makeMountable(TAGS.pdp
|
|
11941
|
+
var SyntroPdpMountable = makeMountable(TAGS.pdp, {
|
|
11942
|
+
afterCreate: (el, config) => {
|
|
11943
|
+
el.runtimeRef = config?.runtime ?? null;
|
|
11944
|
+
}
|
|
11945
|
+
});
|
|
12105
11946
|
var SyntroRecoMountable = makeMountable(TAGS.reco);
|
|
11947
|
+
var ProductDetailsMountable = makeMountable(TAGS.details);
|
|
12106
11948
|
var runtime = {
|
|
12107
11949
|
id: "adaptive-product",
|
|
12108
11950
|
version: "1.0.0",
|
|
@@ -12185,6 +12027,15 @@ var runtime = {
|
|
|
12185
12027
|
description: "Horizontally-scrollable carousel of recommended products with image, price, hook copy, and PDP links.",
|
|
12186
12028
|
icon: "\u{1F3A0}"
|
|
12187
12029
|
}
|
|
12030
|
+
},
|
|
12031
|
+
{
|
|
12032
|
+
id: "adaptive-product:details",
|
|
12033
|
+
component: ProductDetailsMountable,
|
|
12034
|
+
metadata: {
|
|
12035
|
+
name: "Product details",
|
|
12036
|
+
description: "The store's own product details",
|
|
12037
|
+
icon: "\u{1F4CB}"
|
|
12038
|
+
}
|
|
12188
12039
|
}
|
|
12189
12040
|
]
|
|
12190
12041
|
};
|
|
@@ -12201,35 +12052,8 @@ export {
|
|
|
12201
12052
|
ProductGridMountable,
|
|
12202
12053
|
SyntroPdpMountable,
|
|
12203
12054
|
SyntroRecoMountable,
|
|
12055
|
+
ProductDetailsMountable,
|
|
12204
12056
|
runtime,
|
|
12205
12057
|
runtime_default
|
|
12206
12058
|
};
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
@lit/context/lib/context-request-event.js:
|
|
12210
|
-
@lit/context/lib/create-context.js:
|
|
12211
|
-
@lit/context/lib/controllers/context-consumer.js:
|
|
12212
|
-
@lit/context/lib/value-notifier.js:
|
|
12213
|
-
@lit/context/lib/controllers/context-provider.js:
|
|
12214
|
-
@lit/context/lib/context-root.js:
|
|
12215
|
-
(**
|
|
12216
|
-
* @license
|
|
12217
|
-
* Copyright 2021 Google LLC
|
|
12218
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
12219
|
-
*)
|
|
12220
|
-
|
|
12221
|
-
@lit/context/lib/decorators/provide.js:
|
|
12222
|
-
(**
|
|
12223
|
-
* @license
|
|
12224
|
-
* Copyright 2017 Google LLC
|
|
12225
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
12226
|
-
*)
|
|
12227
|
-
|
|
12228
|
-
@lit/context/lib/decorators/consume.js:
|
|
12229
|
-
(**
|
|
12230
|
-
* @license
|
|
12231
|
-
* Copyright 2022 Google LLC
|
|
12232
|
-
* SPDX-License-Identifier: BSD-3-Clause
|
|
12233
|
-
*)
|
|
12234
|
-
*/
|
|
12235
|
-
//# sourceMappingURL=chunk-QXR5X22R.js.map
|
|
12059
|
+
//# sourceMappingURL=chunk-YEW72FVW.js.map
|