@flamingo-stack/openframe-frontend-core 0.0.705 → 0.0.706-snapshot.20261006181108
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/chat-protocol/index.cjs +23 -14
- package/dist/chat-protocol/index.js +23 -14
- package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
- package/dist/chat-protocol/source-metadata.d.ts +11 -2
- package/dist/chat-protocol/source-metadata.d.ts.map +1 -1
- package/dist/{chunk-QFXRPRFT.js → chunk-2YHNQAMU.js} +3 -3
- package/dist/{chunk-YHM5AWTT.js → chunk-3FZP73R4.js} +6 -6
- package/dist/{chunk-VR2ZLFCP.js → chunk-4IBPRIQH.js} +1 -1
- package/dist/{chunk-AQWZNP6A.js → chunk-4RVC6TCE.js} +4 -4
- package/dist/{chunk-OUY3HHW6.cjs → chunk-4UMOD5NU.cjs} +928 -539
- package/dist/{chunk-IKTQ3ZAG.cjs → chunk-54L6NMKQ.cjs} +6 -6
- package/dist/{chunk-A75QY6CD.cjs → chunk-56LOCS37.cjs} +21 -21
- package/dist/{chunk-WA6GUYQ4.cjs → chunk-5CMYEGHE.cjs} +104 -104
- package/dist/{chunk-VA5T32XA.js → chunk-632NGOIH.js} +2 -2
- package/dist/{chunk-MMOODW57.cjs → chunk-6NTPY5QA.cjs} +1887 -94
- package/dist/{chunk-VESKITRQ.cjs → chunk-7XVBJDH7.cjs} +728 -732
- package/dist/{chunk-3MX5S4CF.cjs → chunk-AGNG62O2.cjs} +905 -2564
- package/dist/{chunk-ZSAO2HBW.js → chunk-BETJB7UV.js} +15 -15
- package/dist/{chunk-MP2C3LJ4.js → chunk-CUME3VTQ.js} +1 -1
- package/dist/{chunk-PDWVPI3S.js → chunk-CZ7Y7UG4.js} +4 -72
- package/dist/{chunk-F6I6XZMN.js → chunk-E2TQZIOJ.js} +5 -5
- package/dist/{chunk-X5MQ7JRP.js → chunk-ECXW3RIH.js} +9 -9
- package/dist/{chunk-JEZKGCZI.cjs → chunk-EUSDLX5W.cjs} +26 -26
- package/dist/{chunk-6NJKKO73.cjs → chunk-F4E3L3KV.cjs} +15 -15
- package/dist/{chunk-MFFCUVWG.js → chunk-F57ECE2I.js} +1 -1
- package/dist/{chunk-F6AFPNDF.js → chunk-FBY4V3YS.js} +15 -15
- package/dist/{chunk-CH5232HV.js → chunk-FI6GMDO7.js} +2455 -662
- package/dist/{chunk-NDESEPW5.js → chunk-FXJ3WWUB.js} +68 -0
- package/dist/{chunk-FCGC42TS.js → chunk-HTBN3XYF.js} +57 -61
- package/dist/{chunk-ZSAGLQOD.cjs → chunk-I43ZVJRL.cjs} +17 -17
- package/dist/{chunk-7VVFKJY5.js → chunk-IBQZMRWO.js} +1796 -3455
- package/dist/{chunk-4KX6OED2.cjs → chunk-JFSLQ2XS.cjs} +46 -46
- package/dist/{chunk-5HBHO5TB.js → chunk-JPG7OHEG.js} +1 -1
- package/dist/{chunk-MKXO4Q5P.js → chunk-KUC673HM.js} +383 -142
- package/dist/{chunk-4I45Y3DN.cjs → chunk-L3Q7R7CQ.cjs} +6 -6
- package/dist/{chunk-X72J4ACL.cjs → chunk-LLSUVPQC.cjs} +252 -8
- package/dist/{chunk-RKV5B7EU.cjs → chunk-LM7TXMLU.cjs} +72 -4
- package/dist/{chunk-PN2RKPDF.js → chunk-LSQPH73L.js} +10 -10
- package/dist/{chunk-HPWWNJE7.cjs → chunk-MY3JYZQY.cjs} +89 -89
- package/dist/{chunk-2YXVWZVF.js → chunk-N45D4ZXQ.js} +959 -715
- package/dist/{chunk-Z2VNQQ74.js → chunk-NCPTID44.js} +978 -942
- package/dist/{chunk-KOWVJD4V.cjs → chunk-O6WNIA6W.cjs} +56 -56
- package/dist/{chunk-ABU3TGXA.cjs → chunk-OF6GEI5A.cjs} +3 -3
- package/dist/{chunk-OHFS7VOU.cjs → chunk-PLYEKV3N.cjs} +2 -2
- package/dist/{chunk-VHCACDKP.cjs → chunk-RQRX4YC3.cjs} +39 -39
- package/dist/{chunk-DW7G4KG3.js → chunk-RZNRVXHH.js} +3 -3
- package/dist/{chunk-G5A3PSPV.js → chunk-SGJP2WKD.js} +1080 -691
- package/dist/{chunk-7LS22BZU.cjs → chunk-STXYW25T.cjs} +14 -14
- package/dist/{chunk-YFBM2K6X.cjs → chunk-TB5EDWRC.cjs} +4 -4
- package/dist/{chunk-RBDP4WDE.js → chunk-U6BWEKJC.js} +6 -6
- package/dist/{chunk-XOYH2YQE.cjs → chunk-UDFRUKLC.cjs} +2 -2
- package/dist/{chunk-TYWLNCNH.cjs → chunk-UVTD52MD.cjs} +14 -82
- package/dist/{chunk-N3COXDBH.cjs → chunk-VX4E6YSB.cjs} +41 -5
- package/dist/{chunk-2QEUFXNS.cjs → chunk-WCVZCGK5.cjs} +312 -71
- package/dist/{chunk-6XIF2Z7I.js → chunk-WMX26HB5.js} +3 -3
- package/dist/{chunk-IHERL5V4.cjs → chunk-WXAT5C4C.cjs} +34 -34
- package/dist/{chunk-D7BTSVMS.cjs → chunk-Y2GSOUCS.cjs} +2 -2
- package/dist/{chunk-6SEANMOU.js → chunk-Y5VSOYPS.js} +11 -11
- package/dist/{chunk-DENB2XJC.js → chunk-YQWTGQG5.js} +2 -2
- package/dist/components/case-studies/index.cjs +30 -32
- package/dist/components/case-studies/index.js +14 -16
- package/dist/components/chat/entity-cards/case-study-card.d.ts +11 -4
- package/dist/components/chat/entity-cards/case-study-card.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +9 -11
- package/dist/components/chat/index.js +14 -16
- package/dist/components/chat/types/message.types.d.ts +20 -16
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/chat/types/unified-chat-state.types.d.ts +1 -1
- package/dist/components/chat/utils/icon-library.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +12 -14
- package/dist/components/contact/index.js +11 -13
- package/dist/components/docs/index.cjs +15 -17
- package/dist/components/docs/index.js +14 -16
- package/dist/components/embeds/index.cjs +12 -14
- package/dist/components/embeds/index.js +11 -13
- package/dist/components/faq/index.cjs +13 -15
- package/dist/components/faq/index.js +12 -14
- package/dist/components/features/index.cjs +12 -14
- package/dist/components/features/index.js +21 -23
- package/dist/components/help-center-pages/index.cjs +118 -120
- package/dist/components/help-center-pages/index.js +33 -35
- package/dist/components/icons/index.cjs +3 -5
- package/dist/components/icons/index.js +9 -11
- package/dist/components/icons-v2-generated/index.cjs +3 -5
- package/dist/components/icons-v2-generated/index.js +2 -4
- package/dist/components/index.cjs +86 -159
- package/dist/components/index.d.ts +0 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +329 -402
- package/dist/components/loading/page-layout-skeleton.d.ts +1 -1
- package/dist/components/meeting-scheduler/index.cjs +62 -64
- package/dist/components/meeting-scheduler/index.js +12 -14
- package/dist/components/navigation/index.cjs +28 -16
- package/dist/components/navigation/index.d.ts +14 -10
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +38 -26
- package/dist/components/navigation/mingo-ai-button.d.ts +38 -3
- package/dist/components/navigation/mingo-ai-button.d.ts.map +1 -1
- package/dist/components/navigation/mobile-nav-sheet-id.d.ts +3 -0
- package/dist/components/navigation/mobile-nav-sheet-id.d.ts.map +1 -0
- package/dist/components/navigation/mobile-nav-sheet.d.ts +48 -0
- package/dist/components/navigation/mobile-nav-sheet.d.ts.map +1 -0
- package/dist/components/navigation/nav-item-row.d.ts +27 -0
- package/dist/components/navigation/nav-item-row.d.ts.map +1 -0
- package/dist/components/navigation/nav-panel.d.ts +43 -0
- package/dist/components/navigation/nav-panel.d.ts.map +1 -0
- package/dist/components/navigation/navigation-sidebar-item.d.ts +1 -2
- package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
- package/dist/components/navigation/site-footer.d.ts +28 -0
- package/dist/components/navigation/site-footer.d.ts.map +1 -0
- package/dist/components/navigation/site-header.d.ts +61 -0
- package/dist/components/navigation/site-header.d.ts.map +1 -0
- package/dist/components/navigation/site-nav-link.d.ts +26 -0
- package/dist/components/navigation/site-nav-link.d.ts.map +1 -0
- package/dist/components/navigation/top-navigation.d.ts +2 -2
- package/dist/components/navigation/top-navigation.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +15 -17
- package/dist/components/onboarding-guides/index.js +14 -16
- package/dist/components/related-content/index.cjs +10 -8
- package/dist/components/related-content/index.js +9 -7
- package/dist/components/tickets/index.cjs +16 -18
- package/dist/components/tickets/index.js +15 -17
- package/dist/components/ui/index.cjs +12 -14
- package/dist/components/ui/index.js +17 -19
- package/dist/hooks/index.cjs +19 -11
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +19 -11
- package/dist/hooks/ui/index.d.ts +3 -0
- package/dist/hooks/ui/index.d.ts.map +1 -1
- package/dist/hooks/ui/use-active-section.d.ts +10 -0
- package/dist/hooks/ui/use-active-section.d.ts.map +1 -0
- package/dist/hooks/ui/use-nav-menus.d.ts +48 -0
- package/dist/hooks/ui/use-nav-menus.d.ts.map +1 -0
- package/dist/hooks/ui/use-visitor-os.d.ts +15 -0
- package/dist/hooks/ui/use-visitor-os.d.ts.map +1 -0
- package/dist/hooks/use-location-hash.d.ts +9 -0
- package/dist/hooks/use-location-hash.d.ts.map +1 -0
- package/dist/index.cjs +55 -22
- package/dist/index.js +229 -196
- package/dist/types/case-study.d.ts +9 -0
- package/dist/types/case-study.d.ts.map +1 -1
- package/dist/types/navigation.d.ts +95 -65
- package/dist/types/navigation.d.ts.map +1 -1
- package/dist/utils/index.cjs +55 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +50 -1
- package/dist/utils/source-icons.d.ts.map +1 -1
- package/dist/utils/visitor-os.d.ts +45 -0
- package/dist/utils/visitor-os.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/chat-protocol/nats-decoder.ts +10 -8
- package/src/chat-protocol/source-metadata.ts +25 -2
- package/src/components/chat/entity-cards/case-study-card.tsx +115 -5
- package/src/components/chat/types/message.types.ts +21 -15
- package/src/components/chat/types/unified-chat-state.types.ts +1 -1
- package/src/components/chat/utils/icon-library.tsx +35 -1
- package/src/components/chat/utils/process-historical-messages.ts +12 -4
- package/src/components/index.ts +0 -2
- package/src/components/loading/page-layout-skeleton.tsx +1 -1
- package/src/components/navigation/index.ts +21 -14
- package/src/components/navigation/mingo-ai-button.tsx +139 -63
- package/src/components/navigation/mobile-nav-sheet-id.ts +2 -0
- package/src/components/navigation/mobile-nav-sheet.tsx +241 -0
- package/src/components/navigation/nav-item-row.tsx +129 -0
- package/src/components/navigation/nav-panel.tsx +196 -0
- package/src/components/navigation/navigation-sidebar-item.tsx +1 -2
- package/src/components/navigation/site-footer.tsx +256 -0
- package/src/components/navigation/site-header.tsx +339 -0
- package/src/components/navigation/site-nav-link.tsx +41 -0
- package/src/components/navigation/ticket-alerts-button.tsx +2 -2
- package/src/components/navigation/top-navigation.tsx +2 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/ui/index.ts +3 -0
- package/src/hooks/ui/use-active-section.ts +44 -0
- package/src/hooks/ui/use-nav-menus.ts +200 -0
- package/src/hooks/ui/use-visitor-os.ts +33 -0
- package/src/hooks/use-location-hash.ts +34 -0
- package/src/styles/chat-animations.css +57 -2
- package/src/types/case-study.ts +16 -0
- package/src/types/navigation.ts +80 -66
- package/src/utils/index.ts +1 -0
- package/src/utils/source-icons.ts +13 -0
- package/src/utils/visitor-os.ts +84 -0
- package/dist/chunk-LMBC5FFP.js +0 -43
- package/dist/chunk-O43V3EOS.js +0 -252
- package/dist/chunk-QZVSJF2K.cjs +0 -252
- package/dist/chunk-RALRPFDW.cjs +0 -43
- package/dist/components/footer-waitlist-button.d.ts +0 -29
- package/dist/components/footer-waitlist-button.d.ts.map +0 -1
- package/dist/components/footer.d.ts +0 -47
- package/dist/components/footer.d.ts.map +0 -1
- package/dist/components/navigation/client-only-header.d.ts +0 -8
- package/dist/components/navigation/client-only-header.d.ts.map +0 -1
- package/dist/components/navigation/header-skeleton.d.ts +0 -12
- package/dist/components/navigation/header-skeleton.d.ts.map +0 -1
- package/dist/components/navigation/header.d.ts +0 -9
- package/dist/components/navigation/header.d.ts.map +0 -1
- package/dist/components/navigation/mobile-nav-panel.d.ts +0 -10
- package/dist/components/navigation/mobile-nav-panel.d.ts.map +0 -1
- package/src/components/footer-waitlist-button.tsx +0 -53
- package/src/components/footer.tsx +0 -195
- package/src/components/navigation/client-only-header.tsx +0 -22
- package/src/components/navigation/header-skeleton.tsx +0 -74
- package/src/components/navigation/header.tsx +0 -429
- package/src/components/navigation/mobile-nav-panel.tsx +0 -155
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* CaseStudyCard (pure presentation).
|
|
5
|
-
* detail)
|
|
4
|
+
* CaseStudyCard (pure presentation). Four densities — `default` (vertical
|
|
5
|
+
* detail), `sm` (compact horizontal for chat-inline), `portrait` (rails and
|
|
6
|
+
* strips) and `menu` (the site menu's card, hover-plays the highlight cut).
|
|
6
7
|
*
|
|
7
8
|
* The card writes NO click logic — callers wrap with their own anchor
|
|
8
9
|
* and pass the resolved detail URL via `href`.
|
|
@@ -12,9 +13,15 @@
|
|
|
12
13
|
* `useOgPlaceholderUrl(...)`) → `bg-ods-bg`.
|
|
13
14
|
*/
|
|
14
15
|
|
|
16
|
+
import { useState } from 'react';
|
|
15
17
|
import Image from '../../../embed-shims/next-image';
|
|
16
|
-
import
|
|
18
|
+
import Link from '../../../embed-shims/next-link';
|
|
19
|
+
import type { CaseStudyCardData } from '../../../types/case-study';
|
|
17
20
|
import { cn } from '../../../utils/cn';
|
|
21
|
+
import { sortBitesByFeaturedAtDesc } from '../../features/video-bites-shared';
|
|
22
|
+
import { CardHoverPlay } from '../../features/video-center-badge';
|
|
23
|
+
import { VideoHoverPreviewSurface } from '../../features/video-hover-preview';
|
|
24
|
+
import { EntityIcon } from '../../icon-display';
|
|
18
25
|
import { Card } from '../../ui/card';
|
|
19
26
|
import {
|
|
20
27
|
COMPACT_CARD_IMAGE_SLOT,
|
|
@@ -36,7 +43,7 @@ import { useEntityCardLink } from './use-entity-card-link';
|
|
|
36
43
|
import { useEntityCardPlaceholder } from './use-entity-card-placeholder';
|
|
37
44
|
|
|
38
45
|
export interface CaseStudyCardProps {
|
|
39
|
-
study:
|
|
46
|
+
study: CaseStudyCardData;
|
|
40
47
|
/** Detail URL resolved by the caller. */
|
|
41
48
|
href: string;
|
|
42
49
|
/** When `_blank`, opens in a new tab. Set by chat dispatch via
|
|
@@ -46,7 +53,14 @@ export interface CaseStudyCardProps {
|
|
|
46
53
|
targetPlatform?: string | null;
|
|
47
54
|
/** OG placeholder URL, used when `study.featured_image` is missing. */
|
|
48
55
|
placeholderUrl?: string | null;
|
|
49
|
-
|
|
56
|
+
/** `menu` is the site menu's card: the story's video plays muted on hover or
|
|
57
|
+
* focus (the video bites' hover grammar), then who it is about and who tells it. */
|
|
58
|
+
size?: 'default' | 'sm' | 'portrait' | 'menu';
|
|
59
|
+
/** `menu`: mount the hover player (the host passes whether its panel is open,
|
|
60
|
+
* so a closed menu holds no player). Omitted: the card's own viewport gate. */
|
|
61
|
+
mediaMounted?: boolean;
|
|
62
|
+
/** `menu`: runs on click, before the navigation (the host closes its panel). */
|
|
63
|
+
onNavigate?: () => void;
|
|
50
64
|
/** Portrait density: render the content-type chip. Mixed rails only; single-type rails pass false. Default true. */
|
|
51
65
|
showTypeBadge?: boolean;
|
|
52
66
|
className?: string;
|
|
@@ -92,6 +106,22 @@ export function CaseStudyCardSkeleton({ size = 'default' }: { size?: 'default' |
|
|
|
92
106
|
);
|
|
93
107
|
}
|
|
94
108
|
|
|
109
|
+
/**
|
|
110
|
+
* A story's HIGHLIGHT cut, the short video a card previews: its highlight
|
|
111
|
+
* video, else the bite an admin featured (the newest when several are; featuring
|
|
112
|
+
* is independent of the bite's own `published` flag, as on every featured surface). Never
|
|
113
|
+
* the full video: that plays on the story's own page. Null when it has neither.
|
|
114
|
+
*/
|
|
115
|
+
function caseStudyHighlight(study: CaseStudyCardData): { url: string; posterUrl: string | null } | null {
|
|
116
|
+
if (study.highlight_video_url) {
|
|
117
|
+
return { url: study.highlight_video_url, posterUrl: study.highlight_video_thumbnail ?? null };
|
|
118
|
+
}
|
|
119
|
+
const bite = (study.video_bites ?? [])
|
|
120
|
+
.filter(candidate => candidate.featured === true && candidate.url)
|
|
121
|
+
.sort(sortBitesByFeaturedAtDesc)[0];
|
|
122
|
+
return bite ? { url: bite.url, posterUrl: bite.thumbnail_url ?? null } : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
95
125
|
export function CaseStudyCard({
|
|
96
126
|
study,
|
|
97
127
|
href,
|
|
@@ -101,8 +131,11 @@ export function CaseStudyCard({
|
|
|
101
131
|
placeholderUrl: placeholderUrlProp,
|
|
102
132
|
size = 'default',
|
|
103
133
|
showTypeBadge = true,
|
|
134
|
+
mediaMounted,
|
|
135
|
+
onNavigate,
|
|
104
136
|
className,
|
|
105
137
|
}: CaseStudyCardProps) {
|
|
138
|
+
const [hovered, setHovered] = useState(false);
|
|
106
139
|
const { target, rel } = useEntityCardLink({
|
|
107
140
|
href,
|
|
108
141
|
targetPlatform,
|
|
@@ -149,6 +182,83 @@ export function CaseStudyCard({
|
|
|
149
182
|
);
|
|
150
183
|
}
|
|
151
184
|
|
|
185
|
+
if (size === 'menu') {
|
|
186
|
+
const preview = caseStudyHighlight(study);
|
|
187
|
+
const videoUrl = preview?.url ?? null;
|
|
188
|
+
const hasAnyVideo = !!(videoUrl || study.main_video_url);
|
|
189
|
+
// The cover is the FULL video's poster (wide, like the card); the highlight
|
|
190
|
+
// cut's own frame is usually upright and would not fill a 16:9 slot.
|
|
191
|
+
const poster = study.main_video_thumbnail || study.featured_image || preview?.posterUrl || null;
|
|
192
|
+
return (
|
|
193
|
+
<Link
|
|
194
|
+
href={href}
|
|
195
|
+
target={target}
|
|
196
|
+
rel={rel}
|
|
197
|
+
onClick={onNavigate}
|
|
198
|
+
onPointerEnter={() => setHovered(true)}
|
|
199
|
+
onPointerLeave={() => setHovered(false)}
|
|
200
|
+
onFocus={() => setHovered(true)}
|
|
201
|
+
onBlur={() => setHovered(false)}
|
|
202
|
+
className={cn(
|
|
203
|
+
// `group/card` is what the shared play glyph accents on.
|
|
204
|
+
'group/card group flex min-w-0 flex-col gap-[var(--spacing-system-sf)] rounded-xl border border-ods-border bg-ods-bg p-[var(--spacing-system-mf)] text-ods-text-primary transition-colors hover:bg-ods-bg-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ods-accent',
|
|
205
|
+
className,
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
208
|
+
{/* The media never takes the click: the whole card is the link. */}
|
|
209
|
+
<span className="pointer-events-none relative block aspect-video w-full shrink-0 overflow-hidden rounded-md bg-ods-bg">
|
|
210
|
+
{videoUrl ? (
|
|
211
|
+
// The shared hover surface, filled the way the walkthrough card fills
|
|
212
|
+
// its own 16:9 slot (`cover`): an upright cut is cropped to the slot,
|
|
213
|
+
// never letterboxed. Sound on hover, muted when the browser blocks it.
|
|
214
|
+
<VideoHoverPreviewSurface
|
|
215
|
+
url={videoUrl}
|
|
216
|
+
posterUrl={poster}
|
|
217
|
+
active={hovered}
|
|
218
|
+
playerMounted={mediaMounted}
|
|
219
|
+
fit="cover"
|
|
220
|
+
posterSizes="360px"
|
|
221
|
+
/>
|
|
222
|
+
) : (
|
|
223
|
+
<>
|
|
224
|
+
{(poster ?? placeholderUrl) && (
|
|
225
|
+
<Image
|
|
226
|
+
src={poster ?? placeholderUrl ?? ''}
|
|
227
|
+
alt=""
|
|
228
|
+
className="object-cover"
|
|
229
|
+
fill
|
|
230
|
+
sizes="360px"
|
|
231
|
+
unoptimized
|
|
232
|
+
onError={hideOnError}
|
|
233
|
+
/>
|
|
234
|
+
)}
|
|
235
|
+
{hasAnyVideo && <CardHoverPlay size="md" />}
|
|
236
|
+
</>
|
|
237
|
+
)}
|
|
238
|
+
</span>
|
|
239
|
+
{study.msp?.name && (
|
|
240
|
+
<span className="flex items-center gap-[var(--spacing-system-xsf)] text-ods-text-secondary text-h6">
|
|
241
|
+
{study.msp.icon_url && (
|
|
242
|
+
<EntityIcon
|
|
243
|
+
icon={{ url: study.msp.icon_url }}
|
|
244
|
+
size={20}
|
|
245
|
+
className="size-5 shrink-0 rounded object-contain"
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
<span className="min-w-0 truncate">{study.msp.name}</span>
|
|
249
|
+
</span>
|
|
250
|
+
)}
|
|
251
|
+
<span className="line-clamp-2 text-h6">{study.title}</span>
|
|
252
|
+
{study.user?.full_name && (
|
|
253
|
+
<span className="truncate text-ods-text-muted text-h6">
|
|
254
|
+
{study.user.job_title ? `${study.user.full_name}, ${study.user.job_title}` : study.user.full_name}
|
|
255
|
+
</span>
|
|
256
|
+
)}
|
|
257
|
+
<span className="mt-auto text-ods-accent text-h6">{hasAnyVideo ? 'Watch the story' : 'Read the story'}</span>
|
|
258
|
+
</Link>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
152
262
|
if (size === 'portrait') {
|
|
153
263
|
// Rail/strip density — shared <EntityPortraitCard> shell.
|
|
154
264
|
return (
|
|
@@ -12,15 +12,13 @@ export const MESSAGE_TYPE = {
|
|
|
12
12
|
TEXT: 'TEXT',
|
|
13
13
|
THINKING: 'THINKING',
|
|
14
14
|
ASK: 'ASK',
|
|
15
|
-
/** Per-answer
|
|
15
|
+
/** Per-answer attachments (cited documents, videos, entity cards).
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
* deletion here and nothing else. */
|
|
17
|
+
* `ATTACHMENTS` carries `sources` / `videos` / `cards` on the record itself.
|
|
18
|
+
* `GUIDE` is the envelope it replaced — the same arrays under `payload` —
|
|
19
|
+
* and stays only until every backend ships `ATTACHMENTS`. */
|
|
20
|
+
ATTACHMENTS: 'ATTACHMENTS',
|
|
22
21
|
GUIDE: 'GUIDE',
|
|
23
|
-
SOURCES: 'SOURCES',
|
|
24
22
|
EXECUTING_TOOL: 'EXECUTING_TOOL',
|
|
25
23
|
EXECUTED_TOOL: 'EXECUTED_TOOL',
|
|
26
24
|
APPROVAL_REQUEST: 'APPROVAL_REQUEST',
|
|
@@ -412,7 +410,7 @@ export interface ThinkingMessageData extends MessageDataBase {
|
|
|
412
410
|
*
|
|
413
411
|
* Lives here, beside `Message`, because BOTH transports produce it now: the SSE
|
|
414
412
|
* adapter reads it off the per-turn metadata frame, and the NATS path decodes it
|
|
415
|
-
* out of
|
|
413
|
+
* out of an `ATTACHMENTS` chunk. It used to be declared inside
|
|
416
414
|
* `use-sse-chat-adapter`, which made "a Guide answer's citations" structurally a
|
|
417
415
|
* property of one transport — `use-sse-chat-adapter` still re-exports the name
|
|
418
416
|
* so existing imports keep working.
|
|
@@ -451,13 +449,20 @@ export interface ChatSource {
|
|
|
451
449
|
label?: string;
|
|
452
450
|
}
|
|
453
451
|
|
|
454
|
-
/** Persisted
|
|
455
|
-
* carries, so history and realtime share one decoder
|
|
456
|
-
* and cannot drift apart. Left
|
|
457
|
-
*
|
|
458
|
-
|
|
452
|
+
/** Persisted attachments row (GraphQL `AttachmentsData`). The arrays are the
|
|
453
|
+
* SAME ones the live chunk carries, so history and realtime share one decoder
|
|
454
|
+
* (`attachmentsEvent`) and cannot drift apart. Left untyped: it is unvalidated
|
|
455
|
+
* wire data, and the decoder is what turns it into `ChatSource[]` / `ChatRef[]`. */
|
|
456
|
+
export interface AttachmentsMessageData extends MessageDataBase {
|
|
457
|
+
type: 'ATTACHMENTS';
|
|
458
|
+
sources?: unknown;
|
|
459
|
+
videos?: unknown;
|
|
460
|
+
cards?: unknown;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** The envelope `ATTACHMENTS` replaced: the same arrays under `payload`. */
|
|
459
464
|
export interface SourceMetadataMessageData extends MessageDataBase {
|
|
460
|
-
type: 'GUIDE'
|
|
465
|
+
type: 'GUIDE';
|
|
461
466
|
payload?: unknown;
|
|
462
467
|
}
|
|
463
468
|
|
|
@@ -603,6 +608,7 @@ export type MessageData =
|
|
|
603
608
|
| TextMessageData
|
|
604
609
|
| ThinkingMessageData
|
|
605
610
|
| AskMessageData
|
|
611
|
+
| AttachmentsMessageData
|
|
606
612
|
| SourceMetadataMessageData
|
|
607
613
|
| ExecutingToolMessageData
|
|
608
614
|
| ExecutedToolMessageData
|
|
@@ -654,7 +660,7 @@ export interface ProcessedMessage {
|
|
|
654
660
|
* Absent when the source row(s) carried no seq. */
|
|
655
661
|
streamSeq?: number;
|
|
656
662
|
/** Documents this answer cited — see `Message.sources`, the same field.
|
|
657
|
-
* Replayed from the turn's persisted `
|
|
663
|
+
* Replayed from the turn's persisted `ATTACHMENTS` row, so a reloaded
|
|
658
664
|
* answer carries the citations the live one did. */
|
|
659
665
|
sources?: ChatSource[];
|
|
660
666
|
/** Entity references this answer's metadata described — see `Message.refs`. */
|
|
@@ -239,7 +239,7 @@ export interface UnifiedChatMessage {
|
|
|
239
239
|
/**
|
|
240
240
|
* Documents this answer cited, rendered as chips beneath it. Produced by
|
|
241
241
|
* BOTH transports now — SSE reads them off the per-turn metadata frame, NATS
|
|
242
|
-
* decodes them out of
|
|
242
|
+
* decodes them out of an `ATTACHMENTS` chunk.
|
|
243
243
|
*/
|
|
244
244
|
sources?: ChatSource[];
|
|
245
245
|
/**
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import { Megaphone, Bell, Info, Star, Package as PackageGlyph } from 'lucide-react';
|
|
32
32
|
import type { ComponentType, CSSProperties } from 'react';
|
|
33
|
+
import { platformAccentVarName } from '../../../utils/platform-identity';
|
|
33
34
|
import * as IconsV2 from '../../icons-v2-generated';
|
|
34
35
|
import { HeadphoneIcon } from '../../icons-v2-generated/audio-and-visual/headphone-icon';
|
|
35
36
|
import { VideoRecorderIcon } from '../../icons-v2-generated/audio-and-visual/video-recorder-icon';
|
|
@@ -52,6 +53,8 @@ import { GraduationCapIcon } from '../../icons-v2-generated/school/graduation-ca
|
|
|
52
53
|
import { TicketIcon } from '../../icons-v2-generated/shopping/ticket-icon';
|
|
53
54
|
import { Rocket02Icon } from '../../icons-v2-generated/vehicles-and-delivery/rocket-02-icon';
|
|
54
55
|
import { FlamingoLogo } from '../../icons/flamingo-logo';
|
|
56
|
+
import { MiamiCyberGangLogoFaceOnly } from '../../icons/miami-cyber-gang-logo-face-only';
|
|
57
|
+
import { MlgLogo } from '../../icons/mlg-logo';
|
|
55
58
|
import { OpenFrameLogo } from '../../icons/openframe-logo';
|
|
56
59
|
import { OpenmspLogo } from '../../icons/openmsp-logo';
|
|
57
60
|
import { getIconComponent } from './icon-registry';
|
|
@@ -71,8 +74,37 @@ function sizedLogo(Logo: ComponentType<{ className?: string; style?: CSSProperti
|
|
|
71
74
|
return <Logo className={className} style={{ width: size, height: size }} />;
|
|
72
75
|
};
|
|
73
76
|
}
|
|
74
|
-
|
|
77
|
+
// A sister platform's mark is drawn in THAT platform's brand colour (the
|
|
78
|
+
// platform brand registry), never in the accent of the page showing it: on
|
|
79
|
+
// flamingo.run the OpenMSP mark stays OpenMSP yellow, not flamingo pink.
|
|
80
|
+
const brandColor = (platform: string) => `var(${platformAccentVarName(platform)})`;
|
|
81
|
+
// The OpenMSP mark's own defaults are black and white (made for a light page):
|
|
82
|
+
// on the dark surfaces every glyph sits on, the bubbles take the theme's text
|
|
83
|
+
// and background tokens and the back bubble OpenMSP's brand colour.
|
|
84
|
+
const OpenmspLogoIcon: IconComponent = ({ size = 16, className }) => (
|
|
85
|
+
<OpenmspLogo
|
|
86
|
+
className={className}
|
|
87
|
+
style={{ width: size, height: size }}
|
|
88
|
+
frontBubbleColor="var(--color-text-primary)"
|
|
89
|
+
innerFrontBubbleColor="var(--color-bg)"
|
|
90
|
+
backBubbleColor={brandColor('openmsp')}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
75
93
|
const FlamingoLogoIcon = sizedLogo(FlamingoLogo);
|
|
94
|
+
// The two community marks size themselves through a `size` prop, the resolver's own contract.
|
|
95
|
+
// Major League GitHub draws its mark in the theme's text colour on its own site
|
|
96
|
+
// (its header, hero and footer), never in an accent: so does every glyph of it.
|
|
97
|
+
const MlgLogoIcon: IconComponent = ({ size = 16, className, color }) => (
|
|
98
|
+
<MlgLogo size={size} className={className} color={color ?? 'var(--color-text-primary)'} />
|
|
99
|
+
);
|
|
100
|
+
const TmcgLogoIcon: IconComponent = ({ size = 16, className }) => (
|
|
101
|
+
<MiamiCyberGangLogoFaceOnly
|
|
102
|
+
size={size}
|
|
103
|
+
className={className}
|
|
104
|
+
originalPink={brandColor('tmcg')}
|
|
105
|
+
originalWhite="var(--color-text-primary)"
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
76
108
|
|
|
77
109
|
/**
|
|
78
110
|
* `OpenFrameLogo` from `components/icons/` predates the v2 set and:
|
|
@@ -138,6 +170,8 @@ export const ICON_ALIASES: Record<string, IconComponent> = {
|
|
|
138
170
|
// (was the separate `renderSvgIcon` map; now ONE resolver everywhere).
|
|
139
171
|
'openframe-logo': LogoOpenframeIcon,
|
|
140
172
|
'openmsp-logo': OpenmspLogoIcon,
|
|
173
|
+
'mlg-logo': MlgLogoIcon,
|
|
174
|
+
'tmcg-logo': TmcgLogoIcon,
|
|
141
175
|
flamingo: FlamingoLogoIcon,
|
|
142
176
|
'flamingo-logo': FlamingoLogoIcon,
|
|
143
177
|
megaphone: Megaphone,
|
|
@@ -34,7 +34,12 @@ import {
|
|
|
34
34
|
// One normalizer for ask rows, shared with the live decoder — history and the
|
|
35
35
|
// stream must agree on which options are usable.
|
|
36
36
|
import { normalizeAskOptions } from '../../../chat-protocol/nats-decoder';
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
attachmentsEvent,
|
|
39
|
+
mergeSourceMetadata,
|
|
40
|
+
sourceMetadataEvent,
|
|
41
|
+
type SourceMetadata,
|
|
42
|
+
} from '../../../chat-protocol/source-metadata';
|
|
38
43
|
import { applyApprovalStatusToSegment } from '../stream/message-mutations';
|
|
39
44
|
import {
|
|
40
45
|
MESSAGE_TYPE,
|
|
@@ -122,11 +127,14 @@ export function decodeHistoricalMessageData(data: MessageData): ChatStreamEvent
|
|
|
122
127
|
}
|
|
123
128
|
return null;
|
|
124
129
|
|
|
125
|
-
// Persisted
|
|
126
|
-
// (`
|
|
130
|
+
// Persisted attachments, through the SAME decoder the live chunk uses
|
|
131
|
+
// (`attachmentsEvent`). That shared parse is the whole reason a reloaded
|
|
127
132
|
// answer renders identically to the one the reader watched arrive.
|
|
133
|
+
case MESSAGE_TYPE.ATTACHMENTS:
|
|
134
|
+
return attachmentsEvent(data);
|
|
135
|
+
|
|
136
|
+
// The envelope `ATTACHMENTS` replaced (see `MESSAGE_TYPE.GUIDE`).
|
|
128
137
|
case MESSAGE_TYPE.GUIDE:
|
|
129
|
-
case MESSAGE_TYPE.SOURCES:
|
|
130
138
|
return 'payload' in data ? sourceMetadataEvent(data.payload) : null;
|
|
131
139
|
|
|
132
140
|
// Same completeness gate as the live decoder (`decodeNatsChunk`): a
|
package/src/components/index.ts
CHANGED
|
@@ -33,10 +33,8 @@ export * from './faq-accordion';
|
|
|
33
33
|
export * from './faq';
|
|
34
34
|
export * from './related-content';
|
|
35
35
|
export * from './filter-chip';
|
|
36
|
-
export * from './footer';
|
|
37
36
|
export * from './unified-filter-logic';
|
|
38
37
|
export * from './unified-pagination';
|
|
39
|
-
export * from './footer-waitlist-button';
|
|
40
38
|
export * from './hero-image-uploader';
|
|
41
39
|
export * from './icons-block';
|
|
42
40
|
export * from './image-cropper';
|
|
@@ -33,7 +33,7 @@ export function AnnouncementBarSkeleton() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Header skeleton that
|
|
36
|
+
* Header skeleton for a page that is still loading: logo, navigation and action placeholders
|
|
37
37
|
*/
|
|
38
38
|
export function HeaderSkeleton() {
|
|
39
39
|
return (
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
// Navigation component exports
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export {
|
|
4
|
+
// Site navigation: one data model (`SiteNav`), rendered by the header, its
|
|
5
|
+
// menu panels, the mobile sheet and the footer.
|
|
6
|
+
export { SiteHeader } from './site-header';
|
|
7
|
+
export type { SiteHeaderProps } from './site-header';
|
|
8
|
+
export { NavPanel, isMegaMenu } from './nav-panel';
|
|
9
|
+
export type { NavPanelProps } from './nav-panel';
|
|
10
|
+
export { NavItemRow } from './nav-item-row';
|
|
11
|
+
export type { NavItemRowProps, NavItemRowVariant } from './nav-item-row';
|
|
12
|
+
export { ASK_AI_OPEN_EVENT, MingoAiButton, openAskAi } from './mingo-ai-button';
|
|
8
13
|
export type { MingoAiButtonProps } from './mingo-ai-button';
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
export type {
|
|
15
|
-
|
|
16
|
-
export { MobileNavPanel, MOBILE_NAV_PANEL_ID } from './mobile-nav-panel';
|
|
17
|
-
export type { MobileNavPanelProps } from './mobile-nav-panel';
|
|
14
|
+
export { MobileNavSheet, MOBILE_NAV_SHEET_ID, siteNavHasMobileMenus } from './mobile-nav-sheet';
|
|
15
|
+
export type { MobileNavSheetProps } from './mobile-nav-sheet';
|
|
16
|
+
export { SiteFooter } from './site-footer';
|
|
17
|
+
export type { SiteFooterProps } from './site-footer';
|
|
18
|
+
export { defaultRenderSiteNavLink, navLinkLabel } from './site-nav-link';
|
|
19
|
+
export type { SiteNavLinkRenderer } from './site-nav-link';
|
|
18
20
|
|
|
19
21
|
export { SlidingSidebar } from './sliding-sidebar';
|
|
20
22
|
export type { SlidingSidebarProps } from './sliding-sidebar';
|
|
@@ -81,10 +83,15 @@ export type { NavigationNode } from './multi-level-navigation';
|
|
|
81
83
|
|
|
82
84
|
// Re-export types from navigation types
|
|
83
85
|
export type {
|
|
84
|
-
|
|
86
|
+
NavFeature,
|
|
87
|
+
NavGroup,
|
|
88
|
+
NavLink,
|
|
89
|
+
NavMenu,
|
|
85
90
|
NavigationItem,
|
|
86
91
|
NavigationSidebarConfig,
|
|
87
92
|
NavigationSidebarItem,
|
|
93
|
+
SiteNav,
|
|
94
|
+
SiteNavPrimaryCta,
|
|
88
95
|
SlidingSidebarConfig,
|
|
89
96
|
UnifiedSidebarUser,
|
|
90
97
|
} from '../../types/navigation';
|