@flamingo-stack/openframe-frontend-core 0.0.388-snapshot.20260709030118 → 0.0.388
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-LFLFQ422.js → chunk-23BJ64AY.js} +2 -2
- package/dist/{chunk-WIMXB5EU.js → chunk-35C3PUUN.js} +4 -4
- package/dist/{chunk-VXJBE5NX.cjs → chunk-6ZAQOYW4.cjs} +59 -59
- package/dist/{chunk-VXJBE5NX.cjs.map → chunk-6ZAQOYW4.cjs.map} +1 -1
- package/dist/{chunk-4OLIDOLO.js → chunk-7CEJO3FE.js} +2 -2
- package/dist/{chunk-NULFIYK2.js → chunk-7UQ74ZJN.js} +2 -2
- package/dist/{chunk-BJPQJNSC.js → chunk-ATKRYY4U.js} +4 -4
- package/dist/{chunk-2JCSORCJ.cjs → chunk-B4JRV4FB.cjs} +26 -26
- package/dist/{chunk-2JCSORCJ.cjs.map → chunk-B4JRV4FB.cjs.map} +1 -1
- package/dist/{chunk-KA4WXCN3.js → chunk-BGYWTU2E.js} +4 -4
- package/dist/{chunk-XGOX5UQ2.js → chunk-DM3JIOFA.js} +4 -4
- package/dist/{chunk-CQXHHVMR.js → chunk-EQKTBIKH.js} +2 -2
- package/dist/{chunk-ZFZAHLI4.cjs → chunk-EYQESL6X.cjs} +7 -7
- package/dist/{chunk-ZFZAHLI4.cjs.map → chunk-EYQESL6X.cjs.map} +1 -1
- package/dist/{chunk-M6ODI2F5.js → chunk-FS4FVNOU.js} +2 -2
- package/dist/{chunk-3JQUNKWY.cjs → chunk-HIODHJCJ.cjs} +15 -15
- package/dist/{chunk-3JQUNKWY.cjs.map → chunk-HIODHJCJ.cjs.map} +1 -1
- package/dist/{chunk-MPOP47ME.cjs → chunk-HQG5AY64.cjs} +12 -12
- package/dist/{chunk-MPOP47ME.cjs.map → chunk-HQG5AY64.cjs.map} +1 -1
- package/dist/{chunk-Q4YGG3CL.cjs → chunk-KYN2GYUI.cjs} +31 -31
- package/dist/{chunk-Q4YGG3CL.cjs.map → chunk-KYN2GYUI.cjs.map} +1 -1
- package/dist/{chunk-OBKCQ7LA.cjs → chunk-ONXN77KP.cjs} +62 -62
- package/dist/{chunk-OBKCQ7LA.cjs.map → chunk-ONXN77KP.cjs.map} +1 -1
- package/dist/{chunk-44Q3IXZM.js → chunk-SENYQRPF.js} +2 -2
- package/dist/{chunk-TQZLC3BQ.cjs → chunk-SNFSLYVA.cjs} +5 -5
- package/dist/{chunk-TQZLC3BQ.cjs.map → chunk-SNFSLYVA.cjs.map} +1 -1
- package/dist/{chunk-JSFHTIKA.js → chunk-UCQWQ7LM.js} +2 -2
- package/dist/{chunk-IDWKP5H5.js → chunk-VCRUAKOJ.js} +245 -521
- package/dist/chunk-VCRUAKOJ.js.map +1 -0
- package/dist/{chunk-GNB5ERZP.cjs → chunk-VOPTCR5I.cjs} +735 -1011
- package/dist/chunk-VOPTCR5I.cjs.map +1 -0
- package/dist/{chunk-MIEODAAV.cjs → chunk-WIKFWION.cjs} +9 -9
- package/dist/{chunk-MIEODAAV.cjs.map → chunk-WIKFWION.cjs.map} +1 -1
- package/dist/{chunk-7MKU2EB4.cjs → chunk-YKPQINSZ.cjs} +26 -26
- package/dist/{chunk-7MKU2EB4.cjs.map → chunk-YKPQINSZ.cjs.map} +1 -1
- package/dist/{chunk-EHTE3CTT.cjs → chunk-ZVFDJ35S.cjs} +37 -37
- package/dist/{chunk-EHTE3CTT.cjs.map → chunk-ZVFDJ35S.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -4
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.js +1 -3
- package/dist/components/features/video-bites-strip.d.ts +4 -43
- package/dist/components/features/video-bites-strip.d.ts.map +1 -1
- package/dist/components/features/video.d.ts +0 -6
- package/dist/components/features/video.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +120 -122
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +9 -11
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +2 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -3
- package/dist/types/case-study.d.ts +1 -1
- package/dist/types/case-study.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/features/video-bites-strip.tsx +72 -317
- package/src/components/features/video.tsx +37 -167
- package/src/components/user-display.tsx +2 -2
- package/src/types/case-study.ts +1 -1
- package/dist/chunk-GNB5ERZP.cjs.map +0 -1
- package/dist/chunk-IDWKP5H5.js.map +0 -1
- /package/dist/{chunk-LFLFQ422.js.map → chunk-23BJ64AY.js.map} +0 -0
- /package/dist/{chunk-WIMXB5EU.js.map → chunk-35C3PUUN.js.map} +0 -0
- /package/dist/{chunk-4OLIDOLO.js.map → chunk-7CEJO3FE.js.map} +0 -0
- /package/dist/{chunk-NULFIYK2.js.map → chunk-7UQ74ZJN.js.map} +0 -0
- /package/dist/{chunk-BJPQJNSC.js.map → chunk-ATKRYY4U.js.map} +0 -0
- /package/dist/{chunk-KA4WXCN3.js.map → chunk-BGYWTU2E.js.map} +0 -0
- /package/dist/{chunk-XGOX5UQ2.js.map → chunk-DM3JIOFA.js.map} +0 -0
- /package/dist/{chunk-CQXHHVMR.js.map → chunk-EQKTBIKH.js.map} +0 -0
- /package/dist/{chunk-M6ODI2F5.js.map → chunk-FS4FVNOU.js.map} +0 -0
- /package/dist/{chunk-44Q3IXZM.js.map → chunk-SENYQRPF.js.map} +0 -0
- /package/dist/{chunk-JSFHTIKA.js.map → chunk-UCQWQ7LM.js.map} +0 -0
|
@@ -30,10 +30,9 @@
|
|
|
30
30
|
* layout="native" → intrinsic aspect ratio. Bites grid, blog cards.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import React, {
|
|
33
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
34
34
|
import MuxPlayer from '@mux/mux-player-react';
|
|
35
35
|
import { PlayIcon } from '../icons-v2-generated/media-playback/play-icon';
|
|
36
|
-
import { VolumeXmarkIcon } from '../icons-v2-generated/audio-and-visual/volume-xmark-icon';
|
|
37
36
|
import { fetchPriorityProp } from '../../utils/fetch-priority';
|
|
38
37
|
|
|
39
38
|
// =============================================================================
|
|
@@ -106,32 +105,6 @@ if (typeof window !== 'undefined' && typeof console !== 'undefined') {
|
|
|
106
105
|
}
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
// =============================================================================
|
|
110
|
-
// User-activation tracker (module scope) — Chrome's autoplay policy rejects
|
|
111
|
-
// UNMUTED play() until the user has interacted with the page (click/keydown;
|
|
112
|
-
// pointer MOVEMENT does not count). Hover-preview surfaces use this to pick
|
|
113
|
-
// the right first move (sound vs muted) WITHOUT a rejection round-trip, and
|
|
114
|
-
// to unmute a live muted preview the instant the first gesture lands.
|
|
115
|
-
// =============================================================================
|
|
116
|
-
|
|
117
|
-
let userHasInteracted = false;
|
|
118
|
-
const activationWaiters = new Set<() => void>();
|
|
119
|
-
if (typeof window !== 'undefined') {
|
|
120
|
-
const w = window as unknown as { __VIDEO_ACTIVATION_TRACKED__?: boolean };
|
|
121
|
-
if (!w.__VIDEO_ACTIVATION_TRACKED__) {
|
|
122
|
-
w.__VIDEO_ACTIVATION_TRACKED__ = true;
|
|
123
|
-
const markActivated = () => {
|
|
124
|
-
userHasInteracted = true;
|
|
125
|
-
activationWaiters.forEach(fn => { try { fn(); } catch { /* ignore */ } });
|
|
126
|
-
activationWaiters.clear();
|
|
127
|
-
window.removeEventListener('pointerdown', markActivated, true);
|
|
128
|
-
window.removeEventListener('keydown', markActivated, true);
|
|
129
|
-
};
|
|
130
|
-
window.addEventListener('pointerdown', markActivated, true);
|
|
131
|
-
window.addEventListener('keydown', markActivated, true);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
108
|
// =============================================================================
|
|
136
109
|
// URL classifiers (private — `<Video>` is the only consumer)
|
|
137
110
|
// =============================================================================
|
|
@@ -245,11 +218,6 @@ interface VideoFileProps extends VideoCommonProps {
|
|
|
245
218
|
* sound at 50% volume first (bite-strip behavior); falls back to muted when
|
|
246
219
|
* the browser's autoplay policy rejects unmuted hover playback. */
|
|
247
220
|
playOnHover?: boolean;
|
|
248
|
-
/** CONTROLLED variant of playOnHover: the host owns the hover state (e.g.
|
|
249
|
-
* the bite-strip card, whose overlay also counts as "hovering the card").
|
|
250
|
-
* true → start hover playback, false → pause. When provided, the internal
|
|
251
|
-
* pointer handlers are disabled. */
|
|
252
|
-
playWhenHovered?: boolean;
|
|
253
221
|
/** Hide the bottom control bar, keep only the CENTER play/pause control
|
|
254
222
|
* (bite-strip cards per Figma). */
|
|
255
223
|
centerControlsOnly?: boolean;
|
|
@@ -271,7 +239,6 @@ interface VideoAutoProps extends VideoCommonProps {
|
|
|
271
239
|
loop?: boolean;
|
|
272
240
|
chromeless?: boolean;
|
|
273
241
|
playOnHover?: boolean;
|
|
274
|
-
playWhenHovered?: boolean;
|
|
275
242
|
centerControlsOnly?: boolean;
|
|
276
243
|
}
|
|
277
244
|
|
|
@@ -308,7 +275,6 @@ export function Video(props: VideoProps): React.ReactElement | null {
|
|
|
308
275
|
loop={'loop' in props ? props.loop : undefined}
|
|
309
276
|
chromeless={'chromeless' in props ? props.chromeless : undefined}
|
|
310
277
|
playOnHover={'playOnHover' in props ? props.playOnHover : undefined}
|
|
311
|
-
playWhenHovered={'playWhenHovered' in props ? props.playWhenHovered : undefined}
|
|
312
278
|
centerControlsOnly={'centerControlsOnly' in props ? props.centerControlsOnly : undefined}
|
|
313
279
|
className={props.className}
|
|
314
280
|
/>
|
|
@@ -387,7 +353,6 @@ interface FilePlayerProps {
|
|
|
387
353
|
loop?: boolean;
|
|
388
354
|
chromeless?: boolean;
|
|
389
355
|
playOnHover?: boolean;
|
|
390
|
-
playWhenHovered?: boolean;
|
|
391
356
|
centerControlsOnly?: boolean;
|
|
392
357
|
className?: string;
|
|
393
358
|
}
|
|
@@ -402,18 +367,12 @@ function FilePlayer({
|
|
|
402
367
|
loop,
|
|
403
368
|
chromeless,
|
|
404
369
|
playOnHover,
|
|
405
|
-
playWhenHovered,
|
|
406
370
|
centerControlsOnly,
|
|
407
371
|
className,
|
|
408
372
|
}: FilePlayerProps): React.ReactElement {
|
|
409
373
|
// centerControlsOnly: the center play button shows at REST only — while
|
|
410
374
|
// playing, ALL chrome hides (no pause sign over the hover-preview).
|
|
411
375
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
412
|
-
// True while hover playback is running MUTED because the browser's autoplay
|
|
413
|
-
// policy blocked sound (no user activation yet). Drives the center unmute
|
|
414
|
-
// control — the industry pattern (muted autoplay + explicit unmute button)
|
|
415
|
-
// instead of silently waiting for a click somewhere.
|
|
416
|
-
const [hoverMutedFallback, setHoverMutedFallback] = useState(false);
|
|
417
376
|
// playOnHover drives the underlying mux-player element imperatively — the
|
|
418
377
|
// element exposes native play()/pause()/muted/volume; the chrome stays as
|
|
419
378
|
// configured. Sound-first: volume 0.5 unmuted, muted fallback when the
|
|
@@ -434,102 +393,42 @@ function FilePlayer({
|
|
|
434
393
|
// Per-enter generation token: a LATE NotAllowedError from a previous enter
|
|
435
394
|
// must not fire the muted fallback into a newer (intended-sound) session.
|
|
436
395
|
const hoverGenerationRef = useRef(0);
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
const name = (err as { name?: string } | null)?.name;
|
|
464
|
-
if (
|
|
465
|
-
name === 'NotAllowedError' &&
|
|
466
|
-
hoverActiveRef.current &&
|
|
467
|
-
generation === hoverGenerationRef.current
|
|
468
|
-
) {
|
|
469
|
-
try {
|
|
470
|
-
el.muted = true;
|
|
471
|
-
(el.play?.() as Promise<void> | undefined)?.catch?.(() => {});
|
|
472
|
-
setHoverMutedFallback(true);
|
|
473
|
-
} catch { /* give up silently */ }
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
} else {
|
|
477
|
-
// Pre-activation: unmuted WOULD be rejected (hover isn't a gesture in
|
|
478
|
-
// Chrome's activation model) — start muted immediately with no
|
|
479
|
-
// rejection round-trip, and UNMUTE LIVE the instant the user's first
|
|
480
|
-
// click/keydown lands anywhere while this hover is still active.
|
|
481
|
-
el.muted = true;
|
|
482
|
-
(el.play?.() as Promise<void> | undefined)?.catch?.(() => {});
|
|
483
|
-
setHoverMutedFallback(true);
|
|
484
|
-
clearActivationWaiter();
|
|
485
|
-
const waiter = () => {
|
|
486
|
-
activationWaiterRef.current = null;
|
|
487
|
-
if (hoverActiveRef.current && generation === hoverGenerationRef.current) {
|
|
488
|
-
try { el.muted = false; el.volume = 0.5; } catch { /* ignore */ }
|
|
489
|
-
setHoverMutedFallback(false);
|
|
396
|
+
const handleHoverEnter = playOnHover
|
|
397
|
+
? () => {
|
|
398
|
+
hoverActiveRef.current = true;
|
|
399
|
+
const generation = ++hoverGenerationRef.current;
|
|
400
|
+
const el = hoverPlayerRef.current;
|
|
401
|
+
if (!el) return;
|
|
402
|
+
try {
|
|
403
|
+
el.volume = 0.5;
|
|
404
|
+
el.muted = false;
|
|
405
|
+
const attempt = el.play?.();
|
|
406
|
+
if (attempt && typeof (attempt as Promise<void>).catch === 'function') {
|
|
407
|
+
(attempt as Promise<void>).catch((err: unknown) => {
|
|
408
|
+
const name = (err as { name?: string } | null)?.name;
|
|
409
|
+
if (
|
|
410
|
+
name === 'NotAllowedError' &&
|
|
411
|
+
hoverActiveRef.current &&
|
|
412
|
+
generation === hoverGenerationRef.current
|
|
413
|
+
) {
|
|
414
|
+
try {
|
|
415
|
+
el.muted = true;
|
|
416
|
+
// Swallow the retry's own rejection too (a hover-out mid-retry
|
|
417
|
+
// aborts it — that must not surface as an unhandled rejection).
|
|
418
|
+
(el.play?.() as Promise<void> | undefined)?.catch?.(() => {});
|
|
419
|
+
} catch { /* give up silently */ }
|
|
420
|
+
}
|
|
421
|
+
});
|
|
490
422
|
}
|
|
491
|
-
}
|
|
492
|
-
activationWaiterRef.current = waiter;
|
|
493
|
-
activationWaiters.add(waiter);
|
|
423
|
+
} catch { /* ignore */ }
|
|
494
424
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
clearActivationWaiter();
|
|
501
|
-
try { hoverPlayerRef.current?.pause?.(); } catch { /* already torn down */ }
|
|
502
|
-
}, [clearActivationWaiter]);
|
|
503
|
-
|
|
504
|
-
// Explicit unmute affordance: the click IS the user activation the autoplay
|
|
505
|
-
// policy wants, so unmuting here always succeeds (and the window-level
|
|
506
|
-
// activation listener flips the module flag for every other player too).
|
|
507
|
-
const unmuteNow = useCallback((e: React.MouseEvent) => {
|
|
508
|
-
e.stopPropagation();
|
|
509
|
-
e.preventDefault();
|
|
510
|
-
const el = hoverPlayerRef.current;
|
|
511
|
-
try {
|
|
512
|
-
if (el) {
|
|
513
|
-
el.muted = false;
|
|
514
|
-
el.volume = 0.5;
|
|
515
|
-
(el.play?.() as Promise<void> | undefined)?.catch?.(() => {});
|
|
425
|
+
: undefined;
|
|
426
|
+
const handleHoverLeave = playOnHover
|
|
427
|
+
? () => {
|
|
428
|
+
hoverActiveRef.current = false;
|
|
429
|
+
try { hoverPlayerRef.current?.pause?.(); } catch { /* already torn down */ }
|
|
516
430
|
}
|
|
517
|
-
|
|
518
|
-
setHoverMutedFallback(false);
|
|
519
|
-
}, []);
|
|
520
|
-
|
|
521
|
-
// Controlled hover mode (playWhenHovered): the HOST owns hover detection —
|
|
522
|
-
// e.g. the bite-strip card, where the detail overlay is part of the card and
|
|
523
|
-
// must NOT pause playback when the pointer moves onto it.
|
|
524
|
-
const hoverControlled = typeof playWhenHovered === 'boolean';
|
|
525
|
-
useEffect(() => {
|
|
526
|
-
if (!hoverControlled) return;
|
|
527
|
-
if (playWhenHovered) startHoverPlayback();
|
|
528
|
-
else stopHoverPlayback();
|
|
529
|
-
}, [hoverControlled, playWhenHovered, startHoverPlayback, stopHoverPlayback]);
|
|
530
|
-
|
|
531
|
-
const handleHoverEnter = playOnHover && !hoverControlled ? startHoverPlayback : undefined;
|
|
532
|
-
const handleHoverLeave = playOnHover && !hoverControlled ? stopHoverPlayback : undefined;
|
|
431
|
+
: undefined;
|
|
533
432
|
// Raw SRT text is unusable without a custom overlay — and we just deleted
|
|
534
433
|
// the 900-LOC custom-controls layer that owned that overlay. Consumers
|
|
535
434
|
// pass `captionsUrl` (the API-side VTT conversion) alongside `srtContent`
|
|
@@ -604,45 +503,16 @@ function FilePlayer({
|
|
|
604
503
|
</MuxPlayer>
|
|
605
504
|
);
|
|
606
505
|
|
|
607
|
-
// Center unmute control — shown while hover playback runs muted because the
|
|
608
|
-
// autoplay policy blocked sound. Best-practice pattern (Mux / FB / IG):
|
|
609
|
-
// muted autoplay + an explicit unmute affordance, never forced sound.
|
|
610
|
-
// Styled to match media-chrome's center controls exactly (the play glyph in
|
|
611
|
-
// the same slot): plain large white glyph, no circle/border/background,
|
|
612
|
-
// slight dim on hover — so unmute reads as just another center control.
|
|
613
|
-
const unmuteBadge = hoverMutedFallback ? (
|
|
614
|
-
<button
|
|
615
|
-
type="button"
|
|
616
|
-
aria-label="Unmute"
|
|
617
|
-
title="Unmute"
|
|
618
|
-
onClick={unmuteNow}
|
|
619
|
-
className="absolute inset-0 z-10 m-auto flex h-14 w-14 items-center justify-center text-ods-text-primary transition-opacity hover:opacity-75"
|
|
620
|
-
>
|
|
621
|
-
<VolumeXmarkIcon size={56} />
|
|
622
|
-
</button>
|
|
623
|
-
) : null;
|
|
624
|
-
|
|
625
506
|
// MuxPlayerProps has no pointer-event props — the hover-play handlers live
|
|
626
|
-
// on a full-size wrapper instead (only in
|
|
627
|
-
|
|
628
|
-
// hover-capable mode gets a relative wrapper so the unmute badge can dock.
|
|
629
|
-
if (playOnHover && !hoverControlled) {
|
|
507
|
+
// on a full-size wrapper instead (only rendered in playOnHover mode).
|
|
508
|
+
if (playOnHover) {
|
|
630
509
|
return (
|
|
631
510
|
<div
|
|
632
|
-
className="
|
|
511
|
+
className="w-full h-full"
|
|
633
512
|
onPointerEnter={handleHoverEnter}
|
|
634
513
|
onPointerLeave={handleHoverLeave}
|
|
635
514
|
>
|
|
636
515
|
{player}
|
|
637
|
-
{unmuteBadge}
|
|
638
|
-
</div>
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
if (hoverControlled) {
|
|
642
|
-
return (
|
|
643
|
-
<div className="relative w-full h-full">
|
|
644
|
-
{player}
|
|
645
|
-
{unmuteBadge}
|
|
646
516
|
</div>
|
|
647
517
|
);
|
|
648
518
|
}
|
|
@@ -29,14 +29,14 @@ export function UserDisplay({ name, avatarUrl, subtitle, size = 32, shape = 'squ
|
|
|
29
29
|
<div className="min-w-0 flex-1">
|
|
30
30
|
<p className={cn(
|
|
31
31
|
"font-['DM_Sans'] text-ods-text-primary truncate",
|
|
32
|
-
compact ? 'text-
|
|
32
|
+
compact ? 'text-sm leading-5 font-medium' : 'text-lg leading-[22px]',
|
|
33
33
|
)}>
|
|
34
34
|
{name}
|
|
35
35
|
</p>
|
|
36
36
|
{subtitle && (
|
|
37
37
|
<span className={cn(
|
|
38
38
|
"font-['DM_Sans'] text-ods-text-secondary truncate block",
|
|
39
|
-
compact ? 'text-
|
|
39
|
+
compact ? 'text-sm leading-5 font-medium' : 'text-md leading-[16px]',
|
|
40
40
|
)}>
|
|
41
41
|
{subtitle}
|
|
42
42
|
</span>
|
package/src/types/case-study.ts
CHANGED
|
@@ -106,7 +106,7 @@ export interface CreateCaseStudyData {
|
|
|
106
106
|
config?: Record<string, unknown> | null
|
|
107
107
|
highlight_video_url?: string | null
|
|
108
108
|
highlight_video_thumbnail?: string | null
|
|
109
|
-
highlight_video_source?:
|
|
109
|
+
highlight_video_source?: string | null
|
|
110
110
|
highlight_video_duration_ms?: number | null
|
|
111
111
|
// SEO
|
|
112
112
|
seo_title?: string
|