@wix/web5-core 1.56.1 → 1.57.1

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 (192) hide show
  1. package/dist/cjs/client/wixAuthFetch.js +2 -2
  2. package/dist/cjs/client/wixAuthFetch.js.map +1 -1
  3. package/dist/cjs/component/componentDefinitions/HeroEntitySectionDefinition.js +20 -15
  4. package/dist/cjs/component/componentDefinitions/HeroEntitySectionDefinition.js.map +1 -1
  5. package/dist/cjs/component/componentDefinitions/HeroSectionDefinition.js +12 -4
  6. package/dist/cjs/component/componentDefinitions/HeroSectionDefinition.js.map +1 -1
  7. package/dist/cjs/component/componentDefinitions/listItemsSectionDefinition.js +24 -29
  8. package/dist/cjs/component/componentDefinitions/listItemsSectionDefinition.js.map +1 -1
  9. package/dist/cjs/component/componentParser.js +8 -1
  10. package/dist/cjs/component/componentParser.js.map +1 -1
  11. package/dist/cjs/component/types.js.map +1 -1
  12. package/dist/cjs/components/placement/PlacementResponseRenderer.css +0 -4
  13. package/dist/cjs/components/placement/PlacementResponseRenderer.js +11 -33
  14. package/dist/cjs/components/placement/PlacementResponseRenderer.js.map +1 -1
  15. package/dist/cjs/components/placement/buildPlacementDependencies.js.map +1 -1
  16. package/dist/cjs/components/ui/OptimizedImage.js +59 -14
  17. package/dist/cjs/components/ui/OptimizedImage.js.map +1 -1
  18. package/dist/cjs/components/ui/SectionSkeleton.css +19 -0
  19. package/dist/cjs/components/ui/SectionSkeleton.js +8 -4
  20. package/dist/cjs/components/ui/SectionSkeleton.js.map +1 -1
  21. package/dist/cjs/components/ui/UserQuery.css +72 -0
  22. package/dist/cjs/components/ui/UserQuery.js +131 -37
  23. package/dist/cjs/components/ui/UserQuery.js.map +1 -1
  24. package/dist/cjs/context/UserQueryContext.js +34 -0
  25. package/dist/cjs/context/UserQueryContext.js.map +1 -0
  26. package/dist/cjs/entity/defaultExtractor.js +9 -2
  27. package/dist/cjs/entity/defaultExtractor.js.map +1 -1
  28. package/dist/cjs/entity/fetchEntityListData.js +13 -26
  29. package/dist/cjs/entity/fetchEntityListData.js.map +1 -1
  30. package/dist/cjs/entity/index.js +5 -1
  31. package/dist/cjs/entity/index.js.map +1 -1
  32. package/dist/cjs/entity/listEntityItems.js +90 -0
  33. package/dist/cjs/entity/listEntityItems.js.map +1 -0
  34. package/dist/cjs/hostScope.js +63 -0
  35. package/dist/cjs/hostScope.js.map +1 -0
  36. package/dist/cjs/index.js +38 -13
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/cjs/patternValidator/validator.test.js +61 -61
  39. package/dist/cjs/patternValidator/validator.test.js.map +1 -1
  40. package/dist/cjs/registry/ComponentRegistry.js +22 -2
  41. package/dist/cjs/registry/ComponentRegistry.js.map +1 -1
  42. package/dist/cjs/registry/index.js +1 -3
  43. package/dist/cjs/registry/index.js.map +1 -1
  44. package/dist/cjs/registry/types.js.map +1 -1
  45. package/dist/cjs/styles/base-utilities.css +35 -0
  46. package/dist/cjs/styles/components.css +1 -0
  47. package/dist/cjs/styles/host-reset.css +100 -0
  48. package/dist/cjs/styles/tailwind-theme.css +6 -6
  49. package/dist/cjs/utils/backendEnvironment.js +78 -0
  50. package/dist/cjs/utils/backendEnvironment.js.map +1 -0
  51. package/dist/cjs/utils/entityLinkParser.js +3 -1
  52. package/dist/cjs/utils/entityLinkParser.js.map +1 -1
  53. package/dist/cjs/utils/imageBackdrop.js +269 -0
  54. package/dist/cjs/utils/imageBackdrop.js.map +1 -0
  55. package/dist/cjs/utils/intentExtractor.js +20 -9
  56. package/dist/cjs/utils/intentExtractor.js.map +1 -1
  57. package/dist/cjs/utils/productBackHandoff.js +106 -0
  58. package/dist/cjs/utils/productBackHandoff.js.map +1 -0
  59. package/dist/cjs/utils/refreshPrompts.js +72 -0
  60. package/dist/cjs/utils/refreshPrompts.js.map +1 -0
  61. package/dist/esm/client/wixAuthFetch.js +2 -2
  62. package/dist/esm/client/wixAuthFetch.js.map +1 -1
  63. package/dist/esm/component/componentDefinitions/HeroEntitySectionDefinition.js +20 -15
  64. package/dist/esm/component/componentDefinitions/HeroEntitySectionDefinition.js.map +1 -1
  65. package/dist/esm/component/componentDefinitions/HeroSectionDefinition.js +12 -4
  66. package/dist/esm/component/componentDefinitions/HeroSectionDefinition.js.map +1 -1
  67. package/dist/esm/component/componentDefinitions/listItemsSectionDefinition.js +25 -30
  68. package/dist/esm/component/componentDefinitions/listItemsSectionDefinition.js.map +1 -1
  69. package/dist/esm/component/componentParser.js +8 -1
  70. package/dist/esm/component/componentParser.js.map +1 -1
  71. package/dist/esm/component/types.js.map +1 -1
  72. package/dist/esm/components/placement/PlacementResponseRenderer.css +0 -4
  73. package/dist/esm/components/placement/PlacementResponseRenderer.js +1 -16
  74. package/dist/esm/components/placement/PlacementResponseRenderer.js.map +1 -1
  75. package/dist/esm/components/placement/buildPlacementDependencies.js.map +1 -1
  76. package/dist/esm/components/ui/OptimizedImage.js +50 -5
  77. package/dist/esm/components/ui/OptimizedImage.js.map +1 -1
  78. package/dist/esm/components/ui/SectionSkeleton.css +19 -0
  79. package/dist/esm/components/ui/SectionSkeleton.js +5 -1
  80. package/dist/esm/components/ui/SectionSkeleton.js.map +1 -1
  81. package/dist/esm/components/ui/UserQuery.css +72 -0
  82. package/dist/esm/components/ui/UserQuery.js +52 -6
  83. package/dist/esm/components/ui/UserQuery.js.map +1 -1
  84. package/dist/esm/context/UserQueryContext.js +21 -0
  85. package/dist/esm/context/UserQueryContext.js.map +1 -0
  86. package/dist/esm/entity/defaultExtractor.js +9 -2
  87. package/dist/esm/entity/defaultExtractor.js.map +1 -1
  88. package/dist/esm/entity/fetchEntityListData.js +14 -26
  89. package/dist/esm/entity/fetchEntityListData.js.map +1 -1
  90. package/dist/esm/entity/index.js +1 -0
  91. package/dist/esm/entity/index.js.map +1 -1
  92. package/dist/esm/entity/listEntityItems.js +85 -0
  93. package/dist/esm/entity/listEntityItems.js.map +1 -0
  94. package/dist/esm/hostScope.js +59 -0
  95. package/dist/esm/hostScope.js.map +1 -0
  96. package/dist/esm/index.js +13 -4
  97. package/dist/esm/index.js.map +1 -1
  98. package/dist/esm/patternValidator/validator.test.js +61 -61
  99. package/dist/esm/patternValidator/validator.test.js.map +1 -1
  100. package/dist/esm/registry/ComponentRegistry.js +22 -2
  101. package/dist/esm/registry/ComponentRegistry.js.map +1 -1
  102. package/dist/esm/registry/index.js +0 -1
  103. package/dist/esm/registry/index.js.map +1 -1
  104. package/dist/esm/registry/types.js.map +1 -1
  105. package/dist/esm/styles/base-utilities.css +35 -0
  106. package/dist/esm/styles/components.css +1 -0
  107. package/dist/esm/styles/host-reset.css +100 -0
  108. package/dist/esm/styles/tailwind-theme.css +6 -6
  109. package/dist/esm/utils/backendEnvironment.js +71 -0
  110. package/dist/esm/utils/backendEnvironment.js.map +1 -0
  111. package/dist/esm/utils/entityLinkParser.js +3 -1
  112. package/dist/esm/utils/entityLinkParser.js.map +1 -1
  113. package/dist/esm/utils/imageBackdrop.js +262 -0
  114. package/dist/esm/utils/imageBackdrop.js.map +1 -0
  115. package/dist/esm/utils/intentExtractor.js +21 -12
  116. package/dist/esm/utils/intentExtractor.js.map +1 -1
  117. package/dist/esm/utils/productBackHandoff.js +100 -0
  118. package/dist/esm/utils/productBackHandoff.js.map +1 -0
  119. package/dist/esm/utils/refreshPrompts.js +67 -0
  120. package/dist/esm/utils/refreshPrompts.js.map +1 -0
  121. package/dist/types/component/componentDefinitions/HeroEntitySectionDefinition.d.ts.map +1 -1
  122. package/dist/types/component/componentDefinitions/HeroSectionDefinition.d.ts.map +1 -1
  123. package/dist/types/component/componentDefinitions/listItemsSectionDefinition.d.ts +2 -2
  124. package/dist/types/component/componentDefinitions/listItemsSectionDefinition.d.ts.map +1 -1
  125. package/dist/types/component/componentParser.d.ts.map +1 -1
  126. package/dist/types/component/types.d.ts +1 -5
  127. package/dist/types/component/types.d.ts.map +1 -1
  128. package/dist/types/components/placement/PlacementResponseRenderer.d.ts +1 -1
  129. package/dist/types/components/placement/PlacementResponseRenderer.d.ts.map +1 -1
  130. package/dist/types/components/placement/buildPlacementDependencies.d.ts +1 -2
  131. package/dist/types/components/placement/buildPlacementDependencies.d.ts.map +1 -1
  132. package/dist/types/components/ui/OptimizedImage.d.ts +9 -0
  133. package/dist/types/components/ui/OptimizedImage.d.ts.map +1 -1
  134. package/dist/types/components/ui/SectionSkeleton.d.ts.map +1 -1
  135. package/dist/types/components/ui/UserQuery.d.ts +21 -0
  136. package/dist/types/components/ui/UserQuery.d.ts.map +1 -1
  137. package/dist/types/context/UserQueryContext.d.ts +12 -0
  138. package/dist/types/context/UserQueryContext.d.ts.map +1 -0
  139. package/dist/types/entity/defaultExtractor.d.ts.map +1 -1
  140. package/dist/types/entity/fetchEntityListData.d.ts +0 -2
  141. package/dist/types/entity/fetchEntityListData.d.ts.map +1 -1
  142. package/dist/types/entity/index.d.ts +1 -0
  143. package/dist/types/entity/index.d.ts.map +1 -1
  144. package/dist/types/entity/listEntityItems.d.ts +40 -0
  145. package/dist/types/entity/listEntityItems.d.ts.map +1 -0
  146. package/dist/types/hostScope.d.ts +83 -0
  147. package/dist/types/hostScope.d.ts.map +1 -0
  148. package/dist/types/index.d.ts +11 -7
  149. package/dist/types/index.d.ts.map +1 -1
  150. package/dist/types/registry/ComponentRegistry.d.ts +8 -0
  151. package/dist/types/registry/ComponentRegistry.d.ts.map +1 -1
  152. package/dist/types/registry/index.d.ts +0 -1
  153. package/dist/types/registry/index.d.ts.map +1 -1
  154. package/dist/types/registry/types.d.ts +5 -6
  155. package/dist/types/registry/types.d.ts.map +1 -1
  156. package/dist/types/utils/backendEnvironment.d.ts +30 -0
  157. package/dist/types/utils/backendEnvironment.d.ts.map +1 -0
  158. package/dist/types/utils/entityLinkParser.d.ts.map +1 -1
  159. package/dist/types/utils/imageBackdrop.d.ts +102 -0
  160. package/dist/types/utils/imageBackdrop.d.ts.map +1 -0
  161. package/dist/types/utils/intentExtractor.d.ts +21 -4
  162. package/dist/types/utils/intentExtractor.d.ts.map +1 -1
  163. package/dist/types/utils/productBackHandoff.d.ts +43 -0
  164. package/dist/types/utils/productBackHandoff.d.ts.map +1 -0
  165. package/dist/types/utils/refreshPrompts.d.ts +32 -0
  166. package/dist/types/utils/refreshPrompts.d.ts.map +1 -0
  167. package/package.json +2 -2
  168. package/src/components/placement/PlacementResponseRenderer.css +0 -4
  169. package/src/components/ui/SectionSkeleton.css +19 -0
  170. package/src/components/ui/UserQuery.css +72 -0
  171. package/src/styles/base-utilities.css +35 -0
  172. package/src/styles/components.css +1 -0
  173. package/src/styles/host-reset.css +100 -0
  174. package/src/styles/tailwind-theme.css +6 -6
  175. package/dist/cjs/registry/Intent.js +0 -19
  176. package/dist/cjs/registry/Intent.js.map +0 -1
  177. package/dist/cjs/utils/conversationApi.js +0 -52
  178. package/dist/cjs/utils/conversationApi.js.map +0 -1
  179. package/dist/cjs/utils/conversationMode.js +0 -23
  180. package/dist/cjs/utils/conversationMode.js.map +0 -1
  181. package/dist/esm/registry/Intent.js +0 -15
  182. package/dist/esm/registry/Intent.js.map +0 -1
  183. package/dist/esm/utils/conversationApi.js +0 -45
  184. package/dist/esm/utils/conversationApi.js.map +0 -1
  185. package/dist/esm/utils/conversationMode.js +0 -18
  186. package/dist/esm/utils/conversationMode.js.map +0 -1
  187. package/dist/types/registry/Intent.d.ts +0 -12
  188. package/dist/types/registry/Intent.d.ts.map +0 -1
  189. package/dist/types/utils/conversationApi.d.ts +0 -26
  190. package/dist/types/utils/conversationApi.d.ts.map +0 -1
  191. package/dist/types/utils/conversationMode.d.ts +0 -16
  192. package/dist/types/utils/conversationMode.d.ts.map +0 -1
@@ -0,0 +1,262 @@
1
+ import { getResizedImageUrl, normalizeImageUrl } from './image-utils.js';
2
+
3
+ /**
4
+ * Result of inspecting an image's edges to decide how it should sit in a slot
5
+ * whose aspect ratio differs from the image's own.
6
+ *
7
+ * The problem: a single CSS `object-fit` can't serve both kinds of imagery a
8
+ * conversational page surfaces. Product cutouts (subject on a seamless white
9
+ * sweep, or a transparent PNG) must never be cropped — `cover` chops the
10
+ * product. Full-bleed scenes (a model in a street, a flat-lay that fills the
11
+ * frame) look broken when letterboxed — they want `cover`.
12
+ *
13
+ * We can't tell these apart from the resolver metadata (it only exposes url +
14
+ * optional dimensions), so we sniff the pixels: sample the image's border ring
15
+ * and ask "is there a consistent backdrop colour behind the subject?".
16
+ * - Consistent border → seamless backdrop → render `contain` and paint the
17
+ * slot that backdrop colour, so the "letterbox" becomes an invisible
18
+ * extension of the image's own sweep (white shoe → white fill; green
19
+ * sweater sweep → green fill). The whole subject is always visible.
20
+ * - Noisy border → edge-to-edge scene → render `cover`; cropping the
21
+ * margins is safe and letterboxing would look wrong.
22
+ */
23
+
24
+ const NO_BACKDROP = {
25
+ hasBackdrop: false
26
+ };
27
+
28
+ /** Pixels at/beyond this alpha are treated as opaque. */
29
+ const OPAQUE_ALPHA = 250;
30
+ /** A border is "transparent" when at least this fraction of it is see-through. */
31
+ const TRANSPARENT_BORDER_FRACTION = 0.6;
32
+ /** Max per-channel distance from the median for a pixel to count as "backdrop". */
33
+ const COLOR_TOLERANCE = 28;
34
+ /** A border is a "backdrop" when at least this fraction sits near the median. */
35
+ const BACKDROP_AGREEMENT = 0.85;
36
+ function median(values) {
37
+ if (values.length === 0) {
38
+ return 0;
39
+ }
40
+ const sorted = [...values].sort((a, b) => a - b);
41
+ const mid = Math.floor(sorted.length / 2);
42
+ return sorted.length % 2 === 0 ? Math.round((sorted[mid - 1] + sorted[mid]) / 2) : sorted[mid];
43
+ }
44
+
45
+ /**
46
+ * Inspect the 1px border ring of an RGBA buffer and classify the backdrop.
47
+ * Pure and synchronous so it can be unit-tested without a DOM.
48
+ */
49
+ export function analyzeBackdrop(data, width, height) {
50
+ if (width < 3 || height < 3 || data.length < width * height * 4) {
51
+ return NO_BACKDROP;
52
+ }
53
+ const reds = [];
54
+ const greens = [];
55
+ const blues = [];
56
+ let transparentCount = 0;
57
+ let total = 0;
58
+ const sample = (x, y) => {
59
+ const i = (y * width + x) * 4;
60
+ total += 1;
61
+ if (data[i + 3] < OPAQUE_ALPHA) {
62
+ transparentCount += 1;
63
+ return;
64
+ }
65
+ reds.push(data[i]);
66
+ greens.push(data[i + 1]);
67
+ blues.push(data[i + 2]);
68
+ };
69
+ for (let x = 0; x < width; x += 1) {
70
+ sample(x, 0);
71
+ sample(x, height - 1);
72
+ }
73
+ for (let y = 1; y < height - 1; y += 1) {
74
+ sample(0, y);
75
+ sample(width - 1, y);
76
+ }
77
+ if (total === 0) {
78
+ return NO_BACKDROP;
79
+ }
80
+
81
+ // Mostly-transparent border → a cutout PNG. Let the page background show.
82
+ if (transparentCount / total >= TRANSPARENT_BORDER_FRACTION) {
83
+ return {
84
+ hasBackdrop: true,
85
+ backgroundColor: 'transparent'
86
+ };
87
+ }
88
+ const opaque = reds.length;
89
+ if (opaque === 0) {
90
+ return NO_BACKDROP;
91
+ }
92
+ const mr = median(reds);
93
+ const mg = median(greens);
94
+ const mb = median(blues);
95
+ let agree = 0;
96
+ for (let k = 0; k < opaque; k += 1) {
97
+ if (Math.abs(reds[k] - mr) <= COLOR_TOLERANCE && Math.abs(greens[k] - mg) <= COLOR_TOLERANCE && Math.abs(blues[k] - mb) <= COLOR_TOLERANCE) {
98
+ agree += 1;
99
+ }
100
+ }
101
+ if (agree / opaque >= BACKDROP_AGREEMENT) {
102
+ return {
103
+ hasBackdrop: true,
104
+ backgroundColor: `rgb(${mr}, ${mg}, ${mb})`
105
+ };
106
+ }
107
+ return NO_BACKDROP;
108
+ }
109
+
110
+ /**
111
+ * Bounding box of the non-background content in a probed image, in sample-grid
112
+ * coordinates (0..size-1), plus the sampled background colour. Used by callers
113
+ * that need *where the subject sits* (e.g. crop-safety in a fixed-aspect slot),
114
+ * which is a different question from `analyzeBackdrop`'s "is there a backdrop".
115
+ */
116
+
117
+ /**
118
+ * Find the bounding box of the subject by treating the top-left pixel as the
119
+ * background colour and flagging every pixel that differs from it (or is
120
+ * transparent). Pure and synchronous. Returns null for an all-background frame.
121
+ *
122
+ * `backgroundTolerance`/`alphaThreshold` default to the values tuned for
123
+ * uniform studio catalog shots; callers can override.
124
+ */
125
+ export function computeContentBBox(data, size, options) {
126
+ const tol = (options == null ? void 0 : options.backgroundTolerance) ?? 12;
127
+ const alpha = (options == null ? void 0 : options.alphaThreshold) ?? 16;
128
+ if (size < 1 || data.length < size * size * 4) {
129
+ return null;
130
+ }
131
+ const br = data[0];
132
+ const bg = data[1];
133
+ const bb = data[2];
134
+ const isBg = i => data[i + 3] < alpha || Math.abs(data[i] - br) < tol && Math.abs(data[i + 1] - bg) < tol && Math.abs(data[i + 2] - bb) < tol;
135
+ let top = size;
136
+ let bottom = -1;
137
+ let left = size;
138
+ let right = -1;
139
+ for (let y = 0; y < size; y += 1) {
140
+ for (let x = 0; x < size; x += 1) {
141
+ if (!isBg((y * size + x) * 4)) {
142
+ top = Math.min(top, y);
143
+ bottom = Math.max(bottom, y);
144
+ left = Math.min(left, x);
145
+ right = Math.max(right, x);
146
+ }
147
+ }
148
+ }
149
+ if (bottom < 0) {
150
+ return null;
151
+ }
152
+ return {
153
+ top,
154
+ bottom,
155
+ left,
156
+ right,
157
+ background: {
158
+ r: br,
159
+ g: bg,
160
+ b: bb
161
+ }
162
+ };
163
+ }
164
+
165
+ /** Square sample grid for the probe — small enough to be cheap, big enough to
166
+ * read edges and locate a subject. */
167
+ const PROBE_BOX = 64;
168
+
169
+ /**
170
+ * Build the URL of a tiny, aspect-preserving (`fit`) version of an image so we
171
+ * can read its true edges. Wix media is downscaled by the CDN; other URLs are
172
+ * probed at full size (still drawn onto a small canvas).
173
+ */
174
+ export function buildProbeUrl(effectiveFilename) {
175
+ const normalized = normalizeImageUrl(effectiveFilename);
176
+ if (!normalized.includes('static.wixstatic.com')) {
177
+ return normalized;
178
+ }
179
+ const match = normalized.match(/media\/([^/?]+)/);
180
+ if (!(match != null && match[1])) {
181
+ return normalized;
182
+ }
183
+ return getResizedImageUrl(match[1], PROBE_BOX, PROBE_BOX, {
184
+ quality: 70,
185
+ imageMode: 'fit'
186
+ });
187
+ }
188
+
189
+ /** RGBA pixels of a probed image, drawn onto a `size`×`size` grid, alongside the
190
+ * source's true intrinsic dimensions (needed for aspect-aware crop math). */
191
+
192
+ /**
193
+ * Shared probe plumbing: load a (downscaled, for Wix) version of the image
194
+ * cross-origin, draw it onto a `size`×`size` canvas, and return its pixels plus
195
+ * intrinsic dimensions. Resolves to `null` on any failure — missing DOM, a
196
+ * data/blob source, a load error, or a tainted canvas (if CORS ever regresses)
197
+ * — so every caller can fall back to its own default. Browser-only.
198
+ *
199
+ * Both the backdrop classifier (`loadBackdropAnalysis`) and bounding-box callers
200
+ * build on this so the canvas/CORS/taint handling lives in exactly one place.
201
+ */
202
+ export function loadImagePixels(effectiveFilename, options, signal) {
203
+ const size = (options == null ? void 0 : options.size) ?? PROBE_BOX;
204
+ return new Promise(resolve => {
205
+ if (typeof document === 'undefined' || !effectiveFilename) {
206
+ resolve(null);
207
+ return;
208
+ }
209
+ const probeUrl = buildProbeUrl(effectiveFilename);
210
+ if (probeUrl.startsWith('data:') || probeUrl.startsWith('blob:')) {
211
+ resolve(null);
212
+ return;
213
+ }
214
+ const img = new Image();
215
+ img.crossOrigin = 'anonymous';
216
+ img.decoding = 'async';
217
+ img.onload = () => {
218
+ if (signal != null && signal.aborted) {
219
+ resolve(null);
220
+ return;
221
+ }
222
+ try {
223
+ const canvas = document.createElement('canvas');
224
+ canvas.width = size;
225
+ canvas.height = size;
226
+ const ctx = canvas.getContext('2d', {
227
+ willReadFrequently: true
228
+ });
229
+ if (!ctx) {
230
+ resolve(null);
231
+ return;
232
+ }
233
+ ctx.drawImage(img, 0, 0, size, size);
234
+ const {
235
+ data
236
+ } = ctx.getImageData(0, 0, size, size);
237
+ resolve({
238
+ data,
239
+ size,
240
+ naturalWidth: img.naturalWidth || size,
241
+ naturalHeight: img.naturalHeight || size
242
+ });
243
+ } catch {
244
+ // Tainted canvas or any read failure → let the caller default.
245
+ resolve(null);
246
+ }
247
+ };
248
+ img.onerror = () => resolve(null);
249
+ img.src = probeUrl;
250
+ });
251
+ }
252
+
253
+ /**
254
+ * Load a probe image and analyze its backdrop. Resolves to `NO_BACKDROP` on any
255
+ * failure so callers fall back to their default fit. Browser-only.
256
+ */
257
+ export function loadBackdropAnalysis(effectiveFilename, signal) {
258
+ return loadImagePixels(effectiveFilename, {
259
+ size: PROBE_BOX
260
+ }, signal).then(pixels => pixels ? analyzeBackdrop(pixels.data, pixels.size, pixels.size) : NO_BACKDROP);
261
+ }
262
+ //# sourceMappingURL=imageBackdrop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getResizedImageUrl","normalizeImageUrl","NO_BACKDROP","hasBackdrop","OPAQUE_ALPHA","TRANSPARENT_BORDER_FRACTION","COLOR_TOLERANCE","BACKDROP_AGREEMENT","median","values","length","sorted","sort","a","b","mid","Math","floor","round","analyzeBackdrop","data","width","height","reds","greens","blues","transparentCount","total","sample","x","y","i","push","backgroundColor","opaque","mr","mg","mb","agree","k","abs","computeContentBBox","size","options","tol","backgroundTolerance","alpha","alphaThreshold","br","bg","bb","isBg","top","bottom","left","right","min","max","background","r","g","PROBE_BOX","buildProbeUrl","effectiveFilename","normalized","includes","match","quality","imageMode","loadImagePixels","signal","Promise","resolve","document","probeUrl","startsWith","img","Image","crossOrigin","decoding","onload","aborted","canvas","createElement","ctx","getContext","willReadFrequently","drawImage","getImageData","naturalWidth","naturalHeight","onerror","src","loadBackdropAnalysis","then","pixels"],"sources":["../../../src/utils/imageBackdrop.ts"],"sourcesContent":["import { getResizedImageUrl, normalizeImageUrl } from './image-utils';\n\n/**\n * Result of inspecting an image's edges to decide how it should sit in a slot\n * whose aspect ratio differs from the image's own.\n *\n * The problem: a single CSS `object-fit` can't serve both kinds of imagery a\n * conversational page surfaces. Product cutouts (subject on a seamless white\n * sweep, or a transparent PNG) must never be cropped — `cover` chops the\n * product. Full-bleed scenes (a model in a street, a flat-lay that fills the\n * frame) look broken when letterboxed — they want `cover`.\n *\n * We can't tell these apart from the resolver metadata (it only exposes url +\n * optional dimensions), so we sniff the pixels: sample the image's border ring\n * and ask \"is there a consistent backdrop colour behind the subject?\".\n * - Consistent border → seamless backdrop → render `contain` and paint the\n * slot that backdrop colour, so the \"letterbox\" becomes an invisible\n * extension of the image's own sweep (white shoe → white fill; green\n * sweater sweep → green fill). The whole subject is always visible.\n * - Noisy border → edge-to-edge scene → render `cover`; cropping the\n * margins is safe and letterboxing would look wrong.\n */\nexport interface BackdropAnalysis {\n /** True when the border reads as a seamless backdrop (uniform or transparent). */\n hasBackdrop: boolean;\n /**\n * CSS colour to paint behind a `contain`ed image so the letterbox blends.\n * `'transparent'` for cutouts with an alpha border (the page background then\n * shows through). Undefined when `hasBackdrop` is false.\n */\n backgroundColor?: string;\n}\n\nconst NO_BACKDROP: BackdropAnalysis = { hasBackdrop: false };\n\n/** Pixels at/beyond this alpha are treated as opaque. */\nconst OPAQUE_ALPHA = 250;\n/** A border is \"transparent\" when at least this fraction of it is see-through. */\nconst TRANSPARENT_BORDER_FRACTION = 0.6;\n/** Max per-channel distance from the median for a pixel to count as \"backdrop\". */\nconst COLOR_TOLERANCE = 28;\n/** A border is a \"backdrop\" when at least this fraction sits near the median. */\nconst BACKDROP_AGREEMENT = 0.85;\n\nfunction median(values: number[]): number {\n if (values.length === 0) {\n return 0;\n }\n const sorted = [...values].sort((a, b) => a - b);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 0\n ? Math.round((sorted[mid - 1] + sorted[mid]) / 2)\n : sorted[mid];\n}\n\n/**\n * Inspect the 1px border ring of an RGBA buffer and classify the backdrop.\n * Pure and synchronous so it can be unit-tested without a DOM.\n */\nexport function analyzeBackdrop(\n data: Uint8ClampedArray,\n width: number,\n height: number,\n): BackdropAnalysis {\n if (width < 3 || height < 3 || data.length < width * height * 4) {\n return NO_BACKDROP;\n }\n\n const reds: number[] = [];\n const greens: number[] = [];\n const blues: number[] = [];\n let transparentCount = 0;\n let total = 0;\n\n const sample = (x: number, y: number) => {\n const i = (y * width + x) * 4;\n total += 1;\n if (data[i + 3] < OPAQUE_ALPHA) {\n transparentCount += 1;\n return;\n }\n reds.push(data[i]);\n greens.push(data[i + 1]);\n blues.push(data[i + 2]);\n };\n\n for (let x = 0; x < width; x += 1) {\n sample(x, 0);\n sample(x, height - 1);\n }\n for (let y = 1; y < height - 1; y += 1) {\n sample(0, y);\n sample(width - 1, y);\n }\n\n if (total === 0) {\n return NO_BACKDROP;\n }\n\n // Mostly-transparent border → a cutout PNG. Let the page background show.\n if (transparentCount / total >= TRANSPARENT_BORDER_FRACTION) {\n return { hasBackdrop: true, backgroundColor: 'transparent' };\n }\n\n const opaque = reds.length;\n if (opaque === 0) {\n return NO_BACKDROP;\n }\n\n const mr = median(reds);\n const mg = median(greens);\n const mb = median(blues);\n\n let agree = 0;\n for (let k = 0; k < opaque; k += 1) {\n if (\n Math.abs(reds[k] - mr) <= COLOR_TOLERANCE &&\n Math.abs(greens[k] - mg) <= COLOR_TOLERANCE &&\n Math.abs(blues[k] - mb) <= COLOR_TOLERANCE\n ) {\n agree += 1;\n }\n }\n\n if (agree / opaque >= BACKDROP_AGREEMENT) {\n return { hasBackdrop: true, backgroundColor: `rgb(${mr}, ${mg}, ${mb})` };\n }\n\n return NO_BACKDROP;\n}\n\n/**\n * Bounding box of the non-background content in a probed image, in sample-grid\n * coordinates (0..size-1), plus the sampled background colour. Used by callers\n * that need *where the subject sits* (e.g. crop-safety in a fixed-aspect slot),\n * which is a different question from `analyzeBackdrop`'s \"is there a backdrop\".\n */\nexport interface ContentBBox {\n top: number;\n bottom: number;\n left: number;\n right: number;\n /** Background colour, sampled from the top-left pixel. */\n background: { r: number; g: number; b: number };\n}\n\n/**\n * Find the bounding box of the subject by treating the top-left pixel as the\n * background colour and flagging every pixel that differs from it (or is\n * transparent). Pure and synchronous. Returns null for an all-background frame.\n *\n * `backgroundTolerance`/`alphaThreshold` default to the values tuned for\n * uniform studio catalog shots; callers can override.\n */\nexport function computeContentBBox(\n data: Uint8ClampedArray,\n size: number,\n options?: { backgroundTolerance?: number; alphaThreshold?: number },\n): ContentBBox | null {\n const tol = options?.backgroundTolerance ?? 12;\n const alpha = options?.alphaThreshold ?? 16;\n if (size < 1 || data.length < size * size * 4) {\n return null;\n }\n\n const br = data[0];\n const bg = data[1];\n const bb = data[2];\n const isBg = (i: number) =>\n data[i + 3] < alpha ||\n (Math.abs(data[i] - br) < tol &&\n Math.abs(data[i + 1] - bg) < tol &&\n Math.abs(data[i + 2] - bb) < tol);\n\n let top = size;\n let bottom = -1;\n let left = size;\n let right = -1;\n for (let y = 0; y < size; y += 1) {\n for (let x = 0; x < size; x += 1) {\n if (!isBg((y * size + x) * 4)) {\n top = Math.min(top, y);\n bottom = Math.max(bottom, y);\n left = Math.min(left, x);\n right = Math.max(right, x);\n }\n }\n }\n\n if (bottom < 0) {\n return null;\n }\n return { top, bottom, left, right, background: { r: br, g: bg, b: bb } };\n}\n\n/** Square sample grid for the probe — small enough to be cheap, big enough to\n * read edges and locate a subject. */\nconst PROBE_BOX = 64;\n\n/**\n * Build the URL of a tiny, aspect-preserving (`fit`) version of an image so we\n * can read its true edges. Wix media is downscaled by the CDN; other URLs are\n * probed at full size (still drawn onto a small canvas).\n */\nexport function buildProbeUrl(effectiveFilename: string): string {\n const normalized = normalizeImageUrl(effectiveFilename);\n if (!normalized.includes('static.wixstatic.com')) {\n return normalized;\n }\n const match = normalized.match(/media\\/([^/?]+)/);\n if (!match?.[1]) {\n return normalized;\n }\n return getResizedImageUrl(match[1], PROBE_BOX, PROBE_BOX, {\n quality: 70,\n imageMode: 'fit',\n });\n}\n\n/** RGBA pixels of a probed image, drawn onto a `size`×`size` grid, alongside the\n * source's true intrinsic dimensions (needed for aspect-aware crop math). */\nexport interface ImagePixels {\n data: Uint8ClampedArray;\n size: number;\n naturalWidth: number;\n naturalHeight: number;\n}\n\n/**\n * Shared probe plumbing: load a (downscaled, for Wix) version of the image\n * cross-origin, draw it onto a `size`×`size` canvas, and return its pixels plus\n * intrinsic dimensions. Resolves to `null` on any failure — missing DOM, a\n * data/blob source, a load error, or a tainted canvas (if CORS ever regresses)\n * — so every caller can fall back to its own default. Browser-only.\n *\n * Both the backdrop classifier (`loadBackdropAnalysis`) and bounding-box callers\n * build on this so the canvas/CORS/taint handling lives in exactly one place.\n */\nexport function loadImagePixels(\n effectiveFilename: string,\n options?: { size?: number },\n signal?: { aborted: boolean },\n): Promise<ImagePixels | null> {\n const size = options?.size ?? PROBE_BOX;\n return new Promise((resolve) => {\n if (typeof document === 'undefined' || !effectiveFilename) {\n resolve(null);\n return;\n }\n const probeUrl = buildProbeUrl(effectiveFilename);\n if (probeUrl.startsWith('data:') || probeUrl.startsWith('blob:')) {\n resolve(null);\n return;\n }\n\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.decoding = 'async';\n\n img.onload = () => {\n if (signal?.aborted) {\n resolve(null);\n return;\n }\n try {\n const canvas = document.createElement('canvas');\n canvas.width = size;\n canvas.height = size;\n const ctx = canvas.getContext('2d', { willReadFrequently: true });\n if (!ctx) {\n resolve(null);\n return;\n }\n ctx.drawImage(img, 0, 0, size, size);\n const { data } = ctx.getImageData(0, 0, size, size);\n resolve({\n data,\n size,\n naturalWidth: img.naturalWidth || size,\n naturalHeight: img.naturalHeight || size,\n });\n } catch {\n // Tainted canvas or any read failure → let the caller default.\n resolve(null);\n }\n };\n img.onerror = () => resolve(null);\n img.src = probeUrl;\n });\n}\n\n/**\n * Load a probe image and analyze its backdrop. Resolves to `NO_BACKDROP` on any\n * failure so callers fall back to their default fit. Browser-only.\n */\nexport function loadBackdropAnalysis(\n effectiveFilename: string,\n signal?: { aborted: boolean },\n): Promise<BackdropAnalysis> {\n return loadImagePixels(effectiveFilename, { size: PROBE_BOX }, signal).then(\n (pixels) =>\n pixels\n ? analyzeBackdrop(pixels.data, pixels.size, pixels.size)\n : NO_BACKDROP,\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,18 +1,30 @@
1
- import { Intent } from '../registry/Intent.js';
2
1
  import { parseHtmlComment } from './htmlCommentParser.js';
3
- const INTENT_VALUES = new Set(Object.values(Intent));
4
- function normalizeIntent(intent) {
2
+
3
+ /**
4
+ * Controls how a raw intent string is validated / defaulted during extraction.
5
+ * Core holds NO intent list of its own — the taxonomy is client-specific. A
6
+ * caller supplies the client's list (typically `registry.getKnownIntents()`) to
7
+ * make the FE honour that client's intents; with no list, any emitted intent is
8
+ * accepted verbatim (resolution then falls back to the section default when the
9
+ * intent has no registered variant).
10
+ */
11
+
12
+ function normalizeIntent(intent, validIntents) {
5
13
  if (!intent) {
6
14
  return null;
7
15
  }
8
16
  const normalized = intent.trim().toUpperCase();
9
- return INTENT_VALUES.has(normalized) ? normalized : null;
17
+ if (!validIntents) {
18
+ return normalized;
19
+ }
20
+ const allowed = new Set(validIntents.map(value => value.toUpperCase()));
21
+ return allowed.has(normalized) ? normalized : null;
10
22
  }
11
23
  function normalizeState(state) {
12
24
  const normalized = state == null ? void 0 : state.trim().toLowerCase();
13
25
  return normalized === 'partial' ? 'partial' : 'full';
14
26
  }
15
- export function extractIntentFromMarkdown(markdown) {
27
+ export function extractIntentFromMarkdown(markdown, options) {
16
28
  if (!markdown) {
17
29
  return null;
18
30
  }
@@ -22,7 +34,7 @@ export function extractIntentFromMarkdown(markdown) {
22
34
  }
23
35
  const comment = match[0];
24
36
  const metadata = parseHtmlComment(comment);
25
- const intent = normalizeIntent(metadata.intent);
37
+ const intent = normalizeIntent(metadata.intent, options == null ? void 0 : options.validIntents);
26
38
  if (!intent) {
27
39
  return null;
28
40
  }
@@ -33,12 +45,9 @@ export function extractIntentFromMarkdown(markdown) {
33
45
  state: normalizeState(metadata.state)
34
46
  };
35
47
  }
36
- export function getIntentFromMarkdown(markdown, defaultIntent) {
37
- if (defaultIntent === void 0) {
38
- defaultIntent = Intent.DISCOVERY;
39
- }
40
- return extractIntentFromMarkdown(markdown) ?? {
41
- intent: defaultIntent,
48
+ export function getIntentFromMarkdown(markdown, options) {
49
+ return extractIntentFromMarkdown(markdown, options) ?? {
50
+ intent: (options == null ? void 0 : options.defaultIntent) ?? '',
42
51
  state: 'full'
43
52
  };
44
53
  }
@@ -1 +1 @@
1
- {"version":3,"names":["Intent","parseHtmlComment","INTENT_VALUES","Set","Object","values","normalizeIntent","intent","normalized","trim","toUpperCase","has","normalizeState","state","toLowerCase","extractIntentFromMarkdown","markdown","match","comment","metadata","stage","reason","getIntentFromMarkdown","defaultIntent","DISCOVERY"],"sources":["../../../src/utils/intentExtractor.ts"],"sourcesContent":["import { Intent } from '../registry/Intent';\nimport { parseHtmlComment } from './htmlCommentParser';\n\nexport type ResponseState = 'full' | 'partial';\n\nexport interface IntentInfo {\n intent: Intent;\n stage?: string;\n reason?: string;\n state?: ResponseState;\n}\n\nconst INTENT_VALUES = new Set<Intent>(Object.values(Intent) as Intent[]);\n\nfunction normalizeIntent(intent?: string): Intent | null {\n if (!intent) {\n return null;\n }\n const normalized = intent.trim().toUpperCase() as Intent;\n return INTENT_VALUES.has(normalized) ? normalized : null;\n}\n\nfunction normalizeState(state?: string): ResponseState {\n const normalized = state?.trim().toLowerCase();\n return normalized === 'partial' ? 'partial' : 'full';\n}\n\nexport function extractIntentFromMarkdown(markdown: string): IntentInfo | null {\n if (!markdown) {\n return null;\n }\n\n const match = markdown.match(/^\\s*<!--[\\s\\S]*?-->/);\n if (!match) {\n return null;\n }\n\n const comment = match[0];\n const metadata = parseHtmlComment(comment);\n const intent = normalizeIntent(metadata.intent);\n\n if (!intent) {\n return null;\n }\n\n return {\n intent,\n stage: metadata.stage,\n reason: metadata.reason,\n state: normalizeState(metadata.state),\n };\n}\n\nexport function getIntentFromMarkdown(\n markdown: string,\n defaultIntent: Intent = Intent.DISCOVERY,\n): IntentInfo {\n return (\n extractIntentFromMarkdown(markdown) ?? {\n intent: defaultIntent,\n state: 'full',\n }\n );\n}\n"],"mappings":"AAAA,SAASA,MAAM,QAAQ,oBAAoB;AAC3C,SAASC,gBAAgB,QAAQ,qBAAqB;AAWtD,MAAMC,aAAa,GAAG,IAAIC,GAAG,CAASC,MAAM,CAACC,MAAM,CAACL,MAAM,CAAa,CAAC;AAExE,SAASM,eAAeA,CAACC,MAAe,EAAiB;EACvD,IAAI,CAACA,MAAM,EAAE;IACX,OAAO,IAAI;EACb;EACA,MAAMC,UAAU,GAAGD,MAAM,CAACE,IAAI,CAAC,CAAC,CAACC,WAAW,CAAC,CAAW;EACxD,OAAOR,aAAa,CAACS,GAAG,CAACH,UAAU,CAAC,GAAGA,UAAU,GAAG,IAAI;AAC1D;AAEA,SAASI,cAAcA,CAACC,KAAc,EAAiB;EACrD,MAAML,UAAU,GAAGK,KAAK,oBAALA,KAAK,CAAEJ,IAAI,CAAC,CAAC,CAACK,WAAW,CAAC,CAAC;EAC9C,OAAON,UAAU,KAAK,SAAS,GAAG,SAAS,GAAG,MAAM;AACtD;AAEA,OAAO,SAASO,yBAAyBA,CAACC,QAAgB,EAAqB;EAC7E,IAAI,CAACA,QAAQ,EAAE;IACb,OAAO,IAAI;EACb;EAEA,MAAMC,KAAK,GAAGD,QAAQ,CAACC,KAAK,CAAC,qBAAqB,CAAC;EACnD,IAAI,CAACA,KAAK,EAAE;IACV,OAAO,IAAI;EACb;EAEA,MAAMC,OAAO,GAAGD,KAAK,CAAC,CAAC,CAAC;EACxB,MAAME,QAAQ,GAAGlB,gBAAgB,CAACiB,OAAO,CAAC;EAC1C,MAAMX,MAAM,GAAGD,eAAe,CAACa,QAAQ,CAACZ,MAAM,CAAC;EAE/C,IAAI,CAACA,MAAM,EAAE;IACX,OAAO,IAAI;EACb;EAEA,OAAO;IACLA,MAAM;IACNa,KAAK,EAAED,QAAQ,CAACC,KAAK;IACrBC,MAAM,EAAEF,QAAQ,CAACE,MAAM;IACvBR,KAAK,EAAED,cAAc,CAACO,QAAQ,CAACN,KAAK;EACtC,CAAC;AACH;AAEA,OAAO,SAASS,qBAAqBA,CACnCN,QAAgB,EAChBO,aAAqB,EACT;EAAA,IADZA,aAAqB;IAArBA,aAAqB,GAAGvB,MAAM,CAACwB,SAAS;EAAA;EAExC,OACET,yBAAyB,CAACC,QAAQ,CAAC,IAAI;IACrCT,MAAM,EAAEgB,aAAa;IACrBV,KAAK,EAAE;EACT,CAAC;AAEL","ignoreList":[]}
1
+ {"version":3,"names":["parseHtmlComment","normalizeIntent","intent","validIntents","normalized","trim","toUpperCase","allowed","Set","map","value","has","normalizeState","state","toLowerCase","extractIntentFromMarkdown","markdown","options","match","comment","metadata","stage","reason","getIntentFromMarkdown","defaultIntent"],"sources":["../../../src/utils/intentExtractor.ts"],"sourcesContent":["import { parseHtmlComment } from './htmlCommentParser';\n\nexport type ResponseState = 'full' | 'partial';\n\nexport interface IntentInfo {\n intent: string;\n stage?: string;\n reason?: string;\n state?: ResponseState;\n}\n\n/**\n * Controls how a raw intent string is validated / defaulted during extraction.\n * Core holds NO intent list of its own — the taxonomy is client-specific. A\n * caller supplies the client's list (typically `registry.getKnownIntents()`) to\n * make the FE honour that client's intents; with no list, any emitted intent is\n * accepted verbatim (resolution then falls back to the section default when the\n * intent has no registered variant).\n */\nexport interface IntentExtractionOptions {\n /**\n * The intents to accept (matched case-insensitively). An emitted intent\n * outside this list is treated as absent. When omitted, no filtering is\n * applied and the emitted intent passes through as-is.\n */\n validIntents?: readonly string[];\n /** Intent assumed when the markdown carries no intent annotation. Defaults to ''. */\n defaultIntent?: string;\n}\n\nfunction normalizeIntent(\n intent: string | undefined,\n validIntents?: readonly string[],\n): string | null {\n if (!intent) {\n return null;\n }\n const normalized = intent.trim().toUpperCase();\n if (!validIntents) {\n return normalized;\n }\n const allowed = new Set(validIntents.map((value) => value.toUpperCase()));\n return allowed.has(normalized) ? normalized : null;\n}\n\nfunction normalizeState(state?: string): ResponseState {\n const normalized = state?.trim().toLowerCase();\n return normalized === 'partial' ? 'partial' : 'full';\n}\n\nexport function extractIntentFromMarkdown(\n markdown: string,\n options?: IntentExtractionOptions,\n): IntentInfo | null {\n if (!markdown) {\n return null;\n }\n\n const match = markdown.match(/^\\s*<!--[\\s\\S]*?-->/);\n if (!match) {\n return null;\n }\n\n const comment = match[0];\n const metadata = parseHtmlComment(comment);\n const intent = normalizeIntent(metadata.intent, options?.validIntents);\n\n if (!intent) {\n return null;\n }\n\n return {\n intent,\n stage: metadata.stage,\n reason: metadata.reason,\n state: normalizeState(metadata.state),\n };\n}\n\nexport function getIntentFromMarkdown(\n markdown: string,\n options?: IntentExtractionOptions,\n): IntentInfo {\n return (\n extractIntentFromMarkdown(markdown, options) ?? {\n intent: options?.defaultIntent ?? '',\n state: 'full',\n }\n );\n}\n"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,qBAAqB;;AAWtD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAYA,SAASC,eAAeA,CACtBC,MAA0B,EAC1BC,YAAgC,EACjB;EACf,IAAI,CAACD,MAAM,EAAE;IACX,OAAO,IAAI;EACb;EACA,MAAME,UAAU,GAAGF,MAAM,CAACG,IAAI,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC;EAC9C,IAAI,CAACH,YAAY,EAAE;IACjB,OAAOC,UAAU;EACnB;EACA,MAAMG,OAAO,GAAG,IAAIC,GAAG,CAACL,YAAY,CAACM,GAAG,CAAEC,KAAK,IAAKA,KAAK,CAACJ,WAAW,CAAC,CAAC,CAAC,CAAC;EACzE,OAAOC,OAAO,CAACI,GAAG,CAACP,UAAU,CAAC,GAAGA,UAAU,GAAG,IAAI;AACpD;AAEA,SAASQ,cAAcA,CAACC,KAAc,EAAiB;EACrD,MAAMT,UAAU,GAAGS,KAAK,oBAALA,KAAK,CAAER,IAAI,CAAC,CAAC,CAACS,WAAW,CAAC,CAAC;EAC9C,OAAOV,UAAU,KAAK,SAAS,GAAG,SAAS,GAAG,MAAM;AACtD;AAEA,OAAO,SAASW,yBAAyBA,CACvCC,QAAgB,EAChBC,OAAiC,EACd;EACnB,IAAI,CAACD,QAAQ,EAAE;IACb,OAAO,IAAI;EACb;EAEA,MAAME,KAAK,GAAGF,QAAQ,CAACE,KAAK,CAAC,qBAAqB,CAAC;EACnD,IAAI,CAACA,KAAK,EAAE;IACV,OAAO,IAAI;EACb;EAEA,MAAMC,OAAO,GAAGD,KAAK,CAAC,CAAC,CAAC;EACxB,MAAME,QAAQ,GAAGpB,gBAAgB,CAACmB,OAAO,CAAC;EAC1C,MAAMjB,MAAM,GAAGD,eAAe,CAACmB,QAAQ,CAAClB,MAAM,EAAEe,OAAO,oBAAPA,OAAO,CAAEd,YAAY,CAAC;EAEtE,IAAI,CAACD,MAAM,EAAE;IACX,OAAO,IAAI;EACb;EAEA,OAAO;IACLA,MAAM;IACNmB,KAAK,EAAED,QAAQ,CAACC,KAAK;IACrBC,MAAM,EAAEF,QAAQ,CAACE,MAAM;IACvBT,KAAK,EAAED,cAAc,CAACQ,QAAQ,CAACP,KAAK;EACtC,CAAC;AACH;AAEA,OAAO,SAASU,qBAAqBA,CACnCP,QAAgB,EAChBC,OAAiC,EACrB;EACZ,OACEF,yBAAyB,CAACC,QAAQ,EAAEC,OAAO,CAAC,IAAI;IAC9Cf,MAAM,EAAE,CAAAe,OAAO,oBAAPA,OAAO,CAAEO,aAAa,KAAI,EAAE;IACpCX,KAAK,EAAE;EACT,CAAC;AAEL","ignoreList":[]}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Product-page → full-app "back" context hand-off (DL #114).
3
+ *
4
+ * A PDP placement writes the product context to `sessionStorage` immediately
5
+ * before redirecting the visitor to the full Web5 page; the app reads it once
6
+ * on load and renders a back-to-product badge in the conversation header.
7
+ *
8
+ * The context never enters the URL, so there is no shareable/tamperable link
9
+ * that could spoof the header (the security driver in DL #114). The hand-off
10
+ * relies on the redirect being a same-origin, same-tab top-level navigation
11
+ * (`searchRedirectUrl`, e.g. `/ai`), which is what keeps `sessionStorage`
12
+ * readable on the other side. Cross-origin → nothing is read → no badge.
13
+ */
14
+ export const PRODUCT_BACK_SESSION_KEY = 'web5:productBack';
15
+ const isBrowser = () => typeof window !== 'undefined' && typeof window.sessionStorage !== 'undefined';
16
+ function readMeta(property) {
17
+ var _el$getAttribute;
18
+ if (typeof document === 'undefined') {
19
+ return undefined;
20
+ }
21
+ const el = document.querySelector(`meta[property="${property}"], meta[name="${property}"]`);
22
+ const content = el == null || (_el$getAttribute = el.getAttribute('content')) == null ? void 0 : _el$getAttribute.trim();
23
+ return content || undefined;
24
+ }
25
+
26
+ /**
27
+ * Resolve a meta-tag image URL to an absolute `https:` URL. Handles the
28
+ * protocol-relative (`//cdn…`) and root-relative (`/img…`) forms some stores
29
+ * (e.g. Shopify themes) emit, which the reader's `https:`-only check would
30
+ * otherwise drop. Returns undefined for anything that can't be made absolute.
31
+ */
32
+ function readImageMeta() {
33
+ const raw = readMeta('og:image') ?? readMeta('twitter:image');
34
+ if (!raw || typeof window === 'undefined') {
35
+ return raw;
36
+ }
37
+ let url = raw;
38
+ if (url.startsWith('//')) {
39
+ url = `https:${url}`;
40
+ } else if (url.startsWith('/')) {
41
+ url = `${window.location.origin}${url}`;
42
+ }
43
+ return url;
44
+ }
45
+
46
+ /**
47
+ * Write the back-context just before navigating away from a product page.
48
+ * Values default to the page's own first-party Open Graph tags and URL, so a
49
+ * standard PDP needs no extra wiring; pass `overrides` to supply exact data.
50
+ * No-op outside the browser or when no product title can be determined.
51
+ */
52
+ export function writeProductBackHandoff(overrides) {
53
+ if (!isBrowser()) {
54
+ return;
55
+ }
56
+ const title = (overrides == null ? void 0 : overrides.title) ?? readMeta('og:title');
57
+ if (!title) {
58
+ // Nothing meaningful to show — don't write a badge-less entry.
59
+ return;
60
+ }
61
+ const handoff = {
62
+ title,
63
+ imageUrl: (overrides == null ? void 0 : overrides.imageUrl) ?? readImageMeta(),
64
+ backUrl: (overrides == null ? void 0 : overrides.backUrl) ?? window.location.href,
65
+ ts: Date.now()
66
+ };
67
+ try {
68
+ window.sessionStorage.setItem(PRODUCT_BACK_SESSION_KEY, JSON.stringify(handoff));
69
+ } catch {
70
+ // sessionStorage unavailable / quota exceeded — the badge simply won't show.
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Read and immediately delete the back-context (consume-once: survives exactly
76
+ * one page load, so a reload or bfcache restore won't replay it). Returns null
77
+ * when absent or unparseable. Callers must still validate the values before use
78
+ * (freshness window, allowed-domain check on `backUrl`, `https:` on `imageUrl`).
79
+ */
80
+ export function readProductBackHandoff() {
81
+ if (!isBrowser()) {
82
+ return null;
83
+ }
84
+ let raw = null;
85
+ try {
86
+ raw = window.sessionStorage.getItem(PRODUCT_BACK_SESSION_KEY);
87
+ window.sessionStorage.removeItem(PRODUCT_BACK_SESSION_KEY);
88
+ } catch {
89
+ return null;
90
+ }
91
+ if (!raw) {
92
+ return null;
93
+ }
94
+ try {
95
+ return JSON.parse(raw);
96
+ } catch {
97
+ return null;
98
+ }
99
+ }
100
+ //# sourceMappingURL=productBackHandoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["PRODUCT_BACK_SESSION_KEY","isBrowser","window","sessionStorage","readMeta","property","_el$getAttribute","document","undefined","el","querySelector","content","getAttribute","trim","readImageMeta","raw","url","startsWith","location","origin","writeProductBackHandoff","overrides","title","handoff","imageUrl","backUrl","href","ts","Date","now","setItem","JSON","stringify","readProductBackHandoff","getItem","removeItem","parse"],"sources":["../../../src/utils/productBackHandoff.ts"],"sourcesContent":["/**\n * Product-page → full-app \"back\" context hand-off (DL #114).\n *\n * A PDP placement writes the product context to `sessionStorage` immediately\n * before redirecting the visitor to the full Web5 page; the app reads it once\n * on load and renders a back-to-product badge in the conversation header.\n *\n * The context never enters the URL, so there is no shareable/tamperable link\n * that could spoof the header (the security driver in DL #114). The hand-off\n * relies on the redirect being a same-origin, same-tab top-level navigation\n * (`searchRedirectUrl`, e.g. `/ai`), which is what keeps `sessionStorage`\n * readable on the other side. Cross-origin → nothing is read → no badge.\n */\nexport const PRODUCT_BACK_SESSION_KEY = 'web5:productBack';\n\nexport interface ProductBackHandoff {\n /** Product display name (first-party; e.g. the PDP `og:title`). */\n title?: string;\n /** Product thumbnail URL (first-party; e.g. the PDP `og:image`). */\n imageUrl?: string;\n /** The product page to return to — the PDP's own URL. */\n backUrl?: string;\n /** Epoch ms when written; the reader drops stale entries. */\n ts?: number;\n}\n\nconst isBrowser = (): boolean =>\n typeof window !== 'undefined' && typeof window.sessionStorage !== 'undefined';\n\nfunction readMeta(property: string): string | undefined {\n if (typeof document === 'undefined') {\n return undefined;\n }\n const el = document.querySelector(\n `meta[property=\"${property}\"], meta[name=\"${property}\"]`,\n );\n const content = el?.getAttribute('content')?.trim();\n return content || undefined;\n}\n\n/**\n * Resolve a meta-tag image URL to an absolute `https:` URL. Handles the\n * protocol-relative (`//cdn…`) and root-relative (`/img…`) forms some stores\n * (e.g. Shopify themes) emit, which the reader's `https:`-only check would\n * otherwise drop. Returns undefined for anything that can't be made absolute.\n */\nfunction readImageMeta(): string | undefined {\n const raw = readMeta('og:image') ?? readMeta('twitter:image');\n if (!raw || typeof window === 'undefined') {\n return raw;\n }\n let url = raw;\n if (url.startsWith('//')) {\n url = `https:${url}`;\n } else if (url.startsWith('/')) {\n url = `${window.location.origin}${url}`;\n }\n return url;\n}\n\n/**\n * Write the back-context just before navigating away from a product page.\n * Values default to the page's own first-party Open Graph tags and URL, so a\n * standard PDP needs no extra wiring; pass `overrides` to supply exact data.\n * No-op outside the browser or when no product title can be determined.\n */\nexport function writeProductBackHandoff(overrides?: {\n title?: string;\n imageUrl?: string;\n backUrl?: string;\n}): void {\n if (!isBrowser()) {\n return;\n }\n const title = overrides?.title ?? readMeta('og:title');\n if (!title) {\n // Nothing meaningful to show — don't write a badge-less entry.\n return;\n }\n const handoff: ProductBackHandoff = {\n title,\n imageUrl: overrides?.imageUrl ?? readImageMeta(),\n backUrl: overrides?.backUrl ?? window.location.href,\n ts: Date.now(),\n };\n try {\n window.sessionStorage.setItem(\n PRODUCT_BACK_SESSION_KEY,\n JSON.stringify(handoff),\n );\n } catch {\n // sessionStorage unavailable / quota exceeded — the badge simply won't show.\n }\n}\n\n/**\n * Read and immediately delete the back-context (consume-once: survives exactly\n * one page load, so a reload or bfcache restore won't replay it). Returns null\n * when absent or unparseable. Callers must still validate the values before use\n * (freshness window, allowed-domain check on `backUrl`, `https:` on `imageUrl`).\n */\nexport function readProductBackHandoff(): ProductBackHandoff | null {\n if (!isBrowser()) {\n return null;\n }\n let raw: string | null = null;\n try {\n raw = window.sessionStorage.getItem(PRODUCT_BACK_SESSION_KEY);\n window.sessionStorage.removeItem(PRODUCT_BACK_SESSION_KEY);\n } catch {\n return null;\n }\n if (!raw) {\n return null;\n }\n try {\n return JSON.parse(raw) as ProductBackHandoff;\n } catch {\n return null;\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Refresh-prompts flag (debug-only).
3
+ *
4
+ * When active, the Turn RPC request (`POST /v1/turn`, DL #110) carries
5
+ * `additionalData.refresh_prompts: 'true'`, asking the BE to re-pull prompts
6
+ * instead of serving cached ones.
7
+ *
8
+ * Keeping it on slows the app, so the flag self-expires: rather than a bare
9
+ * boolean we persist an expiry timestamp in
10
+ * `localStorage["web5_refresh_prompts_until"]`. `shouldRefreshPrompts()` is
11
+ * true only while now < expiry, so the cap holds even across reloads or if a
12
+ * UI timer never fires. Lives in web5-core so the request builder and any
13
+ * surface honor the same toggle.
14
+ */
15
+
16
+ export const REFRESH_PROMPTS_UNTIL_KEY = 'web5_refresh_prompts_until';
17
+
18
+ /** How long the flag stays active once enabled (2 minutes). */
19
+ export const REFRESH_PROMPTS_WINDOW_MS = 2 * 60 * 1000;
20
+
21
+ /**
22
+ * Read the expiry timestamp (ms epoch) from localStorage. Returns 0 when
23
+ * unset, invalid, or storage is unavailable.
24
+ */
25
+ export const getRefreshPromptsExpiry = () => {
26
+ try {
27
+ const raw = localStorage.getItem(REFRESH_PROMPTS_UNTIL_KEY);
28
+ if (!raw) {
29
+ return 0;
30
+ }
31
+ const ts = Number(raw);
32
+ return Number.isFinite(ts) ? ts : 0;
33
+ } catch {
34
+ // localStorage can throw in privacy mode / non-DOM environments.
35
+ return 0;
36
+ }
37
+ };
38
+
39
+ /** Whether the refresh-prompts flag is currently active (within its window). */
40
+ export const shouldRefreshPrompts = () => getRefreshPromptsExpiry() > Date.now();
41
+
42
+ /**
43
+ * Activate the flag for `windowMs` from now and return the new expiry
44
+ * timestamp. Re-enabling restarts the window.
45
+ */
46
+ export const enableRefreshPrompts = function (windowMs) {
47
+ if (windowMs === void 0) {
48
+ windowMs = REFRESH_PROMPTS_WINDOW_MS;
49
+ }
50
+ const until = Date.now() + windowMs;
51
+ try {
52
+ localStorage.setItem(REFRESH_PROMPTS_UNTIL_KEY, String(until));
53
+ } catch {
54
+ // Ignore — flag simply won't persist.
55
+ }
56
+ return until;
57
+ };
58
+
59
+ /** Deactivate the flag immediately (proactive off). */
60
+ export const disableRefreshPrompts = () => {
61
+ try {
62
+ localStorage.removeItem(REFRESH_PROMPTS_UNTIL_KEY);
63
+ } catch {
64
+ // Ignore.
65
+ }
66
+ };
67
+ //# sourceMappingURL=refreshPrompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["REFRESH_PROMPTS_UNTIL_KEY","REFRESH_PROMPTS_WINDOW_MS","getRefreshPromptsExpiry","raw","localStorage","getItem","ts","Number","isFinite","shouldRefreshPrompts","Date","now","enableRefreshPrompts","windowMs","until","setItem","String","disableRefreshPrompts","removeItem"],"sources":["../../../src/utils/refreshPrompts.ts"],"sourcesContent":["/**\n * Refresh-prompts flag (debug-only).\n *\n * When active, the Turn RPC request (`POST /v1/turn`, DL #110) carries\n * `additionalData.refresh_prompts: 'true'`, asking the BE to re-pull prompts\n * instead of serving cached ones.\n *\n * Keeping it on slows the app, so the flag self-expires: rather than a bare\n * boolean we persist an expiry timestamp in\n * `localStorage[\"web5_refresh_prompts_until\"]`. `shouldRefreshPrompts()` is\n * true only while now < expiry, so the cap holds even across reloads or if a\n * UI timer never fires. Lives in web5-core so the request builder and any\n * surface honor the same toggle.\n */\n\nexport const REFRESH_PROMPTS_UNTIL_KEY = 'web5_refresh_prompts_until';\n\n/** How long the flag stays active once enabled (2 minutes). */\nexport const REFRESH_PROMPTS_WINDOW_MS = 2 * 60 * 1000;\n\n/**\n * Read the expiry timestamp (ms epoch) from localStorage. Returns 0 when\n * unset, invalid, or storage is unavailable.\n */\nexport const getRefreshPromptsExpiry = (): number => {\n try {\n const raw = localStorage.getItem(REFRESH_PROMPTS_UNTIL_KEY);\n if (!raw) {\n return 0;\n }\n const ts = Number(raw);\n return Number.isFinite(ts) ? ts : 0;\n } catch {\n // localStorage can throw in privacy mode / non-DOM environments.\n return 0;\n }\n};\n\n/** Whether the refresh-prompts flag is currently active (within its window). */\nexport const shouldRefreshPrompts = (): boolean =>\n getRefreshPromptsExpiry() > Date.now();\n\n/**\n * Activate the flag for `windowMs` from now and return the new expiry\n * timestamp. Re-enabling restarts the window.\n */\nexport const enableRefreshPrompts = (\n windowMs: number = REFRESH_PROMPTS_WINDOW_MS,\n): number => {\n const until = Date.now() + windowMs;\n try {\n localStorage.setItem(REFRESH_PROMPTS_UNTIL_KEY, String(until));\n } catch {\n // Ignore — flag simply won't persist.\n }\n return until;\n};\n\n/** Deactivate the flag immediately (proactive off). */\nexport const disableRefreshPrompts = (): void => {\n try {\n localStorage.removeItem(REFRESH_PROMPTS_UNTIL_KEY);\n } catch {\n // Ignore.\n }\n};\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAO,MAAMA,yBAAyB,GAAG,4BAA4B;;AAErE;AACA,OAAO,MAAMC,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;;AAEtD;AACA;AACA;AACA;AACA,OAAO,MAAMC,uBAAuB,GAAGA,CAAA,KAAc;EACnD,IAAI;IACF,MAAMC,GAAG,GAAGC,YAAY,CAACC,OAAO,CAACL,yBAAyB,CAAC;IAC3D,IAAI,CAACG,GAAG,EAAE;MACR,OAAO,CAAC;IACV;IACA,MAAMG,EAAE,GAAGC,MAAM,CAACJ,GAAG,CAAC;IACtB,OAAOI,MAAM,CAACC,QAAQ,CAACF,EAAE,CAAC,GAAGA,EAAE,GAAG,CAAC;EACrC,CAAC,CAAC,MAAM;IACN;IACA,OAAO,CAAC;EACV;AACF,CAAC;;AAED;AACA,OAAO,MAAMG,oBAAoB,GAAGA,CAAA,KAClCP,uBAAuB,CAAC,CAAC,GAAGQ,IAAI,CAACC,GAAG,CAAC,CAAC;;AAExC;AACA;AACA;AACA;AACA,OAAO,MAAMC,oBAAoB,GAAG,SAAAA,CAClCC,QAAgB,EACL;EAAA,IADXA,QAAgB;IAAhBA,QAAgB,GAAGZ,yBAAyB;EAAA;EAE5C,MAAMa,KAAK,GAAGJ,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGE,QAAQ;EACnC,IAAI;IACFT,YAAY,CAACW,OAAO,CAACf,yBAAyB,EAAEgB,MAAM,CAACF,KAAK,CAAC,CAAC;EAChE,CAAC,CAAC,MAAM;IACN;EAAA;EAEF,OAAOA,KAAK;AACd,CAAC;;AAED;AACA,OAAO,MAAMG,qBAAqB,GAAGA,CAAA,KAAY;EAC/C,IAAI;IACFb,YAAY,CAACc,UAAU,CAAClB,yBAAyB,CAAC;EACpD,CAAC,CAAC,MAAM;IACN;EAAA;AAEJ,CAAC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"HeroEntitySectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/HeroEntitySectionDefinition.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,KAAK,EAEV,mBAAmB,EACnB,0BAA0B,EAC1B,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG7E;;;;;;;;;;GAUG;AACH,qBAAa,2BACX,YAAW,iBAAiB,CAAC,0BAA0B,CAAC;IAExD,WAAW,gBAA0B;IACrC,cAAc,UAAQ;IACtB,QAAQ,WAKN;IAEF,QAAQ,EAAE,mBAAmB,CAW3B;IAEF,QAAQ,EAAE,cAAc,CAAC,mBAAmB,CAAC,EAAE,CAyC7C;IAEF,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,mBAAmB,GAAG,IAAI;CAsDzE"}
1
+ {"version":3,"file":"HeroEntitySectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/HeroEntitySectionDefinition.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,KAAK,EAEV,mBAAmB,EACnB,0BAA0B,EAC1B,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG7E;;;;;;;;;;GAUG;AACH,qBAAa,2BACX,YAAW,iBAAiB,CAAC,0BAA0B,CAAC;IAExD,WAAW,gBAA0B;IACrC,cAAc,UAAQ;IACtB,QAAQ,WASN;IAEF,QAAQ,EAAE,mBAAmB,CAW3B;IAEF,QAAQ,EAAE,cAAc,CAAC,mBAAmB,CAAC,EAAE,CAyC7C;IAEF,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,mBAAmB,GAAG,IAAI;CAuDzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"HeroSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/HeroSectionDefinition.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAoC,MAAM,mBAAmB,CAAC;AAEhF,OAAO,KAAK,EAEV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,qBAAa,qBACX,YAAW,iBAAiB,CAAC,oBAAoB,CAAC;IAElD,WAAW,SAAmB;IAC9B,cAAc,UAAQ;IACtB,QAAQ,WAEN;IAEF,QAAQ,EAAE,aAAa,CAIrB;IAEF,QAAQ,EAAE,cAAc,CAAC,aAAa,CAAC,EAAE,CAqDvC;IAEF,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,aAAa;CAgJpC"}
1
+ {"version":3,"file":"HeroSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/HeroSectionDefinition.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAoC,MAAM,mBAAmB,CAAC;AAEhF,OAAO,KAAK,EAEV,aAAa,EACb,oBAAoB,EACpB,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,qBAAa,qBACX,YAAW,iBAAiB,CAAC,oBAAoB,CAAC;IAElD,WAAW,SAAmB;IAC9B,cAAc,UAAQ;IACtB,QAAQ,WAQN;IAEF,QAAQ,EAAE,aAAa,CAIrB;IAEF,QAAQ,EAAE,cAAc,CAAC,aAAa,CAAC,EAAE,CAqDvC;IAEF,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,aAAa;CAiJpC"}
@@ -1,11 +1,11 @@
1
1
  import { type Part } from '../../parts/parts';
2
2
  import type { ListItemsSectionCompProps, ListItemsSectionComponent, SectionFixture } from '../types';
3
- import type { SectionDefinition, ParseContext } from '../section-definition';
3
+ import type { SectionDefinition } from '../section-definition';
4
4
  export declare class ListItemsSectionDefinition implements SectionDefinition<ListItemsSectionComponent> {
5
5
  sectionType: "listItemsSection";
6
6
  patterns: string[];
7
7
  defaults: ListItemsSectionCompProps;
8
8
  fixtures: SectionFixture<ListItemsSectionCompProps>[];
9
- parse(parts: Part[], context?: ParseContext): ListItemsSectionCompProps;
9
+ parse(parts: Part[]): ListItemsSectionCompProps | null;
10
10
  }
11
11
  //# sourceMappingURL=listItemsSectionDefinition.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"listItemsSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/listItemsSectionDefinition.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,IAAI,EAAiB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EACzB,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AA6B7E,qBAAa,0BACX,YAAW,iBAAiB,CAAC,yBAAyB,CAAC;IAEvD,WAAW,qBAA+B;IAC1C,QAAQ,WAGN;IAEF,QAAQ,EAAE,yBAAyB,CAIjC;IAEF,QAAQ,EAAE,cAAc,CAAC,yBAAyB,CAAC,EAAE,CAAM;IAE3D,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,yBAAyB;CA2DxE"}
1
+ {"version":3,"file":"listItemsSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/listItemsSectionDefinition.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,IAAI,EAAiB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EACV,yBAAyB,EACzB,yBAAyB,EACzB,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK/D,qBAAa,0BACX,YAAW,iBAAiB,CAAC,yBAAyB,CAAC;IAEvD,WAAW,qBAA+B;IAW1C,QAAQ,WAEN;IAEF,QAAQ,EAAE,yBAAyB,CAGjC;IAEF,QAAQ,EAAE,cAAc,CAAC,yBAAyB,CAAC,EAAE,CAAM;IAE3D,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,yBAAyB,GAAG,IAAI;CAmDvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"componentParser.d.ts","sourceRoot":"","sources":["../../../src/component/componentParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EACL,oBAAoB,EACrB,MAAM,+BAA+B,CAAC;AAMvC,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGlF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,EAAE,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,2EAA2E;IAC3E,YAAY,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,gFAAgF;IAChF,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,oBAAoB,EAAE,oBAAoB,CAAC;CAC5C;AA2BD;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,gBAAgB,EAAE,WAAW,EAAE,EAC/B,WAAW,EAAE,WAAW,EAAE,GACzB,WAAW,EAAE,CA2Cf;AAyFD,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,oBAAoB,GAC5B,mBAAmB,CAkIrB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAAE,EACpB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,aAAa,GACrB;IAAE,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAkKjD"}
1
+ {"version":3,"file":"componentParser.d.ts","sourceRoot":"","sources":["../../../src/component/componentParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EACL,oBAAoB,EACrB,MAAM,+BAA+B,CAAC;AAMvC,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGlF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,EAAE,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3C,2EAA2E;IAC3E,YAAY,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,gFAAgF;IAChF,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,oBAAoB,EAAE,oBAAoB,CAAC;CAC5C;AA2BD;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,gBAAgB,EAAE,WAAW,EAAE,EAC/B,WAAW,EAAE,WAAW,EAAE,GACzB,WAAW,EAAE,CA2Cf;AAyFD,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,oBAAoB,GAC5B,mBAAmB,CAyIrB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAAE,EACpB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,aAAa,GACrB;IAAE,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAkKjD"}