@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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export interface MediaItem {
|
|
2
|
-
|
|
2
|
+
/** Optional stable key. The carousel keys slides by index; converters may
|
|
3
|
+
* omit this (hub vendor/profile/category converters all do). */
|
|
4
|
+
id?: string;
|
|
3
5
|
type: 'image' | 'video' | 'youtube';
|
|
4
6
|
src: string;
|
|
5
7
|
alt?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-carousel-utils-stub.d.ts","sourceRoot":"","sources":["../../src/utils/media-carousel-utils-stub.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"media-carousel-utils-stub.d.ts","sourceRoot":"","sources":["../../src/utils/media-carousel-utils-stub.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB;qEACiE;IACjE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAE3E;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAEnD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAElE"}
|
|
@@ -75,7 +75,8 @@ export declare function usePlatformColors(platform?: Platform): {
|
|
|
75
75
|
};
|
|
76
76
|
/**
|
|
77
77
|
* Picks a near-black or near-white text color with adequate contrast on `hex`.
|
|
78
|
-
*
|
|
78
|
+
* Thin wrapper over `pickReadableTextColor` (color-analysis.ts) — ONE
|
|
79
|
+
* light-or-dark decision formula (WCAG relative luminance) for the whole lib.
|
|
79
80
|
*/
|
|
80
81
|
export declare function getReadableTextColor(hex: string): string;
|
|
81
82
|
export declare const HEX_PATTERN: RegExp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ods-color-utils.d.ts","sourceRoot":"","sources":["../../src/utils/ods-color-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"ods-color-utils.d.ts","sourceRoot":"","sources":["../../src/utils/ods-color-utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { X } from 'lucide-react';
|
|
5
5
|
import { Button } from './ui/button';
|
|
6
6
|
import { EntityIcon } from './icon-display';
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isAnnouncementDismissed,
|
|
10
10
|
clearLegacyAnnouncementCache,
|
|
11
11
|
} from '../utils/announcement-storage';
|
|
12
|
-
import type { AnnouncementBarProps, AnnouncementResponse } from '../types/announcement';
|
|
12
|
+
import type { Announcement, AnnouncementBarProps, AnnouncementResponse } from '../types/announcement';
|
|
13
13
|
import { getAppType } from '../utils/app-config';
|
|
14
14
|
import { useEndpointsRuntime } from '../contexts/endpoints-runtime-context';
|
|
15
15
|
import { useSelfFetch } from '../hooks/use-self-fetch';
|
|
@@ -71,6 +71,15 @@ export function AnnouncementBar({
|
|
|
71
71
|
});
|
|
72
72
|
const announcement = data?.announcement ?? null;
|
|
73
73
|
|
|
74
|
+
// Hold the last non-null announcement so DEACTIVATION (a revalidation that
|
|
75
|
+
// returns null) collapses with the same 1fr->0fr animation as dismissal
|
|
76
|
+
// instead of unmounting in place (a hard 44px jump).
|
|
77
|
+
const lastAnnouncementRef = useRef<Announcement | null>(initialAnnouncement ?? null);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (announcement) lastAnnouncementRef.current = announcement;
|
|
80
|
+
}, [announcement]);
|
|
81
|
+
const displayAnnouncement = announcement ?? lastAnnouncementRef.current;
|
|
82
|
+
|
|
74
83
|
// Expanded (height) state — initial value is a pure function of props so
|
|
75
84
|
// the SSR HTML and the hydration render agree for every cohort.
|
|
76
85
|
const [expandedState, setExpandedState] = useState<boolean>(() => initialAnnouncement != null);
|
|
@@ -109,6 +118,10 @@ export function AnnouncementBar({
|
|
|
109
118
|
const handleDismiss = () => {
|
|
110
119
|
if (previewMode || !announcement) return;
|
|
111
120
|
dismissAnnouncement(platform, announcement.id);
|
|
121
|
+
// Release focus BEFORE the wrapper goes aria-hidden — a focused descendant
|
|
122
|
+
// inside an aria-hidden subtree strands screen-reader focus (and Chrome
|
|
123
|
+
// refuses to apply the attribute).
|
|
124
|
+
(document.activeElement as HTMLElement | null)?.blur?.();
|
|
112
125
|
setExpandedState(false);
|
|
113
126
|
};
|
|
114
127
|
|
|
@@ -119,7 +132,9 @@ export function AnnouncementBar({
|
|
|
119
132
|
// via location.href / window.open (stored XSS).
|
|
120
133
|
let safeUrl: string;
|
|
121
134
|
try {
|
|
122
|
-
|
|
135
|
+
// Resolve against the CURRENT page (not the origin) so path-relative and
|
|
136
|
+
// fragment CTAs keep their pre-guard semantics.
|
|
137
|
+
const parsed = new URL(announcement.cta_url, window.location.href);
|
|
123
138
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return;
|
|
124
139
|
safeUrl = parsed.href;
|
|
125
140
|
} catch {
|
|
@@ -130,10 +145,10 @@ export function AnnouncementBar({
|
|
|
130
145
|
: (window.location.href = safeUrl);
|
|
131
146
|
};
|
|
132
147
|
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
// collapse animates.
|
|
136
|
-
if (!
|
|
148
|
+
// Never had anything to show: render nothing. Dismissal AND deactivation
|
|
149
|
+
// keep the element mounted (displayAnnouncement holds the last content) at
|
|
150
|
+
// 0fr so the collapse animates.
|
|
151
|
+
if (!displayAnnouncement) return null;
|
|
137
152
|
|
|
138
153
|
// Contrast is solved with the ODS THEME system, not color literals: the
|
|
139
154
|
// admin background's tone selects which ODS theme scopes the bar's content
|
|
@@ -141,7 +156,7 @@ export function AnnouncementBar({
|
|
|
141
156
|
// dark-toned needs the dark theme's). `.theme-light` / `.theme-dark` are
|
|
142
157
|
// the design system's own scoping classes (ods-colors.css) and flip every
|
|
143
158
|
// Tier-1 `--ods-*` primitive for descendants.
|
|
144
|
-
const themeScope = pickReadableTextColor(
|
|
159
|
+
const themeScope = pickReadableTextColor(displayAnnouncement.background_color) === 'dark' ? 'theme-light' : 'theme-dark';
|
|
145
160
|
|
|
146
161
|
// Inside the scope, colors reference Tier-1 theme primitives directly
|
|
147
162
|
// (Tier-2 `--color-*` aliases resolve once at :root and do NOT re-resolve
|
|
@@ -152,7 +167,7 @@ export function AnnouncementBar({
|
|
|
152
167
|
const barButtonClasses =
|
|
153
168
|
'text-[color:var(--ods-system-greys-white)] hover:bg-[var(--ods-system-greys-black-hover)] active:bg-[var(--ods-system-greys-black-action)]';
|
|
154
169
|
|
|
155
|
-
const hasCta = Boolean(
|
|
170
|
+
const hasCta = Boolean(displayAnnouncement.cta_enabled && displayAnnouncement.cta_url && displayAnnouncement.cta_text);
|
|
156
171
|
|
|
157
172
|
return (
|
|
158
173
|
<div
|
|
@@ -171,9 +186,11 @@ export function AnnouncementBar({
|
|
|
171
186
|
compact CTA on the right, and a ghost-icon dismiss (design-system
|
|
172
187
|
icon-sm: 32px target, >= the 24px WCAG 2.2 SC 2.5.8 AA floor).
|
|
173
188
|
*/}
|
|
174
|
-
<div className={`min-h-0 overflow-hidden ${themeScope}`} style={{ backgroundColor:
|
|
189
|
+
<div className={`min-h-0 overflow-hidden ${themeScope}`} style={{ backgroundColor: displayAnnouncement.background_color }}>
|
|
175
190
|
<div className="flex items-center w-full max-w-full min-h-11 text-[color:var(--ods-system-greys-white)]">
|
|
176
|
-
{/* Mobile:
|
|
191
|
+
{/* Mobile: whole-bar tap target (touch-first by design; the CTA
|
|
192
|
+
Button below is the keyboard/AT path and is CSS-hidden < md —
|
|
193
|
+
known tradeoff carried from the original bar). */}
|
|
177
194
|
<div
|
|
178
195
|
className={`flex flex-row gap-2 md:gap-3 items-center pl-4 md:pl-6 py-1.5 flex-1 min-w-0 ${
|
|
179
196
|
hasCta ? 'md:cursor-default cursor-pointer' : ''
|
|
@@ -190,9 +207,9 @@ export function AnnouncementBar({
|
|
|
190
207
|
wins, else a library glyph by name (+ props), via <EntityIcon>. */}
|
|
191
208
|
<EntityIcon
|
|
192
209
|
icon={{
|
|
193
|
-
name:
|
|
194
|
-
url:
|
|
195
|
-
props:
|
|
210
|
+
name: displayAnnouncement.icon_name || 'openframe-logo',
|
|
211
|
+
url: displayAnnouncement.icon_url,
|
|
212
|
+
props: displayAnnouncement.icon_props,
|
|
196
213
|
}}
|
|
197
214
|
size={24}
|
|
198
215
|
className="relative shrink-0 w-5 h-5 md:w-6 md:h-6"
|
|
@@ -202,9 +219,9 @@ export function AnnouncementBar({
|
|
|
202
219
|
truncating as one unit. Separator is a middot (house rule: no
|
|
203
220
|
en/em dashes in copy). */}
|
|
204
221
|
<p className="font-body flex-1 min-w-0 max-w-full text-[13px] md:text-sm leading-snug truncate mb-0">
|
|
205
|
-
<span className="font-semibold">{
|
|
206
|
-
{
|
|
207
|
-
<span className="hidden sm:inline opacity-80"> · {
|
|
222
|
+
<span className="font-semibold">{displayAnnouncement.title}</span>
|
|
223
|
+
{displayAnnouncement.description && (
|
|
224
|
+
<span className="hidden sm:inline opacity-80"> · {displayAnnouncement.description}</span>
|
|
208
225
|
)}
|
|
209
226
|
</p>
|
|
210
227
|
|
|
@@ -214,7 +231,7 @@ export function AnnouncementBar({
|
|
|
214
231
|
admin color. The admin cta_button_* colors are NOT applied:
|
|
215
232
|
they were designed for the legacy bespoke treatment. Hidden
|
|
216
233
|
on mobile, where the whole bar is the tap target. */}
|
|
217
|
-
{hasCta &&
|
|
234
|
+
{hasCta && displayAnnouncement.cta_text && (
|
|
218
235
|
<div className="hidden md:flex flex-shrink-0 ml-1">
|
|
219
236
|
<Button
|
|
220
237
|
onClick={handleCtaClick}
|
|
@@ -223,10 +240,10 @@ export function AnnouncementBar({
|
|
|
223
240
|
className={barButtonClasses}
|
|
224
241
|
tabIndex={expanded ? 0 : -1}
|
|
225
242
|
leftIcon={
|
|
226
|
-
|
|
243
|
+
displayAnnouncement.cta_show_icon && displayAnnouncement.cta_icon_name
|
|
227
244
|
? (
|
|
228
245
|
<EntityIcon
|
|
229
|
-
icon={{ name:
|
|
246
|
+
icon={{ name: displayAnnouncement.cta_icon_name, props: displayAnnouncement.cta_icon_props }}
|
|
230
247
|
size={14}
|
|
231
248
|
className="w-3.5 h-3.5"
|
|
232
249
|
/>
|
|
@@ -234,7 +251,7 @@ export function AnnouncementBar({
|
|
|
234
251
|
: undefined
|
|
235
252
|
}
|
|
236
253
|
>
|
|
237
|
-
{
|
|
254
|
+
{displayAnnouncement.cta_text}
|
|
238
255
|
</Button>
|
|
239
256
|
</div>
|
|
240
257
|
)}
|
|
@@ -141,6 +141,13 @@ export interface QuickActionChipButtonProps {
|
|
|
141
141
|
lozenge?: QuickActionChipLozenge
|
|
142
142
|
/** `'primary'` = accent (yellow) chip, `'outline'` = bordered chip (default). */
|
|
143
143
|
variant?: 'primary' | 'outline'
|
|
144
|
+
/** Active single-select state (Figma "Feature Item" active): renders the
|
|
145
|
+
* Tag's `selected` variant (pink border + pink-secondary fill), overriding
|
|
146
|
+
* `variant`. Used by chip groups acting as tabs (OpenFrame categories). */
|
|
147
|
+
selected?: boolean
|
|
148
|
+
/** Chip scale, forwarded to `Tag` — `'large'` is the Figma "Feature Item"
|
|
149
|
+
* 48px chip (h3 bold label, 24px icon box). Default `'default'` (32px). */
|
|
150
|
+
size?: 'default' | 'large'
|
|
144
151
|
onSelect?: () => void
|
|
145
152
|
/** Pointer/keyboard focus enters the chip — e.g. preview the full prompt. */
|
|
146
153
|
onHoverStart?: () => void
|
|
@@ -164,6 +171,8 @@ export interface QuickActionChipSkeletonProps {
|
|
|
164
171
|
icon?: boolean
|
|
165
172
|
/** Reserve the leading lozenge affix slot. */
|
|
166
173
|
lozenge?: boolean
|
|
174
|
+
/** Chip scale — MUST match the loaded chips' `size` or the swap jumps. */
|
|
175
|
+
size?: 'default' | 'large'
|
|
167
176
|
className?: string
|
|
168
177
|
}
|
|
169
178
|
|
|
@@ -182,12 +191,13 @@ function ChipSkelBar({ className, style }: { className?: string; style?: React.C
|
|
|
182
191
|
* construction. Use anywhere quick actions stream in (chat empty states,
|
|
183
192
|
* marketing walls, deck panels).
|
|
184
193
|
*/
|
|
185
|
-
export function QuickActionChipSkeleton({ labelCh = 16, icon = true, lozenge = false, className }: QuickActionChipSkeletonProps) {
|
|
194
|
+
export function QuickActionChipSkeleton({ labelCh = 16, icon = true, lozenge = false, size = 'default', className }: QuickActionChipSkeletonProps) {
|
|
186
195
|
return (
|
|
187
196
|
<Tag
|
|
188
197
|
variant="outline"
|
|
198
|
+
size={size}
|
|
189
199
|
className={className}
|
|
190
|
-
icon={icon ? <ChipSkelBar className=
|
|
200
|
+
icon={icon ? <ChipSkelBar className={size === 'large' ? 'block size-5' : 'block size-4'} /> : undefined}
|
|
191
201
|
label={
|
|
192
202
|
<>
|
|
193
203
|
{lozenge && <ChipSkelBar className="mr-2 inline-block h-[16px] w-[26px] translate-y-[2px]" />}
|
|
@@ -215,6 +225,8 @@ export function QuickActionChipButton({
|
|
|
215
225
|
icon,
|
|
216
226
|
lozenge,
|
|
217
227
|
variant = 'outline',
|
|
228
|
+
selected = false,
|
|
229
|
+
size = 'default',
|
|
218
230
|
onSelect,
|
|
219
231
|
onHoverStart,
|
|
220
232
|
onHoverEnd,
|
|
@@ -223,8 +235,9 @@ export function QuickActionChipButton({
|
|
|
223
235
|
}: QuickActionChipButtonProps) {
|
|
224
236
|
const resolvedIcon = renderQuickActionIcon(icon)
|
|
225
237
|
const resolvedLabel = composeChipLabel(label, lozenge)
|
|
238
|
+
const tagVariant = selected ? 'selected' : variant
|
|
226
239
|
if (!interactive) {
|
|
227
|
-
return <Tag variant={
|
|
240
|
+
return <Tag variant={tagVariant} size={size} icon={resolvedIcon} label={resolvedLabel} className={className} />
|
|
228
241
|
}
|
|
229
242
|
return (
|
|
230
243
|
<button
|
|
@@ -234,12 +247,13 @@ export function QuickActionChipButton({
|
|
|
234
247
|
onMouseLeave={onHoverEnd}
|
|
235
248
|
onFocus={onHoverStart}
|
|
236
249
|
onBlur={onHoverEnd}
|
|
250
|
+
aria-pressed={selected || undefined}
|
|
237
251
|
className={cn(
|
|
238
252
|
'shrink-0 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent',
|
|
239
253
|
className,
|
|
240
254
|
)}
|
|
241
255
|
>
|
|
242
|
-
<Tag variant={
|
|
256
|
+
<Tag variant={tagVariant} size={size} icon={resolvedIcon} label={resolvedLabel} />
|
|
243
257
|
</button>
|
|
244
258
|
)
|
|
245
259
|
}
|
|
@@ -448,8 +448,6 @@ export function RedditEmbedClient({ url, maxWidth = 700 }: RedditEmbedProps) {
|
|
|
448
448
|
height: m.height,
|
|
449
449
|
}));
|
|
450
450
|
|
|
451
|
-
console.log('🎬 MediaCarousel will render with:', mediaContent.length, 'items', mediaContent);
|
|
452
|
-
|
|
453
451
|
// Format time
|
|
454
452
|
const formatTimeAgo = (timestamp: number) => {
|
|
455
453
|
const now = Math.floor(Date.now() / 1000);
|
|
@@ -515,7 +513,6 @@ export function RedditEmbedClient({ url, maxWidth = 700 }: RedditEmbedProps) {
|
|
|
515
513
|
<MediaCarousel
|
|
516
514
|
media={carouselItems}
|
|
517
515
|
aspectRatio="16/9"
|
|
518
|
-
showThumbnails={carouselItems.length > 1}
|
|
519
516
|
/>
|
|
520
517
|
)}
|
|
521
518
|
|
package/src/components/index.ts
CHANGED
|
@@ -38,6 +38,11 @@ export * from './hero-image-uploader'
|
|
|
38
38
|
export * from './icons-block'
|
|
39
39
|
export * from './image-cropper'
|
|
40
40
|
export * from './media-carousel'
|
|
41
|
+
// Explicit re-export disambiguates the star-export collision with the
|
|
42
|
+
// features barrel's gallery-manager MediaItem: from THIS barrel, `MediaItem`
|
|
43
|
+
// is the carousel item shape (the lib-wide SSOT the hub re-types against);
|
|
44
|
+
// the gallery-manager shape stays reachable via `./components/features`.
|
|
45
|
+
export type { MediaItem } from '../utils/media-carousel-utils-stub'
|
|
41
46
|
export * from './metric-value'
|
|
42
47
|
export * from './msp-display'
|
|
43
48
|
export * from './open-source-features'
|