@syntrologie/adapt-reviews 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/cdn.js +2 -1
- package/dist/cdn.js.map +1 -1
- package/dist/chunk-IGIU6S7P.js +4051 -0
- package/dist/chunk-IGIU6S7P.js.map +7 -0
- package/dist/{chunk-ATGQNO24.js → chunk-M64YCKZP.js} +614 -15
- package/dist/chunk-M64YCKZP.js.map +7 -0
- package/dist/reviewsFlip.d.ts +10 -0
- package/dist/reviewsFlip.d.ts.map +1 -1
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -1
- package/dist/schema.js +13 -10
- package/dist/schema.js.map +1 -1
- package/package.json +2 -3
- package/dist/chunk-ATGQNO24.js.map +0 -7
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/reviewDisplay.ts", "../../../canvas-sdk/src/flip.ts", "../src/reviewsFlip.ts", "../src/runtime.ts"],
|
|
4
|
-
"sourcesContent": ["import { html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\n\nexport type ReviewSource = 'imported' | 'submitted' | string;\n\nexport interface DisplayReview {\n /** Stable per-review id (dedup key). The reviews-tile hydrates by this. */\n id: string;\n product_id?: string | null;\n product_url?: string | null;\n rating: number;\n title?: string | null;\n body: string;\n reviewer_display_name?: string | null;\n verified_purchase?: boolean | null;\n created_at?: string | null;\n source: ReviewSource;\n}\n\nexport interface ReviewDisplayResponse {\n reviews: DisplayReview[];\n review_count: number;\n average_rating: number | null;\n}\n\nexport type SyntroAuthedFetch = (path: string, init?: RequestInit) => Promise<Response>;\nexport type LoadReviews = (\n limit: number,\n productId?: string | null\n) => Promise<ReviewDisplayResponse>;\n/** Hydrate an EXACT set of reviews by id (order preserved) \u2014 the id-driven\n * reviews tile's loader. Distinct from `LoadReviews` (recency/product scan). */\nexport type LoadReviewsByIds = (ids: string[]) => Promise<ReviewDisplayResponse>;\n\n/**\n * Host-page attribute a product page sets to declare which product the\n * reviews widget should scope to. Paired with the widget's\n * `productIdSelector` prop: the widget reads this attribute off the element\n * that selector matches. Keeping it a shared constant avoids drift between\n * the host page and the widget.\n */\nexport const PRODUCT_ID_ATTRIBUTE = 'data-syntro-product-id';\n\nexport async function fetchReviewDisplay(\n authedFetch: SyntroAuthedFetch,\n options: { limit?: number; productId?: string | null } = {}\n): Promise<ReviewDisplayResponse> {\n const limit = normalizeLimit(options.limit);\n const productId = options.productId?.trim();\n const productParam = productId ? `&product_id=${encodeURIComponent(productId)}` : '';\n const response = await authedFetch(`/api/adaptive/reviews?limit=${limit}${productParam}`, {\n method: 'GET',\n headers: { Accept: 'application/json' },\n });\n if (!response.ok) {\n throw new Error('Reviews are unavailable');\n }\n return (await response.json()) as ReviewDisplayResponse;\n}\n\nfunction normalizeLimit(limit: number | undefined): number {\n if (!Number.isFinite(limit)) return 20;\n return Math.min(50, Math.max(1, Math.trunc(limit as number)));\n}\n\n/**\n * Hydrate an exact, ordered set of reviews by id. The id-driven reviews tile\n * moves ONLY ids (the LLM never authors bodies); this fetches the real rows for\n * those ids from the runtime `?ids=` path. An empty id list resolves to an empty\n * response without a network call. Unknown ids are simply absent from the\n * response (the endpoint skips them, never 500s).\n */\nexport async function fetchReviewsByIds(\n authedFetch: SyntroAuthedFetch,\n ids: string[]\n): Promise<ReviewDisplayResponse> {\n const clean = ids.map((id) => id.trim()).filter((id) => id.length > 0);\n if (clean.length === 0) {\n return { reviews: [], review_count: 0, average_rating: null };\n }\n const idsParam = clean.map((id) => encodeURIComponent(id)).join(',');\n const response = await authedFetch(`/api/adaptive/reviews?ids=${idsParam}`, {\n method: 'GET',\n headers: { Accept: 'application/json' },\n });\n if (!response.ok) {\n throw new Error('Reviews are unavailable');\n }\n return (await response.json()) as ReviewDisplayResponse;\n}\n\nfunction defaultLoadReviews(\n limit: number,\n productId?: string | null\n): Promise<ReviewDisplayResponse> {\n const authedFetch = (window as unknown as { SynOS?: { authedFetch?: SyntroAuthedFetch } }).SynOS\n ?.authedFetch;\n if (!authedFetch) {\n return Promise.reject(new Error('Reviews are unavailable'));\n }\n return fetchReviewDisplay(authedFetch, { limit, productId });\n}\n\nfunction formatAverage(value: number | null): string {\n return typeof value === 'number' ? value.toFixed(1) : '0.0';\n}\n\nfunction reviewCountLabel(count: number): string {\n return count === 1 ? '1 review' : `${count} reviews`;\n}\n\nfunction createdAtLabel(value?: string | null): string | null {\n if (!value) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n return date.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n}\n\nexport class SyntroReviews extends LitElement {\n static override properties = {\n heading: { type: String },\n limit: { type: Number },\n productId: { type: String },\n productIdSelector: { type: String },\n loadReviews: { attribute: false },\n _isLoading: { state: true },\n _error: { state: true },\n _data: { state: true },\n };\n\n heading = 'Customer reviews';\n limit = 20;\n /** Explicit product to scope reviews to. Overridden by `productIdSelector` when that resolves. */\n productId?: string;\n /**\n * CSS selector for a host-page element carrying `data-syntro-product-id`.\n * When set and resolvable, the widget scopes reviews to that product \u2014 this\n * is how one config drives per-product reviews across many product pages.\n * When unset (or unresolved), reviews are workspace-wide.\n */\n productIdSelector?: string;\n loadReviews?: LoadReviews;\n\n private _isLoading = true;\n private _error: string | null = null;\n private _data: ReviewDisplayResponse | null = null;\n private _requestId = 0;\n private _lastProductId: string | null = null;\n private _anchorObserver?: MutationObserver;\n private _observedAnchor: Element | null = null;\n\n override createRenderRoot() {\n return this;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._syncAnchorObserver();\n void this._load();\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n this._anchorObserver?.disconnect();\n this._anchorObserver = undefined;\n this._observedAnchor = null;\n }\n\n override updated(changed: Map<string, unknown>): void {\n if (changed.has('productIdSelector')) this._syncAnchorObserver();\n const reloadKeys = ['limit', 'productId', 'productIdSelector'];\n if (reloadKeys.some((key) => changed.has(key)) && !changed.has('_isLoading')) {\n void this._load();\n }\n }\n\n /**\n * Resolve the host-page element the `productIdSelector` points at. Guarded:\n * an invalid selector must not throw and brick the widget \u2014 treat it as\n * unresolved.\n */\n private _resolveAnchorEl(): Element | null {\n if (!this.productIdSelector || typeof document === 'undefined') return null;\n try {\n return document.querySelector(this.productIdSelector);\n } catch {\n return null;\n }\n }\n\n /**\n * Resolve the product to scope reviews to. A `productIdSelector` element\n * carrying `data-syntro-product-id` wins; otherwise fall back to the\n * explicit `productId`; otherwise null.\n */\n private _resolveProductId(): string | null {\n const fromDom = this._resolveAnchorEl()?.getAttribute(PRODUCT_ID_ATTRIBUTE)?.trim();\n if (fromDom) return fromDom;\n const explicit = this.productId?.trim();\n return explicit ? explicit : null;\n }\n\n /**\n * Watch the anchor's `data-syntro-product-id` so the widget re-scopes on SPA\n * navigation. On a route like `/product/:slug`, the host reconciles the SAME\n * anchor node and just mutates the attribute \u2014 nothing re-triggers a Lit\n * prop change, so without this observer the widget would keep showing the\n * previous product's reviews.\n */\n private _syncAnchorObserver(): void {\n const anchor = this._resolveAnchorEl();\n if (anchor === this._observedAnchor) return;\n this._anchorObserver?.disconnect();\n this._observedAnchor = anchor;\n if (!anchor || typeof MutationObserver === 'undefined') return;\n this._anchorObserver = new MutationObserver(() => {\n if (this._resolveProductId() !== this._lastProductId) void this._load();\n });\n this._anchorObserver.observe(anchor, {\n attributes: true,\n attributeFilter: [PRODUCT_ID_ATTRIBUTE],\n });\n }\n\n private async _load(): Promise<void> {\n const requestId = ++this._requestId;\n const productId = this._resolveProductId();\n // Fail closed: when a productIdSelector is configured but nothing resolves,\n // do NOT fall back to a workspace-wide fetch \u2014 that would bleed other\n // products' reviews onto this page. Show an empty state; the anchor\n // observer re-fires _load once `data-syntro-product-id` appears.\n if (this.productIdSelector && !productId) {\n this._lastProductId = null;\n this._isLoading = false;\n this._error = null;\n this._data = { reviews: [], review_count: 0, average_rating: null };\n return;\n }\n this._lastProductId = productId;\n this._isLoading = true;\n this._error = null;\n try {\n const loader = this.loadReviews ?? defaultLoadReviews;\n const data = await loader(normalizeLimit(this.limit), productId);\n if (requestId !== this._requestId) return;\n this._data = data;\n } catch {\n if (requestId !== this._requestId) return;\n this._data = null;\n this._error = 'Reviews are unavailable';\n } finally {\n if (requestId === this._requestId) {\n this._isLoading = false;\n }\n }\n }\n\n override render() {\n const rootStyles: Record<string, string> = {\n display: 'block',\n width: '100%',\n color: 'var(--sc-tile-text, var(--sc-overlay-text-color, #111827))',\n fontFamily: 'var(--sc-font-family, inherit)',\n };\n\n return html`\n <section data-syntro-reviews style=${styleMap(rootStyles)} aria-label=${this.heading}>\n ${\n this._isLoading\n ? this._renderFrame(html`<div style=${styleMap(this._stateStyles())}>Loading...</div>`)\n : this._error\n ? this._renderFrame(\n html`<div style=${styleMap(this._stateStyles())}>${this._error}</div>`\n )\n : this._data && this._data.reviews.length > 0\n ? this._renderReviews(this._data)\n : this._renderFrame(\n html`<div style=${styleMap(this._stateStyles())}>No reviews yet</div>`\n )\n }\n </section>\n `;\n }\n\n private _renderFrame(content: unknown) {\n return html`\n <div style=${styleMap(this._panelStyles())}>\n <div style=${styleMap(this._headingStyles())}>${this.heading}</div>\n ${content}\n </div>\n `;\n }\n\n private _renderReviews(data: ReviewDisplayResponse) {\n return html`\n <div style=${styleMap(this._panelStyles())}>\n <div style=${styleMap(this._summaryStyles())}>\n <div>\n <div style=${styleMap(this._headingStyles())}>${this.heading}</div>\n <div style=${styleMap(this._metaStyles())}>${reviewCountLabel(data.review_count)}</div>\n </div>\n <div style=${styleMap(this._ratingBadgeStyles())} aria-label=${`${formatAverage(data.average_rating)} out of 5`}>\n <span style=${styleMap({ fontSize: '20px', fontWeight: '700' })}>${formatAverage(data.average_rating)}</span>\n <span aria-hidden=\"true\">\u2605</span>\n </div>\n </div>\n <div style=${styleMap({ display: 'grid', gap: '10px' })}>\n ${data.reviews.map((review) => this._renderReview(review))}\n </div>\n </div>\n `;\n }\n\n private _renderReview(review: DisplayReview) {\n const date = createdAtLabel(review.created_at);\n return html`\n <article style=${styleMap(this._reviewStyles())}>\n <div style=${styleMap(this._reviewHeaderStyles())}>\n <div style=${styleMap({ fontWeight: '700' })}>${review.title || 'Review'}</div>\n <div aria-label=${`${review.rating} out of 5`} style=${styleMap(this._starsStyles())}>\n ${'\u2605'.repeat(Math.max(0, Math.min(5, Math.round(review.rating))))}\n </div>\n </div>\n <p style=${styleMap(this._bodyStyles())}>${review.body}</p>\n <div style=${styleMap(this._metaStyles())}>\n ${review.reviewer_display_name || 'Customer'}\n ${review.verified_purchase ? html`<span> \u00B7 Verified purchase</span>` : nothing}\n ${date ? html`<span> \u00B7 ${date}</span>` : nothing}\n </div>\n </article>\n `;\n }\n\n private _panelStyles(): Record<string, string> {\n return {\n boxSizing: 'border-box',\n border: '1px solid var(--sc-tile-border-color, rgba(148, 163, 184, 0.24))',\n borderRadius: '8px',\n background: 'var(--sc-tile-background, #ffffff)',\n padding: '14px',\n boxShadow: 'var(--sc-tile-shadow, 0 8px 24px rgba(15, 23, 42, 0.08))',\n };\n }\n\n private _headingStyles(): Record<string, string> {\n return {\n fontSize: '15px',\n lineHeight: '20px',\n fontWeight: '700',\n margin: '0',\n };\n }\n\n private _summaryStyles(): Record<string, string> {\n return {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '12px',\n marginBottom: '12px',\n };\n }\n\n private _ratingBadgeStyles(): Record<string, string> {\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n color: 'var(--sc-accent, #2563eb)',\n whiteSpace: 'nowrap',\n };\n }\n\n private _reviewStyles(): Record<string, string> {\n return {\n borderTop: '1px solid var(--sc-tile-border-color, rgba(148, 163, 184, 0.2))',\n paddingTop: '10px',\n };\n }\n\n private _reviewHeaderStyles(): Record<string, string> {\n return {\n display: 'flex',\n alignItems: 'baseline',\n justifyContent: 'space-between',\n gap: '10px',\n fontSize: '14px',\n lineHeight: '20px',\n };\n }\n\n private _starsStyles(): Record<string, string> {\n return {\n color: 'var(--sc-accent, #2563eb)',\n fontSize: '13px',\n lineHeight: '18px',\n whiteSpace: 'nowrap',\n };\n }\n\n private _bodyStyles(): Record<string, string> {\n return {\n margin: '6px 0',\n fontSize: '14px',\n lineHeight: '20px',\n };\n }\n\n private _metaStyles(): Record<string, string> {\n return {\n color: 'var(--sc-muted-text, #64748b)',\n fontSize: '12px',\n lineHeight: '18px',\n };\n }\n\n private _stateStyles(): Record<string, string> {\n return {\n marginTop: '8px',\n color: 'var(--sc-muted-text, #64748b)',\n fontSize: '14px',\n lineHeight: '20px',\n };\n }\n}\n\nif (!customElements.get('syntro-reviews')) {\n customElements.define('syntro-reviews', SyntroReviews);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'syntro-reviews': SyntroReviews;\n }\n}\n", "/**\n * Shared canvas flip primitive \u2014 the front-list \u21C4 back-detail 3D takeover.\n *\n * A canvas tile that swaps between a LIST face and a single-item DETAIL face\n * does so with a 3D rotateY flip (mirroring the product card's `data-face`\n * idiom) so the two faces feel consistent inside velvet's bounded deck card.\n * The FAQ, the product card, and velvet each grew their own copy of this same\n * technique; this module is the single source they can share. The flip is a\n * canvas-LAYER presentation concern (layout + animation), not an adaptive one \u2014\n * so it lives in `@syntrologie/canvas-sdk`, not in any one widget.\n *\n * Two hard-won contracts are baked in and MUST be preserved by every consumer:\n *\n * 1. **The 3D machinery is gated to the animating window only.** perspective /\n * preserve-3d / rotateY / per-face backface exist ONLY while a flip runs;\n * at rest the active face renders FLAT (`transform:none`) and the inactive\n * face is `display:none`. A resting face left on a rotateY+perspective GPU\n * layer blanks its text on some Android GPUs and rasterizes it soft\n * everywhere (the same hazard the product card's variant flip hit).\n *\n * 2. **The back-face teardown timer is coupled to the CSS transition token.**\n * On flip-OUT the detail stays mounted through the rotation (never blanked\n * mid-flip); it clears on the container's own `animationend`, or \u2014 if that\n * event is dropped (backgrounded tab, engine quirk, reduced-motion) \u2014 a belt\n * timer whose delay is `>=` the computed `--vc-transition-duration`. A host\n * that sets a slower token must never blank the back face early.\n *\n * `prefers-reduced-motion` disables the rotation: the controller settles\n * synchronously (no animation to await).\n *\n * Everything is **namespace-parameterized** (`ns`) so each consumer keeps its\n * own attribute + keyframe names and velvet's generic `[data-active-face]`\n * rotate rule can't double-apply: FAQ uses `ns:'faq'` (`data-faq-*`,\n * `sc-faq-flip-*`), reviews use `ns:'rev'`, etc.\n */\n\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport type FlipFace = 'front' | 'back';\nexport type FlipDir = 'in' | 'out';\n\n// Flip transition window FALLBACK, used only when the computed\n// `--vc-transition-duration` token can't be read (no live layout, jsdom). The\n// velvet house tokens are ~240ms; we clear the open detail a little after that\n// so the back face isn't blanked before the flip visually completes.\nconst FLIP_TRANSITION_MS = 360;\n\n// Minimum extra time added on top of the CSS transition duration before the\n// back face is torn down, so the flip has visibly settled first.\nconst FLIP_CLEAR_BUFFER_MS = 120;\n\n/** Whether the visitor has asked for reduced motion \u2014 the flip is disabled and\n * the takeover swaps instantly instead of rotating. */\nexport function prefersReducedMotion(): boolean {\n if (typeof window === 'undefined') return false;\n return window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;\n}\n\nexport interface FlipStyleOptions {\n /** Attribute namespace, e.g. `faq` \u2192 `data-faq-flip`, `data-faq-active-face`. */\n ns: string;\n /** Keyframe name prefix, e.g. `sc-faq-flip` \u2192 `sc-faq-flip-in` / `-out`. */\n keyframePrefix: string;\n}\n\n/**\n * The self-injected flip CSS, scoped to `scope` (a selector such as\n * `[data-adaptive-id=\"\u2026\"]`) and namespaced by `opts.ns`. Returns a raw CSS\n * string \u2014 the caller wraps it in a `<style>` (light-DOM widgets) or an\n * adopted stylesheet.\n *\n * Two deliberate departures from a naive rotate rule (both required for depth\n * and safety, see the module doc):\n * - PERSPECTIVE lives on the PARENT viewport wrapper (a descendant's transform\n * gets no depth from perspective on the element that itself rotates), and is\n * ALSO folded into the rotate transform so depth survives a bounded\n * `overflow:hidden` ancestor.\n * - The active-face attribute is NAMESPACED so velvet's generic\n * `[data-active-face]` rule can't also reach this container.\n */\nexport function flipStyles(scope: string, opts: FlipStyleOptions): string {\n const f = opts.ns;\n const kf = opts.keyframePrefix;\n return `\n ${scope} [data-${f}-flip] {\n position: relative;\n }\n /* --- 3D machinery: ONLY while the flip is animating --- */\n /* Perspective (the composited GPU context) lives on the parent viewport,\n gated to the animating window so the RESTING open face never sits on a\n perspective layer (GPU/compositing hazard: rotateY+perspective at rest\n blanks text on some Android GPUs, rasterizes soft everywhere). */\n ${scope} [data-${f}-flip-viewport][data-${f}-flip-animating] {\n perspective: 1000px;\n }\n ${scope} [data-${f}-flip][data-${f}-flip-animating] {\n transform-style: preserve-3d;\n }\n ${scope} [data-${f}-flip][data-${f}-flip-animating] > [data-face] {\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n }\n ${scope} [data-${f}-flip][data-${f}-flip-animating] > [data-face=\"front\"] {\n position: relative;\n }\n ${scope} [data-${f}-flip][data-${f}-flip-animating] > [data-face=\"back\"] {\n position: absolute;\n inset: 0;\n transform: rotateY(180deg);\n }\n ${scope} [data-${f}-flip][data-${f}-flip-animating][data-${f}-flip-dir=\"in\"] {\n animation: ${kf}-in var(--vc-transition-duration, 240ms)\n var(--vc-transition-easing, ease-out) forwards;\n }\n ${scope} [data-${f}-flip][data-${f}-flip-animating][data-${f}-flip-dir=\"out\"] {\n animation: ${kf}-out var(--vc-transition-duration, 240ms)\n var(--vc-transition-easing, ease-out) forwards;\n }\n @keyframes ${kf}-in {\n from { transform: perspective(1000px) rotateY(0deg); }\n to { transform: perspective(1000px) rotateY(180deg); }\n }\n @keyframes ${kf}-out {\n from { transform: perspective(1000px) rotateY(180deg); }\n to { transform: perspective(1000px) rotateY(0deg); }\n }\n /* --- RESTING state: the active face is FLAT, the inactive face is gone --- */\n ${scope} [data-${f}-flip]:not([data-${f}-flip-animating]) {\n transform: none;\n }\n ${scope} [data-${f}-flip]:not([data-${f}-flip-animating]) > [data-face] {\n position: relative;\n transform: none;\n }\n ${scope} [data-${f}-flip]:not([data-${f}-flip-animating])[data-${f}-active-face=\"front\"] > [data-face=\"back\"] {\n display: none;\n }\n ${scope} [data-${f}-flip]:not([data-${f}-flip-animating])[data-${f}-active-face=\"back\"] > [data-face=\"front\"] {\n display: none;\n }\n @media (prefers-reduced-motion: reduce) {\n ${scope} [data-${f}-flip] { animation: none; }\n }\n `;\n}\n\nexport interface FlipControllerOptions {\n /**\n * animationName prefix \u2014 the running keyframes are `${keyframePrefix}-in` and\n * `${keyframePrefix}-out`. MUST match the `keyframePrefix` passed to\n * {@link flipStyles} so `onAnimationEnd` recognises the container's animation.\n */\n keyframePrefix: string;\n /** Fallback transition window (ms) when the CSS token can't be read. */\n fallbackMs?: number;\n /** Extra ms added on top of the token before back-face teardown. */\n clearBufferMs?: number;\n}\n\ntype HostEl = ReactiveControllerHost & HTMLElement;\n\n/**\n * Owns the flip state machine: which item is open (`openId`), which face is\n * active, whether a rotation is running, and the settle timer coupled to the\n * CSS transition token. A consumer widget renders two faces (front list + back\n * detail) and drives them from this controller's state; the controller calls\n * `host.requestUpdate()` on every transition so the render stays in sync.\n *\n * The controller is deliberately UI-agnostic: focus management, row selectors,\n * and the meaning of an id are the widget's concern. It exposes exactly the\n * transitions every consumer needs \u2014 {@link openTo}, {@link back},\n * {@link reset}, {@link onAnimationEnd} \u2014 and no more.\n */\nexport class FlipController implements ReactiveController {\n private readonly host: HostEl;\n private readonly keyframePrefix: string;\n private readonly fallbackMs: number;\n private readonly clearBufferMs: number;\n\n /** Open item id (null = list view). Kept populated through a flip-OUT so the\n * back face never blanks mid-rotation; cleared once the flip settles. */\n openId: string | null = null;\n /** Which face is showing. Both faces stay mounted; this drives the CSS. */\n activeFace: FlipFace = 'front';\n /** True ONLY while a rotation runs \u2014 the window the 3D machinery exists for. */\n animating = false;\n /** Which keyframes run: 'in' = front\u2192back (open), 'out' = back\u2192front (close). */\n dir: FlipDir = 'in';\n\n // Belt timer that force-SETTLES the flip if the container's `animationend`\n // never fires; also the point a flip-OUT finally clears `openId`.\n private settleTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(host: HostEl, opts: FlipControllerOptions) {\n this.host = host;\n this.keyframePrefix = opts.keyframePrefix;\n this.fallbackMs = opts.fallbackMs ?? FLIP_TRANSITION_MS;\n this.clearBufferMs = opts.clearBufferMs ?? FLIP_CLEAR_BUFFER_MS;\n host.addController(this);\n }\n\n hostDisconnected(): void {\n this.clearTimer();\n }\n\n /**\n * Open `id` to the DETAIL (back) face and flip to it. A re-open before a\n * previous flip settled clears the pending settle and restarts cleanly. Under\n * reduced motion, settles straight to the flat back face (no animation).\n */\n openTo(id: string): void {\n this.clearTimer();\n this.openId = id;\n this.activeFace = 'back';\n if (prefersReducedMotion()) {\n this.animating = false;\n this.host.requestUpdate();\n return;\n }\n this.dir = 'in';\n this.animating = true;\n this.settleTimer = setTimeout(this.settle, this.durationMs());\n this.host.requestUpdate();\n }\n\n /**\n * Flip from the DETAIL (back) face back to the LIST (front) face. With motion,\n * `openId` is kept through the rotation (back face stays populated, never\n * blank mid-flip) and cleared on settle; under reduced motion it clears\n * synchronously.\n */\n back(): void {\n this.activeFace = 'front';\n this.clearTimer();\n if (prefersReducedMotion()) {\n this.animating = false;\n this.openId = null;\n } else {\n this.dir = 'out';\n this.animating = true;\n this.settleTimer = setTimeout(this.settle, this.durationMs());\n }\n this.host.requestUpdate();\n }\n\n /**\n * Hard reset to the front face \u2014 the open item is gone (removed / gated out),\n * so there's nothing to keep mounted through a flip-out. Clears the timer and\n * settles synchronously.\n */\n reset(): void {\n this.clearTimer();\n this.animating = false;\n this.openId = null;\n this.activeFace = 'front';\n this.host.requestUpdate();\n }\n\n /** animationend on the flip container \u2192 the rotation is done, render flat. */\n onAnimationEnd = (e: AnimationEvent): void => {\n if (\n e.animationName === `${this.keyframePrefix}-in` ||\n e.animationName === `${this.keyframePrefix}-out`\n ) {\n this.settle();\n }\n };\n\n /** Tear down the 3D machinery so the shown face renders FLAT. A flip-OUT also\n * clears `openId` here \u2014 the moment it's safe to empty the back face. */\n private settle = (): void => {\n this.clearTimer();\n this.animating = false;\n if (this.activeFace === 'front') {\n this.openId = null;\n }\n this.host.requestUpdate();\n };\n\n private clearTimer(): void {\n if (this.settleTimer !== null) {\n clearTimeout(this.settleTimer);\n this.settleTimer = null;\n }\n }\n\n /**\n * The flip transition duration in ms, coupled to the CSS. Reads the computed\n * `--vc-transition-duration` token off the host so the JS tear-down timer is\n * ALWAYS `>=` the CSS transition \u2014 a host that sets the token slower than our\n * fallback no longer blanks the back face before the flip finishes. Falls\n * back to `fallbackMs` when the token is absent/unreadable/malformed.\n */\n private durationMs(): number {\n let tokenMs = 0;\n if (typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {\n try {\n const raw = window\n .getComputedStyle(this.host)\n .getPropertyValue('--vc-transition-duration')\n .trim();\n if (raw.endsWith('ms')) tokenMs = Number.parseFloat(raw);\n else if (raw.endsWith('s')) tokenMs = Number.parseFloat(raw) * 1000;\n } catch {\n // getComputedStyle can throw on a detached element \u2014 fall through.\n }\n }\n // A malformed token (e.g. \"abcms\") makes parseFloat return NaN. Left\n // unguarded, Math.max(NaN, fallback) is NaN \u2192 setTimeout(\u2026, NaN) \u2248 0, which\n // clears openId instantly and blanks the back face mid-flip \u2014 the exact bug\n // this method exists to prevent. Treat any non-finite token as absent.\n if (!Number.isFinite(tokenMs)) tokenMs = 0;\n return Math.max(tokenMs, this.fallbackMs) + (tokenMs > 0 ? this.clearBufferMs : 0);\n }\n}\n", "/**\n * adaptive-reviews \u2014 SyntroReviewsFlip (`<syntro-reviews-flip>`)\n *\n * The id-driven reviews TILE: a dumb, FAQ-style flip surface. It takes an\n * ordered list of review **ids** + an LLM-authored heading, hydrates the real\n * rows by id, and renders one line per review on the FRONT face; tapping a line\n * FLIPS the card to that review's full body on the BACK face. It carries NO\n * query and NO selection logic \u2014 `get_reviews(query)` (the semantic tool) does\n * all of that and hands the tile ids; the tile only ever moves ids and renders\n * real, server-hydrated bodies (the model never authors review text).\n *\n * The 3D flip is the shared canvas primitive (`@syntrologie/canvas-sdk/flip`),\n * namespaced `rev` (`data-rev-*` / `sc-rev-flip-*`) so velvet's generic\n * `[data-active-face]` rule and the FAQ's `data-faq-*` rules can't reach it.\n * Light DOM so the host page's `--sc-*` theme tokens apply.\n */\n\nimport { FlipController, flipStyles } from '@syntrologie/canvas-sdk/flip';\nimport { html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport {\n type DisplayReview,\n fetchReviewsByIds,\n type LoadReviewsByIds,\n type ReviewDisplayResponse,\n type SyntroAuthedFetch,\n} from './reviewDisplay.js';\n\nconst DEFAULT_INSTANCE_ID = 'reviews-flip';\n\n// Monotonic counter for generated scope uids (default-instanceId fallback), so\n// two default-id tiles don't cross-scope their injected <style>.\nlet _revUidCounter = 0;\n\nfunction starLabel(rating: number): string {\n return '\u2605'.repeat(Math.max(0, Math.min(5, Math.round(rating))));\n}\n\nfunction createdAtLabel(value?: string | null): string | null {\n if (!value) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });\n}\n\n/** The one-line teaser for a front-face row: the title if present, else a\n * trimmed first line of the body. The full body only ever shows on the back. */\nfunction reviewOneLiner(review: DisplayReview): string {\n const title = review.title?.trim();\n if (title) return title;\n const body = (review.body ?? '').trim().replace(/\\s+/g, ' ');\n return body.length > 90 ? `${body.slice(0, 89)}\u2026` : body;\n}\n\nfunction defaultLoadReviewsByIds(ids: string[]): Promise<ReviewDisplayResponse> {\n const authedFetch = (window as unknown as { SynOS?: { authedFetch?: SyntroAuthedFetch } }).SynOS\n ?.authedFetch;\n if (!authedFetch) {\n return Promise.reject(new Error('Reviews are unavailable'));\n }\n return fetchReviewsByIds(authedFetch, ids);\n}\n\nexport class SyntroReviewsFlip extends LitElement {\n static override properties = {\n heading: { type: String },\n reviewIds: { attribute: false },\n max: { type: Number },\n instanceId: { type: String },\n loadReviewsByIds: { attribute: false },\n _isLoading: { state: true },\n _error: { state: true },\n _reviews: { state: true },\n };\n\n /** LLM-authored, grounded framing (\"What runners say about the ventilation\").\n * The tile renders it verbatim; it never fabricates one. */\n heading = 'Customer reviews';\n /** The ordered ids to hydrate + show. The ONLY thing the LLM moves in. */\n reviewIds: string[] = [];\n /** Optional cap on how many of `reviewIds` to render. */\n max?: number;\n instanceId: string = DEFAULT_INSTANCE_ID;\n /** Injectable loader (tests / non-SynOS hosts); defaults to the `?ids=` fetch. */\n loadReviewsByIds?: LoadReviewsByIds;\n\n private _isLoading = true;\n private _error: string | null = null;\n private _reviews: DisplayReview[] = [];\n private _requestId = 0;\n private _generatedUid: string | null = null;\n\n // The flip state machine (open id, active face, animating, settle timer, the\n // token-coupled GPU-teardown + reduced-motion contract) is the shared\n // controller; `sc-rev-flip` matches what _renderFlipStyles emits.\n private readonly _flip = new FlipController(this, { keyframePrefix: 'sc-rev-flip' });\n\n // a11y focus intent, consumed in updated(): move focus to the \u2039 Back button on\n // flip-to-back, and back to the originating row on flip-to-front.\n private _pendingFocus: 'front' | 'back' | null = null;\n private _focusReturnId: string | null = null;\n\n override createRenderRoot() {\n return this;\n }\n\n // (Re)hydrate when the id set changes. Doing this in willUpdate (not updated)\n // keeps the synchronous `_isLoading` flip inside the SAME update cycle \u2014 a\n // reactive write in updated() would schedule a wasteful follow-up update. The\n // async resolution's state write schedules its own (expected) update. Guarded\n // to `reviewIds`/`max` so the loader's own `_reviews`/`_isLoading` writes can't\n // re-trigger a load. The first update cycle includes any pre-connection props,\n // so this also covers initial mount without a separate connectedCallback load.\n override willUpdate(changed: Map<string, unknown>): void {\n if (changed.has('reviewIds') || changed.has('max')) {\n void this._load();\n }\n }\n\n override updated(): void {\n this._applyPendingFocus();\n }\n\n private get _scopeId(): string {\n if (this.instanceId && this.instanceId !== DEFAULT_INSTANCE_ID) return this.instanceId;\n if (this._generatedUid === null) {\n _revUidCounter += 1;\n this._generatedUid = `rev-${_revUidCounter}`;\n }\n return this._generatedUid;\n }\n\n /** The ids to actually render (capped by `max` when set). */\n private _effectiveIds(): string[] {\n const ids = (this.reviewIds ?? []).map((id) => id.trim()).filter((id) => id.length > 0);\n if (typeof this.max === 'number' && this.max >= 0) return ids.slice(0, this.max);\n return ids;\n }\n\n private async _load(): Promise<void> {\n const requestId = ++this._requestId;\n const ids = this._effectiveIds();\n if (ids.length === 0) {\n this._isLoading = false;\n this._error = null;\n this._reviews = [];\n return;\n }\n this._isLoading = true;\n this._error = null;\n try {\n const loader = this.loadReviewsByIds ?? defaultLoadReviewsByIds;\n const data = await loader(ids);\n if (requestId !== this._requestId) return;\n // Preserve the requested order (the LLM's ranking); the endpoint should\n // already do this, but re-order defensively and drop unknown ids.\n const byId = new Map(data.reviews.map((r) => [r.id, r]));\n this._reviews = ids.map((id) => byId.get(id)).filter((r): r is DisplayReview => Boolean(r));\n } catch {\n if (requestId !== this._requestId) return;\n this._reviews = [];\n this._error = 'Reviews are unavailable';\n } finally {\n if (requestId === this._requestId) this._isLoading = false;\n }\n }\n\n private _applyPendingFocus(): void {\n if (this._pendingFocus === null) return;\n const target = this._pendingFocus;\n this._pendingFocus = null;\n if (target === 'back') {\n this.querySelector<HTMLElement>('[data-rev-back]')?.focus();\n return;\n }\n const id = this._focusReturnId;\n this._focusReturnId = null;\n if (!id) return;\n this.querySelector<HTMLElement>(`[data-rev-item-id=\"${cssAttrEscape(id)}\"] button`)?.focus();\n }\n\n private _open(id: string): void {\n this._pendingFocus = 'back';\n this._focusReturnId = id;\n this._flip.openTo(id);\n }\n\n private _back(): void {\n this._pendingFocus = 'front';\n this._flip.back();\n }\n\n override render() {\n const rootStyles: Record<string, string> = {\n display: 'block',\n width: '100%',\n color: 'var(--sc-content-text-color, #111827)',\n fontFamily: 'var(--sc-font-family, inherit)',\n };\n\n return html`\n <section\n data-syntro-reviews-flip\n data-adaptive-id=${this._scopeId}\n data-adaptive-type=\"adaptive-reviews-flip\"\n style=${styleMap(rootStyles)}\n aria-label=${this.heading}\n >\n ${this._renderBody()}\n </section>\n `;\n }\n\n private _renderBody() {\n if (this._isLoading) return this._renderFrame(this._renderState('Loading\u2026'));\n if (this._error) return this._renderFrame(this._renderState(this._error));\n if (this._reviews.length === 0) return this._renderFrame(this._renderState('No reviews yet'));\n return this._renderFlip();\n }\n\n private _renderFrame(content: unknown) {\n return html`\n <div style=${styleMap(this._panelStyles())}>${content}</div>\n `;\n }\n\n /**\n * Loading / empty / error state. Previously a bare unpadded line that hugged\n * the tile's top-left corner (\"Loading\u2026\" with no padding, off-theme). Now the\n * message sits CENTERED with padding in an at-least-88px box (height:100% is\n * inert here \u2014 the host/panel chain isn't definite-height \u2014 so minHeight\n * governs; it's a padded, centered placeholder, not stray text). role=\"status\"\n * announces the loading/error line to assistive tech (was silent).\n */\n private _renderState(text: string) {\n return html`<div\n role=\"status\"\n style=${styleMap({\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n minHeight: '88px',\n padding: '20px 16px',\n textAlign: 'center',\n })}\n ><span style=${styleMap(this._stateStyles())}>${text}</span></div>`;\n }\n\n private _renderFlip() {\n const scope = `[data-adaptive-id=\"${this._scopeId}\"]`;\n const openReview =\n this._flip.openId !== null\n ? this._reviews.find((r) => r.id === this._flip.openId)\n : undefined;\n const activeFace = openReview ? this._flip.activeFace : 'front';\n const backActive = activeFace === 'back';\n const animating = this._flip.animating;\n\n return html`\n <div style=${styleMap(this._panelStyles())}>\n <style>${flipStyles(scope, { ns: 'rev', keyframePrefix: 'sc-rev-flip' })}</style>\n <div data-rev-flip-viewport ?data-rev-flip-animating=${animating}>\n <div\n data-rev-flip\n data-rev-active-face=${activeFace}\n ?data-rev-flip-animating=${animating}\n data-rev-flip-dir=${animating ? this._flip.dir : nothing}\n @animationend=${this._flip.onAnimationEnd}\n >\n <div data-face=\"front\" ?inert=${backActive} aria-hidden=${backActive}>\n ${this._renderListFace()}\n </div>\n <div data-face=\"back\" ?inert=${!backActive} aria-hidden=${!backActive}>\n ${openReview ? this._renderDetail(openReview) : nothing}\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n private _renderListFace() {\n return html`\n <div\n style=${styleMap({\n display: 'grid',\n // Cap the column at the container width (min 0) so a nowrap one-liner\n // can't expand the grid past the tile \u2014 otherwise the row (and its \u203A\n // chevron) overflows the bounded deck card and the ellipsis never\n // engages.\n gridTemplateColumns: 'minmax(0, 1fr)',\n gap: 'var(--sc-content-item-gap, 6px)',\n })}\n >\n ${this._reviews.map((review) => this._renderRow(review))}\n </div>\n `;\n }\n\n private _renderRow(review: DisplayReview) {\n return html`\n <div style=${styleMap({ position: 'relative', minWidth: '0' })} data-rev-item-id=${review.id}>\n <button type=\"button\" style=${styleMap(this._rowStyles())} @click=${() => this._open(review.id)}>\n <span style=${styleMap(this._starsStyles())} aria-label=${`${review.rating} out of 5`}>\n ${starLabel(review.rating)}\n </span>\n <span style=${styleMap(this._oneLinerStyles())}>${reviewOneLiner(review)}</span>\n <span style=${styleMap(this._chevronStyles())}>\u203A</span>\n </button>\n </div>\n `;\n }\n\n private _renderDetail(review: DisplayReview) {\n const date = createdAtLabel(review.created_at);\n return html`\n <div style=${styleMap(this._detailCardStyles())} data-rev-detail=${review.id}>\n <button\n type=\"button\"\n data-rev-back\n style=${styleMap(this._backStyles())}\n aria-label=\"Back to reviews\"\n @click=${() => this._back()}\n >\n \u2039 Back to reviews\n </button>\n <div style=${styleMap(this._detailHeaderStyles())}>\n <div style=${styleMap({ fontWeight: '700', color: 'var(--sc-content-text-color, inherit)' })}>\n ${review.title || 'Review'}\n </div>\n <div style=${styleMap(this._starsStyles())} aria-label=${`${review.rating} out of 5`}>\n ${starLabel(review.rating)}\n </div>\n </div>\n <p style=${styleMap(this._bodyStyles())}>${review.body}</p>\n <div style=${styleMap(this._metaStyles())}>\n ${review.reviewer_display_name || 'Customer'}\n ${review.verified_purchase ? html`<span> \u00B7 Verified purchase</span>` : nothing}\n ${date ? html`<span> \u00B7 ${date}</span>` : nothing}\n </div>\n </div>\n `;\n }\n\n // Transparent container \u2014 the tile lives inside the canvas deck card, which\n // provides the surface chrome (bg/border/shadow). Like the FAQ, we don't draw\n // a second card; the rows are self-styled item cards (see _rowStyles).\n private _panelStyles(): Record<string, string> {\n return { boxSizing: 'border-box' };\n }\n\n // A FAQ-style item card (same --sc-content-* language as the FAQ questions) so\n // the two tiles read as one family in the deck.\n private _rowStyles(): Record<string, string> {\n return {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n width: '100%',\n boxSizing: 'border-box',\n padding: 'var(--sc-content-item-padding, 10px 14px)',\n borderRadius: 'var(--sc-content-item-border-radius, 10px)',\n border: 'var(--sc-content-border, 1px solid rgba(0, 0, 0, 0.08))',\n background: 'var(--sc-content-background, rgba(255, 255, 255, 0.5))',\n textAlign: 'left',\n cursor: 'pointer',\n color: 'var(--sc-content-text-color, inherit)',\n font: 'inherit',\n };\n }\n\n private _oneLinerStyles(): Record<string, string> {\n return {\n flex: '1 1 auto',\n minWidth: '0',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n fontSize: '14px',\n lineHeight: '20px',\n };\n }\n\n private _starsStyles(): Record<string, string> {\n return {\n // Brand accent \u2014 the SAME token the FAQ uses for its primary marks, so the\n // rating follows the workspace brand (not a hardcoded blue).\n color: 'var(--sc-color-primary, #723b32)',\n fontSize: '13px',\n lineHeight: '18px',\n whiteSpace: 'nowrap',\n };\n }\n\n private _chevronStyles(): Record<string, string> {\n return { color: 'var(--sc-content-chevron-color, currentColor)', flex: '0 0 auto' };\n }\n\n // The flipped-open review is a padded card in the same --sc-content-* language\n // as the front-face row cards \u2014 otherwise the review title + body sit cramped\n // against the tile edge with no padding.\n private _detailCardStyles(): Record<string, string> {\n return {\n display: 'grid',\n gap: '8px',\n boxSizing: 'border-box',\n padding: 'var(--sc-content-item-padding, 10px 14px)',\n borderRadius: 'var(--sc-content-item-border-radius, 10px)',\n border: 'var(--sc-content-border, 1px solid rgba(0, 0, 0, 0.08))',\n background: 'var(--sc-content-background, rgba(255, 255, 255, 0.5))',\n };\n }\n\n private _backStyles(): Record<string, string> {\n return {\n justifySelf: 'start',\n padding: '4px 0',\n background: 'transparent',\n border: 'none',\n cursor: 'pointer',\n color: 'var(--sc-content-text-secondary-color, #64748b)',\n font: 'inherit',\n fontSize: '13px',\n fontWeight: '600',\n };\n }\n\n private _detailHeaderStyles(): Record<string, string> {\n return {\n display: 'flex',\n alignItems: 'baseline',\n justifyContent: 'space-between',\n gap: '10px',\n };\n }\n\n private _bodyStyles(): Record<string, string> {\n return {\n margin: '0',\n fontSize: 'var(--sc-content-body-font-size, 14px)',\n lineHeight: '20px',\n color: 'var(--sc-content-text-secondary-color, inherit)',\n };\n }\n\n private _metaStyles(): Record<string, string> {\n return {\n color: 'var(--sc-content-text-secondary-color, #64748b)',\n fontSize: '12px',\n lineHeight: '18px',\n };\n }\n\n private _stateStyles(): Record<string, string> {\n return {\n margin: '0',\n color: 'var(--sc-content-text-secondary-color, #64748b)',\n fontSize: '14px',\n lineHeight: '20px',\n };\n }\n}\n\n/** Escape a value for safe use inside a double-quoted attribute selector. */\nfunction cssAttrEscape(value: string): string {\n return value.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\nif (!customElements.get('syntro-reviews-flip')) {\n customElements.define('syntro-reviews-flip', SyntroReviewsFlip);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'syntro-reviews-flip': SyntroReviewsFlip;\n }\n}\n", "/**\n * adaptive-reviews \u2014 Runtime manifest (flat shape)\n *\n * Exports the runtime descriptor consumed by the SDK's AppLoader. Importing\n * this module registers the <syntro-reviews> and <syntro-reviews-flip> custom\n * elements as a side effect. This is a lazy CDN adaptive (see\n * `syntrologie.bundlingMode: cdn-only`), so it must NOT import runtime-sdk\n * internals \u2014 the flat shape here is wrapped into a manifest by `cdn.ts` before\n * self-registering.\n */\n\nimport './reviewDisplay.js';\nimport type { ReviewDisplayResponse, SyntroReviews } from './reviewDisplay.js';\nimport './reviewsFlip.js';\nimport type { LoadReviewsByIds } from './reviewDisplay.js';\nimport type { SyntroReviewsFlip } from './reviewsFlip.js';\n\nexport const REVIEWS_ADAPTIVE_APP_ID = 'adaptive-reviews';\nexport const REVIEWS_ADAPTIVE_WIDGET_ID = 'adaptive-reviews:reviews';\n/** The id-driven FAQ-style flip tile \u2014 the concierge mounts this with a list of\n * review ids + a grounded heading (see `get_reviews` + `mount_adaptive_reviews`). */\nexport const REVIEWS_FLIP_WIDGET_ID = 'adaptive-reviews:reviews-flip';\n\ntype ReviewLoader = (limit: number, productId?: string | null) => Promise<ReviewDisplayResponse>;\n\nfunction asPositiveLimit(value: unknown): number | undefined {\n const numeric =\n typeof value === 'number' ? value : typeof value === 'string' ? Number(value) : NaN;\n if (!Number.isFinite(numeric)) return undefined;\n return Math.max(1, Math.floor(numeric));\n}\n\nfunction applyReviewsConfig(el: SyntroReviews, config?: Record<string, unknown> | null): void {\n if (!config) return;\n\n if (typeof config.heading === 'string') {\n el.heading = config.heading;\n }\n\n const limit = asPositiveLimit(config.limit);\n if (limit !== undefined) {\n el.limit = limit;\n }\n\n if (typeof config.productId === 'string') {\n el.productId = config.productId;\n }\n\n if (typeof config.productIdSelector === 'string') {\n el.productIdSelector = config.productIdSelector;\n }\n\n if (typeof config.loadReviews === 'function') {\n el.loadReviews = config.loadReviews as ReviewLoader;\n }\n}\n\n/**\n * Mountable widget: receives the tile container + validated props, returns an\n * unmount fn. Same contract as the other adaptives' mountables.\n */\nexport const reviewsWidget = {\n mount(container: HTMLElement, config?: Record<string, unknown> | null) {\n const el = document.createElement('syntro-reviews') as SyntroReviews;\n applyReviewsConfig(el, config);\n container.appendChild(el);\n return () => el.remove();\n },\n\n update(container: HTMLElement, config?: Record<string, unknown> | null) {\n const el = container.querySelector('syntro-reviews') as SyntroReviews | null;\n if (el) applyReviewsConfig(el, config);\n },\n};\n\n/** Normalize `review_ids` to a string[] \u2014 accepts an array or a comma string\n * (SDK config / tool payloads have arrived both ways). */\nfunction asIdList(value: unknown): string[] | undefined {\n if (Array.isArray(value)) {\n return value.map((v) => String(v).trim()).filter((v) => v.length > 0);\n }\n if (typeof value === 'string') {\n return value\n .split(',')\n .map((v) => v.trim())\n .filter((v) => v.length > 0);\n }\n return undefined;\n}\n\nfunction applyReviewsFlipConfig(\n el: SyntroReviewsFlip,\n config?: Record<string, unknown> | null\n): void {\n if (!config) return;\n // The mount authors `title` (lifted to the SyntroTileCard header like every\n // other tile). Accept legacy `heading` too. This only feeds the section\n // aria-label now \u2014 the visible title is the card chrome's job, not ours.\n if (typeof config.title === 'string') el.heading = config.title;\n else if (typeof config.heading === 'string') el.heading = config.heading;\n // Accept both `review_ids` (mount payload / schema) and `reviewIds` (JS prop).\n const ids = asIdList(config.review_ids) ?? asIdList(config.reviewIds);\n if (ids !== undefined) el.reviewIds = ids;\n const max = asPositiveLimit(config.max);\n if (max !== undefined) el.max = max;\n if (typeof config.instanceId === 'string') el.instanceId = config.instanceId;\n if (typeof config.loadReviewsByIds === 'function') {\n el.loadReviewsByIds = config.loadReviewsByIds as LoadReviewsByIds;\n }\n}\n\n/**\n * Mountable for the id-driven flip tile. `review_ids` in \u2192 hydrate by id \u2192\n * render the FAQ-style flip. No query/selection here; the tile is dumb.\n */\nexport const reviewsFlipWidget = {\n mount(container: HTMLElement, config?: Record<string, unknown> | null) {\n const el = document.createElement('syntro-reviews-flip') as SyntroReviewsFlip;\n applyReviewsFlipConfig(el, config);\n container.appendChild(el);\n return () => el.remove();\n },\n\n update(container: HTMLElement, config?: Record<string, unknown> | null) {\n const el = container.querySelector('syntro-reviews-flip') as SyntroReviewsFlip | null;\n if (el) applyReviewsFlipConfig(el, config);\n },\n};\n\nexport const runtime = {\n id: REVIEWS_ADAPTIVE_APP_ID,\n version: '0.1.0',\n name: 'Reviews',\n description: 'Display imported and submitted customer reviews through the runtime SDK.',\n\n /** No DOM-mutation executors \u2014 this widget renders only. */\n executors: [],\n\n widgets: [\n {\n id: REVIEWS_ADAPTIVE_WIDGET_ID,\n component: reviewsWidget,\n metadata: {\n name: 'Reviews',\n description: 'Display customer reviews from the adaptive review endpoint.',\n icon: '\\u{1F4AC}',\n },\n },\n {\n id: REVIEWS_FLIP_WIDGET_ID,\n component: reviewsFlipWidget,\n metadata: {\n name: 'Reviews (flip)',\n description:\n 'Id-driven reviews tile: supply review_ids from get_reviews under a grounded heading; it hydrates and renders the real review bodies by id.',\n icon: '\\u{1F4AC}',\n subtitle: 'From verified buyers.',\n },\n },\n ],\n};\n\n// Back-compat alias for existing importers/tests.\nexport const reviewsAdaptiveRuntime = runtime;\n\nexport default runtime;\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["html", "LitElement", "nothing", "styleMap", "createdAtLabel", "LitElement", "html", "styleMap", "nothing"]
|
|
7
|
-
}
|