@syntrologie/adapt-product 2.55.0 → 2.56.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-QXR5X22R.js → chunk-QNZXBFP5.js} +1021 -1209
- package/dist/chunk-QNZXBFP5.js.map +7 -0
- package/dist/chunk-RUE5UUEN.js +5206 -0
- package/dist/chunk-RUE5UUEN.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/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,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VISIBLE_FACT_KEYS,
|
|
3
|
+
ViewportClassController,
|
|
3
4
|
__privateAdd,
|
|
4
5
|
__privateGet,
|
|
5
6
|
__privateMethod,
|
|
6
7
|
__privateSet,
|
|
7
8
|
cardSchema,
|
|
8
9
|
comparisonSchema,
|
|
10
|
+
dispatchDiveDeeper,
|
|
11
|
+
external_exports,
|
|
12
|
+
fetchLivePrice,
|
|
9
13
|
getProductPlatformAdapter,
|
|
10
14
|
gridSchema,
|
|
11
15
|
heroSchema,
|
|
12
16
|
recoSchema,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function n(n2) {
|
|
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
|
-
};
|
|
17
|
+
stripMountPlumbing,
|
|
18
|
+
takeoverPlanSchema,
|
|
19
|
+
watchLivePrice
|
|
20
|
+
} from "./chunk-RUE5UUEN.js";
|
|
544
21
|
|
|
545
22
|
// src/presets-store.ts
|
|
546
23
|
function presetsWindow() {
|
|
@@ -577,9 +54,9 @@ function selectPoolItems(items, decision, max) {
|
|
|
577
54
|
if (items.length <= max) return [...items];
|
|
578
55
|
const sharp = decision && decision !== UNKNOWN_DECISION;
|
|
579
56
|
if (sharp) {
|
|
580
|
-
const matched = items.filter((
|
|
581
|
-
const general = items.filter((
|
|
582
|
-
const rest = items.filter((
|
|
57
|
+
const matched = items.filter((i) => tagsOf(i).includes(decision));
|
|
58
|
+
const general = items.filter((i) => tagsOf(i).length === 0);
|
|
59
|
+
const rest = items.filter((i) => !matched.includes(i) && !general.includes(i));
|
|
583
60
|
return [...matched, ...general, ...rest].slice(0, max);
|
|
584
61
|
}
|
|
585
62
|
const picked = [];
|
|
@@ -587,9 +64,9 @@ function selectPoolItems(items, decision, max) {
|
|
|
587
64
|
for (const item of items) {
|
|
588
65
|
const tags = tagsOf(item);
|
|
589
66
|
if (tags.length === 0) continue;
|
|
590
|
-
if (tags.some((
|
|
67
|
+
if (tags.some((t) => covered.has(t))) continue;
|
|
591
68
|
picked.push(item);
|
|
592
|
-
for (const
|
|
69
|
+
for (const t of tags) covered.add(t);
|
|
593
70
|
if (picked.length >= max) return picked.slice(0, max);
|
|
594
71
|
}
|
|
595
72
|
for (const item of items) {
|
|
@@ -1052,8 +529,8 @@ async function resolveScoredTemplate(opts) {
|
|
|
1052
529
|
value = await opts.score(opts.scoreSpec);
|
|
1053
530
|
}
|
|
1054
531
|
const variant = opts.presets[value] ?? opts.presets[opts.fallbackValue ?? "unknown"] ?? {};
|
|
1055
|
-
const setSlots = opts.slotIds.filter((
|
|
1056
|
-
const unsetSlots = opts.slotIds.filter((
|
|
532
|
+
const setSlots = opts.slotIds.filter((s) => s in variant);
|
|
533
|
+
const unsetSlots = opts.slotIds.filter((s) => !(s in variant));
|
|
1057
534
|
const pageOrder = opts.orderSlots ? opts.orderSlots([...opts.slotIds], (slot) => slot in variant) : opts.slotIds;
|
|
1058
535
|
if (pageOrder.length > 0) {
|
|
1059
536
|
opts.emitPlan(opts.buildPlan(pageOrder.map((slot) => ({ slot, preset: variant[slot] }))));
|
|
@@ -1068,6 +545,62 @@ async function resolveScoredTemplate(opts) {
|
|
|
1068
545
|
return { value, decisionValue, setSlots, unsetSlots };
|
|
1069
546
|
}
|
|
1070
547
|
|
|
548
|
+
// src/storefront-urls.ts
|
|
549
|
+
function storefrontBase(runtime2) {
|
|
550
|
+
if (!runtime2 || typeof runtime2 !== "object" || !("storefrontUrl" in runtime2)) return void 0;
|
|
551
|
+
if (typeof runtime2.storefrontUrl !== "string") return void 0;
|
|
552
|
+
try {
|
|
553
|
+
const base = new URL(runtime2.storefrontUrl);
|
|
554
|
+
return base.protocol === "https:" || base.protocol === "http:" ? base : void 0;
|
|
555
|
+
} catch {
|
|
556
|
+
return void 0;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function resolveAgainst(value, base) {
|
|
560
|
+
if (typeof value !== "string" || !value || /^(?:[a-z][a-z\d+.-]*:|\/\/|[?#])/i.test(value))
|
|
561
|
+
return value;
|
|
562
|
+
try {
|
|
563
|
+
const resolved = new URL(value, base);
|
|
564
|
+
return resolved.protocol === "https:" || resolved.protocol === "http:" ? resolved.href : value;
|
|
565
|
+
} catch {
|
|
566
|
+
return value;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function resolveStorePath(value, runtime2) {
|
|
570
|
+
const base = storefrontBase(runtime2);
|
|
571
|
+
return base ? resolveAgainst(value, base) : value;
|
|
572
|
+
}
|
|
573
|
+
function resolveStorefrontProps(props, runtime2) {
|
|
574
|
+
const base = storefrontBase(runtime2);
|
|
575
|
+
if (!base) return props;
|
|
576
|
+
const resolve = (value) => resolveAgainst(value, base);
|
|
577
|
+
const product = (value) => ({
|
|
578
|
+
...value,
|
|
579
|
+
...value.image ? { image: { ...value.image, src: resolve(value.image.src) } } : {},
|
|
580
|
+
...Array.isArray(value.ctas) ? {
|
|
581
|
+
ctas: value.ctas.map((cta) => ({
|
|
582
|
+
...cta,
|
|
583
|
+
...cta.href ? { href: resolve(cta.href) } : {}
|
|
584
|
+
}))
|
|
585
|
+
} : {}
|
|
586
|
+
});
|
|
587
|
+
const content = props;
|
|
588
|
+
return {
|
|
589
|
+
...props,
|
|
590
|
+
...content.product ? { product: product(content.product) } : {},
|
|
591
|
+
...Array.isArray(content.products) ? { products: content.products.map((value) => value ? product(value) : value) } : {},
|
|
592
|
+
...Array.isArray(content.items) ? {
|
|
593
|
+
items: content.items.map(
|
|
594
|
+
(item) => item ? {
|
|
595
|
+
...item,
|
|
596
|
+
...item.image_url ? { image_url: resolve(item.image_url) } : {},
|
|
597
|
+
pdp_href: resolve(item.pdp_href)
|
|
598
|
+
} : item
|
|
599
|
+
)
|
|
600
|
+
} : {}
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
1071
604
|
// src/takeover/resurrectionGuard.ts
|
|
1072
605
|
function startResurrectionGuard(opts) {
|
|
1073
606
|
const maxRetries = opts.maxRetries ?? 3;
|
|
@@ -1096,8 +629,8 @@ function startResurrectionGuard(opts) {
|
|
|
1096
629
|
opts.onBudgetExhausted(exhausted.sectionId, exhausted.el);
|
|
1097
630
|
return;
|
|
1098
631
|
}
|
|
1099
|
-
for (const { sectionId, el, n
|
|
1100
|
-
counts.set(sectionId,
|
|
632
|
+
for (const { sectionId, el, n } of present) {
|
|
633
|
+
counts.set(sectionId, n);
|
|
1101
634
|
opts.onResurrected(sectionId, el);
|
|
1102
635
|
}
|
|
1103
636
|
};
|
|
@@ -1154,7 +687,7 @@ var NOOP_DISPOSE = () => {
|
|
|
1154
687
|
};
|
|
1155
688
|
async function runTakeover(config, ports, opts = {}) {
|
|
1156
689
|
if (!ports.hasSignals()) {
|
|
1157
|
-
const declaredRoles = new Set(config.hostSections.map((
|
|
690
|
+
const declaredRoles = new Set(config.hostSections.map((s) => s.role));
|
|
1158
691
|
void Promise.resolve().then(() => ports.fetchPlan()).then(
|
|
1159
692
|
(response2) => ports.renderModules(
|
|
1160
693
|
{
|
|
@@ -1164,9 +697,9 @@ async function runTakeover(config, ports, opts = {}) {
|
|
|
1164
697
|
// 1. host-derived entries (kind:sectionId) — review F2;
|
|
1165
698
|
// 2. native kinds superseding a DECLARED host role (two-Q&As
|
|
1166
699
|
// bug: our qa next to their attached FAQ).
|
|
1167
|
-
order: response2.plan.order.filter((
|
|
1168
|
-
if (
|
|
1169
|
-
const superseded = KIND_SUPERSEDES_ROLE[
|
|
700
|
+
order: response2.plan.order.filter((entry) => {
|
|
701
|
+
if (entry.includes(":")) return false;
|
|
702
|
+
const superseded = KIND_SUPERSEDES_ROLE[entry];
|
|
1170
703
|
return !(superseded && declaredRoles.has(superseded));
|
|
1171
704
|
}),
|
|
1172
705
|
suppress: []
|
|
@@ -1200,7 +733,7 @@ async function runTakeover(config, ports, opts = {}) {
|
|
|
1200
733
|
return { outcome: "aborted-precommit", dispose: NOOP_DISPOSE };
|
|
1201
734
|
}
|
|
1202
735
|
const plan = response.plan;
|
|
1203
|
-
const sectionsById = new Map(config.hostSections.map((
|
|
736
|
+
const sectionsById = new Map(config.hostSections.map((s) => [s.id, s]));
|
|
1204
737
|
const toSuppress = [];
|
|
1205
738
|
let reservedPx = 0;
|
|
1206
739
|
for (const id of plan.suppress) {
|
|
@@ -1272,9 +805,8 @@ async function runTakeover(config, ports, opts = {}) {
|
|
|
1272
805
|
}
|
|
1273
806
|
|
|
1274
807
|
// src/takeover/fetchPlan.ts
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
decisionArchetype: z2.string().min(1),
|
|
808
|
+
var takeoverPlanResponseSchema = external_exports.object({
|
|
809
|
+
decisionArchetype: external_exports.string().min(1),
|
|
1278
810
|
plan: takeoverPlanSchema
|
|
1279
811
|
}).strict();
|
|
1280
812
|
function createTakeoverPlanFetcher(deps) {
|
|
@@ -1343,7 +875,7 @@ function applyRegionCurtain(region, opts = {}) {
|
|
|
1343
875
|
return lift;
|
|
1344
876
|
}
|
|
1345
877
|
function applyCurtainToTargets(targets, opts = {}) {
|
|
1346
|
-
const lifts = targets.map((
|
|
878
|
+
const lifts = targets.map((t) => applyRegionCurtain(t, opts));
|
|
1347
879
|
return () => {
|
|
1348
880
|
for (const lift of lifts) lift();
|
|
1349
881
|
};
|
|
@@ -1398,14 +930,20 @@ function attachLifecycleDispose(result) {
|
|
|
1398
930
|
|
|
1399
931
|
// src/widgets/PdpSectionHeaderLit.ts
|
|
1400
932
|
import { html, LitElement, nothing } from "lit";
|
|
1401
|
-
|
|
933
|
+
|
|
934
|
+
// src/widgets/ensureStylesInRoot.ts
|
|
935
|
+
function ensureStylesInRoot(el, id, css) {
|
|
1402
936
|
if (typeof document === "undefined") return;
|
|
1403
|
-
|
|
1404
|
-
const
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
937
|
+
const root = el.getRootNode();
|
|
938
|
+
const target = root instanceof ShadowRoot ? root : document.head;
|
|
939
|
+
if (target.querySelector(`#${id}`)) return;
|
|
940
|
+
const style = document.createElement("style");
|
|
941
|
+
style.id = id;
|
|
942
|
+
style.textContent = css;
|
|
943
|
+
target.appendChild(style);
|
|
1408
944
|
}
|
|
945
|
+
|
|
946
|
+
// src/widgets/PdpSectionHeaderLit.ts
|
|
1409
947
|
var SECTION_HEADER_CSS = `
|
|
1410
948
|
/* The header renders at the FULL-WIDTH section (band) level so the eyebrow
|
|
1411
949
|
can center across the whole horizontal slice, while the headline below
|
|
@@ -1530,7 +1068,7 @@ var PdpSectionHeaderLit = class extends LitElement {
|
|
|
1530
1068
|
}
|
|
1531
1069
|
connectedCallback() {
|
|
1532
1070
|
super.connectedCallback();
|
|
1533
|
-
|
|
1071
|
+
ensureStylesInRoot(this, "syntro-pdp-section-header-style", SECTION_HEADER_CSS);
|
|
1534
1072
|
}
|
|
1535
1073
|
render() {
|
|
1536
1074
|
const split = this.accent ? splitTitleAt(this.title, this.accent) : null;
|
|
@@ -1562,14 +1100,6 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-sec
|
|
|
1562
1100
|
|
|
1563
1101
|
// src/widgets/PdpSlotLit.ts
|
|
1564
1102
|
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
1103
|
var SLOT_CSS = `
|
|
1574
1104
|
.syntro-pdp-slot {
|
|
1575
1105
|
--sl-primary: var(--syntro-pdp-primary, hsl(150 32% 38%));
|
|
@@ -1731,7 +1261,7 @@ var PdpSlotLit = class extends LitElement2 {
|
|
|
1731
1261
|
}
|
|
1732
1262
|
connectedCallback() {
|
|
1733
1263
|
super.connectedCallback();
|
|
1734
|
-
|
|
1264
|
+
ensureStylesInRoot(this, "syntro-pdp-slot-style", SLOT_CSS);
|
|
1735
1265
|
}
|
|
1736
1266
|
willUpdate() {
|
|
1737
1267
|
this.dataset.state = this.state;
|
|
@@ -1774,23 +1304,23 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-slo
|
|
|
1774
1304
|
}
|
|
1775
1305
|
|
|
1776
1306
|
// src/takeover/renderModules.ts
|
|
1777
|
-
function parsePlanEntry(
|
|
1778
|
-
const
|
|
1779
|
-
if (
|
|
1780
|
-
return { kind:
|
|
1307
|
+
function parsePlanEntry(entry) {
|
|
1308
|
+
const i = entry.indexOf(":");
|
|
1309
|
+
if (i === -1) return { kind: entry, hostSectionId: null };
|
|
1310
|
+
return { kind: entry.slice(0, i), hostSectionId: entry.slice(i + 1) || null };
|
|
1781
1311
|
}
|
|
1782
1312
|
function createTakeoverModuleRenderer(deps) {
|
|
1783
1313
|
return async (plan, onModuleFailed) => {
|
|
1784
1314
|
const decisionArchetype = deps.getDecisionArchetype();
|
|
1785
1315
|
const row = deps.getPresetRow(decisionArchetype) ?? {};
|
|
1786
1316
|
const bakedWork = [];
|
|
1787
|
-
plan.order.forEach((
|
|
1788
|
-
const { kind, hostSectionId } = parsePlanEntry(
|
|
1317
|
+
plan.order.forEach((entry, index) => {
|
|
1318
|
+
const { kind, hostSectionId } = parsePlanEntry(entry);
|
|
1789
1319
|
const slotId = `pdp-slot-${kind}`;
|
|
1790
1320
|
const preset = row[slotId];
|
|
1791
1321
|
const tag = deps.resolveTag(kind, preset?.variant ?? null);
|
|
1792
1322
|
if (!tag) {
|
|
1793
|
-
deps.telemetry("takeover.module_kind_unknown", { entry
|
|
1323
|
+
deps.telemetry("takeover.module_kind_unknown", { entry, kind });
|
|
1794
1324
|
onModuleFailed(hostSectionId);
|
|
1795
1325
|
return;
|
|
1796
1326
|
}
|
|
@@ -1799,7 +1329,7 @@ function createTakeoverModuleRenderer(deps) {
|
|
|
1799
1329
|
const section = document.createElement("section");
|
|
1800
1330
|
section.toggleAttribute("data-syntro-pdp-section", true);
|
|
1801
1331
|
section.setAttribute("data-slot", slotId);
|
|
1802
|
-
section.setAttribute("data-syntro-takeover-entry",
|
|
1332
|
+
section.setAttribute("data-syntro-takeover-entry", entry);
|
|
1803
1333
|
const header = document.createElement("syntro-pdp-section-header");
|
|
1804
1334
|
header.eyebrow = task.header.eyebrow ?? "";
|
|
1805
1335
|
header.title = task.header.title ?? "";
|
|
@@ -1820,7 +1350,7 @@ function createTakeoverModuleRenderer(deps) {
|
|
|
1820
1350
|
} else {
|
|
1821
1351
|
slotEl.state = "failed";
|
|
1822
1352
|
}
|
|
1823
|
-
deps.telemetry("takeover.module_failed", { entry
|
|
1353
|
+
deps.telemetry("takeover.module_failed", { entry, kind });
|
|
1824
1354
|
onModuleFailed(hostSectionId);
|
|
1825
1355
|
};
|
|
1826
1356
|
const mountPayload = async (payload) => {
|
|
@@ -1857,14 +1387,6 @@ var RELATIONSHIP_LABELS = {
|
|
|
1857
1387
|
complement: "Pairs with this",
|
|
1858
1388
|
alternative: "Alternative"
|
|
1859
1389
|
};
|
|
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
1390
|
var OTHER_PRODUCT_TILE_CSS = `
|
|
1869
1391
|
.opt-card {
|
|
1870
1392
|
--opt-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -1909,7 +1431,7 @@ var OTHER_PRODUCT_TILE_CSS = `
|
|
|
1909
1431
|
font-size: 10px;
|
|
1910
1432
|
text-transform: uppercase;
|
|
1911
1433
|
letter-spacing: 0.14em;
|
|
1912
|
-
color: var(--opt-primary);
|
|
1434
|
+
color: var(--syntro-pdp-primary-ink, var(--opt-primary));
|
|
1913
1435
|
font-weight: 600;
|
|
1914
1436
|
display: flex;
|
|
1915
1437
|
align-items: center;
|
|
@@ -1994,64 +1516,107 @@ var OTHER_PRODUCT_TILE_CSS = `
|
|
|
1994
1516
|
background: color-mix(in srgb, var(--opt-primary) 10%, transparent);
|
|
1995
1517
|
}
|
|
1996
1518
|
`;
|
|
1519
|
+
var _unwatch, _OtherProductTileLit_instances, watchPrice_fn;
|
|
1997
1520
|
var OtherProductTileLit = class extends LitElement3 {
|
|
1998
1521
|
constructor() {
|
|
1999
1522
|
super(...arguments);
|
|
1523
|
+
__privateAdd(this, _OtherProductTileLit_instances);
|
|
2000
1524
|
this.tile = null;
|
|
1525
|
+
/** The store's live display price, painted over the catalog price. */
|
|
1526
|
+
this._livePrice = null;
|
|
1527
|
+
__privateAdd(this, _unwatch, null);
|
|
1528
|
+
/**
|
|
1529
|
+
* View asks whoever holds the page to open this product. The event crosses
|
|
1530
|
+
* shadow roots, so a canvas that mounts the page can answer it; on a page
|
|
1531
|
+
* where nothing listens, the button does nothing.
|
|
1532
|
+
*/
|
|
1533
|
+
this._onView = () => {
|
|
1534
|
+
if (!this.tile) return;
|
|
1535
|
+
const { recommended_product_id: productId, name, image_url, price_cents } = this.tile;
|
|
1536
|
+
const price = this._livePrice ?? this.tile.price;
|
|
1537
|
+
this.dispatchEvent(
|
|
1538
|
+
new CustomEvent("syntro:view-product", {
|
|
1539
|
+
bubbles: true,
|
|
1540
|
+
composed: true,
|
|
1541
|
+
detail: { productId, name, image_url, price, price_cents }
|
|
1542
|
+
})
|
|
1543
|
+
);
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
willUpdate(changed) {
|
|
1547
|
+
if (!changed.has("tile")) return;
|
|
1548
|
+
this._livePrice = null;
|
|
1549
|
+
if (this.isConnected) __privateMethod(this, _OtherProductTileLit_instances, watchPrice_fn).call(this);
|
|
1550
|
+
}
|
|
1551
|
+
disconnectedCallback() {
|
|
1552
|
+
var _a;
|
|
1553
|
+
super.disconnectedCallback();
|
|
1554
|
+
(_a = __privateGet(this, _unwatch)) == null ? void 0 : _a.call(this);
|
|
1555
|
+
__privateSet(this, _unwatch, null);
|
|
2001
1556
|
}
|
|
2002
1557
|
createRenderRoot() {
|
|
2003
1558
|
return this;
|
|
2004
1559
|
}
|
|
2005
1560
|
connectedCallback() {
|
|
2006
1561
|
super.connectedCallback();
|
|
2007
|
-
|
|
1562
|
+
ensureStylesInRoot(this, "syntro-other-product-tile-style", OTHER_PRODUCT_TILE_CSS);
|
|
1563
|
+
__privateMethod(this, _OtherProductTileLit_instances, watchPrice_fn).call(this);
|
|
2008
1564
|
}
|
|
2009
1565
|
render() {
|
|
2010
|
-
const
|
|
2011
|
-
if (!
|
|
1566
|
+
const t = this.tile;
|
|
1567
|
+
if (!t) {
|
|
2012
1568
|
return html3``;
|
|
2013
1569
|
}
|
|
2014
|
-
const relationshipLabel = RELATIONSHIP_LABELS[
|
|
2015
|
-
const price =
|
|
1570
|
+
const relationshipLabel = RELATIONSHIP_LABELS[t.relationship];
|
|
1571
|
+
const price = this._livePrice ?? t.price ?? "";
|
|
2016
1572
|
return html3`
|
|
2017
|
-
<article class="opt-card" data-relationship="${
|
|
2018
|
-
${
|
|
2019
|
-
<img src="${
|
|
1573
|
+
<article class="opt-card" data-relationship="${t.relationship}">
|
|
1574
|
+
${t.image_url ? html3`<div class="opt-image">
|
|
1575
|
+
<img src="${t.image_url}" alt="${t.name}" loading="lazy" />
|
|
2020
1576
|
</div>` : null}
|
|
2021
|
-
<div class="opt-relationship" data-relationship="${
|
|
1577
|
+
<div class="opt-relationship" data-relationship="${t.relationship}">
|
|
2022
1578
|
${relationshipLabel ? html3`${relationshipLabel}` : null}
|
|
2023
|
-
<span class="opt-relationship-type">${
|
|
1579
|
+
<span class="opt-relationship-type">${t.relationship}</span>
|
|
2024
1580
|
</div>
|
|
2025
|
-
<h3 class="opt-name">${
|
|
2026
|
-
${
|
|
2027
|
-
${
|
|
2028
|
-
${
|
|
2029
|
-
${
|
|
1581
|
+
<h3 class="opt-name">${t.name}</h3>
|
|
1582
|
+
${t.tagline ? html3`<p class="opt-tagline">${t.tagline}</p>` : null}
|
|
1583
|
+
${t.why_blurb ? html3`<p class="opt-why">${t.why_blurb}</p>` : null}
|
|
1584
|
+
${t.evidence_chips.length ? html3`<div class="opt-chips">
|
|
1585
|
+
${t.evidence_chips.map((c) => html3`<span class="opt-chip">${c.display}</span>`)}
|
|
2030
1586
|
</div>` : null}
|
|
2031
1587
|
<div class="opt-footer">
|
|
2032
1588
|
<span class="opt-price">${price}</span>
|
|
2033
|
-
<button type="button" class="opt-cta" data-action="view-product">
|
|
1589
|
+
<button type="button" class="opt-cta" data-action="view-product" @click=${this._onView}>
|
|
1590
|
+
View
|
|
1591
|
+
</button>
|
|
2034
1592
|
</div>
|
|
2035
1593
|
</article>
|
|
2036
1594
|
`;
|
|
2037
1595
|
}
|
|
2038
1596
|
};
|
|
1597
|
+
_unwatch = new WeakMap();
|
|
1598
|
+
_OtherProductTileLit_instances = new WeakSet();
|
|
1599
|
+
/** The store's live price, now and whenever the runtime publishes a
|
|
1600
|
+
* commerce adapter; no answer keeps the catalog price the server sent. */
|
|
1601
|
+
watchPrice_fn = function() {
|
|
1602
|
+
var _a;
|
|
1603
|
+
(_a = __privateGet(this, _unwatch)) == null ? void 0 : _a.call(this);
|
|
1604
|
+
__privateSet(this, _unwatch, null);
|
|
1605
|
+
const id = this.tile?.recommended_product_id;
|
|
1606
|
+
if (!id || !this.tile?.price) return;
|
|
1607
|
+
__privateSet(this, _unwatch, watchLivePrice(id, (live) => {
|
|
1608
|
+
if (this.tile?.recommended_product_id === id) this._livePrice = live.price;
|
|
1609
|
+
}));
|
|
1610
|
+
};
|
|
2039
1611
|
OtherProductTileLit.properties = {
|
|
2040
|
-
tile: { attribute: false }
|
|
1612
|
+
tile: { attribute: false },
|
|
1613
|
+
_livePrice: { state: true }
|
|
2041
1614
|
};
|
|
2042
1615
|
if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-other-product-tile")) {
|
|
2043
1616
|
customElements.define("syntro-pdp-other-product-tile", OtherProductTileLit);
|
|
2044
1617
|
}
|
|
2045
1618
|
|
|
2046
1619
|
// 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
1620
|
var CAROUSEL_CSS = `
|
|
2056
1621
|
/* Host containment \u2014 must not bust the PDP container width. */
|
|
2057
1622
|
syntro-pdp-other-products-carousel {
|
|
@@ -2125,7 +1690,7 @@ var PdpOtherProductsCarouselLit = class extends LitElement4 {
|
|
|
2125
1690
|
}
|
|
2126
1691
|
connectedCallback() {
|
|
2127
1692
|
super.connectedCallback();
|
|
2128
|
-
|
|
1693
|
+
ensureStylesInRoot(this, "syntro-pdp-other-products-carousel-style", CAROUSEL_CSS);
|
|
2129
1694
|
}
|
|
2130
1695
|
render() {
|
|
2131
1696
|
if (!this.data) return html4``;
|
|
@@ -2153,14 +1718,6 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-oth
|
|
|
2153
1718
|
|
|
2154
1719
|
// src/widgets/PdpOtherProductsGridLit.ts
|
|
2155
1720
|
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
1721
|
var GRID_CSS = `
|
|
2165
1722
|
syntro-pdp-other-products-grid {
|
|
2166
1723
|
display: block;
|
|
@@ -2197,7 +1754,7 @@ var PdpOtherProductsGridLit = class extends LitElement5 {
|
|
|
2197
1754
|
}
|
|
2198
1755
|
connectedCallback() {
|
|
2199
1756
|
super.connectedCallback();
|
|
2200
|
-
|
|
1757
|
+
ensureStylesInRoot(this, "syntro-pdp-other-products-grid-style", GRID_CSS);
|
|
2201
1758
|
}
|
|
2202
1759
|
render() {
|
|
2203
1760
|
if (!this.data) return html5``;
|
|
@@ -2235,7 +1792,7 @@ function _readPdpDecisionArchetypes() {
|
|
|
2235
1792
|
if (typeof window === "undefined") return void 0;
|
|
2236
1793
|
return window.__SYNTRO_PDP_DECISION_ARCHETYPES__;
|
|
2237
1794
|
}
|
|
2238
|
-
async function composeModule(authedFetch, args) {
|
|
1795
|
+
async function composeModule(authedFetch, args, signal) {
|
|
2239
1796
|
try {
|
|
2240
1797
|
const requestBody = {
|
|
2241
1798
|
product_name: args.productName,
|
|
@@ -2254,7 +1811,8 @@ async function composeModule(authedFetch, args) {
|
|
|
2254
1811
|
};
|
|
2255
1812
|
const resp = await authedFetch("/api/pdp/module", {
|
|
2256
1813
|
method: "POST",
|
|
2257
|
-
body: JSON.stringify(requestBody)
|
|
1814
|
+
body: JSON.stringify(requestBody),
|
|
1815
|
+
signal
|
|
2258
1816
|
});
|
|
2259
1817
|
if (!resp.ok) return null;
|
|
2260
1818
|
const data = await resp.json();
|
|
@@ -2292,33 +1850,33 @@ function _titleCase(slug) {
|
|
|
2292
1850
|
return slug.split(/[-_\s]+/).map((w) => w ? w[0].toUpperCase() + w.slice(1) : "").join(" ");
|
|
2293
1851
|
}
|
|
2294
1852
|
function _countSignals(signals) {
|
|
2295
|
-
let
|
|
1853
|
+
let n = 0;
|
|
2296
1854
|
const walk = (v) => {
|
|
2297
1855
|
if (v && typeof v === "object") {
|
|
2298
1856
|
for (const x of Object.values(v)) walk(x);
|
|
2299
1857
|
} else if (v !== "" && v != null) {
|
|
2300
|
-
|
|
1858
|
+
n++;
|
|
2301
1859
|
}
|
|
2302
1860
|
};
|
|
2303
1861
|
walk(signals);
|
|
2304
|
-
return
|
|
1862
|
+
return n;
|
|
2305
1863
|
}
|
|
2306
1864
|
function _signalDimensions(signals) {
|
|
2307
|
-
let
|
|
1865
|
+
let n = 0;
|
|
2308
1866
|
for (const v of Object.values(signals)) {
|
|
2309
1867
|
if (v && typeof v === "object") {
|
|
2310
|
-
if (Object.keys(v).length)
|
|
1868
|
+
if (Object.keys(v).length) n++;
|
|
2311
1869
|
} else if (v !== "" && v != null && v !== false) {
|
|
2312
|
-
|
|
1870
|
+
n++;
|
|
2313
1871
|
}
|
|
2314
1872
|
}
|
|
2315
|
-
return
|
|
1873
|
+
return n;
|
|
2316
1874
|
}
|
|
2317
1875
|
function _fingerprint(signals) {
|
|
2318
|
-
const
|
|
1876
|
+
const s = JSON.stringify(signals);
|
|
2319
1877
|
let h = 2166136261;
|
|
2320
|
-
for (let
|
|
2321
|
-
h ^=
|
|
1878
|
+
for (let i = 0; i < s.length; i++) {
|
|
1879
|
+
h ^= s.charCodeAt(i);
|
|
2322
1880
|
h = Math.imul(h, 16777619);
|
|
2323
1881
|
}
|
|
2324
1882
|
return (h >>> 0).toString(16).padStart(8, "0");
|
|
@@ -2416,9 +1974,9 @@ function buildExplain(opts) {
|
|
|
2416
1974
|
dossier
|
|
2417
1975
|
};
|
|
2418
1976
|
}
|
|
2419
|
-
async function fetchDossier(authedFetch) {
|
|
1977
|
+
async function fetchDossier(authedFetch, signal) {
|
|
2420
1978
|
try {
|
|
2421
|
-
const resp = await authedFetch("/api/pdp/dossier", { method: "POST" });
|
|
1979
|
+
const resp = await authedFetch("/api/pdp/dossier", { method: "POST", signal });
|
|
2422
1980
|
if (!resp.ok) return [];
|
|
2423
1981
|
const data = await resp.json();
|
|
2424
1982
|
const rows = data.metrics;
|
|
@@ -2558,8 +2116,8 @@ var _PAGE_TRAIL_MAX_CHARS = 600;
|
|
|
2558
2116
|
function _renderPageTrail(history) {
|
|
2559
2117
|
const paths = [];
|
|
2560
2118
|
let length = 0;
|
|
2561
|
-
for (let
|
|
2562
|
-
let path = history[
|
|
2119
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
2120
|
+
let path = history[i].url;
|
|
2563
2121
|
try {
|
|
2564
2122
|
const u = new URL(path);
|
|
2565
2123
|
path = `${u.pathname}${u.search}`;
|
|
@@ -2572,14 +2130,6 @@ function _renderPageTrail(history) {
|
|
|
2572
2130
|
}
|
|
2573
2131
|
return paths.join(" \u2192 ");
|
|
2574
2132
|
}
|
|
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
2133
|
var SECTION_LAYOUT_CSS = `
|
|
2584
2134
|
[data-syntro-pdp-root] {
|
|
2585
2135
|
--sc-pdp-max-w: var(--syntro-pdp-max-width, 1080px);
|
|
@@ -2760,6 +2310,7 @@ function resolveTagForMount(inst) {
|
|
|
2760
2310
|
}
|
|
2761
2311
|
return null;
|
|
2762
2312
|
}
|
|
2313
|
+
var _abort;
|
|
2763
2314
|
var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
2764
2315
|
constructor() {
|
|
2765
2316
|
super(...arguments);
|
|
@@ -2840,6 +2391,15 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2840
2391
|
* after reconnect must undo that before the engine renders again. */
|
|
2841
2392
|
this._takeoverRanBefore = false;
|
|
2842
2393
|
this._takeoverLateWarned = false;
|
|
2394
|
+
/**
|
|
2395
|
+
* Cancels in-flight /api/pdp/module + /api/pdp/dossier calls when the
|
|
2396
|
+
* element is removed mid-request — a canvas card can be torn down while a
|
|
2397
|
+
* fetch is still pending, and an unaborted response landing after teardown
|
|
2398
|
+
* would apply a mutation/mount to a disconnected element. Reset to a fresh
|
|
2399
|
+
* controller on (re)connect so a re-added element gets a live signal
|
|
2400
|
+
* instead of one that's already aborted from the previous lifecycle.
|
|
2401
|
+
*/
|
|
2402
|
+
__privateAdd(this, _abort, new AbortController());
|
|
2843
2403
|
/**
|
|
2844
2404
|
* bfcache re-entry (review F4). pagehide disposes the run
|
|
2845
2405
|
* (attachLifecycleDispose) but a bfcache restore does NOT re-fire
|
|
@@ -2872,8 +2432,8 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2872
2432
|
warn: (code, props) => this._warnDecision(code, props),
|
|
2873
2433
|
cleanup: () => this._cleanupDecisionEffects()
|
|
2874
2434
|
});
|
|
2875
|
-
this._onDossierToggle = (
|
|
2876
|
-
const open =
|
|
2435
|
+
this._onDossierToggle = (e) => {
|
|
2436
|
+
const open = e.target.open;
|
|
2877
2437
|
this._dispatchDecision({ type: "dossier-toggled", open });
|
|
2878
2438
|
};
|
|
2879
2439
|
/** Stamps data-viewport on this tag (central classification,
|
|
@@ -2909,6 +2469,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2909
2469
|
return this;
|
|
2910
2470
|
}
|
|
2911
2471
|
connectedCallback() {
|
|
2472
|
+
if (__privateGet(this, _abort).signal.aborted) __privateSet(this, _abort, new AbortController());
|
|
2912
2473
|
if (!this._takeoverModeLatched) {
|
|
2913
2474
|
this._takeoverModeLatched = true;
|
|
2914
2475
|
this._takeoverMode = Boolean(this.takeover);
|
|
@@ -2920,7 +2481,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2920
2481
|
}
|
|
2921
2482
|
if (this._takeoverMode) {
|
|
2922
2483
|
super.connectedCallback();
|
|
2923
|
-
|
|
2484
|
+
ensureStylesInRoot(this, "syntro-pdp-layout-style", SECTION_LAYOUT_CSS);
|
|
2924
2485
|
if (typeof window !== "undefined") {
|
|
2925
2486
|
window.removeEventListener("pageshow", this._onTakeoverPageShow);
|
|
2926
2487
|
window.addEventListener("pageshow", this._onTakeoverPageShow);
|
|
@@ -2930,7 +2491,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2930
2491
|
}
|
|
2931
2492
|
const reconnecting = this._decisionState.connection === "disconnected";
|
|
2932
2493
|
super.connectedCallback();
|
|
2933
|
-
|
|
2494
|
+
ensureStylesInRoot(this, "syntro-pdp-layout-style", SECTION_LAYOUT_CSS);
|
|
2934
2495
|
if (!this.productId) this._selfIdentify = true;
|
|
2935
2496
|
this._refreshNativeSlots();
|
|
2936
2497
|
this._syncDecisionRuntime(reconnecting);
|
|
@@ -2944,6 +2505,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
2944
2505
|
}
|
|
2945
2506
|
}
|
|
2946
2507
|
disconnectedCallback() {
|
|
2508
|
+
__privateGet(this, _abort).abort();
|
|
2947
2509
|
if (this._takeoverMode) {
|
|
2948
2510
|
super.disconnectedCallback();
|
|
2949
2511
|
if (typeof window !== "undefined") {
|
|
@@ -3145,24 +2707,28 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3145
2707
|
}),
|
|
3146
2708
|
requestLiveModule: async (kind, decisionArchetype) => {
|
|
3147
2709
|
if (!authedFetch) return null;
|
|
3148
|
-
return composeModule(
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
slug:
|
|
3161
|
-
|
|
2710
|
+
return composeModule(
|
|
2711
|
+
authedFetch,
|
|
2712
|
+
{
|
|
2713
|
+
productName: this.productName,
|
|
2714
|
+
// Guaranteed non-empty: _startTakeover gates on a missing product id.
|
|
2715
|
+
productId: this.productId,
|
|
2716
|
+
kind,
|
|
2717
|
+
variant: null,
|
|
2718
|
+
// The takeover round trip scores only the decision axis (the server
|
|
2719
|
+
// resolves the plan from it); the sport-archetype axis is unscored
|
|
2720
|
+
// on this path, so it is sent as an explicit 'unknown' — never a
|
|
2721
|
+
// silently-invented value.
|
|
2722
|
+
archetype: { slug: "unknown", description: "" },
|
|
2723
|
+
decisionArchetype: decisionArchetype === "unknown" ? null : {
|
|
2724
|
+
slug: decisionArchetype,
|
|
2725
|
+
description: decisionDescriptions[decisionArchetype] ?? ""
|
|
2726
|
+
},
|
|
2727
|
+
behaviorSummary: "",
|
|
2728
|
+
signals
|
|
3162
2729
|
},
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
});
|
|
2730
|
+
__privateGet(this, _abort).signal
|
|
2731
|
+
);
|
|
3166
2732
|
},
|
|
3167
2733
|
telemetry: telemetry2,
|
|
3168
2734
|
getDecisionArchetype: () => scored.decisionArchetype
|
|
@@ -3329,13 +2895,35 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3329
2895
|
}
|
|
3330
2896
|
container.innerHTML = "";
|
|
3331
2897
|
const el = document.createElement(tag);
|
|
3332
|
-
el.data = inst.content;
|
|
2898
|
+
el.data = this._withStoreImages(tag, inst.content);
|
|
3333
2899
|
container.appendChild(el);
|
|
3334
2900
|
this._mountDurations.set(slotId, Math.round(performance.now() - slotStartedAt));
|
|
3335
2901
|
outcomes.push({ status: "mounted", slot: slotId });
|
|
3336
2902
|
}
|
|
3337
2903
|
return outcomes;
|
|
3338
2904
|
}
|
|
2905
|
+
/**
|
|
2906
|
+
* The cross-sell tiles' images are store-relative catalog paths. Inside an
|
|
2907
|
+
* app host's card the document is the host's sandbox and <base> is blocked,
|
|
2908
|
+
* so each is resolved against the runtime's storefront before the carousel
|
|
2909
|
+
* or grid gets it. On a storefront page there is no storefrontUrl and
|
|
2910
|
+
* nothing changes.
|
|
2911
|
+
*/
|
|
2912
|
+
_withStoreImages(tag, content) {
|
|
2913
|
+
if (tag !== "syntro-pdp-other-products-carousel" && tag !== "syntro-pdp-other-products-grid")
|
|
2914
|
+
return content;
|
|
2915
|
+
const tiles = content?.tiles;
|
|
2916
|
+
if (!Array.isArray(tiles)) return content;
|
|
2917
|
+
return {
|
|
2918
|
+
...content,
|
|
2919
|
+
tiles: tiles.map(
|
|
2920
|
+
(tile) => tile && typeof tile === "object" && "image_url" in tile ? {
|
|
2921
|
+
...tile,
|
|
2922
|
+
image_url: resolveStorePath(tile.image_url, this.runtimeRef)
|
|
2923
|
+
} : tile
|
|
2924
|
+
)
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
3339
2927
|
/** Render from config: score the archetype → mount the preset set-values. */
|
|
3340
2928
|
async _fireFromConfig(productPresets, score, authedFetch, generation, run, scoreMulti) {
|
|
3341
2929
|
const startedAt = performance.now();
|
|
@@ -3353,7 +2941,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3353
2941
|
let scoredArgs = null;
|
|
3354
2942
|
const composeAdapter = createPdpComposeAdapter({
|
|
3355
2943
|
scoreSummary: () => authedFetch ? score({ model_id: "summary", name: "behavior_summary", type: "string", signals }) : Promise.resolve("unknown"),
|
|
3356
|
-
requestDossier: () => authedFetch ? fetchDossier(authedFetch) : Promise.resolve([]),
|
|
2944
|
+
requestDossier: () => authedFetch ? fetchDossier(authedFetch, __privateGet(this, _abort).signal) : Promise.resolve([]),
|
|
3357
2945
|
resolveTemplate: (_currentGeneration, composeUnset) => resolveScoredTemplate({
|
|
3358
2946
|
// Wrap the scorer so the resolved SPORT archetype lands on the
|
|
3359
2947
|
// element the instant it scores — the native-takeover check needs it
|
|
@@ -3441,22 +3029,26 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3441
3029
|
requestModule: async ({ slot, archetype, decisionArchetype, behaviorSummary }) => {
|
|
3442
3030
|
if (!authedFetch || !run.isCurrent()) return;
|
|
3443
3031
|
const kind = slot.replace("pdp-slot-", "");
|
|
3444
|
-
const payload = await composeModule(
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3032
|
+
const payload = await composeModule(
|
|
3033
|
+
authedFetch,
|
|
3034
|
+
{
|
|
3035
|
+
productName: this.productName,
|
|
3036
|
+
productId: this.productId || null,
|
|
3037
|
+
kind,
|
|
3038
|
+
variant: null,
|
|
3039
|
+
archetype: {
|
|
3040
|
+
slug: archetype,
|
|
3041
|
+
description: archetypeDescriptions[archetype] ?? ""
|
|
3042
|
+
},
|
|
3043
|
+
decisionArchetype: decisionArchetype === "unknown" ? null : {
|
|
3044
|
+
slug: decisionArchetype,
|
|
3045
|
+
description: decisionDescriptions[decisionArchetype] ?? ""
|
|
3046
|
+
},
|
|
3047
|
+
behaviorSummary,
|
|
3048
|
+
signals
|
|
3452
3049
|
},
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
description: decisionDescriptions[decisionArchetype] ?? ""
|
|
3456
|
-
},
|
|
3457
|
-
behaviorSummary,
|
|
3458
|
-
signals
|
|
3459
|
-
});
|
|
3050
|
+
__privateGet(this, _abort).signal
|
|
3051
|
+
);
|
|
3460
3052
|
if (!run.isCurrent()) return;
|
|
3461
3053
|
if (!payload) {
|
|
3462
3054
|
run.dispatch({ type: "module-failed", generation, slot });
|
|
@@ -3582,7 +3174,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3582
3174
|
_renderWhyBox(view) {
|
|
3583
3175
|
if (view.status === "hidden") return "";
|
|
3584
3176
|
if (view.status === "loading") return this._renderWhyBoxLoading();
|
|
3585
|
-
const
|
|
3177
|
+
const e = view.explanation;
|
|
3586
3178
|
return html6`
|
|
3587
3179
|
<style>
|
|
3588
3180
|
/* Token bridge: every color resolves from a --syntro-pdp-* var the
|
|
@@ -3624,7 +3216,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3624
3216
|
[data-syntro-pdp-why][open] > summary { margin-bottom: 6px; }
|
|
3625
3217
|
[data-syntro-pdp-why-note] { font-size: 11px; color: var(--syntro-pdp-muted, #666); margin: 0 0 4px; }
|
|
3626
3218
|
[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); }
|
|
3219
|
+
[data-syntro-pdp-why-archetype] strong { color: var(--syntro-pdp-primary-ink, var(--syntro-pdp-primary, #1a1a1a)); }
|
|
3628
3220
|
[data-syntro-pdp-why-summary] {
|
|
3629
3221
|
font-size: 12.5px; font-style: italic; color: var(--syntro-pdp-muted, #666); margin: 6px 0 8px;
|
|
3630
3222
|
border-left: 2px solid var(--syntro-pdp-accent, #555); padding-left: 10px;
|
|
@@ -3670,14 +3262,14 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3670
3262
|
Shown in demo mode only — a peek under the hood at the live personalization.
|
|
3671
3263
|
</div>
|
|
3672
3264
|
<div data-syntro-pdp-why-archetype>
|
|
3673
|
-
Shopping as: <strong>${
|
|
3265
|
+
Shopping as: <strong>${e.archetypeLabel}</strong>${e.archetypeDesc ? html6` — ${e.archetypeDesc}` : ""}
|
|
3674
3266
|
</div>
|
|
3675
|
-
${
|
|
3676
|
-
What they're weighing: <strong>${
|
|
3267
|
+
${e.decisionLabel ? html6`<div data-syntro-pdp-why-archetype>
|
|
3268
|
+
What they're weighing: <strong>${e.decisionLabel}</strong>${e.decisionDesc ? html6` — “${e.decisionDesc}”` : ""}
|
|
3677
3269
|
</div>` : ""}
|
|
3678
|
-
${
|
|
3270
|
+
${e.summary ? html6`<div data-syntro-pdp-why-summary>"${e.summary}"</div>` : ""}
|
|
3679
3271
|
<div data-syntro-pdp-why-metrics>
|
|
3680
|
-
${
|
|
3272
|
+
${e.metrics.map(
|
|
3681
3273
|
(m) => html6`<div data-syntro-pdp-why-metric>
|
|
3682
3274
|
<span data-syntro-pdp-why-metric-value>${m.value}</span>
|
|
3683
3275
|
<span data-syntro-pdp-why-metric-label>${m.label}</span>
|
|
@@ -3760,10 +3352,10 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3760
3352
|
${skeletonStyles}
|
|
3761
3353
|
<div data-syntro-pdp-root data-state="pending" data-syntro-pdp-skeleton>
|
|
3762
3354
|
${[0, 1, 2].map(
|
|
3763
|
-
(
|
|
3355
|
+
(i) => html6`
|
|
3764
3356
|
<section
|
|
3765
3357
|
data-syntro-pdp-skeleton-section
|
|
3766
|
-
style="animation-delay: ${
|
|
3358
|
+
style="animation-delay: ${i * 0.15}s"
|
|
3767
3359
|
>
|
|
3768
3360
|
<div data-syntro-pdp-skeleton-header>
|
|
3769
3361
|
<div data-syntro-pdp-skeleton-eyebrow></div>
|
|
@@ -3820,6 +3412,7 @@ var _SyntroPdpLit = class _SyntroPdpLit extends LitElement6 {
|
|
|
3820
3412
|
`;
|
|
3821
3413
|
}
|
|
3822
3414
|
};
|
|
3415
|
+
_abort = new WeakMap();
|
|
3823
3416
|
_SyntroPdpLit.properties = {
|
|
3824
3417
|
productId: { type: String, attribute: "product-id" },
|
|
3825
3418
|
productName: { type: String, attribute: "product-name" },
|
|
@@ -3833,9 +3426,9 @@ _SyntroPdpLit.properties = {
|
|
|
3833
3426
|
/**
|
|
3834
3427
|
* Self-contained skeleton (shimmer) CSS. Rendered inside the component
|
|
3835
3428
|
* during the pre-plan / streaming phase. The global section-layout +
|
|
3836
|
-
* per-slot accent rules live in
|
|
3837
|
-
*
|
|
3838
|
-
*
|
|
3429
|
+
* per-slot accent rules live in `SECTION_LAYOUT_CSS`, injected once per
|
|
3430
|
+
* root via `ensureStylesInRoot`, not here, so they're emitted once per
|
|
3431
|
+
* root rather than once per `<syntro-pdp>` instance.
|
|
3839
3432
|
*/
|
|
3840
3433
|
_SyntroPdpLit._skeletonStyles = html6`
|
|
3841
3434
|
<style>
|
|
@@ -4258,14 +3851,14 @@ function collectFromNode(node) {
|
|
|
4258
3851
|
const containers = [];
|
|
4259
3852
|
if (node.matches(`[${NATIVE_SLOT_ATTR}]`)) containers.push(node);
|
|
4260
3853
|
containers.push(...node.querySelectorAll(`[${NATIVE_SLOT_ATTR}]`));
|
|
4261
|
-
for (const
|
|
4262
|
-
if (
|
|
4263
|
-
const pid = nativeProductId(
|
|
3854
|
+
for (const c of containers) {
|
|
3855
|
+
if (c.closest("syntro-pdp")) continue;
|
|
3856
|
+
const pid = nativeProductId(c);
|
|
4264
3857
|
if (pid) pending.add(pid);
|
|
4265
3858
|
}
|
|
4266
3859
|
}
|
|
4267
|
-
var onDemoToggle = (
|
|
4268
|
-
const detail =
|
|
3860
|
+
var onDemoToggle = (e) => {
|
|
3861
|
+
const detail = e.detail;
|
|
4269
3862
|
if (!detail || typeof detail.on !== "boolean") return;
|
|
4270
3863
|
toggleOn = detail.on;
|
|
4271
3864
|
renderSession();
|
|
@@ -4341,9 +3934,9 @@ async function resolveDomSelectorImage(input, _signal) {
|
|
|
4341
3934
|
// src/bind/jsonld.ts
|
|
4342
3935
|
function isProduct(node) {
|
|
4343
3936
|
if (typeof node !== "object" || node === null) return false;
|
|
4344
|
-
const
|
|
4345
|
-
if (typeof
|
|
4346
|
-
if (Array.isArray(
|
|
3937
|
+
const t = node["@type"];
|
|
3938
|
+
if (typeof t === "string") return t === "Product";
|
|
3939
|
+
if (Array.isArray(t)) return t.includes("Product");
|
|
4347
3940
|
return false;
|
|
4348
3941
|
}
|
|
4349
3942
|
function parseScripts(scripts) {
|
|
@@ -4358,7 +3951,7 @@ function parseScripts(scripts) {
|
|
|
4358
3951
|
while (stack.length) {
|
|
4359
3952
|
const cur = stack.pop();
|
|
4360
3953
|
if (Array.isArray(cur)) {
|
|
4361
|
-
for (const
|
|
3954
|
+
for (const c of cur) stack.push(c);
|
|
4362
3955
|
continue;
|
|
4363
3956
|
}
|
|
4364
3957
|
if (isProduct(cur)) return cur;
|
|
@@ -4563,14 +4156,15 @@ async function resolveImageBind(input, signal) {
|
|
|
4563
4156
|
}
|
|
4564
4157
|
|
|
4565
4158
|
// src/bind/controller.ts
|
|
4566
|
-
var
|
|
4159
|
+
var _abort2, _pending, _unwatch2, _destroyed, _BindController_instances, resolveImage_fn, resolvePrice_fn;
|
|
4567
4160
|
var BindController = class {
|
|
4568
4161
|
constructor(products, callback) {
|
|
4569
4162
|
this.products = products;
|
|
4570
4163
|
this.callback = callback;
|
|
4571
4164
|
__privateAdd(this, _BindController_instances);
|
|
4572
|
-
__privateAdd(this,
|
|
4165
|
+
__privateAdd(this, _abort2, new AbortController());
|
|
4573
4166
|
__privateAdd(this, _pending, []);
|
|
4167
|
+
__privateAdd(this, _unwatch2, []);
|
|
4574
4168
|
__privateAdd(this, _destroyed, false);
|
|
4575
4169
|
}
|
|
4576
4170
|
start() {
|
|
@@ -4580,6 +4174,15 @@ var BindController = class {
|
|
|
4580
4174
|
}
|
|
4581
4175
|
if (product.price?.bind) {
|
|
4582
4176
|
__privateGet(this, _pending).push(__privateMethod(this, _BindController_instances, resolvePrice_fn).call(this, product.id, product.price.bind));
|
|
4177
|
+
} else if (product.price || product.salePrice) {
|
|
4178
|
+
__privateGet(this, _unwatch2).push(
|
|
4179
|
+
watchLivePrice(product.id, (live) => {
|
|
4180
|
+
if (__privateGet(this, _destroyed)) return;
|
|
4181
|
+
this.callback(product.id, "price", live.price);
|
|
4182
|
+
this.callback(product.id, "compare_at", live.compareAt);
|
|
4183
|
+
})
|
|
4184
|
+
);
|
|
4185
|
+
__privateGet(this, _pending).push(fetchLivePrice(product.id).then(() => void 0));
|
|
4583
4186
|
}
|
|
4584
4187
|
}
|
|
4585
4188
|
}
|
|
@@ -4589,23 +4192,42 @@ var BindController = class {
|
|
|
4589
4192
|
destroy() {
|
|
4590
4193
|
if (__privateGet(this, _destroyed)) return;
|
|
4591
4194
|
__privateSet(this, _destroyed, true);
|
|
4592
|
-
__privateGet(this,
|
|
4195
|
+
__privateGet(this, _abort2).abort();
|
|
4196
|
+
for (const stop of __privateGet(this, _unwatch2)) stop();
|
|
4197
|
+
__privateSet(this, _unwatch2, []);
|
|
4593
4198
|
}
|
|
4594
4199
|
};
|
|
4595
|
-
|
|
4200
|
+
_abort2 = new WeakMap();
|
|
4596
4201
|
_pending = new WeakMap();
|
|
4202
|
+
_unwatch2 = new WeakMap();
|
|
4597
4203
|
_destroyed = new WeakMap();
|
|
4598
4204
|
_BindController_instances = new WeakSet();
|
|
4599
4205
|
resolveImage_fn = async function(productId, bind) {
|
|
4600
|
-
const value = await resolveImageBind(bind, __privateGet(this,
|
|
4206
|
+
const value = await resolveImageBind(bind, __privateGet(this, _abort2).signal);
|
|
4601
4207
|
if (__privateGet(this, _destroyed)) return;
|
|
4602
4208
|
this.callback(productId, "image", value);
|
|
4603
4209
|
};
|
|
4604
4210
|
resolvePrice_fn = async function(productId, bind) {
|
|
4605
|
-
const value = await resolvePriceBind(bind, __privateGet(this,
|
|
4211
|
+
const value = await resolvePriceBind(bind, __privateGet(this, _abort2).signal);
|
|
4606
4212
|
if (__privateGet(this, _destroyed)) return;
|
|
4607
4213
|
this.callback(productId, "price", value);
|
|
4608
4214
|
};
|
|
4215
|
+
function mergeResolved(resolved, productId, kind, value) {
|
|
4216
|
+
if (kind === "compare_at") {
|
|
4217
|
+
return { ...resolved, [productId]: { ...resolved[productId], compareAt: value } };
|
|
4218
|
+
}
|
|
4219
|
+
if (value == null) return resolved;
|
|
4220
|
+
return { ...resolved, [productId]: { ...resolved[productId], [kind]: value } };
|
|
4221
|
+
}
|
|
4222
|
+
function keepResolved(resolved, products) {
|
|
4223
|
+
const kept = {};
|
|
4224
|
+
for (const product of products) {
|
|
4225
|
+
const entry = resolved[product.id];
|
|
4226
|
+
if (!entry || product.price?.bind || !("compareAt" in entry)) continue;
|
|
4227
|
+
kept[product.id] = { price: entry.price, compareAt: entry.compareAt };
|
|
4228
|
+
}
|
|
4229
|
+
return kept;
|
|
4230
|
+
}
|
|
4609
4231
|
|
|
4610
4232
|
// src/decision/card.ts
|
|
4611
4233
|
var VARIANT_ERROR_COPY = {
|
|
@@ -4627,9 +4249,11 @@ function createInitialCardState(generation = 0) {
|
|
|
4627
4249
|
variants: { status: "idle" },
|
|
4628
4250
|
cart: { status: "idle" },
|
|
4629
4251
|
selection: {},
|
|
4630
|
-
activation: { status: "idle" }
|
|
4252
|
+
activation: { status: "idle" },
|
|
4253
|
+
optionsPinned: false
|
|
4631
4254
|
};
|
|
4632
4255
|
}
|
|
4256
|
+
var optionsOpen = (s) => s.face === "back" || s.optionsPinned;
|
|
4633
4257
|
function selectCardView(state) {
|
|
4634
4258
|
const primaryCta = state.cart.status === "added" ? {
|
|
4635
4259
|
mode: "view-cart",
|
|
@@ -4639,7 +4263,7 @@ function selectCardView(state) {
|
|
|
4639
4263
|
disabled: state.connection !== "connected" || state.commerce !== "available" || state.product.status !== "valid"
|
|
4640
4264
|
};
|
|
4641
4265
|
let panel = { status: "hidden", payload: null, error: null, retryable: false };
|
|
4642
|
-
if (state
|
|
4266
|
+
if (optionsOpen(state)) {
|
|
4643
4267
|
switch (state.variants.status) {
|
|
4644
4268
|
case "ready":
|
|
4645
4269
|
panel = { status: "ready", payload: state.variants.payload, error: null, retryable: false };
|
|
@@ -4684,7 +4308,11 @@ function replaceProps(state, event) {
|
|
|
4684
4308
|
return { state, effects: effects2 };
|
|
4685
4309
|
}
|
|
4686
4310
|
const generation = state.generation + 1;
|
|
4687
|
-
const next = {
|
|
4311
|
+
const next = {
|
|
4312
|
+
...createInitialCardState(generation),
|
|
4313
|
+
commerce: state.commerce,
|
|
4314
|
+
optionsPinned: state.optionsPinned
|
|
4315
|
+
};
|
|
4688
4316
|
const effects = [{ type: "cancel-flip-settle" }];
|
|
4689
4317
|
if (!event.valid || event.productId === null) {
|
|
4690
4318
|
return {
|
|
@@ -4717,7 +4345,13 @@ function activatePrimary(state, event) {
|
|
|
4717
4345
|
// CTA is a plain navigating <a>, never dispatched here) — but the state
|
|
4718
4346
|
// machine itself must also refuse to start the Woo/Shopify variant-
|
|
4719
4347
|
// lookup flow for a product that has none.
|
|
4720
|
-
!state.product.commerceCapable || state.face !== "front")
|
|
4348
|
+
!state.product.commerceCapable || state.face !== "front" || // A pinned (double-wide) card has no primary CTA to activate — Add to
|
|
4349
|
+
// cart lives in the options panel beside the front face. Without this
|
|
4350
|
+
// guard a stray primary-activated dispatch after an add (cart status
|
|
4351
|
+
// 'added', face still 'front') would fall into the view-cart branch below
|
|
4352
|
+
// and emit close-sdk/open-cart/view_cart_clicked, which a pinned card must
|
|
4353
|
+
// never do (Task 1 keeps the card pinned open across the add).
|
|
4354
|
+
state.optionsPinned) {
|
|
4721
4355
|
return unchanged2(state);
|
|
4722
4356
|
}
|
|
4723
4357
|
const productId = state.product.id;
|
|
@@ -4785,7 +4419,7 @@ function serializeSelection(selection) {
|
|
|
4785
4419
|
};
|
|
4786
4420
|
}
|
|
4787
4421
|
function activateVariantAdd(state, event) {
|
|
4788
|
-
if (state.connection !== "connected" || state.commerce !== "available" || state.product.status !== "valid" || state
|
|
4422
|
+
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
4423
|
return unchanged2(state);
|
|
4790
4424
|
}
|
|
4791
4425
|
const variant = state.variants.payload.variants.find(
|
|
@@ -4844,6 +4478,9 @@ function transitionCard(state, event) {
|
|
|
4844
4478
|
const commerce = event.available ? "available" : "unavailable";
|
|
4845
4479
|
return commerce === state.commerce ? unchanged2(state) : { state: { ...state, commerce }, effects: [] };
|
|
4846
4480
|
}
|
|
4481
|
+
if (event.type === "options-pinned") {
|
|
4482
|
+
return event.pinned === state.optionsPinned ? unchanged2(state) : { state: { ...state, optionsPinned: event.pinned }, effects: [] };
|
|
4483
|
+
}
|
|
4847
4484
|
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
4485
|
return unchanged2(state);
|
|
4849
4486
|
}
|
|
@@ -4884,7 +4521,7 @@ function transitionCard(state, event) {
|
|
|
4884
4521
|
return { state: resumed.state, effects: [failureEffect, ...resumed.effects] };
|
|
4885
4522
|
}
|
|
4886
4523
|
case "selection-changed":
|
|
4887
|
-
if (state.commerce !== "available" || state
|
|
4524
|
+
if (state.commerce !== "available" || !optionsOpen(state) || state.variants.status !== "ready") {
|
|
4888
4525
|
return unchanged2(state);
|
|
4889
4526
|
}
|
|
4890
4527
|
return {
|
|
@@ -5109,9 +4746,9 @@ function createCardEffectExecutor(ports) {
|
|
|
5109
4746
|
var VIEW_DETAIL_ACTION_ID = "view_detail";
|
|
5110
4747
|
function pickDetailCta(ctas) {
|
|
5111
4748
|
if (!ctas?.length) return void 0;
|
|
5112
|
-
const navigates = ctas.filter((
|
|
4749
|
+
const navigates = ctas.filter((c) => !!c.href);
|
|
5113
4750
|
if (!navigates.length) return void 0;
|
|
5114
|
-
return navigates.find((
|
|
4751
|
+
return navigates.find((c) => c.actionId === VIEW_DETAIL_ACTION_ID) ?? navigates.find((c) => c.variant && c.variant !== "primary") ?? navigates[0];
|
|
5115
4752
|
}
|
|
5116
4753
|
|
|
5117
4754
|
// src/sanitizer.ts
|
|
@@ -5157,12 +4794,12 @@ function isDangerousUrlAttr(name, value) {
|
|
|
5157
4794
|
const normalized = normalizeUrlAttr(value);
|
|
5158
4795
|
return normalized.startsWith("javascript:") || normalized.startsWith("vbscript:") || normalized.startsWith("data:");
|
|
5159
4796
|
}
|
|
5160
|
-
function sanitizeHtml(
|
|
4797
|
+
function sanitizeHtml(html32) {
|
|
5161
4798
|
const hasNative = typeof window.Sanitizer === "function";
|
|
5162
4799
|
if (hasNative) {
|
|
5163
4800
|
try {
|
|
5164
|
-
const
|
|
5165
|
-
const frag =
|
|
4801
|
+
const s = new window.Sanitizer({});
|
|
4802
|
+
const frag = s.sanitizeToFragment(html32);
|
|
5166
4803
|
const div = document.createElement("div");
|
|
5167
4804
|
div.append(frag);
|
|
5168
4805
|
return div.innerHTML;
|
|
@@ -5170,7 +4807,7 @@ function sanitizeHtml(html31) {
|
|
|
5170
4807
|
}
|
|
5171
4808
|
}
|
|
5172
4809
|
const tpl = document.createElement("template");
|
|
5173
|
-
tpl.innerHTML =
|
|
4810
|
+
tpl.innerHTML = html32;
|
|
5174
4811
|
const root = tpl.content;
|
|
5175
4812
|
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);
|
|
5176
4813
|
const toRemove = [];
|
|
@@ -5197,17 +4834,65 @@ function sanitizeHtml(html31) {
|
|
|
5197
4834
|
}
|
|
5198
4835
|
return tpl.innerHTML;
|
|
5199
4836
|
}
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
4837
|
+
var DESCRIPTION_TAGS = /* @__PURE__ */ new Set([
|
|
4838
|
+
"h3",
|
|
4839
|
+
"h4",
|
|
4840
|
+
"h5",
|
|
4841
|
+
"h6",
|
|
4842
|
+
"p",
|
|
4843
|
+
"br",
|
|
4844
|
+
"ul",
|
|
4845
|
+
"ol",
|
|
4846
|
+
"li",
|
|
4847
|
+
"strong",
|
|
4848
|
+
"b",
|
|
4849
|
+
"em",
|
|
4850
|
+
"i",
|
|
4851
|
+
"u",
|
|
4852
|
+
"small",
|
|
4853
|
+
"sup",
|
|
4854
|
+
"sub",
|
|
4855
|
+
"table",
|
|
4856
|
+
"thead",
|
|
4857
|
+
"tbody",
|
|
4858
|
+
"tr",
|
|
4859
|
+
"th",
|
|
4860
|
+
"td",
|
|
4861
|
+
"blockquote"
|
|
4862
|
+
]);
|
|
4863
|
+
var DEMOTED_HEADING = {
|
|
4864
|
+
h1: "h3",
|
|
4865
|
+
h2: "h3",
|
|
4866
|
+
h3: "h4",
|
|
4867
|
+
h4: "h5",
|
|
4868
|
+
h5: "h6",
|
|
4869
|
+
h6: "h6"
|
|
4870
|
+
};
|
|
4871
|
+
function sanitizeProductDescription(html32) {
|
|
4872
|
+
if (typeof html32 !== "string" || html32 === "") return "";
|
|
4873
|
+
const doc = new DOMParser().parseFromString(html32, "text/html");
|
|
4874
|
+
const out = document.createElement("div");
|
|
4875
|
+
const copy = (from, to) => {
|
|
4876
|
+
for (const child of Array.from(from.childNodes)) {
|
|
4877
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
4878
|
+
to.appendChild(document.createTextNode(child.textContent ?? ""));
|
|
4879
|
+
continue;
|
|
4880
|
+
}
|
|
4881
|
+
if (!(child instanceof Element)) continue;
|
|
4882
|
+
const tag = child.tagName.toLowerCase();
|
|
4883
|
+
if (tag === "script" || tag === "style" || tag === "template" || tag === "iframe") continue;
|
|
4884
|
+
const kept = DEMOTED_HEADING[tag] ?? tag;
|
|
4885
|
+
if (DESCRIPTION_TAGS.has(kept)) {
|
|
4886
|
+
const el = document.createElement(kept);
|
|
4887
|
+
copy(child, el);
|
|
4888
|
+
to.appendChild(el);
|
|
4889
|
+
} else {
|
|
4890
|
+
copy(child, to);
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
};
|
|
4894
|
+
copy(doc.body, out);
|
|
4895
|
+
return out.innerHTML;
|
|
5211
4896
|
}
|
|
5212
4897
|
|
|
5213
4898
|
// src/widgets/imageStandin.ts
|
|
@@ -5222,12 +4907,25 @@ function standinMonogram(name) {
|
|
|
5222
4907
|
>`;
|
|
5223
4908
|
}
|
|
5224
4909
|
|
|
4910
|
+
// src/widgets/presentationContext.ts
|
|
4911
|
+
function presentationFromContext(el) {
|
|
4912
|
+
let node = el;
|
|
4913
|
+
while (node) {
|
|
4914
|
+
if (node instanceof Element && node.getAttribute("data-product-presentation") === "double") {
|
|
4915
|
+
return "double";
|
|
4916
|
+
}
|
|
4917
|
+
const parent = node.parentNode;
|
|
4918
|
+
node = parent instanceof ShadowRoot ? parent.host : parent;
|
|
4919
|
+
}
|
|
4920
|
+
return void 0;
|
|
4921
|
+
}
|
|
4922
|
+
|
|
5225
4923
|
// src/widgets/renderHealth.ts
|
|
5226
4924
|
var BLANK_MIN_AREA = 60 * 60;
|
|
5227
4925
|
function elementPath(el, stopAt) {
|
|
5228
|
-
const seg = (
|
|
5229
|
-
const tag =
|
|
5230
|
-
const cls = typeof
|
|
4926
|
+
const seg = (e) => {
|
|
4927
|
+
const tag = e.tagName.toLowerCase();
|
|
4928
|
+
const cls = typeof e.className === "string" && e.className.trim() ? `.${e.className.trim().split(/\s+/)[0]}` : "";
|
|
5231
4929
|
return `${tag}${cls}`;
|
|
5232
4930
|
};
|
|
5233
4931
|
const parts = [seg(el)];
|
|
@@ -5276,7 +4974,7 @@ function measureRenderHealth(card) {
|
|
|
5276
4974
|
const r = el.getBoundingClientRect();
|
|
5277
4975
|
if (r.width === 0 || r.height === 0) continue;
|
|
5278
4976
|
const ownText = Array.from(el.childNodes).some(
|
|
5279
|
-
(
|
|
4977
|
+
(n) => n.nodeType === 3 && (n.textContent ?? "").trim().length > 0
|
|
5280
4978
|
);
|
|
5281
4979
|
if (ownText) hasText = true;
|
|
5282
4980
|
if (el.tagName === "IMG") {
|
|
@@ -5310,8 +5008,8 @@ function measureRenderHealth(card) {
|
|
|
5310
5008
|
Math.round(ar.top - r.top)
|
|
5311
5009
|
);
|
|
5312
5010
|
if (esc > 1 && !hasClipOkAncestor(el, card) && !hasClipOkAncestor(a, card)) {
|
|
5313
|
-
const
|
|
5314
|
-
if (
|
|
5011
|
+
const t = (el.textContent ?? "").trim();
|
|
5012
|
+
if (t || ["IMG", "BUTTON", "A"].includes(el.tagName)) {
|
|
5315
5013
|
push({ path: elementPath(el, card), over_px: esc });
|
|
5316
5014
|
}
|
|
5317
5015
|
}
|
|
@@ -5345,9 +5043,9 @@ function reportRenderHealth(root, opts) {
|
|
|
5345
5043
|
synEmit(`app:${opts.category}:blank`, base);
|
|
5346
5044
|
return health;
|
|
5347
5045
|
}
|
|
5348
|
-
const paths = health.clips.map((
|
|
5349
|
-
const over_px = health.clips.reduce((m,
|
|
5350
|
-
const identityTruncated = health.clips.some((
|
|
5046
|
+
const paths = health.clips.map((c) => c.path).sort();
|
|
5047
|
+
const over_px = health.clips.reduce((m, c) => Math.max(m, c.over_px), 0);
|
|
5048
|
+
const identityTruncated = health.clips.some((c) => c.reason === "identity-truncated");
|
|
5351
5049
|
synEmit(`app:${opts.category}:clipped`, {
|
|
5352
5050
|
paths: paths.join(","),
|
|
5353
5051
|
over_px,
|
|
@@ -5648,6 +5346,12 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5648
5346
|
__privateAdd(this, _VariantPanelLit_instances);
|
|
5649
5347
|
this.payload = null;
|
|
5650
5348
|
this.productTitle = "";
|
|
5349
|
+
/** Set by a double-wide card presentation: the panel sits BESIDE the front
|
|
5350
|
+
* face rather than flipped to. There is no Back — the front already shows
|
|
5351
|
+
* the identity, so the panel's own title element is skipped too (the
|
|
5352
|
+
* "Showing options for …" live-region status line still speaks it, so
|
|
5353
|
+
* screen reader users get the same announcement either way). */
|
|
5354
|
+
this.pinned = false;
|
|
5651
5355
|
/** Product-level hero image, passed from the card. Shown on the back face and
|
|
5652
5356
|
* used as the fallback when the selected variant carries no featured_image. */
|
|
5653
5357
|
this.productImage = null;
|
|
@@ -5671,8 +5375,8 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5671
5375
|
this._expandedAxes = /* @__PURE__ */ new Set();
|
|
5672
5376
|
/** @internal — Tier-C per-axis filter text. */
|
|
5673
5377
|
this._axisFilter = {};
|
|
5674
|
-
this._onKeydown = (
|
|
5675
|
-
if (
|
|
5378
|
+
this._onKeydown = (e) => {
|
|
5379
|
+
if (e.key === "Escape") {
|
|
5676
5380
|
this.dispatchEvent(new CustomEvent("variant-panel-back", { bubbles: true, composed: true }));
|
|
5677
5381
|
}
|
|
5678
5382
|
};
|
|
@@ -5794,7 +5498,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5794
5498
|
/>` : nothing3}
|
|
5795
5499
|
</div>
|
|
5796
5500
|
<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}
|
|
5501
|
+
${this.productTitle && !this.pinned ? html8`<div class="svp-product-title" data-product-title data-critical-text>${this.productTitle}</div>` : nothing3}
|
|
5798
5502
|
<div class="svp-attributes-scroll" data-attributes-scroll data-clip-ok>
|
|
5799
5503
|
${singleVariant ? html8`<div class="svp-single-variant-note" data-single-variant-note>
|
|
5800
5504
|
Just one option — ready to add.
|
|
@@ -5813,7 +5517,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5813
5517
|
// so Back fits beside it on the same row).
|
|
5814
5518
|
ctaPrice != null ? html8`Add ${compareAt ? html8`<span class="svp-compare">$${compareAt.toFixed(2)}</span>` : nothing3}· $${ctaPrice.toFixed(2)}` : html8`Add to cart`}
|
|
5815
5519
|
</button>
|
|
5816
|
-
${backBtn}
|
|
5520
|
+
${this.pinned ? nothing3 : backBtn}
|
|
5817
5521
|
</div>
|
|
5818
5522
|
<div class="svp-live" data-live data-clip-ok aria-live="polite">
|
|
5819
5523
|
${this.productTitle ? `Showing options for ${this.productTitle}` : nothing3}
|
|
@@ -5907,8 +5611,8 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5907
5611
|
)?.focus();
|
|
5908
5612
|
});
|
|
5909
5613
|
}
|
|
5910
|
-
_onAxisFilterInput(attrKey,
|
|
5911
|
-
this._axisFilter = { ...this._axisFilter, [attrKey]:
|
|
5614
|
+
_onAxisFilterInput(attrKey, e) {
|
|
5615
|
+
this._axisFilter = { ...this._axisFilter, [attrKey]: e.target.value };
|
|
5912
5616
|
}
|
|
5913
5617
|
/** Density Tier C — select-style trigger + bounded scrollable listbox.
|
|
5914
5618
|
* ONLY this axis collapses; siblings keep their own tier. Filter appears
|
|
@@ -5926,9 +5630,9 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5926
5630
|
class="svp-collapse"
|
|
5927
5631
|
data-collapsed-axis
|
|
5928
5632
|
data-attribute-key=${attr.key}
|
|
5929
|
-
@keydown=${(
|
|
5930
|
-
if (
|
|
5931
|
-
|
|
5633
|
+
@keydown=${(e) => {
|
|
5634
|
+
if (e.key === "Escape" && expanded) {
|
|
5635
|
+
e.stopPropagation();
|
|
5932
5636
|
this._toggleAxis(attr.key);
|
|
5933
5637
|
void this.updateComplete.then(() => {
|
|
5934
5638
|
this.querySelector(
|
|
@@ -5965,7 +5669,7 @@ var _VariantPanelLit = class _VariantPanelLit extends LitElement7 {
|
|
|
5965
5669
|
type="text"
|
|
5966
5670
|
placeholder=${`Filter ${attr.label.toLowerCase()}\u2026`}
|
|
5967
5671
|
.value=${this._axisFilter[attr.key] ?? ""}
|
|
5968
|
-
@input=${(
|
|
5672
|
+
@input=${(e) => this._onAxisFilterInput(attr.key, e)}
|
|
5969
5673
|
/>` : nothing3}
|
|
5970
5674
|
${filtered.map((v) => {
|
|
5971
5675
|
const disabled2 = this._isValueDisabled(attr.key, v.id);
|
|
@@ -6007,6 +5711,7 @@ _VariantPanelLit.properties = {
|
|
|
6007
5711
|
state: { type: String },
|
|
6008
5712
|
error: { type: String },
|
|
6009
5713
|
retryable: { type: Boolean },
|
|
5714
|
+
pinned: { type: Boolean },
|
|
6010
5715
|
_selection: { state: true },
|
|
6011
5716
|
_touched: { state: true },
|
|
6012
5717
|
_expandedAxes: { state: true },
|
|
@@ -6127,7 +5832,7 @@ var CARD_FLIP_CSS = `
|
|
|
6127
5832
|
.sc-product-card [data-card-flip] { animation: none; }
|
|
6128
5833
|
}
|
|
6129
5834
|
`;
|
|
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;
|
|
5835
|
+
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
5836
|
var ProductCardLit = class extends LitElement8 {
|
|
6132
5837
|
constructor() {
|
|
6133
5838
|
super(...arguments);
|
|
@@ -6151,6 +5856,12 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6151
5856
|
}));
|
|
6152
5857
|
__privateAdd(this, _controller);
|
|
6153
5858
|
__privateAdd(this, _unsubDevice);
|
|
5859
|
+
/** Set once at connect from the canvas DOM around the card
|
|
5860
|
+
* (`data-product-presentation="double"`) — never from `props`, so the
|
|
5861
|
+
* agent that writes card props has no path to setting it. Drives the
|
|
5862
|
+
* double-wide render branch: front face and options side by side, options
|
|
5863
|
+
* pinned open. */
|
|
5864
|
+
__privateAdd(this, _double, false);
|
|
6154
5865
|
/** Fingerprint of the last reported schema-validation failure (sorted
|
|
6155
5866
|
* invalid paths). Guards `render_failed` telemetry against re-render spam:
|
|
6156
5867
|
* the same bad props re-rendering must not re-emit. Reset on a successful
|
|
@@ -6164,25 +5875,25 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6164
5875
|
* the next scheduled pass or on disconnect. */
|
|
6165
5876
|
__privateAdd(this, _healthRaf);
|
|
6166
5877
|
/** animationend on the flip container → the turn is done, render flat. */
|
|
6167
|
-
__privateAdd(this, _onFlipAnimationEnd, (
|
|
6168
|
-
if (
|
|
5878
|
+
__privateAdd(this, _onFlipAnimationEnd, (e) => {
|
|
5879
|
+
if (e.animationName === "sc-card-flip-in") __privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "animation-settled" });
|
|
6169
5880
|
});
|
|
6170
5881
|
this._onVariantBack = () => {
|
|
6171
5882
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6172
5883
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "back-activated" });
|
|
6173
5884
|
};
|
|
6174
|
-
this._onVariantSelectionChange = (
|
|
5885
|
+
this._onVariantSelectionChange = (e) => {
|
|
6175
5886
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6176
|
-
const detail =
|
|
5887
|
+
const detail = e.detail;
|
|
6177
5888
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, {
|
|
6178
5889
|
type: "selection-changed",
|
|
6179
5890
|
attributeKey: detail.attribute_key,
|
|
6180
5891
|
valueId: detail.value_id
|
|
6181
5892
|
});
|
|
6182
5893
|
};
|
|
6183
|
-
this._onVariantAdd = (
|
|
5894
|
+
this._onVariantAdd = (e) => {
|
|
6184
5895
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6185
|
-
const detail =
|
|
5896
|
+
const detail = e.detail;
|
|
6186
5897
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "variant-add-activated", variantId: detail.variant.id });
|
|
6187
5898
|
};
|
|
6188
5899
|
this._onVariantRetry = () => {
|
|
@@ -6195,6 +5906,8 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6195
5906
|
}
|
|
6196
5907
|
connectedCallback() {
|
|
6197
5908
|
super.connectedCallback();
|
|
5909
|
+
__privateSet(this, _double, presentationFromContext(this) === "double");
|
|
5910
|
+
if (__privateGet(this, _double)) __privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "options-pinned", pinned: true });
|
|
6198
5911
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "reconnected" });
|
|
6199
5912
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, {
|
|
6200
5913
|
type: "commerce-updated",
|
|
@@ -6204,8 +5917,8 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6204
5917
|
this.style.color = "var(--sc-tile-text-color, rgba(20, 20, 20, 0.92))";
|
|
6205
5918
|
this._device = _readDevice();
|
|
6206
5919
|
const ctx = synWindow().SynOS?.runtime?.context;
|
|
6207
|
-
__privateSet(this, _unsubDevice, ctx?.subscribe?.((
|
|
6208
|
-
const d =
|
|
5920
|
+
__privateSet(this, _unsubDevice, ctx?.subscribe?.((c) => {
|
|
5921
|
+
const d = c?.surface?.device;
|
|
6209
5922
|
const next = d === "mobile" || d === "tablet" ? d : "desktop";
|
|
6210
5923
|
if (next !== this._device) this._device = next;
|
|
6211
5924
|
}));
|
|
@@ -6238,18 +5951,37 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6238
5951
|
const resolved = this.resolved[product.id];
|
|
6239
5952
|
const view = selectCardView(this._decisionState);
|
|
6240
5953
|
const articleStyles = _buildArticleStyles(density, view.face);
|
|
6241
|
-
const onPrimaryCtaClick = (
|
|
6242
|
-
|
|
5954
|
+
const onPrimaryCtaClick = (e) => {
|
|
5955
|
+
e.preventDefault();
|
|
6243
5956
|
__privateMethod(this, _ProductCardLit_instances, syncDecisionInputsBeforeIntent_fn).call(this);
|
|
6244
5957
|
const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
6245
5958
|
__privateMethod(this, _ProductCardLit_instances, dispatchCard_fn).call(this, { type: "primary-activated", reducedMotion });
|
|
6246
5959
|
};
|
|
6247
|
-
const ctasWithMorphedLabel = product.ctas.map((
|
|
6248
|
-
if (
|
|
6249
|
-
return { ...
|
|
5960
|
+
const ctasWithMorphedLabel = product.ctas.map((c, i) => {
|
|
5961
|
+
if (i === 0 && view.primaryCta.mode === "view-cart") {
|
|
5962
|
+
return { ...c, label: "View cart \u2192" };
|
|
6250
5963
|
}
|
|
6251
|
-
return
|
|
5964
|
+
return c;
|
|
6252
5965
|
});
|
|
5966
|
+
if (__privateGet(this, _double)) {
|
|
5967
|
+
return html9`<article
|
|
5968
|
+
class="sc-product-card"
|
|
5969
|
+
data-presentation="double"
|
|
5970
|
+
data-density=${density}
|
|
5971
|
+
style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));gap:16px;align-items:start;"
|
|
5972
|
+
>
|
|
5973
|
+
<div data-face="front">
|
|
5974
|
+
${renderProductCardInner(product, density, resolved, visibleFacts, [], void 0, true)}
|
|
5975
|
+
</div>
|
|
5976
|
+
<div data-face="back">
|
|
5977
|
+
${__privateMethod(this, _ProductCardLit_instances, renderVariantPanel_fn).call(this, view, product, {
|
|
5978
|
+
pinned: true,
|
|
5979
|
+
productImage: null,
|
|
5980
|
+
wireBack: false
|
|
5981
|
+
})}
|
|
5982
|
+
</div>
|
|
5983
|
+
</article>`;
|
|
5984
|
+
}
|
|
6253
5985
|
const frontContent = renderProductCardInner(
|
|
6254
5986
|
product,
|
|
6255
5987
|
density,
|
|
@@ -6259,19 +5991,11 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6259
5991
|
onPrimaryCtaClick,
|
|
6260
5992
|
!view.primaryCta.disabled
|
|
6261
5993
|
);
|
|
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}`;
|
|
5994
|
+
const backFace = __privateMethod(this, _ProductCardLit_instances, renderVariantPanel_fn).call(this, view, product, {
|
|
5995
|
+
pinned: false,
|
|
5996
|
+
productImage: product.image ? { src: product.image.src, alt: product.image.alt } : null,
|
|
5997
|
+
wireBack: true
|
|
5998
|
+
});
|
|
6275
5999
|
const backView = view.face === "back" && view.flipAnimating ? (
|
|
6276
6000
|
// Mid-flip: both faces coexist inside a preserve-3d container. The
|
|
6277
6001
|
// front turns away (inert + aria-hidden) while the back turns in.
|
|
@@ -6302,6 +6026,7 @@ var ProductCardLit = class extends LitElement8 {
|
|
|
6302
6026
|
_effectExecutor = new WeakMap();
|
|
6303
6027
|
_controller = new WeakMap();
|
|
6304
6028
|
_unsubDevice = new WeakMap();
|
|
6029
|
+
_double = new WeakMap();
|
|
6305
6030
|
_lastFailureFingerprint = new WeakMap();
|
|
6306
6031
|
_reportedHealthStates = new WeakMap();
|
|
6307
6032
|
_healthRaf = new WeakMap();
|
|
@@ -6366,6 +6091,7 @@ dispatchCard_fn = function(event) {
|
|
|
6366
6091
|
_onFlipAnimationEnd = new WeakMap();
|
|
6367
6092
|
startBind_fn = function() {
|
|
6368
6093
|
__privateGet(this, _controller)?.destroy();
|
|
6094
|
+
const previous = this.resolved;
|
|
6369
6095
|
this.resolved = {};
|
|
6370
6096
|
if (!this.props) {
|
|
6371
6097
|
this.validationError = false;
|
|
@@ -6392,15 +6118,41 @@ startBind_fn = function() {
|
|
|
6392
6118
|
__privateSet(this, _lastFailureFingerprint, null);
|
|
6393
6119
|
this.validationError = false;
|
|
6394
6120
|
const parsed = result.data;
|
|
6121
|
+
this.resolved = keepResolved(previous, [parsed.product]);
|
|
6395
6122
|
__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
|
-
};
|
|
6123
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
6401
6124
|
}));
|
|
6402
6125
|
__privateGet(this, _controller).start();
|
|
6403
6126
|
};
|
|
6127
|
+
/**
|
|
6128
|
+
* The `<syntro-variant-panel>` markup, shared by the flip card's back face
|
|
6129
|
+
* and the double-wide presentation's side panel — everything (payload,
|
|
6130
|
+
* title, state, error, retryable, the add/retry/selection-change handlers,
|
|
6131
|
+
* and the addError block) is identical between the two callers. Only three
|
|
6132
|
+
* things differ, so they're the only knobs: `pinned` (double-wide has no
|
|
6133
|
+
* Back and no title element — see VariantPanelLit), `productImage` (the
|
|
6134
|
+
* flip back face shows the product's hero image; the double-wide panel
|
|
6135
|
+
* shows none, since the front face right beside it already carries the
|
|
6136
|
+
* image), and `wireBack` (the double-wide panel has no Back control to
|
|
6137
|
+
* fire it and the card never flips there, so wiring the handler would be
|
|
6138
|
+
* live but pointless — omitted to match the panel's own no-Back contract).
|
|
6139
|
+
*/
|
|
6140
|
+
renderVariantPanel_fn = function(view, product, options) {
|
|
6141
|
+
return html9`<syntro-variant-panel
|
|
6142
|
+
?pinned=${options.pinned}
|
|
6143
|
+
.payload=${view.panel.payload}
|
|
6144
|
+
.productTitle=${product.name ?? ""}
|
|
6145
|
+
.productImage=${options.productImage}
|
|
6146
|
+
.state=${view.panel.status === "error" ? "error" : view.panel.status === "ready" ? "idle" : "loading"}
|
|
6147
|
+
.error=${view.panel.error}
|
|
6148
|
+
.retryable=${view.panel.retryable}
|
|
6149
|
+
@variant-panel-back=${options.wireBack ? this._onVariantBack : void 0}
|
|
6150
|
+
@variant-panel-add=${this._onVariantAdd}
|
|
6151
|
+
@variant-panel-retry=${this._onVariantRetry}
|
|
6152
|
+
@variant-panel-selection-change=${this._onVariantSelectionChange}
|
|
6153
|
+
></syntro-variant-panel>
|
|
6154
|
+
${view.addError ? html9`<div data-add-to-cart-error>${view.addError}</div>` : nothing4}`;
|
|
6155
|
+
};
|
|
6404
6156
|
readProductObservation_fn = function() {
|
|
6405
6157
|
const parsed = this.props ? cardSchema.safeParse(this.props) : null;
|
|
6406
6158
|
const rawProductId = this.props?.product?.id;
|
|
@@ -6527,7 +6279,9 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
6527
6279
|
const factOrder = Array.from(
|
|
6528
6280
|
/* @__PURE__ */ new Set([...CORE_FACTS, ...visibleFacts ?? VISIBLE_FACT_KEYS])
|
|
6529
6281
|
);
|
|
6530
|
-
const
|
|
6282
|
+
const storeAnswered = resolved !== void 0 && "compareAt" in resolved;
|
|
6283
|
+
const hasSalePrice = storeAnswered ? Boolean(resolved?.compareAt) : product.salePrice !== void 0;
|
|
6284
|
+
const originalPriceText = storeAnswered ? resolved?.compareAt : product.price?.amount;
|
|
6531
6285
|
const activePriceText = resolved?.price ?? product.salePrice?.amount ?? product.price?.amount;
|
|
6532
6286
|
const activeCadence = hasSalePrice ? product.salePrice?.cadence : product.price?.cadence;
|
|
6533
6287
|
const overlay = density === "compact";
|
|
@@ -6830,10 +6584,15 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
6830
6584
|
borderRadius: "9999px",
|
|
6831
6585
|
border: "0",
|
|
6832
6586
|
background: "transparent",
|
|
6833
|
-
|
|
6587
|
+
// Accent as ink on `transparent` is ink on the HOST's ground inside a
|
|
6588
|
+
// card, where vela's #723b32 measures 1.3:1. The host's own ink wins
|
|
6589
|
+
// there; the merchant's accent everywhere else.
|
|
6590
|
+
color: "var(--sc-host-link-color, var(--sc-color-primary, #3d8a5e))",
|
|
6834
6591
|
fontWeight: "600",
|
|
6835
6592
|
fontSize: "0.82rem",
|
|
6836
|
-
|
|
6593
|
+
// Once the link is the same colour as the text around it, colour alone no
|
|
6594
|
+
// longer says it is a link (WCAG 1.4.1), so the host card underlines it.
|
|
6595
|
+
textDecoration: "var(--sc-host-link-decoration, none)",
|
|
6837
6596
|
textAlign: "center",
|
|
6838
6597
|
lineHeight: "1.15",
|
|
6839
6598
|
// Same terminal policy as the primary pill: shrink + wrap, never overflow.
|
|
@@ -6843,41 +6602,41 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
6843
6602
|
maxWidth: "100%",
|
|
6844
6603
|
flexShrink: "1"
|
|
6845
6604
|
};
|
|
6846
|
-
const ctaRow = html9`<div class="sc-product-card__ctas" style=${styleMap(ctasStyles)}>
|
|
6847
|
-
${ctas.map((
|
|
6848
|
-
const disabled2 =
|
|
6605
|
+
const ctaRow = ctas.length === 0 ? nothing4 : html9`<div class="sc-product-card__ctas" style=${styleMap(ctasStyles)}>
|
|
6606
|
+
${ctas.map((c, i) => {
|
|
6607
|
+
const disabled2 = c.actionId === "add_to_cart" && !commerceAvailable;
|
|
6849
6608
|
const ctaStyles = {
|
|
6850
|
-
...
|
|
6609
|
+
...i === 0 ? primaryCtaStyles : secondaryCtaStyles,
|
|
6851
6610
|
...disabled2 ? { opacity: "0.5", "pointer-events": "none", cursor: "not-allowed" } : {}
|
|
6852
6611
|
};
|
|
6853
|
-
const ctaLabel =
|
|
6612
|
+
const ctaLabel = c.label;
|
|
6854
6613
|
return html9`<a
|
|
6855
6614
|
class="sc-product-card__cta"
|
|
6856
|
-
data-product-cta=${
|
|
6615
|
+
data-product-cta=${i === 0 ? "primary" : "secondary"}
|
|
6857
6616
|
data-critical-text
|
|
6858
|
-
data-variant=${
|
|
6617
|
+
data-variant=${c.variant}
|
|
6859
6618
|
data-disabled=${disabled2 ? "true" : nothing4}
|
|
6860
6619
|
aria-disabled=${disabled2 ? "true" : nothing4}
|
|
6861
|
-
href=${
|
|
6862
|
-
target=${
|
|
6863
|
-
rel=${
|
|
6864
|
-
data-action-id=${
|
|
6620
|
+
href=${c.actionId === "add_to_cart" ? "#" : c.href ?? "#"}
|
|
6621
|
+
target=${c.target}
|
|
6622
|
+
rel=${c.target === "_blank" ? "noopener noreferrer" : ""}
|
|
6623
|
+
data-action-id=${c.actionId ?? ""}
|
|
6865
6624
|
style=${styleMap(ctaStyles)}
|
|
6866
|
-
@click=${disabled2 ? (
|
|
6867
|
-
|
|
6625
|
+
@click=${disabled2 ? (e) => {
|
|
6626
|
+
e.preventDefault();
|
|
6868
6627
|
} : (
|
|
6869
6628
|
// The commerce (Add-to-cart / open-picker) intent wires ONLY to
|
|
6870
6629
|
// an actual add_to_cart CTA. A plain navigating CTA — e.g. a
|
|
6871
6630
|
// booking-fulfilment product's "Book a consult" href, which
|
|
6872
6631
|
// sits at i===0 too — must fall through to its own <a href>
|
|
6873
6632
|
// and never enter the Woo/Shopify variant-lookup flow.
|
|
6874
|
-
|
|
6875
|
-
|
|
6633
|
+
i === 0 && c.actionId === "add_to_cart" && onPrimaryCtaClick ? onPrimaryCtaClick : c.actionId === "add_to_cart" ? (e) => {
|
|
6634
|
+
e.preventDefault();
|
|
6876
6635
|
} : void 0
|
|
6877
6636
|
)}
|
|
6878
6637
|
>${// Secondary "Product Page" link gets a trailing chevron (panel's
|
|
6879
6638
|
// quiet-secondary affordance) unless the author already added one.
|
|
6880
|
-
|
|
6639
|
+
i !== 0 && !/[›>→]\s*$/.test(ctaLabel) ? `${ctaLabel} \u203A` : ctaLabel}</a
|
|
6881
6640
|
>`;
|
|
6882
6641
|
})}
|
|
6883
6642
|
</div>`;
|
|
@@ -7080,11 +6839,11 @@ function renderProductCardInner(product, density, resolved, visibleFacts, ctas,
|
|
|
7080
6839
|
data-product-price
|
|
7081
6840
|
style=${styleMap(priceBlockStyles)}
|
|
7082
6841
|
>
|
|
7083
|
-
${hasSalePrice &&
|
|
6842
|
+
${hasSalePrice && originalPriceText ? html9`<span
|
|
7084
6843
|
class="sc-product-card__amount-original"
|
|
7085
6844
|
data-product-price-original
|
|
7086
6845
|
style=${styleMap(priceOriginalStyles)}
|
|
7087
|
-
>${
|
|
6846
|
+
>${originalPriceText}</span
|
|
7088
6847
|
>` : nothing4}
|
|
7089
6848
|
<span
|
|
7090
6849
|
class="sc-product-card__amount"
|
|
@@ -7246,8 +7005,8 @@ function renderProductCard(product, density, resolved, visibleFacts) {
|
|
|
7246
7005
|
</article>
|
|
7247
7006
|
`;
|
|
7248
7007
|
}
|
|
7249
|
-
function onImageError(
|
|
7250
|
-
const img =
|
|
7008
|
+
function onImageError(e) {
|
|
7009
|
+
const img = e.currentTarget;
|
|
7251
7010
|
img.dataset.imageError = "true";
|
|
7252
7011
|
img.removeAttribute("src");
|
|
7253
7012
|
}
|
|
@@ -7422,6 +7181,7 @@ syncScrollCue_fn = function() {
|
|
|
7422
7181
|
};
|
|
7423
7182
|
startBind_fn2 = function() {
|
|
7424
7183
|
__privateGet(this, _controller2)?.destroy();
|
|
7184
|
+
const previous = this.resolved;
|
|
7425
7185
|
this.resolved = {};
|
|
7426
7186
|
if (!this.props) {
|
|
7427
7187
|
this.validationError = false;
|
|
@@ -7437,12 +7197,9 @@ startBind_fn2 = function() {
|
|
|
7437
7197
|
}
|
|
7438
7198
|
this.validationError = false;
|
|
7439
7199
|
__privateSet(this, _parsed, result.data);
|
|
7200
|
+
this.resolved = keepResolved(previous, result.data.products);
|
|
7440
7201
|
__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
|
-
};
|
|
7202
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
7446
7203
|
}));
|
|
7447
7204
|
__privateGet(this, _controller2).start();
|
|
7448
7205
|
};
|
|
@@ -7457,7 +7214,7 @@ startBind_fn2 = function() {
|
|
|
7457
7214
|
*/
|
|
7458
7215
|
renderDiveDeeper_fn = function(parsed) {
|
|
7459
7216
|
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;";
|
|
7217
|
+
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
7218
|
return html10`<button
|
|
7462
7219
|
type="button"
|
|
7463
7220
|
class="sc-product-comparison__dive-deeper"
|
|
@@ -7502,8 +7259,8 @@ rowComplete_fn = function(row, productCount) {
|
|
|
7502
7259
|
maxFields_fn = function() {
|
|
7503
7260
|
const raw = getComputedStyle(this).getPropertyValue("--sc-comparison-max-fields").trim();
|
|
7504
7261
|
if (!raw) return null;
|
|
7505
|
-
const
|
|
7506
|
-
return Number.isFinite(
|
|
7262
|
+
const n = Number.parseInt(raw, 10);
|
|
7263
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
7507
7264
|
};
|
|
7508
7265
|
/**
|
|
7509
7266
|
* Build comparison rows from the products' own `attributes` when the assistant
|
|
@@ -7520,10 +7277,10 @@ synthesizeRows_fn = function(products) {
|
|
|
7520
7277
|
}
|
|
7521
7278
|
}
|
|
7522
7279
|
return labels.map((label) => {
|
|
7523
|
-
const [a, b,
|
|
7280
|
+
const [a, b, c, d] = products.map(
|
|
7524
7281
|
(p) => (p.attributes ?? []).find((x) => x.label === label)?.value ?? "\u2014"
|
|
7525
7282
|
);
|
|
7526
|
-
return { label, valueA: a ?? "\u2014", valueB: b ?? "\u2014", valueC:
|
|
7283
|
+
return { label, valueA: a ?? "\u2014", valueB: b ?? "\u2014", valueC: c, valueD: d };
|
|
7527
7284
|
});
|
|
7528
7285
|
};
|
|
7529
7286
|
/**
|
|
@@ -7535,8 +7292,8 @@ renderMatrix_fn = function(products, rows) {
|
|
|
7535
7292
|
const cols = `minmax(62px, 0.55fr) repeat(${products.length}, minmax(0, 1fr))`;
|
|
7536
7293
|
const compactHeaders = products.length >= 3;
|
|
7537
7294
|
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-
|
|
7295
|
+
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;";
|
|
7296
|
+
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
7297
|
return html10`
|
|
7541
7298
|
<div
|
|
7542
7299
|
class="sc-product-comparison__matrix"
|
|
@@ -7638,8 +7395,8 @@ renderMatrix_fn = function(products, rows) {
|
|
|
7638
7395
|
>${row.label}</span
|
|
7639
7396
|
>
|
|
7640
7397
|
${products.map(
|
|
7641
|
-
(_p,
|
|
7642
|
-
>${cells[
|
|
7398
|
+
(_p, i) => html10`<span class="sc-product-comparison__matrix-cell" role="cell" style=${cellStyle}
|
|
7399
|
+
>${cells[i] ?? "\u2014"}</span
|
|
7643
7400
|
>`
|
|
7644
7401
|
)}
|
|
7645
7402
|
</div>
|
|
@@ -7655,10 +7412,167 @@ ProductComparisonLit.properties = {
|
|
|
7655
7412
|
scrollFade: { state: true }
|
|
7656
7413
|
};
|
|
7657
7414
|
|
|
7658
|
-
// src/widgets/
|
|
7415
|
+
// src/widgets/ProductDetailsLit.ts
|
|
7659
7416
|
import { html as html11, LitElement as LitElement10, nothing as nothing6 } from "lit";
|
|
7417
|
+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
7418
|
+
var EMPTY_MARKER = html11`<span data-sc-empty hidden></span>`;
|
|
7419
|
+
var DETAILS_CSS = `
|
|
7420
|
+
.pd {
|
|
7421
|
+
max-width: 1040px;
|
|
7422
|
+
margin: 0 auto 16px;
|
|
7423
|
+
display: grid;
|
|
7424
|
+
gap: 16px;
|
|
7425
|
+
}
|
|
7426
|
+
.pd-lead p {
|
|
7427
|
+
margin: 0 0 6px;
|
|
7428
|
+
}
|
|
7429
|
+
.pd-eyebrow {
|
|
7430
|
+
font-size: 0.75rem;
|
|
7431
|
+
letter-spacing: 0.06em;
|
|
7432
|
+
text-transform: uppercase;
|
|
7433
|
+
opacity: 0.75;
|
|
7434
|
+
}
|
|
7435
|
+
.pd-badge {
|
|
7436
|
+
margin-left: 6px;
|
|
7437
|
+
padding: 1px 8px;
|
|
7438
|
+
border-radius: 9999px;
|
|
7439
|
+
box-shadow: inset 0 0 0 1px currentColor;
|
|
7440
|
+
text-transform: none;
|
|
7441
|
+
letter-spacing: 0;
|
|
7442
|
+
}
|
|
7443
|
+
.pd-rating {
|
|
7444
|
+
font-size: 0.875rem;
|
|
7445
|
+
}
|
|
7446
|
+
.pd-desc {
|
|
7447
|
+
line-height: 1.5;
|
|
7448
|
+
max-width: 70ch;
|
|
7449
|
+
}
|
|
7450
|
+
.pd-benefits {
|
|
7451
|
+
margin: 4px 0 0;
|
|
7452
|
+
padding-left: 18px;
|
|
7453
|
+
}
|
|
7454
|
+
.pd-cols {
|
|
7455
|
+
display: grid;
|
|
7456
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
|
|
7457
|
+
gap: 16px;
|
|
7458
|
+
padding: 14px 0;
|
|
7459
|
+
border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
|
7460
|
+
border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
|
7461
|
+
font-size: 0.875rem;
|
|
7462
|
+
}
|
|
7463
|
+
.pd-cols h3 {
|
|
7464
|
+
margin: 0 0 6px;
|
|
7465
|
+
font-size: 0.8125rem;
|
|
7466
|
+
letter-spacing: 0.04em;
|
|
7467
|
+
text-transform: uppercase;
|
|
7468
|
+
opacity: 0.8;
|
|
7469
|
+
}
|
|
7470
|
+
.pd-cols p,
|
|
7471
|
+
.pd-cols ul {
|
|
7472
|
+
margin: 0 0 4px;
|
|
7473
|
+
}
|
|
7474
|
+
.pd-cols ul {
|
|
7475
|
+
padding-left: 18px;
|
|
7476
|
+
}
|
|
7477
|
+
`;
|
|
7478
|
+
var ProductDetailsLit = class extends LitElement10 {
|
|
7479
|
+
constructor() {
|
|
7480
|
+
super(...arguments);
|
|
7481
|
+
this._productId = "";
|
|
7482
|
+
this._details = null;
|
|
7483
|
+
}
|
|
7484
|
+
createRenderRoot() {
|
|
7485
|
+
return this;
|
|
7486
|
+
}
|
|
7487
|
+
connectedCallback() {
|
|
7488
|
+
super.connectedCallback();
|
|
7489
|
+
ensureStylesInRoot(this, "syntro-product-details-style", DETAILS_CSS);
|
|
7490
|
+
}
|
|
7491
|
+
/**
|
|
7492
|
+
* Widget props (the `adaptive-product:details` mountable sets `el.props`
|
|
7493
|
+
* before appending). Starts the details fetch immediately — reading
|
|
7494
|
+
* `productId` off the adapter, not off any attribute — and keys the
|
|
7495
|
+
* in-flight request by productId so a stale answer arriving after a later
|
|
7496
|
+
* `props` set (e.g. the card swapped to a different product) is dropped.
|
|
7497
|
+
*/
|
|
7498
|
+
set props(value) {
|
|
7499
|
+
const id = value?.productId ?? "";
|
|
7500
|
+
this._productId = id;
|
|
7501
|
+
this._details = null;
|
|
7502
|
+
if (!id) return;
|
|
7503
|
+
const adapter = getProductPlatformAdapter();
|
|
7504
|
+
if (typeof adapter?.fetchProductDetails !== "function") return;
|
|
7505
|
+
adapter.fetchProductDetails(id).then((details) => {
|
|
7506
|
+
if (this._productId !== id) return;
|
|
7507
|
+
this._details = details;
|
|
7508
|
+
}).catch(() => {
|
|
7509
|
+
});
|
|
7510
|
+
}
|
|
7511
|
+
get props() {
|
|
7512
|
+
return this._productId ? { productId: this._productId } : void 0;
|
|
7513
|
+
}
|
|
7514
|
+
render() {
|
|
7515
|
+
const details = this._details;
|
|
7516
|
+
if (!details) return EMPTY_MARKER;
|
|
7517
|
+
const d = details.details;
|
|
7518
|
+
const eyebrow = [d?.category, d?.gender].filter(Boolean).join(" \xB7 ");
|
|
7519
|
+
const badges = d?.badges ?? [];
|
|
7520
|
+
const benefits = d?.benefits ?? [];
|
|
7521
|
+
const moreBenefits = benefits.slice(3);
|
|
7522
|
+
const hasFabric = Boolean(d?.fabric || moreBenefits.length);
|
|
7523
|
+
const hasCols = Boolean(
|
|
7524
|
+
d && (hasFabric || (d.care?.length ?? 0) > 0 || d.shipping || d.returns)
|
|
7525
|
+
);
|
|
7526
|
+
const hasLead = Boolean(
|
|
7527
|
+
eyebrow || badges.length || details.rating || details.description || benefits.length
|
|
7528
|
+
);
|
|
7529
|
+
if (!hasLead && !hasCols) return EMPTY_MARKER;
|
|
7530
|
+
return html11`
|
|
7531
|
+
<section class="pd" data-product-details aria-label="Product details">
|
|
7532
|
+
<div class="pd-lead">
|
|
7533
|
+
${eyebrow || badges.length ? html11`<p class="pd-eyebrow">
|
|
7534
|
+
${eyebrow}${badges.map((b) => html11` <span class="pd-badge">${b}</span>`)}
|
|
7535
|
+
</p>` : nothing6}
|
|
7536
|
+
${details.rating ? html11`<p class="pd-rating">
|
|
7537
|
+
★ ${details.rating.value} · ${details.rating.count.toLocaleString("en-US")} reviews
|
|
7538
|
+
</p>` : nothing6}
|
|
7539
|
+
${details.description ? html11`<div class="pd-desc">${unsafeHTML(sanitizeProductDescription(details.description))}</div>` : nothing6}
|
|
7540
|
+
${benefits.length ? html11`<ul class="pd-benefits">
|
|
7541
|
+
${benefits.slice(0, 3).map((b) => html11`<li>${b}</li>`)}
|
|
7542
|
+
</ul>` : nothing6}
|
|
7543
|
+
</div>
|
|
7544
|
+
${hasCols ? html11`<div class="pd-cols">
|
|
7545
|
+
${hasFabric ? html11`<div>
|
|
7546
|
+
<h3>Fabric & benefits</h3>
|
|
7547
|
+
${d?.fabric ? html11`<p>${d.fabric}</p>` : nothing6}
|
|
7548
|
+
${moreBenefits.length ? html11`<ul>
|
|
7549
|
+
${moreBenefits.map((b) => html11`<li>${b}</li>`)}
|
|
7550
|
+
</ul>` : nothing6}
|
|
7551
|
+
</div>` : nothing6}
|
|
7552
|
+
<div>
|
|
7553
|
+
<h3>Shipping & returns</h3>
|
|
7554
|
+
${d?.shipping ? html11`<p>${d.shipping}</p>` : nothing6}
|
|
7555
|
+
${d?.returns ? html11`<p>${d.returns}</p>` : nothing6}
|
|
7556
|
+
</div>
|
|
7557
|
+
<div>
|
|
7558
|
+
<h3>Care</h3>
|
|
7559
|
+
${d?.care?.length ? html11`<ul>
|
|
7560
|
+
${d.care.map((c) => html11`<li>${c}</li>`)}
|
|
7561
|
+
</ul>` : nothing6}
|
|
7562
|
+
</div>
|
|
7563
|
+
</div>` : nothing6}
|
|
7564
|
+
</section>
|
|
7565
|
+
`;
|
|
7566
|
+
}
|
|
7567
|
+
};
|
|
7568
|
+
ProductDetailsLit.properties = {
|
|
7569
|
+
_details: { state: true }
|
|
7570
|
+
};
|
|
7571
|
+
|
|
7572
|
+
// src/widgets/ProductGridLit.ts
|
|
7573
|
+
import { html as html12, LitElement as LitElement11, nothing as nothing7 } from "lit";
|
|
7660
7574
|
var _controller3, _ProductGridLit_instances, startBind_fn3;
|
|
7661
|
-
var ProductGridLit = class extends
|
|
7575
|
+
var ProductGridLit = class extends LitElement11 {
|
|
7662
7576
|
constructor() {
|
|
7663
7577
|
super(...arguments);
|
|
7664
7578
|
__privateAdd(this, _ProductGridLit_instances);
|
|
@@ -7684,20 +7598,20 @@ var ProductGridLit = class extends LitElement10 {
|
|
|
7684
7598
|
__privateGet(this, _controller3)?.destroy();
|
|
7685
7599
|
}
|
|
7686
7600
|
render() {
|
|
7687
|
-
if (this.validationError || !this.props) return
|
|
7601
|
+
if (this.validationError || !this.props) return html12``;
|
|
7688
7602
|
const parsed = gridSchema.safeParse(this.props);
|
|
7689
|
-
if (!parsed.success) return
|
|
7603
|
+
if (!parsed.success) return html12``;
|
|
7690
7604
|
const { products, desktopColumns, heading } = parsed.data;
|
|
7691
|
-
return
|
|
7605
|
+
return html12`
|
|
7692
7606
|
<section class="sc-product-grid">
|
|
7693
|
-
${heading ?
|
|
7607
|
+
${heading ? html12`<h2 data-grid-heading class="sc-product-grid__heading">${heading}</h2>` : nothing7}
|
|
7694
7608
|
<div
|
|
7695
7609
|
data-grid-root
|
|
7696
7610
|
class="sc-product-grid__cells"
|
|
7697
7611
|
style=${`--sc-product-grid-cols:${desktopColumns}`}
|
|
7698
7612
|
>
|
|
7699
7613
|
${products.map(
|
|
7700
|
-
(p) =>
|
|
7614
|
+
(p) => html12`<div data-grid-cell class="sc-product-grid__cell">
|
|
7701
7615
|
${renderProductCard(p, "standard", this.resolved[p.id])}
|
|
7702
7616
|
</div>`
|
|
7703
7617
|
)}
|
|
@@ -7710,6 +7624,7 @@ _controller3 = new WeakMap();
|
|
|
7710
7624
|
_ProductGridLit_instances = new WeakSet();
|
|
7711
7625
|
startBind_fn3 = function() {
|
|
7712
7626
|
__privateGet(this, _controller3)?.destroy();
|
|
7627
|
+
const previous = this.resolved;
|
|
7713
7628
|
this.resolved = {};
|
|
7714
7629
|
if (!this.props) {
|
|
7715
7630
|
this.validationError = false;
|
|
@@ -7722,12 +7637,9 @@ startBind_fn3 = function() {
|
|
|
7722
7637
|
return;
|
|
7723
7638
|
}
|
|
7724
7639
|
this.validationError = false;
|
|
7640
|
+
this.resolved = keepResolved(previous, result.data.products);
|
|
7725
7641
|
__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
|
-
};
|
|
7642
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
7731
7643
|
}));
|
|
7732
7644
|
__privateGet(this, _controller3).start();
|
|
7733
7645
|
};
|
|
@@ -7738,9 +7650,9 @@ ProductGridLit.properties = {
|
|
|
7738
7650
|
};
|
|
7739
7651
|
|
|
7740
7652
|
// src/widgets/ProductHeroLit.ts
|
|
7741
|
-
import { html as
|
|
7653
|
+
import { html as html13, LitElement as LitElement12, nothing as nothing8 } from "lit";
|
|
7742
7654
|
var _controller4, _ProductHeroLit_instances, startBind_fn4;
|
|
7743
|
-
var ProductHeroLit = class extends
|
|
7655
|
+
var ProductHeroLit = class extends LitElement12 {
|
|
7744
7656
|
constructor() {
|
|
7745
7657
|
super(...arguments);
|
|
7746
7658
|
__privateAdd(this, _ProductHeroLit_instances);
|
|
@@ -7766,9 +7678,9 @@ var ProductHeroLit = class extends LitElement11 {
|
|
|
7766
7678
|
__privateGet(this, _controller4)?.destroy();
|
|
7767
7679
|
}
|
|
7768
7680
|
render() {
|
|
7769
|
-
if (this.validationError || !this.props) return
|
|
7681
|
+
if (this.validationError || !this.props) return html13``;
|
|
7770
7682
|
const parsed = heroSchema.safeParse(this.props);
|
|
7771
|
-
if (!parsed.success) return
|
|
7683
|
+
if (!parsed.success) return html13``;
|
|
7772
7684
|
const { product, layout, longCopy } = parsed.data;
|
|
7773
7685
|
const resolved = this.resolved[product.id];
|
|
7774
7686
|
const imgSrc = resolved?.image ?? product.image?.src;
|
|
@@ -7777,15 +7689,15 @@ var ProductHeroLit = class extends LitElement11 {
|
|
|
7777
7689
|
const sectionStyles = isTopLayout ? "display:block;" : `display:flex;flex-direction:${layout === "image-right" ? "row-reverse" : "row"};gap:1rem;align-items:flex-start;`;
|
|
7778
7690
|
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
7691
|
const contentStyles = isTopLayout ? "" : "flex:1;min-width:0;";
|
|
7780
|
-
return
|
|
7692
|
+
return html13`
|
|
7781
7693
|
<section class="sc-product-hero" data-hero-layout=${layout} style=${sectionStyles}>
|
|
7782
|
-
${imgSrc ?
|
|
7694
|
+
${imgSrc ? html13`<img
|
|
7783
7695
|
class="sc-product-hero__image"
|
|
7784
7696
|
src=${imgSrc}
|
|
7785
7697
|
alt=${product.image?.alt ?? product.name}
|
|
7786
7698
|
loading="lazy"
|
|
7787
7699
|
style=${imageStyles}
|
|
7788
|
-
/>` :
|
|
7700
|
+
/>` : html13`<div
|
|
7789
7701
|
class="sc-product-hero__image"
|
|
7790
7702
|
data-product-image-standin
|
|
7791
7703
|
aria-hidden="true"
|
|
@@ -7793,25 +7705,25 @@ var ProductHeroLit = class extends LitElement11 {
|
|
|
7793
7705
|
>${standinMonogram(product.name)}</div>`}
|
|
7794
7706
|
<div class="sc-product-hero__content" style=${contentStyles}>
|
|
7795
7707
|
<h2 class="sc-product-hero__name">${product.name}</h2>
|
|
7796
|
-
${product.tagline ?
|
|
7797
|
-
${product.price ?
|
|
7798
|
-
${priceText}${product.price.cadence ?
|
|
7799
|
-
</p>` :
|
|
7800
|
-
${longCopy ?
|
|
7708
|
+
${product.tagline ? html13`<p class="sc-product-hero__tagline">${product.tagline}</p>` : nothing8}
|
|
7709
|
+
${product.price ? html13`<p class="sc-product-hero__price">
|
|
7710
|
+
${priceText}${product.price.cadence ? html13` <small>${product.price.cadence}</small>` : nothing8}
|
|
7711
|
+
</p>` : nothing8}
|
|
7712
|
+
${longCopy ? html13`<div
|
|
7801
7713
|
data-hero-longcopy
|
|
7802
7714
|
class="sc-product-hero__longcopy"
|
|
7803
7715
|
.innerHTML=${sanitizeHtml(longCopy)}
|
|
7804
|
-
></div>` :
|
|
7716
|
+
></div>` : nothing8}
|
|
7805
7717
|
<div class="sc-product-hero__ctas">
|
|
7806
7718
|
${product.ctas.map(
|
|
7807
|
-
(
|
|
7719
|
+
(c, i) => html13`<a
|
|
7808
7720
|
class="sc-product-hero__cta"
|
|
7809
|
-
data-product-cta=${
|
|
7810
|
-
data-variant=${
|
|
7811
|
-
href=${
|
|
7812
|
-
target=${
|
|
7813
|
-
rel=${
|
|
7814
|
-
>${
|
|
7721
|
+
data-product-cta=${i === 0 ? "primary" : "secondary"}
|
|
7722
|
+
data-variant=${c.variant}
|
|
7723
|
+
href=${c.href ?? "#"}
|
|
7724
|
+
target=${c.target}
|
|
7725
|
+
rel=${c.target === "_blank" ? "noopener noreferrer" : ""}
|
|
7726
|
+
>${c.label}</a
|
|
7815
7727
|
>`
|
|
7816
7728
|
)}
|
|
7817
7729
|
</div>
|
|
@@ -7824,6 +7736,7 @@ _controller4 = new WeakMap();
|
|
|
7824
7736
|
_ProductHeroLit_instances = new WeakSet();
|
|
7825
7737
|
startBind_fn4 = function() {
|
|
7826
7738
|
__privateGet(this, _controller4)?.destroy();
|
|
7739
|
+
const previous = this.resolved;
|
|
7827
7740
|
this.resolved = {};
|
|
7828
7741
|
if (!this.props) {
|
|
7829
7742
|
this.validationError = false;
|
|
@@ -7836,12 +7749,9 @@ startBind_fn4 = function() {
|
|
|
7836
7749
|
return;
|
|
7837
7750
|
}
|
|
7838
7751
|
this.validationError = false;
|
|
7752
|
+
this.resolved = keepResolved(previous, [result.data.product]);
|
|
7839
7753
|
__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
|
-
};
|
|
7754
|
+
this.resolved = mergeResolved(this.resolved, productId, kind, value);
|
|
7845
7755
|
}));
|
|
7846
7756
|
__privateGet(this, _controller4).start();
|
|
7847
7757
|
};
|
|
@@ -7852,15 +7762,17 @@ ProductHeroLit.properties = {
|
|
|
7852
7762
|
};
|
|
7853
7763
|
|
|
7854
7764
|
// src/widgets/ProductRecoCarouselLit.ts
|
|
7855
|
-
import { html as
|
|
7765
|
+
import { html as html14, LitElement as LitElement13, nothing as nothing9 } from "lit";
|
|
7856
7766
|
import { styleMap as styleMap2 } from "lit/directives/style-map.js";
|
|
7857
|
-
var _ProductRecoCarouselLit_instances, healthProbe_fn3;
|
|
7858
|
-
var ProductRecoCarouselLit = class extends
|
|
7767
|
+
var _unwatch3, _ProductRecoCarouselLit_instances, healthProbe_fn3, watchPrices_fn, stopWatching_fn;
|
|
7768
|
+
var ProductRecoCarouselLit = class extends LitElement13 {
|
|
7859
7769
|
constructor() {
|
|
7860
7770
|
super();
|
|
7861
7771
|
__privateAdd(this, _ProductRecoCarouselLit_instances);
|
|
7772
|
+
__privateAdd(this, _unwatch3, []);
|
|
7862
7773
|
this.props = void 0;
|
|
7863
7774
|
this._parsed = void 0;
|
|
7775
|
+
this._live = {};
|
|
7864
7776
|
new RenderHealthController(this, () => __privateMethod(this, _ProductRecoCarouselLit_instances, healthProbe_fn3).call(this));
|
|
7865
7777
|
}
|
|
7866
7778
|
createRenderRoot() {
|
|
@@ -7870,24 +7782,34 @@ var ProductRecoCarouselLit = class extends LitElement12 {
|
|
|
7870
7782
|
if (changed.has("props")) {
|
|
7871
7783
|
const r = this.props ? recoSchema.safeParse(this.props) : void 0;
|
|
7872
7784
|
this._parsed = r?.success ? r.data : void 0;
|
|
7785
|
+
this._live = {};
|
|
7786
|
+
if (this.isConnected) __privateMethod(this, _ProductRecoCarouselLit_instances, watchPrices_fn).call(this);
|
|
7873
7787
|
}
|
|
7874
7788
|
}
|
|
7789
|
+
connectedCallback() {
|
|
7790
|
+
super.connectedCallback();
|
|
7791
|
+
__privateMethod(this, _ProductRecoCarouselLit_instances, watchPrices_fn).call(this);
|
|
7792
|
+
}
|
|
7793
|
+
disconnectedCallback() {
|
|
7794
|
+
super.disconnectedCallback();
|
|
7795
|
+
__privateMethod(this, _ProductRecoCarouselLit_instances, stopWatching_fn).call(this);
|
|
7796
|
+
}
|
|
7875
7797
|
render() {
|
|
7876
7798
|
const items = this._parsed?.items ?? [];
|
|
7877
|
-
if (items.length === 0) return
|
|
7878
|
-
return
|
|
7799
|
+
if (items.length === 0) return html14``;
|
|
7800
|
+
return html14`
|
|
7879
7801
|
<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
7802
|
<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
7803
|
${items.map(
|
|
7882
|
-
(it) =>
|
|
7804
|
+
(it) => html14`
|
|
7883
7805
|
<div role="listitem" style="display:contents;">
|
|
7884
7806
|
<a data-reco-item data-product-cta="primary" href=${it.pdp_href}
|
|
7885
7807
|
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 ?
|
|
7808
|
+
${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;">` : nothing9}
|
|
7887
7809
|
<span style="position:absolute;inset:auto 0 0 0;height:60%;background:linear-gradient(transparent,rgba(0,0,0,.62));"></span>
|
|
7888
7810
|
<span style="position:absolute;left:8px;right:8px;bottom:8px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.5);">
|
|
7889
|
-
${it.price ?
|
|
7890
|
-
${it.hook ?
|
|
7811
|
+
${it.product_id && this._live[it.product_id] || it.price ? html14`<span style="display:block;font-weight:700;font-size:0.9rem;">${it.product_id && this._live[it.product_id] || it.price}</span>` : nothing9}
|
|
7812
|
+
${it.hook ? html14`<span style="display:block;font-size:0.72rem;line-height:1.2;opacity:.95;">${it.hook}</span>` : nothing9}
|
|
7891
7813
|
</span>
|
|
7892
7814
|
</a>
|
|
7893
7815
|
</div>`
|
|
@@ -7896,6 +7818,7 @@ var ProductRecoCarouselLit = class extends LitElement12 {
|
|
|
7896
7818
|
</section>`;
|
|
7897
7819
|
}
|
|
7898
7820
|
};
|
|
7821
|
+
_unwatch3 = new WeakMap();
|
|
7899
7822
|
_ProductRecoCarouselLit_instances = new WeakSet();
|
|
7900
7823
|
/** Measure only the SETTLED carousel (items rendered). An empty carousel
|
|
7901
7824
|
* renders nothing and is never "blank". NON-PII stateKey. */
|
|
@@ -7911,7 +7834,30 @@ healthProbe_fn3 = function() {
|
|
|
7911
7834
|
widgetKind: "adaptive-product:reco"
|
|
7912
7835
|
};
|
|
7913
7836
|
};
|
|
7914
|
-
|
|
7837
|
+
/** The store's live price for each item, now and whenever the runtime
|
|
7838
|
+
* publishes a commerce adapter; an item the store cannot price keeps the
|
|
7839
|
+
* catalog price the server sent. */
|
|
7840
|
+
watchPrices_fn = function() {
|
|
7841
|
+
__privateMethod(this, _ProductRecoCarouselLit_instances, stopWatching_fn).call(this);
|
|
7842
|
+
for (const item of this._parsed?.items ?? []) {
|
|
7843
|
+
const id = item.product_id;
|
|
7844
|
+
if (!id || !item.price) continue;
|
|
7845
|
+
__privateGet(this, _unwatch3).push(
|
|
7846
|
+
watchLivePrice(id, (live) => {
|
|
7847
|
+
this._live = { ...this._live, [id]: live.price };
|
|
7848
|
+
})
|
|
7849
|
+
);
|
|
7850
|
+
}
|
|
7851
|
+
};
|
|
7852
|
+
stopWatching_fn = function() {
|
|
7853
|
+
for (const stop of __privateGet(this, _unwatch3)) stop();
|
|
7854
|
+
__privateSet(this, _unwatch3, []);
|
|
7855
|
+
};
|
|
7856
|
+
ProductRecoCarouselLit.properties = {
|
|
7857
|
+
props: { attribute: false },
|
|
7858
|
+
_parsed: { state: true },
|
|
7859
|
+
_live: { state: true }
|
|
7860
|
+
};
|
|
7915
7861
|
ProductRecoCarouselLit._testing = {
|
|
7916
7862
|
healthProbe: (host) => {
|
|
7917
7863
|
var _a;
|
|
@@ -7922,7 +7868,7 @@ if (!customElements.get("syntro-product-reco-carousel"))
|
|
|
7922
7868
|
customElements.define("syntro-product-reco-carousel", ProductRecoCarouselLit);
|
|
7923
7869
|
|
|
7924
7870
|
// src/widgets/PdpTrendingNewsLit.ts
|
|
7925
|
-
import { html as
|
|
7871
|
+
import { html as html15, LitElement as LitElement14 } from "lit";
|
|
7926
7872
|
|
|
7927
7873
|
// src/widgets/entrance.ts
|
|
7928
7874
|
function armEntranceSettle(onSettle, container, settleMs, fastPath = false) {
|
|
@@ -7950,14 +7896,6 @@ function armEntranceSettle(onSettle, container, settleMs, fastPath = false) {
|
|
|
7950
7896
|
}
|
|
7951
7897
|
|
|
7952
7898
|
// 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
7899
|
var TRENDING_NEWS_CSS = `
|
|
7962
7900
|
.pdp-trending-news {
|
|
7963
7901
|
--tn-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8163,11 +8101,12 @@ var TRENDING_NEWS_CSS = `
|
|
|
8163
8101
|
outline: none;
|
|
8164
8102
|
}
|
|
8165
8103
|
.pdp-tn-headline-link::after {
|
|
8166
|
-
|
|
8104
|
+
/* Decoration: empty alternative text keeps it out of the link's name. */
|
|
8105
|
+
content: ' \\2197' / '';
|
|
8167
8106
|
font-family: var(--tn-sans);
|
|
8168
8107
|
font-size: 0.65em;
|
|
8169
8108
|
letter-spacing: 0;
|
|
8170
|
-
color: var(--tn-primary);
|
|
8109
|
+
color: var(--syntro-pdp-primary-ink, var(--tn-primary));
|
|
8171
8110
|
opacity: 0.55;
|
|
8172
8111
|
vertical-align: 0.18em;
|
|
8173
8112
|
margin-left: 0.2em;
|
|
@@ -8186,7 +8125,7 @@ var TRENDING_NEWS_CSS = `
|
|
|
8186
8125
|
font-style: italic;
|
|
8187
8126
|
font-size: 11px;
|
|
8188
8127
|
letter-spacing: 0.06em;
|
|
8189
|
-
color: color-mix(in srgb, var(--tn-primary) 70%, transparent);
|
|
8128
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--tn-primary) 70%, transparent));
|
|
8190
8129
|
padding: 3px 9px 3px 8px;
|
|
8191
8130
|
border-radius: 999px;
|
|
8192
8131
|
background: color-mix(in srgb, var(--tn-primary) 12%, transparent);
|
|
@@ -8269,7 +8208,7 @@ var TRENDING_NEWS_CSS = `
|
|
|
8269
8208
|
}
|
|
8270
8209
|
}
|
|
8271
8210
|
`;
|
|
8272
|
-
var PdpTrendingNewsLit = class extends
|
|
8211
|
+
var PdpTrendingNewsLit = class extends LitElement14 {
|
|
8273
8212
|
constructor() {
|
|
8274
8213
|
super(...arguments);
|
|
8275
8214
|
this.data = void 0;
|
|
@@ -8282,7 +8221,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8282
8221
|
}
|
|
8283
8222
|
connectedCallback() {
|
|
8284
8223
|
super.connectedCallback();
|
|
8285
|
-
|
|
8224
|
+
ensureStylesInRoot(this, "syntro-pdp-trending-news-style", TRENDING_NEWS_CSS);
|
|
8286
8225
|
if (this._entering) this.requestUpdate();
|
|
8287
8226
|
}
|
|
8288
8227
|
disconnectedCallback() {
|
|
@@ -8305,7 +8244,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8305
8244
|
}
|
|
8306
8245
|
render() {
|
|
8307
8246
|
if (!this.data) {
|
|
8308
|
-
return
|
|
8247
|
+
return html15`<div data-skeleton class="pdp-trending-news-skeleton">
|
|
8309
8248
|
<div class="pdp-skeleton-bar" style="width:60%"></div>
|
|
8310
8249
|
<div class="pdp-skeleton-bar" style="width:90%"></div>
|
|
8311
8250
|
<div class="pdp-skeleton-bar" style="width:80%"></div>
|
|
@@ -8315,7 +8254,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8315
8254
|
const items = this.data.items;
|
|
8316
8255
|
const featured = items[0];
|
|
8317
8256
|
const rest = items.slice(1);
|
|
8318
|
-
return
|
|
8257
|
+
return html15`
|
|
8319
8258
|
<section
|
|
8320
8259
|
class="${this._entering ? "pdp-trending-news is-entering" : "pdp-trending-news"}"
|
|
8321
8260
|
aria-label="Trending news"
|
|
@@ -8332,7 +8271,7 @@ var PdpTrendingNewsLit = class extends LitElement13 {
|
|
|
8332
8271
|
<p class="pdp-tn-blurb">${featured.blurb}</p>
|
|
8333
8272
|
</article>
|
|
8334
8273
|
${rest.map(
|
|
8335
|
-
(item) =>
|
|
8274
|
+
(item) => html15`
|
|
8336
8275
|
<article class="pdp-tn-card">
|
|
8337
8276
|
<div class="pdp-tn-meta">
|
|
8338
8277
|
<span class="pdp-tn-dot" aria-hidden="true"></span>
|
|
@@ -8354,8 +8293,8 @@ PdpTrendingNewsLit.properties = {
|
|
|
8354
8293
|
_entering: { state: true }
|
|
8355
8294
|
};
|
|
8356
8295
|
function renderHeadlineLink(item) {
|
|
8357
|
-
if (!item.url) return
|
|
8358
|
-
return
|
|
8296
|
+
if (!item.url) return html15`${item.headline}`;
|
|
8297
|
+
return html15`<a
|
|
8359
8298
|
class="pdp-tn-headline-link"
|
|
8360
8299
|
href=${item.url}
|
|
8361
8300
|
target="_blank"
|
|
@@ -8368,15 +8307,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
8368
8307
|
}
|
|
8369
8308
|
|
|
8370
8309
|
// 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
|
-
}
|
|
8310
|
+
import { html as html16, LitElement as LitElement15 } from "lit";
|
|
8380
8311
|
var MAGAZINE_CSS = `
|
|
8381
8312
|
.pdp-tn-mag {
|
|
8382
8313
|
--tn-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8548,7 +8479,7 @@ var MAGAZINE_CSS = `
|
|
|
8548
8479
|
}
|
|
8549
8480
|
}
|
|
8550
8481
|
`;
|
|
8551
|
-
var PdpTrendingNewsMagazineLit = class extends
|
|
8482
|
+
var PdpTrendingNewsMagazineLit = class extends LitElement15 {
|
|
8552
8483
|
constructor() {
|
|
8553
8484
|
super(...arguments);
|
|
8554
8485
|
this.data = void 0;
|
|
@@ -8558,17 +8489,17 @@ var PdpTrendingNewsMagazineLit = class extends LitElement14 {
|
|
|
8558
8489
|
}
|
|
8559
8490
|
connectedCallback() {
|
|
8560
8491
|
super.connectedCallback();
|
|
8561
|
-
|
|
8492
|
+
ensureStylesInRoot(this, "syntro-pdp-trending-news-magazine-style", MAGAZINE_CSS);
|
|
8562
8493
|
}
|
|
8563
8494
|
render() {
|
|
8564
8495
|
if (!this.data) {
|
|
8565
|
-
return
|
|
8496
|
+
return html16`<div aria-busy="true" class="pdp-tn-mag" style="opacity:0.5;">…</div>`;
|
|
8566
8497
|
}
|
|
8567
8498
|
const items = this.data.items;
|
|
8568
8499
|
const [lead, ...rest] = items;
|
|
8569
|
-
return
|
|
8500
|
+
return html16`
|
|
8570
8501
|
<section class="pdp-tn-mag" aria-label="Trending news">
|
|
8571
|
-
${lead ?
|
|
8502
|
+
${lead ? html16`<article class="pdp-tn-mag__lead">
|
|
8572
8503
|
<div class="pdp-tn-mag__meta">
|
|
8573
8504
|
<span class="pdp-tn-mag__meta-source">${lead.source}</span>
|
|
8574
8505
|
· ${lead.published_relative}
|
|
@@ -8577,7 +8508,7 @@ var PdpTrendingNewsMagazineLit = class extends LitElement14 {
|
|
|
8577
8508
|
<p class="pdp-tn-mag__blurb">${lead.blurb}</p>
|
|
8578
8509
|
</article>` : ""}
|
|
8579
8510
|
${rest.map(
|
|
8580
|
-
(it) =>
|
|
8511
|
+
(it) => html16`<article class="pdp-tn-mag__item">
|
|
8581
8512
|
<div class="pdp-tn-mag__meta">
|
|
8582
8513
|
<span class="pdp-tn-mag__meta-source">${it.source}</span>
|
|
8583
8514
|
· ${it.published_relative}
|
|
@@ -8594,8 +8525,8 @@ PdpTrendingNewsMagazineLit.properties = {
|
|
|
8594
8525
|
data: { attribute: false }
|
|
8595
8526
|
};
|
|
8596
8527
|
function renderHeadlineLink2(item) {
|
|
8597
|
-
if (!item.url) return
|
|
8598
|
-
return
|
|
8528
|
+
if (!item.url) return html16`${item.headline}`;
|
|
8529
|
+
return html16`<a
|
|
8599
8530
|
class="pdp-tn-mag__headline-link"
|
|
8600
8531
|
href=${item.url}
|
|
8601
8532
|
target="_blank"
|
|
@@ -8608,15 +8539,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
8608
8539
|
}
|
|
8609
8540
|
|
|
8610
8541
|
// 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
|
-
}
|
|
8542
|
+
import { html as html17, LitElement as LitElement16 } from "lit";
|
|
8620
8543
|
var HERO_CSS = `
|
|
8621
8544
|
.pdp-tn-hero {
|
|
8622
8545
|
--tn-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8799,7 +8722,7 @@ var HERO_CSS = `
|
|
|
8799
8722
|
}
|
|
8800
8723
|
}
|
|
8801
8724
|
`;
|
|
8802
|
-
var PdpTrendingNewsHeroLit = class extends
|
|
8725
|
+
var PdpTrendingNewsHeroLit = class extends LitElement16 {
|
|
8803
8726
|
constructor() {
|
|
8804
8727
|
super(...arguments);
|
|
8805
8728
|
this.data = void 0;
|
|
@@ -8809,15 +8732,15 @@ var PdpTrendingNewsHeroLit = class extends LitElement15 {
|
|
|
8809
8732
|
}
|
|
8810
8733
|
connectedCallback() {
|
|
8811
8734
|
super.connectedCallback();
|
|
8812
|
-
|
|
8735
|
+
ensureStylesInRoot(this, "syntro-pdp-trending-news-hero-style", HERO_CSS);
|
|
8813
8736
|
}
|
|
8814
8737
|
render() {
|
|
8815
8738
|
if (!this.data) {
|
|
8816
|
-
return
|
|
8739
|
+
return html17`<div aria-busy="true" class="pdp-tn-hero" style="opacity:0.5;">…</div>`;
|
|
8817
8740
|
}
|
|
8818
8741
|
const [lead, ...rest] = this.data.items;
|
|
8819
|
-
if (!lead) return
|
|
8820
|
-
return
|
|
8742
|
+
if (!lead) return html17``;
|
|
8743
|
+
return html17`
|
|
8821
8744
|
<section class="pdp-tn-hero" aria-label="Trending news">
|
|
8822
8745
|
<article class="pdp-tn-hero__panel">
|
|
8823
8746
|
<blockquote class="pdp-tn-hero__quote">${lead.blurb}</blockquote>
|
|
@@ -8828,9 +8751,9 @@ var PdpTrendingNewsHeroLit = class extends LitElement15 {
|
|
|
8828
8751
|
</footer>
|
|
8829
8752
|
<p class="pdp-tn-hero__headline">${renderHeadlineLink3(lead)}</p>
|
|
8830
8753
|
</article>
|
|
8831
|
-
${rest.length ?
|
|
8754
|
+
${rest.length ? html17`<div class="pdp-tn-hero__followon">
|
|
8832
8755
|
${rest.map(
|
|
8833
|
-
(it) =>
|
|
8756
|
+
(it) => html17`<div class="pdp-tn-hero__row">
|
|
8834
8757
|
<p class="pdp-tn-hero__row-headline">${renderHeadlineLink3(it)}</p>
|
|
8835
8758
|
<span class="pdp-tn-hero__row-meta">${it.source}</span>
|
|
8836
8759
|
</div>`
|
|
@@ -8844,8 +8767,8 @@ PdpTrendingNewsHeroLit.properties = {
|
|
|
8844
8767
|
data: { attribute: false }
|
|
8845
8768
|
};
|
|
8846
8769
|
function renderHeadlineLink3(item) {
|
|
8847
|
-
if (!item.url) return
|
|
8848
|
-
return
|
|
8770
|
+
if (!item.url) return html17`${item.headline}`;
|
|
8771
|
+
return html17`<a
|
|
8849
8772
|
class="pdp-tn-hero__headline-link"
|
|
8850
8773
|
href=${item.url}
|
|
8851
8774
|
target="_blank"
|
|
@@ -8858,15 +8781,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-tre
|
|
|
8858
8781
|
}
|
|
8859
8782
|
|
|
8860
8783
|
// 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
|
-
}
|
|
8784
|
+
import { html as html18, LitElement as LitElement17 } from "lit";
|
|
8870
8785
|
var MASONRY_CSS = `
|
|
8871
8786
|
.pdp-pf-mas {
|
|
8872
8787
|
--pf-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -8997,12 +8912,12 @@ var MASONRY_CSS = `
|
|
|
8997
8912
|
padding-top: 10px;
|
|
8998
8913
|
}
|
|
8999
8914
|
`;
|
|
9000
|
-
function personaHue(
|
|
8915
|
+
function personaHue(s) {
|
|
9001
8916
|
let h = 0;
|
|
9002
|
-
for (let
|
|
8917
|
+
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
|
|
9003
8918
|
return Math.abs(h) % 360;
|
|
9004
8919
|
}
|
|
9005
|
-
var PdpPeerFeedMasonryLit = class extends
|
|
8920
|
+
var PdpPeerFeedMasonryLit = class extends LitElement17 {
|
|
9006
8921
|
constructor() {
|
|
9007
8922
|
super(...arguments);
|
|
9008
8923
|
this.data = void 0;
|
|
@@ -9012,17 +8927,17 @@ var PdpPeerFeedMasonryLit = class extends LitElement16 {
|
|
|
9012
8927
|
}
|
|
9013
8928
|
connectedCallback() {
|
|
9014
8929
|
super.connectedCallback();
|
|
9015
|
-
|
|
8930
|
+
ensureStylesInRoot(this, "syntro-pdp-peer-feed-masonry-style", MASONRY_CSS);
|
|
9016
8931
|
}
|
|
9017
8932
|
render() {
|
|
9018
8933
|
if (!this.data) {
|
|
9019
|
-
return
|
|
8934
|
+
return html18`<div aria-busy="true" class="pdp-pf-mas" style="opacity:0.5;">…</div>`;
|
|
9020
8935
|
}
|
|
9021
|
-
return
|
|
8936
|
+
return html18`
|
|
9022
8937
|
<section class="pdp-pf-mas" aria-label="Peer voices">
|
|
9023
8938
|
<div class="pdp-pf-mas__grid">
|
|
9024
8939
|
${this.data.peers.map(
|
|
9025
|
-
(p) =>
|
|
8940
|
+
(p) => html18`<article
|
|
9026
8941
|
class="pdp-pf-mas__card"
|
|
9027
8942
|
style=${`--pf-avatar-hue: ${personaHue(p.persona)};`}
|
|
9028
8943
|
>
|
|
@@ -9050,15 +8965,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-pee
|
|
|
9050
8965
|
}
|
|
9051
8966
|
|
|
9052
8967
|
// 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
|
-
}
|
|
8968
|
+
import { html as html19, LitElement as LitElement18, svg } from "lit";
|
|
9062
8969
|
var CAROUSEL_CSS2 = `
|
|
9063
8970
|
/* The custom-element tag itself + its slot-mount parent must declare
|
|
9064
8971
|
display:block AND min-width:0 so they participate in the grid cell's
|
|
@@ -9227,12 +9134,12 @@ syntro-pdp-peer-feed-carousel {
|
|
|
9227
9134
|
var GLYPHS = ["waveform", "gauge", "leaf", "spark"];
|
|
9228
9135
|
function glyphFor(persona) {
|
|
9229
9136
|
let h = 0;
|
|
9230
|
-
for (let
|
|
9137
|
+
for (let i = 0; i < persona.length; i++) h = h * 31 + persona.charCodeAt(i) | 0;
|
|
9231
9138
|
return GLYPHS[Math.abs(h) % GLYPHS.length];
|
|
9232
9139
|
}
|
|
9233
9140
|
function glyphHue(persona) {
|
|
9234
9141
|
let h = 0;
|
|
9235
|
-
for (let
|
|
9142
|
+
for (let i = 0; i < persona.length; i++) h = h * 17 + persona.charCodeAt(i) | 0;
|
|
9236
9143
|
return Math.abs(h) % 360;
|
|
9237
9144
|
}
|
|
9238
9145
|
function renderGlyph(kind) {
|
|
@@ -9266,7 +9173,7 @@ function renderGlyph(kind) {
|
|
|
9266
9173
|
<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
9174
|
</svg>`;
|
|
9268
9175
|
}
|
|
9269
|
-
var PdpPeerFeedCarouselLit = class extends
|
|
9176
|
+
var PdpPeerFeedCarouselLit = class extends LitElement18 {
|
|
9270
9177
|
constructor() {
|
|
9271
9178
|
super(...arguments);
|
|
9272
9179
|
this.data = void 0;
|
|
@@ -9276,19 +9183,19 @@ var PdpPeerFeedCarouselLit = class extends LitElement17 {
|
|
|
9276
9183
|
}
|
|
9277
9184
|
connectedCallback() {
|
|
9278
9185
|
super.connectedCallback();
|
|
9279
|
-
|
|
9186
|
+
ensureStylesInRoot(this, "syntro-pdp-peer-feed-carousel-style", CAROUSEL_CSS2);
|
|
9280
9187
|
}
|
|
9281
9188
|
render() {
|
|
9282
9189
|
if (!this.data) {
|
|
9283
|
-
return
|
|
9190
|
+
return html19`<div aria-busy="true" class="pdp-pf-car" style="opacity:0.5;">…</div>`;
|
|
9284
9191
|
}
|
|
9285
|
-
return
|
|
9192
|
+
return html19`
|
|
9286
9193
|
<section class="pdp-pf-car" aria-label="Peer voices">
|
|
9287
9194
|
<div class="pdp-pf-car__track">
|
|
9288
9195
|
${this.data.peers.map((p) => {
|
|
9289
9196
|
const kind = glyphFor(p.persona);
|
|
9290
9197
|
const hue = glyphHue(p.persona);
|
|
9291
|
-
return
|
|
9198
|
+
return html19`<article class="pdp-pf-car__card" style=${`--pf-glyph-hue: ${hue};`}>
|
|
9292
9199
|
<span class="pdp-pf-car__glyph" aria-hidden="true">${renderGlyph(kind)}</span>
|
|
9293
9200
|
<span class="pdp-pf-car__persona">${p.persona}</span>
|
|
9294
9201
|
<blockquote class="pdp-pf-car__quote">${p.quote}</blockquote>
|
|
@@ -9313,7 +9220,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-pee
|
|
|
9313
9220
|
}
|
|
9314
9221
|
|
|
9315
9222
|
// src/widgets/PdpChartLit.ts
|
|
9316
|
-
import { html as
|
|
9223
|
+
import { html as html20, LitElement as LitElement19, nothing as nothing10, svg as svg2 } from "lit";
|
|
9317
9224
|
function formatNum(v) {
|
|
9318
9225
|
if (v === void 0 || !Number.isFinite(v)) return "\u2014";
|
|
9319
9226
|
if (Number.isInteger(v)) return v.toLocaleString("en-US");
|
|
@@ -9323,7 +9230,7 @@ function formatNum(v) {
|
|
|
9323
9230
|
return decPart ? `${formatted}.${decPart}` : formatted;
|
|
9324
9231
|
}
|
|
9325
9232
|
function buildComparisonMatrix(data) {
|
|
9326
|
-
const colLabels = data.series.map((
|
|
9233
|
+
const colLabels = data.series.map((s) => s.name);
|
|
9327
9234
|
const seenRows = /* @__PURE__ */ new Map();
|
|
9328
9235
|
const rowLabels = [];
|
|
9329
9236
|
for (const series of data.series) {
|
|
@@ -9391,7 +9298,7 @@ function niceTicks(yMax) {
|
|
|
9391
9298
|
const rawStep = yMax / targetCount;
|
|
9392
9299
|
const mag = 10 ** Math.floor(Math.log10(rawStep));
|
|
9393
9300
|
const niceSteps = [1, 2, 2.5, 5, 10];
|
|
9394
|
-
const step = niceSteps.find((
|
|
9301
|
+
const step = niceSteps.find((s) => s * mag >= rawStep);
|
|
9395
9302
|
const niceStep = step * mag;
|
|
9396
9303
|
const ticks = [];
|
|
9397
9304
|
for (let v = 0; v <= yMax + niceStep * 1e-3; v += niceStep) {
|
|
@@ -9402,8 +9309,8 @@ function niceTicks(yMax) {
|
|
|
9402
9309
|
function buildLinePaths(data) {
|
|
9403
9310
|
const seenX = /* @__PURE__ */ new Map();
|
|
9404
9311
|
const xLabels = [];
|
|
9405
|
-
for (const
|
|
9406
|
-
for (const p of
|
|
9312
|
+
for (const s of data.series) {
|
|
9313
|
+
for (const p of s.points) {
|
|
9407
9314
|
if (!seenX.has(p.x)) {
|
|
9408
9315
|
seenX.set(p.x, xLabels.length);
|
|
9409
9316
|
xLabels.push(p.x);
|
|
@@ -9412,16 +9319,16 @@ function buildLinePaths(data) {
|
|
|
9412
9319
|
}
|
|
9413
9320
|
const denom = Math.max(1, xLabels.length - 1);
|
|
9414
9321
|
let yMax = 0;
|
|
9415
|
-
for (const
|
|
9416
|
-
for (const p of
|
|
9322
|
+
for (const s of data.series) {
|
|
9323
|
+
for (const p of s.points) {
|
|
9417
9324
|
if (p.y > yMax) yMax = p.y;
|
|
9418
9325
|
}
|
|
9419
9326
|
}
|
|
9420
9327
|
if (yMax === 0) yMax = 1;
|
|
9421
|
-
const seriesOut = data.series.map((
|
|
9422
|
-
name:
|
|
9328
|
+
const seriesOut = data.series.map((s, sIdx) => ({
|
|
9329
|
+
name: s.name,
|
|
9423
9330
|
seriesIdx: sIdx,
|
|
9424
|
-
points:
|
|
9331
|
+
points: s.points.map((p) => {
|
|
9425
9332
|
const xIdx = seenX.get(p.x);
|
|
9426
9333
|
return {
|
|
9427
9334
|
cx: xIdx / denom * 100,
|
|
@@ -9433,14 +9340,6 @@ function buildLinePaths(data) {
|
|
|
9433
9340
|
}));
|
|
9434
9341
|
return { series: seriesOut, xLabels, yMax };
|
|
9435
9342
|
}
|
|
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
9343
|
var CHART_CSS = `
|
|
9445
9344
|
.pdp-chart-frame {
|
|
9446
9345
|
--pc-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -9808,12 +9707,12 @@ var CHART_CSS = `
|
|
|
9808
9707
|
background: linear-gradient(90deg, color-mix(in srgb, var(--pc-primary) 12%, transparent), transparent 80%);
|
|
9809
9708
|
}
|
|
9810
9709
|
.pdp-chart-table tbody tr.is-winning td:first-child {
|
|
9811
|
-
color: color-mix(in srgb, var(--pc-primary) 70%, white 30%);
|
|
9710
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pc-primary) 70%, white 30%));
|
|
9812
9711
|
}
|
|
9813
9712
|
.pdp-chart-table tbody tr.is-winning td:first-child::before {
|
|
9814
9713
|
content: '\\2726 ';
|
|
9815
9714
|
font-size: 11px;
|
|
9816
|
-
color: color-mix(in srgb, var(--pc-primary) 60%, white 40%);
|
|
9715
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pc-primary) 60%, white 40%));
|
|
9817
9716
|
vertical-align: 1px;
|
|
9818
9717
|
margin-right: 2px;
|
|
9819
9718
|
}
|
|
@@ -9936,7 +9835,7 @@ function colorVar(seriesIdx) {
|
|
|
9936
9835
|
return `var(--pc-color-${seriesIdx % PALETTE_SIZE})`;
|
|
9937
9836
|
}
|
|
9938
9837
|
var _PdpChartLit_instances, renderLegend_fn, renderTable_fn, renderBars_fn, renderLine_fn;
|
|
9939
|
-
var PdpChartLit = class extends
|
|
9838
|
+
var PdpChartLit = class extends LitElement19 {
|
|
9940
9839
|
constructor() {
|
|
9941
9840
|
super(...arguments);
|
|
9942
9841
|
__privateAdd(this, _PdpChartLit_instances);
|
|
@@ -9949,7 +9848,7 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
9949
9848
|
}
|
|
9950
9849
|
connectedCallback() {
|
|
9951
9850
|
super.connectedCallback();
|
|
9952
|
-
|
|
9851
|
+
ensureStylesInRoot(this, "syntro-pdp-chart-style", CHART_CSS);
|
|
9953
9852
|
if (this._entering) this.requestUpdate();
|
|
9954
9853
|
}
|
|
9955
9854
|
disconnectedCallback() {
|
|
@@ -9972,16 +9871,16 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
9972
9871
|
}
|
|
9973
9872
|
render() {
|
|
9974
9873
|
if (!this.data) {
|
|
9975
|
-
return
|
|
9874
|
+
return html20`<div data-skeleton class="pdp-chart-skeleton">
|
|
9976
9875
|
<div class="pdp-skeleton-bar" style="width:50%"></div>
|
|
9977
9876
|
<div class="pdp-skeleton-rect" style="height:200px"></div>
|
|
9978
9877
|
</div>`;
|
|
9979
9878
|
}
|
|
9980
9879
|
const isTable = this.data.viz_kind === "table";
|
|
9981
|
-
const allSeriesOnePoint = this.data.series.every((
|
|
9880
|
+
const allSeriesOnePoint = this.data.series.every((s) => s.points.length <= 1);
|
|
9982
9881
|
const effectiveKind = isTable ? "table" : this.data.viz_kind === "line" && !allSeriesOnePoint ? "line" : "bar";
|
|
9983
9882
|
const isMultiSeries = this.data.series.length > 1;
|
|
9984
|
-
return
|
|
9883
|
+
return html20`
|
|
9985
9884
|
<section
|
|
9986
9885
|
class="${this._entering ? "pdp-chart pdp-chart-frame is-entering" : "pdp-chart pdp-chart-frame"}"
|
|
9987
9886
|
aria-label="${this.data.title}"
|
|
@@ -9992,11 +9891,11 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
9992
9891
|
</div>
|
|
9993
9892
|
<h3 class="pdp-chart-title">${this.data.title}</h3>
|
|
9994
9893
|
|
|
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 ?
|
|
9894
|
+
${effectiveKind === "table" ? __privateMethod(this, _PdpChartLit_instances, renderTable_fn).call(this) : nothing10}
|
|
9895
|
+
${effectiveKind === "bar" ? __privateMethod(this, _PdpChartLit_instances, renderBars_fn).call(this) : nothing10}
|
|
9896
|
+
${effectiveKind === "line" ? __privateMethod(this, _PdpChartLit_instances, renderLine_fn).call(this) : nothing10}
|
|
9897
|
+
${effectiveKind !== "table" && isMultiSeries ? __privateMethod(this, _PdpChartLit_instances, renderLegend_fn).call(this) : nothing10}
|
|
9898
|
+
${this.data.rationale ? html20`<p class="pdp-chart-rationale">${this.data.rationale}</p>` : nothing10}
|
|
10000
9899
|
</section>
|
|
10001
9900
|
`;
|
|
10002
9901
|
}
|
|
@@ -10004,17 +9903,17 @@ var PdpChartLit = class extends LitElement18 {
|
|
|
10004
9903
|
_PdpChartLit_instances = new WeakSet();
|
|
10005
9904
|
renderLegend_fn = function() {
|
|
10006
9905
|
const data = this.data;
|
|
10007
|
-
return
|
|
9906
|
+
return html20`
|
|
10008
9907
|
<ol class="pdp-chart-legend" aria-label="Legend">
|
|
10009
9908
|
${data.series.map(
|
|
10010
|
-
(
|
|
9909
|
+
(s, idx) => html20`
|
|
10011
9910
|
<li class="pdp-chart-legend-item">
|
|
10012
9911
|
<span
|
|
10013
9912
|
class="pdp-chart-legend-swatch"
|
|
10014
9913
|
style="background:${colorVar(idx)}"
|
|
10015
9914
|
aria-hidden="true"
|
|
10016
9915
|
></span>
|
|
10017
|
-
<span>${
|
|
9916
|
+
<span>${s.name}</span>
|
|
10018
9917
|
</li>
|
|
10019
9918
|
`
|
|
10020
9919
|
)}
|
|
@@ -10023,22 +9922,22 @@ renderLegend_fn = function() {
|
|
|
10023
9922
|
};
|
|
10024
9923
|
renderTable_fn = function() {
|
|
10025
9924
|
const matrix = buildComparisonMatrix(this.data);
|
|
10026
|
-
return
|
|
9925
|
+
return html20`
|
|
10027
9926
|
<div class="pdp-chart-table-wrap">
|
|
10028
9927
|
<table class="pdp-chart-table">
|
|
10029
9928
|
<thead>
|
|
10030
9929
|
<tr>
|
|
10031
9930
|
<th></th>
|
|
10032
|
-
${matrix.colLabels.map((label) =>
|
|
9931
|
+
${matrix.colLabels.map((label) => html20`<th>${label}</th>`)}
|
|
10033
9932
|
</tr>
|
|
10034
9933
|
</thead>
|
|
10035
9934
|
<tbody>
|
|
10036
9935
|
${matrix.rowLabels.map(
|
|
10037
|
-
(row, rowIdx) =>
|
|
9936
|
+
(row, rowIdx) => html20`
|
|
10038
9937
|
<tr class=${rowIdx === matrix.winningRow ? "is-winning" : ""}>
|
|
10039
9938
|
<td>${row}</td>
|
|
10040
9939
|
${matrix.cells[rowIdx].map(
|
|
10041
|
-
(cell) =>
|
|
9940
|
+
(cell) => html20`<td class="pdp-chart-numeric-cell">${formatNum(cell)}</td>`
|
|
10042
9941
|
)}
|
|
10043
9942
|
</tr>
|
|
10044
9943
|
`
|
|
@@ -10053,7 +9952,7 @@ renderBars_fn = function() {
|
|
|
10053
9952
|
const { buckets, yMax } = buildBarBuckets(data);
|
|
10054
9953
|
const ticks = niceTicks(yMax);
|
|
10055
9954
|
const scaleMax = Math.max(yMax, ticks[ticks.length - 1]);
|
|
10056
|
-
return
|
|
9955
|
+
return html20`
|
|
10057
9956
|
<div
|
|
10058
9957
|
class="pdp-chart-bars"
|
|
10059
9958
|
role="figure"
|
|
@@ -10062,33 +9961,33 @@ renderBars_fn = function() {
|
|
|
10062
9961
|
>
|
|
10063
9962
|
<div class="pdp-chart-yaxis" aria-hidden="true">
|
|
10064
9963
|
${ticks.map(
|
|
10065
|
-
(
|
|
9964
|
+
(t) => html20`
|
|
10066
9965
|
<span
|
|
10067
9966
|
class="pdp-chart-yaxis-tick"
|
|
10068
|
-
style="bottom:${
|
|
9967
|
+
style="bottom:${t / scaleMax * 100}%"
|
|
10069
9968
|
>
|
|
10070
|
-
${formatNum(
|
|
9969
|
+
${formatNum(t)}
|
|
10071
9970
|
</span>
|
|
10072
9971
|
`
|
|
10073
9972
|
)}
|
|
10074
9973
|
</div>
|
|
10075
9974
|
<div class="pdp-chart-bars-area">
|
|
10076
9975
|
${ticks.map(
|
|
10077
|
-
(
|
|
9976
|
+
(t) => html20`
|
|
10078
9977
|
<div
|
|
10079
9978
|
class="pdp-chart-bars-gridline"
|
|
10080
|
-
style="bottom:${
|
|
9979
|
+
style="bottom:${t / scaleMax * 100}%"
|
|
10081
9980
|
aria-hidden="true"
|
|
10082
9981
|
></div>
|
|
10083
9982
|
`
|
|
10084
9983
|
)}
|
|
10085
9984
|
<div class="pdp-chart-bars-baseline" aria-hidden="true"></div>
|
|
10086
9985
|
${buckets.map(
|
|
10087
|
-
(bucket, bIdx) =>
|
|
9986
|
+
(bucket, bIdx) => html20`
|
|
10088
9987
|
<div class="pdp-chart-bars-bucket">
|
|
10089
9988
|
<div class="pdp-chart-bars-stack">
|
|
10090
9989
|
${bucket.bars.map(
|
|
10091
|
-
(bar, barIdxInBucket) =>
|
|
9990
|
+
(bar, barIdxInBucket) => html20`
|
|
10092
9991
|
<div
|
|
10093
9992
|
class="pdp-chart-bar"
|
|
10094
9993
|
style="
|
|
@@ -10099,9 +9998,9 @@ renderBars_fn = function() {
|
|
|
10099
9998
|
role="img"
|
|
10100
9999
|
aria-label="${bucket.label} — ${bar.seriesName}: ${formatNum(bar.value)}"
|
|
10101
10000
|
>
|
|
10102
|
-
${bucket.bars.length === 1 ?
|
|
10001
|
+
${bucket.bars.length === 1 ? html20`<span class="pdp-chart-bar-value"
|
|
10103
10002
|
>${formatNum(bar.value)}</span
|
|
10104
|
-
>` :
|
|
10003
|
+
>` : nothing10}
|
|
10105
10004
|
</div>
|
|
10106
10005
|
`
|
|
10107
10006
|
)}
|
|
@@ -10119,7 +10018,7 @@ renderLine_fn = function() {
|
|
|
10119
10018
|
const { series, xLabels, yMax } = buildLinePaths(data);
|
|
10120
10019
|
const ticks = niceTicks(yMax);
|
|
10121
10020
|
const scaleMax = Math.max(yMax, ticks[ticks.length - 1]);
|
|
10122
|
-
return
|
|
10021
|
+
return html20`
|
|
10123
10022
|
<div
|
|
10124
10023
|
class="pdp-chart-line"
|
|
10125
10024
|
role="figure"
|
|
@@ -10129,12 +10028,12 @@ renderLine_fn = function() {
|
|
|
10129
10028
|
>
|
|
10130
10029
|
<div class="pdp-chart-line-yaxis" aria-hidden="true">
|
|
10131
10030
|
${ticks.map(
|
|
10132
|
-
(
|
|
10031
|
+
(t) => html20`
|
|
10133
10032
|
<span
|
|
10134
10033
|
class="pdp-chart-yaxis-tick"
|
|
10135
|
-
style="bottom:${
|
|
10034
|
+
style="bottom:${t / scaleMax * 100}%"
|
|
10136
10035
|
>
|
|
10137
|
-
${formatNum(
|
|
10036
|
+
${formatNum(t)}
|
|
10138
10037
|
</span>
|
|
10139
10038
|
`
|
|
10140
10039
|
)}
|
|
@@ -10146,25 +10045,25 @@ renderLine_fn = function() {
|
|
|
10146
10045
|
preserveAspectRatio="none"
|
|
10147
10046
|
aria-hidden="true"
|
|
10148
10047
|
>
|
|
10149
|
-
${ticks.map((
|
|
10150
|
-
const y = 60 -
|
|
10048
|
+
${ticks.map((t) => {
|
|
10049
|
+
const y = 60 - t / scaleMax * 56 - 2;
|
|
10151
10050
|
return svg2`<line class="pdp-chart-line-gridline" x1="0" y1=${y} x2="100" y2=${y} />`;
|
|
10152
10051
|
})}
|
|
10153
10052
|
${series.map(
|
|
10154
|
-
(
|
|
10053
|
+
(s) => svg2`
|
|
10155
10054
|
<polyline
|
|
10156
10055
|
class="pdp-chart-line-stroke"
|
|
10157
|
-
style=${`--line-color:${colorVar(
|
|
10158
|
-
points=${
|
|
10056
|
+
style=${`--line-color:${colorVar(s.seriesIdx)}; --delay:${s.seriesIdx * 200}ms`}
|
|
10057
|
+
points=${s.points.map((p) => `${p.cx},${p.cy}`).join(" ")}
|
|
10159
10058
|
/>
|
|
10160
|
-
${
|
|
10059
|
+
${s.points.map(
|
|
10161
10060
|
(p, idx) => svg2`
|
|
10162
10061
|
<circle
|
|
10163
10062
|
class="pdp-chart-line-dot"
|
|
10164
10063
|
cx=${p.cx}
|
|
10165
10064
|
cy=${p.cy}
|
|
10166
10065
|
r="1.4"
|
|
10167
|
-
style=${`--line-color:${colorVar(
|
|
10066
|
+
style=${`--line-color:${colorVar(s.seriesIdx)}; --delay:${s.seriesIdx * 200}ms; --dot-delay:${800 + idx * 60}ms`}
|
|
10168
10067
|
/>
|
|
10169
10068
|
`
|
|
10170
10069
|
)}
|
|
@@ -10178,7 +10077,7 @@ renderLine_fn = function() {
|
|
|
10178
10077
|
style="--col-count:${xLabels.length}"
|
|
10179
10078
|
aria-hidden="true"
|
|
10180
10079
|
>
|
|
10181
|
-
${xLabels.map((label) =>
|
|
10080
|
+
${xLabels.map((label) => html20`<span>${label}</span>`)}
|
|
10182
10081
|
</div>
|
|
10183
10082
|
`;
|
|
10184
10083
|
};
|
|
@@ -10191,15 +10090,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
10191
10090
|
}
|
|
10192
10091
|
|
|
10193
10092
|
// 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
|
-
}
|
|
10093
|
+
import { html as html21, LitElement as LitElement20 } from "lit";
|
|
10203
10094
|
var CSS = `
|
|
10204
10095
|
.pdp-chart-bar {
|
|
10205
10096
|
--c-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10279,7 +10170,7 @@ var CSS = `
|
|
|
10279
10170
|
}
|
|
10280
10171
|
`;
|
|
10281
10172
|
var PALETTE = ["hsl(150 32% 50%)", "hsl(40 75% 60%)", "hsl(208 50% 55%)", "hsl(320 38% 60%)"];
|
|
10282
|
-
var PdpChartBarStackedLit = class extends
|
|
10173
|
+
var PdpChartBarStackedLit = class extends LitElement20 {
|
|
10283
10174
|
constructor() {
|
|
10284
10175
|
super(...arguments);
|
|
10285
10176
|
this.data = void 0;
|
|
@@ -10289,39 +10180,39 @@ var PdpChartBarStackedLit = class extends LitElement19 {
|
|
|
10289
10180
|
}
|
|
10290
10181
|
connectedCallback() {
|
|
10291
10182
|
super.connectedCallback();
|
|
10292
|
-
|
|
10183
|
+
ensureStylesInRoot(this, "syntro-pdp-chart-bar-style", CSS);
|
|
10293
10184
|
}
|
|
10294
10185
|
render() {
|
|
10295
|
-
if (!this.data) return
|
|
10186
|
+
if (!this.data) return html21`<div class="pdp-chart-bar" style="opacity:0.5;">…</div>`;
|
|
10296
10187
|
const { title, series } = this.data;
|
|
10297
|
-
const allX = Array.from(new Set(series.flatMap((
|
|
10298
|
-
const max = Math.max(...series.flatMap((
|
|
10299
|
-
return
|
|
10188
|
+
const allX = Array.from(new Set(series.flatMap((s) => s.points.map((p) => p.x))));
|
|
10189
|
+
const max = Math.max(...series.flatMap((s) => s.points.map((p) => p.y)), 1);
|
|
10190
|
+
return html21`
|
|
10300
10191
|
<section class="pdp-chart-bar" aria-label=${title}>
|
|
10301
10192
|
<h3 class="pdp-chart-bar__title">${title}</h3>
|
|
10302
|
-
${series.length > 1 ?
|
|
10193
|
+
${series.length > 1 ? html21`<div class="pdp-chart-bar__legend">
|
|
10303
10194
|
${series.map(
|
|
10304
|
-
(
|
|
10195
|
+
(s, i) => html21`<span
|
|
10305
10196
|
><span
|
|
10306
10197
|
class="pdp-chart-bar__legend-dot"
|
|
10307
|
-
style=${`background: ${PALETTE[
|
|
10198
|
+
style=${`background: ${PALETTE[i % PALETTE.length]};`}
|
|
10308
10199
|
></span
|
|
10309
|
-
>${
|
|
10200
|
+
>${s.name}</span
|
|
10310
10201
|
>`
|
|
10311
10202
|
)}
|
|
10312
10203
|
</div>` : ""}
|
|
10313
10204
|
${allX.map(
|
|
10314
|
-
(x) =>
|
|
10205
|
+
(x) => html21`<div class="pdp-chart-bar__row">
|
|
10315
10206
|
<div class="pdp-chart-bar__row-label">${x}</div>
|
|
10316
10207
|
<div class="pdp-chart-bar__row-bars">
|
|
10317
|
-
${series.map((
|
|
10318
|
-
const point =
|
|
10208
|
+
${series.map((s, i) => {
|
|
10209
|
+
const point = s.points.find((p) => p.x === x);
|
|
10319
10210
|
if (!point) return "";
|
|
10320
10211
|
const pct = point.y / max * 100;
|
|
10321
|
-
return
|
|
10212
|
+
return html21`<div class="pdp-chart-bar__bar-track">
|
|
10322
10213
|
<div
|
|
10323
10214
|
class="pdp-chart-bar__bar-fill"
|
|
10324
|
-
style=${`width: ${pct.toFixed(1)}%; background: ${PALETTE[
|
|
10215
|
+
style=${`width: ${pct.toFixed(1)}%; background: ${PALETTE[i % PALETTE.length]};`}
|
|
10325
10216
|
></div>
|
|
10326
10217
|
<span class="pdp-chart-bar__bar-value">${point.y}</span>
|
|
10327
10218
|
</div>`;
|
|
@@ -10341,15 +10232,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
10341
10232
|
}
|
|
10342
10233
|
|
|
10343
10234
|
// 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
|
-
}
|
|
10235
|
+
import { html as html22, LitElement as LitElement21, svg as svg3 } from "lit";
|
|
10353
10236
|
var CSS2 = `
|
|
10354
10237
|
.pdp-chart-met {
|
|
10355
10238
|
--c-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10443,17 +10326,17 @@ var CSS2 = `
|
|
|
10443
10326
|
function buildSparkline(points) {
|
|
10444
10327
|
const w = 200;
|
|
10445
10328
|
const h = 38;
|
|
10446
|
-
const xs = points.map((_,
|
|
10329
|
+
const xs = points.map((_, i) => i / Math.max(points.length - 1, 1) * (w - 4) + 2);
|
|
10447
10330
|
const min = Math.min(...points.map((p) => p.y));
|
|
10448
10331
|
const max = Math.max(...points.map((p) => p.y));
|
|
10449
10332
|
const range = max - min || 1;
|
|
10450
10333
|
const ys = points.map((p) => h - 4 - (p.y - min) / range * (h - 8));
|
|
10451
|
-
const line = xs.map((x,
|
|
10334
|
+
const line = xs.map((x, i) => `${i === 0 ? "M" : "L"} ${x.toFixed(1)} ${ys[i].toFixed(1)}`).join(" ");
|
|
10452
10335
|
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,
|
|
10336
|
+
const peakIndex = points.reduce((best, p, i) => p.y > points[best].y ? i : best, 0);
|
|
10454
10337
|
return { linePath: line, areaPath: area, peakIndex };
|
|
10455
10338
|
}
|
|
10456
|
-
var PdpChartMetricPullLit = class extends
|
|
10339
|
+
var PdpChartMetricPullLit = class extends LitElement21 {
|
|
10457
10340
|
constructor() {
|
|
10458
10341
|
super(...arguments);
|
|
10459
10342
|
this.data = void 0;
|
|
@@ -10463,16 +10346,16 @@ var PdpChartMetricPullLit = class extends LitElement20 {
|
|
|
10463
10346
|
}
|
|
10464
10347
|
connectedCallback() {
|
|
10465
10348
|
super.connectedCallback();
|
|
10466
|
-
|
|
10349
|
+
ensureStylesInRoot(this, "syntro-pdp-chart-metric-style", CSS2);
|
|
10467
10350
|
}
|
|
10468
10351
|
render() {
|
|
10469
|
-
if (!this.data) return
|
|
10352
|
+
if (!this.data) return html22`<div class="pdp-chart-met" style="opacity:0.5;">…</div>`;
|
|
10470
10353
|
const { title, series } = this.data;
|
|
10471
10354
|
const first = series[0];
|
|
10472
|
-
if (!first || first.points.length === 0) return
|
|
10355
|
+
if (!first || first.points.length === 0) return html22``;
|
|
10473
10356
|
const points = first.points;
|
|
10474
10357
|
const peak = points.reduce((b, p) => p.y > b.y ? p : b, points[0]);
|
|
10475
|
-
const mean = points.reduce((
|
|
10358
|
+
const mean = points.reduce((s, p) => s + p.y, 0) / points.length;
|
|
10476
10359
|
const delta = (peak.y - mean) / Math.max(mean, 1) * 100;
|
|
10477
10360
|
const spark = buildSparkline(points);
|
|
10478
10361
|
const w = 200;
|
|
@@ -10482,7 +10365,7 @@ var PdpChartMetricPullLit = class extends LitElement20 {
|
|
|
10482
10365
|
const max = Math.max(...points.map((p) => p.y));
|
|
10483
10366
|
const range = max - min || 1;
|
|
10484
10367
|
const py = h - 4 - (peak.y - min) / range * (h - 8);
|
|
10485
|
-
return
|
|
10368
|
+
return html22`
|
|
10486
10369
|
<section class="pdp-chart-met" aria-label=${title}>
|
|
10487
10370
|
<div class="pdp-chart-met__metric">
|
|
10488
10371
|
<span class="pdp-chart-met__big">${peak.y}</span>
|
|
@@ -10514,15 +10397,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-cha
|
|
|
10514
10397
|
}
|
|
10515
10398
|
|
|
10516
10399
|
// 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
|
-
}
|
|
10400
|
+
import { html as html23, LitElement as LitElement22 } from "lit";
|
|
10526
10401
|
var CSS3 = `
|
|
10527
10402
|
.pdp-qa-acc {
|
|
10528
10403
|
--qa-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10596,7 +10471,7 @@ var CSS3 = `
|
|
|
10596
10471
|
max-width: 56ch;
|
|
10597
10472
|
}
|
|
10598
10473
|
`;
|
|
10599
|
-
var PdpQaAccordionLit = class extends
|
|
10474
|
+
var PdpQaAccordionLit = class extends LitElement22 {
|
|
10600
10475
|
constructor() {
|
|
10601
10476
|
super(...arguments);
|
|
10602
10477
|
this.data = void 0;
|
|
@@ -10606,14 +10481,14 @@ var PdpQaAccordionLit = class extends LitElement21 {
|
|
|
10606
10481
|
}
|
|
10607
10482
|
connectedCallback() {
|
|
10608
10483
|
super.connectedCallback();
|
|
10609
|
-
|
|
10484
|
+
ensureStylesInRoot(this, "syntro-pdp-qa-accordion-style", CSS3);
|
|
10610
10485
|
}
|
|
10611
10486
|
render() {
|
|
10612
|
-
if (!this.data) return
|
|
10613
|
-
return
|
|
10487
|
+
if (!this.data) return html23`<div class="pdp-qa-acc" style="opacity:0.5;">…</div>`;
|
|
10488
|
+
return html23`
|
|
10614
10489
|
<section class="pdp-qa-acc" aria-label="Frequently asked questions">
|
|
10615
10490
|
${this.data.items.map(
|
|
10616
|
-
(it) =>
|
|
10491
|
+
(it) => html23`<details class="pdp-qa-acc__item">
|
|
10617
10492
|
<summary>
|
|
10618
10493
|
<span class="pdp-qa-acc__chev" aria-hidden="true"></span>
|
|
10619
10494
|
<span>${it.question}</span>
|
|
@@ -10633,15 +10508,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa-
|
|
|
10633
10508
|
}
|
|
10634
10509
|
|
|
10635
10510
|
// 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
|
-
}
|
|
10511
|
+
import { html as html24, LitElement as LitElement23 } from "lit";
|
|
10645
10512
|
var CSS4 = `
|
|
10646
10513
|
.pdp-qa-side {
|
|
10647
10514
|
--qa-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10707,7 +10574,7 @@ var CSS4 = `
|
|
|
10707
10574
|
margin: 0;
|
|
10708
10575
|
}
|
|
10709
10576
|
`;
|
|
10710
|
-
var PdpQaSideBySideLit = class extends
|
|
10577
|
+
var PdpQaSideBySideLit = class extends LitElement23 {
|
|
10711
10578
|
constructor() {
|
|
10712
10579
|
super(...arguments);
|
|
10713
10580
|
this.data = void 0;
|
|
@@ -10717,14 +10584,14 @@ var PdpQaSideBySideLit = class extends LitElement22 {
|
|
|
10717
10584
|
}
|
|
10718
10585
|
connectedCallback() {
|
|
10719
10586
|
super.connectedCallback();
|
|
10720
|
-
|
|
10587
|
+
ensureStylesInRoot(this, "syntro-pdp-qa-side-style", CSS4);
|
|
10721
10588
|
}
|
|
10722
10589
|
render() {
|
|
10723
|
-
if (!this.data) return
|
|
10724
|
-
return
|
|
10590
|
+
if (!this.data) return html24`<div class="pdp-qa-side" style="opacity:0.5;">…</div>`;
|
|
10591
|
+
return html24`
|
|
10725
10592
|
<section class="pdp-qa-side" aria-label="Q&A">
|
|
10726
10593
|
${this.data.items.map(
|
|
10727
|
-
(it) =>
|
|
10594
|
+
(it) => html24`<div class="pdp-qa-side__item">
|
|
10728
10595
|
<h4 class="pdp-qa-side__q">${it.question}</h4>
|
|
10729
10596
|
<p class="pdp-qa-side__a">${it.answer}</p>
|
|
10730
10597
|
</div>`
|
|
@@ -10741,15 +10608,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa-
|
|
|
10741
10608
|
}
|
|
10742
10609
|
|
|
10743
10610
|
// 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
|
-
}
|
|
10611
|
+
import { html as html25, LitElement as LitElement24 } from "lit";
|
|
10753
10612
|
var CSS5 = `
|
|
10754
10613
|
.pdp-reg-fb {
|
|
10755
10614
|
--r-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10832,7 +10691,7 @@ var CSS5 = `
|
|
|
10832
10691
|
font-style: italic;
|
|
10833
10692
|
}
|
|
10834
10693
|
`;
|
|
10835
|
-
var PdpRegionalFullBleedLit = class extends
|
|
10694
|
+
var PdpRegionalFullBleedLit = class extends LitElement24 {
|
|
10836
10695
|
constructor() {
|
|
10837
10696
|
super(...arguments);
|
|
10838
10697
|
this.data = void 0;
|
|
@@ -10842,12 +10701,12 @@ var PdpRegionalFullBleedLit = class extends LitElement23 {
|
|
|
10842
10701
|
}
|
|
10843
10702
|
connectedCallback() {
|
|
10844
10703
|
super.connectedCallback();
|
|
10845
|
-
|
|
10704
|
+
ensureStylesInRoot(this, "syntro-pdp-regional-fullbleed-style", CSS5);
|
|
10846
10705
|
}
|
|
10847
10706
|
render() {
|
|
10848
|
-
if (!this.data) return
|
|
10707
|
+
if (!this.data) return html25`<div class="pdp-reg-fb" style="opacity:0.5;">…</div>`;
|
|
10849
10708
|
const d = this.data;
|
|
10850
|
-
return
|
|
10709
|
+
return html25`
|
|
10851
10710
|
<section class="pdp-reg-fb" aria-label="Where you are">
|
|
10852
10711
|
<article class="pdp-reg-fb__panel">
|
|
10853
10712
|
<svg class="pdp-reg-fb__glyph" viewBox="0 0 56 56" aria-hidden="true">
|
|
@@ -10890,15 +10749,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
10890
10749
|
}
|
|
10891
10750
|
|
|
10892
10751
|
// 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
|
-
}
|
|
10752
|
+
import { html as html26, LitElement as LitElement25 } from "lit";
|
|
10902
10753
|
var CSS6 = `
|
|
10903
10754
|
.pdp-reg-in {
|
|
10904
10755
|
--r-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -10951,7 +10802,7 @@ var CSS6 = `
|
|
|
10951
10802
|
font-style: italic;
|
|
10952
10803
|
}
|
|
10953
10804
|
`;
|
|
10954
|
-
var PdpRegionalInlineNoteLit = class extends
|
|
10805
|
+
var PdpRegionalInlineNoteLit = class extends LitElement25 {
|
|
10955
10806
|
constructor() {
|
|
10956
10807
|
super(...arguments);
|
|
10957
10808
|
this.data = void 0;
|
|
@@ -10961,12 +10812,12 @@ var PdpRegionalInlineNoteLit = class extends LitElement24 {
|
|
|
10961
10812
|
}
|
|
10962
10813
|
connectedCallback() {
|
|
10963
10814
|
super.connectedCallback();
|
|
10964
|
-
|
|
10815
|
+
ensureStylesInRoot(this, "syntro-pdp-regional-inline-style", CSS6);
|
|
10965
10816
|
}
|
|
10966
10817
|
render() {
|
|
10967
|
-
if (!this.data) return
|
|
10818
|
+
if (!this.data) return html26`<div class="pdp-reg-in" style="opacity:0.5;">…</div>`;
|
|
10968
10819
|
const d = this.data;
|
|
10969
|
-
return
|
|
10820
|
+
return html26`
|
|
10970
10821
|
<aside class="pdp-reg-in" aria-label="Regional note">
|
|
10971
10822
|
<svg class="pdp-reg-in__icon" viewBox="0 0 22 22" aria-hidden="true">
|
|
10972
10823
|
<circle cx="11" cy="11" r="9" fill="none" stroke="currentColor" stroke-width="1.6"/>
|
|
@@ -10994,8 +10845,8 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
10994
10845
|
}
|
|
10995
10846
|
|
|
10996
10847
|
// src/widgets/PdpSlotPlaceholderLit.ts
|
|
10997
|
-
import { html as
|
|
10998
|
-
var PdpSlotPlaceholderLit = class extends
|
|
10848
|
+
import { html as html27, LitElement as LitElement26 } from "lit";
|
|
10849
|
+
var PdpSlotPlaceholderLit = class extends LitElement26 {
|
|
10999
10850
|
constructor() {
|
|
11000
10851
|
super(...arguments);
|
|
11001
10852
|
this.label = "More personalized content coming soon";
|
|
@@ -11004,7 +10855,7 @@ var PdpSlotPlaceholderLit = class extends LitElement25 {
|
|
|
11004
10855
|
return this;
|
|
11005
10856
|
}
|
|
11006
10857
|
render() {
|
|
11007
|
-
return
|
|
10858
|
+
return html27`
|
|
11008
10859
|
<div class="pdp-slot-placeholder" data-placeholder>
|
|
11009
10860
|
<p>${this.label}</p>
|
|
11010
10861
|
<small>Ask in chat to explore this product further.</small>
|
|
@@ -11020,15 +10871,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-slo
|
|
|
11020
10871
|
}
|
|
11021
10872
|
|
|
11022
10873
|
// 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
|
-
}
|
|
10874
|
+
import { html as html28, LitElement as LitElement27, nothing as nothing11 } from "lit";
|
|
11032
10875
|
var RIBBON_CSS = `
|
|
11033
10876
|
.syntro-pdp-ribbon {
|
|
11034
10877
|
--rb-primary: var(--syntro-pdp-primary, hsl(150 32% 38%));
|
|
@@ -11087,7 +10930,7 @@ var RIBBON_CSS = `
|
|
|
11087
10930
|
width: 16px;
|
|
11088
10931
|
height: 16px;
|
|
11089
10932
|
display: inline-block;
|
|
11090
|
-
color: var(--rb-primary);
|
|
10933
|
+
color: var(--syntro-pdp-primary-ink, var(--rb-primary));
|
|
11091
10934
|
}
|
|
11092
10935
|
.syntro-pdp-ribbon-copy {
|
|
11093
10936
|
font-family: var(--rb-display);
|
|
@@ -11104,7 +10947,7 @@ var RIBBON_CSS = `
|
|
|
11104
10947
|
font-style: normal;
|
|
11105
10948
|
font-family: var(--syntro-pdp-sans, inherit);
|
|
11106
10949
|
font-weight: 500;
|
|
11107
|
-
color: var(--rb-primary);
|
|
10950
|
+
color: var(--syntro-pdp-primary-ink, var(--rb-primary));
|
|
11108
10951
|
font-size: 14.5px;
|
|
11109
10952
|
letter-spacing: 0;
|
|
11110
10953
|
}
|
|
@@ -11149,7 +10992,7 @@ var RIBBON_CSS = `
|
|
|
11149
10992
|
100% { transform: translateX(110%); }
|
|
11150
10993
|
}
|
|
11151
10994
|
`;
|
|
11152
|
-
var PdpRibbonLit = class extends
|
|
10995
|
+
var PdpRibbonLit = class extends LitElement27 {
|
|
11153
10996
|
constructor() {
|
|
11154
10997
|
super(...arguments);
|
|
11155
10998
|
this.topic = "";
|
|
@@ -11163,7 +11006,7 @@ var PdpRibbonLit = class extends LitElement26 {
|
|
|
11163
11006
|
}
|
|
11164
11007
|
connectedCallback() {
|
|
11165
11008
|
super.connectedCallback();
|
|
11166
|
-
|
|
11009
|
+
ensureStylesInRoot(this, "syntro-pdp-ribbon-style", RIBBON_CSS);
|
|
11167
11010
|
if (this._entering) this.requestUpdate();
|
|
11168
11011
|
}
|
|
11169
11012
|
disconnectedCallback() {
|
|
@@ -11187,7 +11030,7 @@ var PdpRibbonLit = class extends LitElement26 {
|
|
|
11187
11030
|
}
|
|
11188
11031
|
render() {
|
|
11189
11032
|
const cls = this._entering ? "syntro-pdp-ribbon is-entering" : "syntro-pdp-ribbon";
|
|
11190
|
-
return
|
|
11033
|
+
return html28`
|
|
11191
11034
|
<div class="${cls}" role="status" aria-live="polite">
|
|
11192
11035
|
<span class="syntro-pdp-ribbon-sweep" aria-hidden="true"></span>
|
|
11193
11036
|
<svg
|
|
@@ -11207,7 +11050,7 @@ var PdpRibbonLit = class extends LitElement26 {
|
|
|
11207
11050
|
</svg>
|
|
11208
11051
|
<p class="syntro-pdp-ribbon-copy">
|
|
11209
11052
|
Tailored to your conversation about
|
|
11210
|
-
${this.topic ?
|
|
11053
|
+
${this.topic ? html28` <span class="syntro-pdp-ribbon-topic">${this.topic}</span>.` : nothing11}
|
|
11211
11054
|
<span class="syntro-pdp-ribbon-trail"> ${this.subtitle}</span>
|
|
11212
11055
|
</p>
|
|
11213
11056
|
</div>
|
|
@@ -11224,15 +11067,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-rib
|
|
|
11224
11067
|
}
|
|
11225
11068
|
|
|
11226
11069
|
// 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
|
-
}
|
|
11070
|
+
import { html as html29, LitElement as LitElement28, nothing as nothing12 } from "lit";
|
|
11236
11071
|
var QA_CSS = `
|
|
11237
11072
|
.pdp-qa {
|
|
11238
11073
|
--qa-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -11314,7 +11149,7 @@ var QA_CSS = `
|
|
|
11314
11149
|
background: color-mix(in srgb, var(--qa-primary) 14%, transparent);
|
|
11315
11150
|
display: grid;
|
|
11316
11151
|
place-items: center;
|
|
11317
|
-
color: color-mix(in srgb, var(--qa-primary) 80%, white 20%);
|
|
11152
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--qa-primary) 80%, white 20%));
|
|
11318
11153
|
transition: background 200ms ease, transform 200ms ease;
|
|
11319
11154
|
}
|
|
11320
11155
|
.pdp-qa-item[open] .pdp-qa-toggle {
|
|
@@ -11386,7 +11221,7 @@ var QA_CSS = `
|
|
|
11386
11221
|
}
|
|
11387
11222
|
}
|
|
11388
11223
|
`;
|
|
11389
|
-
var PdpQaLit = class extends
|
|
11224
|
+
var PdpQaLit = class extends LitElement28 {
|
|
11390
11225
|
constructor() {
|
|
11391
11226
|
super(...arguments);
|
|
11392
11227
|
this.data = void 0;
|
|
@@ -11398,7 +11233,7 @@ var PdpQaLit = class extends LitElement27 {
|
|
|
11398
11233
|
}
|
|
11399
11234
|
connectedCallback() {
|
|
11400
11235
|
super.connectedCallback();
|
|
11401
|
-
|
|
11236
|
+
ensureStylesInRoot(this, "syntro-pdp-qa-style", QA_CSS);
|
|
11402
11237
|
if (this._entering) this.requestUpdate();
|
|
11403
11238
|
}
|
|
11404
11239
|
disconnectedCallback() {
|
|
@@ -11421,19 +11256,19 @@ var PdpQaLit = class extends LitElement27 {
|
|
|
11421
11256
|
}
|
|
11422
11257
|
render() {
|
|
11423
11258
|
if (!this.data) {
|
|
11424
|
-
return
|
|
11259
|
+
return html29`<div data-skeleton class="pdp-qa-skeleton">
|
|
11425
11260
|
<div class="pdp-qa-skeleton-row"></div>
|
|
11426
11261
|
<div class="pdp-qa-skeleton-row"></div>
|
|
11427
11262
|
<div class="pdp-qa-skeleton-row"></div>
|
|
11428
11263
|
</div>`;
|
|
11429
11264
|
}
|
|
11430
|
-
return
|
|
11265
|
+
return html29`
|
|
11431
11266
|
<section
|
|
11432
11267
|
class="${this._entering ? "pdp-qa is-entering" : "pdp-qa"}"
|
|
11433
11268
|
aria-label="Frequently asked questions"
|
|
11434
11269
|
>
|
|
11435
11270
|
${this.data.items.map(
|
|
11436
|
-
(item, idx) =>
|
|
11271
|
+
(item, idx) => html29`
|
|
11437
11272
|
<details class="pdp-qa-item" ?open=${idx === 0}>
|
|
11438
11273
|
<summary class="pdp-qa-question">
|
|
11439
11274
|
<span>${item.question}</span>
|
|
@@ -11448,7 +11283,7 @@ var PdpQaLit = class extends LitElement27 {
|
|
|
11448
11283
|
</details>
|
|
11449
11284
|
`
|
|
11450
11285
|
)}
|
|
11451
|
-
${
|
|
11286
|
+
${nothing12}
|
|
11452
11287
|
</section>
|
|
11453
11288
|
`;
|
|
11454
11289
|
}
|
|
@@ -11462,15 +11297,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-qa"
|
|
|
11462
11297
|
}
|
|
11463
11298
|
|
|
11464
11299
|
// 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
|
-
}
|
|
11300
|
+
import { html as html30, LitElement as LitElement29 } from "lit";
|
|
11474
11301
|
var REGIONAL_CSS = `
|
|
11475
11302
|
.pdp-regional {
|
|
11476
11303
|
--rg-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -11548,7 +11375,7 @@ var REGIONAL_CSS = `
|
|
|
11548
11375
|
font-size: 11px;
|
|
11549
11376
|
letter-spacing: 0.12em;
|
|
11550
11377
|
text-transform: uppercase;
|
|
11551
|
-
color: color-mix(in srgb, var(--rg-primary) 75%, white 25%);
|
|
11378
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--rg-primary) 75%, white 25%));
|
|
11552
11379
|
font-weight: 500;
|
|
11553
11380
|
margin-bottom: 22px;
|
|
11554
11381
|
}
|
|
@@ -11580,7 +11407,7 @@ var REGIONAL_CSS = `
|
|
|
11580
11407
|
.pdp-regional-headline em {
|
|
11581
11408
|
font-style: italic;
|
|
11582
11409
|
font-variation-settings: 'SOFT' 100, 'WONK' 1;
|
|
11583
|
-
color: var(--rg-primary);
|
|
11410
|
+
color: var(--syntro-pdp-primary-ink, var(--rg-primary));
|
|
11584
11411
|
}
|
|
11585
11412
|
|
|
11586
11413
|
.pdp-regional-body {
|
|
@@ -11604,7 +11431,7 @@ var REGIONAL_CSS = `
|
|
|
11604
11431
|
font-family: var(--rg-display);
|
|
11605
11432
|
font-size: 14.5px;
|
|
11606
11433
|
font-variation-settings: 'SOFT' 70;
|
|
11607
|
-
color: color-mix(in srgb, var(--rg-primary) 65%, white 35%);
|
|
11434
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--rg-primary) 65%, white 35%));
|
|
11608
11435
|
line-height: 1.5;
|
|
11609
11436
|
}
|
|
11610
11437
|
.pdp-regional-footer-spark {
|
|
@@ -11650,7 +11477,7 @@ var REGIONAL_CSS = `
|
|
|
11650
11477
|
to { opacity: 1; transform: translateY(0); }
|
|
11651
11478
|
}
|
|
11652
11479
|
`;
|
|
11653
|
-
var PdpRegionalLit = class extends
|
|
11480
|
+
var PdpRegionalLit = class extends LitElement29 {
|
|
11654
11481
|
constructor() {
|
|
11655
11482
|
super(...arguments);
|
|
11656
11483
|
this.data = void 0;
|
|
@@ -11662,7 +11489,7 @@ var PdpRegionalLit = class extends LitElement28 {
|
|
|
11662
11489
|
}
|
|
11663
11490
|
connectedCallback() {
|
|
11664
11491
|
super.connectedCallback();
|
|
11665
|
-
|
|
11492
|
+
ensureStylesInRoot(this, "syntro-pdp-regional-style", REGIONAL_CSS);
|
|
11666
11493
|
if (this._entering) this.requestUpdate();
|
|
11667
11494
|
}
|
|
11668
11495
|
disconnectedCallback() {
|
|
@@ -11686,11 +11513,11 @@ var PdpRegionalLit = class extends LitElement28 {
|
|
|
11686
11513
|
}
|
|
11687
11514
|
render() {
|
|
11688
11515
|
if (!this.data) {
|
|
11689
|
-
return
|
|
11516
|
+
return html30`<div data-skeleton class="pdp-qa-skeleton" style="height:200px;">
|
|
11690
11517
|
<div class="pdp-qa-skeleton-row" style="height:200px;border-radius:24px;"></div>
|
|
11691
11518
|
</div>`;
|
|
11692
11519
|
}
|
|
11693
|
-
return
|
|
11520
|
+
return html30`
|
|
11694
11521
|
<section
|
|
11695
11522
|
class="${this._entering ? "pdp-regional is-entering" : "pdp-regional"}"
|
|
11696
11523
|
aria-label="Regional and temporal context"
|
|
@@ -11735,15 +11562,7 @@ if (typeof customElements !== "undefined" && !customElements.get("syntro-pdp-reg
|
|
|
11735
11562
|
}
|
|
11736
11563
|
|
|
11737
11564
|
// 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
|
-
}
|
|
11565
|
+
import { html as html31, LitElement as LitElement30 } from "lit";
|
|
11747
11566
|
var PEER_FEED_CSS = `
|
|
11748
11567
|
.pdp-peer-feed {
|
|
11749
11568
|
--pf-fg: var(--syntro-pdp-fg, hsl(35 15% 92%));
|
|
@@ -11859,14 +11678,14 @@ var PEER_FEED_CSS = `
|
|
|
11859
11678
|
}
|
|
11860
11679
|
.pdp-peer-quote::before {
|
|
11861
11680
|
content: '\u201C ';
|
|
11862
|
-
color: color-mix(in srgb, var(--pf-primary) 60%, transparent);
|
|
11681
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pf-primary) 60%, transparent));
|
|
11863
11682
|
font-size: 1.4em;
|
|
11864
11683
|
vertical-align: -0.2em;
|
|
11865
11684
|
margin-right: -2px;
|
|
11866
11685
|
}
|
|
11867
11686
|
.pdp-peer-quote::after {
|
|
11868
11687
|
content: ' \u201D';
|
|
11869
|
-
color: color-mix(in srgb, var(--pf-primary) 60%, transparent);
|
|
11688
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pf-primary) 60%, transparent));
|
|
11870
11689
|
font-size: 1.4em;
|
|
11871
11690
|
vertical-align: -0.2em;
|
|
11872
11691
|
margin-left: -2px;
|
|
@@ -11877,7 +11696,7 @@ var PEER_FEED_CSS = `
|
|
|
11877
11696
|
align-items: center;
|
|
11878
11697
|
gap: 7px;
|
|
11879
11698
|
font-size: 12.5px;
|
|
11880
|
-
color: color-mix(in srgb, var(--pf-primary) 75%, white 25%);
|
|
11699
|
+
color: var(--syntro-pdp-primary-ink, color-mix(in srgb, var(--pf-primary) 75%, white 25%));
|
|
11881
11700
|
font-style: italic;
|
|
11882
11701
|
font-family: var(--pf-display);
|
|
11883
11702
|
margin-top: 2px;
|
|
@@ -11947,7 +11766,7 @@ var PEER_FEED_CSS = `
|
|
|
11947
11766
|
}
|
|
11948
11767
|
}
|
|
11949
11768
|
`;
|
|
11950
|
-
var PdpPeerFeedLit = class extends
|
|
11769
|
+
var PdpPeerFeedLit = class extends LitElement30 {
|
|
11951
11770
|
constructor() {
|
|
11952
11771
|
super(...arguments);
|
|
11953
11772
|
this.data = void 0;
|
|
@@ -11959,7 +11778,7 @@ var PdpPeerFeedLit = class extends LitElement29 {
|
|
|
11959
11778
|
}
|
|
11960
11779
|
connectedCallback() {
|
|
11961
11780
|
super.connectedCallback();
|
|
11962
|
-
|
|
11781
|
+
ensureStylesInRoot(this, "syntro-pdp-peer-feed-style", PEER_FEED_CSS);
|
|
11963
11782
|
if (this._entering) this.requestUpdate();
|
|
11964
11783
|
}
|
|
11965
11784
|
disconnectedCallback() {
|
|
@@ -11982,19 +11801,19 @@ var PdpPeerFeedLit = class extends LitElement29 {
|
|
|
11982
11801
|
}
|
|
11983
11802
|
render() {
|
|
11984
11803
|
if (!this.data) {
|
|
11985
|
-
return
|
|
11804
|
+
return html31`<div data-skeleton class="pdp-peer-skeleton">
|
|
11986
11805
|
<div class="pdp-peer-skeleton-row"></div>
|
|
11987
11806
|
<div class="pdp-peer-skeleton-row"></div>
|
|
11988
11807
|
<div class="pdp-peer-skeleton-row"></div>
|
|
11989
11808
|
</div>`;
|
|
11990
11809
|
}
|
|
11991
|
-
return
|
|
11810
|
+
return html31`
|
|
11992
11811
|
<section
|
|
11993
11812
|
class="${this._entering ? "pdp-peer-feed is-entering" : "pdp-peer-feed"}"
|
|
11994
11813
|
aria-label="Users like you"
|
|
11995
11814
|
>
|
|
11996
11815
|
${this.data.peers.map(
|
|
11997
|
-
(peer) =>
|
|
11816
|
+
(peer) => html31`
|
|
11998
11817
|
<article class="pdp-peer-post">
|
|
11999
11818
|
<div class="pdp-peer-avatar" aria-hidden="true">${peer.initials}</div>
|
|
12000
11819
|
<div class="pdp-peer-body">
|
|
@@ -12031,7 +11850,8 @@ var TAGS = {
|
|
|
12031
11850
|
hero: "syntro-product-hero",
|
|
12032
11851
|
grid: "syntro-product-grid",
|
|
12033
11852
|
pdp: "syntro-pdp",
|
|
12034
|
-
reco: "syntro-product-reco-carousel"
|
|
11853
|
+
reco: "syntro-product-reco-carousel",
|
|
11854
|
+
details: "syntro-product-details"
|
|
12035
11855
|
};
|
|
12036
11856
|
if (typeof customElements !== "undefined") {
|
|
12037
11857
|
if (!customElements.get(TAGS.card)) customElements.define(TAGS.card, ProductCardLit);
|
|
@@ -12041,6 +11861,7 @@ if (typeof customElements !== "undefined") {
|
|
|
12041
11861
|
if (!customElements.get(TAGS.grid)) customElements.define(TAGS.grid, ProductGridLit);
|
|
12042
11862
|
if (!customElements.get(TAGS.pdp)) customElements.define(TAGS.pdp, SyntroPdpLit);
|
|
12043
11863
|
if (!customElements.get(TAGS.reco)) customElements.define(TAGS.reco, ProductRecoCarouselLit);
|
|
11864
|
+
if (!customElements.get(TAGS.details)) customElements.define(TAGS.details, ProductDetailsLit);
|
|
12044
11865
|
}
|
|
12045
11866
|
if (typeof document !== "undefined") startNativeSlotWatcher();
|
|
12046
11867
|
var _WIRED_RUNTIMES = /* @__PURE__ */ new WeakSet();
|
|
@@ -12081,17 +11902,21 @@ function onActivate(runtime2) {
|
|
|
12081
11902
|
const observer2 = wirePdpElements(runtime2, document);
|
|
12082
11903
|
return () => {
|
|
12083
11904
|
observer2.disconnect();
|
|
12084
|
-
const
|
|
12085
|
-
if (
|
|
11905
|
+
const i = _ACTIVE_OBSERVERS.indexOf(observer2);
|
|
11906
|
+
if (i >= 0) _ACTIVE_OBSERVERS.splice(i, 1);
|
|
12086
11907
|
_WIRED_RUNTIMES.delete(runtime2);
|
|
12087
11908
|
};
|
|
12088
11909
|
}
|
|
12089
|
-
function makeMountable(tag) {
|
|
11910
|
+
function makeMountable(tag, hooks) {
|
|
12090
11911
|
return {
|
|
12091
11912
|
mount(container, config) {
|
|
12092
|
-
const widgetProps =
|
|
11913
|
+
const widgetProps = resolveStorefrontProps(
|
|
11914
|
+
stripMountPlumbing(config ?? null),
|
|
11915
|
+
config?.runtime
|
|
11916
|
+
);
|
|
12093
11917
|
const el = document.createElement(tag);
|
|
12094
11918
|
el.props = widgetProps;
|
|
11919
|
+
hooks?.afterCreate?.(el, config);
|
|
12095
11920
|
container.appendChild(el);
|
|
12096
11921
|
return () => el.remove();
|
|
12097
11922
|
}
|
|
@@ -12101,8 +11926,13 @@ var ProductCardMountable = makeMountable(TAGS.card);
|
|
|
12101
11926
|
var ProductComparisonMountable = makeMountable(TAGS.comparison);
|
|
12102
11927
|
var ProductHeroMountable = makeMountable(TAGS.hero);
|
|
12103
11928
|
var ProductGridMountable = makeMountable(TAGS.grid);
|
|
12104
|
-
var SyntroPdpMountable = makeMountable(TAGS.pdp
|
|
11929
|
+
var SyntroPdpMountable = makeMountable(TAGS.pdp, {
|
|
11930
|
+
afterCreate: (el, config) => {
|
|
11931
|
+
el.runtimeRef = config?.runtime ?? null;
|
|
11932
|
+
}
|
|
11933
|
+
});
|
|
12105
11934
|
var SyntroRecoMountable = makeMountable(TAGS.reco);
|
|
11935
|
+
var ProductDetailsMountable = makeMountable(TAGS.details);
|
|
12106
11936
|
var runtime = {
|
|
12107
11937
|
id: "adaptive-product",
|
|
12108
11938
|
version: "1.0.0",
|
|
@@ -12185,6 +12015,15 @@ var runtime = {
|
|
|
12185
12015
|
description: "Horizontally-scrollable carousel of recommended products with image, price, hook copy, and PDP links.",
|
|
12186
12016
|
icon: "\u{1F3A0}"
|
|
12187
12017
|
}
|
|
12018
|
+
},
|
|
12019
|
+
{
|
|
12020
|
+
id: "adaptive-product:details",
|
|
12021
|
+
component: ProductDetailsMountable,
|
|
12022
|
+
metadata: {
|
|
12023
|
+
name: "Product details",
|
|
12024
|
+
description: "The store's own product details",
|
|
12025
|
+
icon: "\u{1F4CB}"
|
|
12026
|
+
}
|
|
12188
12027
|
}
|
|
12189
12028
|
]
|
|
12190
12029
|
};
|
|
@@ -12201,35 +12040,8 @@ export {
|
|
|
12201
12040
|
ProductGridMountable,
|
|
12202
12041
|
SyntroPdpMountable,
|
|
12203
12042
|
SyntroRecoMountable,
|
|
12043
|
+
ProductDetailsMountable,
|
|
12204
12044
|
runtime,
|
|
12205
12045
|
runtime_default
|
|
12206
12046
|
};
|
|
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
|
|
12047
|
+
//# sourceMappingURL=chunk-QNZXBFP5.js.map
|