{/* Main content wrapper - takes remaining space when panel is open */}
{/* Header */}
{t("streamCrafter")}
{statusText}
{/* Content area (preview + mixer) - responsive layout */}
{/* Preview wrapper for flex sizing */}
{/* Video Preview (flush - no padding) */}
{/* Empty State */}
{!mediaStream && (
{t("addSourcePrompt")}
)}
{/* Status Overlay - Connecting/Reconnecting */}
{(state === "connecting" || state === "reconnecting") && (
)}
{/* Live Badge */}
{isStreaming &&
{t("live")}
}
{/* Compositor Controls Overlay (inside preview for positioning) */}
{enableCompositor && (
compositor.cycleSourceOrder(direction)}
/>
)}
{/* Sources Mixer Section - moves to right on wide screens */}
{sources.length > 0 && (
setShowSources(!showSources)}
title={showSources ? t("collapseMixer") : t("expandMixer")}
>
{t("mixer")} ({sources.length})
{showSources ? (
) : (
)}
{showSources && (
{sources.map((source: MediaSource) => {
// Get visibility state for compositor mode
const layer = compositor.activeScene?.layers.find(
(l) => l.sourceId === source.id
);
const isVisibleInCompositor = layer?.visible ?? false;
return (
toggleSourceMute(source.id, source.muted)}
onVolumeChange={(vol) => setSourceVolume(source.id, vol)}
onSetPrimary={() => setPrimaryVideoSource(source.id)}
onRemove={() => removeSource(source.id)}
disabled={false}
isStreaming={isStreaming}
hasVideo={source.stream.getVideoTracks().length > 0}
t={t}
isCompositorEnabled={enableCompositor}
isVisibleInCompositor={isVisibleInCompositor}
onVisibilityToggle={() => {
if (!compositor.activeSceneId) return;
if (layer) {
compositor.setLayerVisibility(
compositor.activeSceneId,
layer.id,
!layer.visible
);
} else {
compositor.addLayer(compositor.activeSceneId, source.id);
}
}}
/>
);
})}
)}
)}
{/* VU Meter (horizontal bar under content area) */}
{isCapturing &&
}
{/* Error Display */}
{(error || endpointError) && (
{t("error")}
{error || endpointError}
)}
{/* No Endpoint Warning */}
{!resolvedWhipUrl && !error && !endpointError && !isResolvingEndpoint && (
{t("warning")}
{t("configureWhipEndpoint")}
)}
{/* Resolving Endpoint State */}
{isResolvingEndpoint && (
{t("resolvingEndpoint")}
{t("resolvingEndpoint")}
)}
{/* Action Bar */}
{/* Secondary actions: Camera, Screen, Settings */}
{/* Settings button in action bar */}
setShowSettings(!showSettings)}
title={t("settings")}
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
>
{/* Settings Popup - positioned above button */}
{showSettings && (
{/* Quality Section */}
{t("quality")}
{qualityProfiles.map((p) => (
{
if (!isStreaming) {
setQualityProfile(p.id);
if (!devMode) setShowSettings(false);
}
}}
disabled={isStreaming}
style={{
width: "100%",
padding: "6px 8px",
textAlign: "left",
fontSize: "12px",
borderRadius: "4px",
transition: "all 0.15s",
border: "none",
cursor: isStreaming ? "not-allowed" : "pointer",
opacity: isStreaming ? 0.5 : 1,
background:
qualityProfile === p.id
? "rgba(122, 162, 247, 0.2)"
: "transparent",
color: qualityProfile === p.id ? "#7aa2f7" : "#a9b1d6",
}}
>
{p.label}
{p.description}
))}
{/* Dev Info Section */}
{devMode && (
{t("debug")}
{t("state")}
{state}
{t("audio")}
{isMonitoring ? t("active") : t("inactive")}
{t("whip")}
{resolvedWhipUrl ? t("ok") : t("notSet")}
)}
)}
{/* Primary action: Go Live / Stop */}
{!isStreaming ? (
{state === "connecting" ? t("connecting") : t("goLive")}
) : (
{t("stopStreaming")}
)}
{/* Advanced Panel - side panel when open */}
{devMode && isAdvancedPanelOpen && (