@openrails/contentkit-ui 0.90.2 → 0.91.0
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/README.md +6 -4
- package/dist/client/client.d.ts +1 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/http.d.ts +13 -8
- package/dist/client/http.js +16 -4
- package/dist/client/http.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/media/client.d.ts +3 -3
- package/dist/client/media/client.js +2 -2
- package/dist/client/media/client.js.map +1 -1
- package/dist/components/content/search.js +1 -1
- package/dist/components/content/search.js.map +1 -1
- package/dist/components/hover-preview.js +1 -1
- package/dist/components/hover-preview.js.map +1 -1
- package/dist/components/media-gallery.d.ts +2 -2
- package/dist/components/media-gallery.js +4 -4
- package/dist/components/media-gallery.js.map +1 -1
- package/dist/components/video-mini-player.js +1 -1
- package/dist/components/video-mini-player.js.map +1 -1
- package/dist/components/video-player.d.ts +3 -3
- package/dist/components/video-player.js +3 -3
- package/dist/components/video-player.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/react/gallery.d.ts +6 -3
- package/dist/react/gallery.js +303 -228
- package/dist/react/gallery.js.map +1 -1
- package/dist/react/player.d.ts +4 -6
- package/dist/react/player.js +54 -61
- package/dist/react/player.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import { jsx as a } from "react/jsx-runtime";
|
|
|
7
7
|
function o({ item: o, dir: s = "hls/", duration: c, start: l, available: u = !0, target: d, enabled: f, abr: p, className: m, client: h }) {
|
|
8
8
|
let { media: g } = t(h), [_, v] = i(null), y = n({
|
|
9
9
|
src: `${g.hlsBase(o, s)}master.m3u8`,
|
|
10
|
-
|
|
10
|
+
loader: g.hls,
|
|
11
11
|
abr: p,
|
|
12
12
|
qualityKey: null
|
|
13
13
|
}), b = c ?? 0, x = Math.max(0, l ?? (b > 1 ? Math.min(b * .1, b - 1) : 0));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hover-preview.js","names":[],"sources":["../../src/components/hover-preview.tsx"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { RefBody } from \"../client/generated/wire.js\";\nimport { useContentKitClient } from \"../react/context.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useInlinePreview } from \"../react/inline-preview.js\";\n\nexport interface HoverPreviewProps extends Pick<HlsPlayerOptions, \"abr\"> {\n /** The video item. */\n item: RefBody;\n /** Its HLS folder (a read's `hls` entry). Default \"hls/\". */\n dir?: string;\n /** Seconds; the preview starts 10% in. */\n duration?: number;\n /** Where the preview starts, seconds; overrides the 10% default. */\n start?: number;\n /** The viewer may play it (free, or entitled). Default true. */\n available?: boolean;\n /** What is hovered (fine pointers) or watched for visibility (touch); default the preview's parent, the card. */\n target?: HTMLElement | null;\n /** Previews on; default the provider's `inlinePreview`. */\n enabled?: boolean;\n className?: string;\n client?: ContentKitClient;\n}\n\n/**\n * A card's muted inline preview: the item's real HLS stream through the\n * client (its base and playlist auth) at the lowest rendition, fading in over\n * the card's thumbnail on hover, or while it is the most visible card on\n * touch screens. One plays page-wide; none with reduced motion or Save-Data.\n * Place it inside the card, which must be positioned.\n */\nexport function HoverPreview({ item, dir = \"hls/\", duration, start, available = true, target, enabled, abr, className, client: own }: HoverPreviewProps) {\n const { media } = useContentKitClient(own);\n const [el, setEl] = useState<HTMLVideoElement | null>(null);\n const player = useHlsPlayer({ src: `${media.hlsBase(item, dir)}master.m3u8`,
|
|
1
|
+
{"version":3,"file":"hover-preview.js","names":[],"sources":["../../src/components/hover-preview.tsx"],"sourcesContent":["import { useCallback, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { RefBody } from \"../client/generated/wire.js\";\nimport { useContentKitClient } from \"../react/context.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useInlinePreview } from \"../react/inline-preview.js\";\n\nexport interface HoverPreviewProps extends Pick<HlsPlayerOptions, \"abr\"> {\n /** The video item. */\n item: RefBody;\n /** Its HLS folder (a read's `hls` entry). Default \"hls/\". */\n dir?: string;\n /** Seconds; the preview starts 10% in. */\n duration?: number;\n /** Where the preview starts, seconds; overrides the 10% default. */\n start?: number;\n /** The viewer may play it (free, or entitled). Default true. */\n available?: boolean;\n /** What is hovered (fine pointers) or watched for visibility (touch); default the preview's parent, the card. */\n target?: HTMLElement | null;\n /** Previews on; default the provider's `inlinePreview`. */\n enabled?: boolean;\n className?: string;\n client?: ContentKitClient;\n}\n\n/**\n * A card's muted inline preview: the item's real HLS stream through the\n * client (its base and playlist auth) at the lowest rendition, fading in over\n * the card's thumbnail on hover, or while it is the most visible card on\n * touch screens. One plays page-wide; none with reduced motion or Save-Data.\n * Place it inside the card, which must be positioned.\n */\nexport function HoverPreview({ item, dir = \"hls/\", duration, start, available = true, target, enabled, abr, className, client: own }: HoverPreviewProps) {\n const { media } = useContentKitClient(own);\n const [el, setEl] = useState<HTMLVideoElement | null>(null);\n const player = useHlsPlayer({ src: `${media.hlsBase(item, dir)}master.m3u8`, loader: media.hls, abr, qualityKey: null });\n const d = duration ?? 0;\n const at = Math.max(0, start ?? (d > 1 ? Math.min(d * 0.1, d - 1) : 0));\n useInlinePreview({ setting: enabled, available, target: target ?? el?.parentElement ?? null, start: () => player.preview(at), stop: player.unload });\n const attach = player.ref;\n const ref = useCallback(\n (v: HTMLVideoElement | null) => {\n attach(v);\n setEl(v);\n },\n [attach],\n );\n return (\n <video\n ref={ref}\n muted\n playsInline\n preload=\"none\"\n aria-hidden\n tabIndex={-1}\n className={className}\n style={{\n position: \"absolute\",\n inset: 0,\n width: \"100%\",\n height: \"100%\",\n objectFit: \"cover\",\n pointerEvents: \"none\",\n opacity: player.previewing && player.status === \"playing\" ? 1 : 0,\n transition: \"opacity 200ms\",\n }}\n data-ckui=\"hover-preview\"\n data-playing={player.previewing && player.status === \"playing\" ? \"\" : undefined}\n />\n );\n}\n"],"mappings":";;;;;;AAiCA,SAAgB,EAAa,EAAE,SAAM,SAAM,QAAQ,aAAU,UAAO,eAAY,IAAM,WAAQ,YAAS,QAAK,cAAW,QAAQ,KAA0B;CACvJ,IAAM,EAAE,aAAU,EAAoB,CAAG,GACnC,CAAC,GAAI,KAAS,EAAkC,IAAI,GACpD,IAAS,EAAa;EAAE,KAAK,GAAG,EAAM,QAAQ,GAAM,CAAG,EAAE;EAAc,QAAQ,EAAM;EAAK;EAAK,YAAY;CAAK,CAAC,GACjH,IAAI,KAAY,GAChB,IAAK,KAAK,IAAI,GAAG,MAAU,IAAI,IAAI,KAAK,IAAI,IAAI,IAAK,IAAI,CAAC,IAAI,EAAE;CACtE,EAAiB;EAAE,SAAS;EAAS;EAAW,QAAQ,KAAU,GAAI,iBAAiB;EAAM,aAAa,EAAO,QAAQ,CAAE;EAAG,MAAM,EAAO;CAAO,CAAC;CACnJ,IAAM,IAAS,EAAO,KAChB,IAAM,GACT,MAA+B;EAE9B,AADA,EAAO,CAAC,GACR,EAAM,CAAC;CACT,GACA,CAAC,CAAM,CACT;CACA,OACE,kBAAC,SAAD;EACO;EACL,OAAA;EACA,aAAA;EACA,SAAQ;EACR,eAAA;EACA,UAAU;EACC;EACX,OAAO;GACL,UAAU;GACV,OAAO;GACP,OAAO;GACP,QAAQ;GACR,WAAW;GACX,eAAe;GACf,SAAS,EAAO,cAAc,EAAO,WAAW,YAAY,IAAI;GAChE,YAAY;EACd;EACA,aAAU;EACV,gBAAc,EAAO,cAAc,EAAO,WAAW,YAAY,KAAK,KAAA;CACvE,CAAA;AAEL"}
|
|
@@ -5,11 +5,11 @@ import { GalleryViewOptions, HlsPlayerOptions } from '../react/gallery.js';
|
|
|
5
5
|
import { PublicPreset } from '../client/public.js';
|
|
6
6
|
import { ContentKitClient } from '../client/client.js';
|
|
7
7
|
import { ReadResult, RefBody } from '../client/generated/wire.js';
|
|
8
|
-
export interface MediaGalleryProps extends GalleryViewOptions, Pick<HlsPlayerOptions, "
|
|
8
|
+
export interface MediaGalleryProps extends GalleryViewOptions, Pick<HlsPlayerOptions, "loader" | "refresh" | "abr"> {
|
|
9
9
|
/**
|
|
10
10
|
* The item to show, read through the client: its read (refreshed before
|
|
11
11
|
* the URLs expire, after commits), HLS bases, playlist auth and grant
|
|
12
|
-
* refresh. Or pass `read` (and hlsBase,
|
|
12
|
+
* refresh. Or pass `read` (and hlsBase, loader, refresh) yourself.
|
|
13
13
|
*/
|
|
14
14
|
item?: RefBody;
|
|
15
15
|
/** With item: only files under this path prefix. */
|
|
@@ -114,7 +114,7 @@ function W(e) {
|
|
|
114
114
|
...e,
|
|
115
115
|
read: n.read,
|
|
116
116
|
hlsBase: e.hlsBase ?? i,
|
|
117
|
-
|
|
117
|
+
loader: e.loader ?? t.media.hls,
|
|
118
118
|
refresh: e.refresh ?? n.refresh
|
|
119
119
|
};
|
|
120
120
|
}
|
|
@@ -253,7 +253,7 @@ function q({ ctx: e, item: t, position: n, active: i, lightbox: a }) {
|
|
|
253
253
|
height: s.h,
|
|
254
254
|
duration: s.dur,
|
|
255
255
|
active: i,
|
|
256
|
-
|
|
256
|
+
loader: e.loader,
|
|
257
257
|
refresh: e.refresh,
|
|
258
258
|
expires: e.read?.expires,
|
|
259
259
|
abr: e.abr,
|
|
@@ -450,7 +450,7 @@ function ee({ ctx: e, item: r, label: i, onOpen: a }) {
|
|
|
450
450
|
className: "absolute inset-0 size-full object-cover"
|
|
451
451
|
}) : d ? /* @__PURE__ */ T(g, {
|
|
452
452
|
vtt: `${d}sprite.vtt`,
|
|
453
|
-
|
|
453
|
+
loader: e.loader,
|
|
454
454
|
fit: "cover"
|
|
455
455
|
}) : null,
|
|
456
456
|
d && /* @__PURE__ */ T(te, {
|
|
@@ -493,7 +493,7 @@ function ee({ ctx: e, item: r, label: i, onOpen: a }) {
|
|
|
493
493
|
function te({ ctx: t, base: n, start: r, target: i }) {
|
|
494
494
|
let a = m({
|
|
495
495
|
src: `${n}master.m3u8`,
|
|
496
|
-
|
|
496
|
+
loader: t.loader,
|
|
497
497
|
refresh: t.refresh,
|
|
498
498
|
expires: t.read?.expires,
|
|
499
499
|
abr: t.abr,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-gallery.js","names":[],"sources":["../../src/components/media-gallery.tsx"],"sourcesContent":["import { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\nimport {\n AlertCircleIcon,\n ArrowLeft01Icon,\n ArrowRight01Icon,\n Cancel01Icon,\n CarouselHorizontalIcon,\n Download01Icon,\n GridViewIcon,\n MusicNote01Icon,\n PlayIcon,\n SquareLock02Icon,\n} from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useMemo, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { formatDuration, galleryItems, stageAspect, type GalleryItem, type GalleryLockedItem, type GalleryMediaItem } from \"../client/gallery.js\";\nimport { useCarousel, useGalleryView, useHlsPlayer, useRefreshBeforeExpiry, type GalleryViewOptions, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useInlinePreview } from \"../react/inline-preview.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { publicRenditions, type PublicPreset } from \"../client/public.js\";\nimport { ContentKitUiRoot, useScopeProps } from \"../scope.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { FileInfo, ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport { useOptionalContentKitClient } from \"../react/context.js\";\nimport { useMediaRead } from \"../react/read.js\";\nimport { previewStartAt, SpriteFrame, VideoPlayer } from \"./video-player.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { ToggleGroup, ToggleGroupItem } from \"#ckui/ui/toggle-group\";\n\nexport interface MediaGalleryProps extends GalleryViewOptions, Pick<HlsPlayerOptions, \"xhrSetup\" | \"refresh\" | \"abr\"> {\n /**\n * The item to show, read through the client: its read (refreshed before\n * the URLs expire, after commits), HLS bases, playlist auth and grant\n * refresh. Or pass `read` (and hlsBase, xhrSetup, refresh) yourself.\n */\n item?: RefBody;\n /** With item: only files under this path prefix. */\n prefix?: string;\n /** With item: each audio file's download name signed into its URL. */\n download?: boolean;\n /**\n * The read API result: files in manifest order with this viewer's access\n * (scope it with a prefix). A download read adds each audio file's download.\n * With `refresh`, the gallery reads again shortly before `read.expires`.\n */\n read?: ReadResult | null;\n /** A ladder's HLS folder from the read's `hls` dir: client.media.hlsBase(ref, dir). */\n hlsBase?: (dir: string) => string;\n /** The item's poster (its public preset, or a URL), drawn on the first video. */\n poster?: PublicPreset | string | null;\n /** Where the first video's preview starts, seconds (e.g. the poster's frame); default 10% in. */\n previewStart?: number;\n /** Playable videos preview muted inline (hover, or in view on touch); default the provider's. */\n inlinePreview?: boolean;\n /** The host's unlock call to action, drawn over the locked item. */\n renderLocked?: () => ReactNode;\n /** Below the carousel, for its current item (e.g. downloads). */\n renderDetails?: (item: GalleryItem) => ReactNode;\n /** `sizes` for carousel images. Default \"(min-width: 768px) 720px, 100vw\". */\n sizes?: string;\n /** Tallest the carousel gets; taller media letterboxes. Default \"80svh\": every slide at its native aspect, full width, never taller than the screen; \"none\" lifts it. */\n maxHeight?: string;\n label?: string;\n className?: string;\n appearance?: ContentKitUiAppearance;\n /** Overrides the provider's client (item). */\n client?: ContentKitClient;\n}\n\ninterface Ctx extends MediaGalleryProps {\n items: GalleryItem[];\n}\n\n// The item's poster and preview start belong to its first video.\nfunction videoArt(ctx: Ctx, item: GalleryMediaItem): { poster?: PublicPreset | string; start: number } {\n const first = ctx.items.find((i): i is GalleryMediaItem => i.kind === \"video\") === item;\n return { poster: first ? (ctx.poster ?? undefined) : undefined, start: previewStartAt(first ? ctx.previewStart : undefined, item.file.dur) };\n}\n\nconst baseOf = (ctx: Ctx, item: GalleryMediaItem) => (item.dir && ctx.hlsBase ? ctx.hlsBase(item.dir) : null);\n\n/**\n * A post's images and videos as a swipeable carousel or a tile grid that opens\n * a lightbox, with a view toggle. One item renders alone.\n */\nexport function MediaGallery(given: MediaGalleryProps) {\n const props = useItemRead(given);\n const { read, label, className, appearance, view: chosen, defaultView, onViewChange, storageKey } = props;\n const { t } = useMessages();\n const items = useMemo(() => galleryItems(read), [read]);\n useRefreshBeforeExpiry(read?.expires, props.refresh);\n const [view, setView] = useGalleryView({ view: chosen, defaultView, onViewChange, storageKey });\n const [index, setIndex] = useState(0);\n const [lightbox, setLightbox] = useState<number | null>(null);\n if (items.length === 0) return null;\n const ctx: Ctx = { ...props, items };\n const multi = items.length > 1;\n const current = Math.min(index, items.length - 1);\n const shown = multi ? view : \"carousel\";\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"@container grid gap-2\", className)} data-ckui=\"media-gallery\" data-view={shown} role=\"region\" aria-label={label ?? t(\"gallery.label\")}>\n {multi && (\n <div className=\"flex items-center justify-between gap-2\" data-ckui=\"gallery-header\">\n <span className=\"text-sm text-muted-foreground tabular-nums\" data-ckui=\"gallery-count\">\n {shown === \"carousel\" ? t(\"gallery.counter\", { current: current + 1, total: items.length }) : null}\n </span>\n <ToggleGroup aria-label={t(\"gallery.view\")} value={[shown]} onValueChange={(v) => v[0] && setView(v[0] as typeof view)}>\n <ToggleGroupItem value=\"carousel\" aria-label={t(\"gallery.carousel\")} title={t(\"gallery.carousel\")}>\n <HugeiconsIcon icon={CarouselHorizontalIcon} strokeWidth={2} />\n </ToggleGroupItem>\n <ToggleGroupItem value=\"grid\" aria-label={t(\"gallery.grid\")} title={t(\"gallery.grid\")}>\n <HugeiconsIcon icon={GridViewIcon} strokeWidth={2} />\n </ToggleGroupItem>\n </ToggleGroup>\n </div>\n )}\n {shown === \"carousel\" ? (\n <>\n <Carousel ctx={ctx} index={current} onIndex={setIndex} />\n {props.renderDetails?.(items[current]!)}\n </>\n ) : (\n <Grid ctx={ctx} onOpen={setLightbox} />\n )}\n <Lightbox ctx={ctx} index={lightbox} onIndex={setLightbox} />\n </ContentKitUiRoot>\n );\n}\n\nconst NO_ITEM: RefBody = { kind: \"\", id: \"\" };\n\n// With item, the read, HLS bases, playlist auth and refresh come from the client.\nfunction useItemRead(p: MediaGalleryProps): MediaGalleryProps {\n const client = useOptionalContentKitClient(p.client);\n if (p.item && !client) throw new Error(\"contentkit-ui: MediaGallery item needs `client` or a <ContentKitProvider>\");\n // One refresh timer for the gallery and its players, keyed by r.refresh.\n const r = useMediaRead(p.item ?? NO_ITEM, { prefix: p.prefix, download: p.download, read: p.item ? p.read : null, refresh: false, client });\n const item = p.item;\n const hlsBase = useCallback((dir: string) => client!.media.hlsBase(item!, dir), [client, item]);\n if (!item || !client) return p;\n return { ...p, read: r.read, hlsBase: p.hlsBase ?? hlsBase, xhrSetup: p.xhrSetup ?? client.media.xhrSetup, refresh: p.refresh ?? r.refresh };\n}\n\nfunction Carousel({ ctx, index: given, onIndex, lightbox }: { ctx: Ctx; index: number; onIndex: (i: number) => void; lightbox?: boolean }) {\n const { t } = useMessages();\n const { items } = ctx;\n const c = useCarousel({ count: items.length, index: given, onIndexChange: onIndex });\n const { index } = c;\n const multi = items.length > 1;\n const stage = lightbox ? {} : { aspectRatio: String(stageAspect(items, index)), maxHeight: ctx.maxHeight ?? \"80svh\" };\n return (\n <div\n className={cn(\"group/carousel relative outline-none\", lightbox ? \"size-full\" : \"grid gap-2\")}\n role={multi ? \"group\" : undefined}\n aria-roledescription={multi ? \"carousel\" : undefined}\n aria-label={multi ? (ctx.label ?? t(\"gallery.label\")) : undefined}\n tabIndex={multi && !lightbox ? 0 : undefined}\n onKeyDown={multi && !lightbox ? c.onKeyDown : undefined}\n data-ckui=\"carousel\"\n >\n <div\n className={cn(\n \"relative w-full touch-pan-y overflow-hidden select-none\",\n lightbox ? \"h-full\" : \"rounded-lg bg-muted transition-[aspect-ratio] duration-300 ease-out motion-reduce:transition-none\",\n )}\n style={stage}\n data-ckui=\"stage\"\n {...(multi ? c.swipe : {})}\n >\n <div\n className={cn(\"flex h-full\", c.dragging ? \"transition-none\" : \"transition-transform duration-300 ease-out motion-reduce:transition-none\")}\n style={{ transform: `translateX(calc(${-index * 100}% + ${c.offset}px))` }}\n >\n {items.map((item, i) => {\n const near = Math.abs(i - index) <= 1;\n return (\n <div\n key={item.key}\n className=\"relative h-full w-full shrink-0 overflow-hidden\"\n role={multi ? \"group\" : undefined}\n aria-roledescription={multi ? \"slide\" : undefined}\n aria-label={multi ? t(\"gallery.slide\", { current: i + 1, total: items.length }) : undefined}\n aria-hidden={i !== index || undefined}\n inert={i !== index}\n data-ckui=\"slide\"\n data-current={i === index ? \"\" : undefined}\n >\n {near && <Slide ctx={ctx} item={item} position={i} active={i === index} lightbox={lightbox} />}\n </div>\n );\n })}\n </div>\n {multi && (\n <>\n <NavButton side=\"prev\" hidden={index === 0} last={index === 1} onClick={c.prev} label={t(\"gallery.previous\")} lightbox={lightbox} />\n <NavButton side=\"next\" hidden={index === items.length - 1} last={index === items.length - 2} onClick={c.next} label={t(\"gallery.next\")} lightbox={lightbox} />\n </>\n )}\n </div>\n {multi && !lightbox && items.length <= 12 && (\n <div className=\"flex justify-center gap-0.5\" data-ckui=\"dots\">\n {items.map((item, i) => (\n <button\n key={item.key}\n type=\"button\"\n className=\"group/dot flex size-4 items-center justify-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n aria-label={t(\"gallery.goTo\", { index: i + 1 })}\n aria-current={i === index || undefined}\n onClick={() => c.go(i)}\n >\n <span className={cn(\"size-1.5 rounded-full transition-colors\", i === index ? \"bg-foreground\" : \"bg-foreground/25 group-hover/dot:bg-foreground/50\")} />\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// A strip down the stage's side, clear of a video's control bar: the stage's\n// height follows each slide's aspect, so a centred-only target would move\n// between quick clicks. A swipe may start on it. The click that reaches an end\n// removes the button, so focus moves to the carousel and the arrow keys keep working.\nfunction NavButton({ side, hidden, last, onClick, label, lightbox }: { side: \"prev\" | \"next\"; hidden: boolean; last: boolean; onClick: () => void; label: string; lightbox?: boolean }) {\n if (hidden) return null;\n return (\n <button\n type=\"button\"\n aria-label={label}\n data-ckui=\"gallery-nav\"\n data-side={side}\n onClick={(e) => {\n const root = e.currentTarget.closest<HTMLElement>(\"[data-ckui=lightbox], [data-ckui=carousel][tabindex]\");\n onClick();\n if (last && document.activeElement === e.currentTarget) root?.focus({ preventScroll: true });\n }}\n className={cn(\n \"group/nav absolute top-0 bottom-14 z-10 flex items-center outline-none\",\n lightbox ? \"w-16 sm:w-20\" : \"w-12\",\n side === \"prev\" ? (lightbox ? \"left-0 justify-start pl-3 sm:pl-5\" : \"left-0 justify-start pl-2\") : lightbox ? \"right-0 justify-end pr-3 sm:pr-5\" : \"right-0 justify-end pr-2\",\n )}\n >\n <span\n className={cn(\n \"flex items-center justify-center rounded-full backdrop-blur-sm transition-colors group-focus-visible/nav:ring-3 group-focus-visible/nav:ring-ring/60\",\n lightbox ? \"size-10 bg-white/10 text-white group-hover/nav:bg-white/20\" : \"size-8 bg-white/85 text-zinc-900 shadow-md group-hover/nav:bg-white\",\n )}\n >\n <HugeiconsIcon icon={side === \"prev\" ? ArrowLeft01Icon : ArrowRight01Icon} strokeWidth={2} className=\"size-4\" />\n </span>\n </button>\n );\n}\n\nfunction Slide({ ctx, item, position, active, lightbox }: { ctx: Ctx; item: GalleryItem; position: number; active: boolean; lightbox?: boolean }) {\n const { t } = useMessages();\n if (item.kind === \"locked\") return <Locked ctx={ctx} item={item} />;\n const f = item.file;\n if (item.kind === \"audio\") return <AudioSlide item={item} position={position} />;\n if (item.kind === \"image\") {\n if (!f.url) return f.failed ? <ImageFailed file={f} /> : <Processing>{t(\"gallery.processingImage\")}</Processing>;\n // A public teaser of a locked item carries the unlock, so no one swipes past it to find it.\n const locked = item.key.startsWith(\"preview/\") && !lightbox ? ctx.items.find((i): i is GalleryLockedItem => i.kind === \"locked\") : undefined;\n return (\n <>\n <img\n src={f.url}\n alt={t(\"gallery.image\", { index: position + 1 })}\n width={f.w}\n height={f.h}\n sizes={lightbox ? \"100vw\" : (ctx.sizes ?? \"(min-width: 768px) 720px, 100vw\")}\n loading={active ? \"eager\" : \"lazy\"}\n decoding=\"async\"\n draggable={false}\n className=\"absolute inset-0 size-full object-contain\"\n />\n {locked && <LockedBar ctx={ctx} />}\n </>\n );\n }\n const { poster, start } = videoArt(ctx, item);\n return (\n <VideoPlayer\n inlinePreview={lightbox ? false : ctx.inlinePreview}\n previewStart={start}\n layout=\"fill\"\n base={baseOf(ctx, item)}\n poster={poster}\n width={f.w}\n height={f.h}\n duration={f.dur}\n active={active}\n xhrSetup={ctx.xhrSetup}\n refresh={ctx.refresh}\n expires={ctx.read?.expires}\n abr={ctx.abr}\n label={t(\"gallery.video\", { index: position + 1 })}\n className={lightbox ? \"bg-transparent\" : undefined}\n />\n );\n}\n\n// An audio file (a ladder's M4A): a native player, its duration and, in a\n// download read, its download.\nfunction AudioSlide({ item, position }: { item: GalleryMediaItem; position: number }) {\n const { t } = useMessages();\n const f = item.file;\n const name = (item.dir ?? f.path).replace(/\\/$/, \"\").split(\"/\").pop();\n const label = t(\"gallery.audio\", { index: position + 1 });\n return (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-3 bg-muted p-4\" data-ckui=\"audio\">\n <div className=\"flex min-w-0 items-center gap-2 text-sm text-muted-foreground\">\n <HugeiconsIcon icon={MusicNote01Icon} className=\"size-5 shrink-0\" strokeWidth={1.75} />\n {name && <span className=\"truncate\">{name}</span>}\n {f.dur ? <span className=\"tabular-nums\">{formatDuration(f.dur)}</span> : null}\n </div>\n {f.failed ? (\n <p className=\"text-sm text-destructive\" role=\"alert\">\n {t(\"gallery.failedAudio\")}\n </p>\n ) : f.url ? (\n <audio src={f.url} controls preload=\"metadata\" aria-label={label} className=\"w-full max-w-md\" data-ckui-noswipe=\"\" />\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t(\"gallery.processingAudio\")}</p>\n )}\n {f.download && f.url && (\n <a href={f.url} download={f.download} className=\"inline-flex items-center gap-1.5 text-sm underline-offset-4 hover:underline\" data-ckui-noswipe=\"\">\n <HugeiconsIcon icon={Download01Icon} className=\"size-4\" strokeWidth={2} />\n {t(\"gallery.download\")}\n </a>\n )}\n </div>\n );\n}\n\n// Why an image cannot be shown (editors only: the read API's failed fields).\nfunction ImageFailed({ file }: { file: FileInfo }) {\n const { t, error } = useMessages();\n const f = file.failed;\n const text = f?.code ? error({ code: f.code, message: f.message, details: f.details, refusal: true }) : t(\"gallery.failedImage\");\n return (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-2 bg-muted p-4 text-center text-sm text-destructive\" role=\"alert\" data-ckui=\"image-failed\">\n <HugeiconsIcon icon={AlertCircleIcon} className=\"size-6\" />\n <p className=\"max-w-sm text-balance\">{text}</p>\n </div>\n );\n}\n\nfunction Processing({ children }: { children: string }) {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center gap-2 bg-muted text-sm text-muted-foreground\">\n <span aria-hidden className=\"size-4 rounded-full border-2 border-current/30 border-t-current motion-safe:animate-spin\" />\n {children}\n </div>\n );\n}\n\nfunction Locked({ ctx, item, tile }: { ctx: Ctx; item: GalleryLockedItem; tile?: boolean }) {\n const { t } = useMessages();\n return (\n <div className=\"absolute inset-0 overflow-hidden bg-muted\" data-ckui=\"locked\">\n {item.backdrop && <img src={item.backdrop} alt=\"\" draggable={false} className=\"absolute inset-0 size-full scale-110 object-cover blur-xl\" />}\n <div className={cn(\"absolute inset-0 flex flex-col items-center justify-center gap-3 p-4 text-center\", item.backdrop ? \"bg-black/45 text-white\" : \"text-foreground\")}>\n <HugeiconsIcon icon={SquareLock02Icon} className={tile ? \"size-6\" : \"size-8\"} strokeWidth={1.75} />\n {!tile && (\n <>\n <p className=\"text-sm font-medium text-balance\">{t(\"gallery.locked\")}</p>\n {ctx.renderLocked && <div data-ckui-noswipe=\"\">{ctx.renderLocked()}</div>}\n </>\n )}\n </div>\n </div>\n );\n}\n\nfunction LockedBar({ ctx }: { ctx: Ctx }) {\n const { t } = useMessages();\n return (\n <div\n className=\"absolute inset-x-0 bottom-0 flex flex-wrap items-center justify-between gap-x-3 gap-y-2 bg-linear-to-t from-black/75 via-black/45 to-transparent px-4 pt-10 pb-4 text-white\"\n data-ckui=\"locked-bar\"\n >\n <span className=\"flex items-center gap-2 text-sm font-medium\">\n <HugeiconsIcon icon={SquareLock02Icon} className=\"size-5 shrink-0\" strokeWidth={1.75} />\n {t(\"gallery.locked\")}\n </span>\n {ctx.renderLocked && <div data-ckui-noswipe=\"\">{ctx.renderLocked()}</div>}\n </div>\n );\n}\n\nfunction Grid({ ctx, onOpen }: { ctx: Ctx; onOpen: (i: number) => void }) {\n const { t } = useMessages();\n const n = ctx.items.length;\n return (\n <ul className={cn(\"grid gap-1\", n === 2 || n === 4 ? \"grid-cols-2\" : \"grid-cols-2 @md:grid-cols-3\")} data-ckui=\"grid\">\n {ctx.items.map((item, i) => (\n <li key={item.key}>\n <Tile ctx={ctx} item={item} label={t(\"gallery.open\", { index: i + 1, total: n })} onOpen={() => onOpen(i)} />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction Tile({ ctx, item, label, onOpen }: { ctx: Ctx; item: GalleryItem; label: string; onOpen: () => void }) {\n const [el, setEl] = useState<HTMLButtonElement | null>(null);\n let body: ReactNode;\n if (item.kind === \"locked\") body = <Locked ctx={ctx} item={item} tile />;\n else if (item.kind === \"audio\")\n body = (\n <span className=\"absolute inset-0 flex flex-col items-center justify-center gap-1 bg-muted text-muted-foreground\">\n <HugeiconsIcon icon={item.file.failed ? AlertCircleIcon : MusicNote01Icon} className={cn(\"size-7\", item.file.failed && \"text-destructive\")} strokeWidth={1.75} />\n {item.file.dur ? <span className=\"text-[11px] font-medium tabular-nums\">{formatDuration(item.file.dur)}</span> : null}\n </span>\n );\n else if (item.kind === \"image\")\n body = item.file.url ? (\n <img src={item.file.url} alt=\"\" loading=\"lazy\" decoding=\"async\" draggable={false} className=\"absolute inset-0 size-full object-cover transition-transform duration-300 motion-safe:group-hover:scale-[1.03]\" />\n ) : item.file.failed ? (\n <span className=\"absolute inset-0 flex items-center justify-center bg-muted text-destructive\">\n <HugeiconsIcon icon={AlertCircleIcon} className=\"size-6\" />\n </span>\n ) : (\n <Processing>{\"\"}</Processing>\n );\n else {\n const f = item.file;\n const { poster, start } = videoArt(ctx, item);\n const covers = typeof poster === \"string\" ? [] : publicRenditions(poster);\n const base = baseOf(ctx, item);\n body = (\n <>\n {typeof poster === \"string\" ? (\n <img src={poster} alt=\"\" loading=\"lazy\" decoding=\"async\" className=\"absolute inset-0 size-full object-cover\" />\n ) : covers.length ? (\n <RenditionImg outputs={covers} loading=\"lazy\" className=\"absolute inset-0 size-full object-cover\" />\n ) : base ? (\n <SpriteFrame vtt={`${base}sprite.vtt`} xhrSetup={ctx.xhrSetup} fit=\"cover\" />\n ) : null}\n {base && <TilePreview ctx={ctx} base={base} start={start} target={el} />}\n {f.failed ? (\n <span className=\"absolute inset-0 flex items-center justify-center bg-muted text-destructive\">\n <HugeiconsIcon icon={AlertCircleIcon} className=\"size-6\" />\n </span>\n ) : !base ? (\n <Processing>{\"\"}</Processing>\n ) : (\n <span className=\"pointer-events-none absolute top-1/2 left-1/2 flex size-10 -translate-1/2 items-center justify-center rounded-full bg-black/55 text-white backdrop-blur-sm transition-opacity group-hover:opacity-0\">\n <HugeiconsIcon icon={PlayIcon} className=\"ml-0.5 size-5 fill-current\" strokeWidth={1.5} />\n </span>\n )}\n {f.dur ? (\n <span className=\"pointer-events-none absolute right-1.5 bottom-1.5 rounded bg-black/65 px-1.5 py-0.5 text-[11px] font-medium text-white tabular-nums\">\n {formatDuration(f.dur)}\n </span>\n ) : null}\n </>\n );\n }\n return (\n <button\n type=\"button\"\n aria-label={label}\n onClick={onOpen}\n ref={setEl}\n className=\"group relative block aspect-square w-full overflow-hidden rounded-md bg-muted outline-none focus-visible:ring-3 focus-visible:ring-ring/60\"\n data-ckui=\"tile\"\n data-kind={item.kind}\n >\n {body}\n </button>\n );\n}\n\n// A grid tile's inline preview; the tile opens the lightbox for real playback.\nfunction TilePreview({ ctx, base, start, target }: { ctx: Ctx; base: string; start: number; target: HTMLElement | null }) {\n const player = useHlsPlayer({ src: `${base}master.m3u8`, xhrSetup: ctx.xhrSetup, refresh: ctx.refresh, expires: ctx.read?.expires, abr: ctx.abr, qualityKey: null });\n useInlinePreview({ setting: ctx.inlinePreview, available: true, target, start: () => player.preview(start), stop: player.unload });\n return (\n <video\n ref={player.ref}\n muted\n playsInline\n preload=\"none\"\n aria-hidden\n tabIndex={-1}\n className={cn(\n \"pointer-events-none absolute inset-0 size-full object-cover opacity-0 transition-opacity duration-300 motion-reduce:transition-none\",\n player.previewing && player.status === \"playing\" && \"opacity-100\",\n )}\n data-ckui=\"tile-preview\"\n />\n );\n}\n\nfunction Lightbox({ ctx, index, onIndex }: { ctx: Ctx; index: number | null; onIndex: (i: number | null) => void }) {\n const { t } = useMessages();\n const scope = useScopeProps(ctx.appearance);\n const [shown, setShown] = useState(index ?? 0);\n const [prev, setPrev] = useState(index);\n if (index !== prev) {\n setPrev(index);\n if (index !== null) setShown(index);\n }\n const open = index !== null;\n const c = useCarousel({ count: ctx.items.length, index: shown, onIndexChange: setShown });\n return (\n <DialogPrimitive.Root open={open} onOpenChange={(o) => !o && onIndex(null)}>\n <DialogPrimitive.Portal {...scope} data-ckui-theme=\"dark\">\n <DialogPrimitive.Backdrop className=\"fixed inset-0 z-50 bg-black duration-150 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 motion-reduce:animate-none\" />\n <DialogPrimitive.Popup\n className=\"fixed inset-0 z-50 flex flex-col bg-black text-white outline-none duration-150 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 motion-reduce:animate-none\"\n data-ckui=\"lightbox\"\n onKeyDown={c.onKeyDown}\n >\n <DialogPrimitive.Title className=\"sr-only\">{ctx.label ?? t(\"gallery.lightbox\")}</DialogPrimitive.Title>\n <div className=\"flex h-14 shrink-0 items-center justify-between px-4\">\n <span className=\"text-sm text-white/80 tabular-nums\">{ctx.items.length > 1 ? t(\"gallery.counter\", { current: shown + 1, total: ctx.items.length }) : null}</span>\n <DialogPrimitive.Close render={<Button variant=\"ghost\" size=\"icon\" className=\"rounded-full text-white hover:bg-white/10 hover:text-white\" />}>\n <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} />\n <span className=\"sr-only\">{t(\"common.close\")}</span>\n </DialogPrimitive.Close>\n </div>\n <div className=\"min-h-0 flex-1 pb-4 sm:px-4 sm:pb-6\">\n {open && <Carousel ctx={ctx} index={shown} onIndex={setShown} lightbox />}\n </div>\n </DialogPrimitive.Popup>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6EA,SAAS,EAAS,GAAU,GAA2E;CACrG,IAAM,IAAQ,EAAI,MAAM,MAAM,MAA6B,EAAE,SAAS,OAAO,MAAM;CACnF,OAAO;EAAE,QAAQ,IAAS,EAAI,UAAU,KAAA,IAAa,KAAA;EAAW,OAAO,EAAe,IAAQ,EAAI,eAAe,KAAA,GAAW,EAAK,KAAK,GAAG;CAAE;AAC7I;AAEA,IAAM,KAAU,GAAU,MAA4B,EAAK,OAAO,EAAI,UAAU,EAAI,QAAQ,EAAK,GAAG,IAAI;AAMxG,SAAgB,EAAa,GAA0B;CACrD,IAAM,IAAQ,EAAY,CAAK,GACzB,EAAE,SAAM,UAAO,cAAW,eAAY,MAAM,GAAQ,gBAAa,iBAAc,kBAAe,GAC9F,EAAE,SAAM,EAAY,GACpB,IAAQ,QAAc,EAAa,CAAI,GAAG,CAAC,CAAI,CAAC;CACtD,EAAuB,GAAM,SAAS,EAAM,OAAO;CACnD,IAAM,CAAC,GAAM,KAAW,EAAe;EAAE,MAAM;EAAQ;EAAa;EAAc;CAAW,CAAC,GACxF,CAAC,GAAO,KAAY,EAAS,CAAC,GAC9B,CAAC,GAAU,KAAe,EAAwB,IAAI;CAC5D,IAAI,EAAM,WAAW,GAAG,OAAO;CAC/B,IAAM,IAAW;EAAE,GAAG;EAAO;CAAM,GAC7B,IAAQ,EAAM,SAAS,GACvB,IAAU,KAAK,IAAI,GAAO,EAAM,SAAS,CAAC,GAC1C,IAAQ,IAAQ,IAAO;CAC7B,OACE,kBAAC,GAAD;EAA8B;EAAY,WAAW,EAAG,yBAAyB,CAAS;EAAG,aAAU;EAAgB,aAAW;EAAO,MAAK;EAAS,cAAY,KAAS,EAAE,eAAe;EAA7L,UAAA;GACG,KACC,kBAAC,OAAD;IAAK,WAAU;IAA0C,aAAU;IAAnE,UAAA,CACE,kBAAC,QAAD;KAAM,WAAU;KAA6C,aAAU;KACpE,UAAA,MAAU,aAAa,EAAE,mBAAmB;MAAE,SAAS,IAAU;MAAG,OAAO,EAAM;KAAO,CAAC,IAAI;IAC1F,CAAA,GACN,kBAAC,GAAD;KAAa,cAAY,EAAE,cAAc;KAAG,OAAO,CAAC,CAAK;KAAG,gBAAgB,MAAM,EAAE,MAAM,EAAQ,EAAE,EAAiB;KAArH,UAAA,CACE,kBAAC,GAAD;MAAiB,OAAM;MAAW,cAAY,EAAE,kBAAkB;MAAG,OAAO,EAAE,kBAAkB;MAC9F,UAAA,kBAAC,GAAD;OAAe,MAAM;OAAwB,aAAa;MAAI,CAAA;KAC/C,CAAA,GACjB,kBAAC,GAAD;MAAiB,OAAM;MAAO,cAAY,EAAE,cAAc;MAAG,OAAO,EAAE,cAAc;MAClF,UAAA,kBAAC,GAAD;OAAe,MAAM;OAAc,aAAa;MAAI,CAAA;KACrC,CAAA,CACN;IACV,CAAA,CAAA;;GAEN,MAAU,aACT,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;IAAe;IAAK,OAAO;IAAS,SAAS;GAAW,CAAA,GACvD,EAAM,gBAAgB,EAAM,EAAS,CACtC,EAAA,CAAA,IAEF,kBAAC,GAAD;IAAW;IAAK,QAAQ;GAAc,CAAA;GAExC,kBAAC,IAAD;IAAe;IAAK,OAAO;IAAU,SAAS;GAAc,CAAA;EAC5C;;AAEtB;AAEA,IAAM,IAAmB;CAAE,MAAM;CAAI,IAAI;AAAG;AAG5C,SAAS,EAAY,GAAyC;CAC5D,IAAM,IAAS,EAA4B,EAAE,MAAM;CACnD,IAAI,EAAE,QAAQ,CAAC,GAAQ,MAAU,MAAM,2EAA2E;CAElH,IAAM,IAAI,EAAa,EAAE,QAAQ,GAAS;EAAE,QAAQ,EAAE;EAAQ,UAAU,EAAE;EAAU,MAAM,EAAE,OAAO,EAAE,OAAO;EAAM,SAAS;EAAO;CAAO,CAAC,GACpI,IAAO,EAAE,MACT,IAAU,GAAa,MAAgB,EAAQ,MAAM,QAAQ,GAAO,CAAG,GAAG,CAAC,GAAQ,CAAI,CAAC;CAE9F,OADI,CAAC,KAAQ,CAAC,IAAe,IACtB;EAAE,GAAG;EAAG,MAAM,EAAE;EAAM,SAAS,EAAE,WAAW;EAAS,UAAU,EAAE,YAAY,EAAO,MAAM;EAAU,SAAS,EAAE,WAAW,EAAE;CAAQ;AAC7I;AAEA,SAAS,EAAS,EAAE,QAAK,OAAO,GAAO,YAAS,eAA2F;CACzI,IAAM,EAAE,SAAM,EAAY,GACpB,EAAE,aAAU,GACZ,IAAI,EAAY;EAAE,OAAO,EAAM;EAAQ,OAAO;EAAO,eAAe;CAAQ,CAAC,GAC7E,EAAE,aAAU,GACZ,IAAQ,EAAM,SAAS,GACvB,IAAQ,IAAW,CAAC,IAAI;EAAE,aAAa,OAAO,EAAY,GAAO,CAAK,CAAC;EAAG,WAAW,EAAI,aAAa;CAAQ;CACpH,OACE,kBAAC,OAAD;EACE,WAAW,EAAG,wCAAwC,IAAW,cAAc,YAAY;EAC3F,MAAM,IAAQ,UAAU,KAAA;EACxB,wBAAsB,IAAQ,aAAa,KAAA;EAC3C,cAAY,IAAS,EAAI,SAAS,EAAE,eAAe,IAAK,KAAA;EACxD,UAAU,KAAS,CAAC,IAAW,IAAI,KAAA;EACnC,WAAW,KAAS,CAAC,IAAW,EAAE,YAAY,KAAA;EAC9C,aAAU;EAPZ,UAAA,CASE,kBAAC,OAAD;GACE,WAAW,EACT,2DACA,IAAW,WAAW,mGACxB;GACA,OAAO;GACP,aAAU;GACV,GAAK,IAAQ,EAAE,QAAQ,CAAC;GAP1B,UAAA,CASE,kBAAC,OAAD;IACE,WAAW,EAAG,eAAe,EAAE,WAAW,oBAAoB,0EAA0E;IACxI,OAAO,EAAE,WAAW,mBAAmB,CAAC,IAAQ,IAAI,MAAM,EAAE,OAAO,MAAM;IAExE,UAAA,EAAM,KAAK,GAAM,MAAM;KACtB,IAAM,IAAO,KAAK,IAAI,IAAI,CAAK,KAAK;KACpC,OACE,kBAAC,OAAD;MAEE,WAAU;MACV,MAAM,IAAQ,UAAU,KAAA;MACxB,wBAAsB,IAAQ,UAAU,KAAA;MACxC,cAAY,IAAQ,EAAE,iBAAiB;OAAE,SAAS,IAAI;OAAG,OAAO,EAAM;MAAO,CAAC,IAAI,KAAA;MAClF,eAAa,MAAM,KAAS,KAAA;MAC5B,OAAO,MAAM;MACb,aAAU;MACV,gBAAc,MAAM,IAAQ,KAAK,KAAA;MAEhC,UAAA,KAAQ,kBAAC,GAAD;OAAY;OAAW;OAAM,UAAU;OAAG,QAAQ,MAAM;OAAiB;MAAW,CAAA;KAC1F,GAXE,EAAK,GAWP;IAET,CAAC;GACE,CAAA,GACJ,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;IAAW,MAAK;IAAO,QAAQ,MAAU;IAAG,MAAM,MAAU;IAAG,SAAS,EAAE;IAAM,OAAO,EAAE,kBAAkB;IAAa;GAAW,CAAA,GACnI,kBAAC,GAAD;IAAW,MAAK;IAAO,QAAQ,MAAU,EAAM,SAAS;IAAG,MAAM,MAAU,EAAM,SAAS;IAAG,SAAS,EAAE;IAAM,OAAO,EAAE,cAAc;IAAa;GAAW,CAAA,CAC7J,EAAA,CAAA,CAED;EACJ,CAAA,GAAA,KAAS,CAAC,KAAY,EAAM,UAAU,MACrC,kBAAC,OAAD;GAAK,WAAU;GAA8B,aAAU;GACpD,UAAA,EAAM,KAAK,GAAM,MAChB,kBAAC,UAAD;IAEE,MAAK;IACL,WAAU;IACV,cAAY,EAAE,gBAAgB,EAAE,OAAO,IAAI,EAAE,CAAC;IAC9C,gBAAc,MAAM,KAAS,KAAA;IAC7B,eAAe,EAAE,GAAG,CAAC;IAErB,UAAA,kBAAC,QAAD,EAAM,WAAW,EAAG,2CAA2C,MAAM,IAAQ,kBAAkB,mDAAmD,EAAI,CAAA;GAChJ,GARD,EAAK,GAQJ,CACT;EACE,CAAA,CAEJ;;AAET;AAMA,SAAS,EAAU,EAAE,SAAM,WAAQ,SAAM,YAAS,UAAO,eAA+H;CAEtL,OADI,IAAe,OAEjB,kBAAC,UAAD;EACE,MAAK;EACL,cAAY;EACZ,aAAU;EACV,aAAW;EACX,UAAU,MAAM;GACd,IAAM,IAAO,EAAE,cAAc,QAAqB,sDAAsD;GAExG,AADA,EAAQ,GACJ,KAAQ,SAAS,kBAAkB,EAAE,iBAAe,GAAM,MAAM,EAAE,eAAe,GAAK,CAAC;EAC7F;EACA,WAAW,EACT,0EACA,IAAW,iBAAiB,QAC5B,MAAS,SAAU,IAAW,sCAAsC,8BAA+B,IAAW,qCAAqC,0BACrJ;EAEA,UAAA,kBAAC,QAAD;GACE,WAAW,EACT,wJACA,IAAW,+DAA+D,qEAC5E;GAEA,UAAA,kBAAC,GAAD;IAAe,MAAM,MAAS,SAAS,IAAkB;IAAkB,aAAa;IAAG,WAAU;GAAU,CAAA;EAC3G,CAAA;CACA,CAAA;AAEZ;AAEA,SAAS,EAAM,EAAE,QAAK,SAAM,aAAU,WAAQ,eAAoG;CAChJ,IAAM,EAAE,SAAM,EAAY;CAC1B,IAAI,EAAK,SAAS,UAAU,OAAO,kBAAC,GAAD;EAAa;EAAW;CAAO,CAAA;CAClE,IAAM,IAAI,EAAK;CACf,IAAI,EAAK,SAAS,SAAS,OAAO,kBAAC,GAAD;EAAkB;EAAgB;CAAW,CAAA;CAC/E,IAAI,EAAK,SAAS,SAAS;EACzB,IAAI,CAAC,EAAE,KAAK,OAAO,EAAE,SAAS,kBAAC,GAAD,EAAa,MAAM,EAAI,CAAA,IAAI,kBAAC,GAAD,EAAA,UAAa,EAAE,yBAAyB,EAAc,CAAA;EAE/G,IAAM,IAAS,EAAK,IAAI,WAAW,UAAU,KAAK,CAAC,IAAW,EAAI,MAAM,MAAM,MAA8B,EAAE,SAAS,QAAQ,IAAI,KAAA;EACnI,OACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD;GACE,KAAK,EAAE;GACP,KAAK,EAAE,iBAAiB,EAAE,OAAO,IAAW,EAAE,CAAC;GAC/C,OAAO,EAAE;GACT,QAAQ,EAAE;GACV,OAAO,IAAW,UAAW,EAAI,SAAS;GAC1C,SAAS,IAAS,UAAU;GAC5B,UAAS;GACT,WAAW;GACX,WAAU;EACX,CAAA,GACA,KAAU,kBAAC,GAAD,EAAgB,OAAM,CAAA,CACjC,EAAA,CAAA;CAEN;CACA,IAAM,EAAE,WAAQ,aAAU,EAAS,GAAK,CAAI;CAC5C,OACE,kBAAC,GAAD;EACE,eAAe,MAAmB,EAAI;EACtC,cAAc;EACd,QAAO;EACP,MAAM,EAAO,GAAK,CAAI;EACd;EACR,OAAO,EAAE;EACT,QAAQ,EAAE;EACV,UAAU,EAAE;EACJ;EACR,UAAU,EAAI;EACd,SAAS,EAAI;EACb,SAAS,EAAI,MAAM;EACnB,KAAK,EAAI;EACT,OAAO,EAAE,iBAAiB,EAAE,OAAO,IAAW,EAAE,CAAC;EACjD,WAAW,IAAW,mBAAmB,KAAA;CAC1C,CAAA;AAEL;AAIA,SAAS,EAAW,EAAE,SAAM,eAA0D;CACpF,IAAM,EAAE,SAAM,EAAY,GACpB,IAAI,EAAK,MACT,KAAQ,EAAK,OAAO,EAAE,KAAA,CAAM,QAAQ,OAAO,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAC9D,IAAQ,EAAE,iBAAiB,EAAE,OAAO,IAAW,EAAE,CAAC;CACxD,OACE,kBAAC,OAAD;EAAK,WAAU;EAAgF,aAAU;EAAzG,UAAA;GACE,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,GAAD;MAAe,MAAM;MAAiB,WAAU;MAAkB,aAAa;KAAO,CAAA;KACrF,KAAQ,kBAAC,QAAD;MAAM,WAAU;MAAY,UAAA;KAAW,CAAA;KAC/C,EAAE,MAAM,kBAAC,QAAD;MAAM,WAAU;MAAgB,UAAA,EAAe,EAAE,GAAG;KAAQ,CAAA,IAAI;IACtE;;GACJ,EAAE,SACD,kBAAC,KAAD;IAAG,WAAU;IAA2B,MAAK;IAC1C,UAAA,EAAE,qBAAqB;GACvB,CAAA,IACD,EAAE,MACJ,kBAAC,SAAD;IAAO,KAAK,EAAE;IAAK,UAAA;IAAS,SAAQ;IAAW,cAAY;IAAO,WAAU;IAAkB,qBAAkB;GAAI,CAAA,IAEpH,kBAAC,KAAD;IAAG,WAAU;IAAiC,UAAA,EAAE,yBAAyB;GAAK,CAAA;GAE/E,EAAE,YAAY,EAAE,OACf,kBAAC,KAAD;IAAG,MAAM,EAAE;IAAK,UAAU,EAAE;IAAU,WAAU;IAA8E,qBAAkB;IAAhJ,UAAA,CACE,kBAAC,GAAD;KAAe,MAAM;KAAgB,WAAU;KAAS,aAAa;IAAI,CAAA,GACxE,EAAE,kBAAkB,CACpB;;EAEF;;AAET;AAGA,SAAS,EAAY,EAAE,WAA4B;CACjD,IAAM,EAAE,GAAG,aAAU,EAAY,GAC3B,IAAI,EAAK,QACT,IAAO,GAAG,OAAO,EAAM;EAAE,MAAM,EAAE;EAAM,SAAS,EAAE;EAAS,SAAS,EAAE;EAAS,SAAS;CAAK,CAAC,IAAI,EAAE,qBAAqB;CAC/H,OACE,kBAAC,OAAD;EAAK,WAAU;EAAqH,MAAK;EAAQ,aAAU;EAA3J,UAAA,CACE,kBAAC,GAAD;GAAe,MAAM;GAAiB,WAAU;EAAU,CAAA,GAC1D,kBAAC,KAAD;GAAG,WAAU;GAAyB,UAAA;EAAQ,CAAA,CAC3C;;AAET;AAEA,SAAS,EAAW,EAAE,eAAkC;CACtD,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,QAAD;GAAM,eAAA;GAAY,WAAU;EAA4F,CAAA,GACvH,CACE;;AAET;AAEA,SAAS,EAAO,EAAE,QAAK,SAAM,WAA+D;CAC1F,IAAM,EAAE,SAAM,EAAY;CAC1B,OACE,kBAAC,OAAD;EAAK,WAAU;EAA4C,aAAU;EAArE,UAAA,CACG,EAAK,YAAY,kBAAC,OAAD;GAAK,KAAK,EAAK;GAAU,KAAI;GAAG,WAAW;GAAO,WAAU;EAA6D,CAAA,GAC3I,kBAAC,OAAD;GAAK,WAAW,EAAG,oFAAoF,EAAK,WAAW,2BAA2B,iBAAiB;GAAnK,UAAA,CACE,kBAAC,GAAD;IAAe,MAAM;IAAkB,WAAW,IAAO,WAAW;IAAU,aAAa;GAAO,CAAA,GACjG,CAAC,KACA,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,KAAD;IAAG,WAAU;IAAoC,UAAA,EAAE,gBAAgB;GAAK,CAAA,GACvE,EAAI,gBAAgB,kBAAC,OAAD;IAAK,qBAAkB;IAAI,UAAA,EAAI,aAAa;GAAO,CAAA,CACxE,EAAA,CAAA,CAED;EACF,CAAA,CAAA;;AAET;AAEA,SAAS,EAAU,EAAE,UAAqB;CACxC,IAAM,EAAE,MAAM,EAAY;CAC1B,OACE,kBAAC,OAAD;EACE,WAAU;EACV,aAAU;EAFZ,UAAA,CAIE,kBAAC,QAAD;GAAM,WAAU;GAAhB,UAAA,CACE,kBAAC,GAAD;IAAe,MAAM;IAAkB,WAAU;IAAkB,aAAa;GAAO,CAAA,GACtF,EAAE,gBAAgB,CACf;EACL,CAAA,GAAA,EAAI,gBAAgB,kBAAC,OAAD;GAAK,qBAAkB;GAAI,UAAA,EAAI,aAAa;EAAO,CAAA,CACrE;;AAET;AAEA,SAAS,EAAK,EAAE,QAAK,aAAqD;CACxE,IAAM,EAAE,SAAM,EAAY,GACpB,IAAI,EAAI,MAAM;CACpB,OACE,kBAAC,MAAD;EAAI,WAAW,EAAG,cAAc,MAAM,KAAK,MAAM,IAAI,gBAAgB,6BAA6B;EAAG,aAAU;EAC5G,UAAA,EAAI,MAAM,KAAK,GAAM,MACpB,kBAAC,MAAD,EAAA,UACE,kBAAC,IAAD;GAAW;GAAW;GAAM,OAAO,EAAE,gBAAgB;IAAE,OAAO,IAAI;IAAG,OAAO;GAAE,CAAC;GAAG,cAAc,EAAO,CAAC;EAAI,CAAA,EAC1G,GAFK,EAAK,GAEV,CACL;CACC,CAAA;AAER;AAEA,SAAS,GAAK,EAAE,QAAK,SAAM,UAAO,aAA8E;CAC9G,IAAM,CAAC,GAAI,KAAS,EAAmC,IAAI,GACvD;CACJ,IAAI,EAAK,SAAS,UAAU,IAAO,kBAAC,GAAD;EAAa;EAAW;EAAM,MAAA;CAAM,CAAA;MAClE,IAAI,EAAK,SAAS,SACrB,IACE,kBAAC,QAAD;EAAM,WAAU;EAAhB,UAAA,CACE,kBAAC,GAAD;GAAe,MAAM,EAAK,KAAK,SAAS,IAAkB;GAAiB,WAAW,EAAG,UAAU,EAAK,KAAK,UAAU,kBAAkB;GAAG,aAAa;EAAO,CAAA,GAC/J,EAAK,KAAK,MAAM,kBAAC,QAAD;GAAM,WAAU;GAAwC,UAAA,EAAe,EAAK,KAAK,GAAG;EAAQ,CAAA,IAAI,IAC7G;;MAEL,IAAI,EAAK,SAAS,SACrB,IAAO,EAAK,KAAK,MACf,kBAAC,OAAD;EAAK,KAAK,EAAK,KAAK;EAAK,KAAI;EAAG,SAAQ;EAAO,UAAS;EAAQ,WAAW;EAAO,WAAU;CAAkH,CAAA,IAC5M,EAAK,KAAK,SACZ,kBAAC,QAAD;EAAM,WAAU;EACd,UAAA,kBAAC,GAAD;GAAe,MAAM;GAAiB,WAAU;EAAU,CAAA;CACtD,CAAA,IAEN,kBAAC,GAAD,EAAA,UAAa,GAAe,CAAA;MAE3B;EACH,IAAM,IAAI,EAAK,MACT,EAAE,WAAQ,aAAU,EAAS,GAAK,CAAI,GACtC,IAAS,OAAO,KAAW,WAAW,CAAC,IAAI,EAAiB,CAAM,GAClE,IAAO,EAAO,GAAK,CAAI;EAC7B,IACE,kBAAA,GAAA,EAAA,UAAA;GACG,OAAO,KAAW,WACjB,kBAAC,OAAD;IAAK,KAAK;IAAQ,KAAI;IAAG,SAAQ;IAAO,UAAS;IAAQ,WAAU;GAA2C,CAAA,IAC5G,EAAO,SACT,kBAAC,GAAD;IAAc,SAAS;IAAQ,SAAQ;IAAO,WAAU;GAA2C,CAAA,IACjG,IACF,kBAAC,GAAD;IAAa,KAAK,GAAG,EAAK;IAAa,UAAU,EAAI;IAAU,KAAI;GAAS,CAAA,IAC1E;GACH,KAAQ,kBAAC,IAAD;IAAkB;IAAW;IAAa;IAAO,QAAQ;GAAK,CAAA;GACtE,EAAE,SACD,kBAAC,QAAD;IAAM,WAAU;IACd,UAAA,kBAAC,GAAD;KAAe,MAAM;KAAiB,WAAU;IAAU,CAAA;GACtD,CAAA,IACH,IAGH,kBAAC,QAAD;IAAM,WAAU;IACd,UAAA,kBAAC,GAAD;KAAe,MAAM;KAAU,WAAU;KAA6B,aAAa;IAAM,CAAA;GACrF,CAAA,IAJN,kBAAC,GAAD,EAAA,UAAa,GAAe,CAAA;GAM7B,EAAE,MACD,kBAAC,QAAD;IAAM,WAAU;IACb,UAAA,EAAe,EAAE,GAAG;GACjB,CAAA,IACJ;EACJ,EAAA,CAAA;CAEN;CACA,OACE,kBAAC,UAAD;EACE,MAAK;EACL,cAAY;EACZ,SAAS;EACT,KAAK;EACL,WAAU;EACV,aAAU;EACV,aAAW,EAAK;EAEf,UAAA;CACK,CAAA;AAEZ;AAGA,SAAS,GAAY,EAAE,QAAK,SAAM,UAAO,aAAiF;CACxH,IAAM,IAAS,EAAa;EAAE,KAAK,GAAG,EAAK;EAAc,UAAU,EAAI;EAAU,SAAS,EAAI;EAAS,SAAS,EAAI,MAAM;EAAS,KAAK,EAAI;EAAK,YAAY;CAAK,CAAC;CAEnK,OADA,EAAiB;EAAE,SAAS,EAAI;EAAe,WAAW;EAAM;EAAQ,aAAa,EAAO,QAAQ,CAAK;EAAG,MAAM,EAAO;CAAO,CAAC,GAE/H,kBAAC,SAAD;EACE,KAAK,EAAO;EACZ,OAAA;EACA,aAAA;EACA,SAAQ;EACR,eAAA;EACA,UAAU;EACV,WAAW,EACT,uIACA,EAAO,cAAc,EAAO,WAAW,aAAa,aACtD;EACA,aAAU;CACX,CAAA;AAEL;AAEA,SAAS,GAAS,EAAE,QAAK,UAAO,cAAoF;CAClH,IAAM,EAAE,SAAM,EAAY,GACpB,IAAQ,EAAc,EAAI,UAAU,GACpC,CAAC,GAAO,KAAY,EAAS,KAAS,CAAC,GACvC,CAAC,GAAM,KAAW,EAAS,CAAK;CACtC,AAAI,MAAU,MACZ,EAAQ,CAAK,GACT,MAAU,QAAM,EAAS,CAAK;CAEpC,IAAM,IAAO,MAAU,MACjB,IAAI,EAAY;EAAE,OAAO,EAAI,MAAM;EAAQ,OAAO;EAAO,eAAe;CAAS,CAAC;CACxF,OACE,kBAAC,EAAgB,MAAjB;EAA4B;EAAM,eAAe,MAAM,CAAC,KAAK,EAAQ,IAAI;EACvE,UAAA,kBAAC,EAAgB,QAAjB;GAAwB,GAAI;GAAO,mBAAgB;GAAnD,UAAA,CACE,kBAAC,EAAgB,UAAjB,EAA0B,WAAU,8JAA+J,CAAA,GACnM,kBAAC,EAAgB,OAAjB;IACE,WAAU;IACV,aAAU;IACV,WAAW,EAAE;IAHf,UAAA;KAKE,kBAAC,EAAgB,OAAjB;MAAuB,WAAU;MAAW,UAAA,EAAI,SAAS,EAAE,kBAAkB;KAAyB,CAAA;KACtG,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,kBAAC,QAAD;OAAM,WAAU;OAAsC,UAAA,EAAI,MAAM,SAAS,IAAI,EAAE,mBAAmB;QAAE,SAAS,IAAQ;QAAG,OAAO,EAAI,MAAM;OAAO,CAAC,IAAI;MAAW,CAAA,GAChK,kBAAC,EAAgB,OAAjB;OAAuB,QAAQ,kBAAC,GAAD;QAAQ,SAAQ;QAAQ,MAAK;QAAO,WAAU;OAA8D,CAAA;OAA3I,UAAA,CACE,kBAAC,GAAD;QAAe,MAAM;QAAc,aAAa;OAAI,CAAA,GACpD,kBAAC,QAAD;QAAM,WAAU;QAAW,UAAA,EAAE,cAAc;OAAQ,CAAA,CAC9B;MACpB,CAAA,CAAA;;KACL,kBAAC,OAAD;MAAK,WAAU;MACZ,UAAA,KAAQ,kBAAC,GAAD;OAAe;OAAK,OAAO;OAAO,SAAS;OAAU,UAAA;MAAU,CAAA;KACrE,CAAA;IACgB;GACD,CAAA,CAAA;;CACJ,CAAA;AAE1B"}
|
|
1
|
+
{"version":3,"file":"media-gallery.js","names":[],"sources":["../../src/components/media-gallery.tsx"],"sourcesContent":["import { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\nimport {\n AlertCircleIcon,\n ArrowLeft01Icon,\n ArrowRight01Icon,\n Cancel01Icon,\n CarouselHorizontalIcon,\n Download01Icon,\n GridViewIcon,\n MusicNote01Icon,\n PlayIcon,\n SquareLock02Icon,\n} from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useMemo, useState, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { formatDuration, galleryItems, stageAspect, type GalleryItem, type GalleryLockedItem, type GalleryMediaItem } from \"../client/gallery.js\";\nimport { useCarousel, useGalleryView, useHlsPlayer, useRefreshBeforeExpiry, type GalleryViewOptions, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useInlinePreview } from \"../react/inline-preview.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { publicRenditions, type PublicPreset } from \"../client/public.js\";\nimport { ContentKitUiRoot, useScopeProps } from \"../scope.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { FileInfo, ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport { useOptionalContentKitClient } from \"../react/context.js\";\nimport { useMediaRead } from \"../react/read.js\";\nimport { previewStartAt, SpriteFrame, VideoPlayer } from \"./video-player.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { ToggleGroup, ToggleGroupItem } from \"#ckui/ui/toggle-group\";\n\nexport interface MediaGalleryProps extends GalleryViewOptions, Pick<HlsPlayerOptions, \"loader\" | \"refresh\" | \"abr\"> {\n /**\n * The item to show, read through the client: its read (refreshed before\n * the URLs expire, after commits), HLS bases, playlist auth and grant\n * refresh. Or pass `read` (and hlsBase, loader, refresh) yourself.\n */\n item?: RefBody;\n /** With item: only files under this path prefix. */\n prefix?: string;\n /** With item: each audio file's download name signed into its URL. */\n download?: boolean;\n /**\n * The read API result: files in manifest order with this viewer's access\n * (scope it with a prefix). A download read adds each audio file's download.\n * With `refresh`, the gallery reads again shortly before `read.expires`.\n */\n read?: ReadResult | null;\n /** A ladder's HLS folder from the read's `hls` dir: client.media.hlsBase(ref, dir). */\n hlsBase?: (dir: string) => string;\n /** The item's poster (its public preset, or a URL), drawn on the first video. */\n poster?: PublicPreset | string | null;\n /** Where the first video's preview starts, seconds (e.g. the poster's frame); default 10% in. */\n previewStart?: number;\n /** Playable videos preview muted inline (hover, or in view on touch); default the provider's. */\n inlinePreview?: boolean;\n /** The host's unlock call to action, drawn over the locked item. */\n renderLocked?: () => ReactNode;\n /** Below the carousel, for its current item (e.g. downloads). */\n renderDetails?: (item: GalleryItem) => ReactNode;\n /** `sizes` for carousel images. Default \"(min-width: 768px) 720px, 100vw\". */\n sizes?: string;\n /** Tallest the carousel gets; taller media letterboxes. Default \"80svh\": every slide at its native aspect, full width, never taller than the screen; \"none\" lifts it. */\n maxHeight?: string;\n label?: string;\n className?: string;\n appearance?: ContentKitUiAppearance;\n /** Overrides the provider's client (item). */\n client?: ContentKitClient;\n}\n\ninterface Ctx extends MediaGalleryProps {\n items: GalleryItem[];\n}\n\n// The item's poster and preview start belong to its first video.\nfunction videoArt(ctx: Ctx, item: GalleryMediaItem): { poster?: PublicPreset | string; start: number } {\n const first = ctx.items.find((i): i is GalleryMediaItem => i.kind === \"video\") === item;\n return { poster: first ? (ctx.poster ?? undefined) : undefined, start: previewStartAt(first ? ctx.previewStart : undefined, item.file.dur) };\n}\n\nconst baseOf = (ctx: Ctx, item: GalleryMediaItem) => (item.dir && ctx.hlsBase ? ctx.hlsBase(item.dir) : null);\n\n/**\n * A post's images and videos as a swipeable carousel or a tile grid that opens\n * a lightbox, with a view toggle. One item renders alone.\n */\nexport function MediaGallery(given: MediaGalleryProps) {\n const props = useItemRead(given);\n const { read, label, className, appearance, view: chosen, defaultView, onViewChange, storageKey } = props;\n const { t } = useMessages();\n const items = useMemo(() => galleryItems(read), [read]);\n useRefreshBeforeExpiry(read?.expires, props.refresh);\n const [view, setView] = useGalleryView({ view: chosen, defaultView, onViewChange, storageKey });\n const [index, setIndex] = useState(0);\n const [lightbox, setLightbox] = useState<number | null>(null);\n if (items.length === 0) return null;\n const ctx: Ctx = { ...props, items };\n const multi = items.length > 1;\n const current = Math.min(index, items.length - 1);\n const shown = multi ? view : \"carousel\";\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"@container grid gap-2\", className)} data-ckui=\"media-gallery\" data-view={shown} role=\"region\" aria-label={label ?? t(\"gallery.label\")}>\n {multi && (\n <div className=\"flex items-center justify-between gap-2\" data-ckui=\"gallery-header\">\n <span className=\"text-sm text-muted-foreground tabular-nums\" data-ckui=\"gallery-count\">\n {shown === \"carousel\" ? t(\"gallery.counter\", { current: current + 1, total: items.length }) : null}\n </span>\n <ToggleGroup aria-label={t(\"gallery.view\")} value={[shown]} onValueChange={(v) => v[0] && setView(v[0] as typeof view)}>\n <ToggleGroupItem value=\"carousel\" aria-label={t(\"gallery.carousel\")} title={t(\"gallery.carousel\")}>\n <HugeiconsIcon icon={CarouselHorizontalIcon} strokeWidth={2} />\n </ToggleGroupItem>\n <ToggleGroupItem value=\"grid\" aria-label={t(\"gallery.grid\")} title={t(\"gallery.grid\")}>\n <HugeiconsIcon icon={GridViewIcon} strokeWidth={2} />\n </ToggleGroupItem>\n </ToggleGroup>\n </div>\n )}\n {shown === \"carousel\" ? (\n <>\n <Carousel ctx={ctx} index={current} onIndex={setIndex} />\n {props.renderDetails?.(items[current]!)}\n </>\n ) : (\n <Grid ctx={ctx} onOpen={setLightbox} />\n )}\n <Lightbox ctx={ctx} index={lightbox} onIndex={setLightbox} />\n </ContentKitUiRoot>\n );\n}\n\nconst NO_ITEM: RefBody = { kind: \"\", id: \"\" };\n\n// With item, the read, HLS bases, playlist auth and refresh come from the client.\nfunction useItemRead(p: MediaGalleryProps): MediaGalleryProps {\n const client = useOptionalContentKitClient(p.client);\n if (p.item && !client) throw new Error(\"contentkit-ui: MediaGallery item needs `client` or a <ContentKitProvider>\");\n // One refresh timer for the gallery and its players, keyed by r.refresh.\n const r = useMediaRead(p.item ?? NO_ITEM, { prefix: p.prefix, download: p.download, read: p.item ? p.read : null, refresh: false, client });\n const item = p.item;\n const hlsBase = useCallback((dir: string) => client!.media.hlsBase(item!, dir), [client, item]);\n if (!item || !client) return p;\n return { ...p, read: r.read, hlsBase: p.hlsBase ?? hlsBase, loader: p.loader ?? client.media.hls, refresh: p.refresh ?? r.refresh };\n}\n\nfunction Carousel({ ctx, index: given, onIndex, lightbox }: { ctx: Ctx; index: number; onIndex: (i: number) => void; lightbox?: boolean }) {\n const { t } = useMessages();\n const { items } = ctx;\n const c = useCarousel({ count: items.length, index: given, onIndexChange: onIndex });\n const { index } = c;\n const multi = items.length > 1;\n const stage = lightbox ? {} : { aspectRatio: String(stageAspect(items, index)), maxHeight: ctx.maxHeight ?? \"80svh\" };\n return (\n <div\n className={cn(\"group/carousel relative outline-none\", lightbox ? \"size-full\" : \"grid gap-2\")}\n role={multi ? \"group\" : undefined}\n aria-roledescription={multi ? \"carousel\" : undefined}\n aria-label={multi ? (ctx.label ?? t(\"gallery.label\")) : undefined}\n tabIndex={multi && !lightbox ? 0 : undefined}\n onKeyDown={multi && !lightbox ? c.onKeyDown : undefined}\n data-ckui=\"carousel\"\n >\n <div\n className={cn(\n \"relative w-full touch-pan-y overflow-hidden select-none\",\n lightbox ? \"h-full\" : \"rounded-lg bg-muted transition-[aspect-ratio] duration-300 ease-out motion-reduce:transition-none\",\n )}\n style={stage}\n data-ckui=\"stage\"\n {...(multi ? c.swipe : {})}\n >\n <div\n className={cn(\"flex h-full\", c.dragging ? \"transition-none\" : \"transition-transform duration-300 ease-out motion-reduce:transition-none\")}\n style={{ transform: `translateX(calc(${-index * 100}% + ${c.offset}px))` }}\n >\n {items.map((item, i) => {\n const near = Math.abs(i - index) <= 1;\n return (\n <div\n key={item.key}\n className=\"relative h-full w-full shrink-0 overflow-hidden\"\n role={multi ? \"group\" : undefined}\n aria-roledescription={multi ? \"slide\" : undefined}\n aria-label={multi ? t(\"gallery.slide\", { current: i + 1, total: items.length }) : undefined}\n aria-hidden={i !== index || undefined}\n inert={i !== index}\n data-ckui=\"slide\"\n data-current={i === index ? \"\" : undefined}\n >\n {near && <Slide ctx={ctx} item={item} position={i} active={i === index} lightbox={lightbox} />}\n </div>\n );\n })}\n </div>\n {multi && (\n <>\n <NavButton side=\"prev\" hidden={index === 0} last={index === 1} onClick={c.prev} label={t(\"gallery.previous\")} lightbox={lightbox} />\n <NavButton side=\"next\" hidden={index === items.length - 1} last={index === items.length - 2} onClick={c.next} label={t(\"gallery.next\")} lightbox={lightbox} />\n </>\n )}\n </div>\n {multi && !lightbox && items.length <= 12 && (\n <div className=\"flex justify-center gap-0.5\" data-ckui=\"dots\">\n {items.map((item, i) => (\n <button\n key={item.key}\n type=\"button\"\n className=\"group/dot flex size-4 items-center justify-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n aria-label={t(\"gallery.goTo\", { index: i + 1 })}\n aria-current={i === index || undefined}\n onClick={() => c.go(i)}\n >\n <span className={cn(\"size-1.5 rounded-full transition-colors\", i === index ? \"bg-foreground\" : \"bg-foreground/25 group-hover/dot:bg-foreground/50\")} />\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// A strip down the stage's side, clear of a video's control bar: the stage's\n// height follows each slide's aspect, so a centred-only target would move\n// between quick clicks. A swipe may start on it. The click that reaches an end\n// removes the button, so focus moves to the carousel and the arrow keys keep working.\nfunction NavButton({ side, hidden, last, onClick, label, lightbox }: { side: \"prev\" | \"next\"; hidden: boolean; last: boolean; onClick: () => void; label: string; lightbox?: boolean }) {\n if (hidden) return null;\n return (\n <button\n type=\"button\"\n aria-label={label}\n data-ckui=\"gallery-nav\"\n data-side={side}\n onClick={(e) => {\n const root = e.currentTarget.closest<HTMLElement>(\"[data-ckui=lightbox], [data-ckui=carousel][tabindex]\");\n onClick();\n if (last && document.activeElement === e.currentTarget) root?.focus({ preventScroll: true });\n }}\n className={cn(\n \"group/nav absolute top-0 bottom-14 z-10 flex items-center outline-none\",\n lightbox ? \"w-16 sm:w-20\" : \"w-12\",\n side === \"prev\" ? (lightbox ? \"left-0 justify-start pl-3 sm:pl-5\" : \"left-0 justify-start pl-2\") : lightbox ? \"right-0 justify-end pr-3 sm:pr-5\" : \"right-0 justify-end pr-2\",\n )}\n >\n <span\n className={cn(\n \"flex items-center justify-center rounded-full backdrop-blur-sm transition-colors group-focus-visible/nav:ring-3 group-focus-visible/nav:ring-ring/60\",\n lightbox ? \"size-10 bg-white/10 text-white group-hover/nav:bg-white/20\" : \"size-8 bg-white/85 text-zinc-900 shadow-md group-hover/nav:bg-white\",\n )}\n >\n <HugeiconsIcon icon={side === \"prev\" ? ArrowLeft01Icon : ArrowRight01Icon} strokeWidth={2} className=\"size-4\" />\n </span>\n </button>\n );\n}\n\nfunction Slide({ ctx, item, position, active, lightbox }: { ctx: Ctx; item: GalleryItem; position: number; active: boolean; lightbox?: boolean }) {\n const { t } = useMessages();\n if (item.kind === \"locked\") return <Locked ctx={ctx} item={item} />;\n const f = item.file;\n if (item.kind === \"audio\") return <AudioSlide item={item} position={position} />;\n if (item.kind === \"image\") {\n if (!f.url) return f.failed ? <ImageFailed file={f} /> : <Processing>{t(\"gallery.processingImage\")}</Processing>;\n // A public teaser of a locked item carries the unlock, so no one swipes past it to find it.\n const locked = item.key.startsWith(\"preview/\") && !lightbox ? ctx.items.find((i): i is GalleryLockedItem => i.kind === \"locked\") : undefined;\n return (\n <>\n <img\n src={f.url}\n alt={t(\"gallery.image\", { index: position + 1 })}\n width={f.w}\n height={f.h}\n sizes={lightbox ? \"100vw\" : (ctx.sizes ?? \"(min-width: 768px) 720px, 100vw\")}\n loading={active ? \"eager\" : \"lazy\"}\n decoding=\"async\"\n draggable={false}\n className=\"absolute inset-0 size-full object-contain\"\n />\n {locked && <LockedBar ctx={ctx} />}\n </>\n );\n }\n const { poster, start } = videoArt(ctx, item);\n return (\n <VideoPlayer\n inlinePreview={lightbox ? false : ctx.inlinePreview}\n previewStart={start}\n layout=\"fill\"\n base={baseOf(ctx, item)}\n poster={poster}\n width={f.w}\n height={f.h}\n duration={f.dur}\n active={active}\n loader={ctx.loader}\n refresh={ctx.refresh}\n expires={ctx.read?.expires}\n abr={ctx.abr}\n label={t(\"gallery.video\", { index: position + 1 })}\n className={lightbox ? \"bg-transparent\" : undefined}\n />\n );\n}\n\n// An audio file (a ladder's M4A): a native player, its duration and, in a\n// download read, its download.\nfunction AudioSlide({ item, position }: { item: GalleryMediaItem; position: number }) {\n const { t } = useMessages();\n const f = item.file;\n const name = (item.dir ?? f.path).replace(/\\/$/, \"\").split(\"/\").pop();\n const label = t(\"gallery.audio\", { index: position + 1 });\n return (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-3 bg-muted p-4\" data-ckui=\"audio\">\n <div className=\"flex min-w-0 items-center gap-2 text-sm text-muted-foreground\">\n <HugeiconsIcon icon={MusicNote01Icon} className=\"size-5 shrink-0\" strokeWidth={1.75} />\n {name && <span className=\"truncate\">{name}</span>}\n {f.dur ? <span className=\"tabular-nums\">{formatDuration(f.dur)}</span> : null}\n </div>\n {f.failed ? (\n <p className=\"text-sm text-destructive\" role=\"alert\">\n {t(\"gallery.failedAudio\")}\n </p>\n ) : f.url ? (\n <audio src={f.url} controls preload=\"metadata\" aria-label={label} className=\"w-full max-w-md\" data-ckui-noswipe=\"\" />\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t(\"gallery.processingAudio\")}</p>\n )}\n {f.download && f.url && (\n <a href={f.url} download={f.download} className=\"inline-flex items-center gap-1.5 text-sm underline-offset-4 hover:underline\" data-ckui-noswipe=\"\">\n <HugeiconsIcon icon={Download01Icon} className=\"size-4\" strokeWidth={2} />\n {t(\"gallery.download\")}\n </a>\n )}\n </div>\n );\n}\n\n// Why an image cannot be shown (editors only: the read API's failed fields).\nfunction ImageFailed({ file }: { file: FileInfo }) {\n const { t, error } = useMessages();\n const f = file.failed;\n const text = f?.code ? error({ code: f.code, message: f.message, details: f.details, refusal: true }) : t(\"gallery.failedImage\");\n return (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center gap-2 bg-muted p-4 text-center text-sm text-destructive\" role=\"alert\" data-ckui=\"image-failed\">\n <HugeiconsIcon icon={AlertCircleIcon} className=\"size-6\" />\n <p className=\"max-w-sm text-balance\">{text}</p>\n </div>\n );\n}\n\nfunction Processing({ children }: { children: string }) {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center gap-2 bg-muted text-sm text-muted-foreground\">\n <span aria-hidden className=\"size-4 rounded-full border-2 border-current/30 border-t-current motion-safe:animate-spin\" />\n {children}\n </div>\n );\n}\n\nfunction Locked({ ctx, item, tile }: { ctx: Ctx; item: GalleryLockedItem; tile?: boolean }) {\n const { t } = useMessages();\n return (\n <div className=\"absolute inset-0 overflow-hidden bg-muted\" data-ckui=\"locked\">\n {item.backdrop && <img src={item.backdrop} alt=\"\" draggable={false} className=\"absolute inset-0 size-full scale-110 object-cover blur-xl\" />}\n <div className={cn(\"absolute inset-0 flex flex-col items-center justify-center gap-3 p-4 text-center\", item.backdrop ? \"bg-black/45 text-white\" : \"text-foreground\")}>\n <HugeiconsIcon icon={SquareLock02Icon} className={tile ? \"size-6\" : \"size-8\"} strokeWidth={1.75} />\n {!tile && (\n <>\n <p className=\"text-sm font-medium text-balance\">{t(\"gallery.locked\")}</p>\n {ctx.renderLocked && <div data-ckui-noswipe=\"\">{ctx.renderLocked()}</div>}\n </>\n )}\n </div>\n </div>\n );\n}\n\nfunction LockedBar({ ctx }: { ctx: Ctx }) {\n const { t } = useMessages();\n return (\n <div\n className=\"absolute inset-x-0 bottom-0 flex flex-wrap items-center justify-between gap-x-3 gap-y-2 bg-linear-to-t from-black/75 via-black/45 to-transparent px-4 pt-10 pb-4 text-white\"\n data-ckui=\"locked-bar\"\n >\n <span className=\"flex items-center gap-2 text-sm font-medium\">\n <HugeiconsIcon icon={SquareLock02Icon} className=\"size-5 shrink-0\" strokeWidth={1.75} />\n {t(\"gallery.locked\")}\n </span>\n {ctx.renderLocked && <div data-ckui-noswipe=\"\">{ctx.renderLocked()}</div>}\n </div>\n );\n}\n\nfunction Grid({ ctx, onOpen }: { ctx: Ctx; onOpen: (i: number) => void }) {\n const { t } = useMessages();\n const n = ctx.items.length;\n return (\n <ul className={cn(\"grid gap-1\", n === 2 || n === 4 ? \"grid-cols-2\" : \"grid-cols-2 @md:grid-cols-3\")} data-ckui=\"grid\">\n {ctx.items.map((item, i) => (\n <li key={item.key}>\n <Tile ctx={ctx} item={item} label={t(\"gallery.open\", { index: i + 1, total: n })} onOpen={() => onOpen(i)} />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction Tile({ ctx, item, label, onOpen }: { ctx: Ctx; item: GalleryItem; label: string; onOpen: () => void }) {\n const [el, setEl] = useState<HTMLButtonElement | null>(null);\n let body: ReactNode;\n if (item.kind === \"locked\") body = <Locked ctx={ctx} item={item} tile />;\n else if (item.kind === \"audio\")\n body = (\n <span className=\"absolute inset-0 flex flex-col items-center justify-center gap-1 bg-muted text-muted-foreground\">\n <HugeiconsIcon icon={item.file.failed ? AlertCircleIcon : MusicNote01Icon} className={cn(\"size-7\", item.file.failed && \"text-destructive\")} strokeWidth={1.75} />\n {item.file.dur ? <span className=\"text-[11px] font-medium tabular-nums\">{formatDuration(item.file.dur)}</span> : null}\n </span>\n );\n else if (item.kind === \"image\")\n body = item.file.url ? (\n <img src={item.file.url} alt=\"\" loading=\"lazy\" decoding=\"async\" draggable={false} className=\"absolute inset-0 size-full object-cover transition-transform duration-300 motion-safe:group-hover:scale-[1.03]\" />\n ) : item.file.failed ? (\n <span className=\"absolute inset-0 flex items-center justify-center bg-muted text-destructive\">\n <HugeiconsIcon icon={AlertCircleIcon} className=\"size-6\" />\n </span>\n ) : (\n <Processing>{\"\"}</Processing>\n );\n else {\n const f = item.file;\n const { poster, start } = videoArt(ctx, item);\n const covers = typeof poster === \"string\" ? [] : publicRenditions(poster);\n const base = baseOf(ctx, item);\n body = (\n <>\n {typeof poster === \"string\" ? (\n <img src={poster} alt=\"\" loading=\"lazy\" decoding=\"async\" className=\"absolute inset-0 size-full object-cover\" />\n ) : covers.length ? (\n <RenditionImg outputs={covers} loading=\"lazy\" className=\"absolute inset-0 size-full object-cover\" />\n ) : base ? (\n <SpriteFrame vtt={`${base}sprite.vtt`} loader={ctx.loader} fit=\"cover\" />\n ) : null}\n {base && <TilePreview ctx={ctx} base={base} start={start} target={el} />}\n {f.failed ? (\n <span className=\"absolute inset-0 flex items-center justify-center bg-muted text-destructive\">\n <HugeiconsIcon icon={AlertCircleIcon} className=\"size-6\" />\n </span>\n ) : !base ? (\n <Processing>{\"\"}</Processing>\n ) : (\n <span className=\"pointer-events-none absolute top-1/2 left-1/2 flex size-10 -translate-1/2 items-center justify-center rounded-full bg-black/55 text-white backdrop-blur-sm transition-opacity group-hover:opacity-0\">\n <HugeiconsIcon icon={PlayIcon} className=\"ml-0.5 size-5 fill-current\" strokeWidth={1.5} />\n </span>\n )}\n {f.dur ? (\n <span className=\"pointer-events-none absolute right-1.5 bottom-1.5 rounded bg-black/65 px-1.5 py-0.5 text-[11px] font-medium text-white tabular-nums\">\n {formatDuration(f.dur)}\n </span>\n ) : null}\n </>\n );\n }\n return (\n <button\n type=\"button\"\n aria-label={label}\n onClick={onOpen}\n ref={setEl}\n className=\"group relative block aspect-square w-full overflow-hidden rounded-md bg-muted outline-none focus-visible:ring-3 focus-visible:ring-ring/60\"\n data-ckui=\"tile\"\n data-kind={item.kind}\n >\n {body}\n </button>\n );\n}\n\n// A grid tile's inline preview; the tile opens the lightbox for real playback.\nfunction TilePreview({ ctx, base, start, target }: { ctx: Ctx; base: string; start: number; target: HTMLElement | null }) {\n const player = useHlsPlayer({ src: `${base}master.m3u8`, loader: ctx.loader, refresh: ctx.refresh, expires: ctx.read?.expires, abr: ctx.abr, qualityKey: null });\n useInlinePreview({ setting: ctx.inlinePreview, available: true, target, start: () => player.preview(start), stop: player.unload });\n return (\n <video\n ref={player.ref}\n muted\n playsInline\n preload=\"none\"\n aria-hidden\n tabIndex={-1}\n className={cn(\n \"pointer-events-none absolute inset-0 size-full object-cover opacity-0 transition-opacity duration-300 motion-reduce:transition-none\",\n player.previewing && player.status === \"playing\" && \"opacity-100\",\n )}\n data-ckui=\"tile-preview\"\n />\n );\n}\n\nfunction Lightbox({ ctx, index, onIndex }: { ctx: Ctx; index: number | null; onIndex: (i: number | null) => void }) {\n const { t } = useMessages();\n const scope = useScopeProps(ctx.appearance);\n const [shown, setShown] = useState(index ?? 0);\n const [prev, setPrev] = useState(index);\n if (index !== prev) {\n setPrev(index);\n if (index !== null) setShown(index);\n }\n const open = index !== null;\n const c = useCarousel({ count: ctx.items.length, index: shown, onIndexChange: setShown });\n return (\n <DialogPrimitive.Root open={open} onOpenChange={(o) => !o && onIndex(null)}>\n <DialogPrimitive.Portal {...scope} data-ckui-theme=\"dark\">\n <DialogPrimitive.Backdrop className=\"fixed inset-0 z-50 bg-black duration-150 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 motion-reduce:animate-none\" />\n <DialogPrimitive.Popup\n className=\"fixed inset-0 z-50 flex flex-col bg-black text-white outline-none duration-150 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 motion-reduce:animate-none\"\n data-ckui=\"lightbox\"\n onKeyDown={c.onKeyDown}\n >\n <DialogPrimitive.Title className=\"sr-only\">{ctx.label ?? t(\"gallery.lightbox\")}</DialogPrimitive.Title>\n <div className=\"flex h-14 shrink-0 items-center justify-between px-4\">\n <span className=\"text-sm text-white/80 tabular-nums\">{ctx.items.length > 1 ? t(\"gallery.counter\", { current: shown + 1, total: ctx.items.length }) : null}</span>\n <DialogPrimitive.Close render={<Button variant=\"ghost\" size=\"icon\" className=\"rounded-full text-white hover:bg-white/10 hover:text-white\" />}>\n <HugeiconsIcon icon={Cancel01Icon} strokeWidth={2} />\n <span className=\"sr-only\">{t(\"common.close\")}</span>\n </DialogPrimitive.Close>\n </div>\n <div className=\"min-h-0 flex-1 pb-4 sm:px-4 sm:pb-6\">\n {open && <Carousel ctx={ctx} index={shown} onIndex={setShown} lightbox />}\n </div>\n </DialogPrimitive.Popup>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n );\n}\n"],"mappings":"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"}
|
|
@@ -121,7 +121,7 @@ function v({ handoff: v, onExpand: _e, onClose: y, onProgress: b, progressInterv
|
|
|
121
121
|
className: "pointer-events-none absolute inset-0 size-full object-contain"
|
|
122
122
|
}) : /* @__PURE__ */ h(l, {
|
|
123
123
|
vtt: `${v.base}sprite.vtt`,
|
|
124
|
-
|
|
124
|
+
loader: C.loader
|
|
125
125
|
})),
|
|
126
126
|
/* @__PURE__ */ h("div", {
|
|
127
127
|
"aria-hidden": !0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-mini-player.js","names":[],"sources":["../../src/components/video-mini-player.tsx"],"sourcesContent":["import { Cancel01Icon, GoBackward10SecIcon, GoForward10SecIcon, SquareArrowExpand01Icon, PauseIcon, PlayIcon, VolumeHighIcon, VolumeOffIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type MouseEvent } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { JUMP_SEEK, PLAYER_VOLUME_KEY, playerKeyAction, writeVolume, type PlayerAction } from \"../client/player.js\";\nimport { publicRenditions } from \"../client/public.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useActiveCues, useMediaState, useProgressReports, type PlaybackProgress } from \"../react/player.js\";\nimport { ContentKitUiRoot } from \"../scope.js\";\nimport { Captions, ControlButton, SeekBar, Spinner, clock } from \"./player-controls.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport { SpriteFrame, keyAllowed, type PlayerHandoff } from \"./video-player.js\";\n\nconst CONTROLS_IDLE_MS = 2500;\n\nexport interface VideoMiniPlayerProps extends Omit<HlsPlayerOptions, \"src\" | \"startAt\" | \"autoPlay\" | \"audioLanguage\" | \"subtitleLanguage\" | \"active\"> {\n /** What VideoPlayer's onMiniPlayer handed over. */\n handoff: PlayerHandoff;\n /** Back to the full player, where this one is. */\n onExpand: (state: { time: number; playing: boolean }) => void;\n onClose: (state: { time: number }) => void;\n onProgress?: (progress: PlaybackProgress) => void;\n /** Milliseconds. Default 10000. */\n progressInterval?: number;\n /** K, J, L and M page-wide (default), every shortcut while focus is in it, or none. */\n keyboard?: \"global\" | \"focus\" | false;\n /** The video's title. */\n label?: string;\n position?: \"bottom-right\" | \"bottom-left\";\n crossOrigin?: \"anonymous\" | \"use-credentials\";\n className?: string;\n style?: CSSProperties;\n appearance?: ContentKitUiAppearance;\n}\n\n/**\n * The video docked in a corner while the viewer browses: play, ±10 s, mute,\n * a seek line, expand back to the full player and close. It starts where\n * the handoff stopped, playing if it was.\n */\nexport function VideoMiniPlayer({\n handoff,\n onExpand,\n onClose,\n onProgress,\n progressInterval = 10_000,\n keyboard = \"global\",\n label,\n position = \"bottom-right\",\n crossOrigin,\n className,\n style,\n appearance,\n ...options\n}: VideoMiniPlayerProps) {\n const { t } = useMessages();\n const src = `${handoff.base}master.m3u8`;\n const player = useHlsPlayer({\n ...options,\n src,\n startAt: handoff.time,\n autoPlay: handoff.playing,\n audioLanguage: handoff.audioLanguage,\n subtitleLanguage: handoff.subtitleLanguage,\n // The handoff carries the viewer's choices; a few hundred pixels need no manual rung.\n trackKey: null,\n qualityKey: null,\n });\n const { status, error, started } = player;\n const [video, setVideo] = useState<HTMLVideoElement | null>(null);\n const attach = player.ref;\n const videoRef = useCallback(\n (el: HTMLVideoElement | null) => {\n attach(el);\n setVideo(el);\n },\n [attach],\n );\n const media = useMediaState(video);\n const cues = useActiveCues(video);\n useProgressReports(video, { enabled: started, source: started ? src : null, interval: progressInterval, onProgress });\n\n const [poked, setPoked] = useState(true);\n const idle = useRef<ReturnType<typeof setTimeout>>(undefined);\n const poke = useCallback(() => {\n setPoked(true);\n clearTimeout(idle.current);\n idle.current = setTimeout(() => setPoked(false), CONTROLS_IDLE_MS);\n }, []);\n const hide = useCallback(() => {\n clearTimeout(idle.current);\n setPoked(false);\n }, []);\n useEffect(() => () => clearTimeout(idle.current), []);\n const [scrubbing, setScrubbing] = useState(false);\n const playing = !media.paused && !media.ended;\n const shown = poked || !playing || scrubbing || !!error;\n\n const done = useRef(false);\n const time = () => video?.currentTime || media.time || handoff.time;\n const expand = () => {\n if (done.current) return;\n done.current = true;\n onExpand({ time: time(), playing });\n };\n const close = () => {\n if (done.current) return;\n done.current = true;\n video?.pause();\n onClose({ time: time() });\n };\n const toggle = () => {\n if (!video) return;\n if (!started || video.paused || video.ended) player.play();\n else video.pause();\n };\n const dur = media.duration || handoff.duration || 0;\n const seek = (to: number) => {\n if (video) video.currentTime = Math.min(dur || to, Math.max(0, to));\n };\n const volumeKey = options.volumeKey === undefined ? PLAYER_VOLUME_KEY : options.volumeKey;\n const toggleMute = () => {\n if (!video) return;\n video.muted = !video.muted;\n if (!video.muted && video.volume === 0) video.volume = 1;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n };\n\n const run = (a: PlayerAction, key: string): boolean => {\n if (!video) return false;\n // Page-wide, only K, J, L and M: Space and the arrows scroll the page.\n if (keyboard === \"global\" && !\"kjlmKJLM\".includes(key)) return false;\n switch (a.type) {\n case \"toggle\":\n toggle();\n break;\n case \"seekBy\":\n if (!started) return false;\n seek(video.currentTime + a.seconds);\n break;\n case \"mute\":\n toggleMute();\n break;\n default:\n return false;\n }\n poke();\n return true;\n };\n const runRef = useRef(run);\n runRef.current = run;\n const [root, setRoot] = useState<HTMLDivElement | null>(null);\n useEffect(() => {\n if (!keyboard || !root) return;\n const on: EventTarget = keyboard === \"global\" ? document : root;\n const down = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.defaultPrevented) return;\n const action = playerKeyAction(k);\n if (!action || !keyAllowed(k.target, k.key, root) || !runRef.current(action, k.key)) return;\n k.preventDefault();\n };\n on.addEventListener(\"keydown\", down);\n return () => on.removeEventListener(\"keydown\", down);\n }, [keyboard, root]);\n\n // Touch has no hover: a tap shows or hides the controls. A click plays and pauses.\n const pointerType = useRef(\"mouse\");\n const onSurfaceClick = (e: MouseEvent) => {\n e.preventDefault();\n if (pointerType.current === \"mouse\") {\n toggle();\n poke();\n } else if (shown && playing) hide();\n else poke();\n };\n\n const aspect = handoff.width && handoff.height ? handoff.width / handoff.height : (media.aspect ?? 16 / 9);\n const posterOutputs = typeof handoff.poster === \"string\" ? [] : publicRenditions(handoff.poster);\n const busy = !error && (status === \"loading\" || status === \"buffering\");\n const overlay = \"rounded-full text-white hover:bg-white/15\";\n return (\n <ContentKitUiRoot\n ref={setRoot}\n appearance={appearance}\n role=\"region\"\n aria-label={label ? `${t(\"player.miniPlayer\")}: ${label}` : t(\"player.miniPlayer\")}\n data-ckui=\"mini-player\"\n data-status={status}\n className={cn(\n \"@container fixed bottom-4 z-50 touch-manipulation overflow-hidden rounded-md bg-black text-white shadow-lg select-none\",\n position === \"bottom-left\" ? \"left-4\" : \"right-4\",\n className,\n )}\n style={{ aspectRatio: String(aspect), width: `min(24rem, calc(100vw - 2rem), calc(60svh * ${aspect}))`, ...style }}\n onPointerDown={(e) => (pointerType.current = e.pointerType || \"mouse\")}\n onPointerMove={(e) => e.pointerType === \"mouse\" && poke()}\n onPointerLeave={(e) => e.pointerType === \"mouse\" && !scrubbing && playing && hide()}\n >\n <video ref={videoRef} className=\"absolute inset-0 size-full object-contain\" playsInline preload=\"none\" crossOrigin={crossOrigin} aria-label={label} />\n {!started &&\n (typeof handoff.poster === \"string\" ? (\n <img alt=\"\" src={handoff.poster} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : posterOutputs.length ? (\n <RenditionImg outputs={posterOutputs} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : (\n <SpriteFrame vtt={`${handoff.base}sprite.vtt`} xhrSetup={options.xhrSetup} />\n ))}\n <div aria-hidden data-ckui=\"surface\" className=\"absolute inset-0\" onClick={onSurfaceClick} />\n <Captions cues={started ? cues : []} bottom={shown ? \"2.75rem\" : \"0.75rem\"} />\n <div\n data-ckui=\"controls\"\n className={cn(\n \"pointer-events-none absolute inset-0 bg-black/45 transition-opacity duration-200 focus-within:opacity-100 motion-reduce:transition-none\",\n shown ? \"opacity-100 [&_button]:pointer-events-auto\" : \"opacity-0\",\n )}\n >\n <div className=\"absolute inset-x-0 top-0 flex items-center justify-between p-1.5\">\n <ControlButton label={t(\"player.expand\")} icon={SquareArrowExpand01Icon} onClick={expand} className={overlay} data-ckui=\"mini-expand\" />\n <ControlButton label={t(\"player.close\")} icon={Cancel01Icon} onClick={close} className={overlay} data-ckui=\"mini-close\" />\n </div>\n <div className=\"absolute inset-0 m-auto flex h-fit w-fit items-center gap-3 @sm:gap-5\">\n <ControlButton\n label={t(\"player.seekBack\", { seconds: JUMP_SEEK })}\n icon={GoBackward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() - JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n <button\n type=\"button\"\n aria-label={playing ? t(\"player.pause\") : t(\"player.resume\")}\n title={playing ? t(\"player.pause\") : t(\"player.resume\")}\n onClick={() => {\n toggle();\n poke();\n }}\n className={cn(\"flex size-12 cursor-pointer items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-white/80\", overlay)}\n data-ckui=\"mini-play\"\n >\n {busy && started ? <Spinner className=\"size-7\" /> : <HugeiconsIcon icon={playing ? PauseIcon : PlayIcon} className=\"size-7 fill-current\" strokeWidth={1.5} aria-hidden />}\n </button>\n <ControlButton\n label={t(\"player.seekForward\", { seconds: JUMP_SEEK })}\n icon={GoForward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() + JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n </div>\n <div className=\"absolute inset-x-0 bottom-3 flex items-center justify-between px-2.5\">\n <span className=\"text-xs font-medium tabular-nums\">{dur > 0 ? `${clock(media.time || handoff.time)} / ${clock(dur)}` : clock(media.time || handoff.time)}</span>\n <ControlButton label={media.muted ? t(\"player.unmute\") : t(\"player.mute\")} icon={media.muted ? VolumeOffIcon : VolumeHighIcon} onClick={toggleMute} className={cn(overlay, \"size-8\")} />\n </div>\n </div>\n <div className=\"absolute inset-x-0 bottom-0 z-10\">\n <SeekBar thin time={media.time || handoff.time} duration={dur} buffered={media.buffered} onSeek={seek} onScrub={(v) => setScrubbing(v !== null)} />\n </div>\n {error && (\n <div role=\"alert\" className=\"absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-black/80 p-3 text-center text-xs\">\n <p>{t(`player.errors.${error.kind}`)}</p>\n <button type=\"button\" onClick={player.retry} className=\"rounded-md bg-white/15 px-2 py-1 font-medium hover:bg-white/25\">\n {t(\"player.retry\")}\n </button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video-mini-player.js","names":[],"sources":["../../src/components/video-mini-player.tsx"],"sourcesContent":["import { Cancel01Icon, GoBackward10SecIcon, GoForward10SecIcon, SquareArrowExpand01Icon, PauseIcon, PlayIcon, VolumeHighIcon, VolumeOffIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type MouseEvent } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { JUMP_SEEK, PLAYER_VOLUME_KEY, playerKeyAction, writeVolume, type PlayerAction } from \"../client/player.js\";\nimport { publicRenditions } from \"../client/public.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useActiveCues, useMediaState, useProgressReports, type PlaybackProgress } from \"../react/player.js\";\nimport { ContentKitUiRoot } from \"../scope.js\";\nimport { Captions, ControlButton, SeekBar, Spinner, clock } from \"./player-controls.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport { SpriteFrame, keyAllowed, type PlayerHandoff } from \"./video-player.js\";\n\nconst CONTROLS_IDLE_MS = 2500;\n\nexport interface VideoMiniPlayerProps extends Omit<HlsPlayerOptions, \"src\" | \"startAt\" | \"autoPlay\" | \"audioLanguage\" | \"subtitleLanguage\" | \"active\"> {\n /** What VideoPlayer's onMiniPlayer handed over. */\n handoff: PlayerHandoff;\n /** Back to the full player, where this one is. */\n onExpand: (state: { time: number; playing: boolean }) => void;\n onClose: (state: { time: number }) => void;\n onProgress?: (progress: PlaybackProgress) => void;\n /** Milliseconds. Default 10000. */\n progressInterval?: number;\n /** K, J, L and M page-wide (default), every shortcut while focus is in it, or none. */\n keyboard?: \"global\" | \"focus\" | false;\n /** The video's title. */\n label?: string;\n position?: \"bottom-right\" | \"bottom-left\";\n crossOrigin?: \"anonymous\" | \"use-credentials\";\n className?: string;\n style?: CSSProperties;\n appearance?: ContentKitUiAppearance;\n}\n\n/**\n * The video docked in a corner while the viewer browses: play, ±10 s, mute,\n * a seek line, expand back to the full player and close. It starts where\n * the handoff stopped, playing if it was.\n */\nexport function VideoMiniPlayer({\n handoff,\n onExpand,\n onClose,\n onProgress,\n progressInterval = 10_000,\n keyboard = \"global\",\n label,\n position = \"bottom-right\",\n crossOrigin,\n className,\n style,\n appearance,\n ...options\n}: VideoMiniPlayerProps) {\n const { t } = useMessages();\n const src = `${handoff.base}master.m3u8`;\n const player = useHlsPlayer({\n ...options,\n src,\n startAt: handoff.time,\n autoPlay: handoff.playing,\n audioLanguage: handoff.audioLanguage,\n subtitleLanguage: handoff.subtitleLanguage,\n // The handoff carries the viewer's choices; a few hundred pixels need no manual rung.\n trackKey: null,\n qualityKey: null,\n });\n const { status, error, started } = player;\n const [video, setVideo] = useState<HTMLVideoElement | null>(null);\n const attach = player.ref;\n const videoRef = useCallback(\n (el: HTMLVideoElement | null) => {\n attach(el);\n setVideo(el);\n },\n [attach],\n );\n const media = useMediaState(video);\n const cues = useActiveCues(video);\n useProgressReports(video, { enabled: started, source: started ? src : null, interval: progressInterval, onProgress });\n\n const [poked, setPoked] = useState(true);\n const idle = useRef<ReturnType<typeof setTimeout>>(undefined);\n const poke = useCallback(() => {\n setPoked(true);\n clearTimeout(idle.current);\n idle.current = setTimeout(() => setPoked(false), CONTROLS_IDLE_MS);\n }, []);\n const hide = useCallback(() => {\n clearTimeout(idle.current);\n setPoked(false);\n }, []);\n useEffect(() => () => clearTimeout(idle.current), []);\n const [scrubbing, setScrubbing] = useState(false);\n const playing = !media.paused && !media.ended;\n const shown = poked || !playing || scrubbing || !!error;\n\n const done = useRef(false);\n const time = () => video?.currentTime || media.time || handoff.time;\n const expand = () => {\n if (done.current) return;\n done.current = true;\n onExpand({ time: time(), playing });\n };\n const close = () => {\n if (done.current) return;\n done.current = true;\n video?.pause();\n onClose({ time: time() });\n };\n const toggle = () => {\n if (!video) return;\n if (!started || video.paused || video.ended) player.play();\n else video.pause();\n };\n const dur = media.duration || handoff.duration || 0;\n const seek = (to: number) => {\n if (video) video.currentTime = Math.min(dur || to, Math.max(0, to));\n };\n const volumeKey = options.volumeKey === undefined ? PLAYER_VOLUME_KEY : options.volumeKey;\n const toggleMute = () => {\n if (!video) return;\n video.muted = !video.muted;\n if (!video.muted && video.volume === 0) video.volume = 1;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n };\n\n const run = (a: PlayerAction, key: string): boolean => {\n if (!video) return false;\n // Page-wide, only K, J, L and M: Space and the arrows scroll the page.\n if (keyboard === \"global\" && !\"kjlmKJLM\".includes(key)) return false;\n switch (a.type) {\n case \"toggle\":\n toggle();\n break;\n case \"seekBy\":\n if (!started) return false;\n seek(video.currentTime + a.seconds);\n break;\n case \"mute\":\n toggleMute();\n break;\n default:\n return false;\n }\n poke();\n return true;\n };\n const runRef = useRef(run);\n runRef.current = run;\n const [root, setRoot] = useState<HTMLDivElement | null>(null);\n useEffect(() => {\n if (!keyboard || !root) return;\n const on: EventTarget = keyboard === \"global\" ? document : root;\n const down = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.defaultPrevented) return;\n const action = playerKeyAction(k);\n if (!action || !keyAllowed(k.target, k.key, root) || !runRef.current(action, k.key)) return;\n k.preventDefault();\n };\n on.addEventListener(\"keydown\", down);\n return () => on.removeEventListener(\"keydown\", down);\n }, [keyboard, root]);\n\n // Touch has no hover: a tap shows or hides the controls. A click plays and pauses.\n const pointerType = useRef(\"mouse\");\n const onSurfaceClick = (e: MouseEvent) => {\n e.preventDefault();\n if (pointerType.current === \"mouse\") {\n toggle();\n poke();\n } else if (shown && playing) hide();\n else poke();\n };\n\n const aspect = handoff.width && handoff.height ? handoff.width / handoff.height : (media.aspect ?? 16 / 9);\n const posterOutputs = typeof handoff.poster === \"string\" ? [] : publicRenditions(handoff.poster);\n const busy = !error && (status === \"loading\" || status === \"buffering\");\n const overlay = \"rounded-full text-white hover:bg-white/15\";\n return (\n <ContentKitUiRoot\n ref={setRoot}\n appearance={appearance}\n role=\"region\"\n aria-label={label ? `${t(\"player.miniPlayer\")}: ${label}` : t(\"player.miniPlayer\")}\n data-ckui=\"mini-player\"\n data-status={status}\n className={cn(\n \"@container fixed bottom-4 z-50 touch-manipulation overflow-hidden rounded-md bg-black text-white shadow-lg select-none\",\n position === \"bottom-left\" ? \"left-4\" : \"right-4\",\n className,\n )}\n style={{ aspectRatio: String(aspect), width: `min(24rem, calc(100vw - 2rem), calc(60svh * ${aspect}))`, ...style }}\n onPointerDown={(e) => (pointerType.current = e.pointerType || \"mouse\")}\n onPointerMove={(e) => e.pointerType === \"mouse\" && poke()}\n onPointerLeave={(e) => e.pointerType === \"mouse\" && !scrubbing && playing && hide()}\n >\n <video ref={videoRef} className=\"absolute inset-0 size-full object-contain\" playsInline preload=\"none\" crossOrigin={crossOrigin} aria-label={label} />\n {!started &&\n (typeof handoff.poster === \"string\" ? (\n <img alt=\"\" src={handoff.poster} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : posterOutputs.length ? (\n <RenditionImg outputs={posterOutputs} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : (\n <SpriteFrame vtt={`${handoff.base}sprite.vtt`} loader={options.loader} />\n ))}\n <div aria-hidden data-ckui=\"surface\" className=\"absolute inset-0\" onClick={onSurfaceClick} />\n <Captions cues={started ? cues : []} bottom={shown ? \"2.75rem\" : \"0.75rem\"} />\n <div\n data-ckui=\"controls\"\n className={cn(\n \"pointer-events-none absolute inset-0 bg-black/45 transition-opacity duration-200 focus-within:opacity-100 motion-reduce:transition-none\",\n shown ? \"opacity-100 [&_button]:pointer-events-auto\" : \"opacity-0\",\n )}\n >\n <div className=\"absolute inset-x-0 top-0 flex items-center justify-between p-1.5\">\n <ControlButton label={t(\"player.expand\")} icon={SquareArrowExpand01Icon} onClick={expand} className={overlay} data-ckui=\"mini-expand\" />\n <ControlButton label={t(\"player.close\")} icon={Cancel01Icon} onClick={close} className={overlay} data-ckui=\"mini-close\" />\n </div>\n <div className=\"absolute inset-0 m-auto flex h-fit w-fit items-center gap-3 @sm:gap-5\">\n <ControlButton\n label={t(\"player.seekBack\", { seconds: JUMP_SEEK })}\n icon={GoBackward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() - JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n <button\n type=\"button\"\n aria-label={playing ? t(\"player.pause\") : t(\"player.resume\")}\n title={playing ? t(\"player.pause\") : t(\"player.resume\")}\n onClick={() => {\n toggle();\n poke();\n }}\n className={cn(\"flex size-12 cursor-pointer items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-white/80\", overlay)}\n data-ckui=\"mini-play\"\n >\n {busy && started ? <Spinner className=\"size-7\" /> : <HugeiconsIcon icon={playing ? PauseIcon : PlayIcon} className=\"size-7 fill-current\" strokeWidth={1.5} aria-hidden />}\n </button>\n <ControlButton\n label={t(\"player.seekForward\", { seconds: JUMP_SEEK })}\n icon={GoForward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() + JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n </div>\n <div className=\"absolute inset-x-0 bottom-3 flex items-center justify-between px-2.5\">\n <span className=\"text-xs font-medium tabular-nums\">{dur > 0 ? `${clock(media.time || handoff.time)} / ${clock(dur)}` : clock(media.time || handoff.time)}</span>\n <ControlButton label={media.muted ? t(\"player.unmute\") : t(\"player.mute\")} icon={media.muted ? VolumeOffIcon : VolumeHighIcon} onClick={toggleMute} className={cn(overlay, \"size-8\")} />\n </div>\n </div>\n <div className=\"absolute inset-x-0 bottom-0 z-10\">\n <SeekBar thin time={media.time || handoff.time} duration={dur} buffered={media.buffered} onSeek={seek} onScrub={(v) => setScrubbing(v !== null)} />\n </div>\n {error && (\n <div role=\"alert\" className=\"absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-black/80 p-3 text-center text-xs\">\n <p>{t(`player.errors.${error.kind}`)}</p>\n <button type=\"button\" onClick={player.retry} className=\"rounded-md bg-white/15 px-2 py-1 font-medium hover:bg-white/25\">\n {t(\"player.retry\")}\n </button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -6,11 +6,11 @@ import { HlsPlayerOptions } from '../react/gallery.js';
|
|
|
6
6
|
import { PlaybackProgress } from '../react/player.js';
|
|
7
7
|
import { PublicPreset } from '../client/public.js';
|
|
8
8
|
import { EncodeProgress as Progress } from '../client/generated/wire.js';
|
|
9
|
-
type
|
|
9
|
+
type Loader = HlsPlayerOptions["loader"];
|
|
10
10
|
/** The first frame of a video's seek sprite (sprite.vtt), fitted like object-fit. */
|
|
11
|
-
export declare function SpriteFrame({ vtt,
|
|
11
|
+
export declare function SpriteFrame({ vtt, loader, fit, className }: {
|
|
12
12
|
vtt: string;
|
|
13
|
-
|
|
13
|
+
loader?: Loader;
|
|
14
14
|
fit?: "contain" | "cover";
|
|
15
15
|
className?: string;
|
|
16
16
|
}): import('react').JSX.Element | null;
|
|
@@ -16,7 +16,7 @@ import { cn as m } from "cn";
|
|
|
16
16
|
import { AlertCircleIcon as h, ArrowShrink02Icon as Ce, ClosedCaptionIcon as we, FullScreenIcon as Te, PauseIcon as Ee, PictureInPicture01Icon as De, PictureInPictureExitIcon as Oe, PictureInPictureOnIcon as ke, PlayIcon as Ae, RectangularIcon as je, Refresh01Icon as Me, Settings01Icon as Ne } from "@hugeicons/core-free-icons";
|
|
17
17
|
import { HugeiconsIcon as g } from "@hugeicons/react";
|
|
18
18
|
//#region src/components/video-player.tsx
|
|
19
|
-
function Pe({ vtt: e,
|
|
19
|
+
function Pe({ vtt: e, loader: t, fit: n = "contain", className: r }) {
|
|
20
20
|
let i = me(e, t), a = i?.cues[0];
|
|
21
21
|
return !i || !a ? null : /* @__PURE__ */ f("svg", {
|
|
22
22
|
"aria-hidden": !0,
|
|
@@ -56,7 +56,7 @@ function b({ base: o, poster: h, width: _, height: v, duration: y, pending: b, p
|
|
|
56
56
|
start: () => j.preview(We(Xe, y)),
|
|
57
57
|
stop: j.unload
|
|
58
58
|
});
|
|
59
|
-
let V = de(B), H = ue(z, B), U = fe(B), mt = le(B), ht = me(P && o ? `${o}sprite.vtt` : null, D.
|
|
59
|
+
let V = de(B), H = ue(z, B), U = fe(B), mt = le(B), ht = me(P && o ? `${o}sprite.vtt` : null, D.loader), gt = !F && P;
|
|
60
60
|
pe(B, {
|
|
61
61
|
enabled: gt,
|
|
62
62
|
source: gt ? lt : null,
|
|
@@ -423,7 +423,7 @@ function b({ base: o, poster: h, width: _, height: v, duration: y, pending: b, p
|
|
|
423
423
|
className: m("pointer-events-none absolute inset-0 size-full object-contain", $)
|
|
424
424
|
}) : /* @__PURE__ */ f(Pe, {
|
|
425
425
|
vtt: `${o}sprite.vtt`,
|
|
426
|
-
|
|
426
|
+
loader: D.loader,
|
|
427
427
|
className: $
|
|
428
428
|
}),
|
|
429
429
|
/* @__PURE__ */ f(Ue, {
|