@flamingo-stack/openframe-frontend-core 0.0.406 → 0.0.407-snapshot.20260711133234
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-3KEBI6TP.cjs → chunk-2B6773QM.cjs} +1 -1
- package/dist/chunk-2B6773QM.cjs.map +1 -0
- package/dist/{chunk-TFDNJTBX.js → chunk-5HOII3WB.js} +4 -4
- package/dist/{chunk-E6YHDT3D.js → chunk-7Q52HP45.js} +2 -2
- package/dist/chunk-7Q52HP45.js.map +1 -0
- package/dist/{chunk-VZGU4BV6.cjs → chunk-A3RW7M6B.cjs} +29 -29
- package/dist/{chunk-VZGU4BV6.cjs.map → chunk-A3RW7M6B.cjs.map} +1 -1
- package/dist/{chunk-QAFRLO5N.cjs → chunk-CUN4CJUN.cjs} +10 -11
- package/dist/chunk-CUN4CJUN.cjs.map +1 -0
- package/dist/{chunk-6WMVBCNA.js → chunk-E4J5KKHH.js} +3 -3
- package/dist/{chunk-P43ZHPM6.cjs → chunk-F4KE5AZO.cjs} +40 -40
- package/dist/{chunk-P43ZHPM6.cjs.map → chunk-F4KE5AZO.cjs.map} +1 -1
- package/dist/{chunk-PQN5R5II.js → chunk-IK5AR6QG.js} +7 -7
- package/dist/{chunk-SOK32D6C.js → chunk-IVM47EGI.js} +346 -297
- package/dist/chunk-IVM47EGI.js.map +1 -0
- package/dist/{chunk-AYFWYOL6.js → chunk-J6M2G6TD.js} +7 -7
- package/dist/{chunk-NA7XJRWB.js → chunk-KFQXO7QB.js} +1 -1
- package/dist/chunk-KFQXO7QB.js.map +1 -0
- package/dist/{chunk-ISYVNPRT.cjs → chunk-KPGKNUJF.cjs} +42 -42
- package/dist/{chunk-ISYVNPRT.cjs.map → chunk-KPGKNUJF.cjs.map} +1 -1
- package/dist/{chunk-GNS65HNC.cjs → chunk-KRLBVBYU.cjs} +18 -18
- package/dist/{chunk-GNS65HNC.cjs.map → chunk-KRLBVBYU.cjs.map} +1 -1
- package/dist/{chunk-ZVUR3A33.js → chunk-KZPVN7AZ.js} +2 -2
- package/dist/{chunk-7RVSGGO6.js → chunk-L6OPVKVR.js} +6 -6
- package/dist/{chunk-OATWTTCI.js → chunk-LQWYXIKB.js} +6 -6
- package/dist/{chunk-COERBDG4.cjs → chunk-NEMUCNCG.cjs} +75 -75
- package/dist/{chunk-COERBDG4.cjs.map → chunk-NEMUCNCG.cjs.map} +1 -1
- package/dist/{chunk-PNJ5LMVK.js → chunk-NO2KT42M.js} +9 -10
- package/dist/{chunk-PNJ5LMVK.js.map → chunk-NO2KT42M.js.map} +1 -1
- package/dist/{chunk-LSPBPUQM.js → chunk-O5W26SCL.js} +2 -2
- package/dist/{chunk-VANFD4FX.cjs → chunk-RL344I7V.cjs} +7 -7
- package/dist/{chunk-VANFD4FX.cjs.map → chunk-RL344I7V.cjs.map} +1 -1
- package/dist/{chunk-CF3LBCNI.cjs → chunk-RPGFIUN3.cjs} +35 -35
- package/dist/{chunk-CF3LBCNI.cjs.map → chunk-RPGFIUN3.cjs.map} +1 -1
- package/dist/{chunk-LL4QQSAD.cjs → chunk-SOALFYU6.cjs} +479 -430
- package/dist/chunk-SOALFYU6.cjs.map +1 -0
- package/dist/{chunk-CCIBZ7TO.cjs → chunk-STGWQFYT.cjs} +5 -5
- package/dist/chunk-STGWQFYT.cjs.map +1 -0
- package/dist/{chunk-VIB4SGCD.cjs → chunk-T6ZUBXPA.cjs} +14 -14
- package/dist/{chunk-VIB4SGCD.cjs.map → chunk-T6ZUBXPA.cjs.map} +1 -1
- package/dist/{chunk-HJJXNMM7.cjs → chunk-V3EZQWHQ.cjs} +7 -7
- package/dist/{chunk-HJJXNMM7.cjs.map → chunk-V3EZQWHQ.cjs.map} +1 -1
- package/dist/{chunk-3KZXOLFK.js → chunk-VUW4G6SU.js} +4 -4
- package/dist/{chunk-JAPP2F4M.cjs → chunk-WGOATV3P.cjs} +77 -77
- package/dist/{chunk-JAPP2F4M.cjs.map → chunk-WGOATV3P.cjs.map} +1 -1
- package/dist/{chunk-SF4G7M6U.cjs → chunk-YRKPMYI3.cjs} +15 -15
- package/dist/{chunk-SF4G7M6U.cjs.map → chunk-YRKPMYI3.cjs.map} +1 -1
- package/dist/{chunk-XXG2Z2JT.js → chunk-ZF5O3KUQ.js} +3 -3
- package/dist/{chunk-C3NPMZBI.js → chunk-ZMFQWMM7.js} +3 -3
- package/dist/components/announcement-bar.d.ts.map +1 -1
- package/dist/components/case-studies/index.cjs +11 -10
- package/dist/components/case-studies/index.cjs.map +1 -1
- package/dist/components/case-studies/index.js +5 -4
- package/dist/components/case-studies/index.js.map +1 -1
- package/dist/components/chat/index.cjs +5 -4
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +4 -3
- package/dist/components/chat/quick-action-chip.d.ts +11 -2
- package/dist/components/chat/quick-action-chip.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +6 -5
- package/dist/components/contact/index.cjs.map +1 -1
- package/dist/components/contact/index.js +5 -4
- package/dist/components/docs/index.cjs +8 -8
- package/dist/components/docs/index.js +7 -7
- package/dist/components/embeds/index.cjs +6 -5
- package/dist/components/embeds/index.cjs.map +1 -1
- package/dist/components/embeds/index.js +5 -4
- package/dist/components/embeds/reddit-embed-client.d.ts.map +1 -1
- package/dist/components/faq/index.cjs +7 -6
- package/dist/components/faq/index.cjs.map +1 -1
- package/dist/components/faq/index.js +6 -5
- package/dist/components/features/index.cjs +5 -4
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.js +4 -3
- package/dist/components/help-center-pages/index.cjs +24 -24
- package/dist/components/help-center-pages/index.js +15 -15
- package/dist/components/index.cjs +186 -180
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +39 -33
- package/dist/components/index.js.map +1 -1
- package/dist/components/media-carousel.d.ts +28 -4
- package/dist/components/media-carousel.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +5 -4
- package/dist/components/navigation/index.cjs.map +1 -1
- package/dist/components/navigation/index.js +4 -3
- package/dist/components/onboarding-guides/index.cjs +9 -9
- package/dist/components/onboarding-guides/index.js +8 -8
- package/dist/components/related-content/index.cjs +7 -6
- package/dist/components/related-content/index.cjs.map +1 -1
- package/dist/components/related-content/index.js +6 -5
- package/dist/components/tickets/index.cjs +8 -8
- package/dist/components/tickets/index.js +7 -7
- package/dist/components/ui/index.cjs +5 -4
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +4 -3
- package/dist/components/ui/tag.d.ts +3 -2
- package/dist/components/ui/tag.d.ts.map +1 -1
- package/dist/contexts/endpoints-runtime-context.d.ts +1 -1
- package/dist/contexts/endpoints-runtime-context.d.ts.map +1 -1
- package/dist/contexts/index.cjs +2 -2
- package/dist/contexts/index.js +1 -1
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/hooks/use-self-fetch.d.ts.map +1 -1
- package/dist/index.cjs +5 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -5
- package/dist/types/announcement.cjs.map +1 -1
- package/dist/types/announcement.d.ts +0 -6
- package/dist/types/announcement.d.ts.map +1 -1
- package/dist/types/announcement.js.map +1 -1
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/announcement-storage.d.ts +3 -0
- package/dist/utils/announcement-storage.d.ts.map +1 -1
- package/dist/utils/app-config.d.ts.map +1 -1
- package/dist/utils/color-analysis.d.ts +0 -4
- package/dist/utils/color-analysis.d.ts.map +1 -1
- package/dist/utils/index.cjs +186 -177
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +185 -176
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/media-carousel-utils-stub.d.ts +3 -1
- package/dist/utils/media-carousel-utils-stub.d.ts.map +1 -1
- package/dist/utils/ods-color-utils.d.ts +2 -1
- package/dist/utils/ods-color-utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/announcement-bar.tsx +38 -21
- package/src/components/chat/quick-action-chip.tsx +18 -4
- package/src/components/embeds/reddit-embed-client.tsx +0 -3
- package/src/components/index.ts +5 -0
- package/src/components/media-carousel.tsx +195 -149
- package/src/components/ui/tag.tsx +21 -3
- package/src/contexts/endpoints-runtime-context.tsx +1 -1
- package/src/hooks/use-self-fetch.ts +10 -9
- package/src/stories/AnnouncementBar.stories.tsx +8 -14
- package/src/styles/ods-design-tokens.css +2 -0
- package/src/types/announcement.ts +0 -8
- package/src/utils/announcement-storage.ts +17 -0
- package/src/utils/app-config.ts +3 -2
- package/src/utils/color-analysis.ts +5 -2
- package/src/utils/media-carousel-utils-stub.ts +3 -1
- package/src/utils/ods-color-utils.ts +4 -7
- package/dist/chunk-3KEBI6TP.cjs.map +0 -1
- package/dist/chunk-CCIBZ7TO.cjs.map +0 -1
- package/dist/chunk-E6YHDT3D.js.map +0 -1
- package/dist/chunk-LL4QQSAD.cjs.map +0 -1
- package/dist/chunk-NA7XJRWB.js.map +0 -1
- package/dist/chunk-QAFRLO5N.cjs.map +0 -1
- package/dist/chunk-SOK32D6C.js.map +0 -1
- package/dist/utils/media-carousel-utils.d.ts +0 -10
- package/dist/utils/media-carousel-utils.d.ts.map +0 -1
- package/src/utils/.media-carousel-utils.md +0 -31
- package/src/utils/media-carousel-utils.ts +0 -22
- /package/dist/{chunk-TFDNJTBX.js.map → chunk-5HOII3WB.js.map} +0 -0
- /package/dist/{chunk-6WMVBCNA.js.map → chunk-E4J5KKHH.js.map} +0 -0
- /package/dist/{chunk-PQN5R5II.js.map → chunk-IK5AR6QG.js.map} +0 -0
- /package/dist/{chunk-AYFWYOL6.js.map → chunk-J6M2G6TD.js.map} +0 -0
- /package/dist/{chunk-ZVUR3A33.js.map → chunk-KZPVN7AZ.js.map} +0 -0
- /package/dist/{chunk-7RVSGGO6.js.map → chunk-L6OPVKVR.js.map} +0 -0
- /package/dist/{chunk-OATWTTCI.js.map → chunk-LQWYXIKB.js.map} +0 -0
- /package/dist/{chunk-LSPBPUQM.js.map → chunk-O5W26SCL.js.map} +0 -0
- /package/dist/{chunk-3KZXOLFK.js.map → chunk-VUW4G6SU.js.map} +0 -0
- /package/dist/{chunk-XXG2Z2JT.js.map → chunk-ZF5O3KUQ.js.map} +0 -0
- /package/dist/{chunk-C3NPMZBI.js.map → chunk-ZMFQWMM7.js.map} +0 -0
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
useOgPlaceholderUrl,
|
|
27
27
|
useOnboardingState,
|
|
28
28
|
useToast
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-7Q52HP45.js";
|
|
30
30
|
import {
|
|
31
31
|
AlertCircleIcon,
|
|
32
32
|
AnthropicLogoGreyIcon,
|
|
@@ -90,6 +90,7 @@ import {
|
|
|
90
90
|
GraduationCapIcon,
|
|
91
91
|
HeadphoneIcon,
|
|
92
92
|
HubspotLogoGreyIcon,
|
|
93
|
+
ImageOffIcon,
|
|
93
94
|
ImagePlusIcon,
|
|
94
95
|
InfoCircleIcon,
|
|
95
96
|
LayersIcon,
|
|
@@ -150,7 +151,7 @@ import {
|
|
|
150
151
|
import {
|
|
151
152
|
useChatRuntime,
|
|
152
153
|
useRequiredChatRuntime
|
|
153
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-KFQXO7QB.js";
|
|
154
155
|
import {
|
|
155
156
|
dynamic
|
|
156
157
|
} from "./chunk-EL5YVPD5.js";
|
|
@@ -424,11 +425,18 @@ init_cn();
|
|
|
424
425
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
425
426
|
var tagVariants = cva(
|
|
426
427
|
[
|
|
427
|
-
"
|
|
428
|
+
"inline-flex items-center justify-center rounded-md",
|
|
428
429
|
"transition-colors duration-150"
|
|
429
430
|
],
|
|
430
431
|
{
|
|
431
432
|
variants: {
|
|
433
|
+
// Chip scale. `default` is the classic 32px mono-uppercase tag; `large`
|
|
434
|
+
// is the Figma "Feature Item" chip (48px, h3 bold body label) used by
|
|
435
|
+
// chip groups acting as tabs (OpenFrame categories).
|
|
436
|
+
size: {
|
|
437
|
+
default: "text-h5 h-8 gap-[var(--spacing-system-xxs)] p-[var(--spacing-system-xsf)]",
|
|
438
|
+
large: "text-h3 font-bold h-12 gap-[var(--spacing-system-xs)] p-[var(--spacing-system-s)]"
|
|
439
|
+
},
|
|
432
440
|
variant: {
|
|
433
441
|
primary: [
|
|
434
442
|
"bg-[var(--ods-open-yellow-base)] text-[var(--ods-system-greys-black)]",
|
|
@@ -459,6 +467,15 @@ var tagVariants = cva(
|
|
|
459
467
|
"bg-[var(--ods-system-greys-soft-grey)] text-[var(--ods-system-greys-grey)]",
|
|
460
468
|
"hover:bg-[var(--ods-system-greys-soft-grey-hover)] active:bg-[var(--ods-system-greys-soft-grey-action)]"
|
|
461
469
|
],
|
|
470
|
+
// Active/selected chip state (Figma "Feature Item" active): pink
|
|
471
|
+
// border + pink-secondary fill. A dedicated variant (not appended
|
|
472
|
+
// utilities) so its own hover rules win — the outline variant's
|
|
473
|
+
// hover:bg/hover:border would otherwise repaint an active chip grey.
|
|
474
|
+
selected: [
|
|
475
|
+
"bg-[var(--ods-flamingo-pink-secondary)] text-[var(--ods-system-greys-white)] border border-[var(--ods-flamingo-pink-base)]",
|
|
476
|
+
"hover:bg-[var(--ods-flamingo-pink-secondary-hover)] hover:border-[var(--ods-flamingo-pink-base)]",
|
|
477
|
+
"active:bg-[var(--ods-flamingo-pink-secondary-action)]"
|
|
478
|
+
],
|
|
462
479
|
// Matches the EntityTagBadges / StatusBadge tag skin (ods-card + ods-border,
|
|
463
480
|
// mono uppercase) so the tag-editor chips render identically to the public
|
|
464
481
|
// tag badges. Used for FilterChipData variant 'tag' (see search-input).
|
|
@@ -469,7 +486,8 @@ var tagVariants = cva(
|
|
|
469
486
|
}
|
|
470
487
|
},
|
|
471
488
|
defaultVariants: {
|
|
472
|
-
variant: "primary"
|
|
489
|
+
variant: "primary",
|
|
490
|
+
size: "default"
|
|
473
491
|
}
|
|
474
492
|
}
|
|
475
493
|
);
|
|
@@ -482,6 +500,7 @@ var disabledTagClasses = [
|
|
|
482
500
|
function Tag({
|
|
483
501
|
label,
|
|
484
502
|
variant,
|
|
503
|
+
size: size2,
|
|
485
504
|
icon,
|
|
486
505
|
onClose,
|
|
487
506
|
className,
|
|
@@ -494,14 +513,14 @@ function Tag({
|
|
|
494
513
|
Comp,
|
|
495
514
|
{
|
|
496
515
|
className: cn(
|
|
497
|
-
tagVariants({ variant }),
|
|
516
|
+
tagVariants({ variant, size: size2 }),
|
|
498
517
|
disabled && disabledTagClasses,
|
|
499
518
|
className
|
|
500
519
|
),
|
|
501
520
|
"aria-disabled": disabled || void 0,
|
|
502
521
|
...props,
|
|
503
522
|
children: [
|
|
504
|
-
icon && /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center size-
|
|
523
|
+
icon && /* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center shrink-0", size2 === "large" ? "size-6" : "size-5"), children: icon }),
|
|
505
524
|
/* @__PURE__ */ jsx("span", { className: cn("truncate", labelClassName), title: typeof label === "string" ? label : void 0, children: label }),
|
|
506
525
|
onClose && /* @__PURE__ */ jsx(
|
|
507
526
|
"button",
|
|
@@ -2745,6 +2764,174 @@ function ColorSwatch({ color, className }) {
|
|
|
2745
2764
|
// src/components/ui/ticket-status-tag.tsx
|
|
2746
2765
|
init_cn();
|
|
2747
2766
|
|
|
2767
|
+
// src/utils/color-analysis.ts
|
|
2768
|
+
var DESIGN_PALETTE = [
|
|
2769
|
+
{ name: "yellow", hex: "#FFC008", rgb: [255, 192, 8] },
|
|
2770
|
+
{ name: "black", hex: "#161616", rgb: [22, 22, 22] },
|
|
2771
|
+
{ name: "gray", hex: "#888888", rgb: [136, 136, 136] }
|
|
2772
|
+
];
|
|
2773
|
+
function hexToRgb(hex) {
|
|
2774
|
+
const normalized = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (_, r, g, b) => `${r}${r}${g}${g}${b}${b}`);
|
|
2775
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(normalized);
|
|
2776
|
+
return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : [0, 0, 0];
|
|
2777
|
+
}
|
|
2778
|
+
function getLuminance(rgb) {
|
|
2779
|
+
const [r, g, b] = rgb.map((c) => {
|
|
2780
|
+
c = c / 255;
|
|
2781
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
2782
|
+
});
|
|
2783
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
2784
|
+
}
|
|
2785
|
+
function getContrastRatio(color1, color2) {
|
|
2786
|
+
const lum1 = getLuminance(color1);
|
|
2787
|
+
const lum2 = getLuminance(color2);
|
|
2788
|
+
const brightest = Math.max(lum1, lum2);
|
|
2789
|
+
const darkest = Math.min(lum1, lum2);
|
|
2790
|
+
return (brightest + 0.05) / (darkest + 0.05);
|
|
2791
|
+
}
|
|
2792
|
+
function pickReadableTextColor(bgHex) {
|
|
2793
|
+
const bg = hexToRgb(bgHex);
|
|
2794
|
+
const darkContrast = getContrastRatio(bg, [26, 26, 26]);
|
|
2795
|
+
const lightContrast = getContrastRatio(bg, [250, 250, 250]);
|
|
2796
|
+
return darkContrast >= lightContrast ? "dark" : "light";
|
|
2797
|
+
}
|
|
2798
|
+
function extractDominantColor(canvas) {
|
|
2799
|
+
const ctx = canvas.getContext("2d");
|
|
2800
|
+
if (!ctx) return [128, 128, 128];
|
|
2801
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
2802
|
+
const data = imageData.data;
|
|
2803
|
+
const sampleSize = Math.max(1, Math.floor(data.length / (4 * 1e3)));
|
|
2804
|
+
const colorCounts = {};
|
|
2805
|
+
for (let i = 0; i < data.length; i += 4 * sampleSize) {
|
|
2806
|
+
const r = data[i];
|
|
2807
|
+
const g = data[i + 1];
|
|
2808
|
+
const b = data[i + 2];
|
|
2809
|
+
const alpha = data[i + 3];
|
|
2810
|
+
if (alpha < 128) continue;
|
|
2811
|
+
const bucketR = Math.round(r / 32) * 32;
|
|
2812
|
+
const bucketG = Math.round(g / 32) * 32;
|
|
2813
|
+
const bucketB = Math.round(b / 32) * 32;
|
|
2814
|
+
const key = `${bucketR},${bucketG},${bucketB}`;
|
|
2815
|
+
colorCounts[key] = (colorCounts[key] || 0) + 1;
|
|
2816
|
+
}
|
|
2817
|
+
let maxCount = 0;
|
|
2818
|
+
let dominantColor = [128, 128, 128];
|
|
2819
|
+
for (const [colorKey, count] of Object.entries(colorCounts)) {
|
|
2820
|
+
if (count > maxCount) {
|
|
2821
|
+
maxCount = count;
|
|
2822
|
+
const [r, g, b] = colorKey.split(",").map(Number);
|
|
2823
|
+
dominantColor = [r, g, b];
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
return dominantColor;
|
|
2827
|
+
}
|
|
2828
|
+
function getBestContrastColor(imageColor) {
|
|
2829
|
+
let bestColor = DESIGN_PALETTE[0];
|
|
2830
|
+
let bestContrast = 0;
|
|
2831
|
+
for (const color of DESIGN_PALETTE) {
|
|
2832
|
+
const contrast = getContrastRatio(imageColor, color.rgb);
|
|
2833
|
+
if (contrast > bestContrast) {
|
|
2834
|
+
bestContrast = contrast;
|
|
2835
|
+
bestColor = color;
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
return bestContrast >= 3 ? bestColor : DESIGN_PALETTE.find((c) => c.name === "black") || bestColor;
|
|
2839
|
+
}
|
|
2840
|
+
function analyzeImageColor(imageSrc) {
|
|
2841
|
+
return new Promise((resolve, reject) => {
|
|
2842
|
+
const img = new Image();
|
|
2843
|
+
img.crossOrigin = "anonymous";
|
|
2844
|
+
img.onload = () => {
|
|
2845
|
+
try {
|
|
2846
|
+
const canvas = document.createElement("canvas");
|
|
2847
|
+
const ctx = canvas.getContext("2d");
|
|
2848
|
+
if (!ctx) {
|
|
2849
|
+
reject(new Error("Could not get canvas context"));
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
const maxSize = 100;
|
|
2853
|
+
const scale = Math.min(maxSize / img.width, maxSize / img.height);
|
|
2854
|
+
canvas.width = img.width * scale;
|
|
2855
|
+
canvas.height = img.height * scale;
|
|
2856
|
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
2857
|
+
const dominantColor = extractDominantColor(canvas);
|
|
2858
|
+
const bestContrastColor = getBestContrastColor(dominantColor);
|
|
2859
|
+
resolve(bestContrastColor);
|
|
2860
|
+
} catch (error) {
|
|
2861
|
+
reject(error);
|
|
2862
|
+
}
|
|
2863
|
+
};
|
|
2864
|
+
img.onerror = () => reject(new Error("Failed to load image"));
|
|
2865
|
+
img.src = imageSrc;
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
async function extractImageEdgeColorAsync(imageUrl) {
|
|
2869
|
+
return new Promise((resolve) => {
|
|
2870
|
+
if (typeof window === "undefined") {
|
|
2871
|
+
resolve("#000000");
|
|
2872
|
+
return;
|
|
2873
|
+
}
|
|
2874
|
+
const img = new Image();
|
|
2875
|
+
img.crossOrigin = "anonymous";
|
|
2876
|
+
img.onload = () => {
|
|
2877
|
+
try {
|
|
2878
|
+
const canvas = document.createElement("canvas");
|
|
2879
|
+
const ctx = canvas.getContext("2d");
|
|
2880
|
+
if (!ctx) {
|
|
2881
|
+
resolve("#000000");
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
const maxSize = 50;
|
|
2885
|
+
const scale = Math.min(maxSize / img.naturalWidth, maxSize / img.naturalHeight);
|
|
2886
|
+
const w = Math.round(img.naturalWidth * scale);
|
|
2887
|
+
const h = Math.round(img.naturalHeight * scale);
|
|
2888
|
+
canvas.width = w;
|
|
2889
|
+
canvas.height = h;
|
|
2890
|
+
ctx.drawImage(img, 0, 0, w, h);
|
|
2891
|
+
const data = ctx.getImageData(0, 0, w, h).data;
|
|
2892
|
+
const edgeW = Math.max(2, Math.round(w * 0.15));
|
|
2893
|
+
const edgeH = Math.max(2, Math.round(h * 0.15));
|
|
2894
|
+
const bucketSize = 32;
|
|
2895
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
2896
|
+
for (let y = 0; y < h; y++) {
|
|
2897
|
+
for (let x = 0; x < w; x++) {
|
|
2898
|
+
if (!(x < edgeW || x >= w - edgeW || y < edgeH || y >= h - edgeH)) continue;
|
|
2899
|
+
const i = (y * w + x) * 4;
|
|
2900
|
+
if (data[i + 3] < 128) continue;
|
|
2901
|
+
const br = Math.floor(data[i] / bucketSize) * bucketSize;
|
|
2902
|
+
const bg = Math.floor(data[i + 1] / bucketSize) * bucketSize;
|
|
2903
|
+
const bb = Math.floor(data[i + 2] / bucketSize) * bucketSize;
|
|
2904
|
+
const key = `${br},${bg},${bb}`;
|
|
2905
|
+
const existing = buckets.get(key);
|
|
2906
|
+
if (existing) {
|
|
2907
|
+
existing.r += data[i];
|
|
2908
|
+
existing.g += data[i + 1];
|
|
2909
|
+
existing.b += data[i + 2];
|
|
2910
|
+
existing.count++;
|
|
2911
|
+
} else {
|
|
2912
|
+
buckets.set(key, { r: data[i], g: data[i + 1], b: data[i + 2], count: 1 });
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
let best = { r: 0, g: 0, b: 0, count: 0 };
|
|
2917
|
+
for (const b2 of buckets.values()) {
|
|
2918
|
+
if (b2.count > best.count) best = b2;
|
|
2919
|
+
}
|
|
2920
|
+
if (best.count === 0) {
|
|
2921
|
+
resolve("#000000");
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
const r = Math.round(best.r / best.count), g = Math.round(best.g / best.count), b = Math.round(best.b / best.count);
|
|
2925
|
+
resolve(`#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`);
|
|
2926
|
+
} catch {
|
|
2927
|
+
resolve("#000000");
|
|
2928
|
+
}
|
|
2929
|
+
};
|
|
2930
|
+
img.onerror = () => resolve("#000000");
|
|
2931
|
+
img.src = imageUrl;
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2934
|
+
|
|
2748
2935
|
// src/utils/ods-color-utils.ts
|
|
2749
2936
|
function getPlatformAccentColor(platform) {
|
|
2750
2937
|
const currentPlatform = platform || getCurrentPlatform();
|
|
@@ -2769,18 +2956,13 @@ function getCurrentPlatform() {
|
|
|
2769
2956
|
return process.env.NEXT_PUBLIC_APP_TYPE || "openmsp";
|
|
2770
2957
|
}
|
|
2771
2958
|
function getReadableTextColor(hex) {
|
|
2772
|
-
|
|
2773
|
-
const r = n >> 16 & 255;
|
|
2774
|
-
const g = n >> 8 & 255;
|
|
2775
|
-
const b = n & 255;
|
|
2776
|
-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
2777
|
-
return luminance > 0.5 ? "#212121" : "#fafafa";
|
|
2959
|
+
return pickReadableTextColor(hex) === "dark" ? "#212121" : "#fafafa";
|
|
2778
2960
|
}
|
|
2779
2961
|
var HEX_PATTERN = /^#[0-9a-fA-F]{6}$/;
|
|
2780
2962
|
function clamp(n, min, max) {
|
|
2781
2963
|
return Math.max(min, Math.min(max, n));
|
|
2782
2964
|
}
|
|
2783
|
-
function
|
|
2965
|
+
function hexToRgb2(hex) {
|
|
2784
2966
|
if (!HEX_PATTERN.test(hex)) return null;
|
|
2785
2967
|
const n = Number.parseInt(hex.slice(1), 16);
|
|
2786
2968
|
return { r: n >> 16 & 255, g: n >> 8 & 255, b: n & 255 };
|
|
@@ -2792,7 +2974,7 @@ function rgbToHex(r, g, b) {
|
|
|
2792
2974
|
var HOVER_DARKEN_STEP = 10;
|
|
2793
2975
|
var ACTIVE_DARKEN_STEP = 20;
|
|
2794
2976
|
function darkenByStep(hex, step) {
|
|
2795
|
-
const rgb =
|
|
2977
|
+
const rgb = hexToRgb2(hex);
|
|
2796
2978
|
if (!rgb) return hex;
|
|
2797
2979
|
return rgbToHex(rgb.r - step, rgb.g - step, rgb.b - step);
|
|
2798
2980
|
}
|
|
@@ -6341,13 +6523,14 @@ function composeChipLabel(label, lozenge) {
|
|
|
6341
6523
|
function ChipSkelBar({ className, style }) {
|
|
6342
6524
|
return /* @__PURE__ */ jsx34("span", { "aria-hidden": true, className: cn("animate-pulse rounded-md bg-ods-border", className), style });
|
|
6343
6525
|
}
|
|
6344
|
-
function QuickActionChipSkeleton({ labelCh = 16, icon = true, lozenge = false, className }) {
|
|
6526
|
+
function QuickActionChipSkeleton({ labelCh = 16, icon = true, lozenge = false, size: size2 = "default", className }) {
|
|
6345
6527
|
return /* @__PURE__ */ jsx34(
|
|
6346
6528
|
Tag,
|
|
6347
6529
|
{
|
|
6348
6530
|
variant: "outline",
|
|
6531
|
+
size: size2,
|
|
6349
6532
|
className,
|
|
6350
|
-
icon: icon ? /* @__PURE__ */ jsx34(ChipSkelBar, { className: "block size-4" }) : void 0,
|
|
6533
|
+
icon: icon ? /* @__PURE__ */ jsx34(ChipSkelBar, { className: size2 === "large" ? "block size-5" : "block size-4" }) : void 0,
|
|
6351
6534
|
label: /* @__PURE__ */ jsxs28(Fragment8, { children: [
|
|
6352
6535
|
lozenge && /* @__PURE__ */ jsx34(ChipSkelBar, { className: "mr-2 inline-block h-[16px] w-[26px] translate-y-[2px]" }),
|
|
6353
6536
|
/* @__PURE__ */ jsx34(ChipSkelBar, { className: "inline-block h-[0.9em] translate-y-[0.08em]", style: { width: `${labelCh}ch` } })
|
|
@@ -6360,6 +6543,8 @@ function QuickActionChipButton({
|
|
|
6360
6543
|
icon,
|
|
6361
6544
|
lozenge,
|
|
6362
6545
|
variant = "outline",
|
|
6546
|
+
selected = false,
|
|
6547
|
+
size: size2 = "default",
|
|
6363
6548
|
onSelect,
|
|
6364
6549
|
onHoverStart,
|
|
6365
6550
|
onHoverEnd,
|
|
@@ -6368,8 +6553,9 @@ function QuickActionChipButton({
|
|
|
6368
6553
|
}) {
|
|
6369
6554
|
const resolvedIcon = renderQuickActionIcon(icon);
|
|
6370
6555
|
const resolvedLabel = composeChipLabel(label, lozenge);
|
|
6556
|
+
const tagVariant = selected ? "selected" : variant;
|
|
6371
6557
|
if (!interactive) {
|
|
6372
|
-
return /* @__PURE__ */ jsx34(Tag, { variant, icon: resolvedIcon, label: resolvedLabel, className });
|
|
6558
|
+
return /* @__PURE__ */ jsx34(Tag, { variant: tagVariant, size: size2, icon: resolvedIcon, label: resolvedLabel, className });
|
|
6373
6559
|
}
|
|
6374
6560
|
return /* @__PURE__ */ jsx34(
|
|
6375
6561
|
"button",
|
|
@@ -6380,11 +6566,12 @@ function QuickActionChipButton({
|
|
|
6380
6566
|
onMouseLeave: onHoverEnd,
|
|
6381
6567
|
onFocus: onHoverStart,
|
|
6382
6568
|
onBlur: onHoverEnd,
|
|
6569
|
+
"aria-pressed": selected || void 0,
|
|
6383
6570
|
className: cn(
|
|
6384
6571
|
"shrink-0 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent",
|
|
6385
6572
|
className
|
|
6386
6573
|
),
|
|
6387
|
-
children: /* @__PURE__ */ jsx34(Tag, { variant, icon: resolvedIcon, label: resolvedLabel })
|
|
6574
|
+
children: /* @__PURE__ */ jsx34(Tag, { variant: tagVariant, size: size2, icon: resolvedIcon, label: resolvedLabel })
|
|
6388
6575
|
}
|
|
6389
6576
|
);
|
|
6390
6577
|
}
|
|
@@ -18521,6 +18708,7 @@ var socialCache = SocialEmbedCache.getInstance();
|
|
|
18521
18708
|
// src/components/media-carousel.tsx
|
|
18522
18709
|
init_cn();
|
|
18523
18710
|
import { useState as useState39, useRef as useRef23, useEffect as useEffect32, memo as memo4, useCallback as useCallback19 } from "react";
|
|
18711
|
+
init_button2();
|
|
18524
18712
|
import { Fragment as Fragment30, jsx as jsx135, jsxs as jsxs104 } from "react/jsx-runtime";
|
|
18525
18713
|
var ChevronLeftIcon = () => /* @__PURE__ */ jsx135("svg", { width: "24", height: "24", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx135("polyline", { points: "15,18 9,12 15,6" }) });
|
|
18526
18714
|
var ChevronRightIcon = () => /* @__PURE__ */ jsx135("svg", { width: "24", height: "24", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx135("polyline", { points: "9,18 15,12 9,6" }) });
|
|
@@ -18528,27 +18716,28 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18528
18716
|
media,
|
|
18529
18717
|
className,
|
|
18530
18718
|
aspectRatio = "16/9",
|
|
18531
|
-
|
|
18532
|
-
|
|
18533
|
-
|
|
18719
|
+
showArrows = true,
|
|
18720
|
+
objectFit = "contain",
|
|
18721
|
+
posterPriority = false,
|
|
18722
|
+
transformImageSrc,
|
|
18723
|
+
reserveDotRow = false
|
|
18534
18724
|
}) {
|
|
18535
18725
|
const [currentIndex, setCurrentIndex] = useState39(0);
|
|
18536
18726
|
const [touchStart, setTouchStart] = useState39(null);
|
|
18537
18727
|
const [touchEnd, setTouchEnd] = useState39(null);
|
|
18728
|
+
const [brokenResourceUrls, setBrokenResourceUrls] = useState39({});
|
|
18729
|
+
const [loadedImageSrc, setLoadedImageSrc] = useState39(null);
|
|
18538
18730
|
const carouselRef = useRef23(null);
|
|
18539
|
-
const
|
|
18731
|
+
const onImageError = useCallback19((originalUrl) => {
|
|
18732
|
+
const key = originalUrl.trim() || "__missing_src__";
|
|
18733
|
+
setBrokenResourceUrls((b) => b[key] ? b : { ...b, [key]: true });
|
|
18734
|
+
}, []);
|
|
18540
18735
|
useEffect32(() => {
|
|
18541
18736
|
if (currentIndex >= media.length && media.length > 0) {
|
|
18542
18737
|
setCurrentIndex(media.length - 1);
|
|
18543
18738
|
}
|
|
18544
18739
|
}, [media.length, currentIndex]);
|
|
18545
|
-
if (!media || media.length === 0) {
|
|
18546
|
-
return null;
|
|
18547
|
-
}
|
|
18548
18740
|
const currentItem = media[currentIndex] || media[0];
|
|
18549
|
-
if (!currentItem) {
|
|
18550
|
-
return null;
|
|
18551
|
-
}
|
|
18552
18741
|
const nextSlide = useCallback19(() => {
|
|
18553
18742
|
setCurrentIndex((prev) => (prev + 1) % media.length);
|
|
18554
18743
|
}, [media.length]);
|
|
@@ -18556,9 +18745,8 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18556
18745
|
setCurrentIndex((prev) => (prev - 1 + media.length) % media.length);
|
|
18557
18746
|
}, [media.length]);
|
|
18558
18747
|
const selectSlide = useCallback19((index) => {
|
|
18559
|
-
|
|
18560
|
-
|
|
18561
|
-
}, [currentIndex]);
|
|
18748
|
+
setCurrentIndex((prev) => index === prev ? prev : index);
|
|
18749
|
+
}, []);
|
|
18562
18750
|
const handleKeyDown = useCallback19((e) => {
|
|
18563
18751
|
if (media.length <= 1) return;
|
|
18564
18752
|
if (e.key === "ArrowLeft") {
|
|
@@ -18569,6 +18757,12 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18569
18757
|
nextSlide();
|
|
18570
18758
|
}
|
|
18571
18759
|
}, [nextSlide, prevSlide, media.length]);
|
|
18760
|
+
if (!media || media.length === 0) {
|
|
18761
|
+
return null;
|
|
18762
|
+
}
|
|
18763
|
+
if (!currentItem) {
|
|
18764
|
+
return null;
|
|
18765
|
+
}
|
|
18572
18766
|
const minSwipeDistance = 50;
|
|
18573
18767
|
const onTouchStart = (e) => {
|
|
18574
18768
|
setTouchEnd(null);
|
|
@@ -18578,7 +18772,7 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18578
18772
|
setTouchEnd(e.targetTouches[0].clientX);
|
|
18579
18773
|
};
|
|
18580
18774
|
const onTouchEnd = () => {
|
|
18581
|
-
if (
|
|
18775
|
+
if (touchStart === null || touchEnd === null) return;
|
|
18582
18776
|
const distance = touchStart - touchEnd;
|
|
18583
18777
|
const isLeftSwipe = distance > minSwipeDistance;
|
|
18584
18778
|
const isRightSwipe = distance < -minSwipeDistance;
|
|
@@ -18589,43 +18783,77 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18589
18783
|
prevSlide();
|
|
18590
18784
|
}
|
|
18591
18785
|
};
|
|
18592
|
-
const
|
|
18593
|
-
Video2,
|
|
18594
|
-
{
|
|
18595
|
-
kind: "youtube",
|
|
18596
|
-
url: item.src,
|
|
18597
|
-
title: item.alt || `Video ${index + 1}`,
|
|
18598
|
-
layout: "fill",
|
|
18599
|
-
priority: index === currentIndex
|
|
18600
|
-
}
|
|
18601
|
-
);
|
|
18786
|
+
const displayImageSrc = (url) => transformImageSrc ? transformImageSrc(url) : url;
|
|
18602
18787
|
const renderVideo = (item, index) => /* @__PURE__ */ jsx135(
|
|
18603
18788
|
Video2,
|
|
18604
18789
|
{
|
|
18605
18790
|
url: item.src,
|
|
18606
|
-
poster: item.poster,
|
|
18791
|
+
poster: item.poster ? displayImageSrc(item.poster) : void 0,
|
|
18792
|
+
title: item.alt || `Video ${index + 1}`,
|
|
18607
18793
|
muted: true,
|
|
18608
18794
|
layout: "fill",
|
|
18609
|
-
priority: index ===
|
|
18610
|
-
|
|
18795
|
+
priority: Boolean(posterPriority && index === 0),
|
|
18796
|
+
className: "w-full h-full bg-black"
|
|
18797
|
+
},
|
|
18798
|
+
`video-${index}-${item.src}`
|
|
18611
18799
|
);
|
|
18612
|
-
const
|
|
18613
|
-
"
|
|
18614
|
-
{
|
|
18615
|
-
|
|
18616
|
-
|
|
18617
|
-
|
|
18618
|
-
|
|
18619
|
-
|
|
18620
|
-
|
|
18621
|
-
|
|
18800
|
+
const renderImageUnavailable = (failedUrl) => /* @__PURE__ */ jsxs104("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-ods-card px-6 text-center", children: [
|
|
18801
|
+
/* @__PURE__ */ jsx135(ImageOffIcon, { className: "size-8 text-ods-text-secondary" }),
|
|
18802
|
+
/* @__PURE__ */ jsx135("p", { className: "text-ods-text-secondary text-sm max-w-md", children: "This image is no longer available at the original URL." }),
|
|
18803
|
+
/* @__PURE__ */ jsx135(
|
|
18804
|
+
next_link_default,
|
|
18805
|
+
{
|
|
18806
|
+
href: failedUrl,
|
|
18807
|
+
target: "_blank",
|
|
18808
|
+
rel: "noopener noreferrer",
|
|
18809
|
+
className: "text-ods-accent text-sm underline break-all",
|
|
18810
|
+
children: "Open link"
|
|
18622
18811
|
}
|
|
18623
|
-
|
|
18624
|
-
|
|
18812
|
+
)
|
|
18813
|
+
] });
|
|
18814
|
+
const renderImage = (item, index) => {
|
|
18815
|
+
const rawSrc = typeof item.src === "string" ? item.src.trim() : "";
|
|
18816
|
+
if (!rawSrc) {
|
|
18817
|
+
return /* @__PURE__ */ jsx135("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-ods-card px-6 text-center", children: /* @__PURE__ */ jsx135("p", { className: "text-ods-text-secondary text-sm max-w-md", children: "No image URL for this slide." }) });
|
|
18818
|
+
}
|
|
18819
|
+
if (brokenResourceUrls[rawSrc]) {
|
|
18820
|
+
return renderImageUnavailable(rawSrc);
|
|
18821
|
+
}
|
|
18822
|
+
const mainImagePriority = Boolean(posterPriority && index === 0);
|
|
18823
|
+
const imageLoaded = loadedImageSrc === rawSrc;
|
|
18824
|
+
return /* @__PURE__ */ jsxs104("div", { className: "absolute inset-0 bg-ods-bg", children: [
|
|
18825
|
+
!imageLoaded && /* @__PURE__ */ jsx135(
|
|
18826
|
+
"div",
|
|
18827
|
+
{
|
|
18828
|
+
className: "absolute inset-0 z-[1] bg-ods-card/90 animate-pulse",
|
|
18829
|
+
"aria-hidden": true
|
|
18830
|
+
}
|
|
18831
|
+
),
|
|
18832
|
+
/* @__PURE__ */ jsx135(
|
|
18833
|
+
next_image_default,
|
|
18834
|
+
{
|
|
18835
|
+
src: displayImageSrc(rawSrc),
|
|
18836
|
+
alt: item.alt || `Media ${index + 1}`,
|
|
18837
|
+
className: cn(
|
|
18838
|
+
`w-full h-full object-${objectFit} relative z-[2]`,
|
|
18839
|
+
!imageLoaded && "opacity-0",
|
|
18840
|
+
imageLoaded && "opacity-100 transition-opacity duration-200"
|
|
18841
|
+
),
|
|
18842
|
+
priority: mainImagePriority,
|
|
18843
|
+
loading: mainImagePriority ? "eager" : "lazy",
|
|
18844
|
+
sizes: "(max-width: 1279px) 100vw, 1428px",
|
|
18845
|
+
width: 1428,
|
|
18846
|
+
height: 802,
|
|
18847
|
+
unoptimized: true,
|
|
18848
|
+
onLoad: () => setLoadedImageSrc(rawSrc),
|
|
18849
|
+
onError: () => onImageError(rawSrc)
|
|
18850
|
+
}
|
|
18851
|
+
)
|
|
18852
|
+
] });
|
|
18853
|
+
};
|
|
18625
18854
|
const renderMainMedia = (item, index) => {
|
|
18626
18855
|
switch (item.type) {
|
|
18627
18856
|
case "youtube":
|
|
18628
|
-
return renderYouTubeEmbed(item, index);
|
|
18629
18857
|
case "video":
|
|
18630
18858
|
return renderVideo(item, index);
|
|
18631
18859
|
case "image":
|
|
@@ -18633,41 +18861,6 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18633
18861
|
return renderImage(item, index);
|
|
18634
18862
|
}
|
|
18635
18863
|
};
|
|
18636
|
-
const renderThumbnail = (item, index) => {
|
|
18637
|
-
const isActive = index === currentIndex;
|
|
18638
|
-
let thumbnailSrc = item.src;
|
|
18639
|
-
if (item.type === "youtube") {
|
|
18640
|
-
const videoId = extractYouTubeId(item.src);
|
|
18641
|
-
thumbnailSrc = videoId ? `https://img.youtube.com/vi/${videoId}/mqdefault.jpg` : item.src;
|
|
18642
|
-
} else if (item.type === "video" && item.poster) {
|
|
18643
|
-
thumbnailSrc = item.poster;
|
|
18644
|
-
}
|
|
18645
|
-
return /* @__PURE__ */ jsxs104(
|
|
18646
|
-
"button",
|
|
18647
|
-
{
|
|
18648
|
-
onClick: () => selectSlide(index),
|
|
18649
|
-
className: cn(
|
|
18650
|
-
"relative flex-shrink-0 overflow-hidden transition-all duration-200",
|
|
18651
|
-
"w-20 h-20 md:w-24 md:h-24 rounded-lg border-2",
|
|
18652
|
-
isActive ? "border-[#FFC008] ring-2 ring-[#FFC008]/20" : "border-ods-border hover:border-[#888888]"
|
|
18653
|
-
),
|
|
18654
|
-
children: [
|
|
18655
|
-
/* @__PURE__ */ jsx135(
|
|
18656
|
-
"img",
|
|
18657
|
-
{
|
|
18658
|
-
src: thumbnailSrc,
|
|
18659
|
-
alt: item.alt || `Thumbnail ${index + 1}`,
|
|
18660
|
-
className: "w-full h-full object-cover",
|
|
18661
|
-
loading: "lazy"
|
|
18662
|
-
}
|
|
18663
|
-
),
|
|
18664
|
-
(item.type === "video" || item.type === "youtube") && /* @__PURE__ */ jsx135("div", { className: "absolute inset-0 flex items-center justify-center bg-black/30", children: /* @__PURE__ */ jsx135(VideoPlayBadge, { size: "sm" }) }),
|
|
18665
|
-
isActive && /* @__PURE__ */ jsx135("div", { className: "absolute bottom-1 right-1 w-2 h-2 bg-[#FFC008] rounded-full" })
|
|
18666
|
-
]
|
|
18667
|
-
},
|
|
18668
|
-
index
|
|
18669
|
-
);
|
|
18670
|
-
};
|
|
18671
18864
|
const getAspectRatioClass = () => {
|
|
18672
18865
|
switch (aspectRatio) {
|
|
18673
18866
|
case "4/3":
|
|
@@ -18681,13 +18874,14 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18681
18874
|
return "aspect-video";
|
|
18682
18875
|
}
|
|
18683
18876
|
};
|
|
18684
|
-
return /* @__PURE__ */ jsxs104("div", { className: cn("
|
|
18877
|
+
return /* @__PURE__ */ jsxs104("div", { className: cn("flex flex-col items-center gap-[var(--spacing-system-l)]", className), children: [
|
|
18685
18878
|
/* @__PURE__ */ jsxs104(
|
|
18686
18879
|
"div",
|
|
18687
18880
|
{
|
|
18688
18881
|
ref: carouselRef,
|
|
18689
18882
|
className: cn(
|
|
18690
|
-
"relative bg-ods-bg border border-ods-border rounded-
|
|
18883
|
+
"relative bg-ods-bg border border-ods-border rounded-md overflow-hidden group w-full",
|
|
18884
|
+
"[box-shadow:var(--shadow-media-frame)]",
|
|
18691
18885
|
getAspectRatioClass()
|
|
18692
18886
|
),
|
|
18693
18887
|
onTouchStart,
|
|
@@ -18699,43 +18893,54 @@ var MediaCarousel = memo4(function MediaCarousel2({
|
|
|
18699
18893
|
"aria-label": media.length > 1 ? "Media carousel, use arrow keys to navigate" : void 0,
|
|
18700
18894
|
children: [
|
|
18701
18895
|
renderMainMedia(currentItem, currentIndex),
|
|
18702
|
-
media.length > 1 && /* @__PURE__ */ jsxs104(Fragment30, { children: [
|
|
18896
|
+
showArrows && media.length > 1 && /* @__PURE__ */ jsxs104(Fragment30, { children: [
|
|
18703
18897
|
/* @__PURE__ */ jsx135(
|
|
18704
|
-
|
|
18898
|
+
Button,
|
|
18705
18899
|
{
|
|
18900
|
+
variant: "transparent",
|
|
18706
18901
|
onClick: prevSlide,
|
|
18707
|
-
className: "absolute left-3 top-1/2
|
|
18902
|
+
className: "absolute left-3 top-1/2 h-auto md:h-auto -translate-y-1/2 rounded-full bg-black/50 p-2 text-white opacity-0 transition-opacity duration-200 group-hover:opacity-100 hover:bg-black/70 z-10",
|
|
18708
18903
|
"aria-label": "Previous media",
|
|
18709
18904
|
children: /* @__PURE__ */ jsx135(ChevronLeftIcon, {})
|
|
18710
18905
|
}
|
|
18711
18906
|
),
|
|
18712
18907
|
/* @__PURE__ */ jsx135(
|
|
18713
|
-
|
|
18908
|
+
Button,
|
|
18714
18909
|
{
|
|
18910
|
+
variant: "transparent",
|
|
18715
18911
|
onClick: nextSlide,
|
|
18716
|
-
className: "absolute right-3 top-1/2
|
|
18912
|
+
className: "absolute right-3 top-1/2 h-auto md:h-auto -translate-y-1/2 rounded-full bg-black/50 p-2 text-white opacity-0 transition-opacity duration-200 group-hover:opacity-100 hover:bg-black/70 z-10",
|
|
18717
18913
|
"aria-label": "Next media",
|
|
18718
18914
|
children: /* @__PURE__ */ jsx135(ChevronRightIcon, {})
|
|
18719
18915
|
}
|
|
18720
|
-
)
|
|
18721
|
-
/* @__PURE__ */ jsxs104("div", { className: "absolute top-3 right-3 bg-black/70 text-white px-3 py-1 rounded-lg text-sm font-medium", children: [
|
|
18722
|
-
currentIndex + 1,
|
|
18723
|
-
" / ",
|
|
18724
|
-
media.length
|
|
18725
|
-
] })
|
|
18916
|
+
)
|
|
18726
18917
|
] })
|
|
18727
18918
|
]
|
|
18728
18919
|
}
|
|
18729
18920
|
),
|
|
18730
|
-
media.length > 1
|
|
18731
|
-
|
|
18732
|
-
|
|
18733
|
-
|
|
18734
|
-
|
|
18735
|
-
|
|
18736
|
-
|
|
18737
|
-
|
|
18738
|
-
|
|
18921
|
+
(media.length > 1 || reserveDotRow) && /* @__PURE__ */ jsx135("div", { className: "flex h-6 items-center gap-[var(--spacing-system-xs)]", role: "group", "aria-label": "Choose slide", children: media.length > 1 && media.map((item, index) => {
|
|
18922
|
+
const isActive = index === currentIndex;
|
|
18923
|
+
return /* @__PURE__ */ jsx135(
|
|
18924
|
+
Button,
|
|
18925
|
+
{
|
|
18926
|
+
variant: "transparent",
|
|
18927
|
+
"aria-current": isActive ? "true" : void 0,
|
|
18928
|
+
"aria-label": `Go to slide ${index + 1}`,
|
|
18929
|
+
onClick: () => selectSlide(index),
|
|
18930
|
+
className: "flex h-6 w-6 md:h-6 shrink-0 items-center justify-center rounded-full p-0",
|
|
18931
|
+
children: /* @__PURE__ */ jsx135(
|
|
18932
|
+
"span",
|
|
18933
|
+
{
|
|
18934
|
+
className: cn(
|
|
18935
|
+
"size-2 rounded-full transition-colors duration-150",
|
|
18936
|
+
isActive ? "bg-[var(--ods-flamingo-pink-base)]" : "bg-[var(--ods-system-greys-soft-grey)] hover:bg-[var(--ods-system-greys-soft-grey-hover)]"
|
|
18937
|
+
)
|
|
18938
|
+
}
|
|
18939
|
+
)
|
|
18940
|
+
},
|
|
18941
|
+
index
|
|
18942
|
+
);
|
|
18943
|
+
}) })
|
|
18739
18944
|
] });
|
|
18740
18945
|
});
|
|
18741
18946
|
|
|
@@ -19071,7 +19276,6 @@ function RedditEmbedClient({ url, maxWidth = 700 }) {
|
|
|
19071
19276
|
width: m.width,
|
|
19072
19277
|
height: m.height
|
|
19073
19278
|
}));
|
|
19074
|
-
console.log("\u{1F3AC} MediaCarousel will render with:", mediaContent.length, "items", mediaContent);
|
|
19075
19279
|
const formatTimeAgo = (timestamp) => {
|
|
19076
19280
|
const now = Math.floor(Date.now() / 1e3);
|
|
19077
19281
|
const diffSeconds = now - timestamp;
|
|
@@ -19118,8 +19322,7 @@ function RedditEmbedClient({ url, maxWidth = 700 }) {
|
|
|
19118
19322
|
MediaCarousel,
|
|
19119
19323
|
{
|
|
19120
19324
|
media: carouselItems,
|
|
19121
|
-
aspectRatio: "16/9"
|
|
19122
|
-
showThumbnails: carouselItems.length > 1
|
|
19325
|
+
aspectRatio: "16/9"
|
|
19123
19326
|
}
|
|
19124
19327
|
),
|
|
19125
19328
|
/* @__PURE__ */ jsxs105("div", { className: "flex items-center space-x-6 text-ods-text-secondary text-sm", children: [
|
|
@@ -22901,173 +23104,6 @@ function isGenericWebsiteDomain(website) {
|
|
|
22901
23104
|
return isGenericDomain(normalizeDomain(website));
|
|
22902
23105
|
}
|
|
22903
23106
|
|
|
22904
|
-
// src/utils/color-analysis.ts
|
|
22905
|
-
var DESIGN_PALETTE = [
|
|
22906
|
-
{ name: "yellow", hex: "#FFC008", rgb: [255, 192, 8] },
|
|
22907
|
-
{ name: "black", hex: "#161616", rgb: [22, 22, 22] },
|
|
22908
|
-
{ name: "gray", hex: "#888888", rgb: [136, 136, 136] }
|
|
22909
|
-
];
|
|
22910
|
-
function hexToRgb2(hex) {
|
|
22911
|
-
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
22912
|
-
return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : [0, 0, 0];
|
|
22913
|
-
}
|
|
22914
|
-
function getLuminance(rgb) {
|
|
22915
|
-
const [r, g, b] = rgb.map((c) => {
|
|
22916
|
-
c = c / 255;
|
|
22917
|
-
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
22918
|
-
});
|
|
22919
|
-
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
22920
|
-
}
|
|
22921
|
-
function getContrastRatio(color1, color2) {
|
|
22922
|
-
const lum1 = getLuminance(color1);
|
|
22923
|
-
const lum2 = getLuminance(color2);
|
|
22924
|
-
const brightest = Math.max(lum1, lum2);
|
|
22925
|
-
const darkest = Math.min(lum1, lum2);
|
|
22926
|
-
return (brightest + 0.05) / (darkest + 0.05);
|
|
22927
|
-
}
|
|
22928
|
-
function pickReadableTextColor(bgHex) {
|
|
22929
|
-
const bg = hexToRgb2(bgHex);
|
|
22930
|
-
const darkContrast = getContrastRatio(bg, [26, 26, 26]);
|
|
22931
|
-
const lightContrast = getContrastRatio(bg, [250, 250, 250]);
|
|
22932
|
-
return darkContrast >= lightContrast ? "dark" : "light";
|
|
22933
|
-
}
|
|
22934
|
-
function extractDominantColor(canvas) {
|
|
22935
|
-
const ctx = canvas.getContext("2d");
|
|
22936
|
-
if (!ctx) return [128, 128, 128];
|
|
22937
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
22938
|
-
const data = imageData.data;
|
|
22939
|
-
const sampleSize = Math.max(1, Math.floor(data.length / (4 * 1e3)));
|
|
22940
|
-
const colorCounts = {};
|
|
22941
|
-
for (let i = 0; i < data.length; i += 4 * sampleSize) {
|
|
22942
|
-
const r = data[i];
|
|
22943
|
-
const g = data[i + 1];
|
|
22944
|
-
const b = data[i + 2];
|
|
22945
|
-
const alpha = data[i + 3];
|
|
22946
|
-
if (alpha < 128) continue;
|
|
22947
|
-
const bucketR = Math.round(r / 32) * 32;
|
|
22948
|
-
const bucketG = Math.round(g / 32) * 32;
|
|
22949
|
-
const bucketB = Math.round(b / 32) * 32;
|
|
22950
|
-
const key = `${bucketR},${bucketG},${bucketB}`;
|
|
22951
|
-
colorCounts[key] = (colorCounts[key] || 0) + 1;
|
|
22952
|
-
}
|
|
22953
|
-
let maxCount = 0;
|
|
22954
|
-
let dominantColor = [128, 128, 128];
|
|
22955
|
-
for (const [colorKey, count] of Object.entries(colorCounts)) {
|
|
22956
|
-
if (count > maxCount) {
|
|
22957
|
-
maxCount = count;
|
|
22958
|
-
const [r, g, b] = colorKey.split(",").map(Number);
|
|
22959
|
-
dominantColor = [r, g, b];
|
|
22960
|
-
}
|
|
22961
|
-
}
|
|
22962
|
-
return dominantColor;
|
|
22963
|
-
}
|
|
22964
|
-
function getBestContrastColor(imageColor) {
|
|
22965
|
-
let bestColor = DESIGN_PALETTE[0];
|
|
22966
|
-
let bestContrast = 0;
|
|
22967
|
-
for (const color of DESIGN_PALETTE) {
|
|
22968
|
-
const contrast = getContrastRatio(imageColor, color.rgb);
|
|
22969
|
-
if (contrast > bestContrast) {
|
|
22970
|
-
bestContrast = contrast;
|
|
22971
|
-
bestColor = color;
|
|
22972
|
-
}
|
|
22973
|
-
}
|
|
22974
|
-
return bestContrast >= 3 ? bestColor : DESIGN_PALETTE.find((c) => c.name === "black") || bestColor;
|
|
22975
|
-
}
|
|
22976
|
-
function analyzeImageColor(imageSrc) {
|
|
22977
|
-
return new Promise((resolve, reject) => {
|
|
22978
|
-
const img = new Image();
|
|
22979
|
-
img.crossOrigin = "anonymous";
|
|
22980
|
-
img.onload = () => {
|
|
22981
|
-
try {
|
|
22982
|
-
const canvas = document.createElement("canvas");
|
|
22983
|
-
const ctx = canvas.getContext("2d");
|
|
22984
|
-
if (!ctx) {
|
|
22985
|
-
reject(new Error("Could not get canvas context"));
|
|
22986
|
-
return;
|
|
22987
|
-
}
|
|
22988
|
-
const maxSize = 100;
|
|
22989
|
-
const scale = Math.min(maxSize / img.width, maxSize / img.height);
|
|
22990
|
-
canvas.width = img.width * scale;
|
|
22991
|
-
canvas.height = img.height * scale;
|
|
22992
|
-
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
22993
|
-
const dominantColor = extractDominantColor(canvas);
|
|
22994
|
-
const bestContrastColor = getBestContrastColor(dominantColor);
|
|
22995
|
-
resolve(bestContrastColor);
|
|
22996
|
-
} catch (error) {
|
|
22997
|
-
reject(error);
|
|
22998
|
-
}
|
|
22999
|
-
};
|
|
23000
|
-
img.onerror = () => reject(new Error("Failed to load image"));
|
|
23001
|
-
img.src = imageSrc;
|
|
23002
|
-
});
|
|
23003
|
-
}
|
|
23004
|
-
async function extractImageEdgeColorAsync(imageUrl) {
|
|
23005
|
-
return new Promise((resolve) => {
|
|
23006
|
-
if (typeof window === "undefined") {
|
|
23007
|
-
resolve("#000000");
|
|
23008
|
-
return;
|
|
23009
|
-
}
|
|
23010
|
-
const img = new Image();
|
|
23011
|
-
img.crossOrigin = "anonymous";
|
|
23012
|
-
img.onload = () => {
|
|
23013
|
-
try {
|
|
23014
|
-
const canvas = document.createElement("canvas");
|
|
23015
|
-
const ctx = canvas.getContext("2d");
|
|
23016
|
-
if (!ctx) {
|
|
23017
|
-
resolve("#000000");
|
|
23018
|
-
return;
|
|
23019
|
-
}
|
|
23020
|
-
const maxSize = 50;
|
|
23021
|
-
const scale = Math.min(maxSize / img.naturalWidth, maxSize / img.naturalHeight);
|
|
23022
|
-
const w = Math.round(img.naturalWidth * scale);
|
|
23023
|
-
const h = Math.round(img.naturalHeight * scale);
|
|
23024
|
-
canvas.width = w;
|
|
23025
|
-
canvas.height = h;
|
|
23026
|
-
ctx.drawImage(img, 0, 0, w, h);
|
|
23027
|
-
const data = ctx.getImageData(0, 0, w, h).data;
|
|
23028
|
-
const edgeW = Math.max(2, Math.round(w * 0.15));
|
|
23029
|
-
const edgeH = Math.max(2, Math.round(h * 0.15));
|
|
23030
|
-
const bucketSize = 32;
|
|
23031
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
23032
|
-
for (let y = 0; y < h; y++) {
|
|
23033
|
-
for (let x = 0; x < w; x++) {
|
|
23034
|
-
if (!(x < edgeW || x >= w - edgeW || y < edgeH || y >= h - edgeH)) continue;
|
|
23035
|
-
const i = (y * w + x) * 4;
|
|
23036
|
-
if (data[i + 3] < 128) continue;
|
|
23037
|
-
const br = Math.floor(data[i] / bucketSize) * bucketSize;
|
|
23038
|
-
const bg = Math.floor(data[i + 1] / bucketSize) * bucketSize;
|
|
23039
|
-
const bb = Math.floor(data[i + 2] / bucketSize) * bucketSize;
|
|
23040
|
-
const key = `${br},${bg},${bb}`;
|
|
23041
|
-
const existing = buckets.get(key);
|
|
23042
|
-
if (existing) {
|
|
23043
|
-
existing.r += data[i];
|
|
23044
|
-
existing.g += data[i + 1];
|
|
23045
|
-
existing.b += data[i + 2];
|
|
23046
|
-
existing.count++;
|
|
23047
|
-
} else {
|
|
23048
|
-
buckets.set(key, { r: data[i], g: data[i + 1], b: data[i + 2], count: 1 });
|
|
23049
|
-
}
|
|
23050
|
-
}
|
|
23051
|
-
}
|
|
23052
|
-
let best = { r: 0, g: 0, b: 0, count: 0 };
|
|
23053
|
-
for (const b2 of buckets.values()) {
|
|
23054
|
-
if (b2.count > best.count) best = b2;
|
|
23055
|
-
}
|
|
23056
|
-
if (best.count === 0) {
|
|
23057
|
-
resolve("#000000");
|
|
23058
|
-
return;
|
|
23059
|
-
}
|
|
23060
|
-
const r = Math.round(best.r / best.count), g = Math.round(best.g / best.count), b = Math.round(best.b / best.count);
|
|
23061
|
-
resolve(`#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`);
|
|
23062
|
-
} catch {
|
|
23063
|
-
resolve("#000000");
|
|
23064
|
-
}
|
|
23065
|
-
};
|
|
23066
|
-
img.onerror = () => resolve("#000000");
|
|
23067
|
-
img.src = imageUrl;
|
|
23068
|
-
});
|
|
23069
|
-
}
|
|
23070
|
-
|
|
23071
23107
|
// src/utils/announcement-storage.ts
|
|
23072
23108
|
function announcementDismissCookieName(platform) {
|
|
23073
23109
|
return `${platform}-announcement-dismissed`;
|
|
@@ -23096,6 +23132,19 @@ function isAnnouncementDismissed(platform, id) {
|
|
|
23096
23132
|
return false;
|
|
23097
23133
|
}
|
|
23098
23134
|
}
|
|
23135
|
+
function clearAnnouncementDismissals(platform) {
|
|
23136
|
+
if (typeof document === "undefined") return;
|
|
23137
|
+
document.cookie = `${announcementDismissCookieName(platform)}=; path=/; max-age=0`;
|
|
23138
|
+
try {
|
|
23139
|
+
const prefix = `${platform}-announcement-`;
|
|
23140
|
+
Object.keys(localStorage).forEach((key) => {
|
|
23141
|
+
if (key.startsWith(prefix) && key.endsWith("-dismissed")) {
|
|
23142
|
+
localStorage.removeItem(key);
|
|
23143
|
+
}
|
|
23144
|
+
});
|
|
23145
|
+
} catch {
|
|
23146
|
+
}
|
|
23147
|
+
}
|
|
23099
23148
|
function clearLegacyAnnouncementCache(platform) {
|
|
23100
23149
|
if (typeof window === "undefined") return;
|
|
23101
23150
|
try {
|
|
@@ -41858,7 +41907,7 @@ function ChannelInput({
|
|
|
41858
41907
|
);
|
|
41859
41908
|
}
|
|
41860
41909
|
function RgbInputs({ value, onChange }) {
|
|
41861
|
-
const rgb =
|
|
41910
|
+
const rgb = hexToRgb2(value) ?? { r: 0, g: 0, b: 0 };
|
|
41862
41911
|
const update = (next) => {
|
|
41863
41912
|
const r = next.r ?? rgb.r;
|
|
41864
41913
|
const g = next.g ?? rgb.g;
|
|
@@ -41872,7 +41921,7 @@ function RgbInputs({ value, onChange }) {
|
|
|
41872
41921
|
] });
|
|
41873
41922
|
}
|
|
41874
41923
|
function HslInputs({ value, onChange }) {
|
|
41875
|
-
const rgb =
|
|
41924
|
+
const rgb = hexToRgb2(value) ?? { r: 0, g: 0, b: 0 };
|
|
41876
41925
|
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);
|
|
41877
41926
|
const update = (next) => {
|
|
41878
41927
|
const h = next.h ?? hsl.h;
|
|
@@ -52466,11 +52515,18 @@ export {
|
|
|
52466
52515
|
ActionsMenu,
|
|
52467
52516
|
ActionsMenuDropdown,
|
|
52468
52517
|
ColorSwatch,
|
|
52518
|
+
DESIGN_PALETTE,
|
|
52519
|
+
getContrastRatio,
|
|
52520
|
+
pickReadableTextColor,
|
|
52521
|
+
extractDominantColor,
|
|
52522
|
+
getBestContrastColor,
|
|
52523
|
+
analyzeImageColor,
|
|
52524
|
+
extractImageEdgeColorAsync,
|
|
52469
52525
|
getPlatformAccentColor,
|
|
52470
52526
|
getCurrentPlatform,
|
|
52471
52527
|
getReadableTextColor,
|
|
52472
52528
|
HEX_PATTERN,
|
|
52473
|
-
hexToRgb,
|
|
52529
|
+
hexToRgb2 as hexToRgb,
|
|
52474
52530
|
rgbToHex,
|
|
52475
52531
|
deriveHoverColor,
|
|
52476
52532
|
deriveActiveColor,
|
|
@@ -53033,18 +53089,11 @@ export {
|
|
|
53033
53089
|
isGenericDomain,
|
|
53034
53090
|
hasGenericEmailDomain,
|
|
53035
53091
|
isGenericWebsiteDomain,
|
|
53036
|
-
DESIGN_PALETTE,
|
|
53037
|
-
getLuminance,
|
|
53038
|
-
getContrastRatio,
|
|
53039
|
-
pickReadableTextColor,
|
|
53040
|
-
extractDominantColor,
|
|
53041
|
-
getBestContrastColor,
|
|
53042
|
-
analyzeImageColor,
|
|
53043
|
-
extractImageEdgeColorAsync,
|
|
53044
53092
|
announcementDismissCookieName,
|
|
53045
53093
|
isDismissedCookieValue,
|
|
53046
53094
|
dismissAnnouncement,
|
|
53047
53095
|
isAnnouncementDismissed,
|
|
53096
|
+
clearAnnouncementDismissals,
|
|
53048
53097
|
clearLegacyAnnouncementCache,
|
|
53049
53098
|
readLeadingDecisionFrame,
|
|
53050
53099
|
ANNOUNCEMENT_CTA_DEFAULTS,
|
|
@@ -53360,4 +53409,4 @@ export {
|
|
|
53360
53409
|
LogsList,
|
|
53361
53410
|
assets
|
|
53362
53411
|
};
|
|
53363
|
-
//# sourceMappingURL=chunk-
|
|
53412
|
+
//# sourceMappingURL=chunk-IVM47EGI.js.map
|