1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-BoGG1wja.js"; 2import { u as useLocation } from "./index-Cn8jK25t.js"; 3import { L as LiveKitRoom, R as RoomAudioRenderer, u as useConnectionState, C as ConnectionState, a as useParticipants, b as useTracks, T as Track, V as VideoTrack } from "./vendor-livekit-__Gc1gfw.js"; 4import { g as getParticipantName, a as getParticipantAvatarUrl, b as getParticipantInitials } from "./utils-J324L3oq.js"; 5import "./vendor-shared-CkCIh-Sx.js"; 6import "./vendor-search-Bv9bACL5.js"; 7import "./vendor-ui-ClIxZ1gL.js"; 8import "./vendor-realtime-LfCGF1rg.js"; 9const isEgressParticipant = (p) => { 10 const identity = String(p.identity || ""); 11 return identity.startsWith("EG_") || /egress/i.test(identity); 12}; 13const STAGE_BG = "#0b0b0f"; 14const AvatarTile = ({ 15 participant 16}) => { 17 const name = getParticipantName(participant) || "Guest"; 18 const avatarUrl = getParticipantAvatarUrl(participant.metadata); 19 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 20 display: "flex", 21 flexDirection: "column", 22 alignItems: "center", 23 justifyContent: "center", 24 gap: 16, 25 width: "100%", 26 height: "100%", 27 background: "linear-gradient(160deg, #16161d 0%, #0e0e13 100%)" 28 }
28, children: avatarUrl ? /* @__PURE__ */ jsxRuntimeExports.jsx( 29 "img", 30 { 31 src: avatarUrl, 32 alt: "", 33 style: { 34 width: "22%", 35 aspectRatio: "1 / 1", 36 minWidth: 88, 37 maxWidth: 200, 38 borderRadius: "50%", 39 objectFit: "cover", 40 border: "3px solid rgba(255,255,255,0.14)" 41 } 42 } 43 ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 44 width: "22%", 45 aspectRatio: "1 / 1", 46 minWidth: 88, 47 maxWidth: 200, 48 borderRadius: "50%", 49 display: "flex", 50 alignItems: "center", 51 justifyContent: "center", 52 background: "rgba(255,255,255,0.09)", 53 color: "rgba(255,255,255,0.85)", 54 fontSize: "clamp(28px, 5vw, 64px)", 55 fontWeight: 600, 56 letterSpacing: 1 57 }, children: getParticipantInitials(name) }) }); 58}; 59const NamePlate = ({ 60 name 61}) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 62 position: "absolute", 63 left: 14, 64 bottom: 12, 65 maxWidth: "calc(100% - 28px)", 66 padding: "5px 12px", 67 borderRadius: 999, 68 background: "rgba(0,0,0,0.55)", 69 color: "#fff", 70 fontSize: 15, 71 fontWeight: 600, 72 lineHeight: 1.2, 73 whiteSpace: "nowrap", 74 overflow: "hidden", 75 textOverflow: "ellipsis" 76}, children: name }); 77const ParticipantTile = ({ 78 participant, 79 cameraTrack 80}) => { 81 var _a; 82 const name = getParticipantName(participant) || "Guest"; 83 const hasVideo = Boolean(cameraTrack && !((_a = cameraTrack.publication) == null ? void 0 : _a.isMuted)); 84 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { 85 position: "relative", 86 width: "100%", 87 height: "100%", 88 overflow: "hidden", 89 borderRadius: 12, 90 background: "#101015" 91 }, children: [ 92 hasVideo && cameraTrack ? /* @__PURE__ */ jsxRuntimeExports.jsx(VideoTrack, { trackRef: cameraTrack, style: { 93 width: "100%", 94 height: "100%", 95 objectFit: "cover" 96 } }) : /* @__PURE__ */ jsxRuntimeExports.jsx(AvatarTile, { participant }), 97 /* @__PURE__ */ jsxRuntimeExports.jsx(NamePlate, { name }) 98 ] }); 99}; 100const EgressStage = () => { 101 const participants = useParticipants(); 102 const tracks = useTracks([Track.Source.Camera, Track.Source.ScreenShare], { 103 onlySubscribed: true 104 }); 105 const humans = reactExports.useMemo(() => participants.filter((p) => !isEgressParticipant(p)), [participants]); 106 const screenShare = reactExports.useMemo(() => tracks.find((t) => t.source === Track.Source.ScreenShare), [tracks]); 107 const cameraByIdentity = reactExports.useMemo(() => { 108 var _a; 109 const map = /* @__PURE__ */ new Map(); 110 for (const t of tracks) { 111 if (t.source === Track.Source.Camera && ((_a = t.participant) == null ? void 0 : _a.identity)) { 112 map.set(t.participant.identity, t); 113 } 114 } 115 return map; 116 }, [tracks]); 117 const columns = Math.max(1, Math.ceil(Math.sqrt(humans.length || 1))); 118 if (humans.length === 0) { 119 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 120 width: "100%", 121 height: "100%", 122 background: STAGE_BG 123 } }); 124 } 125 if (screenShare) { 126 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { 127 display: "flex", 128 width: "100%", 129 height: "100%", 130 gap: 12, 131 padding: 12, 132 background: STAGE_BG, 133 boxSizing: "border-box" 134 }, children: [
135 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 136 flex: "1 1 auto", 137 minWidth: 0, 138 borderRadius: 12, 139 overflow: "hidden" 140 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(VideoTrack, { trackRef: screenShare, style: { 141 width: "100%", 142 height: "100%", 143 objectFit: "contain", 144 background: "#000" 145 } }) }), 146 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 147 flex: "0 0 20%", 148 display: "flex", 149 flexDirection: "column", 150 gap: 12, 151 minWidth: 180 152 }, children: humans.slice(0, 4).map((p) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 153 flex: "0 0 auto", 154 aspectRatio: "16 / 9" 155 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ParticipantTile, { participant: p, cameraTrack: cameraByIdentity.get(p.identity) }) }, p.sid)) }) 156 ] }); 157 } 158 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 159 display: "grid", 160 gridTemplateColumns: `repeat(${columns}, 1fr)`, 161 gap: 12, 162 padding: 12, 163 width: "100%", 164 height: "100%", 165 background: STAGE_BG, 166 boxSizing: "border-box" 167 }, children: humans.map((p) => /* @__PURE__ */ jsxRuntimeExports.jsx(ParticipantTile, { participant: p, cameraTrack: cameraByIdentity.get(p.identity) }, p.sid)) }); 168}; 169const RecorderLifecycle = () => { 170 const connectionState = useConnectionState(); 171 const startedRef = reactExports.useRef(false); 172 reactExports.useEffect(() => { 173 if (startedRef.current) return; 174 if (connectionState !== ConnectionState.Connected) return; 175 startedRef.current = true; 176 requestAnimationFrame(() => { 177 requestAnimationFrame(() => { 178 console.log("START_RECORDING"); 179 }); 180 }); 181 }, [connectionState]); 182 reactExports.useEffect(() => { 183 if (!startedRef.current) return; 184 if (connectionState !== ConnectionState.Disconnected) return; 185 console.log("END_RECORDING"); 186 }, [connectionState]); 187 return null; 188}; 189const EgressTemplatePage = () => { 190 const { 191 search 192 } = useLocation(); 193 const { 194 serverUrl, 195 token 196 } = reactExports.useMemo(() => { 197 const params = new URLSearchParams(search || ""); 198 return { 199 serverUrl: params.get("url") || "", 200 token: params.get("token") || "" 201 // `layout` is also passed by the egress. This template renders one 202 // responsive layout that adapts to screen share and participant count, so 203 // there is nothing to branch on yet. 204 }; 205 }, [search]); 206 const roomOptions = reactExports.useMemo(() => ({ 207 adaptiveStream: false, 208 dynacast: false 209 }), []); 210 reactExports.useEffect(() => { 211 const { 212 body, 213 documentElement 214 } = document; 215 const previous = body.style.cssText; 216 body.style.cssText = "margin:0;padding:0;overflow:hidden;background:#0b0b0f;"; 217 documentElement.style.background = "#0b0b0f"; 218 return () => { 219 body.style.cssText = previous; 220 }; 221 }, []); 222 if (!serverUrl || !token) { 223 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 224 display: "flex", 225 alignItems: "center", 226 justifyContent: "center", 227 width: "100vw", 228 height: "100vh", 229 background: STAGE_BG, 230 color: "rgba(255,255,255,0.4)", 231 fontFamily: "system-ui, sans-serif", 232 fontSize: 14 233 }, children: "Recording template â expects ?url= and &token= from the egress service." }); 234 } 235 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { 236 width: "100vw", 237 height: "100vh", 238 background: STAGE_BG 239 }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs( 240 LiveKitRoom, 241 { 242 serverUrl, 243 token, 244 connect: true, 245 audio: false, 246 video: false, 247 options: roomOptions, 248 style: { 249 width: "100%", 250 height: "100%" 251 }, 252 children: [
253 /* @__PURE__ */ jsxRuntimeExports.jsx(RoomAudioRenderer, {}), 254 /* @__PURE__ */ jsxRuntimeExports.jsx(RecorderLifecycle, {}), 255 /* @__PURE__ */ jsxRuntimeExports.jsx(EgressStage, {}) 256 ] 257 } 258 ) }); 259}; 260export { 261 EgressTemplatePage as default 262};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.