@redacto.io/consent-sdk-react 10.2.2-beta.3 → 10.3.0-beta.4

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.
@@ -0,0 +1,618 @@
1
+ // src/shared/idempotency.ts
2
+ var HEADER = "Idempotency-Key";
3
+ var pending = /* @__PURE__ */ new Map();
4
+ var MAX_PENDING = 16;
5
+ var mintKey = () => {
6
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
7
+ return crypto.randomUUID();
8
+ }
9
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 14)}-${Math.random().toString(36).slice(2, 14)}`;
10
+ };
11
+ var statusOf = (outcome) => {
12
+ if (typeof Response !== "undefined" && outcome instanceof Response) {
13
+ return outcome.status;
14
+ }
15
+ if (outcome && typeof outcome === "object" && "status" in outcome) {
16
+ const status = outcome.status;
17
+ if (typeof status === "number") return status;
18
+ }
19
+ return void 0;
20
+ };
21
+ var settledOnResolve = (result) => {
22
+ const status = statusOf(result);
23
+ return status === void 0 || status < 500;
24
+ };
25
+ var settledOnReject = (error) => {
26
+ const status = statusOf(error);
27
+ return status !== void 0 && status < 500;
28
+ };
29
+ var withIdempotencyKey = async (url, body, send) => {
30
+ const signature = `${url}
31
+ ${body}`;
32
+ let entry = pending.get(signature);
33
+ if (!entry) {
34
+ entry = { key: mintKey(), inFlight: 0, keep: false };
35
+ if (pending.size >= MAX_PENDING) {
36
+ const oldest = pending.keys().next().value;
37
+ if (oldest !== void 0) pending.delete(oldest);
38
+ }
39
+ pending.set(signature, entry);
40
+ } else if (entry.inFlight === 0) {
41
+ entry.keep = false;
42
+ }
43
+ entry.inFlight += 1;
44
+ const settle = (settled) => {
45
+ entry.inFlight -= 1;
46
+ if (!settled) entry.keep = true;
47
+ if (entry.inFlight === 0 && !entry.keep) pending.delete(signature);
48
+ };
49
+ try {
50
+ const result = await send({ [HEADER]: entry.key });
51
+ settle(settledOnResolve(result));
52
+ return result;
53
+ } catch (error) {
54
+ settle(settledOnReject(error));
55
+ throw error;
56
+ }
57
+ };
58
+
59
+ // src/shared/appearance/constants.ts
60
+ var NOTICE_COLOR_KEYS = [
61
+ "background",
62
+ "surface",
63
+ "heading",
64
+ "text",
65
+ "muted_text",
66
+ "link",
67
+ "border",
68
+ "overlay",
69
+ "accept_all_bg",
70
+ "accept_all_text",
71
+ "accept_bg",
72
+ "accept_text",
73
+ "decline_bg",
74
+ "decline_text",
75
+ "decline_border",
76
+ "language_bg",
77
+ "language_text",
78
+ "language_selected_bg",
79
+ "language_selected_text",
80
+ "toggle_on",
81
+ "toggle_off",
82
+ "toggle_knob",
83
+ "tts_highlight_bg",
84
+ "tts_highlight_text"
85
+ ];
86
+ var NOTICE_SELECTION_CONTROLS = ["checkbox", "radio", "dropdown"];
87
+ var NOTICE_CONTROL_STYLES = ["checkbox", "switch"];
88
+ var NOTICE_RADIUS_MIN = 0;
89
+ var NOTICE_RADIUS_MAX = 28;
90
+ var NOTICE_MOBILE_LAYOUTS = ["modal", "sheet"];
91
+ var NOTICE_DESKTOP_POSITIONS = [
92
+ "center",
93
+ "bottom-right",
94
+ "bottom-left",
95
+ "bottom-bar"
96
+ ];
97
+ var NOTICE_BACKDROPS = ["dim", "none"];
98
+ var NOTICE_PHONE_FOOTERS = ["stacked", "paired"];
99
+ var NOTICE_TEXT_SCALES = ["sm", "md", "lg"];
100
+ var DEFAULT_TEXT_SCALE = "md";
101
+ var DEFAULT_DESKTOP_POSITION = "center";
102
+ var NOTICE_TEXT_SCALE_FACTORS = {
103
+ sm: 0.92,
104
+ md: 1,
105
+ lg: 1.1
106
+ };
107
+ var NOTICE_WIDTH_MIN = 320;
108
+ var NOTICE_WIDTH_MAX = 960;
109
+ var NOTICE_LOGO_SIZE_MIN = 16;
110
+ var NOTICE_LOGO_SIZE_MAX = 96;
111
+ var NOTICE_CUSTOM_CSS_MAX_LENGTH = 2e4;
112
+ var NOTICE_DISALLOWED_CSS_TOKENS = [
113
+ "</style",
114
+ "@import",
115
+ "javascript:",
116
+ "expression(",
117
+ "behavior:"
118
+ ];
119
+ var CSS_ESCAPE_RE = /\\([0-9a-f]{1,6})(?:\r\n|[ \t\r\n\f])?|\\([\s\S])/gi;
120
+ var CSS_BRACKET_PAIRS = {
121
+ "{": "}",
122
+ "(": ")",
123
+ "[": "]"
124
+ };
125
+ var CSS_CLOSING_BRACKETS = Object.values(CSS_BRACKET_PAIRS);
126
+ var CSS_QUOTES = ['"', "'"];
127
+ var CSS_NEWLINES = ["\n", "\r", "\f"];
128
+ var CSS_REPLACEMENT_CHARACTER = "\uFFFD";
129
+ var MAX_CODE_POINT = 1114111;
130
+ var HEX_RADIX = 16;
131
+ var SURROGATE_MIN = 55296;
132
+ var SURROGATE_MAX = 57343;
133
+ var NOTICE_HEX_COLOR_RE = /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
134
+ var DEFAULT_TTS_HIGHLIGHT_BG = "#FFF9C4";
135
+ var NOTICE_THEME_VARS = {
136
+ accent: "--redacto-accent",
137
+ controlOff: "--redacto-control-off",
138
+ knob: "--redacto-toggle-knob",
139
+ ttsBackground: "--redacto-tts-bg",
140
+ ttsText: "--redacto-tts-text"
141
+ };
142
+ var THEMED_ACCENT = `var(${NOTICE_THEME_VARS.accent}, #4f87ff)`;
143
+ var THEMED_CONTROL_OFF = `var(${NOTICE_THEME_VARS.controlOff}, #d0d5dd)`;
144
+ var THEMED_TTS_BACKGROUND = `var(${NOTICE_THEME_VARS.ttsBackground}, ${DEFAULT_TTS_HIGHLIGHT_BG})`;
145
+ var TTS_TEXT_ATTRIBUTE = "data-redacto-tts-text";
146
+
147
+ // src/shared/appearance/ttsCssVariables.ts
148
+ var buildTtsCssVariables = (colors) => Object.fromEntries(
149
+ [
150
+ [NOTICE_THEME_VARS.ttsBackground, colors.tts_highlight_bg],
151
+ [NOTICE_THEME_VARS.ttsText, colors.tts_highlight_text]
152
+ ].filter(([, value]) => value !== void 0)
153
+ );
154
+ var buildTtsRootAttributes = (colors) => colors.tts_highlight_text ? { [TTS_TEXT_ATTRIBUTE]: "" } : {};
155
+
156
+ // src/shared/appearance/registry.ts
157
+ var DEFAULT_APPEARANCE = "classic";
158
+ var APPEARANCE_ATTRIBUTE = "data-redacto-appearance";
159
+ var NOTICE_ROOT_ATTRIBUTE = "data-redacto-notice";
160
+ var CLASSIC_LAYOUT = {
161
+ sheet: false,
162
+ collapsibleSections: false,
163
+ switchControl: false,
164
+ pairedFooter: false,
165
+ loaderPill: false,
166
+ sheetOverlays: false
167
+ };
168
+ var APPEARANCES = {
169
+ classic: { layout: CLASSIC_LAYOUT },
170
+ // Minimal and soft restyle classic's surfaces and keep its layout.
171
+ minimal: { layout: CLASSIC_LAYOUT },
172
+ soft: { layout: CLASSIC_LAYOUT },
173
+ glass: {
174
+ layout: {
175
+ sheet: true,
176
+ collapsibleSections: true,
177
+ // Checkboxes until the admin (or the host) picks switches, in every style.
178
+ switchControl: false,
179
+ pairedFooter: true,
180
+ loaderPill: true,
181
+ sheetOverlays: true
182
+ }
183
+ }
184
+ };
185
+ var isAppearance = (value) => typeof value === "string" && Object.prototype.hasOwnProperty.call(APPEARANCES, value);
186
+ var resolveAppearance = (value) => isAppearance(value) ? value : DEFAULT_APPEARANCE;
187
+ var getAppearanceLayout = (appearance) => APPEARANCES[appearance].layout;
188
+
189
+ // src/shared/appearance/layout.ts
190
+ var resolveNoticeLayout = (appearance) => {
191
+ var _a, _b, _c;
192
+ const defaults = getAppearanceLayout(appearance.style);
193
+ return {
194
+ mobileSheet: appearance.mobileLayout ? appearance.mobileLayout === "sheet" : defaults.sheet,
195
+ desktopPosition: (_a = appearance.desktopPosition) != null ? _a : DEFAULT_DESKTOP_POSITION,
196
+ dimBackdrop: appearance.backdrop !== "none",
197
+ collapsibleSections: (_b = appearance.collapsibleSections) != null ? _b : defaults.collapsibleSections,
198
+ pairedFooter: appearance.phoneFooter ? appearance.phoneFooter === "paired" : defaults.pairedFooter,
199
+ switchControl: appearance.controlStyle ? appearance.controlStyle === "switch" : defaults.switchControl,
200
+ motion: (_c = appearance.motion) != null ? _c : true
201
+ };
202
+ };
203
+
204
+ // src/shared/appearance/motion.ts
205
+ var MOTION = {
206
+ /** iOS sheet spring: fast start, long soft settle. */
207
+ sheetEase: "cubic-bezier(0.32, 0.72, 0, 1)",
208
+ outEase: "cubic-bezier(0.22, 1, 0.36, 1)",
209
+ sheetMs: 620,
210
+ dimMs: 420,
211
+ collapseMs: 380,
212
+ /** Classic modals fade out quickly; glass sheets take `dimMs`. */
213
+ fadeMs: 200,
214
+ /**
215
+ * Classic Privacy Center accordions. Must not exceed `collapseMs`, after
216
+ * which PCAccordionBody lifts the clipping on an open body.
217
+ */
218
+ classicCollapseMs: 300
219
+ };
220
+ var COLLAPSE_EASE = `${MOTION.collapseMs}ms ${MOTION.sheetEase}`;
221
+ var prefersReducedMotion = () => {
222
+ var _a, _b;
223
+ return typeof window !== "undefined" && ((_b = (_a = window.matchMedia) == null ? void 0 : _a.call(window, "(prefers-reduced-motion: reduce)")) == null ? void 0 : _b.matches) === true;
224
+ };
225
+
226
+ // src/shared/appearance/customCss.ts
227
+ var decodeCssEscapes = (value) => value.replace(
228
+ CSS_ESCAPE_RE,
229
+ (_match, hex, literal) => {
230
+ if (hex === void 0) {
231
+ return literal;
232
+ }
233
+ const codePoint = Number.parseInt(hex, HEX_RADIX);
234
+ if (codePoint === 0 || codePoint > MAX_CODE_POINT || codePoint >= SURROGATE_MIN && codePoint <= SURROGATE_MAX) {
235
+ return CSS_REPLACEMENT_CHARACTER;
236
+ }
237
+ return String.fromCodePoint(codePoint);
238
+ }
239
+ );
240
+ var hasBalancedBlocks = (css) => {
241
+ const open = [];
242
+ let quote;
243
+ let inComment = false;
244
+ for (let index = 0; index < css.length; index += 1) {
245
+ const char = css[index];
246
+ if (inComment) {
247
+ if (char === "*" && css[index + 1] === "/") {
248
+ inComment = false;
249
+ index += 1;
250
+ }
251
+ } else if (char === "\\") {
252
+ index += 1;
253
+ } else if (quote) {
254
+ if (char === quote) {
255
+ quote = void 0;
256
+ } else if (CSS_NEWLINES.includes(char)) {
257
+ return false;
258
+ }
259
+ } else if (char === "/" && css[index + 1] === "*") {
260
+ inComment = true;
261
+ index += 1;
262
+ } else if (CSS_QUOTES.includes(char)) {
263
+ quote = char;
264
+ } else if (char in CSS_BRACKET_PAIRS) {
265
+ open.push(CSS_BRACKET_PAIRS[char]);
266
+ } else if (CSS_CLOSING_BRACKETS.includes(char) && open.pop() !== char) {
267
+ return false;
268
+ }
269
+ }
270
+ return open.length === 0 && !quote && !inComment;
271
+ };
272
+ var isSafeCustomCss = (css) => {
273
+ if (css.length > NOTICE_CUSTOM_CSS_MAX_LENGTH || !hasBalancedBlocks(css)) {
274
+ return false;
275
+ }
276
+ const lowered = css.toLowerCase();
277
+ const decoded = decodeCssEscapes(css).toLowerCase();
278
+ return NOTICE_DISALLOWED_CSS_TOKENS.every(
279
+ (token) => !lowered.includes(token) && !decoded.includes(token)
280
+ );
281
+ };
282
+
283
+ // src/shared/appearance/resolve.ts
284
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
285
+ var pickColors = (raw) => {
286
+ if (!isRecord(raw)) {
287
+ return {};
288
+ }
289
+ const colors = {};
290
+ for (const key of NOTICE_COLOR_KEYS) {
291
+ const value = raw[key];
292
+ if (typeof value === "string" && NOTICE_HEX_COLOR_RE.test(value)) {
293
+ colors[key] = value;
294
+ }
295
+ }
296
+ return colors;
297
+ };
298
+ var pickPx = (raw, min, max) => {
299
+ if (typeof raw !== "number" || !Number.isFinite(raw)) {
300
+ return void 0;
301
+ }
302
+ return `${Math.min(max, Math.max(min, Math.round(raw)))}px`;
303
+ };
304
+ var pickOption = (options, raw) => options.find((option) => option === raw);
305
+ var pickBoolean = (raw) => typeof raw === "boolean" ? raw : void 0;
306
+ var pickCustomCss = (raw) => typeof raw === "string" && raw.trim() && isSafeCustomCss(raw) ? raw : void 0;
307
+ var resolveNoticeAppearance = (raw) => {
308
+ const appearance = isRecord(raw) ? raw : {};
309
+ return {
310
+ style: resolveAppearance(appearance.style),
311
+ colors: pickColors(appearance.colors),
312
+ radius: pickPx(
313
+ appearance.border_radius,
314
+ NOTICE_RADIUS_MIN,
315
+ NOTICE_RADIUS_MAX
316
+ ),
317
+ selectionControl: pickOption(
318
+ NOTICE_SELECTION_CONTROLS,
319
+ appearance.selection_control
320
+ ),
321
+ controlStyle: pickOption(NOTICE_CONTROL_STYLES, appearance.control_style),
322
+ confirmBeforeSubmit: appearance.confirm_before_submit === true,
323
+ mobileLayout: pickOption(NOTICE_MOBILE_LAYOUTS, appearance.mobile_layout),
324
+ desktopPosition: pickOption(
325
+ NOTICE_DESKTOP_POSITIONS,
326
+ appearance.desktop_position
327
+ ),
328
+ backdrop: pickOption(NOTICE_BACKDROPS, appearance.backdrop),
329
+ collapsibleSections: pickBoolean(appearance.collapsible_sections),
330
+ phoneFooter: pickOption(NOTICE_PHONE_FOOTERS, appearance.phone_footer),
331
+ autoDark: appearance.auto_dark === true,
332
+ darkColors: pickColors(appearance.dark_colors),
333
+ maxWidth: pickPx(appearance.max_width, NOTICE_WIDTH_MIN, NOTICE_WIDTH_MAX),
334
+ logoSize: pickPx(
335
+ appearance.logo_size,
336
+ NOTICE_LOGO_SIZE_MIN,
337
+ NOTICE_LOGO_SIZE_MAX
338
+ ),
339
+ textScale: pickOption(NOTICE_TEXT_SCALES, appearance.text_scale),
340
+ motion: pickBoolean(appearance.motion),
341
+ customCss: pickCustomCss(appearance.custom_css)
342
+ };
343
+ };
344
+ var withColorScheme = (appearance, prefersDark) => appearance.autoDark && prefersDark && Object.keys(appearance.darkColors).length > 0 ? { ...appearance, colors: appearance.darkColors } : appearance;
345
+
346
+ // src/shared/appearance/color.ts
347
+ var HEX_RE = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
348
+ var NUM = String.raw`(\d+(?:\.\d+)?|\.\d+)`;
349
+ var RGB_RE = new RegExp(
350
+ String.raw`^rgba?\(\s*${NUM}(%?)[\s,]+${NUM}(%?)[\s,]+${NUM}(%?)(?:[\s,/]+${NUM}(%?))?\s*\)$`,
351
+ "i"
352
+ );
353
+ var BYTE_MAX = 255;
354
+ var PERCENT_MAX = 100;
355
+ var clampByte = (n) => Math.min(255, Math.max(0, Math.round(n)));
356
+ var clampUnit = (n) => Math.min(1, Math.max(0, n));
357
+ var parseColor = (input) => {
358
+ if (!input) {
359
+ return null;
360
+ }
361
+ const value = input.trim();
362
+ const hex = HEX_RE.exec(value);
363
+ if (hex) {
364
+ let digits = hex[1];
365
+ if (digits.length <= 4) {
366
+ digits = [...digits].map((d) => d + d).join("");
367
+ }
368
+ const n = (i) => parseInt(digits.slice(i, i + 2), 16);
369
+ return {
370
+ r: n(0),
371
+ g: n(2),
372
+ b: n(4),
373
+ a: digits.length === 8 ? n(6) / 255 : 1
374
+ };
375
+ }
376
+ const rgb = RGB_RE.exec(value);
377
+ if (rgb) {
378
+ const channel = (value2, unit) => unit ? Number(value2) * BYTE_MAX / PERCENT_MAX : Number(value2);
379
+ const r = channel(rgb[1], rgb[2]);
380
+ const g = channel(rgb[3], rgb[4]);
381
+ const b = channel(rgb[5], rgb[6]);
382
+ const alpha = rgb[7];
383
+ const a = alpha === void 0 ? 1 : Number(alpha) / (rgb[8] ? 100 : 1);
384
+ return {
385
+ r: clampByte(r),
386
+ g: clampByte(g),
387
+ b: clampByte(b),
388
+ a: clampUnit(a)
389
+ };
390
+ }
391
+ return null;
392
+ };
393
+ var NAMED_COLORS = new Set(
394
+ "aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen transparent currentcolor".split(" ")
395
+ );
396
+ var ARG = String.raw`[^();{}<>"'\\]`;
397
+ var COLOR_FUNCTION_RE = new RegExp(
398
+ String.raw`^(?:hsla?|hwb|lab|lch|oklab|oklch|color|color-mix|var)\((?:${ARG}|\(${ARG}*\))*\)$`,
399
+ "i"
400
+ );
401
+ var URL_RE = /url\s*\(/i;
402
+ var isPassthroughColor = (value) => NAMED_COLORS.has(value.toLowerCase()) || COLOR_FUNCTION_RE.test(value) && !URL_RE.test(value);
403
+ var paintableColor = (color, fallback) => {
404
+ const value = color == null ? void 0 : color.trim();
405
+ if (!value) {
406
+ return fallback;
407
+ }
408
+ return parseColor(value) || isPassthroughColor(value) ? value : fallback;
409
+ };
410
+ var format = ({ r, g, b, a }) => `rgba(${clampByte(r)}, ${clampByte(g)}, ${clampByte(b)}, ${Number(clampUnit(a).toFixed(3))})`;
411
+ var percent = (t) => `${Number((clampUnit(t) * 100).toFixed(1))}%`;
412
+ var withAlpha = (color, alpha) => {
413
+ const parsed = parseColor(color);
414
+ if (parsed) {
415
+ return format({ ...parsed, a: parsed.a * alpha });
416
+ }
417
+ return `color-mix(in srgb, ${color} ${percent(alpha)}, transparent)`;
418
+ };
419
+ var mix = (a, b, t) => {
420
+ const pa = parseColor(a);
421
+ const pb = parseColor(b);
422
+ if (pa && pb) {
423
+ const k = clampUnit(t);
424
+ return format({
425
+ r: pa.r + (pb.r - pa.r) * k,
426
+ g: pa.g + (pb.g - pa.g) * k,
427
+ b: pa.b + (pb.b - pa.b) * k,
428
+ a: 1
429
+ });
430
+ }
431
+ return `color-mix(in srgb, ${a} ${percent(1 - t)}, ${b})`;
432
+ };
433
+ var opaqueOver = (color, base) => {
434
+ const pc = parseColor(color);
435
+ const pb = parseColor(base);
436
+ if (!pc || !pb) {
437
+ return color;
438
+ }
439
+ return mix(base, format({ ...pc, a: 1 }), pc.a);
440
+ };
441
+
442
+ // src/shared/appearance/glass.ts
443
+ var SHADE_BASE = "#05050f";
444
+ var GLASS_DEFAULT_TINT = "#ffffff";
445
+ var GLASS_RADIUS = {
446
+ sheet: "28px",
447
+ panel: "20px",
448
+ control: "12px",
449
+ row: "10px",
450
+ pill: "999px"
451
+ };
452
+ var GLASS_BLUR = "blur(30px) saturate(180%)";
453
+ var glassPalette = ({
454
+ accent,
455
+ tint = GLASS_DEFAULT_TINT
456
+ }) => {
457
+ const shade = mix(accent, SHADE_BASE, 0.7);
458
+ return {
459
+ accent,
460
+ sheet: withAlpha(tint, 0.52),
461
+ sheetDense: withAlpha(tint, 0.86),
462
+ sheetEdge: withAlpha(tint, 0.6),
463
+ panel: withAlpha(tint, 0.5),
464
+ panelEdge: withAlpha(tint, 0.55),
465
+ inset: withAlpha(tint, 0.55),
466
+ control: withAlpha(tint, 0.72),
467
+ footer: `linear-gradient(to bottom, ${withAlpha(tint, 0.35)} 0%, ${withAlpha(tint, 0.9)} 60%)`,
468
+ accentFaint: withAlpha(accent, 0.04),
469
+ accentWash: withAlpha(accent, 0.08),
470
+ accentWashStrong: withAlpha(accent, 0.22),
471
+ hairline: withAlpha(shade, 0.08),
472
+ outline: withAlpha(shade, 0.14),
473
+ dim: withAlpha(shade, 0.3),
474
+ shadow: `0 -18px 50px -12px ${withAlpha(shade, 0.35)}`,
475
+ floatShadow: `0 24px 60px -18px ${withAlpha(shade, 0.45)}`,
476
+ primaryShadow: `0 10px 24px -10px ${withAlpha(accent, 0.6)}`
477
+ };
478
+ };
479
+
480
+ // src/shared/useIsomorphicLayoutEffect.ts
481
+ import { useEffect, useLayoutEffect } from "react";
482
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
483
+
484
+ // src/shared/Collapse.tsx
485
+ import { useEffect as useEffect2, useRef, useState } from "react";
486
+ import { jsx } from "react/jsx-runtime";
487
+ var Collapse = ({ open, id, children }) => {
488
+ const [mounted, setMounted] = useState(open);
489
+ const [expanded, setExpanded] = useState(open);
490
+ const frameRef = useRef(null);
491
+ const rootRef = useRef(null);
492
+ useEffect2(() => {
493
+ if (open) {
494
+ setMounted(true);
495
+ frameRef.current = requestAnimationFrame(() => {
496
+ frameRef.current = requestAnimationFrame(() => setExpanded(true));
497
+ });
498
+ return () => {
499
+ if (frameRef.current !== null) {
500
+ cancelAnimationFrame(frameRef.current);
501
+ }
502
+ };
503
+ }
504
+ setExpanded(false);
505
+ if (prefersReducedMotion()) {
506
+ setMounted(false);
507
+ return;
508
+ }
509
+ const timer = setTimeout(() => setMounted(false), MOTION.collapseMs);
510
+ return () => clearTimeout(timer);
511
+ }, [open]);
512
+ useIsomorphicLayoutEffect(() => {
513
+ const root = rootRef.current;
514
+ if (!root) {
515
+ return;
516
+ }
517
+ root.toggleAttribute("inert", !open);
518
+ if (open) {
519
+ root.removeAttribute("aria-hidden");
520
+ } else {
521
+ root.setAttribute("aria-hidden", "true");
522
+ }
523
+ }, [open, mounted]);
524
+ if (!mounted) {
525
+ return null;
526
+ }
527
+ const still = prefersReducedMotion();
528
+ return /* @__PURE__ */ jsx(
529
+ "div",
530
+ {
531
+ ref: rootRef,
532
+ id,
533
+ style: {
534
+ display: "grid",
535
+ gridTemplateRows: expanded ? "1fr" : "0fr",
536
+ transition: still ? "none" : `grid-template-rows ${COLLAPSE_EASE}`
537
+ },
538
+ children: /* @__PURE__ */ jsx("div", { style: { overflow: "hidden", minHeight: 0 }, children: /* @__PURE__ */ jsx(
539
+ "div",
540
+ {
541
+ style: {
542
+ opacity: expanded ? 1 : 0,
543
+ transform: expanded ? "none" : "translateY(-4px)",
544
+ transition: still ? "none" : `opacity 260ms ${MOTION.outEase}, transform 320ms ${MOTION.outEase}`,
545
+ transitionDelay: expanded ? "80ms" : "0ms"
546
+ },
547
+ children
548
+ }
549
+ ) })
550
+ }
551
+ );
552
+ };
553
+
554
+ // src/RedactoNoticeConsent/useMediaQuery.ts
555
+ import { useState as useState2, useEffect as useEffect3 } from "react";
556
+ var useMediaQuery = (query) => {
557
+ const getMatches = () => typeof window !== "undefined" ? window.matchMedia(query).matches : false;
558
+ const [matches, setMatches] = useState2(getMatches);
559
+ useEffect3(() => {
560
+ if (typeof window === "undefined") return;
561
+ const media = window.matchMedia(query);
562
+ const listener = (e) => {
563
+ setMatches(e.matches);
564
+ };
565
+ if (media.addEventListener) {
566
+ media.addEventListener("change", listener);
567
+ } else {
568
+ media.addListener(listener);
569
+ }
570
+ return () => {
571
+ if (media.removeEventListener) {
572
+ media.removeEventListener("change", listener);
573
+ } else {
574
+ media.removeListener(listener);
575
+ }
576
+ };
577
+ }, [query]);
578
+ return matches;
579
+ };
580
+
581
+ // src/shared/sandbox.ts
582
+ var SANDBOX_TOKEN_HEADER = "X-Consent-Token";
583
+
584
+ export {
585
+ SANDBOX_TOKEN_HEADER,
586
+ withIdempotencyKey,
587
+ DEFAULT_TEXT_SCALE,
588
+ NOTICE_TEXT_SCALE_FACTORS,
589
+ NOTICE_THEME_VARS,
590
+ THEMED_ACCENT,
591
+ THEMED_CONTROL_OFF,
592
+ THEMED_TTS_BACKGROUND,
593
+ TTS_TEXT_ATTRIBUTE,
594
+ buildTtsCssVariables,
595
+ buildTtsRootAttributes,
596
+ isSafeCustomCss,
597
+ DEFAULT_APPEARANCE,
598
+ APPEARANCE_ATTRIBUTE,
599
+ NOTICE_ROOT_ATTRIBUTE,
600
+ isAppearance,
601
+ resolveAppearance,
602
+ getAppearanceLayout,
603
+ resolveNoticeLayout,
604
+ resolveNoticeAppearance,
605
+ withColorScheme,
606
+ paintableColor,
607
+ withAlpha,
608
+ opaqueOver,
609
+ GLASS_DEFAULT_TINT,
610
+ GLASS_RADIUS,
611
+ GLASS_BLUR,
612
+ glassPalette,
613
+ MOTION,
614
+ prefersReducedMotion,
615
+ useIsomorphicLayoutEffect,
616
+ Collapse,
617
+ useMediaQuery
618
+ };