@norskvideo/norsk-studio-built-ins 1.27.0-2026-01-10-23683704 → 1.27.0-2026-01-14-d7f304fc
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/client/info.js +158 -90
- package/lib/input.srt-listener/_gen/types.d.ts +1 -0
- package/lib/input.srt-listener/_gen/yaml-docs.js +3 -0
- package/lib/input.srt-listener/_gen/yaml-docs.js.map +1 -1
- package/lib/input.srt-listener/_gen/zod.js +1 -0
- package/lib/input.srt-listener/_gen/zod.js.map +1 -1
- package/lib/input.srt-listener/info.d.ts +1 -0
- package/lib/input.srt-listener/info.js +9 -0
- package/lib/input.srt-listener/info.js.map +1 -1
- package/lib/input.srt-listener/runtime.d.ts +1 -0
- package/lib/input.srt-listener/runtime.js +7 -0
- package/lib/input.srt-listener/runtime.js.map +1 -1
- package/lib/input.srt-listener/types.yaml +2 -0
- package/lib/processor.browserOverlay/runtime.js +1 -1
- package/lib/processor.browserOverlay/runtime.js.map +1 -1
- package/lib/processor.onscreenGraphic/runtime.js +1 -1
- package/lib/processor.onscreenGraphic/runtime.js.map +1 -1
- package/lib/processor.smartSourceSwitch/inline-view.d.ts +3 -5
- package/lib/processor.smartSourceSwitch/inline-view.js +16 -4
- package/lib/processor.smartSourceSwitch/inline-view.js.map +1 -1
- package/lib/processor.videoCompose/fullscreen.js +28 -10
- package/lib/processor.videoCompose/fullscreen.js.map +1 -1
- package/lib/processor.videoCompose/preset-transition-panel.js +37 -8
- package/lib/processor.videoCompose/preset-transition-panel.js.map +1 -1
- package/lib/processor.videoCompose/runtime.js +42 -42
- package/lib/processor.videoCompose/runtime.js.map +1 -1
- package/lib/processor.videoCompose/visual-preview.js +3 -1
- package/lib/processor.videoCompose/visual-preview.js.map +1 -1
- package/lib/processor.zoomTo/runtime.js +1 -1
- package/lib/processor.zoomTo/runtime.js.map +1 -1
- package/package.json +3 -3
package/client/info.js
CHANGED
|
@@ -25992,7 +25992,7 @@ var require_dist6 = __commonJS({
|
|
|
25992
25992
|
unstable_createCollection: () => createCollection2
|
|
25993
25993
|
});
|
|
25994
25994
|
module.exports = __toCommonJS2(index_exports);
|
|
25995
|
-
var
|
|
25995
|
+
var import_react34 = __toESM2(require_react());
|
|
25996
25996
|
var import_react_context = require_dist3();
|
|
25997
25997
|
var import_react_compose_refs = require_dist4();
|
|
25998
25998
|
var import_react_slot = require_dist5();
|
|
@@ -26006,14 +26006,14 @@ var require_dist6 = __commonJS({
|
|
|
26006
26006
|
);
|
|
26007
26007
|
const CollectionProvider = (props) => {
|
|
26008
26008
|
const { scope, children } = props;
|
|
26009
|
-
const ref =
|
|
26010
|
-
const itemMap =
|
|
26009
|
+
const ref = import_react34.default.useRef(null);
|
|
26010
|
+
const itemMap = import_react34.default.useRef(/* @__PURE__ */ new Map()).current;
|
|
26011
26011
|
return /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(CollectionProviderImpl, { scope, itemMap, collectionRef: ref, children });
|
|
26012
26012
|
};
|
|
26013
26013
|
CollectionProvider.displayName = PROVIDER_NAME;
|
|
26014
26014
|
const COLLECTION_SLOT_NAME = name + "CollectionSlot";
|
|
26015
26015
|
const CollectionSlotImpl = (0, import_react_slot.createSlot)(COLLECTION_SLOT_NAME);
|
|
26016
|
-
const CollectionSlot =
|
|
26016
|
+
const CollectionSlot = import_react34.default.forwardRef(
|
|
26017
26017
|
(props, forwardedRef) => {
|
|
26018
26018
|
const { scope, children } = props;
|
|
26019
26019
|
const context = useCollectionContext(COLLECTION_SLOT_NAME, scope);
|
|
@@ -26025,13 +26025,13 @@ var require_dist6 = __commonJS({
|
|
|
26025
26025
|
const ITEM_SLOT_NAME = name + "CollectionItemSlot";
|
|
26026
26026
|
const ITEM_DATA_ATTR = "data-radix-collection-item";
|
|
26027
26027
|
const CollectionItemSlotImpl = (0, import_react_slot.createSlot)(ITEM_SLOT_NAME);
|
|
26028
|
-
const CollectionItemSlot =
|
|
26028
|
+
const CollectionItemSlot = import_react34.default.forwardRef(
|
|
26029
26029
|
(props, forwardedRef) => {
|
|
26030
26030
|
const { scope, children, ...itemData } = props;
|
|
26031
|
-
const ref =
|
|
26031
|
+
const ref = import_react34.default.useRef(null);
|
|
26032
26032
|
const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, ref);
|
|
26033
26033
|
const context = useCollectionContext(ITEM_SLOT_NAME, scope);
|
|
26034
|
-
|
|
26034
|
+
import_react34.default.useEffect(() => {
|
|
26035
26035
|
context.itemMap.set(ref, { ref, ...itemData });
|
|
26036
26036
|
return () => void context.itemMap.delete(ref);
|
|
26037
26037
|
});
|
|
@@ -26041,7 +26041,7 @@ var require_dist6 = __commonJS({
|
|
|
26041
26041
|
CollectionItemSlot.displayName = ITEM_SLOT_NAME;
|
|
26042
26042
|
function useCollection(scope) {
|
|
26043
26043
|
const context = useCollectionContext(name + "CollectionConsumer", scope);
|
|
26044
|
-
const getItems =
|
|
26044
|
+
const getItems = import_react34.default.useCallback(() => {
|
|
26045
26045
|
const collectionNode = context.collectionRef.current;
|
|
26046
26046
|
if (!collectionNode) return [];
|
|
26047
26047
|
const orderedNodes = Array.from(collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`));
|
|
@@ -62393,6 +62393,15 @@ function info_default6({ defineComponent, mappingsToStreams: mappingsToStreams2,
|
|
|
62393
62393
|
global: unique("sourceName")
|
|
62394
62394
|
}
|
|
62395
62395
|
},
|
|
62396
|
+
burstProtection: {
|
|
62397
|
+
advanced: true,
|
|
62398
|
+
help: "Drop data until input is completely stable",
|
|
62399
|
+
hint: {
|
|
62400
|
+
defaultValue: false,
|
|
62401
|
+
type: "boolean",
|
|
62402
|
+
optional: true
|
|
62403
|
+
}
|
|
62404
|
+
},
|
|
62396
62405
|
decodeOutputs: (0, import_client_types3.DecodeOutputsForm)(),
|
|
62397
62406
|
streamMappings: StreamMappingForm(defaultStreamMapping3, {
|
|
62398
62407
|
sourceNames: (cfg) => cfg.streamIds ?? []
|
|
@@ -73377,13 +73386,17 @@ var import_jsx_runtime55 = __toESM(require_jsx_runtime());
|
|
|
73377
73386
|
var import_FaTimes = __toESM(require_FaTimes());
|
|
73378
73387
|
var import_FaPlay2 = __toESM(require_FaPlay());
|
|
73379
73388
|
var import_FaCircle = __toESM(require_FaCircle());
|
|
73380
|
-
|
|
73389
|
+
var import_react25 = __toESM(require_react());
|
|
73390
|
+
function InlineView18({ state, config, sendCommand }) {
|
|
73381
73391
|
const priorityOrder = state.sourcePriority || config.sources;
|
|
73382
|
-
|
|
73392
|
+
const handlePriorityChange = (0, import_react25.useCallback)((newOrder) => {
|
|
73393
|
+
sendCommand({ type: "set-priority-order", sources: newOrder });
|
|
73394
|
+
}, [sendCommand]);
|
|
73395
|
+
return (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [(0, import_jsx_runtime55.jsx)("h5", { className: "text-gray-900 dark:text-white font-medium", children: "Sources:" }), (0, import_jsx_runtime55.jsxs)("ul", { className: "space-y-2 mt-2", children: [priorityOrder.map((s, index3) => {
|
|
73383
73396
|
const isActive = state.activeSource === s;
|
|
73384
73397
|
const isAvailable = state.availableSources.includes(s);
|
|
73385
73398
|
const isOffline = !isActive && !isAvailable;
|
|
73386
|
-
const priorityRank =
|
|
73399
|
+
const priorityRank = index3 + 1;
|
|
73387
73400
|
return (0, import_jsx_runtime55.jsxs)("li", { className: "flex items-center gap-2 w-full", children: [(0, import_jsx_runtime55.jsx)("div", { className: "relative w-4 h-4 flex items-center justify-center flex-shrink-0", children: isOffline ? (
|
|
73388
73401
|
// Red X for offline sources
|
|
73389
73402
|
(0, import_jsx_runtime55.jsx)(import_FaTimes.FaTimes, { className: "w-4 h-4 text-red-500" })
|
|
@@ -73393,7 +73406,15 @@ function InlineView18({ state, config }) {
|
|
|
73393
73406
|
) : (
|
|
73394
73407
|
// Green circle for available but not active
|
|
73395
73408
|
(0, import_jsx_runtime55.jsx)(import_FaCircle.FaCircle, { className: "w-2 h-2 text-green-500" })
|
|
73396
|
-
) }), (0, import_jsx_runtime55.jsx)("span", { className: "text-gray-900 dark:text-white flex-1", children: s }), (0, import_jsx_runtime55.
|
|
73409
|
+
) }), (0, import_jsx_runtime55.jsx)("span", { className: "text-gray-900 dark:text-white flex-1", children: s }), (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center gap-1", children: [index3 > 0 && (0, import_jsx_runtime55.jsx)("button", { onClick: () => {
|
|
73410
|
+
const newOrder = [...priorityOrder];
|
|
73411
|
+
[newOrder[index3], newOrder[index3 - 1]] = [newOrder[index3 - 1], newOrder[index3]];
|
|
73412
|
+
handlePriorityChange(newOrder);
|
|
73413
|
+
}, className: "w-4 h-4 flex items-center justify-center text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200", title: "Move up", children: (0, import_jsx_runtime55.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", className: "w-3 h-3", children: (0, import_jsx_runtime55.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.5 15.75l7.5-7.5 7.5 7.5" }) }) }), index3 < priorityOrder.length - 1 && (0, import_jsx_runtime55.jsx)("button", { onClick: () => {
|
|
73414
|
+
const newOrder = [...priorityOrder];
|
|
73415
|
+
[newOrder[index3], newOrder[index3 + 1]] = [newOrder[index3 + 1], newOrder[index3]];
|
|
73416
|
+
handlePriorityChange(newOrder);
|
|
73417
|
+
}, className: "w-4 h-4 flex items-center justify-center text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200", title: "Move down", children: (0, import_jsx_runtime55.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 2, stroke: "currentColor", className: "w-3 h-3", children: (0, import_jsx_runtime55.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19.5 8.25l-7.5 7.5-7.5-7.5" }) }) }), (0, import_jsx_runtime55.jsx)("span", { className: "text-gray-500 dark:text-gray-400 text-sm w-4 text-center", children: priorityRank })] })] }, s);
|
|
73397
73418
|
}), (0, import_jsx_runtime55.jsxs)("li", { className: "flex items-center gap-2", children: [(0, import_jsx_runtime55.jsx)("div", { className: "relative w-4 h-4 flex items-center justify-center", children: state.activeSource === "fallback" ? (
|
|
73398
73419
|
// Green play button for active fallback
|
|
73399
73420
|
(0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [(0, import_jsx_runtime55.jsx)(import_FaPlay2.FaPlay, { className: "w-3 h-3 text-green-500" }), (0, import_jsx_runtime55.jsx)("div", { className: "absolute inset-0 flex items-center justify-center opacity-30", children: (0, import_jsx_runtime55.jsx)("div", { className: "w-4 h-4 bg-green-500 animate-ping duration-150" }) })] })
|
|
@@ -73406,12 +73427,12 @@ var inline_view_default18 = InlineView18;
|
|
|
73406
73427
|
|
|
73407
73428
|
// build/client/processor.smartSourceSwitch/fullscreen.js
|
|
73408
73429
|
var import_jsx_runtime56 = __toESM(require_jsx_runtime());
|
|
73409
|
-
var
|
|
73430
|
+
var import_react26 = __toESM(require_react());
|
|
73410
73431
|
function FullscreenView2({ state, config, sendCommand }) {
|
|
73411
|
-
const handleMakeActive = (0,
|
|
73432
|
+
const handleMakeActive = (0, import_react26.useCallback)((source) => {
|
|
73412
73433
|
sendCommand({ type: "make-source-active", source });
|
|
73413
73434
|
}, [sendCommand]);
|
|
73414
|
-
const handlePriorityChange = (0,
|
|
73435
|
+
const handlePriorityChange = (0, import_react26.useCallback)((newOrder) => {
|
|
73415
73436
|
sendCommand({ type: "set-priority-order", sources: newOrder });
|
|
73416
73437
|
}, [sendCommand]);
|
|
73417
73438
|
const displaySources = config.sources;
|
|
@@ -73776,9 +73797,9 @@ var import_config11 = __toESM(require_config2());
|
|
|
73776
73797
|
|
|
73777
73798
|
// build/client/processor.syncExternalAudio/inline-view.js
|
|
73778
73799
|
var import_jsx_runtime58 = __toESM(require_jsx_runtime());
|
|
73779
|
-
var
|
|
73800
|
+
var import_react27 = __toESM(require_react());
|
|
73780
73801
|
function InlineView19({ state, config, raise }) {
|
|
73781
|
-
(0,
|
|
73802
|
+
(0, import_react27.useEffect)(() => {
|
|
73782
73803
|
if (raise) {
|
|
73783
73804
|
raise();
|
|
73784
73805
|
}
|
|
@@ -73790,19 +73811,19 @@ var inline_view_default19 = InlineView19;
|
|
|
73790
73811
|
|
|
73791
73812
|
// build/client/processor.syncExternalAudio/fullscreen-view.js
|
|
73792
73813
|
var import_jsx_runtime59 = __toESM(require_jsx_runtime());
|
|
73793
|
-
var
|
|
73814
|
+
var import_react28 = __toESM(require_react());
|
|
73794
73815
|
function SyncExternalAudioFullscreenView({ state, config, sendCommand }) {
|
|
73795
|
-
const videoContainerRef = (0,
|
|
73796
|
-
const whepClientRef = (0,
|
|
73816
|
+
const videoContainerRef = (0, import_react28.useRef)(null);
|
|
73817
|
+
const whepClientRef = (0, import_react28.useRef)(null);
|
|
73797
73818
|
const commentaries = Object.entries(state.commentaries || {});
|
|
73798
|
-
const [selectedCommentary, setSelectedCommentary] = (0,
|
|
73799
|
-
(0,
|
|
73819
|
+
const [selectedCommentary, setSelectedCommentary] = (0, import_react28.useState)(commentaries[0]?.[0] || "");
|
|
73820
|
+
(0, import_react28.useEffect)(() => {
|
|
73800
73821
|
if (commentaries.length > 0 && !state.commentaries[selectedCommentary]) {
|
|
73801
73822
|
setSelectedCommentary(commentaries[0][0]);
|
|
73802
73823
|
}
|
|
73803
73824
|
}, [commentaries, selectedCommentary, state.commentaries]);
|
|
73804
73825
|
const commentary = state.commentaries[selectedCommentary];
|
|
73805
|
-
(0,
|
|
73826
|
+
(0, import_react28.useEffect)(() => {
|
|
73806
73827
|
if (commentary?.whepUrl && videoContainerRef.current) {
|
|
73807
73828
|
if (whepClientRef.current) {
|
|
73808
73829
|
whepClientRef.current = null;
|
|
@@ -74038,11 +74059,11 @@ var summary_view_default11 = SummaryView11;
|
|
|
74038
74059
|
|
|
74039
74060
|
// build/client/processor.videoCompose/fullscreen.js
|
|
74040
74061
|
var import_jsx_runtime66 = __toESM(require_jsx_runtime());
|
|
74041
|
-
var
|
|
74062
|
+
var import_react31 = __toESM(require_react());
|
|
74042
74063
|
|
|
74043
74064
|
// build/client/processor.videoCompose/preset-transition-panel.js
|
|
74044
74065
|
var import_jsx_runtime63 = __toESM(require_jsx_runtime());
|
|
74045
|
-
var
|
|
74066
|
+
var import_react29 = __toESM(require_react());
|
|
74046
74067
|
|
|
74047
74068
|
// build/client/processor.videoCompose/presets-metadata.js
|
|
74048
74069
|
var PRESET_METADATA = {
|
|
@@ -76651,16 +76672,16 @@ function PresetVisualPreview({ layers, size: size4 = "small", referenceResolutio
|
|
|
76651
76672
|
|
|
76652
76673
|
// build/client/processor.videoCompose/preset-transition-panel.js
|
|
76653
76674
|
function PresetTransitionPanel({ state, sendCommand, selectedPreset, onSelectPreset, sourceSelection, onSourceSelectionChange, presetConfig, onPresetConfigChange }) {
|
|
76654
|
-
const [transition, setTransition] = (0,
|
|
76675
|
+
const [transition, setTransition] = (0, import_react29.useState)({
|
|
76655
76676
|
durationMs: 1e3,
|
|
76656
76677
|
easing: "ease_in_out"
|
|
76657
76678
|
});
|
|
76658
|
-
const lastPresetRef = (0,
|
|
76659
|
-
const loadingSceneRef = (0,
|
|
76679
|
+
const lastPresetRef = (0, import_react29.useRef)(null);
|
|
76680
|
+
const loadingSceneRef = (0, import_react29.useRef)(false);
|
|
76660
76681
|
const metadata = selectedPreset ? PRESET_METADATA[selectedPreset] : null;
|
|
76661
76682
|
const reference = state.sources.find((s) => s.sourceName === state.reference);
|
|
76662
|
-
const onlineSourceNames = (0,
|
|
76663
|
-
(0,
|
|
76683
|
+
const onlineSourceNames = (0, import_react29.useMemo)(() => state.sources.filter((s) => s.status === "online").map((s) => s.sourceName).join(","), [state.sources]);
|
|
76684
|
+
(0, import_react29.useEffect)(() => {
|
|
76664
76685
|
if (!selectedPreset || !metadata)
|
|
76665
76686
|
return;
|
|
76666
76687
|
if (lastPresetRef.current === selectedPreset)
|
|
@@ -76693,13 +76714,13 @@ function PresetTransitionPanel({ state, sendCommand, selectedPreset, onSelectPre
|
|
|
76693
76714
|
onPresetConfigChange(defaults2);
|
|
76694
76715
|
}
|
|
76695
76716
|
}, [metadata, onPresetConfigChange, onSourceSelectionChange, onlineSourceNames, reference?.resolution, selectedPreset, state.sources]);
|
|
76696
|
-
(0,
|
|
76717
|
+
(0, import_react29.useEffect)(() => {
|
|
76697
76718
|
if (!selectedPreset || sourceSelection.length === 0)
|
|
76698
76719
|
return;
|
|
76699
76720
|
const storageKey = `videoCompose.preset.${selectedPreset}.sources`;
|
|
76700
76721
|
localStorage.setItem(storageKey, JSON.stringify(sourceSelection));
|
|
76701
76722
|
}, [selectedPreset, sourceSelection]);
|
|
76702
|
-
const handlePreview = (0,
|
|
76723
|
+
const handlePreview = (0, import_react29.useCallback)(() => {
|
|
76703
76724
|
if (!selectedPreset || !metadata)
|
|
76704
76725
|
return;
|
|
76705
76726
|
const layout = {
|
|
@@ -76713,11 +76734,20 @@ function PresetTransitionPanel({ state, sendCommand, selectedPreset, onSelectPre
|
|
|
76713
76734
|
layout
|
|
76714
76735
|
});
|
|
76715
76736
|
}, [selectedPreset, metadata, sourceSelection, presetConfig, sendCommand]);
|
|
76716
|
-
const handleCancelPreview = (0,
|
|
76737
|
+
const handleCancelPreview = (0, import_react29.useCallback)(() => {
|
|
76717
76738
|
sendCommand({ type: "disable-preview" });
|
|
76718
76739
|
}, [sendCommand]);
|
|
76719
|
-
const
|
|
76720
|
-
|
|
76740
|
+
const hasOrchestration = (0, import_react29.useMemo)(() => {
|
|
76741
|
+
if (!selectedPreset)
|
|
76742
|
+
return false;
|
|
76743
|
+
try {
|
|
76744
|
+
const presetDef = PRESETS[selectedPreset]?.(sourceSelection, presetConfig, { width: 1920, height: 1080 });
|
|
76745
|
+
return !!presetDef?.orchestrations;
|
|
76746
|
+
} catch {
|
|
76747
|
+
return false;
|
|
76748
|
+
}
|
|
76749
|
+
}, [selectedPreset, sourceSelection, presetConfig]);
|
|
76750
|
+
const handleApplyPreview = (0, import_react29.useCallback)(() => {
|
|
76721
76751
|
sendCommand({
|
|
76722
76752
|
type: "apply-preview",
|
|
76723
76753
|
// Only pass transition if there's no orchestration
|
|
@@ -76728,8 +76758,8 @@ function PresetTransitionPanel({ state, sendCommand, selectedPreset, onSelectPre
|
|
|
76728
76758
|
}
|
|
76729
76759
|
}
|
|
76730
76760
|
});
|
|
76731
|
-
}, [sendCommand, transition.durationMs, transition.easing,
|
|
76732
|
-
const handleApplyDirect = (0,
|
|
76761
|
+
}, [sendCommand, transition.durationMs, transition.easing, hasOrchestration]);
|
|
76762
|
+
const handleApplyDirect = (0, import_react29.useCallback)(() => {
|
|
76733
76763
|
if (!selectedPreset || !metadata)
|
|
76734
76764
|
return;
|
|
76735
76765
|
const layout = {
|
|
@@ -76738,7 +76768,6 @@ function PresetTransitionPanel({ state, sendCommand, selectedPreset, onSelectPre
|
|
|
76738
76768
|
sourceSelection: sourceSelection.length > 0 ? sourceSelection : void 0,
|
|
76739
76769
|
...metadata.hasConfig ? { config: presetConfig } : {}
|
|
76740
76770
|
};
|
|
76741
|
-
const hasOrchestration = PRESETS[selectedPreset]?.(sourceSelection, presetConfig, { width: 1920, height: 1080 })?.orchestrations;
|
|
76742
76771
|
sendCommand({
|
|
76743
76772
|
type: "update-layout",
|
|
76744
76773
|
layout,
|
|
@@ -76750,8 +76779,8 @@ function PresetTransitionPanel({ state, sendCommand, selectedPreset, onSelectPre
|
|
|
76750
76779
|
}
|
|
76751
76780
|
}
|
|
76752
76781
|
});
|
|
76753
|
-
}, [selectedPreset, metadata, sourceSelection, presetConfig, sendCommand, transition]);
|
|
76754
|
-
const referenceResolution = (0,
|
|
76782
|
+
}, [selectedPreset, metadata, sourceSelection, presetConfig, sendCommand, transition, hasOrchestration]);
|
|
76783
|
+
const referenceResolution = (0, import_react29.useMemo)(() => {
|
|
76755
76784
|
const reference2 = state.sources.find((s) => s.sourceName === state.reference);
|
|
76756
76785
|
return reference2?.resolution;
|
|
76757
76786
|
}, [state.sources, state.reference]);
|
|
@@ -76776,7 +76805,7 @@ function SceneCard({ scene, isActive, onSelect, onDelete }) {
|
|
|
76776
76805
|
}, className: "absolute top-1 right-1 p-1 rounded bg-red-600 text-white hover:bg-red-700\n opacity-0 group-hover:opacity-100 transition-opacity", title: "Delete scene", children: (0, import_jsx_runtime63.jsx)("svg", { className: "w-3 h-3", fill: "currentColor", viewBox: "0 0 20 20", children: (0, import_jsx_runtime63.jsx)("path", { fillRule: "evenodd", d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z", clipRule: "evenodd" }) }) })] });
|
|
76777
76806
|
}
|
|
76778
76807
|
function PresetCard({ presetKey, metadata, onClick, referenceResolution }) {
|
|
76779
|
-
const previewLayout = (0,
|
|
76808
|
+
const previewLayout = (0, import_react29.useMemo)(() => {
|
|
76780
76809
|
if (!referenceResolution)
|
|
76781
76810
|
return null;
|
|
76782
76811
|
const generator = PRESETS[presetKey];
|
|
@@ -76794,7 +76823,7 @@ function PresetCard({ presetKey, metadata, onClick, referenceResolution }) {
|
|
|
76794
76823
|
return (0, import_jsx_runtime63.jsxs)("button", { className: "p-1.5 rounded border border-gray-300 dark:border-gray-600\n hover:border-blue-500 dark:hover:border-blue-400\n hover:bg-blue-50 dark:hover:bg-blue-900/20\n bg-white dark:bg-gray-800\n text-left transition-colors flex flex-col gap-1.5", onClick, children: [(0, import_jsx_runtime63.jsx)("div", { className: "w-full flex justify-center", children: previewLayout && referenceResolution ? (0, import_jsx_runtime63.jsx)(PresetVisualPreview, { layers: previewLayout.layers, size: "small", referenceResolution }) : (0, import_jsx_runtime63.jsx)("div", { className: "w-16 h-9 bg-gray-700 dark:bg-gray-900 rounded flex items-center justify-center", children: (0, import_jsx_runtime63.jsx)("span", { className: "text-xs text-gray-400", children: "?" }) }) }), (0, import_jsx_runtime63.jsxs)("div", { className: "w-full", children: [(0, import_jsx_runtime63.jsx)("h3", { className: "font-medium text-xs text-gray-900 dark:text-white truncate text-center", children: metadata.name }), (0, import_jsx_runtime63.jsxs)("div", { className: "flex items-center justify-center gap-1 mt-0.5", children: [(0, import_jsx_runtime63.jsx)("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: metadata.minSources === metadata.maxSources ? `${metadata.minSources} src${metadata.minSources > 1 ? "s" : ""}` : `${metadata.minSources}-${metadata.maxSources} srcs` }), metadata.hasConfig && (0, import_jsx_runtime63.jsx)("span", { className: "text-xs bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300\n px-1 py-0.5 rounded", children: "Config" })] })] })] });
|
|
76795
76824
|
}
|
|
76796
76825
|
function SaveSceneForm({ onSave, onCancel }) {
|
|
76797
|
-
const [name, setName] = (0,
|
|
76826
|
+
const [name, setName] = (0, import_react29.useState)("");
|
|
76798
76827
|
return (0, import_jsx_runtime63.jsxs)("div", { className: "space-y-2 p-3 bg-gray-50 dark:bg-gray-900 rounded", children: [(0, import_jsx_runtime63.jsx)("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300", children: "Scene Name" }), (0, import_jsx_runtime63.jsx)("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: "Enter scene name...", className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600\n rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-white", autoFocus: true }), (0, import_jsx_runtime63.jsxs)("div", { className: "flex gap-2", children: [(0, import_jsx_runtime63.jsx)("button", { onClick: () => {
|
|
76799
76828
|
if (name.trim()) {
|
|
76800
76829
|
onSave(name.trim());
|
|
@@ -76807,9 +76836,29 @@ function SaveSceneForm({ onSave, onCancel }) {
|
|
|
76807
76836
|
}
|
|
76808
76837
|
function PresetConfiguration(props) {
|
|
76809
76838
|
const { metadata, onPreview, onCancelPreview, onApplyPreview, onApplyDirect, onCancel, isTransitioning, isPreviewActive, sourceSelection, state: _state, sendCommand } = props;
|
|
76810
|
-
const [activeTab, setActiveTab] = (0,
|
|
76811
|
-
const [showSaveForm, setShowSaveForm] = (0,
|
|
76839
|
+
const [activeTab, setActiveTab] = (0, import_react29.useState)("sources");
|
|
76840
|
+
const [showSaveForm, setShowSaveForm] = (0, import_react29.useState)(false);
|
|
76841
|
+
const [isPending, setIsPending] = (0, import_react29.useState)(false);
|
|
76842
|
+
(0, import_react29.useEffect)(() => {
|
|
76843
|
+
if (isTransitioning) {
|
|
76844
|
+
setIsPending(false);
|
|
76845
|
+
}
|
|
76846
|
+
}, [isTransitioning]);
|
|
76847
|
+
(0, import_react29.useEffect)(() => {
|
|
76848
|
+
if (isPending) {
|
|
76849
|
+
const timeout = setTimeout(() => setIsPending(false), 1e4);
|
|
76850
|
+
return () => clearTimeout(timeout);
|
|
76851
|
+
}
|
|
76852
|
+
}, [isPending]);
|
|
76812
76853
|
const canPreview = sourceSelection.length >= metadata.minSources;
|
|
76854
|
+
const handleApplyPreviewWithPending = (0, import_react29.useCallback)(() => {
|
|
76855
|
+
setIsPending(true);
|
|
76856
|
+
onApplyPreview();
|
|
76857
|
+
}, [onApplyPreview]);
|
|
76858
|
+
const handleApplyDirectWithPending = (0, import_react29.useCallback)(() => {
|
|
76859
|
+
setIsPending(true);
|
|
76860
|
+
onApplyDirect();
|
|
76861
|
+
}, [onApplyDirect]);
|
|
76813
76862
|
const handleSaveScene = (name) => {
|
|
76814
76863
|
const layout = {
|
|
76815
76864
|
type: "preset",
|
|
@@ -76820,7 +76869,7 @@ function PresetConfiguration(props) {
|
|
|
76820
76869
|
sendCommand({ type: "create-scene", name, layout });
|
|
76821
76870
|
setShowSaveForm(false);
|
|
76822
76871
|
};
|
|
76823
|
-
return (0, import_jsx_runtime63.jsxs)("div", { className: "h-full flex flex-col", children: [(0, import_jsx_runtime63.jsxs)("div", { className: "p-6 border-b border-gray-200 dark:border-gray-700", children: [(0, import_jsx_runtime63.jsx)("button", { onClick: onCancel, className: "text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700\n dark:hover:text-blue-300 mb-2", children: "\u2190 Back to Presets" }), (0, import_jsx_runtime63.jsx)("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white", children: metadata.name }), (0, import_jsx_runtime63.jsx)("p", { className: "text-sm text-gray-600 dark:text-gray-400 mt-1", children: metadata.description })] }), (0, import_jsx_runtime63.jsx)("div", { className: "flex-1 overflow-y-auto", children: metadata.hasConfig ? (0, import_jsx_runtime63.jsxs)("div", { children: [(0, import_jsx_runtime63.jsx)("div", { className: "border-b border-gray-200 dark:border-gray-700 px-6", children: (0, import_jsx_runtime63.jsxs)("div", { className: "flex gap-6", children: [(0, import_jsx_runtime63.jsx)("button", { onClick: () => setActiveTab("sources"), className: `py-3 border-b-2 font-medium text-sm transition-colors ${activeTab === "sources" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"}`, children: "Source Ordering" }), (0, import_jsx_runtime63.jsx)("button", { onClick: () => setActiveTab("config"), className: `py-3 border-b-2 font-medium text-sm transition-colors ${activeTab === "config" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"}`, children: "Configuration" })] }) }), (0, import_jsx_runtime63.jsx)("div", { className: "p-6", children: activeTab === "sources" ? (0, import_jsx_runtime63.jsx)(SourceOrderingPanel, { ...props }) : (0, import_jsx_runtime63.jsx)(ConfigPanel, { presetKey: props.presetKey, config: props.config, onConfigChange: props.onConfigChange }) })] }) : (0, import_jsx_runtime63.jsx)("div", { className: "p-6", children: (0, import_jsx_runtime63.jsx)(SourceOrderingPanel, { ...props }) }) }), (0, import_jsx_runtime63.jsxs)("div", { className: "border-t border-gray-200 dark:border-gray-700 p-6 space-y-4", children: [showSaveForm ? (0, import_jsx_runtime63.jsx)(SaveSceneForm, { onSave: handleSaveScene, onCancel: () => setShowSaveForm(false) }) : (0, import_jsx_runtime63.jsx)("button", { onClick: () => setShowSaveForm(true), disabled: !canPreview, className: "w-full px-4 py-2 text-sm rounded border-2 border-dashed\n border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400\n hover:border-green-500 hover:text-green-600 dark:hover:border-green-400\n dark:hover:text-green-400 transition-colors\n disabled:opacity-50 disabled:cursor-not-allowed", children: "Save as Scene" }), (0, import_jsx_runtime63.jsx)(PreviewControls, { transition: props.transition, onTransitionChange: props.onTransitionChange, onPreview, onCancelPreview, onApplyPreview, onApplyDirect, isTransitioning, isPreviewActive, canPreview, presetKey: props.presetKey, sourceSelection, config: props.config })] })] });
|
|
76872
|
+
return (0, import_jsx_runtime63.jsxs)("div", { className: "h-full flex flex-col", children: [(0, import_jsx_runtime63.jsxs)("div", { className: "p-6 border-b border-gray-200 dark:border-gray-700", children: [(0, import_jsx_runtime63.jsx)("button", { onClick: onCancel, className: "text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700\n dark:hover:text-blue-300 mb-2", children: "\u2190 Back to Presets" }), (0, import_jsx_runtime63.jsx)("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white", children: metadata.name }), (0, import_jsx_runtime63.jsx)("p", { className: "text-sm text-gray-600 dark:text-gray-400 mt-1", children: metadata.description })] }), (0, import_jsx_runtime63.jsx)("div", { className: "flex-1 overflow-y-auto", children: metadata.hasConfig ? (0, import_jsx_runtime63.jsxs)("div", { children: [(0, import_jsx_runtime63.jsx)("div", { className: "border-b border-gray-200 dark:border-gray-700 px-6", children: (0, import_jsx_runtime63.jsxs)("div", { className: "flex gap-6", children: [(0, import_jsx_runtime63.jsx)("button", { onClick: () => setActiveTab("sources"), className: `py-3 border-b-2 font-medium text-sm transition-colors ${activeTab === "sources" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"}`, children: "Source Ordering" }), (0, import_jsx_runtime63.jsx)("button", { onClick: () => setActiveTab("config"), className: `py-3 border-b-2 font-medium text-sm transition-colors ${activeTab === "config" ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"}`, children: "Configuration" })] }) }), (0, import_jsx_runtime63.jsx)("div", { className: "p-6", children: activeTab === "sources" ? (0, import_jsx_runtime63.jsx)(SourceOrderingPanel, { ...props }) : (0, import_jsx_runtime63.jsx)(ConfigPanel, { presetKey: props.presetKey, config: props.config, onConfigChange: props.onConfigChange }) })] }) : (0, import_jsx_runtime63.jsx)("div", { className: "p-6", children: (0, import_jsx_runtime63.jsx)(SourceOrderingPanel, { ...props }) }) }), (0, import_jsx_runtime63.jsxs)("div", { className: "border-t border-gray-200 dark:border-gray-700 p-6 space-y-4", children: [showSaveForm ? (0, import_jsx_runtime63.jsx)(SaveSceneForm, { onSave: handleSaveScene, onCancel: () => setShowSaveForm(false) }) : (0, import_jsx_runtime63.jsx)("button", { onClick: () => setShowSaveForm(true), disabled: !canPreview, className: "w-full px-4 py-2 text-sm rounded border-2 border-dashed\n border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400\n hover:border-green-500 hover:text-green-600 dark:hover:border-green-400\n dark:hover:text-green-400 transition-colors\n disabled:opacity-50 disabled:cursor-not-allowed", children: "Save as Scene" }), (0, import_jsx_runtime63.jsx)(PreviewControls, { transition: props.transition, onTransitionChange: props.onTransitionChange, onPreview, onCancelPreview, onApplyPreview: handleApplyPreviewWithPending, onApplyDirect: handleApplyDirectWithPending, isTransitioning, isPreviewActive, canPreview, presetKey: props.presetKey, sourceSelection, config: props.config, isPending })] })] });
|
|
76824
76873
|
}
|
|
76825
76874
|
function SourceOrderingPanel({ metadata, sourceSelection, onSourceSelectionChange, availableSources }) {
|
|
76826
76875
|
const usedSourceNames = new Set(sourceSelection);
|
|
@@ -76878,8 +76927,9 @@ function ConfigPanel({ presetKey, config, onConfigChange }) {
|
|
|
76878
76927
|
}
|
|
76879
76928
|
return null;
|
|
76880
76929
|
}
|
|
76881
|
-
function PreviewControls({ transition, onTransitionChange, onPreview, onCancelPreview, onApplyPreview, onApplyDirect, isTransitioning, isPreviewActive, canPreview, presetKey, sourceSelection, config }) {
|
|
76882
|
-
const
|
|
76930
|
+
function PreviewControls({ transition, onTransitionChange, onPreview, onCancelPreview, onApplyPreview, onApplyDirect, isTransitioning, isPreviewActive, canPreview, presetKey, sourceSelection, config, isPending }) {
|
|
76931
|
+
const isBusy = isPending || isTransitioning;
|
|
76932
|
+
const hasOrchestration = (0, import_react29.useMemo)(() => {
|
|
76883
76933
|
try {
|
|
76884
76934
|
const presetDef = PRESETS[presetKey]?.(sourceSelection, config, { width: 1920, height: 1080 });
|
|
76885
76935
|
return !!presetDef?.orchestrations;
|
|
@@ -76893,22 +76943,22 @@ function PreviewControls({ transition, onTransitionChange, onPreview, onCancelPr
|
|
|
76893
76943
|
}), className: "w-full accent-blue-600" }), (0, import_jsx_runtime63.jsxs)("div", { className: "flex justify-between text-xs text-gray-500", children: [(0, import_jsx_runtime63.jsx)("span", { children: "Instant" }), (0, import_jsx_runtime63.jsx)("span", { children: "3s" })] })] }), (0, import_jsx_runtime63.jsxs)("div", { className: "space-y-2", children: [(0, import_jsx_runtime63.jsx)("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300", children: "Easing" }), (0, import_jsx_runtime63.jsxs)("select", { value: transition.easing, onChange: (e) => onTransitionChange({
|
|
76894
76944
|
...transition,
|
|
76895
76945
|
easing: e.target.value
|
|
76896
|
-
}), className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600\n rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-white", children: [(0, import_jsx_runtime63.jsx)("option", { value: "linear", children: "Linear" }), (0, import_jsx_runtime63.jsx)("option", { value: "ease_in", children: "Ease In" }), (0, import_jsx_runtime63.jsx)("option", { value: "ease_out", children: "Ease Out" }), (0, import_jsx_runtime63.jsx)("option", { value: "ease_in_out", children: "Ease In Out" })] })] })] }), (0, import_jsx_runtime63.jsxs)("div", { className: "space-y-2", children: [(0, import_jsx_runtime63.jsx)("button", { className: "w-full px-4 py-3 rounded-lg font-medium\n bg-green-600 text-white hover:bg-green-700\n disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors", onClick: isPreviewActive ? onApplyPreview : onApplyDirect, disabled: !canPreview ||
|
|
76946
|
+
}), className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600\n rounded bg-white dark:bg-gray-800 text-gray-900 dark:text-white", children: [(0, import_jsx_runtime63.jsx)("option", { value: "linear", children: "Linear" }), (0, import_jsx_runtime63.jsx)("option", { value: "ease_in", children: "Ease In" }), (0, import_jsx_runtime63.jsx)("option", { value: "ease_out", children: "Ease Out" }), (0, import_jsx_runtime63.jsx)("option", { value: "ease_in_out", children: "Ease In Out" })] })] })] }), (0, import_jsx_runtime63.jsxs)("div", { className: "space-y-2", children: [(0, import_jsx_runtime63.jsx)("button", { className: "w-full px-4 py-3 rounded-lg font-medium\n bg-green-600 text-white hover:bg-green-700\n disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors", onClick: isPreviewActive ? onApplyPreview : onApplyDirect, disabled: !canPreview || isBusy, children: isBusy ? "Transitioning..." : "Take to Live" }), isPreviewActive ? (0, import_jsx_runtime63.jsx)("button", { className: "w-full px-4 py-2 rounded-lg font-medium\n bg-red-600 text-white hover:bg-red-700\n transition-colors", onClick: onCancelPreview, children: "Cancel Preview" }) : (0, import_jsx_runtime63.jsx)("button", { className: "w-full px-4 py-2 rounded-lg font-medium\n border border-blue-600 text-blue-600 dark:text-blue-400\n hover:bg-blue-50 dark:hover:bg-blue-900/20\n disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors", onClick: onPreview, disabled: !canPreview || isBusy, children: "Preview Video" })] })] });
|
|
76897
76947
|
}
|
|
76898
76948
|
|
|
76899
76949
|
// build/client/processor.videoCompose/webrtc-preview.js
|
|
76900
76950
|
var import_jsx_runtime64 = __toESM(require_jsx_runtime());
|
|
76901
|
-
var
|
|
76951
|
+
var import_react30 = __toESM(require_react());
|
|
76902
76952
|
var import_webrtc_client2 = __toESM(require_webrtc_client());
|
|
76903
76953
|
function WebRTCPreview({ url, id, onPlaying }) {
|
|
76904
|
-
const [_client, setClient] = (0,
|
|
76905
|
-
const [error, setError] = (0,
|
|
76906
|
-
const [connecting, setConnecting] = (0,
|
|
76907
|
-
const onPlayingRef = (0,
|
|
76908
|
-
(0,
|
|
76954
|
+
const [_client, setClient] = (0, import_react30.useState)(null);
|
|
76955
|
+
const [error, setError] = (0, import_react30.useState)(null);
|
|
76956
|
+
const [connecting, setConnecting] = (0, import_react30.useState)(false);
|
|
76957
|
+
const onPlayingRef = (0, import_react30.useRef)(onPlaying);
|
|
76958
|
+
(0, import_react30.useEffect)(() => {
|
|
76909
76959
|
onPlayingRef.current = onPlaying;
|
|
76910
76960
|
}, [onPlaying]);
|
|
76911
|
-
const createClient = (0,
|
|
76961
|
+
const createClient = (0, import_react30.useCallback)((url2) => {
|
|
76912
76962
|
console.log(`[WebRTCPreview ${id}] Creating WHEP client for URL:`, url2);
|
|
76913
76963
|
setConnecting(true);
|
|
76914
76964
|
const client = new import_webrtc_client2.WhepClient({
|
|
@@ -76949,7 +76999,7 @@ function WebRTCPreview({ url, id, onPlaying }) {
|
|
|
76949
76999
|
});
|
|
76950
77000
|
return client;
|
|
76951
77001
|
}, [id]);
|
|
76952
|
-
const cleanupClient = (0,
|
|
77002
|
+
const cleanupClient = (0, import_react30.useCallback)(() => {
|
|
76953
77003
|
setClient((client) => {
|
|
76954
77004
|
if (!client)
|
|
76955
77005
|
return null;
|
|
@@ -76966,7 +77016,7 @@ function WebRTCPreview({ url, id, onPlaying }) {
|
|
|
76966
77016
|
setError(null);
|
|
76967
77017
|
setConnecting(false);
|
|
76968
77018
|
}, []);
|
|
76969
|
-
(0,
|
|
77019
|
+
(0, import_react30.useEffect)(() => {
|
|
76970
77020
|
if (url) {
|
|
76971
77021
|
setError(null);
|
|
76972
77022
|
const client = createClient(url);
|
|
@@ -77012,7 +77062,9 @@ function VisualPreview({ layout, sourceJpegUrls, reference, title }) {
|
|
|
77012
77062
|
height: `${heightPercent}%`,
|
|
77013
77063
|
opacity: layer.opacity,
|
|
77014
77064
|
zIndex: layer.zIndex
|
|
77015
|
-
}, children: [jpegUrl ? (0, import_jsx_runtime65.jsx)("img", { src: jpegUrl, alt: layer.sourceName, className: "w-full h-full object-cover",
|
|
77065
|
+
}, children: [jpegUrl ? (0, import_jsx_runtime65.jsx)("img", { src: jpegUrl, alt: layer.sourceName, className: "w-full h-full object-cover", loading: "lazy", decoding: "async", onLoad: (e) => {
|
|
77066
|
+
e.currentTarget.style.display = "";
|
|
77067
|
+
}, onError: (e) => {
|
|
77016
77068
|
e.currentTarget.style.display = "none";
|
|
77017
77069
|
} }) : (0, import_jsx_runtime65.jsx)("div", { className: "w-full h-full bg-gray-700 flex items-center justify-center", children: (0, import_jsx_runtime65.jsxs)("div", { className: "text-center p-2", children: [(0, import_jsx_runtime65.jsx)("div", { className: "text-xs text-gray-400 truncate", children: layer.sourceName }), (0, import_jsx_runtime65.jsx)("div", { className: "text-xs text-gray-500 mt-1", children: "No preview" })] }) }), (0, import_jsx_runtime65.jsx)("div", { className: "absolute top-1 left-1 px-1.5 py-0.5\n bg-black/60 rounded text-xs text-white\n font-mono", children: layer.sourceName }), (0, import_jsx_runtime65.jsx)("div", { className: "absolute top-1 right-1 w-5 h-5\n bg-blue-600 rounded-full flex items-center justify-center\n text-xs text-white font-semibold", children: layer.zIndex }), layer.opacity < 1 && (0, import_jsx_runtime65.jsxs)("div", { className: "absolute bottom-1 right-1 px-1.5 py-0.5\n bg-black/60 rounded text-xs text-white", children: [(layer.opacity * 100).toFixed(0), "%"] })] }, idx);
|
|
77018
77070
|
}) })] });
|
|
@@ -77020,19 +77072,34 @@ function VisualPreview({ layout, sourceJpegUrls, reference, title }) {
|
|
|
77020
77072
|
|
|
77021
77073
|
// build/client/processor.videoCompose/fullscreen.js
|
|
77022
77074
|
function FullscreenView3({ state, config: _config, sendCommand }) {
|
|
77023
|
-
const [showVideoPreview, setShowVideoPreview] = (0,
|
|
77024
|
-
const [pendingPreviewCommand, setPendingPreviewCommand] = (0,
|
|
77025
|
-
const [selectedPreset, setSelectedPreset] = (0,
|
|
77026
|
-
const [sourceSelection, setSourceSelection] = (0,
|
|
77027
|
-
const [presetConfig, setPresetConfig] = (0,
|
|
77028
|
-
const prevConfigRef = (0,
|
|
77029
|
-
const
|
|
77075
|
+
const [showVideoPreview, setShowVideoPreview] = (0, import_react31.useState)(false);
|
|
77076
|
+
const [pendingPreviewCommand, setPendingPreviewCommand] = (0, import_react31.useState)(null);
|
|
77077
|
+
const [selectedPreset, setSelectedPreset] = (0, import_react31.useState)(null);
|
|
77078
|
+
const [sourceSelection, setSourceSelection] = (0, import_react31.useState)([]);
|
|
77079
|
+
const [presetConfig, setPresetConfig] = (0, import_react31.useState)({});
|
|
77080
|
+
const prevConfigRef = (0, import_react31.useRef)({ selectedPreset, sourceSelection, presetConfig });
|
|
77081
|
+
const [throttledJpegUrls, setThrottledJpegUrls] = (0, import_react31.useState)({});
|
|
77082
|
+
const lastJpegUpdateRef = (0, import_react31.useRef)(0);
|
|
77083
|
+
(0, import_react31.useEffect)(() => {
|
|
77084
|
+
const now = Date.now();
|
|
77085
|
+
if (now - lastJpegUpdateRef.current > 2e3) {
|
|
77086
|
+
lastJpegUpdateRef.current = now;
|
|
77087
|
+
setThrottledJpegUrls(state.sourceJpegUrls ?? {});
|
|
77088
|
+
}
|
|
77089
|
+
}, [state.sourceJpegUrls]);
|
|
77090
|
+
const [imagesReady, setImagesReady] = (0, import_react31.useState)(false);
|
|
77091
|
+
(0, import_react31.useEffect)(() => {
|
|
77092
|
+
const timer = setTimeout(() => setImagesReady(true), 100);
|
|
77093
|
+
return () => clearTimeout(timer);
|
|
77094
|
+
}, []);
|
|
77095
|
+
const onlineSourceCount = (0, import_react31.useMemo)(() => state.sources.filter((s) => s.status === "online").length, [state.sources]);
|
|
77096
|
+
const handleSourceSelectionChange = (0, import_react31.useCallback)((sources) => {
|
|
77030
77097
|
setSourceSelection(sources);
|
|
77031
77098
|
}, []);
|
|
77032
|
-
const handlePresetConfigChange = (0,
|
|
77099
|
+
const handlePresetConfigChange = (0, import_react31.useCallback)((config) => {
|
|
77033
77100
|
setPresetConfig(config);
|
|
77034
77101
|
}, []);
|
|
77035
|
-
(0,
|
|
77102
|
+
(0, import_react31.useEffect)(() => {
|
|
77036
77103
|
const prev = prevConfigRef.current;
|
|
77037
77104
|
const configChanged = prev.selectedPreset !== selectedPreset || JSON.stringify(prev.sourceSelection) !== JSON.stringify(sourceSelection) || JSON.stringify(prev.presetConfig) !== JSON.stringify(presetConfig);
|
|
77038
77105
|
if (configChanged) {
|
|
@@ -77044,23 +77111,20 @@ function FullscreenView3({ state, config: _config, sendCommand }) {
|
|
|
77044
77111
|
prevConfigRef.current = { selectedPreset, sourceSelection, presetConfig };
|
|
77045
77112
|
}
|
|
77046
77113
|
}, [selectedPreset, sourceSelection, presetConfig, state.previewMode, sendCommand]);
|
|
77047
|
-
const handlePreviewPlaying = (0,
|
|
77114
|
+
const handlePreviewPlaying = (0, import_react31.useCallback)(() => {
|
|
77048
77115
|
console.log("[FullscreenView] Preview video started playing, sending pending command");
|
|
77049
77116
|
if (pendingPreviewCommand) {
|
|
77050
77117
|
sendCommand(pendingPreviewCommand);
|
|
77051
77118
|
setPendingPreviewCommand(null);
|
|
77052
77119
|
}
|
|
77053
77120
|
}, [pendingPreviewCommand, sendCommand]);
|
|
77054
|
-
const handleSendCommand = (0,
|
|
77121
|
+
const handleSendCommand = (0, import_react31.useCallback)((cmd) => {
|
|
77055
77122
|
if (cmd.type === "enable-preview") {
|
|
77056
|
-
console.log("[FullscreenView] Deferring enable-preview until video starts playing");
|
|
77057
77123
|
setShowVideoPreview(true);
|
|
77058
77124
|
setPendingPreviewCommand(cmd);
|
|
77059
77125
|
setTimeout(() => {
|
|
77060
|
-
console.log("[FullscreenView] Timeout reached, checking if command still pending");
|
|
77061
77126
|
setPendingPreviewCommand((pending) => {
|
|
77062
77127
|
if (pending) {
|
|
77063
|
-
console.log("[FullscreenView] Sending command due to timeout");
|
|
77064
77128
|
sendCommand(pending);
|
|
77065
77129
|
return null;
|
|
77066
77130
|
}
|
|
@@ -77075,22 +77139,26 @@ function FullscreenView3({ state, config: _config, sendCommand }) {
|
|
|
77075
77139
|
sendCommand(cmd);
|
|
77076
77140
|
}
|
|
77077
77141
|
}, [sendCommand]);
|
|
77078
|
-
const
|
|
77142
|
+
const referenceResolution = (0, import_react31.useMemo)(() => {
|
|
77143
|
+
const ref = state.sources.find((s) => s.sourceName === state.reference);
|
|
77144
|
+
return ref?.resolution;
|
|
77145
|
+
}, [state.sources, state.reference]);
|
|
77146
|
+
const referenceResolutionKey = referenceResolution ? `${referenceResolution.width}x${referenceResolution.height}` : null;
|
|
77147
|
+
const previewLayout = (0, import_react31.useMemo)(() => {
|
|
77079
77148
|
if (!selectedPreset || sourceSelection.length === 0)
|
|
77080
77149
|
return void 0;
|
|
77081
|
-
|
|
77082
|
-
if (!reference?.resolution)
|
|
77150
|
+
if (!referenceResolution)
|
|
77083
77151
|
return void 0;
|
|
77084
77152
|
const generator = PRESETS[selectedPreset];
|
|
77085
77153
|
if (!generator)
|
|
77086
77154
|
return void 0;
|
|
77087
77155
|
try {
|
|
77088
|
-
const presetDef = generator(sourceSelection, presetConfig, { width:
|
|
77156
|
+
const presetDef = generator(sourceSelection, presetConfig, { width: referenceResolution.width, height: referenceResolution.height });
|
|
77089
77157
|
return presetDef.finalConfig;
|
|
77090
77158
|
} catch (_e) {
|
|
77091
77159
|
return void 0;
|
|
77092
77160
|
}
|
|
77093
|
-
})
|
|
77161
|
+
}, [selectedPreset, sourceSelection, presetConfig, referenceResolutionKey]);
|
|
77094
77162
|
if (!state) {
|
|
77095
77163
|
return (0, import_jsx_runtime66.jsx)("div", { className: "flex h-full items-center justify-center bg-gray-50 dark:bg-gray-900", children: (0, import_jsx_runtime66.jsxs)("div", { className: "text-center", children: [(0, import_jsx_runtime66.jsx)("div", { className: "text-xl font-semibold text-gray-900 dark:text-white mb-2", children: "Loading Video Compose..." }), (0, import_jsx_runtime66.jsx)("div", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Connecting to component state..." })] }) });
|
|
77096
77164
|
}
|
|
@@ -77101,7 +77169,7 @@ function FullscreenView3({ state, config: _config, sendCommand }) {
|
|
|
77101
77169
|
}, sendCommand: handleSendCommand, selectedPreset, onSelectPreset: setSelectedPreset, sourceSelection, onSourceSelectionChange: handleSourceSelectionChange, presetConfig, onPresetConfigChange: handlePresetConfigChange }) }), (0, import_jsx_runtime66.jsxs)("div", { className: "flex-1 flex flex-col", children: [(0, import_jsx_runtime66.jsxs)("div", { className: "flex-1 flex gap-6 p-6 min-h-0", children: [(0, import_jsx_runtime66.jsxs)("div", { className: "flex-1 flex flex-col min-w-0", children: [(0, import_jsx_runtime66.jsx)("div", { className: "flex items-center justify-between mb-4", children: (0, import_jsx_runtime66.jsxs)("div", { className: "flex items-center gap-3", children: [(0, import_jsx_runtime66.jsx)("h2", { className: "text-xl font-bold text-gray-900 dark:text-white", children: showVideoPreview ? "Preview Transition" : "Preview Output" }), state.previewTransitioning && (0, import_jsx_runtime66.jsx)("span", { className: "text-sm text-blue-600 dark:text-blue-400 font-medium", children: "Transitioning..." })] }) }), showVideoPreview ? (0, import_jsx_runtime66.jsx)(WebRTCPreview, { url: state.previewPreviewUrl, id: `${_config.id}-preview`, onPlaying: handlePreviewPlaying }, state.previewPreviewUrl) : (0, import_jsx_runtime66.jsx)("div", { className: "flex-1 flex items-center justify-center bg-gray-900 rounded-lg overflow-hidden", children: (0, import_jsx_runtime66.jsx)("div", { className: "w-full h-full flex items-center justify-center p-4", children: (0, import_jsx_runtime66.jsx)("div", { className: "w-full max-w-full max-h-full", children: (0, import_jsx_runtime66.jsx)(VisualPreview, { layout: (
|
|
77102
77170
|
// Show preset being built, or preview composition, or current layout
|
|
77103
77171
|
previewLayout ? previewLayout : state.previewMode && state.previewResolvedLayout ? state.previewResolvedLayout : state.resolvedLayout
|
|
77104
|
-
), sourceJpegUrls:
|
|
77172
|
+
), sourceJpegUrls: imagesReady ? throttledJpegUrls : {}, reference: referenceResolution, title: previewLayout ? "Preset Preview (Building...)" : state.previewMode ? "Preview Composition" : "Current Layout" }) }) }) })] }), (0, import_jsx_runtime66.jsxs)("div", { className: "flex-1 flex flex-col min-w-0", children: [(0, import_jsx_runtime66.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, import_jsx_runtime66.jsx)("h2", { className: "text-xl font-bold text-gray-900 dark:text-white", children: "Live Output" }), state.transitioning && (0, import_jsx_runtime66.jsx)("span", { className: "text-sm text-blue-600 dark:text-blue-400 font-medium", children: "Transitioning..." }), !state.previewUrl && !state.referenceAvailable && (0, import_jsx_runtime66.jsx)("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Waiting for reference stream..." })] }), (0, import_jsx_runtime66.jsx)(WebRTCPreview, { url: state.previewUrl, id: `${_config.id}-live` })] })] }), (0, import_jsx_runtime66.jsxs)("div", { className: "border-t border-gray-200 dark:border-gray-700\n bg-white dark:bg-gray-800 h-64 flex flex-col", children: [(0, import_jsx_runtime66.jsx)("div", { className: "p-4 border-b border-gray-200 dark:border-gray-700", children: (0, import_jsx_runtime66.jsx)("h2", { className: "text-lg font-bold text-gray-900 dark:text-white", children: "Status" }) }), (0, import_jsx_runtime66.jsxs)("div", { className: "flex-1 overflow-y-auto p-4 space-y-4", children: [(0, import_jsx_runtime66.jsxs)("div", { className: "space-y-2", children: [(0, import_jsx_runtime66.jsxs)("div", { className: "flex items-center justify-between", children: [(0, import_jsx_runtime66.jsx)("h3", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: "Sources" }), (0, import_jsx_runtime66.jsxs)("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [onlineSourceCount, " / ", state.sources.length, " online"] })] }), (0, import_jsx_runtime66.jsx)("div", { className: "space-y-1", children: state.sources.map((source, i) => (0, import_jsx_runtime66.jsxs)("div", { className: "flex items-center gap-2 p-1.5 rounded bg-gray-50 dark:bg-gray-900", children: [(0, import_jsx_runtime66.jsx)("div", { className: `w-1.5 h-1.5 rounded-full flex-shrink-0 ${source.status === "online" ? "bg-live" : "bg-offline"}` }), (0, import_jsx_runtime66.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, import_jsx_runtime66.jsxs)("div", { className: `text-xs font-medium ${source.sourceName === state.reference ? "text-blue-600 dark:text-blue-400" : "text-gray-900 dark:text-white"}`, children: [source.sourceName, source.sourceName === state.reference && " (ref)"] }), source.resolution && (0, import_jsx_runtime66.jsxs)("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: [source.resolution.width, "\xD7", source.resolution.height, source.frameRate && ` @ ${source.frameRate.frames}/${source.frameRate.seconds}fps`] })] })] }, i)) })] }), state.resolvedLayout && state.resolvedLayout.layers.length > 0 && (0, import_jsx_runtime66.jsxs)("div", { className: "space-y-2", children: [(0, import_jsx_runtime66.jsxs)("div", { children: [(0, import_jsx_runtime66.jsx)("h3", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: "Active Layout" }), (0, import_jsx_runtime66.jsxs)("div", { className: "text-xs text-gray-600 dark:text-gray-400 mt-0.5", children: [state.layout?.type === "preset" ? state.layout.preset : "Custom", " \u2022 ", state.resolvedLayout.layers.length, " ", state.resolvedLayout.layers.length === 1 ? "layer" : "layers"] })] }), (0, import_jsx_runtime66.jsx)("div", { className: "space-y-1", children: state.resolvedLayout.layers.map((layer, i) => (0, import_jsx_runtime66.jsxs)("div", { className: "text-xs p-1.5 rounded bg-gray-50 dark:bg-gray-900", children: [(0, import_jsx_runtime66.jsxs)("div", { className: "font-medium text-gray-900 dark:text-white", children: ["L", layer.zIndex, ": ", layer.sourceName] }), (0, import_jsx_runtime66.jsxs)("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: [(layer.opacity * 100).toFixed(0), "%", layer.destRect && (0, import_jsx_runtime66.jsxs)(import_jsx_runtime66.Fragment, { children: [" \u2022 (", layer.destRect.x.toFixed(0), ", ", layer.destRect.y.toFixed(0), ")"] })] })] }, i)) })] }), state.transitioning && (0, import_jsx_runtime66.jsxs)("div", { className: "space-y-1", children: [(0, import_jsx_runtime66.jsx)("h3", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: "Transition" }), (0, import_jsx_runtime66.jsx)("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: "In progress..." })] })] })] })] })] });
|
|
77105
77173
|
}
|
|
77106
77174
|
var fullscreen_default2 = FullscreenView3;
|
|
77107
77175
|
|
|
@@ -77733,7 +77801,7 @@ var import_util15 = __toESM(require_util());
|
|
|
77733
77801
|
|
|
77734
77802
|
// build/client/processor.webRtcDuplex/fullscreen.js
|
|
77735
77803
|
var import_jsx_runtime71 = __toESM(require_jsx_runtime());
|
|
77736
|
-
var
|
|
77804
|
+
var import_react32 = __toESM(require_react());
|
|
77737
77805
|
var import_webrtc_client3 = __toESM(require_webrtc_client());
|
|
77738
77806
|
var DuplexWhepClient = class extends NorskWhepClient {
|
|
77739
77807
|
showVideo;
|
|
@@ -77774,11 +77842,11 @@ var DuplexWhipClient = class extends import_webrtc_client3.WhipClient {
|
|
|
77774
77842
|
}
|
|
77775
77843
|
};
|
|
77776
77844
|
function FullscreenView4({ state, config }) {
|
|
77777
|
-
const [client, setClient] = (0,
|
|
77778
|
-
const [sender, setSender] = (0,
|
|
77779
|
-
const container = (0,
|
|
77780
|
-
const button = (0,
|
|
77781
|
-
const go = (0,
|
|
77845
|
+
const [client, setClient] = (0, import_react32.useState)(void 0);
|
|
77846
|
+
const [sender, setSender] = (0, import_react32.useState)(void 0);
|
|
77847
|
+
const container = (0, import_react32.useRef)(null);
|
|
77848
|
+
const button = (0, import_react32.useRef)(null);
|
|
77849
|
+
const go = (0, import_react32.useCallback)(async () => {
|
|
77782
77850
|
if (!state.publishUrl)
|
|
77783
77851
|
return;
|
|
77784
77852
|
if (!client && state.outputUrl && container.current) {
|
|
@@ -78070,7 +78138,7 @@ function assertUnreachable30(_) {
|
|
|
78070
78138
|
|
|
78071
78139
|
// build/client/util.stats.latency/inline-view.js
|
|
78072
78140
|
var import_jsx_runtime73 = __toESM(require_jsx_runtime());
|
|
78073
|
-
var
|
|
78141
|
+
var import_react33 = __toESM(require_react());
|
|
78074
78142
|
|
|
78075
78143
|
// ../../node_modules/chart.js/auto/auto.js
|
|
78076
78144
|
Chart.register(...registerables);
|
|
@@ -78078,8 +78146,8 @@ var auto_default = Chart;
|
|
|
78078
78146
|
|
|
78079
78147
|
// build/client/util.stats.latency/inline-view.js
|
|
78080
78148
|
function InlineView22({ state, config: _2 }) {
|
|
78081
|
-
const chartContainer = (0,
|
|
78082
|
-
const [chartControl, setChartControl] = (0,
|
|
78149
|
+
const chartContainer = (0, import_react33.useRef)(null);
|
|
78150
|
+
const [chartControl, setChartControl] = (0, import_react33.useState)(void 0);
|
|
78083
78151
|
function makeDataSet(key, color2, values) {
|
|
78084
78152
|
return {
|
|
78085
78153
|
label: key,
|
|
@@ -78090,13 +78158,13 @@ function InlineView22({ state, config: _2 }) {
|
|
|
78090
78158
|
data: values
|
|
78091
78159
|
};
|
|
78092
78160
|
}
|
|
78093
|
-
const makeData = (0,
|
|
78161
|
+
const makeData = (0, import_react33.useCallback)((state2) => {
|
|
78094
78162
|
return {
|
|
78095
78163
|
labels: new Array(state2.values.length).fill(0).map((_, i) => i).map((_) => ""),
|
|
78096
78164
|
datasets: [makeDataSet("latency", "rgba(255, 0, 0, 255)", state2.values)]
|
|
78097
78165
|
};
|
|
78098
78166
|
}, []);
|
|
78099
|
-
(0,
|
|
78167
|
+
(0, import_react33.useEffect)(() => {
|
|
78100
78168
|
if (!chartContainer.current)
|
|
78101
78169
|
return;
|
|
78102
78170
|
auto_default.defaults.color = "#FFF";
|
|
@@ -78134,7 +78202,7 @@ function InlineView22({ state, config: _2 }) {
|
|
|
78134
78202
|
chart.update();
|
|
78135
78203
|
}, 100);
|
|
78136
78204
|
}, [chartContainer, makeData, state]);
|
|
78137
|
-
(0,
|
|
78205
|
+
(0, import_react33.useEffect)(() => {
|
|
78138
78206
|
if (!chartControl)
|
|
78139
78207
|
return;
|
|
78140
78208
|
chartControl.data = makeData(state);
|