@encorekit/web-sdk 2.0.0 → 2.0.2

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.
Files changed (41) hide show
  1. package/README.md +104 -95
  2. package/dist/cjs/index.cjs +1 -1
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/esm/index.js +1 -1
  5. package/dist/esm/index.js.map +1 -1
  6. package/dist/umd/encore.min.js +1 -1
  7. package/dist/umd/encore.min.js.map +1 -1
  8. package/package.json +1 -1
  9. package/types/analytics/events.d.ts +7 -28
  10. package/types/analytics/events.d.ts.map +1 -1
  11. package/types/api/APIClient.d.ts +10 -6
  12. package/types/api/APIClient.d.ts.map +1 -1
  13. package/types/api/models.d.ts +8 -3
  14. package/types/api/models.d.ts.map +1 -1
  15. package/types/core/Configuration.d.ts +0 -4
  16. package/types/core/Configuration.d.ts.map +1 -1
  17. package/types/core/Encore.d.ts +8 -33
  18. package/types/core/Encore.d.ts.map +1 -1
  19. package/types/core/PlacementBuilder.d.ts +7 -31
  20. package/types/core/PlacementBuilder.d.ts.map +1 -1
  21. package/types/core/useCase.d.ts +54 -13
  22. package/types/core/useCase.d.ts.map +1 -1
  23. package/types/core/version.d.ts +1 -1
  24. package/types/types.d.ts +85 -272
  25. package/types/types.d.ts.map +1 -1
  26. package/types/ui/OfferCarousel.d.ts +59 -1
  27. package/types/ui/OfferCarousel.d.ts.map +1 -1
  28. package/types/ui/RewardActions.d.ts +60 -0
  29. package/types/ui/RewardActions.d.ts.map +1 -0
  30. package/types/ui/RewardCard.d.ts +49 -0
  31. package/types/ui/RewardCard.d.ts.map +1 -0
  32. package/types/ui/RewardConfetti.d.ts +46 -0
  33. package/types/ui/RewardConfetti.d.ts.map +1 -0
  34. package/types/ui/RewardLayout.d.ts +55 -0
  35. package/types/ui/RewardLayout.d.ts.map +1 -0
  36. package/types/ui/brandTint.d.ts +61 -0
  37. package/types/ui/brandTint.d.ts.map +1 -0
  38. package/types/ui/styles.d.ts +96 -0
  39. package/types/ui/styles.d.ts.map +1 -1
  40. package/types/utils/text.d.ts +24 -0
  41. package/types/utils/text.d.ts.map +1 -0
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Reward offer card — the tile the Figma's `streaksCarousel` hero shows.
3
+ *
4
+ * A DIFFERENT card from `OfferCard`, not a variant of it. OfferCard is a wide banner
5
+ * image over a merchant row with its own Claim button; this is a 210×260 portrait
6
+ * tile — pill, icon, advertiser, offer line — centred on a wash of the advertiser's
7
+ * colour. They share no element, so expressing this as more flags on OfferCard would
8
+ * have meant a component that renders two unrelated layouts and a churn-intervention
9
+ * card one boolean away from the reward one. Separate file, separate class names:
10
+ * the churn path cannot reach a single line of this.
11
+ *
12
+ * NOT a renderer. Web has no SDUI engine; every value below is read off the Figma by
13
+ * hand (frame 18304:2399, node 18304:3383) and tracked here:
14
+ *
15
+ * card 210×260, radius 24, tint@10% over white
16
+ * shadow 0 4px 12px rgba(0,0,0,.09), 0 4px 12px rgba(0,0,0,.02),
17
+ * 0 4px 74px rgba(0,0,0,.06)
18
+ * pill top 43, white, radius 16, pad 4/10, 14 / medium
19
+ * icon top 84, 72×72, radius 18
20
+ * name top 176, 18 / semibold, tracking -0.18
21
+ * offer top 201, 14 / regular, tracking -0.28
22
+ *
23
+ * The Figma's Audible orange is NOT hardcoded — offers are remote and arbitrary. See
24
+ * `brandTint.ts` for how the wash is derived per offer.
25
+ */
26
+ import type { OfferModel as Offer } from '../api/models';
27
+ /**
28
+ * Copy the card hard-codes, mirroring `RewardActions`' REWARD_COPY.
29
+ *
30
+ * The badge is SDK-owned rather than per-offer because no field carries it: the
31
+ * contract has no badge/category on `Creative`, and the Figma's own cards disagree
32
+ * with each other (a "Free 30 days trial" pill above a "50% discount for 3 month"
33
+ * line). It is overridable for the same reason the trust line is — a publisher whose
34
+ * reward is not a trial must not be stuck asserting one.
35
+ */
36
+ export declare const REWARD_CARD_COPY: {
37
+ readonly badge: "Free trial";
38
+ };
39
+ export interface RewardCardOptions {
40
+ /** Overrides the badge pill. Blank/absent keeps the variant's copy. */
41
+ badgeText?: string;
42
+ }
43
+ export declare class RewardCard {
44
+ #private;
45
+ constructor(options?: RewardCardOptions);
46
+ render(offer: Offer): HTMLElement;
47
+ destroy(): void;
48
+ }
49
+ //# sourceMappingURL=RewardCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RewardCard.d.ts","sourceRoot":"","sources":["../../src/ui/RewardCard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,eAAe,CAAC;AAMzD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB;;CAEnB,CAAC;AAEX,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,UAAU;;gBAMT,OAAO,GAAE,iBAAsB;IAI3C,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW;IA2GjC,OAAO,IAAI,IAAI;CAKhB"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Reward confetti — the variant's celebration layer.
3
+ *
4
+ * `streaksCarousel.json` opens the sheet with a `confetti` burst that web never had:
5
+ *
6
+ * intensity 22 particle count
7
+ * duration 2.0 seconds, then the layer removes itself
8
+ * originY 0.0 emitted from the TOP edge, falling down
9
+ * scatter 46 horizontal spread, in points, around each particle's origin
10
+ * scatterHeight 0.3 origins spread over the top 30% of the sheet
11
+ * style maxWidth/maxHeight infinity, ignoresSafeArea — full-bleed
12
+ *
13
+ * Hand-rolled on a single 2D canvas: the SDK ships zero runtime dependencies, and a
14
+ * confetti library would be several KB for 22 rectangles falling for two seconds.
15
+ *
16
+ * Purely decorative and entirely optional. It is `aria-hidden`, it takes no pointer
17
+ * events, it is skipped outright under `prefers-reduced-motion`, and every entry
18
+ * point is guarded — an environment without a 2D context (jsdom, a locked-down
19
+ * browser) simply gets no confetti rather than an exception on a host page.
20
+ */
21
+ /** The variant's values, named so tests can assert parity by symbol. */
22
+ export declare const CONFETTI_SPEC: {
23
+ readonly intensity: 22;
24
+ readonly durationMs: 2000;
25
+ readonly originY: 0;
26
+ readonly scatter: 46;
27
+ readonly scatterHeight: 0.3;
28
+ };
29
+ export declare class RewardConfetti {
30
+ #private;
31
+ /**
32
+ * True when the burst should be skipped entirely.
33
+ *
34
+ * `prefers-reduced-motion` is a hard skip, not a slowdown: falling confetti is
35
+ * exactly the large-area motion the setting exists to suppress, and the sheet
36
+ * loses nothing functional without it.
37
+ */
38
+ static shouldSkip(): boolean;
39
+ /**
40
+ * Mount the burst inside `host` and start it. Returns silently if confetti cannot
41
+ * run; the caller never has to branch.
42
+ */
43
+ start(host: HTMLElement): void;
44
+ destroy(): void;
45
+ }
46
+ //# sourceMappingURL=RewardConfetti.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RewardConfetti.d.ts","sourceRoot":"","sources":["../../src/ui/RewardConfetti.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,wEAAwE;AACxE,eAAO,MAAM,aAAa;;;;;;CAMhB,CAAC;AAkCX,qBAAa,cAAc;;IASzB;;;;;;OAMG;IACH,MAAM,CAAC,UAAU,IAAI,OAAO;IAU5B;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAmI9B,OAAO,IAAI,IAAI;CAWhB"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Reward chrome — the composition `useCase: 'rewardUsers'` renders.
3
+ *
4
+ * Pure chrome around the unchanged OfferCarousel: a status block (success check +
5
+ * headline + subheadline) above the offers. A pure render function rather than a
6
+ * component class: the chrome is static content with no events, no listeners, and no
7
+ * state — the carousel keeps its own lifecycle and is passed in as a ready element.
8
+ *
9
+ * The `thankyou` class names are deliberately UNCHANGED from when this chrome was
10
+ * also reachable as a publisher-selectable `layout: 'thankYou'` (retired in 3.0.0).
11
+ * They are internal style hooks, not public API, and renaming them would churn the
12
+ * whole visual system — including the `--reward` parity rules that track the native
13
+ * SDKs' `streaksCarousel.json` type scale — for no behavioural gain.
14
+ */
15
+ /**
16
+ * The id given to the status heading so the dialog's accessible name can point at
17
+ * the chrome's real primary heading (the carousel's own header is suppressed in this
18
+ * composition, so it is not available as a fallback `aria-labelledby` target).
19
+ */
20
+ export declare const REWARD_TITLE_ID: string;
21
+ export interface RewardLayoutOptions {
22
+ /** Resolved headline. Required in practice — it is the dialog's accessible name. */
23
+ title?: string;
24
+ /** Resolved subheadline. Omitted (never `''`) when nothing resolved. */
25
+ subtitle?: string;
26
+ /**
27
+ * Status icon above the headline. Defaults to `'none'`, matching the native
28
+ * variant — see `PlacementOptions.statusIcon`. `'success'` opts into a
29
+ * green-circle check.
30
+ */
31
+ statusIcon?: 'success' | 'none';
32
+ /**
33
+ * A ready-built action block placed directly under the carousel — the trust line
34
+ * and the single bottom CTA pair (see RewardActions). The variant puts the CTA
35
+ * stack last in the sheet, and nothing renders below it there.
36
+ *
37
+ * An ELEMENT, not a config object, on purpose: this module is a pure render
38
+ * function with no events, no listeners and no state, and the action block is all
39
+ * three. Taking it pre-built keeps the ownership boundary — RewardActions owns its
40
+ * listeners and its teardown; this function only positions it.
41
+ */
42
+ actions?: HTMLElement;
43
+ }
44
+ /**
45
+ * Compose the reward sheet: status (optional check → title → subtitle) → carousel.
46
+ *
47
+ * All host-supplied copy is set via textContent (text-only, injection-safe).
48
+ *
49
+ * The check is OPT-IN (`statusIcon: 'success'`) and off by default, matching the
50
+ * native reward variant, which renders no checkmark at all. It is `aria-hidden`
51
+ * either way, so its presence never affects the dialog's accessible name — that
52
+ * always comes from the title below.
53
+ */
54
+ export declare function renderRewardLayout(carouselElement: HTMLElement, options?: RewardLayoutOptions): HTMLElement;
55
+ //# sourceMappingURL=RewardLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RewardLayout.d.ts","sourceRoot":"","sources":["../../src/ui/RewardLayout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAAwB,CAAC;AAErD,MAAM,WAAW,mBAAmB;IAClC,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAChC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,eAAe,EAAE,WAAW,EAC5B,OAAO,GAAE,mBAAwB,GAChC,WAAW,CA8Cb"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Per-offer brand tint for the reward card.
3
+ *
4
+ * The Figma renders each card on a wash of the advertiser's own colour — Audible's
5
+ * card is `rgba(255,159,43,0.1)` over white. `streaksCarousel.json` binds that as
6
+ * `offerDominantColor`.
7
+ *
8
+ * THERE IS NO API FIELD BEHIND THAT BINDING, on any platform. iOS and Android compute
9
+ * it from the logo at render time; the contract carries no colour on `Creative`,
10
+ * `Organization` or `Campaign`. So sampling the icon is not a web workaround for a
11
+ * missing field — it IS the canonical implementation, and this module is web's copy
12
+ * of it. Native's rule, verbatim: *"the dominant color used in the icon that is not
13
+ * straight black or straight white."*
14
+ *
15
+ * Two tiers, and the first one always succeeds. Native is single-tier; web needs the
16
+ * extra one because it has a failure mode native does not — a cross-origin logo whose
17
+ * host sends no CORS headers can never be read into a canvas at all.
18
+ *
19
+ * 1. `fallbackTint` — synchronous, deterministic, from the offer's identity. Every
20
+ * card has a tint from its first frame; a card is never briefly untinted and
21
+ * never depends on the network.
22
+ * 2. `sampleLogoTint` — asynchronous, upgrades tier 1 to the real brand colour when
23
+ * the logo is readable. Resolves `null` on ANY failure and never throws.
24
+ *
25
+ * Tier 2 needs a CORS-clean image, and `crossOrigin = 'anonymous'` makes a
26
+ * non-CORS server fail the load OUTRIGHT rather than merely tainting the canvas.
27
+ * That is why this samples a SEPARATE `Image`, never the one on screen: the card's
28
+ * own <img> is loaded without `crossOrigin` so it always renders, and a sampling
29
+ * failure costs a tint upgrade rather than the logo itself.
30
+ */
31
+ /** How much of the tint the card background gets — the Figma's 0.1 over white. */
32
+ export declare const CARD_TINT_ALPHA = 0.1;
33
+ /**
34
+ * The synchronous tier. Always returns a usable colour.
35
+ *
36
+ * Keyed on the advertiser NAME first so every creative from one advertiser tints
37
+ * identically — the card shows the advertiser, so two Audible offers reading as two
38
+ * different brands would be wrong. Falls back through title to id, which are never
39
+ * both empty.
40
+ */
41
+ export declare function fallbackTint(seed: string): string;
42
+ /**
43
+ * Reduce RGBA pixels to the dominant non-black, non-white colour, or `null` when the
44
+ * image has none.
45
+ *
46
+ * Split out from the canvas plumbing and exported so the rule that actually matters —
47
+ * paper and ink are excluded, greys lose to colour, a monochrome logo yields nothing —
48
+ * is unit-testable. jsdom has no 2D context, so it is unreachable through
49
+ * `extractDominantColor` in tests.
50
+ *
51
+ * @internal exported for tests only.
52
+ */
53
+ export declare function dominantColorFromPixels(data: Uint8ClampedArray): string | null;
54
+ /**
55
+ * Try to read the advertiser's colour off its logo. Resolves `null` on every failure
56
+ * path — a missing URL, a CORS refusal, a decode error, a timeout, a greyscale logo,
57
+ * or an environment without canvas. Never rejects and never throws: a tint upgrade is
58
+ * the least important thing on the sheet, and an SDK must not crash a host page for it.
59
+ */
60
+ export declare function sampleLogoTint(url: string | undefined): Promise<string | null>;
61
+ //# sourceMappingURL=brandTint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brandTint.d.ts","sourceRoot":"","sources":["../../src/ui/brandTint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,kFAAkF;AAClF,eAAO,MAAM,eAAe,MAAM,CAAC;AA2InC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD;AA4BD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAuC9E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmC9E"}
@@ -11,6 +11,102 @@ export declare const BREAKPOINTS: {
11
11
  readonly TABLET: "1023px";
12
12
  readonly DESKTOP: "1024px";
13
13
  };
14
+ /**
15
+ * REWARD_VISUAL_NOTES — why the reward block in the stylesheet below looks the way
16
+ * it does. Deliberately a JS comment and NOT a CSS one.
17
+ *
18
+ * The stylesheet is one big template literal, so every `/* … *\/` inside it is STRING
19
+ * CONTENT that terser cannot strip and every user downloads. This file's house style
20
+ * is long prose; in the literal that prose is payload. Keep in-literal comments to a
21
+ * terse line or two and put the reasoning here, where it is free. (Trimming this
22
+ * block's CSS prose cut 3.9KB of raw bundle — it had been 46% of the whole
23
+ * stylesheet delta.)
24
+ *
25
+ * SOURCE OF TRUTH. The interaction model tracks `streaksCarousel.json`; the visuals
26
+ * track Figma frame 18304:2399 (393×852). Where they disagree the Figma wins — see
27
+ * the PR for the itemised list. Web has no SDUI engine, so every value is hand-tracked.
28
+ *
29
+ * SHEET BACKGROUND IS FLAT — a deliberate divergence from both the Figma and the
30
+ * variant, on Junaid's call. The sheet used to layer the Figma's radial brand glow
31
+ * over the variant's two stacked full-bleed gradients (an accent wash and a
32
+ * surface → background canvas); all of it is gone. `.thankyou--reward` now declares
33
+ * no background at all, exactly like the base `.thankyou`, so the sheet inherits the
34
+ * modal's `--encore-bg-color` and follows a publisher's theme instead of a colour
35
+ * this file paints. The brand custom properties stay — they feed the dot, the arrow
36
+ * focus ring, the CTA gradient and the card tint, none of which are background.
37
+ *
38
+ * `isolation: isolate` went with the layers. It was there so the confetti canvas
39
+ * (z-index 0) and the content (z-index 1) resolved inside the reward root rather than
40
+ * the modal's stacking context. Measured in Chrome after the removal: the topmost
41
+ * element over the title, the close button and the CTA is identical with and without
42
+ * it, because the canvas and the content are positioned siblings whose own z-indexes
43
+ * already order them, and the close button's z-index 10 still wins either way.
44
+ * `position: relative` STAYS — it is the canvas's declared positioning context for
45
+ * `inset: 0`. Its box happens to measure the same without it today only because the
46
+ * reward root exactly fills the modal; that coincidence ends the moment the modal
47
+ * scrolls, at which point `inset: 0` would size to the modal's viewport instead of
48
+ * the sheet.
49
+ *
50
+ * TEXT COLOUR stays on theme tokens rather than the Figma's #292D32 / #051E34. The
51
+ * variant's own dark-mode note is explicit that every mode-derived colour must remain
52
+ * a token, and a hardcoded light-mode hex in a mode-reactive layer is exactly bug
53
+ * #426/#427. Figma hexes are used only for the brand wash, which is not mode-derived.
54
+ * The subheadline ships at 0.55 rather than the Figma's 0.50 because the variant
55
+ * specifies 0.55 for secondary copy to clear WCAG AA at this size — on a contrast
56
+ * question the variant wins.
57
+ *
58
+ * THE NEIGHBOUR TREATMENT IS MEASURED, NOT GUESSED. A neighbour's card body samples
59
+ * #F9F9F9 against a #F2F2F2 ground, and its icon matches at the same ratio — that is
60
+ * white at 55%. So it is opacity 0.55 + scale 0.95 + translateY(11px) with NO
61
+ * grayscale/saturate filter; the "desaturated" look is just opacity over a light
62
+ * ground. That is also the fast choice: opacity composites on the GPU and holds 60fps
63
+ * through a drag, where a filter repaints every card every frame.
64
+ *
65
+ * CARD BODY is tint@10% over white — measured #FFF5E9 against Audible's #FF9F2B,
66
+ * exactly white + 10%. The two radial layers add the deeper wash the Figma carries at
67
+ * the right and bottom edges (its decorative vector), derived from the same tint so
68
+ * any advertiser colour works. See `brandTint.ts` for where the tint comes from.
69
+ *
70
+ * DESKTOP WIDTH. The design is a 393-wide phone frame; on the desktop modal
71
+ * (max-width 800) the content column holds 393 and centres, so the hero keeps its
72
+ * proportions instead of stranding a 210px card in an 800px sheet. Content sits at
73
+ * z-index 1 so it clears the confetti canvas, matching the variant's zStack order
74
+ * (confetti → content).
75
+ */
76
+ /**
77
+ * The reward sheet's brand pair, read off the Figma (frame 18304:2399).
78
+ *
79
+ * `#B728F5 → #6515ED` is the primary CTA's gradient (node 18304:2417, 98.22°), and
80
+ * `#B728F5` is also the active dot, the arrow focus ring and the card-tint default —
81
+ * one accent for the whole surface, which is why it is one variable rather than three
82
+ * literals. It no longer paints any background: the sheet is flat (see above).
83
+ *
84
+ * NOT `--encore-primary-color`. `streaksCarousel.json` fills this CTA with a FLAT
85
+ * `accent` (the publisher's brand colour) at `cornerRadius: 12`; the Figma shows this
86
+ * gradient at a full pill. The Figma is the target, so the gradient ships — see the
87
+ * PR for the divergence. Exposed as custom properties on the reward root so the pair
88
+ * stays overridable if that decision is revisited.
89
+ */
90
+ export declare const REWARD_BRAND = "#B728F5";
91
+ export declare const REWARD_BRAND_DEEP = "#6515ED";
92
+ /** Inactive dot ink, Figma 18304:3072 — `#6A6D81` at 30%. */
93
+ export declare const REWARD_DOT_COLOR = "#6A6D81";
94
+ /**
95
+ * Reward hero carousel geometry (Figma: hero 210×260 centred in a 393 frame, ~10px
96
+ * between card edges, neighbours peeking at both edges).
97
+ *
98
+ * Shared as constants because three separate rules have to agree on them: the card
99
+ * itself, the scroll container's centring padding (`50% - CARD_WIDTH/2`), and the
100
+ * arrows' vertical offset (`CARDS_PAD_Y + CARD_HEIGHT/2`). Drifting any one of them
101
+ * apart silently de-centres the hero.
102
+ */
103
+ export declare const REWARD_CARD_GEOMETRY: {
104
+ readonly width: 210;
105
+ readonly height: 260;
106
+ readonly gap: 10;
107
+ /** Vertical breathing room in the scroll container, so the card shadow is not clipped. */
108
+ readonly padY: 26;
109
+ };
14
110
  /**
15
111
  * CSS custom properties for theming
16
112
  */
@@ -1 +1 @@
1
- {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/ui/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAGX,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4gDD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,GAAE,cAAmB,GAAG,IAAI,CAgB7D;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAMnC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAUvD;AAED;;GAEG;AACH,wBAAgB,EAAE,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7C"}
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/ui/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAGX,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,YAAY,CAAC;AACtC,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAC3C,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;;;;IAI/B,0FAA0F;;CAElF,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAwpDD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,GAAE,cAAmB,GAAG,IAAI,CAgB7D;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAMnC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAUvD;AAED;;GAEG;AACH,wBAAgB,EAAE,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7C"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Small text helpers shared across the core and UI layers.
3
+ */
4
+ /**
5
+ * First value that is a non-blank string once trimmed, else `undefined` —
6
+ * the copy-priority primitive for tiered overrides (per-call > backend > default).
7
+ *
8
+ * Exists because neither operator expresses this. `??` treats `' '` as a real
9
+ * value, so a whitespace-only override would win its slot and render an empty
10
+ * headline (taking `aria-labelledby` with it) or an empty subtitle. `||` behaves
11
+ * correctly but trips `prefer-nullish-coalescing`, whose autofix converts it to
12
+ * `??` and reintroduces precisely that bug — so the rule's suggested fix is
13
+ * actively wrong on this shape. A named helper states the rule once and keeps
14
+ * every call site honest.
15
+ *
16
+ * Returning `undefined` rather than `''` is what lets callers OMIT a slot instead
17
+ * of filling it with an empty value — for the header that is the difference
18
+ * between rendering no subtitle node and rendering a blank one that still takes
19
+ * layout space and is announced as an empty element. The reward CARD uses the same
20
+ * property to drop its offer line rather than render an empty node that would still
21
+ * consume its 4px gap.
22
+ */
23
+ export declare function firstNonBlank(...values: (string | null | undefined)[]): string | undefined;
24
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,GAAG,SAAS,CAM1F"}