@norskvideo/ctl-sdk 0.1.11 → 0.1.13
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/components/ShotBar.d.ts +17 -3
- package/components/ShotBar.js +18 -5
- package/package.json +1 -1
package/components/ShotBar.d.ts
CHANGED
|
@@ -24,9 +24,23 @@ export interface ShotBarProps {
|
|
|
24
24
|
onTake: (commands: TakeCommand[], shot: ResolvedShot) => void;
|
|
25
25
|
/** Optional preview bus — omit to hide the Preview control. */
|
|
26
26
|
onPreview?: (commands: TakeCommand[], shot: ResolvedShot) => void;
|
|
27
|
+
/**
|
|
28
|
+
* True while a preview node is up (between Preview and Take/Cancel). Product-
|
|
29
|
+
* owned: the ShotBar just reflects it — swapping the Preview control for
|
|
30
|
+
* Cancel and letting the preview monitor show the live feed. Take commits it.
|
|
31
|
+
*/
|
|
32
|
+
previewing?: boolean;
|
|
33
|
+
/** Back out of a preview without taking it (tears the preview node down). */
|
|
34
|
+
onCancelPreview?: () => void;
|
|
27
35
|
/** Drop-in program monitor (e.g. a WHEP player). */
|
|
28
36
|
renderProgramMonitor?: () => ReactNode;
|
|
29
|
-
/**
|
|
30
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Drop-in preview monitor. Receives the shot currently selected in the bar
|
|
39
|
+
* (or null) so the product can render *that shot's* what-if — the JPEG preset
|
|
40
|
+
* preview before Preview is pressed, swapping to the live preview-node feed
|
|
41
|
+
* once previewing. Selection lives inside the ShotBar, so this is the only way
|
|
42
|
+
* the product learns which shot to preview.
|
|
43
|
+
*/
|
|
44
|
+
renderPreviewMonitor?: (selected: ResolvedShot | null) => ReactNode;
|
|
31
45
|
}
|
|
32
|
-
export declare function ShotBar({ shots, pgmShotId, pvwShotId, graphicBaseUrl, transition, editableGraphics, onTake, onPreview, renderProgramMonitor, renderPreviewMonitor, }: ShotBarProps): import("react").JSX.Element;
|
|
46
|
+
export declare function ShotBar({ shots, pgmShotId, pvwShotId, graphicBaseUrl, transition, editableGraphics, onTake, onPreview, previewing, onCancelPreview, renderProgramMonitor, renderPreviewMonitor, }: ShotBarProps): import("react").JSX.Element;
|
package/components/ShotBar.js
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useState } from "react";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { graphicResolvable, hideReasonLabel, prepareTake } from "./shot-bar-view-model.js";
|
|
4
4
|
import { useUIPrimitives } from "./ui-primitives.js";
|
|
5
5
|
const FAMILY_BADGE = { geometry: "Geometry", graphic: "Graphic" };
|
|
6
|
-
export function ShotBar({ shots, pgmShotId, pvwShotId, graphicBaseUrl, transition, editableGraphics = true, onTake, onPreview, renderProgramMonitor, renderPreviewMonitor, }) {
|
|
6
|
+
export function ShotBar({ shots, pgmShotId, pvwShotId, graphicBaseUrl, transition, editableGraphics = true, onTake, onPreview, previewing = false, onCancelPreview, renderProgramMonitor, renderPreviewMonitor, }) {
|
|
7
7
|
const { Button, Input } = useUIPrimitives();
|
|
8
|
-
const
|
|
9
|
-
const [selectedId, setSelectedId] = useState(firstReady);
|
|
8
|
+
const [selectedId, setSelectedId] = useState(null);
|
|
10
9
|
const [editedUrls, setEditedUrls] = useState({});
|
|
11
10
|
const selected = shots.find((s) => s.id === selectedId && s.status === "ready");
|
|
11
|
+
// Auto-select the first ready shot once live state arrives (shots start empty
|
|
12
|
+
// / all-hidden before the first WS frame, so a mount-time pick would be null),
|
|
13
|
+
// and re-select if the current one stops being ready.
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (selectedId && shots.some((s) => s.id === selectedId && s.status === "ready"))
|
|
16
|
+
return;
|
|
17
|
+
const firstReady = shots.find((s) => s.status === "ready")?.id ?? null;
|
|
18
|
+
if (firstReady !== selectedId)
|
|
19
|
+
setSelectedId(firstReady);
|
|
20
|
+
}, [shots, selectedId]);
|
|
12
21
|
const commands = useMemo(() => (selected ? prepareTake(selected, { graphicBaseUrl, editedUrl: editedUrls[selected.id], transition }) : []), [selected, graphicBaseUrl, editedUrls, transition]);
|
|
13
22
|
// A graphic shot whose page is relative and has no engine-reachable base
|
|
14
23
|
// can't safely change-url — surface it instead of firing an unfetchable page.
|
|
15
24
|
const takeableGraphic = selected
|
|
16
25
|
? graphicResolvable(editedUrls[selected.id] ?? selected.graphicUrl, graphicBaseUrl)
|
|
17
26
|
: true;
|
|
18
|
-
|
|
27
|
+
// Only show the Preview monitor when the product runs a preview bus (a
|
|
28
|
+
// preview take handler or a preview monitor renderer). Otherwise it's a dead
|
|
29
|
+
// pane — a direct-take product has no PVW feed — so give On Air the full row.
|
|
30
|
+
const showPreview = Boolean(onPreview || renderPreviewMonitor);
|
|
31
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 text-zinc-100", children: [_jsxs("div", { className: `grid gap-3 ${showPreview ? "grid-cols-2" : "grid-cols-1"}`, children: [showPreview ? (_jsx(Monitor, { label: "Preview", tone: "pvw", shot: findLabel(shots, pvwShotId), render: renderPreviewMonitor ? () => renderPreviewMonitor(selected ?? null) : undefined })) : null, _jsx(Monitor, { label: "On Air", tone: "pgm", shot: findLabel(shots, pgmShotId), render: renderProgramMonitor })] }), _jsx("div", { className: "flex gap-3 overflow-x-auto pb-1", children: shots.map((shot) => (_jsx(ShotCard, { shot: shot, selected: shot.id === selectedId, onAir: shot.id === pgmShotId, config: shot.status === "ready" ? shot.config : undefined, onSelect: () => shot.status === "ready" && setSelectedId(shot.id) }, shot.id))) }), selected ? (_jsxs("div", { className: "flex flex-col gap-3 rounded-lg border border-zinc-800 bg-zinc-900/60 p-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [selected.label, previewing ? (_jsx("span", { className: "rounded bg-emerald-600/20 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-emerald-400", children: "Previewing" })) : null] }), _jsxs("div", { className: "flex gap-2", children: [previewing ? (onCancelPreview ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: onCancelPreview, children: "Cancel" })) : null) : onPreview ? (_jsx(Button, { variant: "ghost", size: "sm", disabled: !takeableGraphic, onClick: () => onPreview(commands, selected), children: "Preview" })) : null, _jsx(Button, { variant: "primary", size: "sm", disabled: !takeableGraphic, onClick: () => onTake(commands, selected), children: "Take" })] })] }), !takeableGraphic ? (_jsx("p", { className: "text-xs text-amber-500/90", children: "This shot's graphic page is a relative path but no engine-reachable graphic base is configured, so it can't be loaded into the overlay. Set an absolute page URL, or configure the overlay base." })) : null, editableGraphics && selected.graphicUrl !== undefined ? (_jsxs("div", { className: "flex flex-col gap-1 text-xs text-zinc-400", children: [_jsx("label", { htmlFor: "shotbar-graphic-url", children: "Graphic page" }), _jsx(Input, { id: "shotbar-graphic-url", type: "text", value: editedUrls[selected.id] ?? selected.graphicUrl, placeholder: selected.graphicUrl, onChange: (e) => setEditedUrls((prev) => ({ ...prev, [selected.id]: e.target.value })) })] })) : null] })) : (_jsx("div", { className: "rounded-lg border border-dashed border-zinc-800 p-4 text-center text-sm text-zinc-500", children: "No shot selected \u2014 pick an available shot above." }))] }));
|
|
19
32
|
}
|
|
20
33
|
function findLabel(shots, id) {
|
|
21
34
|
if (!id)
|