// src/components/AudioRenderer.tsx
import {
  createEffect,
  onCleanup
} from "solid-js";
var AudioRenderer = (props) => {
  let audioElement;
  createEffect(() => {
    if (!props.isLocal) {
      audioElement = props.track.attach();
      if (props.track.sid) {
        audioElement.setAttribute("data-audio-track-id", props.track.sid);
      }
    }
    onCleanup(() => {
      props.track.detach().forEach((element) => element.remove());
    });
  });
  return null;
};

// src/components/AudioSelectButton.tsx
import {
  createEffect as createEffect3,
  createSignal as createSignal3,
  mergeProps,
  onCleanup as onCleanup2,
  Show as Show2
} from "solid-js";
import {
  FaSolidMicrophone,
  FaSolidMicrophoneSlash
} from "solid-icons/fa";
import { Room } from "livekit-client";

// src/components/ControlButton.tsx
import {
  createSignal as createSignal2,
  createEffect as createEffect2,
  Show,
  For
} from "solid-js";
import { FaSolidChevronDown } from "solid-icons/fa";

// src/components/Popover.tsx
import {
  createSignal
} from "solid-js";
import {
  Popover as HeadlessPopover,
  PopoverButton,
  PopoverPanel,
  Transition
} from "solid-headless";
import usePopper from "solid-popper";
var Popover = (props) => {
  var _a;
  const [anchor, setAnchor] = createSignal();
  const [popper, setPopper] = createSignal();
  usePopper(anchor, popper, {
    placement: (_a = props.placement) != null ? _a : "auto"
  });
  return <HeadlessPopover isOpen={props.isOpen}>{({ isOpen }) => <>
    <PopoverButton ref={setAnchor} type="button">{props.children}</PopoverButton>
    <Transition show={isOpen()}><PopoverPanel ref={setPopper}>{props.content}</PopoverPanel></Transition>
  </>}</HeadlessPopover>;
};

// src/components/ControlButton.tsx
var ControlButton = (props) => {
  const [menuVisible, setMenuVisible] = createSignal2(false);
  const [classes, setClasses] = createSignal2("button");
  createEffect2(() => {
    if (props.className) {
      setClasses((current) => {
        if (props.className) {
          return `${current} ${props.className}`;
        }
        return current;
      });
    }
    if (props.menuItems && props.menuItems.length > 0) {
      setClasses((current) => `${current} hasDropdown`);
    }
  });
  const handleMenuClick = (item) => {
    setMenuVisible(false);
    if (props.onMenuItemClick) {
      props.onMenuItemClick(item);
    }
  };
  const MenuTrigger = () => <Show when={props.menuItems}>{(menuItems) => {
    var _a, _b;
    return <Show when={menuItems.length > 0}><button disabled={props.disabled} classList={{
      "button dropdown": true,
      [(_a = props.popoverTriggerBtnClassName) != null ? _a : ""]: !!props.popoverTriggerBtnClassName
    }}>
      <div classList={{
        separator: true,
        [(_b = props.popoverTriggerBtnSeparatorClassName) != null ? _b : ""]: !!props.popoverTriggerBtnSeparatorClassName
      }} />
      <FaSolidChevronDown height={32} />
    </button></Show>;
  }}</Show>;
  const Menu = () => <Show when={props.menuItems}>{(menuItems) => {
    var _a;
    return <Show when={menuItems.length > 0}><div classList={{
      popoverMenu: true,
      [(_a = props.popoverContainerClassName) != null ? _a : ""]: !!props.popoverContainerClassName
    }}><ul className="list"><For each={menuItems}>{(item) => <li onClick={() => handleMenuClick(item)}>{item.label}</li>}</For></ul></div></Show>;
  }}</Show>;
  return <Popover isOpen={menuVisible()} placement="top" content={<Menu />}><div className="buttonWrapper">
    <button disabled={props.disabled} className={classes()} onClick={() => {
      if (props.onClick) {
        props.onClick();
      }
    }}>
      <Show when={props.icon}>{(icon) => icon}</Show>
      {props.label}
    </button>
    <MenuTrigger />
  </div></Popover>;
};

// src/components/AudioSelectButton.tsx
var AudioSelectButton = (props) => {
  const mergedProps = mergeProps({
    muteText: "Mute",
    unmuteText: "Unmute"
  }, props);
  const [sources, setSources] = createSignal3([]);
  const [menuItems, setMenuItems] = createSignal3([]);
  const listAudioDevices = () => {
    Room.getLocalDevices("audioinput").then((devices) => {
      setSources(devices);
      setMenuItems(devices.map((item) => ({ label: item.label })));
    }, console.error);
  };
  createEffect3(() => {
    listAudioDevices();
    navigator.mediaDevices.addEventListener("devicechange", listAudioDevices);
    onCleanup2(() => {
      navigator.mediaDevices.removeEventListener("devicechange", listAudioDevices);
    });
  });
  const handleMenuItem = (item) => {
    const device = sources().find((source) => source.label === item.label);
    if (device && props.onSourceSelected) {
      props.onSourceSelected(device);
    }
  };
  const AudioSelectIcon = () => <Show2 when={props.isMuted} fallback={<FaSolidMicrophone className="icon" height={32} />}><FaSolidMicrophoneSlash className="icon" height={32} /></Show2>;
  return <ControlButton label={props.isMuted ? mergedProps.unmuteText : mergedProps.muteText} icon={<AudioSelectIcon />} onClick={props.onClick} menuItems={menuItems()} onMenuItemClick={handleMenuItem} className={props.className} popoverContainerClassName={props.popoverContainerClassName} popoverTriggerBtnClassName={props.popoverTriggerBtnClassName} popoverTriggerBtnSeparatorClassName={props.popoverTriggerBtnSeparatorClassName} />;
};

// src/components/ControlsView.tsx
import {
  mergeProps as mergeProps3,
  Show as Show4
} from "solid-js";
import {
  FaSolidDesktop,
  FaSolidStop
} from "solid-icons/fa";

// src/signals/createParticipant.ts
import {
  createEffect as createEffect4,
  createSignal as createSignal4,
  onCleanup as onCleanup3
} from "solid-js";
import {
  LocalParticipant,
  ParticipantEvent,
  Track
} from "livekit-client";
function createParticipant(participant) {
  const [isAudioMuted, setIsAudioMuted] = createSignal4(false);
  const [isVideoMuted, setIsVideoMuted] = createSignal4(false);
  const [connectionQuality, setConnectionQuality] = createSignal4(participant.connectionQuality);
  const [isSpeaking, setSpeaking] = createSignal4(false);
  const [metadata, setMetadata] = createSignal4();
  const [publications, setPublications] = createSignal4([]);
  const [subscribedTracks, setSubscribedTracks] = createSignal4([]);
  const onPublicationsChanged = () => {
    const participantTracks = Array.from(participant.tracks.values());
    setPublications(participantTracks);
    setSubscribedTracks(participantTracks.filter((pub) => pub.isSubscribed && pub.track !== void 0));
  };
  const onMuted = (publication) => {
    if (publication.kind === Track.Kind.Audio) {
      setIsAudioMuted(true);
    } else if (publication.kind === Track.Kind.Video) {
      setIsVideoMuted(true);
    }
  };
  const onUnmuted = (publication) => {
    if (publication.kind === Track.Kind.Audio) {
      setIsAudioMuted(false);
    } else if (publication.kind === Track.Kind.Video) {
      setIsVideoMuted(false);
    }
  };
  const onMetadataChanged = () => {
    if (participant.metadata) {
      setMetadata(participant.metadata);
    }
  };
  const onIsSpeakingChanged = () => {
    setSpeaking(participant.isSpeaking);
  };
  const onConnectionQualityUpdate = () => {
    setConnectionQuality(participant.connectionQuality);
  };
  createEffect4(() => {
    participant.on(ParticipantEvent.TrackMuted, onMuted).on(ParticipantEvent.TrackUnmuted, onUnmuted).on(ParticipantEvent.ParticipantMetadataChanged, onMetadataChanged).on(ParticipantEvent.IsSpeakingChanged, onIsSpeakingChanged).on(ParticipantEvent.TrackPublished, onPublicationsChanged).on(ParticipantEvent.TrackUnpublished, onPublicationsChanged).on(ParticipantEvent.TrackSubscribed, onPublicationsChanged).on(ParticipantEvent.TrackUnsubscribed, onPublicationsChanged).on(ParticipantEvent.LocalTrackPublished, onPublicationsChanged).on(ParticipantEvent.LocalTrackUnpublished, onPublicationsChanged).on(ParticipantEvent.ConnectionQualityChanged, onConnectionQualityUpdate);
    onMetadataChanged();
    onIsSpeakingChanged();
    onPublicationsChanged();
  });
  onCleanup3(() => {
    participant.off(ParticipantEvent.TrackMuted, onMuted).off(ParticipantEvent.TrackUnmuted, onUnmuted).off(ParticipantEvent.ParticipantMetadataChanged, onMetadataChanged).off(ParticipantEvent.IsSpeakingChanged, onIsSpeakingChanged).off(ParticipantEvent.TrackPublished, onPublicationsChanged).off(ParticipantEvent.TrackUnpublished, onPublicationsChanged).off(ParticipantEvent.TrackSubscribed, onPublicationsChanged).off(ParticipantEvent.TrackUnsubscribed, onPublicationsChanged).off(ParticipantEvent.LocalTrackPublished, onPublicationsChanged).off(ParticipantEvent.LocalTrackUnpublished, onPublicationsChanged).off(ParticipantEvent.ConnectionQualityChanged, onConnectionQualityUpdate);
  });
  createEffect4(() => {
    let muted;
    participant.audioTracks.forEach((publication) => {
      muted = publication.isMuted;
    });
    if (typeof muted === "undefined") {
      muted = true;
    }
    setIsAudioMuted((currentValue) => {
      if (currentValue !== muted) {
        return !!muted;
      }
      return currentValue;
    });
    setIsVideoMuted((currentValue) => {
      if (currentValue !== muted) {
        return !!muted;
      }
      return currentValue;
    });
  });
  return () => ({
    isLocal: participant instanceof LocalParticipant,
    isSpeaking: isSpeaking(),
    isAudioMuted: isAudioMuted(),
    isVideoMuted: isVideoMuted(),
    connectionQuality: connectionQuality(),
    publications: publications(),
    subscribedTracks: subscribedTracks(),
    cameraPublication: participant.getTrack(Track.Source.Camera),
    microphonePublication: participant.getTrack(Track.Source.Microphone),
    screenSharePublication: participant.getTrack(Track.Source.ScreenShare),
    metadata: metadata(),
    tracks: participant.tracks
  });
}

// src/components/VideoSelectButton.tsx
import {
  mergeProps as mergeProps2,
  createSignal as createSignal5,
  createEffect as createEffect5,
  Show as Show3,
  onCleanup as onCleanup4
} from "solid-js";
import {
  FaSolidVideo,
  FaSolidVideoSlash
} from "solid-icons/fa";
import { Room as Room2 } from "livekit-client";
var VideoSelectButton = (props) => {
  const mergedProps = mergeProps2({
    disableText: "Disable Video",
    enableText: "Enable Video"
  }, props);
  const [sources, setSources] = createSignal5([]);
  const [menuItems, setMenuItems] = createSignal5([]);
  const listVideoDevices = () => {
    Room2.getLocalDevices("videoinput").then((devices) => {
      setSources(devices);
      setMenuItems(devices.map((item) => ({ label: item.label })));
    }, console.error);
  };
  createEffect5(() => {
    listVideoDevices();
    navigator.mediaDevices.addEventListener("devicechange", listVideoDevices);
    onCleanup4(() => {
      navigator.mediaDevices.removeEventListener("devicechange", listVideoDevices);
    });
  });
  const handleMenuItem = (item) => {
    const device = sources().find((source) => source.label === item.label);
    if (device && props.onSourceSelected) {
      props.onSourceSelected(device);
    }
  };
  const VideoSelectedIcon = () => <Show3 when={props.isEnabled} fallback={<FaSolidVideoSlash className="icon" height={32} />}><FaSolidVideo className="icon" height={32} /></Show3>;
  return <ControlButton label={props.isEnabled ? mergedProps.disableText : mergedProps.enableText} icon={<VideoSelectedIcon />} onClick={props.onClick} menuItems={menuItems()} onMenuItemClick={handleMenuItem} className={props.className} popoverContainerClassName={props.popoverContainerClassName} popoverTriggerBtnClassName={props.popoverTriggerBtnClassName} popoverTriggerBtnSeparatorClassName={props.popoverTriggerBtnSeparatorClassName} />;
};

// src/components/ControlsView.tsx
var ControlsView = (props) => {
  const mergedProps = mergeProps3({
    enableScreenShare: true,
    enableVideo: true,
    enableAudio: true
  }, props);
  const participant = createParticipant(props.room.localParticipant);
  const cameraPublication = () => participant().cameraPublication;
  const MuteButton = () => {
    const enabled = props.room.localParticipant.isMicrophoneEnabled;
    return <Show4 when={mergedProps.enableAudio}><AudioSelectButton isMuted={!enabled} onClick={() => props.room.localParticipant.setMicrophoneEnabled(!enabled)} onSourceSelected={(device) => props.room.switchActiveDevice("audioinput", device.deviceId)} /></Show4>;
  };
  const VideoButton = () => {
    var _a, _b;
    const enabled = !((_b = (_a = cameraPublication()) == null ? void 0 : _a.isMuted) != null ? _b : true);
    return <Show4 when={mergedProps.enableVideo}><VideoSelectButton isEnabled={enabled} onClick={() => props.room.localParticipant.setCameraEnabled(!enabled)} onSourceSelected={(device) => {
      props.room.switchActiveDevice("videoinput", device.deviceId).catch(console.error);
    }} /></Show4>;
  };
  const ScreenButton = () => {
    const enabled = props.room.localParticipant.isScreenShareEnabled;
    const ScreenButtonIcon = () => <Show4 when={enabled} fallback={<FaSolidDesktop className="icon" height={32} />}><FaSolidStop className="icon" height={32} /></Show4>;
    return <Show4 when={mergedProps.enableScreenShare}><ControlButton label={enabled ? "Stop sharing" : "Share screen"} icon={<ScreenButtonIcon />} onClick={() => {
      if (enabled) {
        props.room.localParticipant.setScreenShareEnabled(false).catch(console.error);
      } else {
        props.room.localParticipant.setScreenShareEnabled(true).catch(console.error);
      }
    }} /></Show4>;
  };
  return <div className="controlsWrapper">
    <MuteButton />
    <VideoButton />
    <ScreenButton />
    <Show4 when={props.onLeave}>{(onLeave) => <ControlButton label="End" className="dangerButton" onClick={() => {
      props.room.disconnect();
      onLeave(props.room);
    }} />}</Show4>
  </div>;
};

// src/components/DisplayContext.tsx
import { useContext, createContext } from "solid-js";
var DisplayContext = createContext({
  stageLayout: "grid",
  showStats: false
});
var useDisplay = () => useContext(DisplayContext);

// src/components/ParticipantView.tsx
import {
  createEffect as createEffect7,
  createSignal as createSignal7,
  onCleanup as onCleanup6,
  Show as Show5,
  Switch,
  Match
} from "solid-js";
import AspectRatio from "solid-aspect-ratio";
import {
  FaSolidMicrophone as FaSolidMicrophone2,
  FaSolidMicrophoneSlash as FaSolidMicrophoneSlash2
} from "solid-icons/fa";
import {
  ConnectionQuality as ConnectionQuality2,
  LocalTrack,
  RemoteTrack
} from "livekit-client";

// src/components/ConnectionQualityLow.tsx
function ConnectionQualityLow() {
  return <svg width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M0 6H2V9H0V6Z" fill="#981010" />
    <path d="M3.5 3H5.5V9H3.5V3Z" fill="#1A1B1D" />
    <path d="M7 0H9V9H7V0Z" fill="#1A1B1D" />
  </svg>;
}

// src/components/ConnectionQualityMid.tsx
function ConnectionQualityMid() {
  return <svg width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M0 6H2V9H0V6Z" fill="#F89C13" />
    <path d="M3.5 3H5.5V9H3.5V3Z" fill="#F89C13" />
    <path d="M7 0H9V9H7V0Z" fill="#1A1B1D" />
  </svg>;
}

// src/components/ConnectionQualityHigh.tsx
function ConnectionQualityHigh() {
  return <svg width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M0 6H2V9H0V6Z" fill="#28994E" />
    <path d="M3.5 3H5.5V9H3.5V3Z" fill="#28994E" />
    <path d="M7 0H9V9H7V0Z" fill="#28994E" />
  </svg>;
}

// src/components/VideoRenderer.tsx
import {
  createSignal as createSignal6,
  createEffect as createEffect6,
  onCleanup as onCleanup5
} from "solid-js";
var VideoRenderer = (props) => {
  var _a, _b;
  const [ref, setRef] = createSignal6(null);
  createEffect6(() => {
    const videoElement = ref();
    if (videoElement) {
      videoElement.muted = true;
      props.track.attach(videoElement);
    }
    onCleanup5(() => {
      if (videoElement) {
        props.track.detach(videoElement);
      }
    });
  });
  const handleResize = (event) => {
    if (event.target instanceof HTMLVideoElement) {
      if (props.onSizeChanged) {
        props.onSizeChanged(event.target.videoWidth, event.target.videoHeight);
      }
    }
  };
  createEffect6(() => {
    const videoElement = ref();
    if (videoElement) {
      videoElement.addEventListener("resize", handleResize);
    }
    onCleanup5(() => {
      videoElement == null ? void 0 : videoElement.removeEventListener("resize", handleResize);
    });
  });
  const isFrontFacing = ((_a = props.track.mediaStreamTrack) == null ? void 0 : _a.getSettings().facingMode) !== "environment";
  const style = () => {
    const currentStyle = {
      transform: props.isLocal && isFrontFacing ? "rotateY(180deg)" : "",
      width: props.width,
      height: props.height
    };
    if (props.objectFit) {
      currentStyle["object-fit"] = props.objectFit;
    }
    return currentStyle;
  };
  return <video ref={setRef} className={(_b = props.className) != null ? _b : "video"} style={style()} />;
};

// src/components/ParticipantView.tsx
var ParticipantView = (props) => {
  var _a;
  const participant = createParticipant(props.participant);
  const [videoSize, setVideoSize] = createSignal7();
  const [currentBitrate, setCurrentBitrate] = createSignal7();
  const [objectFit, setObjectFit] = createSignal7("contain");
  const [videoOrientation, setVideoOrientation] = createSignal7();
  const [displayName, setDisplayName] = createSignal7(props.displayName);
  const display = useDisplay();
  const handleResize = (width, height) => {
    setVideoSize(`${width}x${height}`);
  };
  createEffect7(() => {
    const interval = setInterval(() => {
      let total = 0;
      props.participant.tracks.forEach((pub) => {
        if (pub.track instanceof LocalTrack || pub.track instanceof RemoteTrack) {
          total += pub.track.currentBitrate;
        }
      });
      setCurrentBitrate(total);
    }, 1e3);
    onCleanup6(() => clearInterval(interval));
  });
  const containerStyles = {
    width: props.width,
    height: props.height
  };
  let { orientation } = props;
  if (!props.orientation && props.aspectWidth && props.aspectHeight) {
    orientation = props.aspectWidth > props.aspectHeight ? "landscape" : "portrait";
  }
  createEffect7(() => {
    var _a2;
    const dimensions = (_a2 = participant().cameraPublication) == null ? void 0 : _a2.dimensions;
    if (dimensions) {
      const orientationValue = dimensions.width > dimensions.height ? "landscape" : "portrait";
      setVideoOrientation(orientationValue);
    }
  });
  createEffect7(() => {
    if (videoOrientation() === orientation) {
      setObjectFit("cover");
    }
  });
  createEffect7(() => {
    setDisplayName((current) => {
      if (!current) {
        const suffix = participant().isLocal ? " (You)" : "";
        return `${props.participant.name || props.participant.identity}${suffix}`;
      }
      return current;
    });
  });
  const MainElement = () => {
    var _a2, _b, _c;
    const publication = () => participant().cameraPublication;
    return <Show5 when={((_a2 = publication()) == null ? void 0 : _a2.isSubscribed) && !((_b = publication()) == null ? void 0 : _b.isMuted) && ((_c = publication()) == null ? void 0 : _c.track)} fallback={<div className="placeholder" />}>{(track) => <VideoRenderer track={track} isLocal={participant().isLocal} objectFit={objectFit()} width="100%" height="100%" onSizeChanged={handleResize} />}</Show5>;
  };
  const speakerClassName = props.speakerClassName || "speaker";
  return <div classList={{
    participant: true,
    [(_a = props.className) != null ? _a : ""]: !!props.className,
    [speakerClassName]: participant().isSpeaking
  }} style={containerStyles} onMouseEnter={props.onMouseEnter} onMouseLeave={props.onMouseLeave} onClick={props.onClick}>
    <Show5 when={props.aspectWidth} fallback={<MainElement />}>{(aspectWidth) => <Show5 when={props.aspectHeight} fallback={<MainElement />}>{(aspectHeight) => <AspectRatio ratio={aspectWidth / aspectHeight}><MainElement /></AspectRatio>}</Show5>}</Show5>
    <Show5 when={props.showOverlay || display.showStats}><div className="participantBar">
      <div className="name">{displayName}</div>
      <div className="center"><Show5 when={display.showStats}><div className="stats">
        <div>{videoSize}</div>
        <Show5 when={currentBitrate()}>{(bitrate) => <Show5 when={bitrate > 0}><div>
          {Math.round(bitrate / 1024)}
          {" kbps"}
        </div></Show5>}</Show5>
      </div></Show5></div>
      <div><Show5 when={props.showConnectionQuality}><Switch>
        <Match when={participant().connectionQuality === ConnectionQuality2.Excellent}><ConnectionQualityHigh /></Match>
        <Match when={participant().connectionQuality === ConnectionQuality2.Good}><ConnectionQualityMid /></Match>
        <Match when={participant().connectionQuality === ConnectionQuality2.Poor}><ConnectionQualityLow /></Match>
      </Switch></Show5></div>
      <div><Show5 when={props.participant.isMicrophoneEnabled} fallback={<FaSolidMicrophoneSlash2 height={24} className="iconRed" />}><FaSolidMicrophone2 height={24} className="iconGreen" /></Show5></div>
    </div></Show5>
  </div>;
};

// src/components/ScreenShareView.tsx
var ScreenShareView = (props) => <div className="screenShare"><VideoRenderer track={props.track} isLocal={false} width={props.width} height={props.height} /></div>;

// src/components/StageView.tsx
import {
  Show as Show9,
  createSignal as createSignal11,
  createEffect as createEffect11,
  For as For5
} from "solid-js";
import { FaSolidVolumeMute } from "solid-icons/fa";
import { Track as Track4 } from "livekit-client";
import createMediaQuery from "@solid-primitives/media";

// src/components/desktop/GridStage.tsx
import {
  createEffect as createEffect8,
  createSignal as createSignal8,
  Switch as Switch2,
  Match as Match2,
  Show as Show6,
  For as For2
} from "solid-js";
var GridStage = (props) => {
  const [visibleParticipants, setVisibleParticipants] = createSignal8([]);
  const [showOverlay, setShowOverlay] = createSignal8(false);
  const [gridClass, setGridClass] = createSignal8("grid1x1");
  createEffect8(() => {
    let numVisible = 1;
    const participantCount = props.roomState.participants.length;
    if (participantCount === 1) {
      setGridClass("grid1x1");
    } else if (participantCount === 2) {
      setGridClass("grid2x1");
      numVisible = 2;
    } else if (participantCount <= 4) {
      setGridClass("grid2x2");
      numVisible = Math.min(participantCount, 4);
    } else if (participantCount <= 9) {
      setGridClass("grid3x3");
      numVisible = Math.min(participantCount, 9);
    } else if (participantCount <= 16) {
      setGridClass("grid4x4");
      numVisible = Math.min(participantCount, 16);
    } else {
      setGridClass("grid5x5");
      numVisible = Math.min(participantCount, 25);
    }
    setVisibleParticipants((current) => {
      var _a;
      const newParticipants = [];
      const currentRoom = props.roomState.room();
      current.forEach((p) => {
        if ((currentRoom == null ? void 0 : currentRoom.participants.has(p.sid)) || (currentRoom == null ? void 0 : currentRoom.localParticipant.sid) === p.sid) {
          newParticipants.push(p);
        }
      });
      (_a = currentRoom == null ? void 0 : currentRoom.activeSpeakers) == null ? void 0 : _a.forEach((speaker) => {
        if (newParticipants.includes(speaker) || speaker !== (currentRoom == null ? void 0 : currentRoom.localParticipant) && !(currentRoom == null ? void 0 : currentRoom.participants.has(speaker.sid))) {
          return;
        }
        const idx = newParticipants.findIndex((participant) => !participant.isSpeaking);
        if (idx >= 0) {
          newParticipants[idx] = speaker;
        } else {
          newParticipants.push(speaker);
        }
      });
      props.roomState.participants().forEach((participant) => {
        const isFull = newParticipants.length >= numVisible;
        const isVisible = newParticipants.includes(participant) || participant.isSpeaking;
        if (!isFull && !isVisible) {
          newParticipants.push(participant);
        }
      });
      if (newParticipants.length > numVisible) {
        newParticipants.splice(numVisible, newParticipants.length - numVisible);
      }
      return newParticipants;
    });
  });
  return <Switch2 fallback={() => {
    var _a, _b;
    const ParticipantRenderer = (_a = props.participantRenderer) != null ? _a : ParticipantView;
    const ControlRenderer = (_b = props.controlRenderer) != null ? _b : ControlsView;
    return <div className="container">
      <div className={`gridStage ${gridClass()}`}><For2 each={visibleParticipants()}>{(participant) => <ParticipantRenderer participant={participant} orientation="landscape" width="100%" height="100%" showOverlay={showOverlay()} showConnectionQuality onMouseEnter={() => setShowOverlay(true)} onMouseLeave={() => setShowOverlay(false)} />}</For2></div>
      <div className="controlsArea"><Show6 when={props.roomState.room()}>{(room) => <ControlRenderer room={room} onLeave={props.onLeave} />}</Show6></div>
    </div>;
  }}>
    <Match2 when={props.roomState.error()}>{(error) => <div>
      {"error:"}
      {" "}
      {error == null ? void 0 : error.message}
    </div>}</Match2>
    <Match2 when={props.roomState.isConnecting()}>{() => <div>connecting</div>}</Match2>
    <Match2 when={!props.roomState.room()}>{() => <div>room closed</div>}</Match2>
    <Match2 when={!props.roomState.participants().length}>{() => <div>no one is in the room</div>}</Match2>
  </Switch2>;
};

// src/components/desktop/SpeakerStage.tsx
import {
  createEffect as createEffect9,
  createSignal as createSignal9,
  Switch as Switch3,
  Match as Match3,
  Show as Show7,
  For as For3
} from "solid-js";
import { Track as Track2 } from "livekit-client";
var SpeakerStage = (props) => {
  const [showOverlay, setShowOverlay] = createSignal9(false);
  return <Switch3 fallback={() => {
    var _a, _b;
    const ParticipantRenderer = (_a = props.participantRenderer) != null ? _a : ParticipantView;
    const ControlRenderer = (_b = props.controlRenderer) != null ? _b : ControlsView;
    const [screenTrack, setScreenTrack] = createSignal9();
    createEffect9(() => {
      props.roomState.participants().forEach((participant) => {
        setScreenTrack((current) => {
          if (!current) {
            const track = participant.getTrack(Track2.Source.ScreenShare);
            if ((track == null ? void 0 : track.isSubscribed) && track.videoTrack) {
              return track.videoTrack;
            }
          }
          return current;
        });
      });
    });
    const otherParticipants = props.roomState.participants;
    const MainView = () => <Show7 when={screenTrack()} fallback={<Show7 when={props.roomState.participants()[0]}>{(participant) => <ParticipantRenderer participant={participant} width="100%" height="100%" orientation="landscape" showOverlay={showOverlay()} showConnectionQuality onMouseEnter={() => setShowOverlay(true)} onMouseLeave={() => setShowOverlay(false)} />}</Show7>}>{(track) => <ScreenShareView track={track} height="100%" width="100%" />}</Show7>;
    return <div className="container">
      <div className="stage">
        <div className="stageCenter"><MainView /></div>
        <div className="sidebar"><For3 each={otherParticipants()}>{(participant) => <ParticipantRenderer participant={participant} width="100%" aspectWidth={16} aspectHeight={9} showOverlay={showOverlay()} onMouseEnter={() => setShowOverlay(true)} onMouseLeave={() => setShowOverlay(false)} />}</For3></div>
      </div>
      <div className="controlsArea"><Show7 when={props.roomState.room()}>{(room) => <ControlRenderer room={room} enableScreenShare={false} onLeave={props.onLeave} />}</Show7></div>
    </div>;
  }}>
    <Match3 when={props.roomState.error()}>{(error) => <div>
      {"error:"}
      {" "}
      {error == null ? void 0 : error.message}
    </div>}</Match3>
    <Match3 when={props.roomState.isConnecting()}>{() => <div>connecting</div>}</Match3>
    <Match3 when={!props.roomState.room()}>{() => <div>room closed</div>}</Match3>
    <Match3 when={!props.roomState.participants().length}>{() => <div>no one is in the room</div>}</Match3>
  </Switch3>;
};

// src/components/mobile/MobileStage.tsx
import {
  createSignal as createSignal10,
  createEffect as createEffect10,
  Switch as Switch4,
  Match as Match4,
  Show as Show8,
  For as For4
} from "solid-js";
import { Track as Track3 } from "livekit-client";
var MobileStage = (props) => {
  const [showOverlay, setShowOverlay] = createSignal10(false);
  return <Switch4 fallback={() => {
    var _a, _b;
    const ParticipantRenderer = (_a = props.participantRenderer) != null ? _a : ParticipantView;
    const ControlRenderer = (_b = props.controlRenderer) != null ? _b : ControlsView;
    const [screenTrack, setScreenTrack] = createSignal10();
    createEffect10(() => {
      props.roomState.participants().forEach((participant) => {
        setScreenTrack((current) => {
          if (!current) {
            const track = participant.getTrack(Track3.Source.ScreenShare);
            if ((track == null ? void 0 : track.isSubscribed) && track.videoTrack) {
              return track.videoTrack;
            }
          }
          return current;
        });
      });
    });
    const otherParticipants = props.roomState.participants;
    const MainView = () => <Show8 when={screenTrack()} fallback={<Show8 when={props.roomState.participants()[0]}>{(participant) => <ParticipantRenderer participant={participant} showOverlay={showOverlay()} width="100%" height="100%" orientation="portrait" showConnectionQuality onMouseEnter={() => setShowOverlay(true)} onMouseLeave={() => setShowOverlay(false)} />}</Show8>}>{(track) => <ScreenShareView track={track} height="100%" width="100%" />}</Show8>;
    return <div className="container">
      <div className="stage"><MainView /></div>
      <div className="participantsArea"><For4 each={otherParticipants()}>{(participant) => <ParticipantRenderer participant={participant} className="participant" aspectWidth={4} aspectHeight={3} showOverlay={showOverlay()} onMouseEnter={() => setShowOverlay(true)} onMouseLeave={() => setShowOverlay(false)} />}</For4></div>
      <div className="controlsArea"><Show8 when={props.roomState.room()}>{(room) => <ControlRenderer room={room} enableScreenShare={false} onLeave={props.onLeave} />}</Show8></div>
    </div>;
  }}>
    <Match4 when={props.roomState.error()}>{(error) => <div>
      {"error:"}
      {" "}
      {error == null ? void 0 : error.message}
    </div>}</Match4>
    <Match4 when={props.roomState.isConnecting()}>{() => <div>connecting</div>}</Match4>
    <Match4 when={!props.roomState.room()}>{() => <div>room closed</div>}</Match4>
    <Match4 when={!props.roomState.participants().length}>{() => <div>no one is in the room</div>}</Match4>
  </Switch4>;
};

// src/components/StageView.tsx
var StageView = (props) => {
  var _a;
  const isDesktop = createMediaQuery("(min-width: 800px)");
  const display = useDisplay();
  const MainElement = () => <Show9 when={isDesktop()} fallback={<MobileStage {...props} />}>{() => {
    const [screenTrack, setScreenTrack] = createSignal11();
    createEffect11(() => {
      props.roomState.participants().forEach((participant) => {
        setScreenTrack((current) => {
          if (!current) {
            const track = participant.getTrack(Track4.Source.ScreenShare);
            if ((track == null ? void 0 : track.isSubscribed) && track.videoTrack) {
              return track.videoTrack;
            }
          }
          return current;
        });
      });
    });
    return <Show9 when={display.stageLayout === "grid" && screenTrack === void 0} fallback={<SpeakerStage {...props} />}><GridStage {...props} /></Show9>;
  }}</Show9>;
  return <div className="container">
    <MainElement />
    <For5 each={props.roomState.audioTracks()}>{(track) => <AudioRenderer track={track} isLocal={false} />}</For5>
    <Show9 when={((_a = props.roomState.room()) == null ? void 0 : _a.canPlaybackAudio) === false}><div className="overlay"><button className="unmuteButton" onClick={() => {
      var _a2;
      (_a2 = props.roomState.room()) == null ? void 0 : _a2.startAudio().catch(console.error);
    }}>
      <FaSolidVolumeMute className="icon" size="1x" />
      {"Click to Unmute"}
    </button></div></Show9>
  </div>;
};

// src/LiveKitRoom.tsx
import {
  createEffect as createEffect12,
  mergeProps as mergeProps4,
  onCleanup as onCleanup8
} from "solid-js";

// src/signals/createRoom.ts
import {
  createSignal as createSignal12,
  onCleanup as onCleanup7
} from "solid-js";
import {
  connect,
  Track as Track5
} from "livekit-client";

// src/utils/sortParticipants.ts
function sortParticipants(participants, localParticipant) {
  participants.sort((a, b) => {
    var _a, _b, _c, _d, _e, _f, _g, _h;
    if (a.isSpeaking && b.isSpeaking) {
      return b.audioLevel - a.audioLevel;
    }
    if (a.isSpeaking !== b.isSpeaking) {
      if (a.isSpeaking) {
        return -1;
      }
      return 1;
    }
    if (a.lastSpokeAt !== b.lastSpokeAt) {
      const aLast = (_b = (_a = a.lastSpokeAt) == null ? void 0 : _a.getTime()) != null ? _b : 0;
      const bLast = (_d = (_c = b.lastSpokeAt) == null ? void 0 : _c.getTime()) != null ? _d : 0;
      return bLast - aLast;
    }
    const aVideo = a.videoTracks.size > 0;
    const bVideo = b.videoTracks.size > 0;
    if (aVideo !== bVideo) {
      if (aVideo) {
        return -1;
      }
      return 1;
    }
    return ((_f = (_e = a.joinedAt) == null ? void 0 : _e.getTime()) != null ? _f : 0) - ((_h = (_g = b.joinedAt) == null ? void 0 : _g.getTime()) != null ? _h : 0);
  });
  if (localParticipant) {
    const localIdx = participants.indexOf(localParticipant);
    if (localIdx >= 0) {
      participants.splice(localIdx, 1);
      if (participants.length > 0) {
        participants.splice(1, 0, localParticipant);
      } else {
        participants.push(localParticipant);
      }
    }
  }
}

// src/signals/createRoom.ts
function createRoom(options) {
  var _a;
  const [room, setRoom] = createSignal12();
  const [isConnecting, setIsConnecting] = createSignal12(false);
  const [error, setError] = createSignal12();
  const [participants, setParticipants] = createSignal12([]);
  const [audioTracks, setAudioTracks] = createSignal12([]);
  const sortFunction = (_a = options == null ? void 0 : options.sortParticipants) != null ? _a : sortParticipants;
  const connectFunction = async (url, token, connectOptions) => {
    setIsConnecting(true);
    try {
      const newRoom = await connect(url, token, connectOptions);
      setRoom(newRoom);
      const onParticipantsChanged = () => {
        const remotes = Array.from(newRoom.participants.values());
        const localParticipants = [newRoom.localParticipant, ...remotes];
        sortFunction(localParticipants, newRoom.localParticipant);
        setParticipants(localParticipants);
      };
      const onSubscribedTrackChanged = (track) => {
        onParticipantsChanged();
        if (!track || track.kind === Track5.Kind.Audio) {
          const tracks = [];
          newRoom.participants.forEach((participant) => {
            participant.audioTracks.forEach(({ audioTrack }) => {
              if (audioTrack) {
                tracks.push(audioTrack);
              }
            });
          });
          setAudioTracks(tracks);
        }
      };
      newRoom.once("disconnected", () => {
        const timeoutId = setTimeout(() => setRoom(void 0));
        newRoom.off("participantConnected", onParticipantsChanged).off("participantDisconnected", onParticipantsChanged).off("activeSpeakersChanged", onParticipantsChanged).off("trackSubscribed", onSubscribedTrackChanged).off("trackUnsubscribed", onSubscribedTrackChanged).off("localTrackPublished", onParticipantsChanged).off("localTrackUnpublished", onParticipantsChanged).off("audioPlaybackChanged", onParticipantsChanged);
        onCleanup7(() => clearTimeout(timeoutId));
      });
      newRoom.on("participantConnected", onParticipantsChanged).on("participantDisconnected", onParticipantsChanged).on("activeSpeakersChanged", onParticipantsChanged).on("trackSubscribed", onSubscribedTrackChanged).on("trackUnsubscribed", onSubscribedTrackChanged).on("localTrackPublished", onParticipantsChanged).on("localTrackUnpublished", onParticipantsChanged).on("audioPlaybackChanged", onParticipantsChanged);
      setIsConnecting(false);
      onSubscribedTrackChanged();
      return newRoom;
    } catch (err) {
      setIsConnecting(false);
      if (err instanceof Error) {
        setError(err);
      } else {
        setError(new Error("an error has occured"));
      }
      return void 0;
    }
  };
  return {
    connect: connectFunction,
    isConnecting,
    room,
    error,
    participants,
    audioTracks
  };
}

// src/LiveKitRoom.tsx
var LiveKitRoom = (props) => {
  var _a;
  const mergedProps = mergeProps4({ connectOptions: {} }, props);
  const roomState = createRoom({ sortParticipants: props.sortParticipants });
  createEffect12(async () => {
    try {
      const room = await roomState.connect(props.url, props.token, mergedProps.connectOptions);
      if (!room) {
        return;
      }
      if (props.onConnected) {
        props.onConnected(room);
      }
      onCleanup8(() => {
        room.disconnect();
      });
    } catch (error) {
      console.error(error);
    }
  });
  const selectedStageRenderer = (_a = props.stageRenderer) != null ? _a : StageView;
  return selectedStageRenderer({
    roomState,
    participantRenderer: props.participantRenderer,
    controlRenderer: props.controlRenderer,
    onLeave: props.onLeave
  });
};
export {
  AudioRenderer,
  AudioSelectButton,
  ControlButton,
  ControlsView,
  DisplayContext,
  LiveKitRoom,
  ParticipantView,
  ScreenShareView,
  StageView,
  VideoRenderer,
  VideoSelectButton,
  createParticipant,
  createRoom,
  sortParticipants,
  useDisplay
};
//# sourceMappingURL=index.jsx.map
