Merge pull request #1620 from thecodingmachine/embedScreens

Improve UI & Cowebsites
This commit is contained in:
Alexis Faizeau 2022-01-27 16:50:10 +01:00 committed by GitHub
commit f1a674225a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
97 changed files with 3626 additions and 1821 deletions

View file

@ -83,7 +83,8 @@
"SECRET_JITSI_KEY": env.SECRET_JITSI_KEY,
"TURN_SERVER": "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443",
"JITSI_PRIVATE_MODE": if env.SECRET_JITSI_KEY != '' then "true" else "false",
"START_ROOM_URL": "/_/global/maps-"+url+"/starter/map.json"
"START_ROOM_URL": "/_/global/maps-"+url+"/starter/map.json",
"ICON_URL": "//icon-"+url,
}
},
"uploader": {
@ -109,7 +110,15 @@
"redis": {
"image": "redis:6",
"ports": [6379]
}
},
"iconserver": {
"image": "matthiasluedtke/iconserver:v3.13.0",
"host": {
"url": "icon-"+url,
"containerPort": 8080,
},
"ports": [8080]
},
},
"config": {
k8sextension(k8sConf)::
@ -210,6 +219,16 @@
}
}
},
iconserver+: {
ingress+: {
spec+: {
tls+: [{
hosts: ["icon-"+url],
secretName: "certificate-tls"
}]
}
}
},
}
}
}

View file

@ -52,17 +52,17 @@ WA.nav.goToRoom("/_/global/<path to global map>.json#start-layer-2")
### Opening/closing web page in Co-Websites
```
WA.nav.openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = "", position: number = 0): Promise<CoWebsite>
WA.nav.openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = "", position: number, closable: boolean, lazy: boolean): Promise<CoWebsite>
```
Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. `allowApi` allows the webpage to use the "IFrame API" and execute script (it is equivalent to putting the `openWebsiteAllowApi` property in the map). `allowPolicy` grants additional access rights to the iFrame. The `allowPolicy` parameter is turned into an [`allow` feature policy in the iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow), position in whitch slot the web page will be open.
You can have only 5 co-wbesites open simultaneously.
Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. `allowApi` allows the webpage to use the "IFrame API" and execute script (it is equivalent to putting the `openWebsiteAllowApi` property in the map). `allowPolicy` grants additional access rights to the iFrame. The `allowPolicy` parameter is turned into an [`allow` feature policy in the iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow), position in whitch slot the web page will be open, closable allow to close the webpage also you need to close it by the api and lazy
it's to add the cowebsite but don't load it.
Example:
```javascript
const coWebsite = await WA.nav.openCoWebSite('https://www.wikipedia.org/');
const coWebsiteWorkAdventure = await WA.nav.openCoWebSite('https://workadventu.re/', true, "", 1);
const coWebsiteWorkAdventure = await WA.nav.openCoWebSite('https://workadventu.re/', true, "", 1, true, true);
// ...
coWebsite.close();
```

View file

@ -1,4 +1,6 @@
index.html
/js/
/fonts/
style.*.css
!env-config.template.js
*.png

76
front/dist/index.ejs vendored

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><defs><image width="12" height="14" id="img1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAOAQMAAAAhc2+vAAAAAXNSR0IB2cksfwAAAAZQTFRFAAAA////pdmf3QAAAAJ0Uk5TAP9bkSK1AAAAJUlEQVR4nGNgOMDAoMDw/wMDCDgwMDQwQIAASBgE/j8ACRswAACLjwYPIknTggAAAABJRU5ErkJggg=="/><image width="12" height="12" id="img2" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMAgMAAAArG7R0AAAAAXNSR0IB2cksfwAAAAlQTFRFAAAA/////wAAzV63nAAAAAN0Uk5TAP8BHlUJOgAAACJJREFUeJxjYGAQYGBgYGEIDQ0F0xA+BwMDEi80NASqigEAOD8CVqGVgwsAAAAASUVORK5CYII="/></defs><style></style><use href="#img1" x="2" y="1" /><use href="#img2" x="2" y="2" /></svg>

After

Width:  |  Height:  |  Size: 717 B

View file

@ -15,6 +15,7 @@
"@typescript-eslint/eslint-plugin": "^5.6.0",
"@typescript-eslint/parser": "^5.6.0",
"css-loader": "^5.2.4",
"css-minimizer-webpack-plugin": "^3.3.1",
"eslint": "^8.4.1",
"eslint-plugin-svelte3": "^3.2.1",
"fork-ts-checker-webpack-plugin": "^6.5.0",

View file

@ -6,13 +6,14 @@ export const isOpenCoWebsiteEvent = new tg.IsInterface()
allowApi: tg.isOptional(tg.isBoolean),
allowPolicy: tg.isOptional(tg.isString),
position: tg.isOptional(tg.isNumber),
closable: tg.isOptional(tg.isBoolean),
lazy: tg.isOptional(tg.isBoolean),
})
.get();
export const isCoWebsite = new tg.IsInterface()
.withProperties({
id: tg.isString,
position: tg.isNumber,
})
.get();

View file

@ -1,7 +1,7 @@
import { IframeApiContribution, sendToWorkadventure, queryWorkadventure } from "./IframeApiContribution";
export class CoWebsite {
constructor(private readonly id: string, public readonly position: number) {}
constructor(private readonly id: string) {}
close() {
return queryWorkadventure({
@ -41,7 +41,14 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
});
}
async openCoWebSite(url: string, allowApi?: boolean, allowPolicy?: string, position?: number): Promise<CoWebsite> {
async openCoWebSite(
url: string,
allowApi?: boolean,
allowPolicy?: string,
position?: number,
closable?: boolean,
lazy?: boolean
): Promise<CoWebsite> {
const result = await queryWorkadventure({
type: "openCoWebsite",
data: {
@ -49,9 +56,11 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
allowApi,
allowPolicy,
position,
closable,
lazy,
},
});
return new CoWebsite(result.id, result.position);
return new CoWebsite(result.id);
}
async getCoWebSites(): Promise<CoWebsite[]> {
@ -59,7 +68,7 @@ export class WorkadventureNavigationCommands extends IframeApiContribution<Worka
type: "getCoWebsites",
data: undefined,
});
return result.map((cowebsiteEvent) => new CoWebsite(cowebsiteEvent.id, cowebsiteEvent.position));
return result.map((cowebsiteEvent) => new CoWebsite(cowebsiteEvent.id));
}
/**

View file

@ -1,166 +1,52 @@
<script lang="typescript">
import MenuIcon from "./Menu/MenuIcon.svelte";
import { menuIconVisiblilityStore, menuVisiblilityStore } from "../Stores/MenuStore";
import { emoteMenuStore } from "../Stores/EmoteStore";
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
import CameraControls from "./CameraControls.svelte";
import MyCamera from "./MyCamera.svelte";
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
import { selectCompanionSceneVisibleStore } from "../Stores/SelectCompanionStore";
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
import { errorStore } from "../Stores/ErrorStore";
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
import LoginScene from "./Login/LoginScene.svelte";
import Chat from "./Chat/Chat.svelte";
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
import VisitCard from "./VisitCard/VisitCard.svelte";
import { requestVisitCardsStore } from "../Stores/GameStore";
import type { Game } from "../Phaser/Game/Game";
import { chatVisibilityStore } from "../Stores/ChatStore";
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore";
import LimitRoomModal from "./Modal/LimitRoomModal.svelte";
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
import AudioPlaying from "./UI/AudioPlaying.svelte";
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
import { customCharacterSceneVisibleStore } from "../Stores/CustomCharacterStore";
import { errorStore } from "../Stores/ErrorStore";
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
import { selectCompanionSceneVisibleStore } from "../Stores/SelectCompanionStore";
import Chat from "./Chat/Chat.svelte";
import CustomCharacterScene from "./CustomCharacterScene/CustomCharacterScene.svelte";
import EnableCameraScene from "./EnableCamera/EnableCameraScene.svelte";
import LoginScene from "./Login/LoginScene.svelte";
import MainLayout from "./MainLayout.svelte";
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
import ErrorDialog from "./UI/ErrorDialog.svelte";
import Menu from "./Menu/Menu.svelte";
import EmoteMenu from "./EmoteMenu/EmoteMenu.svelte";
import VideoOverlay from "./Video/VideoOverlay.svelte";
import { gameOverlayVisibilityStore } from "../Stores/GameOverlayStoreVisibility";
import BanMessageContainer from "./TypeMessage/BanMessageContainer.svelte";
import TextMessageContainer from "./TypeMessage/TextMessageContainer.svelte";
import { banMessageStore } from "../Stores/TypeMessageStore/BanMessageStore";
import { textMessageStore } from "../Stores/TypeMessageStore/TextMessageStore";
import { warningContainerStore } from "../Stores/MenuStore";
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
import { layoutManagerVisibilityStore } from "../Stores/LayoutManagerStore";
import LayoutManager from "./LayoutManager/LayoutManager.svelte";
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
import AudioManager from "./AudioManager/AudioManager.svelte";
import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore";
import ReportMenu from "./ReportMenu/ReportMenu.svelte";
import { followStateStore } from "../Stores/FollowStore";
import { peerStore } from "../Stores/PeerStore";
import FollowMenu from "./FollowMenu/FollowMenu.svelte";
export let game: Game;
</script>
<div>
{#if $loginSceneVisibleStore}
<div class="scrollable">
<LoginScene {game} />
</div>
{/if}
{#if $selectCharacterSceneVisibleStore}
<div>
<SelectCharacterScene {game} />
</div>
{/if}
{#if $customCharacterSceneVisibleStore}
<div>
<CustomCharacterScene {game} />
</div>
{/if}
{#if $selectCompanionSceneVisibleStore}
<div>
<SelectCompanionScene {game} />
</div>
{/if}
{#if $enableCameraSceneVisibilityStore}
<div class="scrollable">
<EnableCameraScene {game} />
</div>
{/if}
{#if $banMessageStore.length > 0}
<div>
<BanMessageContainer />
</div>
{:else if $textMessageStore.length > 0}
<div>
<TextMessageContainer />
</div>
{/if}
{#if $soundPlayingStore}
<div>
<AudioPlaying url={$soundPlayingStore} />
</div>
{/if}
{#if $audioManagerVisibilityStore}
<div>
<AudioManager />
</div>
{/if}
{#if $layoutManagerVisibilityStore}
<div>
<LayoutManager />
</div>
{/if}
{#if $showReportScreenStore !== userReportEmpty}
<div>
<ReportMenu />
</div>
{/if}
{#if $followStateStore !== "off" || $peerStore.size > 0}
<div>
<FollowMenu />
</div>
{/if}
{#if $menuIconVisiblilityStore}
<div>
<MenuIcon />
</div>
{/if}
{#if $menuVisiblilityStore}
<div>
<Menu />
</div>
{/if}
{#if $emoteMenuStore}
<div>
<EmoteMenu />
</div>
{/if}
{#if $gameOverlayVisibilityStore}
<div>
<VideoOverlay />
<MyCamera />
<CameraControls />
</div>
{/if}
{#if $helpCameraSettingsVisibleStore}
<div>
<HelpCameraSettingsPopup />
</div>
{/if}
{#if $showLimitRoomModalStore}
<div>
<LimitRoomModal />
</div>
{/if}
{#if $showShareLinkMapModalStore}
<div>
<ShareLinkMapModal />
</div>
{/if}
{#if $requestVisitCardsStore}
<VisitCard visitCardUrl={$requestVisitCardsStore} />
{/if}
{#if $errorStore.length > 0}
<div>
<ErrorDialog />
</div>
{/if}
{#if $errorStore.length > 0}
<div>
<ErrorDialog />
</div>
{:else if $loginSceneVisibleStore}
<div class="scrollable">
<LoginScene {game} />
</div>
{:else if $selectCharacterSceneVisibleStore}
<div>
<SelectCharacterScene {game} />
</div>
{:else if $customCharacterSceneVisibleStore}
<div>
<CustomCharacterScene {game} />
</div>
{:else if $selectCompanionSceneVisibleStore}
<div>
<SelectCompanionScene {game} />
</div>
{:else if $enableCameraSceneVisibilityStore}
<div class="scrollable">
<EnableCameraScene {game} />
</div>
{:else}
<MainLayout />
{#if $chatVisibilityStore}
<Chat />
{/if}
{#if $warningContainerStore}
<WarningContainer />
{/if}
</div>
{/if}

View file

@ -157,13 +157,16 @@
<style lang="scss">
div.main-audio-manager.nes-container.is-rounded {
position: relative;
top: 0.5rem;
position: absolute;
top: 1%;
max-height: clamp(150px, 10vh, 15vh); //replace @media for small screen
width: clamp(200px, 15vw, 15vw);
padding: 3px 3px;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
z-index: 550;
background-color: rgb(0, 0, 0, 0.5);
display: grid;

View file

@ -9,10 +9,15 @@
import microphoneCloseImg from "./images/microphone-close.svg";
import layoutPresentationImg from "./images/layout-presentation.svg";
import layoutChatImg from "./images/layout-chat.svg";
import { layoutModeStore } from "../Stores/StreamableCollectionStore";
import followImg from "./images/follow.svg";
import { LayoutMode } from "../WebRtc/LayoutManager";
import { peerStore } from "../Stores/PeerStore";
import { onDestroy } from "svelte";
import { embedScreenLayout } from "../Stores/EmbedScreensStore";
import { followRoleStore, followStateStore, followUsersStore } from "../Stores/FollowStore";
import { gameManager } from "../Phaser/Game/GameManager";
const gameScene = gameManager.getCurrentGameScene();
function screenSharingClick(): void {
if (isSilent) return;
@ -42,10 +47,26 @@
}
function switchLayoutMode() {
if ($layoutModeStore === LayoutMode.Presentation) {
$layoutModeStore = LayoutMode.VideoChat;
if ($embedScreenLayout === LayoutMode.Presentation) {
$embedScreenLayout = LayoutMode.VideoChat;
} else {
$layoutModeStore = LayoutMode.Presentation;
$embedScreenLayout = LayoutMode.Presentation;
}
}
function followClick() {
switch ($followStateStore) {
case "off":
gameScene.connection?.emitFollowRequest();
followRoleStore.set("leader");
followStateStore.set("active");
break;
case "requesting":
case "active":
case "ending":
gameScene.connection?.emitFollowAbort();
followUsersStore.stopFollowing();
break;
}
}
@ -56,40 +77,162 @@
onDestroy(unsubscribeIsSilent);
</script>
<div>
<div class="btn-cam-action">
<div class="btn-layout" on:click={switchLayoutMode} class:hide={$peerStore.size === 0}>
{#if $layoutModeStore === LayoutMode.Presentation}
<img src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode" />
{:else}
<img src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode" />
{/if}
</div>
<div
class="btn-monitor"
on:click={screenSharingClick}
class:hide={!$screenSharingAvailableStore || isSilent}
class:enabled={$requestedScreenSharingState}
>
{#if $requestedScreenSharingState && !isSilent}
<img src={monitorImg} alt="Start screen sharing" />
{:else}
<img src={monitorCloseImg} alt="Stop screen sharing" />
{/if}
</div>
<div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState || isSilent}>
{#if $requestedCameraState && !isSilent}
<img src={cinemaImg} alt="Turn on webcam" />
{:else}
<img src={cinemaCloseImg} alt="Turn off webcam" />
{/if}
</div>
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}>
{#if $requestedMicrophoneState && !isSilent}
<img src={microphoneImg} alt="Turn on microphone" />
{:else}
<img src={microphoneCloseImg} alt="Turn off microphone" />
{/if}
</div>
<div class="btn-cam-action">
<div class="btn-layout" on:click={switchLayoutMode} class:hide={$peerStore.size === 0}>
{#if $embedScreenLayout === LayoutMode.Presentation}
<img class="noselect" src={layoutPresentationImg} style="padding: 2px" alt="Switch to mosaic mode" />
{:else}
<img class="noselect" src={layoutChatImg} style="padding: 2px" alt="Switch to presentation mode" />
{/if}
</div>
<div
class="btn-follow"
class:hide={($peerStore.size === 0 && $followStateStore === "off") || isSilent}
class:disabled={$followStateStore !== "off"}
on:click={followClick}
>
<img class="noselect" src={followImg} alt="" />
</div>
<div
class="btn-monitor"
on:click={screenSharingClick}
class:hide={!$screenSharingAvailableStore || isSilent}
class:enabled={$requestedScreenSharingState}
>
{#if $requestedScreenSharingState && !isSilent}
<img class="noselect" src={monitorImg} alt="Start screen sharing" />
{:else}
<img class="noselect" src={monitorCloseImg} alt="Stop screen sharing" />
{/if}
</div>
<div class="btn-video" on:click={cameraClick} class:disabled={!$requestedCameraState || isSilent}>
{#if $requestedCameraState && !isSilent}
<img class="noselect" src={cinemaImg} alt="Turn on webcam" />
{:else}
<img class="noselect" src={cinemaCloseImg} alt="Turn off webcam" />
{/if}
</div>
<div class="btn-micro" on:click={microphoneClick} class:disabled={!$requestedMicrophoneState || isSilent}>
{#if $requestedMicrophoneState && !isSilent}
<img class="noselect" src={microphoneImg} alt="Turn on microphone" />
{:else}
<img class="noselect" src={microphoneCloseImg} alt="Turn off microphone" />
{/if}
</div>
</div>
<style lang="scss">
@import "../../style/breakpoints.scss";
.btn-cam-action {
pointer-events: all;
position: absolute;
display: inline-flex;
bottom: 10px;
right: 15px;
width: 360px;
height: 40px;
text-align: center;
align-content: center;
justify-content: flex-end;
z-index: 251;
&:hover {
div.hide {
transform: translateY(60px);
}
}
}
/*btn animation*/
.btn-cam-action div {
cursor: url("../../style/images/cursor_pointer.png"), pointer;
display: flex;
align-items: center;
justify-content: center;
border: solid 0px black;
width: 44px;
height: 44px;
background: #666;
box-shadow: 2px 2px 24px #444;
border-radius: 48px;
transform: translateY(15px);
transition-timing-function: ease-in-out;
transition: all 0.3s;
margin: 0 4%;
&.hide {
transform: translateY(60px);
}
}
.btn-cam-action div.disabled {
background: #d75555;
}
.btn-cam-action div.enabled {
background: #73c973;
}
.btn-cam-action:hover div {
transform: translateY(0);
}
.btn-cam-action div:hover {
background: #407cf7;
box-shadow: 4px 4px 48px #666;
transition: 120ms;
}
.btn-micro {
pointer-events: auto;
}
.btn-video {
pointer-events: auto;
transition: all 0.25s;
}
.btn-monitor {
pointer-events: auto;
}
.btn-layout {
pointer-events: auto;
transition: all 0.15s;
}
.btn-cam-action div img {
height: 22px;
width: 30px;
position: relative;
cursor: url("../../style/images/cursor_pointer.png"), pointer;
}
.btn-follow {
pointer-events: auto;
img {
filter: brightness(0) invert(1);
}
}
@media (hover: none) {
/**
* If we cannot hover over elements, let's display camera button in full.
*/
.btn-cam-action {
div {
transform: translateY(0px);
}
}
}
@include media-breakpoint-up(sm) {
.btn-cam-action {
right: 0;
width: 100%;
height: 40%;
max-height: 40px;
div {
width: 20%;
max-height: 44px;
}
}
}
</style>

View file

@ -43,7 +43,7 @@
<svelte:window on:keydown={onKeyDown} on:click={onClick} />
<aside class="chatWindow" transition:fly={{ x: -1000, duration: 500 }} bind:this={chatWindowElement}>
<p class="close-icon" on:click={closeChat}>&times</p>
<p class="close-icon noselect" on:click={closeChat}>&times</p>
<section class="messagesList" bind:this={listDom}>
<ul>
<li><p class="system-text">{$LL.chat.intro()}</p></li>
@ -78,7 +78,7 @@
}
aside.chatWindow {
z-index: 100;
z-index: 1000;
pointer-events: auto;
position: absolute;
top: 0;

View file

@ -83,6 +83,8 @@
</form>
<style lang="scss">
@import "../../../style/breakpoints.scss";
form.customCharacterScene {
font-family: "Press Start 2P";
pointer-events: auto;
@ -129,7 +131,7 @@
}
}
@media only screen and (max-width: 800px) {
@include media-breakpoint-up(md) {
form.customCharacterScene button.customCharacterSceneButtonLeft {
left: 5vw;
}

View file

@ -0,0 +1,32 @@
<script lang="typescript">
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
import MediaBox from "../Video/MediaBox.svelte";
export let highlightedEmbedScreen: EmbedScreen | null;
export let full = false;
$: clickable = !full;
</script>
<aside class="cameras-container" class:full>
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
{#if !highlightedEmbedScreen || highlightedEmbedScreen.type !== "streamable" || (highlightedEmbedScreen.type === "streamable" && highlightedEmbedScreen.embed !== peer)}
<MediaBox streamable={peer} isClickable={clickable} />
{/if}
{/each}
</aside>
<style lang="scss">
.cameras-container {
flex: 0 0 25%;
overflow-y: auto;
overflow-x: hidden;
&:first-child {
margin-top: 2%;
}
&.full {
flex: 0 0 100%;
}
}
</style>

View file

@ -0,0 +1,272 @@
<script lang="typescript">
import { onMount } from "svelte";
import { ICON_URL } from "../../Enum/EnvironmentVariable";
import { coWebsitesNotAsleep, mainCoWebsite } from "../../Stores/CoWebsiteStore";
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { CoWebsite } from "../../WebRtc/CoWebsiteManager";
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
export let index: number;
export let coWebsite: CoWebsite;
export let vertical: boolean;
let icon: HTMLImageElement;
let iconLoaded = false;
let state = coWebsite.state;
const coWebsiteUrl = coWebsite.iframe.src;
const urlObject = new URL(coWebsiteUrl);
onMount(() => {
icon.src = `${ICON_URL}/icon?url=${urlObject.hostname}&size=64..96..256&fallback_icon_color=14304c`;
icon.alt = urlObject.hostname;
icon.onload = () => {
iconLoaded = true;
};
});
async function onClick() {
if (vertical) {
coWebsiteManager.goToMain(coWebsite);
} else if ($mainCoWebsite) {
if ($mainCoWebsite.iframe.id === coWebsite.iframe.id) {
const coWebsites = $coWebsitesNotAsleep;
const newMain = $highlightedEmbedScreen ?? coWebsites.length > 1 ? coWebsites[1] : undefined;
if (newMain) {
coWebsiteManager.goToMain(coWebsite);
}
} else {
highlightedEmbedScreen.toggleHighlight({
type: "cowebsite",
embed: coWebsite,
});
}
}
if ($state === "asleep") {
await coWebsiteManager.loadCoWebsite(coWebsite);
}
coWebsiteManager.resizeAllIframes();
}
function noDrag() {
return false;
}
let isHighlight: boolean = false;
let isMain: boolean = false;
$: {
isMain = $mainCoWebsite !== undefined && $mainCoWebsite.iframe === coWebsite.iframe;
isHighlight =
$highlightedEmbedScreen !== null &&
$highlightedEmbedScreen.type === "cowebsite" &&
$highlightedEmbedScreen.embed.iframe === coWebsite.iframe;
}
</script>
<div
id={"cowebsite-thumbnail-" + index}
class="cowebsite-thumbnail nes-pointer"
class:asleep={$state === "asleep"}
class:loading={$state === "loading"}
class:ready={$state === "ready"}
class:displayed={isMain || isHighlight}
class:vertical
on:click={onClick}
>
<img
class="cowebsite-icon noselect nes-pointer"
class:hide={!iconLoaded}
bind:this={icon}
on:dragstart|preventDefault={noDrag}
alt=""
/>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="cowebsite-icon"
class:hide={iconLoaded}
style="margin: auto; background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%; shape-rendering: auto;"
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid"
>
<rect x="19" y="19" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0s"
calcMode="discrete"
/>
</rect><rect x="40" y="19" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0.125s"
calcMode="discrete"
/>
</rect><rect x="61" y="19" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0.25s"
calcMode="discrete"
/>
</rect><rect x="19" y="40" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0.875s"
calcMode="discrete"
/>
</rect><rect x="61" y="40" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0.375s"
calcMode="discrete"
/>
</rect><rect x="19" y="61" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0.75s"
calcMode="discrete"
/>
</rect><rect x="40" y="61" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0.625s"
calcMode="discrete"
/>
</rect><rect x="61" y="61" width="20" height="20" fill="#14304c">
<animate
attributeName="fill"
values="#365dff;#14304c;#14304c"
keyTimes="0;0.125;1"
dur="1s"
repeatCount="indefinite"
begin="0.5s"
calcMode="discrete"
/>
</rect>
</svg>
</div>
<style lang="scss">
.cowebsite-thumbnail {
position: relative;
padding: 0;
background-color: rgba(#000000, 0.6);
margin: 12px;
margin-top: auto;
margin-bottom: auto;
&::before {
content: "";
position: absolute;
width: 58px;
height: 58px;
left: -8px;
top: -8px;
margin: 4px;
border-style: solid;
border-width: 4px;
border-image-slice: 3;
border-image-width: 3;
border-image-repeat: stretch;
border-image-source: url('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" ?><svg version="1.1" width="8" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M3 1 h1 v1 h-1 z M4 1 h1 v1 h-1 z M2 2 h1 v1 h-1 z M5 2 h1 v1 h-1 z M1 3 h1 v1 h-1 z M6 3 h1 v1 h-1 z M1 4 h1 v1 h-1 z M6 4 h1 v1 h-1 z M2 5 h1 v1 h-1 z M5 5 h1 v1 h-1 z M3 6 h1 v1 h-1 z M4 6 h1 v1 h-1 z" fill="rgb(33,37,41)" /></svg>');
border-image-outset: 1;
}
&.vertical {
margin: 7px;
&::before {
width: 48px;
height: 48px;
}
.cowebsite-icon {
width: 40px;
height: 40px;
}
}
&.displayed {
&:not(.vertical) {
animation: activeThumbnail 300ms ease-in 0s forwards;
}
}
&.asleep {
filter: grayscale(100%);
--webkit-filter: grayscale(100%);
}
&.loading {
animation: 2500ms ease-in-out 0s infinite alternate backgroundLoading;
}
&.ready {
&::before {
border-image-source: url('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8" ?><svg version="1.1" width="8" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M3 1 h1 v1 h-1 z M4 1 h1 v1 h-1 z M2 2 h1 v1 h-1 z M5 2 h1 v1 h-1 z M1 3 h1 v1 h-1 z M6 3 h1 v1 h-1 z M1 4 h1 v1 h-1 z M6 4 h1 v1 h-1 z M2 5 h1 v1 h-1 z M5 5 h1 v1 h-1 z M3 6 h1 v1 h-1 z M4 6 h1 v1 h-1 z" fill="rgb(38, 74, 110)" /></svg>');
}
}
@keyframes backgroundLoading {
0% {
background-color: rgba(#000000, 0.6);
}
100% {
background-color: #25598e;
}
}
@keyframes activeThumbnail {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-15px);
}
}
.cowebsite-icon {
width: 50px;
height: 50px;
object-fit: cover;
&.hide {
display: none;
}
}
}
</style>

View file

@ -0,0 +1,42 @@
<script lang="typescript">
import { coWebsites } from "../../Stores/CoWebsiteStore";
import CoWebsiteThumbnail from "./CoWebsiteThumbnailSlot.svelte";
export let vertical = false;
</script>
{#if $coWebsites.length > 0}
<div id="cowebsite-thumbnail-container" class:vertical>
{#each [...$coWebsites.values()] as coWebsite, index (coWebsite.iframe.id)}
<CoWebsiteThumbnail {index} {coWebsite} {vertical} />
{/each}
</div>
{/if}
<style lang="scss">
#cowebsite-thumbnail-container {
pointer-events: all;
height: 100px;
width: 100%;
display: flex;
position: absolute;
bottom: 5px;
left: 2%;
overflow-x: auto;
overflow-y: hidden;
&.vertical {
height: auto !important;
width: auto !important;
bottom: auto !important;
left: auto !important;
position: relative;
overflow-x: hidden;
overflow-y: auto;
flex-direction: column;
align-items: center;
padding-top: 4px;
padding-bottom: 4px;
}
}
</style>

View file

@ -0,0 +1,22 @@
<script lang="typescript">
import PresentationLayout from "./Layouts/PresentationLayout.svelte";
import MozaicLayout from "./Layouts/MozaicLayout.svelte";
import { LayoutMode } from "../../WebRtc/LayoutManager";
import { embedScreenLayout } from "../../Stores/EmbedScreensStore";
</script>
<div id="embedScreensContainer">
{#if $embedScreenLayout === LayoutMode.Presentation}
<PresentationLayout />
{:else}
<MozaicLayout />
{/if}
</div>
<style lang="scss">
#embedScreensContainer {
display: flex;
padding-top: 2%;
height: 100%;
}
</style>

View file

@ -0,0 +1,61 @@
<script lang="ts">
import { onMount } from "svelte";
import { highlightedEmbedScreen } from "../../../Stores/EmbedScreensStore";
import { streamableCollectionStore } from "../../../Stores/StreamableCollectionStore";
import MediaBox from "../../Video/MediaBox.svelte";
let layoutDom: HTMLDivElement;
const resizeObserver = new ResizeObserver(() => {});
onMount(() => {
resizeObserver.observe(layoutDom);
highlightedEmbedScreen.removeHighlight();
});
</script>
<div id="mozaic-layout" bind:this={layoutDom}>
<div
class="media-container"
class:full-width={$streamableCollectionStore.size === 1 || $streamableCollectionStore.size === 2}
class:quarter={$streamableCollectionStore.size === 3 || $streamableCollectionStore.size === 4}
>
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
<MediaBox
streamable={peer}
mozaicFullWidth={$streamableCollectionStore.size === 1 || $streamableCollectionStore.size === 2}
mozaicQuarter={$streamableCollectionStore.size === 3 || $streamableCollectionStore.size === 4}
/>
{/each}
</div>
</div>
<style lang="scss">
#mozaic-layout {
height: 100%;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
.media-container {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 33.3% 33.3% 33.3%;
align-items: center;
justify-content: center;
overflow-y: auto;
overflow-x: hidden;
&.full-width {
grid-template-columns: 100%;
grid-template-rows: 50% 50%;
}
&.quarter {
grid-template-columns: 50% 50%;
grid-template-rows: 50% 50%;
}
}
}
</style>

View file

@ -0,0 +1,143 @@
<script lang="ts">
import { highlightedEmbedScreen } from "../../../Stores/EmbedScreensStore";
import CamerasContainer from "../CamerasContainer.svelte";
import MediaBox from "../../Video/MediaBox.svelte";
import { coWebsiteManager } from "../../../WebRtc/CoWebsiteManager";
import { afterUpdate, onMount } from "svelte";
import { isMediaBreakpointDown, isMediaBreakpointUp } from "../../../Utils/BreakpointsUtils";
import { peerStore } from "../../../Stores/PeerStore";
function closeCoWebsite() {
if ($highlightedEmbedScreen?.type === "cowebsite") {
if ($highlightedEmbedScreen.embed.closable) {
coWebsiteManager.closeCoWebsite($highlightedEmbedScreen.embed).catch(() => {
console.error("Error during co-website highlighted closing");
});
} else {
coWebsiteManager.unloadCoWebsite($highlightedEmbedScreen.embed).catch(() => {
console.error("Error during co-website highlighted unloading");
});
}
}
}
afterUpdate(() => {
if ($highlightedEmbedScreen) {
coWebsiteManager.resizeAllIframes();
}
});
let layoutDom: HTMLDivElement;
let displayCoWebsiteContainer = isMediaBreakpointDown("lg");
let displayFullMedias = isMediaBreakpointUp("sm");
const resizeObserver = new ResizeObserver(() => {
displayCoWebsiteContainer = isMediaBreakpointDown("lg");
displayFullMedias = isMediaBreakpointUp("sm");
if (!displayCoWebsiteContainer && $highlightedEmbedScreen && $highlightedEmbedScreen.type === "cowebsite") {
highlightedEmbedScreen.removeHighlight();
}
if (displayFullMedias) {
highlightedEmbedScreen.removeHighlight();
}
});
onMount(() => {
resizeObserver.observe(layoutDom);
});
</script>
<div id="presentation-layout" bind:this={layoutDom} class:full-medias={displayFullMedias}>
{#if displayFullMedias}
<div id="full-medias">
<CamerasContainer full={true} highlightedEmbedScreen={$highlightedEmbedScreen} />
</div>
{:else}
<div id="embed-left-block" class:full={$peerStore.size === 0}>
<div id="main-embed-screen">
{#if $highlightedEmbedScreen}
{#if $highlightedEmbedScreen.type === "streamable"}
{#key $highlightedEmbedScreen.embed.uniqueId}
<MediaBox
isHightlighted={true}
isClickable={true}
streamable={$highlightedEmbedScreen.embed}
/>
{/key}
{:else if $highlightedEmbedScreen.type === "cowebsite"}
{#key $highlightedEmbedScreen.embed.iframe.id}
<div
id={"cowebsite-slot-" + $highlightedEmbedScreen.embed.iframe.id}
class="highlighted-cowebsite nes-container is-rounded"
>
<div class="actions">
<button type="button" class="nes-btn is-error close" on:click={closeCoWebsite}
>&times;</button
>
</div>
</div>
{/key}
{/if}
{/if}
</div>
</div>
{#if $peerStore.size > 0}
<CamerasContainer highlightedEmbedScreen={$highlightedEmbedScreen} />
{/if}
{/if}
</div>
<style lang="scss">
#presentation-layout {
height: 100%;
width: 100%;
display: flex;
&.full-medias {
overflow-y: auto;
overflow-x: hidden;
}
}
#embed-left-block {
display: flex;
flex-direction: column;
flex: 0 0 75%;
height: 100%;
width: 75%;
&.full {
flex: 0 0 98% !important;
width: 98% !important;
}
}
#main-embed-screen {
height: 100%;
margin-bottom: 3%;
.highlighted-cowebsite {
height: 100% !important;
width: 96%;
background-color: rgba(#000000, 0.6);
margin: 0 !important;
.actions {
z-index: 200;
position: relative;
display: flex;
flex-direction: row;
justify-content: end;
gap: 2%;
button {
pointer-events: all;
}
}
}
}
</style>

View file

@ -3,8 +3,8 @@
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
import { onDestroy, onMount } from "svelte";
import { EmojiButton } from "@joeattardi/emoji-button";
import { isMobile } from "../../Enum/EnvironmentVariable";
import LL from "../../i18n/i18n-svelte";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
let emojiContainer: HTMLElement;
let picker: EmojiButton;
@ -20,7 +20,7 @@
"--secondary-text-color": "whitesmoke",
"--category-button-color": "whitesmoke",
},
emojisPerRow: isMobile() ? 6 : 8,
emojisPerRow: isMediaBreakpointUp("md") ? 6 : 8,
autoFocusSearch: false,
style: "twemoji",
showPreview: false,
@ -86,6 +86,8 @@
height: 100%;
justify-content: center;
align-items: center;
position: absolute;
z-index: 300;
}
.emote-menu {

View file

@ -127,6 +127,8 @@
</form>
<style lang="scss">
@import "../../../style/breakpoints.scss";
.enableCameraScene {
pointer-events: auto;
margin: 20px auto 0;
@ -214,7 +216,7 @@
}
}
@media only screen and (max-width: 800px) {
@include media-breakpoint-up(md) {
.enableCameraScene h2 {
font-size: 80%;
}

View file

@ -0,0 +1,33 @@
<script lang="typescript">
import followImg from "../images/follow.svg";
export let hidden: Boolean;
let cancelButton = false;
</script>
<div class="btn-follow" class:hide={hidden} class:cancel={cancelButton}>
<img src={followImg} alt="" />
</div>
<style lang="scss">
.btn-follow {
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
display: flex;
align-items: center;
justify-content: center;
border: solid 0px black;
width: 44px;
height: 44px;
background: #666;
box-shadow: 2px 2px 24px #444;
border-radius: 48px;
transform: translateY(15px);
transition-timing-function: ease-in-out;
margin: 0 4%;
img {
filter: brightness(0) invert(1);
}
}
</style>

View file

@ -1,9 +1,5 @@
<!--
vim: ft=typescript
-->
<script lang="ts">
import { gameManager } from "../../Phaser/Game/GameManager";
import followImg from "../images/follow.svg";
import { followStateStore, followRoleStore, followUsersStore } from "../../Stores/FollowStore";
import LL from "../../i18n/i18n-svelte";
@ -14,10 +10,6 @@ vim: ft=typescript
return user ? user.PlayerValue : "";
}
function sendFollowRequest() {
gameScene.CurrentPlayer.sendFollowRequest();
}
function acceptFollowRequest() {
gameScene.CurrentPlayer.startFollowing();
}
@ -83,7 +75,7 @@ vim: ft=typescript
{#if $followStateStore === "active" || $followStateStore === "ending"}
<div class="interact-status nes-container is-rounded">
<section class="interact-status">
<section>
{#if $followRoleStore === "follower"}
<p>{$LL.follow.interactStatus.following({ leader: name($followUsersStore[0]) })}</p>
{:else if $followUsersStore.length === 0}
@ -109,48 +101,27 @@ vim: ft=typescript
</div>
{/if}
{#if $followStateStore === "off"}
<button
type="button"
class="nes-btn is-primary follow-menu-button"
on:click|preventDefault={sendFollowRequest}
title="Ask others to follow"><img class="background-img" src={followImg} alt="" /></button
>
{/if}
{#if $followStateStore === "active" || $followStateStore === "ending"}
{#if $followRoleStore === "follower"}
<button
type="button"
class="nes-btn is-error follow-menu-button"
on:click|preventDefault={reset}
title="Stop following"><img class="background-img" src={followImg} alt="" /></button
>
{:else}
<button
type="button"
class="nes-btn is-error follow-menu-button"
on:click|preventDefault={reset}
title="Stop leading the way"><img class="background-img" src={followImg} alt="" /></button
>
{/if}
{/if}
<style lang="scss">
@import "../../../style/breakpoints.scss";
.nes-container {
padding: 5px;
}
div.interact-status {
.interact-status {
background-color: #333333;
color: whitesmoke;
position: relative;
height: 2.7em;
position: absolute;
max-height: 2.7em;
width: 40vw;
top: 87vh;
margin: auto;
text-align: center;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
z-index: 400;
}
div.interact-menu {
@ -158,10 +129,14 @@ vim: ft=typescript
background-color: #333333;
color: whitesmoke;
position: relative;
position: absolute;
width: 60vw;
top: 60vh;
margin: auto;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
z-index: 150;
section.interact-menu-title {
margin-bottom: 20px;
@ -189,23 +164,16 @@ vim: ft=typescript
}
}
.follow-menu-button {
position: absolute;
bottom: 10px;
left: 10px;
pointer-events: all;
}
@media only screen and (max-width: 800px) {
div.interact-status {
width: 100vw;
@include media-breakpoint-up(md) {
.interact-status {
width: 90vw;
top: 78vh;
font-size: 0.75em;
}
div.interact-menu {
height: 21vh;
width: 100vw;
max-height: 21vh;
width: 90vw;
font-size: 0.75em;
}
}

View file

@ -22,7 +22,7 @@
<form
class="helpCameraSettings nes-container"
on:submit|preventDefault={close}
transition:fly={{ y: -900, duration: 500 }}
transition:fly={{ y: -50, duration: 500 }}
>
<section>
<h2>{$LL.camera.help.title()}</h2>
@ -55,9 +55,12 @@
background: #eceeee;
margin-left: auto;
margin-right: auto;
margin-top: 10vh;
left: 0;
right: 0;
margin-top: 4%;
max-height: 80vh;
max-width: 80vw;
z-index: 600;
overflow: auto;
text-align: center;

View file

@ -21,9 +21,11 @@
left: 0;
right: 0;
bottom: 40px;
margin: 0 auto;
margin-right: auto;
margin-left: auto;
padding: 0;
width: clamp(200px, 20vw, 20vw);
z-index: 155;
display: flex;
flex-direction: column;
@ -31,6 +33,10 @@
animation: moveMessage 0.5s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
div {
margin-bottom: 5%;
}
}
div.nes-container.is-rounded {

View file

@ -0,0 +1,170 @@
<script lang="typescript">
import { onMount } from "svelte";
import { audioManagerVisibilityStore } from "../Stores/AudioManagerStore";
import { embedScreenLayout, hasEmbedScreen } from "../Stores/EmbedScreensStore";
import { emoteMenuStore } from "../Stores/EmoteStore";
import { myCameraVisibilityStore } from "../Stores/MyCameraStoreVisibility";
import { requestVisitCardsStore } from "../Stores/GameStore";
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore";
import { layoutManagerActionVisibilityStore } from "../Stores/LayoutManagerStore";
import { menuIconVisiblilityStore, menuVisiblilityStore, warningContainerStore } from "../Stores/MenuStore";
import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore";
import AudioManager from "./AudioManager/AudioManager.svelte";
import CameraControls from "./CameraControls.svelte";
import EmbedScreensContainer from "./EmbedScreens/EmbedScreensContainer.svelte";
import EmoteMenu from "./EmoteMenu/EmoteMenu.svelte";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
import LayoutActionManager from "./LayoutActionManager/LayoutActionManager.svelte";
import Menu from "./Menu/Menu.svelte";
import MenuIcon from "./Menu/MenuIcon.svelte";
import MyCamera from "./MyCamera.svelte";
import ReportMenu from "./ReportMenu/ReportMenu.svelte";
import VisitCard from "./VisitCard/VisitCard.svelte";
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
import { isMediaBreakpointDown, isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
import CoWebsitesContainer from "./EmbedScreens/CoWebsitesContainer.svelte";
import FollowMenu from "./FollowMenu/FollowMenu.svelte";
import { followStateStore } from "../Stores/FollowStore";
import { peerStore } from "../Stores/PeerStore";
import { banMessageStore } from "../Stores/TypeMessageStore/BanMessageStore";
import BanMessageContainer from "./TypeMessage/BanMessageContainer.svelte";
import { textMessageStore } from "../Stores/TypeMessageStore/TextMessageStore";
import TextMessageContainer from "./TypeMessage/TextMessageContainer.svelte";
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
import AudioPlaying from "./UI/AudioPlaying.svelte";
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore";
import LimitRoomModal from "./Modal/LimitRoomModal.svelte";
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
import { LayoutMode } from "../WebRtc/LayoutManager";
let mainLayout: HTMLDivElement;
let displayCoWebsiteContainerMd = isMediaBreakpointUp("md");
let displayCoWebsiteContainerLg = isMediaBreakpointDown("lg");
const resizeObserver = new ResizeObserver(() => {
displayCoWebsiteContainerMd = isMediaBreakpointUp("md");
displayCoWebsiteContainerLg = isMediaBreakpointDown("lg");
});
onMount(() => {
resizeObserver.observe(mainLayout);
});
</script>
<div id="main-layout" bind:this={mainLayout}>
<aside id="main-layout-left-aside">
{#if $menuIconVisiblilityStore}
<MenuIcon />
{/if}
{#if $embedScreenLayout === LayoutMode.VideoChat || displayCoWebsiteContainerMd}
<CoWebsitesContainer vertical={true} />
{/if}
</aside>
<section id="main-layout-main">
{#if $menuVisiblilityStore}
<Menu />
{/if}
{#if $banMessageStore.length > 0}
<BanMessageContainer />
{:else if $textMessageStore.length > 0}
<TextMessageContainer />
{/if}
{#if $soundPlayingStore}
<AudioPlaying url={$soundPlayingStore} />
{/if}
{#if $warningContainerStore}
<WarningContainer />
{/if}
{#if $showReportScreenStore !== userReportEmpty}
<ReportMenu />
{/if}
{#if $helpCameraSettingsVisibleStore}
<HelpCameraSettingsPopup />
{/if}
{#if $audioManagerVisibilityStore}
<AudioManager />
{/if}
{#if $showLimitRoomModalStore}
<LimitRoomModal />
{/if}
{#if $showShareLinkMapModalStore}
<ShareLinkMapModal />
{/if}
{#if $followStateStore !== "off" || $peerStore.size > 0}
<FollowMenu />
{/if}
{#if $requestVisitCardsStore}
<VisitCard visitCardUrl={$requestVisitCardsStore} />
{/if}
{#if $emoteMenuStore}
<EmoteMenu />
{/if}
{#if hasEmbedScreen}
<EmbedScreensContainer />
{/if}
</section>
<section id="main-layout-baseline">
{#if displayCoWebsiteContainerLg}
<CoWebsitesContainer />
{/if}
{#if $layoutManagerActionVisibilityStore}
<LayoutActionManager />
{/if}
{#if $myCameraVisibilityStore}
<MyCamera />
<CameraControls />
{/if}
</section>
</div>
<style lang="scss">
@import "../../style/breakpoints.scss";
#main-layout {
display: grid;
grid-template-columns: 120px calc(100% - 120px);
grid-template-rows: 80% 20%;
&-left-aside {
min-width: 80px;
}
&-baseline {
grid-column: 1/3;
}
}
@include media-breakpoint-up(md) {
#main-layout {
grid-template-columns: 15% 85%;
&-left-aside {
min-width: auto;
}
}
}
@include media-breakpoint-up(sm) {
#main-layout {
grid-template-columns: 20% 80%;
}
}
</style>

View file

@ -100,6 +100,8 @@
</div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
.string-HTML {
white-space: pre-line;
}
@ -126,7 +128,7 @@
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
@include media-breakpoint-up(md) {
div.about-room-main {
section.container-overflow {
height: calc(100% - 120px);

View file

@ -67,6 +67,8 @@
</div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
div.global-message-main {
height: calc(100% - 50px);
display: grid;
@ -109,7 +111,7 @@
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
@include media-breakpoint-up(md) {
.global-message-content {
height: calc(100% - 5px);
}

View file

@ -36,6 +36,8 @@
</div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
div.guest-main {
height: calc(100% - 56px);
@ -57,7 +59,7 @@
}
}
@media only screen and (max-width: 900px), only screen and (max-height: 600px) {
@include media-breakpoint-up(md) {
div.guest-main {
section.share-url.not-mobile {
display: none;

View file

@ -125,6 +125,8 @@
</div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
.nes-container {
padding: 5px;
}
@ -136,11 +138,15 @@
pointer-events: auto;
height: 80%;
width: 75%;
top: 10%;
top: 4%;
position: relative;
z-index: 80;
margin: auto;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
position: absolute;
z-index: 900;
display: grid;
grid-template-columns: var(--size-first-columns-grid) calc(100% - var(--size-first-columns-grid));
@ -173,12 +179,12 @@
}
}
@media only screen and (max-width: 800px) {
@include media-breakpoint-up(md) {
div.menu-container-main {
--size-first-columns-grid: 120px;
height: 70%;
top: 55px;
width: 100%;
width: 95%;
font-size: 0.5em;
div.menu-nav-sidebar {

View file

@ -14,6 +14,7 @@
function showMenu() {
menuVisiblilityStore.set(!get(menuVisiblilityStore));
}
function showChat() {
chatVisibilityStore.set(true);
}
@ -21,72 +22,97 @@
function register() {
window.open(`${ADMIN_URL}/second-step-register`, "_self");
}
function showInvite() {
showShareLinkMapModalStore.set(true);
}
function noDrag() {
return false;
}
</script>
<svelte:window />
<main class="menuIcon">
<main class="menuIcon noselect">
{#if $limitMapStore}
<img
src={logoInvite}
alt={$LL.menu.icon.open.invite()}
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showInvite}
/>
<img
src={logoRegister}
alt={$LL.menu.icon.open.register()}
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={register}
/>
{:else}
<img src={logoWA} alt={$LL.menu.icon.open.menu()} class="nes-pointer" on:click|preventDefault={showMenu} />
<img src={logoTalk} alt={$LL.menu.icon.open.chat()} class="nes-pointer" on:click|preventDefault={showChat} />
<img
src={logoWA}
alt={$LL.menu.icon.open.menu()}
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showMenu}
/>
<img
src={logoTalk}
alt={$LL.menu.icon.open.chat()}
class="nes-pointer"
draggable="false"
on:dragstart|preventDefault={noDrag}
on:click|preventDefault={showChat}
/>
{/if}
</main>
<style lang="scss">
@import "../../../style/breakpoints.scss";
.menuIcon {
display: inline-grid;
z-index: 90;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20%;
z-index: 800;
position: relative;
margin: 25px;
img {
pointer-events: auto;
width: 60px;
padding-top: 0;
margin: 3px;
image-rendering: pixelated;
margin: 5%;
}
}
.menuIcon img:hover {
transform: scale(1.2);
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
@include media-breakpoint-up(sm) {
.menuIcon {
display: inline-grid;
z-index: 90;
position: relative;
margin: 25px;
margin-top: 10%;
img {
pointer-events: auto;
width: 60px;
padding-top: 0;
margin: 3px;
}
}
.menuIcon img:hover {
transform: scale(1.2);
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
.menuIcon {
margin: 3px;
img {
width: 50px;
}
}
@include media-breakpoint-up(md) {
.menuIcon {
img {
width: 50px;
}
}
}

View file

@ -102,6 +102,8 @@
</div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
div.customize-main {
width: 100%;
display: inline-flex;
@ -161,7 +163,7 @@
}
}
@media only screen and (max-width: 800px) {
@include media-breakpoint-up(md) {
div.customize-main.content section button {
width: 130px;
}

View file

@ -2,11 +2,11 @@
import { localUserStore } from "../../Connexion/LocalUserStore";
import { videoConstraintStore } from "../../Stores/MediaStore";
import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { menuVisiblilityStore } from "../../Stores/MenuStore";
import LL, { locale } from "../../i18n/i18n-svelte";
import type { Locales } from "../../i18n/i18n-types";
import { displayableLocales, setCurrentLocale } from "../../i18n/locales";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
let fullscreen: boolean = localUserStore.getFullscreen();
let notification: boolean = localUserStore.getNotification() === "granted";
@ -85,6 +85,8 @@
function closeMenu() {
menuVisiblilityStore.set(false);
}
const isMobile = isMediaBreakpointUp("md");
</script>
<div class="settings-main" on:submit|preventDefault={saveSetting}>
@ -93,22 +95,22 @@
<div class="nes-select is-dark">
<select bind:value={valueGame}>
<option value={120}
>{isMobile()
>{isMobile
? $LL.menu.settings.gameQuality.short.high()
: $LL.menu.settings.gameQuality.long.high()}</option
>
<option value={60}
>{isMobile()
>{isMobile
? $LL.menu.settings.gameQuality.short.medium()
: $LL.menu.settings.gameQuality.long.medium()}</option
>
<option value={40}
>{isMobile()
>{isMobile
? $LL.menu.settings.gameQuality.short.small()
: $LL.menu.settings.gameQuality.long.small()}</option
>
<option value={20}
>{isMobile()
>{isMobile
? $LL.menu.settings.gameQuality.short.minimum()
: $LL.menu.settings.gameQuality.long.minimum()}</option
>
@ -120,22 +122,22 @@
<div class="nes-select is-dark">
<select bind:value={valueVideo}>
<option value={30}
>{isMobile()
>{isMobile
? $LL.menu.settings.videoQuality.short.high()
: $LL.menu.settings.videoQuality.long.high()}</option
>
<option value={20}
>{isMobile()
>{isMobile
? $LL.menu.settings.videoQuality.short.medium()
: $LL.menu.settings.videoQuality.long.medium()}</option
>
<option value={10}
>{isMobile()
>{isMobile
? $LL.menu.settings.videoQuality.short.small()
: $LL.menu.settings.videoQuality.long.small()}</option
>
<option value={5}
>{isMobile()
>{isMobile
? $LL.menu.settings.videoQuality.short.minimum()
: $LL.menu.settings.videoQuality.long.minimum()}</option
>
@ -199,6 +201,8 @@
</div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
div.settings-main {
height: calc(100% - 40px);
overflow-y: auto;
@ -236,7 +240,7 @@
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
@include media-breakpoint-up(md) {
div.settings-main {
section {
padding: 0;

View file

@ -32,6 +32,7 @@
max-width: 80vw;
overflow: auto;
text-align: center;
z-index: 500;
h2 {
font-family: "Press Start 2P";

View file

@ -75,6 +75,7 @@
max-width: 80vw;
overflow: auto;
text-align: center;
z-index: 450;
h2 {
font-family: "Press Start 2P";

View file

@ -2,7 +2,7 @@
import { obtainedMediaConstraintStore } from "../Stores/MediaStore";
import { localStreamStore, isSilentStore } from "../Stores/MediaStore";
import SoundMeterWidget from "./SoundMeterWidget.svelte";
import { onDestroy } from "svelte";
import { onDestroy, onMount } from "svelte";
import { srcObject } from "./Video/utils";
import LL from "../i18n/i18n-svelte";
@ -23,15 +23,75 @@
isSilent = value;
});
let cameraContainer: HTMLDivElement;
onMount(() => {
cameraContainer.addEventListener("transitionend", () => {
if (cameraContainer.classList.contains("hide")) {
cameraContainer.style.visibility = "hidden";
}
});
cameraContainer.addEventListener("transitionstart", () => {
if (!cameraContainer.classList.contains("hide")) {
cameraContainer.style.visibility = "visible";
}
});
});
onDestroy(unsubscribeIsSilent);
</script>
<div>
<div class="video-container div-myCamVideo" class:hide={!$obtainedMediaConstraintStore.video || isSilent}>
{#if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="myCamVideo" use:srcObject={stream} autoplay muted playsinline />
<SoundMeterWidget {stream} />
{/if}
</div>
<div class="is-silent" class:hide={isSilent}>{$LL.camera.my.silentZone()}</div>
<div
class="nes-container is-rounded my-cam-video-container"
class:hide={($localStreamStore.type !== "success" || !$obtainedMediaConstraintStore.video) && !isSilent}
bind:this={cameraContainer}
>
{#if isSilent}
<div class="is-silent">{$LL.camera.my.silentZone()}</div>
{:else if $localStreamStore.type === "success" && $localStreamStore.stream}
<video class="my-cam-video" use:srcObject={stream} autoplay muted playsinline />
<SoundMeterWidget {stream} />
{/if}
</div>
<style lang="scss">
@import "../../style/breakpoints.scss";
.my-cam-video-container {
position: absolute;
right: 15px;
bottom: 30px;
max-height: 20%;
transition: transform 1000ms;
padding: 0;
background-color: rgba(#000000, 0.6);
background-clip: content-box;
overflow: hidden;
line-height: 0;
z-index: 250;
&.nes-container.is-rounded {
border-image-outset: 1;
}
}
.my-cam-video-container.hide {
transform: translateX(200%);
}
.my-cam-video {
background-color: #00000099;
max-height: 20vh;
max-width: max(25vw, 150px);
width: 100%;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.is-silent {
font-size: 2em;
color: white;
padding: 40px 20px;
}
</style>

View file

@ -108,12 +108,16 @@
pointer-events: auto;
background-color: #333333;
color: whitesmoke;
position: relative;
z-index: 650;
position: absolute;
height: 70vh;
width: 50vw;
top: 10vh;
margin: auto;
top: 4%;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
section.report-menu-title {
display: grid;
@ -137,13 +141,4 @@
display: none;
}
}
@media only screen and (max-width: 800px) {
div.report-menu-main {
top: 21vh;
height: 60vh;
width: 100vw;
font-size: 0.5em;
}
}
</style>

View file

@ -47,6 +47,8 @@
</form>
<style lang="scss">
@import "../../../style/breakpoints.scss";
form.selectCompanionScene {
font-family: "Press Start 2P";
pointer-events: auto;
@ -85,7 +87,7 @@
}
}
@media only screen and (max-width: 800px) {
@include media-breakpoint-up(md) {
form.selectCompanionScene button.selectCharacterButtonLeft {
left: 5vw;
}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { fly, fade } from "svelte/transition";
import { onMount } from "svelte";
import { gameManager } from "../../Phaser/Game/GameManager";
import type { Message } from "../../Stores/TypeMessageStore/MessageStore";
import { banMessageStore } from "../../Stores/TypeMessageStore/BanMessageStore";
import LL from "../../i18n/i18n-svelte";
@ -13,6 +14,8 @@
onMount(() => {
timeToRead();
const gameScene = gameManager.getCurrentGameScene();
gameScene.playSound("audio-report-message");
});
function timeToRead() {
@ -53,18 +56,19 @@
on:click|preventDefault={closeBanMessage}>{nameButton}</button
>
</div>
<!-- svelte-ignore a11y-media-has-caption -->
<audio id="report-message" autoplay>
<source src="/resources/objects/report-message.mp3" type="audio/mp3" />
</audio>
</div>
<style lang="scss">
div.main-ban-message {
display: flex;
flex-direction: column;
position: relative;
top: 15vh;
position: absolute;
top: 4%;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
z-index: 850;
height: 70vh;
width: 60vw;

View file

@ -11,3 +11,9 @@
</div>
{/each}
</div>
<style lang="scss">
.main-ban-message-container {
z-index: 800;
}
</style>

View file

@ -42,14 +42,17 @@
div.main-text-message {
display: flex;
flex-direction: column;
position: absolute;
max-height: 25vh;
width: 80vw;
max-height: 25%;
width: 60%;
margin-right: auto;
margin-left: auto;
margin-bottom: 16px;
margin-top: 0;
top: 6%;
left: 0;
right: 0;
padding-bottom: 0;
z-index: 240;
pointer-events: auto;
background-color: #333333;

View file

@ -15,7 +15,8 @@
</div>
<style lang="scss">
div.main-text-message-container {
.main-text-message-container {
padding-top: 16px;
z-index: 800;
}
</style>

View file

@ -37,6 +37,7 @@
background-color: black;
border-radius: 30px 0 0 30px;
display: inline-flex;
z-index: 750;
img {
border-radius: 50%;

View file

@ -25,11 +25,17 @@
<style lang="scss">
div.error-div {
pointer-events: auto;
margin-top: 10vh;
margin-top: 4%;
margin-right: auto;
margin-left: auto;
left: 0;
right: 0;
position: absolute;
width: max-content;
max-width: 80vw;
z-index: 230;
height: auto !important;
background-clip: padding-box;
.button-bar {
text-align: center;

View file

@ -1,15 +1,33 @@
<script lang="typescript">
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import type { ScreenSharingLocalMedia } from "../../Stores/ScreenSharingStore";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import type { Streamable } from "../../Stores/StreamableCollectionStore";
import { srcObject } from "./utils";
export let clickable = false;
export let peer: ScreenSharingLocalMedia;
let stream = peer.stream;
export let cssClass: string | undefined;
let embedScreen: EmbedScreen;
if (stream) {
embedScreen = {
type: "streamable",
embed: stream as unknown as Streamable,
};
}
</script>
<div class="video-container {cssClass ? cssClass : ''}" class:hide={!stream}>
{#if stream}
<video use:srcObject={stream} autoplay muted playsinline on:click={() => videoFocusStore.toggleFocus(peer)} />
<video
use:srcObject={stream}
autoplay
muted
playsinline
on:click={() => (clickable ? highlightedEmbedScreen.toggleHighlight(embedScreen) : null)}
/>
{/if}
</div>

View file

@ -7,14 +7,110 @@
import type { Streamable } from "../../Stores/StreamableCollectionStore";
export let streamable: Streamable;
export let isHightlighted = false;
export let isClickable = false;
export let mozaicFullWidth = false;
export let mozaicQuarter = false;
</script>
<div class="media-container">
{#if streamable instanceof VideoPeer}
<VideoMediaBox peer={streamable} />
{:else if streamable instanceof ScreenSharingPeer}
<ScreenSharingMediaBox peer={streamable} />
{:else}
<LocalStreamMediaBox peer={streamable} cssClass="" />
{/if}
<div
class="media-container nes-container is-rounded {isHightlighted ? 'hightlighted' : ''}"
class:clickable={isClickable}
class:mozaic-full-width={mozaicFullWidth}
class:mozaic-quarter={mozaicQuarter}
>
<div>
{#if streamable instanceof VideoPeer}
<VideoMediaBox peer={streamable} clickable={isClickable} />
{:else if streamable instanceof ScreenSharingPeer}
<ScreenSharingMediaBox peer={streamable} clickable={isClickable} />
{:else}
<LocalStreamMediaBox peer={streamable} clickable={isClickable} cssClass="" />
{/if}
</div>
</div>
<style lang="scss">
@import "../../../style/breakpoints.scss";
.media-container {
display: flex;
margin-top: 4%;
margin-bottom: 4%;
margin-left: auto;
margin-right: auto;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s,
max-width 0.2s;
pointer-events: auto;
padding: 0;
max-height: 85%;
max-width: 85%;
&:hover {
margin-top: 2%;
margin-bottom: 2%;
}
&.hightlighted {
margin-top: 0% !important;
margin-bottom: 0% !important;
margin-left: 0% !important;
max-height: 100% !important;
max-width: 96% !important;
&:hover {
margin-top: 0% !important;
margin-bottom: 0% !important;
}
}
&.mozaic-full-width {
width: 95%;
max-width: 95%;
margin-left: 3%;
margin-right: 3%;
margin-top: auto;
margin-bottom: auto;
&:hover {
margin-top: auto;
margin-bottom: auto;
}
}
&.mozaic-quarter {
width: 95%;
max-width: 95%;
margin-top: auto;
margin-bottom: auto;
&:hover {
margin-top: auto;
margin-bottom: auto;
}
}
&.nes-container.is-rounded {
border-image-outset: 1;
}
&.clickable {
cursor: url("../../../style/images/cursor_pointer.png"), pointer;
}
> div {
background-color: rgba(0, 0, 0, 0.6);
display: flex;
width: 100%;
}
}
@include media-breakpoint-only(md) {
.media-container {
margin-top: 10%;
margin-bottom: 10%;
}
}
</style>

View file

@ -1,26 +0,0 @@
<script lang="ts">
import { streamableCollectionStore } from "../../Stores/StreamableCollectionStore";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { afterUpdate } from "svelte";
import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStore";
import MediaBox from "./MediaBox.svelte";
afterUpdate(() => {
biggestAvailableAreaStore.recompute();
});
</script>
<div class="main-section">
{#if $videoFocusStore}
{#key $videoFocusStore.uniqueId}
<MediaBox streamable={$videoFocusStore} />
{/key}
{/if}
</div>
<aside class="sidebar">
{#each [...$streamableCollectionStore.values()] as peer (peer.uniqueId)}
{#if peer !== $videoFocusStore}
<MediaBox streamable={peer} />
{/if}
{/each}
</aside>

View file

@ -1,12 +1,26 @@
<script lang="ts">
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import type { Streamable } from "../../Stores/StreamableCollectionStore";
import type { ScreenSharingPeer } from "../../WebRtc/ScreenSharingPeer";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { getColorByString, srcObject } from "./utils";
export let clickable = false;
export let peer: ScreenSharingPeer;
let streamStore = peer.streamStore;
let name = peer.userName;
let statusStore = peer.statusStore;
let embedScreen: EmbedScreen;
if (peer) {
embedScreen = {
type: "streamable",
embed: peer as unknown as Streamable,
};
}
</script>
<div class="video-container">
@ -20,7 +34,12 @@
<i style="background-color: {getColorByString(name)};">{name}</i>
{:else}
<!-- svelte-ignore a11y-media-has-caption -->
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)} />
<video
use:srcObject={$streamStore}
autoplay
playsinline
on:click={() => (clickable ? highlightedEmbedScreen.toggleHighlight(embedScreen) : null)}
/>
{/if}
</div>

View file

@ -4,11 +4,17 @@
import microphoneCloseImg from "../images/microphone-close.svg";
import reportImg from "./images/report.svg";
import blockSignImg from "./images/blockSign.svg";
import { videoFocusStore } from "../../Stores/VideoFocusStore";
import { showReportScreenStore } from "../../Stores/ShowReportScreenStore";
import { getColorByString, srcObject } from "./utils";
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
import type { EmbedScreen } from "../../Stores/EmbedScreensStore";
import type { Streamable } from "../../Stores/StreamableCollectionStore";
import Woka from "../Woka/Woka.svelte";
import { onMount } from "svelte";
import { isMediaBreakpointOnly } from "../../Utils/BreakpointsUtils";
export let clickable = false;
export let peer: VideoPeer;
let streamStore = peer.streamStore;
@ -19,9 +25,32 @@
function openReport(peer: VideoPeer): void {
showReportScreenStore.set({ userId: peer.userId, userName: peer.userName });
}
let embedScreen: EmbedScreen;
let videoContainer: HTMLDivElement;
let minimized = isMediaBreakpointOnly("md");
if (peer) {
embedScreen = {
type: "streamable",
embed: peer as unknown as Streamable,
};
}
function noDrag() {
return false;
}
const resizeObserver = new ResizeObserver(() => {
minimized = isMediaBreakpointOnly("md");
});
onMount(() => {
resizeObserver.observe(videoContainer);
});
</script>
<div class="video-container">
<div class="video-container" class:no-clikable={!clickable} bind:this={videoContainer}>
{#if $statusStore === "connecting"}
<div class="connecting-spinner" />
{/if}
@ -30,42 +59,64 @@
{/if}
<!-- {#if !$constraintStore || $constraintStore.video === false} -->
<i
class="container {!$constraintStore || $constraintStore.video === false ? '' : 'minimized'}"
class="container"
class:has-video={$constraintStore && $constraintStore.video === true}
class:minimized={(!$constraintStore || $constraintStore.video !== true) && minimized}
style="background-color: {getColorByString(name)};"
>
<span>{peer.userName}</span>
<span style="noselect">{peer.userName}</span>
<div class="woka-icon"><Woka userId={peer.userId} placeholderSrc={""} /></div>
</i>
<!-- {/if} -->
{#if $constraintStore && $constraintStore.audio === false}
<img src={microphoneCloseImg} class="active" alt="Muted" />
<img
src={microphoneCloseImg}
class="active noselect"
draggable="false"
on:dragstart|preventDefault={noDrag}
alt="Muted"
/>
{/if}
<button class="report" on:click={() => openReport(peer)}>
<img alt="Report this user" src={reportImg} />
<span>Report/Block</span>
<img alt="Report this user" draggable="false" on:dragstart|preventDefault={noDrag} src={reportImg} />
<span class="noselect">Report/Block</span>
</button>
<!-- svelte-ignore a11y-media-has-caption -->
<video use:srcObject={$streamStore} autoplay playsinline on:click={() => videoFocusStore.toggleFocus(peer)} />
<img src={blockSignImg} class="block-logo" alt="Block" />
<video
class:no-video={!$constraintStore || $constraintStore.video === false}
use:srcObject={$streamStore}
autoplay
playsinline
on:click={() => (clickable ? highlightedEmbedScreen.toggleHighlight(embedScreen) : null)}
/>
<img src={blockSignImg} draggable="false" on:dragstart|preventDefault={noDrag} class="block-logo" alt="Block" />
{#if $constraintStore && $constraintStore.audio !== false}
<SoundMeterWidget stream={$streamStore} />
{/if}
</div>
<style>
<style lang="scss">
.container {
display: flex;
flex-direction: column;
padding-top: 15px;
}
.minimized {
left: auto;
transform: scale(0.5);
opacity: 0.5;
}
&.has-video {
left: auto;
transform: scale(0.5);
opacity: 0.5;
}
.woka-icon {
margin-right: 3px;
&.minimized {
transform: scale(0.5);
opacity: 0.5;
}
.woka-icon {
margin-right: 3px;
}
}
video.no-video {
visibility: collapse;
}
</style>

View file

@ -1,16 +1,16 @@
<script lang="ts">
import { LayoutMode } from "../../WebRtc/LayoutManager";
import { layoutModeStore } from "../../Stores/StreamableCollectionStore";
import PresentationLayout from "./PresentationLayout.svelte";
import ChatLayout from "./ChatLayout.svelte";
// import {LayoutMode} from "../../WebRtc/LayoutManager";
// import {layoutModeStore} from "../../Stores/StreamableCollectionStore";
// import PresentationLayout from "./PresentationLayout.svelte";
// import ChatLayout from "./ChatLayout.svelte";
</script>
<div class="video-overlay">
{#if $layoutModeStore === LayoutMode.Presentation}
<!-- {#if $layoutModeStore === LayoutMode.Presentation }
<PresentationLayout />
{:else}
<ChatLayout />
{/if}
{/if} -->
</div>
<style lang="scss">

View file

@ -57,6 +57,7 @@
height: 120px;
margin: auto;
animation: spin 2s linear infinite;
z-index: 350;
}
@keyframes spin {

View file

@ -27,18 +27,22 @@
<style lang="scss">
main.warningMain {
pointer-events: auto;
width: 100vw;
width: 80%;
background-color: #f9e81e;
color: #14304c;
text-align: center;
position: absolute;
left: 50%;
top: 4%;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
transform: translate(-50%, 0);
font-family: Lato;
min-width: 300px;
opacity: 0.9;
z-index: 2;
z-index: 700;
h2 {
padding: 5px;
}

View file

@ -22,10 +22,21 @@
src = source ?? placeholderSrc;
});
function noDrag() {
return false;
}
onDestroy(unsubscribe);
</script>
<img {src} alt="" class="nes-pointer" style="--theme-width: {width}; --theme-height: {height}" />
<img
{src}
alt=""
class="nes-pointer noselect"
style="--theme-width: {width}; --theme-height: {height}"
draggable="false"
on:dragstart|preventDefault={noDrag}
/>
<style>
img {

View file

@ -49,6 +49,8 @@
</form>
<style lang="scss">
@import "../../../style/breakpoints.scss";
form.selectCharacterScene {
font-family: "Press Start 2P";
pointer-events: auto;
@ -91,7 +93,7 @@
}
}
@media only screen and (max-width: 800px) {
@include media-breakpoint-up(md) {
form.selectCharacterScene button.selectCharacterButtonLeft {
left: 5vw;
}

View file

@ -42,8 +42,6 @@ export const DISABLE_ANONYMOUS: boolean = getEnv("DISABLE_ANONYMOUS") === "true"
export const OPID_LOGIN_SCREEN_PROVIDER = getEnv("OPID_LOGIN_SCREEN_PROVIDER");
const FALLBACK_LOCALE = getEnv("FALLBACK_LOCALE") || undefined;
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
export {
DEBUG_MODE,
START_ROOM_URL,

View file

@ -63,8 +63,6 @@ export class SoundMeter {
//this.slow = 0.95 * that.slow + 0.05 * that.instant;
//this.clip = clipcount / input.length;
//console.log('instant', this.instant, 'clip', this.clip);
return this.instant;
}

View file

@ -20,7 +20,6 @@ export enum GameMapProperties {
OPEN_WEBSITE = "openWebsite",
OPEN_WEBSITE_ALLOW_API = "openWebsiteAllowApi",
OPEN_WEBSITE_POLICY = "openWebsitePolicy",
OPEN_WEBSITE_WIDTH = "openWebsiteWidth",
OPEN_WEBSITE_POSITION = "openWebsitePosition",
OPEN_WEBSITE_TRIGGER = "openWebsiteTrigger",
OPEN_WEBSITE_TRIGGER_MESSAGE = "openWebsiteTriggerMessage",

View file

@ -9,21 +9,21 @@ import { get } from "svelte/store";
import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager";
import type { ITiledMapLayer } from "../Map/ITiledMap";
import { GameMapProperties } from "./GameMapProperties";
import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore";
enum OpenCoWebsiteState {
LOADING,
ASLEEP,
OPENED,
MUST_BE_CLOSE,
}
interface OpenCoWebsite {
coWebsite: CoWebsite | undefined;
coWebsite: CoWebsite;
state: OpenCoWebsiteState;
}
export class GameMapPropertiesListener {
private coWebsitesOpenByLayer = new Map<ITiledMapLayer, OpenCoWebsite>();
private coWebsitesActionTriggerByLayer = new Map<ITiledMapLayer, string>();
constructor(private scene: GameScene, private gameMap: GameMap) {}
@ -64,10 +64,8 @@ export class GameMapPropertiesListener {
let openWebsiteProperty: string | undefined;
let allowApiProperty: boolean | undefined;
let websitePolicyProperty: string | undefined;
let websiteWidthProperty: number | undefined;
let websitePositionProperty: number | undefined;
let websiteTriggerProperty: string | undefined;
let websiteTriggerMessageProperty: string | undefined;
layer.properties.forEach((property) => {
switch (property.name) {
@ -80,18 +78,12 @@ export class GameMapPropertiesListener {
case GameMapProperties.OPEN_WEBSITE_POLICY:
websitePolicyProperty = property.value as string | undefined;
break;
case GameMapProperties.OPEN_WEBSITE_WIDTH:
websiteWidthProperty = property.value as number | undefined;
break;
case GameMapProperties.OPEN_WEBSITE_POSITION:
websitePositionProperty = property.value as number | undefined;
break;
case GameMapProperties.OPEN_WEBSITE_TRIGGER:
websiteTriggerProperty = property.value as string | undefined;
break;
case GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE:
websiteTriggerMessageProperty = property.value as string | undefined;
break;
}
});
@ -105,27 +97,30 @@ export class GameMapPropertiesListener {
return;
}
const coWebsite = coWebsiteManager.addCoWebsite(
openWebsiteProperty,
this.scene.MapUrlFile,
allowApiProperty,
websitePolicyProperty,
websitePositionProperty,
false
);
this.coWebsitesOpenByLayer.set(layer, {
coWebsite: undefined,
state: OpenCoWebsiteState.LOADING,
coWebsite: coWebsite,
state: OpenCoWebsiteState.ASLEEP,
});
const openWebsiteFunction = () => {
coWebsiteManager
.loadCoWebsite(
openWebsiteProperty as string,
this.scene.MapUrlFile,
allowApiProperty,
websitePolicyProperty,
websiteWidthProperty,
websitePositionProperty
)
.loadCoWebsite(coWebsite)
.then((coWebsite) => {
const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer);
if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) {
coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => console.error(e));
coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
console.error("Error during a co-website closing");
});
this.coWebsitesOpenByLayer.delete(layer);
this.coWebsitesActionTriggerByLayer.delete(layer);
} else {
this.coWebsitesOpenByLayer.set(layer, {
coWebsite,
@ -133,27 +128,17 @@ export class GameMapPropertiesListener {
});
}
})
.catch((e) => console.error(e));
.catch(() => {
console.error("Error during loading a co-website: " + coWebsite.url);
});
layoutManagerActionStore.removeAction(actionUuid);
};
const forceTrigger = localUserStore.getForceCowebsiteTrigger();
if (forceTrigger || websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON) {
if (!websiteTriggerMessageProperty) {
websiteTriggerMessageProperty = "Press SPACE or touch here to open web site";
}
this.coWebsitesActionTriggerByLayer.set(layer, actionUuid);
layoutManagerActionStore.addAction({
uuid: actionUuid,
type: "message",
message: websiteTriggerMessageProperty,
callback: () => openWebsiteFunction(),
userInputManager: this.scene.userInputManager,
});
} else {
if (
!localUserStore.getForceCowebsiteTrigger() &&
websiteTriggerProperty !== ON_ACTION_TRIGGER_BUTTON
) {
openWebsiteFunction();
}
});
@ -194,7 +179,7 @@ export class GameMapPropertiesListener {
return;
}
if (coWebsiteOpen.state === OpenCoWebsiteState.LOADING) {
if (coWebsiteOpen.state === OpenCoWebsiteState.ASLEEP) {
coWebsiteOpen.state = OpenCoWebsiteState.MUST_BE_CLOSE;
}
@ -203,26 +188,6 @@ export class GameMapPropertiesListener {
}
this.coWebsitesOpenByLayer.delete(layer);
if (!websiteTriggerProperty) {
return;
}
const actionStore = get(layoutManagerActionStore);
const actionTriggerUuid = this.coWebsitesActionTriggerByLayer.get(layer);
if (!actionTriggerUuid) {
return;
}
const action =
actionStore && actionStore.length > 0
? actionStore.find((action) => action.uuid === actionTriggerUuid)
: undefined;
if (action) {
layoutManagerActionStore.removeAction(actionTriggerUuid);
}
});
};

View file

@ -252,7 +252,7 @@ export class GameScene extends DirtyScene {
}
this.load.audio("audio-webrtc-in", "/resources/objects/webrtc-in.mp3");
this.load.audio("audio-webrtc-out", "/resources/objects/webrtc-out.mp3");
//this.load.audio('audio-report-message', '/resources/objects/report-message.mp3');
this.load.audio("audio-report-message", "/resources/objects/report-message.mp3");
this.sound.pauseOnBlur = false;
this.load.on(FILE_LOAD_ERROR, (file: { src: string }) => {
@ -632,13 +632,9 @@ export class GameScene extends DirtyScene {
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
const newPeerNumber = peers.size;
if (newPeerNumber > oldPeerNumber) {
this.sound.play("audio-webrtc-in", {
volume: 0.2,
});
this.playSound("audio-webrtc-in");
} else if (newPeerNumber < oldPeerNumber) {
this.sound.play("audio-webrtc-out", {
volume: 0.2,
});
this.playSound("audio-webrtc-out");
}
oldPeerNumber = newPeerNumber;
});
@ -1265,21 +1261,21 @@ ${escapedMessage}
throw new Error("Unknown query source");
}
const coWebsite = await coWebsiteManager.loadCoWebsite(
const coWebsite = coWebsiteManager.addCoWebsite(
openCoWebsite.url,
iframeListener.getBaseUrlFromSource(source),
openCoWebsite.allowApi,
openCoWebsite.allowPolicy,
openCoWebsite.position
openCoWebsite.position,
openCoWebsite.closable ?? true
);
if (!coWebsite) {
throw new Error("Error on opening co-website");
if (openCoWebsite.lazy !== undefined && !openCoWebsite.lazy) {
await coWebsiteManager.loadCoWebsite(coWebsite);
}
return {
id: coWebsite.iframe.id,
position: coWebsite.position,
};
});
@ -1289,7 +1285,6 @@ ${escapedMessage}
return coWebsites.map((coWebsite: CoWebsite) => {
return {
id: coWebsite.iframe.id,
position: coWebsite.position,
};
});
});
@ -1565,6 +1560,12 @@ ${escapedMessage}
}
}
public playSound(sound: string) {
this.sound.play(sound, {
volume: 0.2,
});
}
public cleanupClosingScene(): void {
// stop playing audio, close any open website, stop any open Jitsi
coWebsiteManager.closeCoWebsites().catch((e) => console.error(e));
@ -1602,7 +1603,7 @@ ${escapedMessage}
this.sharedVariablesManager?.close();
this.embeddedWebsiteManager?.close();
mediaManager.hideGameOverlay();
mediaManager.hideMyCamera();
for (const iframeEvents of this.iframeSubscriptionList) {
iframeEvents.unsubscribe();
@ -2111,6 +2112,17 @@ ${escapedMessage}
biggestAvailableAreaStore.recompute();
}
public enableMediaBehaviors() {
const silent = this.gameMap.getCurrentProperties().get(GameMapProperties.SILENT);
this.connection?.setSilent(!!silent);
mediaManager.showMyCamera();
}
public disableMediaBehaviors() {
this.connection?.setSilent(true);
mediaManager.hideMyCamera();
}
public startJitsi(roomName: string, jwt?: string): void {
const allProps = this.gameMap.getCurrentProperties();
const jitsiConfig = this.safeParseJSONstring(
@ -2122,28 +2134,21 @@ ${escapedMessage}
GameMapProperties.JITSI_INTERFACE_CONFIG
);
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
const jitsiWidth = allProps.get(GameMapProperties.JITSI_WIDTH) as number | undefined;
jitsiFactory
.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth)
.catch((e) => console.error(e));
this.connection?.setSilent(true);
mediaManager.hideGameOverlay();
analyticsClient.enteredJitsi(roomName, this.room.id);
//permit to stop jitsi when user close iframe
mediaManager.addTriggerCloseJitsiFrameButton("close-jitsi", () => {
this.stopJitsi();
jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl).catch(() => {
console.error("Cannot start a Jitsi co-website");
});
this.disableMediaBehaviors();
analyticsClient.enteredJitsi(roomName, this.room.id);
}
public stopJitsi(): void {
const silent = this.gameMap.getCurrentProperties().get(GameMapProperties.SILENT);
this.connection?.setSilent(!!silent);
jitsiFactory.stop();
mediaManager.showGameOverlay();
mediaManager.removeTriggerCloseJitsiFrameButton("close-jitsi");
const coWebsite = coWebsiteManager.searchJitsi();
if (coWebsite) {
coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => {
console.error("Error during Jitsi co-website closing", e);
});
}
}
//todo: put this into an 'orchestrator' scene (EntryScene?)

View file

@ -11,10 +11,10 @@ import { areCharacterLayersValid } from "../../Connexion/LocalUser";
import { SelectCharacterSceneName } from "./SelectCharacterScene";
import { activeRowStore, customCharacterSceneVisibleStore } from "../../Stores/CustomCharacterStore";
import { waScaleManager } from "../Services/WaScaleManager";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { CustomizedCharacter } from "../Entity/CustomizedCharacter";
import { get } from "svelte/store";
import { analyticsClient } from "../../Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
export const CustomizeSceneName = "CustomizeScene";
@ -67,12 +67,12 @@ export class CustomizeScene extends AbstractCharacterScene {
customCharacterSceneVisibleStore.set(true);
this.events.addListener("wake", () => {
waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 3 : 1;
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
customCharacterSceneVisibleStore.set(true);
});
waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 3 : 1;
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 3 : 1;
this.Rectangle = this.add.rectangle(
this.cameras.main.worldView.x + this.cameras.main.width / 2,

View file

@ -12,8 +12,8 @@ import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { PinchManager } from "../UserInput/PinchManager";
import { selectCharacterSceneVisibleStore } from "../../Stores/SelectCharacterStore";
import { waScaleManager } from "../Services/WaScaleManager";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { analyticsClient } from "../../Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
//todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene";
@ -60,7 +60,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
selectCharacterSceneVisibleStore.set(true);
this.events.addListener("wake", () => {
waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
selectCharacterSceneVisibleStore.set(true);
});
@ -69,7 +69,7 @@ export class SelectCharacterScene extends AbstractCharacterScene {
}
waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16;
this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xffffff);

View file

@ -9,7 +9,7 @@ import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { PinchManager } from "../UserInput/PinchManager";
import { selectCompanionSceneVisibleStore } from "../../Stores/SelectCompanionStore";
import { waScaleManager } from "../Services/WaScaleManager";
import { isMobile } from "../../Enum/EnvironmentVariable";
import { isMediaBreakpointUp } from "../../Utils/BreakpointsUtils";
export const SelectCompanionSceneName = "SelectCompanionScene";
@ -44,7 +44,7 @@ export class SelectCompanionScene extends ResizableScene {
selectCompanionSceneVisibleStore.set(true);
waScaleManager.saveZoom();
waScaleManager.zoomModifier = isMobile() ? 2 : 1;
waScaleManager.zoomModifier = isMediaBreakpointUp("md") ? 2 : 1;
if (touchScreenManager.supportTouchScreen) {
new PinchManager(this);

View file

@ -37,19 +37,17 @@ export class WaScaleManager {
height: height * devicePixelRatio,
});
if (gameSize.width == 0) {
return;
if (realSize.width !== 0 && gameSize.width !== 0 && devicePixelRatio !== 0) {
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
}
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
this.scaleManager.setZoom(this.actualZoom);
this.scaleManager.resize(gameSize.width, gameSize.height);
// Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves
const style = this.scaleManager.canvas.style;
style.width = Math.ceil(realSize.width / devicePixelRatio) + "px";
style.height = Math.ceil(realSize.height / devicePixelRatio) + "px";
style.width = Math.ceil(realSize.width !== 0 ? realSize.width / devicePixelRatio : 0) + "px";
style.height = Math.ceil(realSize.height !== 0 ? realSize.height / devicePixelRatio : 0) + "px";
// Resize the game element at the same size at the canvas
const gameStyle = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("game").style;

View file

@ -2,14 +2,14 @@ import { get, writable } from "svelte/store";
import type { Box } from "../WebRtc/LayoutManager";
import { HtmlUtils } from "../WebRtc/HtmlUtils";
import { LayoutMode } from "../WebRtc/LayoutManager";
import { layoutModeStore } from "./StreamableCollectionStore";
import { embedScreenLayout } from "./EmbedScreensStore";
/**
* Tries to find the biggest available box of remaining space (this is a space where we can center the character)
*/
function findBiggestAvailableArea(): Box {
const game = HtmlUtils.querySelectorOrFail<HTMLCanvasElement>("#game canvas");
if (get(layoutModeStore) === LayoutMode.VideoChat) {
if (get(embedScreenLayout) === LayoutMode.VideoChat) {
const children = document.querySelectorAll<HTMLDivElement>("div.chat-mode > div");
const htmlChildren = Array.from(children.values());

View file

@ -0,0 +1,51 @@
import { derived, get, writable } from "svelte/store";
import type { CoWebsite } from "../WebRtc/CoWebsiteManager";
function createCoWebsiteStore() {
const { subscribe, set, update } = writable(Array<CoWebsite>());
set(Array<CoWebsite>());
return {
subscribe,
add: (coWebsite: CoWebsite, position?: number) => {
coWebsite.state.subscribe((value) => {
update((currentArray) => currentArray);
});
if (position || position === 0) {
update((currentArray) => {
if (position === 0) {
return [coWebsite, ...currentArray];
} else if (currentArray.length > position) {
const test = [...currentArray.splice(position, 0, coWebsite)];
return [...currentArray.splice(position, 0, coWebsite)];
}
return [...currentArray, coWebsite];
});
return;
}
update((currentArray) => [...currentArray, coWebsite]);
},
remove: (coWebsite: CoWebsite) => {
update((currentArray) => [
...currentArray.filter((currentCoWebsite) => currentCoWebsite.iframe.id !== coWebsite.iframe.id),
]);
},
empty: () => {
set(Array<CoWebsite>());
},
};
}
export const coWebsites = createCoWebsiteStore();
export const coWebsitesNotAsleep = derived([coWebsites], ([$coWebsites]) =>
$coWebsites.filter((coWebsite) => get(coWebsite.state) !== "asleep")
);
export const mainCoWebsite = derived([coWebsites], ([$coWebsites]) =>
$coWebsites.find((coWebsite) => get(coWebsite.state) !== "asleep")
);

View file

@ -0,0 +1,51 @@
import { derived, get, writable } from "svelte/store";
import type { CoWebsite } from "../WebRtc/CoWebsiteManager";
import { LayoutMode } from "../WebRtc/LayoutManager";
import { coWebsites } from "./CoWebsiteStore";
import { Streamable, streamableCollectionStore } from "./StreamableCollectionStore";
export type EmbedScreen =
| {
type: "streamable";
embed: Streamable;
}
| {
type: "cowebsite";
embed: CoWebsite;
};
function createHighlightedEmbedScreenStore() {
const { subscribe, set, update } = writable<EmbedScreen | null>(null);
return {
subscribe,
highlight: (embedScreen: EmbedScreen) => {
set(embedScreen);
},
removeHighlight: () => {
set(null);
},
toggleHighlight: (embedScreen: EmbedScreen) => {
update((currentEmbedScreen) =>
!currentEmbedScreen ||
embedScreen.type !== currentEmbedScreen.type ||
(embedScreen.type === "cowebsite" &&
currentEmbedScreen.type === "cowebsite" &&
embedScreen.embed.iframe.id !== currentEmbedScreen.embed.iframe.id) ||
(embedScreen.type === "streamable" &&
currentEmbedScreen.type === "streamable" &&
embedScreen.embed.uniqueId !== currentEmbedScreen.embed.uniqueId)
? embedScreen
: null
);
},
};
}
export const highlightedEmbedScreen = createHighlightedEmbedScreenStore();
export const embedScreenLayout = writable<LayoutMode>(LayoutMode.Presentation);
export const hasEmbedScreen = derived(
[streamableCollectionStore],
($values) => get(streamableCollectionStore).size + get(coWebsites).length > 0
);

View file

@ -1,17 +0,0 @@
import { writable } from "svelte/store";
/**
* A store that contains whether the game overlay is shown or not.
* Typically, the overlay is hidden when entering Jitsi meet.
*/
function createGameOverlayVisibilityStore() {
const { subscribe, set, update } = writable(false);
return {
subscribe,
showGameOverlay: () => set(true),
hideGameOverlay: () => set(false),
};
}
export const gameOverlayVisibilityStore = createGameOverlayVisibilityStore();

View file

@ -51,6 +51,6 @@ function createLayoutManagerAction() {
export const layoutManagerActionStore = createLayoutManagerAction();
export const layoutManagerVisibilityStore = derived(layoutManagerActionStore, ($layoutManagerActionStore) => {
export const layoutManagerActionVisibilityStore = derived(layoutManagerActionStore, ($layoutManagerActionStore) => {
return !!$layoutManagerActionStore.length;
});

View file

@ -6,7 +6,7 @@ import { BrowserTooOldError } from "./Errors/BrowserTooOldError";
import { errorStore } from "./ErrorStore";
import { getNavigatorType, isIOS, NavigatorType } from "../WebRtc/DeviceUtils";
import { WebviewOnOldIOS } from "./Errors/WebviewOnOldIOS";
import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility";
import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
import { peerStore } from "./PeerStore";
import { privacyShutdownStore } from "./PrivacyShutdownStore";
import { MediaStreamConstraintsError } from "./Errors/MediaStreamConstraintsError";
@ -233,7 +233,7 @@ export const mediaStreamConstraintsStore = derived(
[
requestedCameraState,
requestedMicrophoneState,
gameOverlayVisibilityStore,
myCameraVisibilityStore,
enableCameraSceneVisibilityStore,
videoConstraintStore,
audioConstraintStore,
@ -245,7 +245,7 @@ export const mediaStreamConstraintsStore = derived(
[
$requestedCameraState,
$requestedMicrophoneState,
$gameOverlayVisibilityStore,
$myCameraVisibilityStore,
$enableCameraSceneVisibilityStore,
$videoConstraintStore,
$audioConstraintStore,
@ -283,7 +283,7 @@ export const mediaStreamConstraintsStore = derived(
}
// Disable webcam and microphone when in a Jitsi
if ($gameOverlayVisibilityStore === false) {
if ($myCameraVisibilityStore === false) {
currentVideoConstraint = false;
currentAudioConstraint = false;
}

View file

@ -0,0 +1,8 @@
import { writable } from "svelte/store";
/**
* A store that contains whether my camera & actions is shown or not.
* Typically, the overlay is hidden when entering Jitsi meet.
*/
export const myCameraVisibilityStore = writable(false);

View file

@ -1,7 +1,7 @@
import { derived, Readable, readable, writable } from "svelte/store";
import { peerStore } from "./PeerStore";
import type { LocalStreamStoreValue } from "./MediaStore";
import { gameOverlayVisibilityStore } from "./GameOverlayStoreVisibility";
import { myCameraVisibilityStore } from "./MyCameraStoreVisibility";
declare const navigator: any; // eslint-disable-line @typescript-eslint/no-explicit-any
@ -41,8 +41,8 @@ let previousComputedAudioConstraint: boolean | MediaTrackConstraints = false;
* A store containing the media constraints we want to apply.
*/
export const screenSharingConstraintsStore = derived(
[requestedScreenSharingState, gameOverlayVisibilityStore, peerStore],
([$requestedScreenSharingState, $gameOverlayVisibilityStore, $peerStore], set) => {
[requestedScreenSharingState, myCameraVisibilityStore, peerStore],
([$requestedScreenSharingState, $myCameraVisibilityStore, $peerStore], set) => {
let currentVideoConstraint: boolean | MediaTrackConstraints = true;
let currentAudioConstraint: boolean | MediaTrackConstraints = false;
@ -53,7 +53,7 @@ export const screenSharingConstraintsStore = derived(
}
// Disable screen sharing when in a Jitsi
if (!$gameOverlayVisibilityStore) {
if (!$myCameraVisibilityStore) {
currentVideoConstraint = false;
currentAudioConstraint = false;
}

View file

@ -1,13 +1,10 @@
import { derived, get, Readable, writable } from "svelte/store";
import { derived, get, Readable } from "svelte/store";
import { ScreenSharingLocalMedia, screenSharingLocalMedia } from "./ScreenSharingStore";
import { peerStore, screenSharingStreamStore } from "./PeerStore";
import type { RemotePeer } from "../WebRtc/SimplePeer";
import { LayoutMode } from "../WebRtc/LayoutManager";
export type Streamable = RemotePeer | ScreenSharingLocalMedia;
export const layoutModeStore = writable<LayoutMode>(LayoutMode.Presentation);
/**
* A store that contains everything that can produce a stream (so the peers + the local screen sharing stream)
*/

View file

@ -0,0 +1,112 @@
type InternalBreakpoint = {
beforeBreakpoint: InternalBreakpoint | undefined;
nextBreakpoint: InternalBreakpoint | undefined;
pixels: number;
};
function generateBreakpointsMap(): Map<string, InternalBreakpoint> {
// If is changed don't forget to also change it on SASS.
const breakpoints: { [key: string]: number } = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1400,
};
let beforeBreakpoint: InternalBreakpoint | undefined;
let beforeBreakpointTag: string | undefined;
const mapRender = new Map<string, InternalBreakpoint>();
for (const breakpoint in breakpoints) {
const newBreakpoint = {
beforeBreakpoint: beforeBreakpoint,
nextBreakpoint: undefined,
pixels: breakpoints[breakpoint],
};
if (beforeBreakpointTag && beforeBreakpoint) {
beforeBreakpoint.nextBreakpoint = newBreakpoint;
mapRender.set(beforeBreakpointTag, beforeBreakpoint);
}
mapRender.set(breakpoint, {
beforeBreakpoint: beforeBreakpoint,
nextBreakpoint: undefined,
pixels: breakpoints[breakpoint],
});
beforeBreakpointTag = breakpoint;
beforeBreakpoint = newBreakpoint;
}
return mapRender;
}
const breakpoints = generateBreakpointsMap();
export type Breakpoint = "xs" | "sm" | "md" | "lg" | "xl" | "xxl";
export function isMediaBreakpointUp(breakpoint: Breakpoint): boolean {
if (breakpoint === "xxl") {
return true;
}
const breakpointObject = breakpoints.get(breakpoint);
if (!breakpointObject) {
throw new Error(`Unknown breakpoint: ${breakpoint}`);
}
if (!breakpointObject.nextBreakpoint) {
return false;
}
return breakpointObject.nextBreakpoint.pixels - 1 >= window.innerWidth;
}
export function isMediaBreakpointDown(breakpoint: Breakpoint): boolean {
if (breakpoint === "xs") {
return true;
}
const breakpointObject = breakpoints.get(breakpoint);
if (!breakpointObject) {
throw new Error(`Unknown breakpoint: ${breakpoint}`);
}
return breakpointObject.pixels <= window.innerWidth;
}
export function isMediaBreakpointOnly(breakpoint: Breakpoint): boolean {
const breakpointObject = breakpoints.get(breakpoint);
if (!breakpointObject) {
throw new Error(`Unknown breakpoint: ${breakpoint}`);
}
return (
breakpointObject.pixels <= window.innerWidth &&
(!breakpointObject.nextBreakpoint || breakpointObject.nextBreakpoint.pixels - 1 >= window.innerWidth)
);
}
export function isMediaBreakpointBetween(startBreakpoint: Breakpoint, endBreakpoint: Breakpoint): boolean {
const startBreakpointObject = breakpoints.get(startBreakpoint);
const endBreakpointObject = breakpoints.get(endBreakpoint);
if (!startBreakpointObject) {
throw new Error(`Unknown start breakpoint: ${startBreakpointObject}`);
}
if (!endBreakpointObject) {
throw new Error(`Unknown end breakpoint: ${endBreakpointObject}`);
}
return (
startBreakpointObject.pixels <= innerWidth &&
(!endBreakpointObject.nextBreakpoint || endBreakpointObject.nextBreakpoint.pixels - 1 >= window.innerWidth)
);
}

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,11 @@ export class HtmlUtils {
throw new Error("Cannot find HTML element with id '" + id + "'");
}
public static getElementById<T extends HTMLElement>(id: string): T | undefined {
const elem = document.getElementById(id);
return HtmlUtils.isHtmlElement<T>(elem) ? elem : undefined;
}
public static querySelectorOrFail<T extends HTMLElement>(selector: string): T {
const elem = document.querySelector<T>(selector);
if (HtmlUtils.isHtmlElement<T>(elem)) {

View file

@ -7,6 +7,7 @@ interface jitsiConfigInterface {
startWithAudioMuted: boolean;
startWithVideoMuted: boolean;
prejoinPageEnabled: boolean;
disableDeepLinking: boolean;
}
interface JitsiOptions {
@ -40,6 +41,7 @@ const getDefaultConfig = (): jitsiConfigInterface => {
startWithAudioMuted: !get(requestedMicrophoneState),
startWithVideoMuted: !get(requestedCameraState),
prejoinPageEnabled: false,
disableDeepLinking: false,
};
};
@ -132,64 +134,112 @@ class JitsiFactory {
return slugify(instance.replace("/", "-") + "-" + roomName);
}
public start(
public async start(
roomName: string,
playerName: string,
jwt?: string,
config?: object,
interfaceConfig?: object,
jitsiUrl?: string,
jitsiWidth?: number
): Promise<CoWebsite> {
return coWebsiteManager.addCoWebsite(
async (cowebsiteDiv) => {
// Jitsi meet external API maintains some data in local storage
// which is sent via the appData URL parameter when joining a
// conference. Problem is that this data grows indefinitely. Thus
// after some time the URLs get so huge that loading the iframe
// becomes slow and eventually breaks completely. Thus lets just
// clear jitsi local storage before starting a new conference.
window.localStorage.removeItem("jitsiLocalStorage");
jitsiUrl?: string
) {
const coWebsite = coWebsiteManager.searchJitsi();
const domain = jitsiUrl || JITSI_URL;
if (domain === undefined) {
throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map.");
}
await this.loadJitsiScript(domain);
if (coWebsite) {
await coWebsiteManager.closeCoWebsite(coWebsite);
}
const options: JitsiOptions = {
roomName: roomName,
jwt: jwt,
width: "100%",
height: "100%",
parentNode: cowebsiteDiv,
configOverwrite: mergeConfig(config),
interfaceConfigOverwrite: { ...defaultInterfaceConfig, ...interfaceConfig },
};
if (!options.jwt) {
delete options.jwt;
}
// Jitsi meet external API maintains some data in local storage
// which is sent via the appData URL parameter when joining a
// conference. Problem is that this data grows indefinitely. Thus
// after some time the URLs get so huge that loading the iframe
// becomes slow and eventually breaks completely. Thus lets just
// clear jitsi local storage before starting a new conference.
window.localStorage.removeItem("jitsiLocalStorage");
return new Promise((resolve, reject) => {
const doResolve = (): void => {
const iframe = cowebsiteDiv.querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
if (iframe === null) {
throw new Error("Could not find Jitsi Iframe");
}
resolve(iframe);
};
options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations.
setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
this.jitsiApi.executeCommand("displayName", playerName);
const domain = jitsiUrl || JITSI_URL;
if (domain === undefined) {
throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map.");
}
await this.loadJitsiScript(domain);
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
const options: JitsiOptions = {
roomName: roomName,
jwt: jwt,
width: "100%",
height: "100%",
parentNode: coWebsiteManager.getCoWebsiteBuffer(),
configOverwrite: mergeConfig(config),
interfaceConfigOverwrite: { ...defaultInterfaceConfig, ...interfaceConfig },
};
if (!options.jwt) {
delete options.jwt;
}
const doResolve = (): void => {
const iframe = coWebsiteManager.getCoWebsiteBuffer().querySelector<HTMLIFrameElement>('[id*="jitsi" i]');
if (iframe && this.jitsiApi) {
const coWebsite = coWebsiteManager.addCoWebsiteFromIframe(iframe, false, undefined, 0, false, true);
this.jitsiApi.addListener("videoConferenceLeft", () => {
this.closeOrUnload(coWebsite);
});
},
jitsiWidth,
0
);
this.jitsiApi.addListener("readyToClose", () => {
this.closeOrUnload(coWebsite);
});
}
coWebsiteManager.resizeAllIframes();
};
this.jitsiApi = undefined;
options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations.
setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load
this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options);
this.jitsiApi.executeCommand("displayName", playerName);
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
}
private closeOrUnload = function (coWebsite: CoWebsite) {
if (coWebsite.closable) {
coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
console.error("Error during closing a Jitsi Meet");
});
} else {
coWebsiteManager.unloadCoWebsite(coWebsite).catch(() => {
console.error("Error during unloading a Jitsi Meet");
});
}
};
public restart() {
if (!this.jitsiApi) {
return;
}
this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback);
const coWebsite = coWebsiteManager.searchJitsi();
console.log("jitsi api ", this.jitsiApi);
console.log("iframe cowebsite", coWebsite?.iframe);
if (!coWebsite) {
this.destroy();
return;
}
this.jitsiApi.addListener("videoConferenceLeft", () => {
this.closeOrUnload(coWebsite);
});
this.jitsiApi.addListener("readyToClose", () => {
this.closeOrUnload(coWebsite);
});
}
public stop() {
@ -197,14 +247,16 @@ class JitsiFactory {
return;
}
const jitsiCoWebsite = coWebsiteManager.searchJitsi();
if (jitsiCoWebsite) {
coWebsiteManager.closeJitsi().catch((e) => console.error(e));
}
this.jitsiApi.removeListener("audioMuteStatusChanged", this.audioCallback);
this.jitsiApi.removeListener("videoMuteStatusChanged", this.videoCallback);
}
public destroy() {
if (!this.jitsiApi) {
return;
}
this.stop();
this.jitsiApi?.dispose();
}

View file

@ -7,8 +7,7 @@ import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStor
export type StartScreenSharingCallback = (media: MediaStream) => void;
export type StopScreenSharingCallback = (media: MediaStream) => void;
import { cowebsiteCloseButtonId } from "./CoWebsiteManager";
import { gameOverlayVisibilityStore } from "../Stores/GameOverlayStoreVisibility";
import { myCameraVisibilityStore } from "../Stores/MyCameraStoreVisibility";
import { layoutManagerActionStore } from "../Stores/LayoutManagerStore";
import { MediaStreamConstraintsError } from "../Stores/Errors/MediaStreamConstraintsError";
import { localUserStore } from "../Connexion/LocalUserStore";
@ -20,8 +19,6 @@ export class MediaManager {
startScreenSharingCallBacks: Set<StartScreenSharingCallback> = new Set<StartScreenSharingCallback>();
stopScreenSharingCallBacks: Set<StopScreenSharingCallback> = new Set<StopScreenSharingCallback>();
private triggerCloseJistiFrame: Map<String, Function> = new Map<String, Function>();
private userInputManager?: UserInputManager;
constructor() {
@ -73,36 +70,12 @@ export class MediaManager {
});
}
public showGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail("game-overlay");
gameOverlay.classList.add("active");
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
const functionTrigger = () => {
this.triggerCloseJitsiFrameButton();
};
buttonCloseFrame.removeEventListener("click", () => {
buttonCloseFrame.blur();
functionTrigger();
});
gameOverlayVisibilityStore.showGameOverlay();
public showMyCamera(): void {
myCameraVisibilityStore.set(true);
}
public hideGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail("game-overlay");
gameOverlay.classList.remove("active");
const buttonCloseFrame = HtmlUtils.getElementByIdOrFail(cowebsiteCloseButtonId);
const functionTrigger = () => {
this.triggerCloseJitsiFrameButton();
};
buttonCloseFrame.addEventListener("click", () => {
buttonCloseFrame.blur();
functionTrigger();
});
gameOverlayVisibilityStore.hideGameOverlay();
public hideMyCamera(): void {
myCameraVisibilityStore.set(false);
}
private getScreenSharingId(userId: string): string {
@ -179,20 +152,6 @@ export class MediaManager {
return connectingSpinnerDiv;
}
public addTriggerCloseJitsiFrameButton(id: String, Function: Function) {
this.triggerCloseJistiFrame.set(id, Function);
}
public removeTriggerCloseJitsiFrameButton(id: String) {
this.triggerCloseJistiFrame.delete(id);
}
private triggerCloseJitsiFrameButton(): void {
for (const callback of this.triggerCloseJistiFrame.values()) {
callback();
}
}
public setUserInputManager(userInputManager: UserInputManager) {
this.userInputManager = userInputManager;
}

View file

@ -3,9 +3,9 @@ import type { RoomConnection } from "../Connexion/RoomConnection";
import { MESSAGE_TYPE_CONSTRAINT, PeerStatus } from "./VideoPeer";
import type { UserSimplePeerInterface } from "./SimplePeer";
import { Readable, readable } from "svelte/store";
import { videoFocusStore } from "../Stores/VideoFocusStore";
import { getIceServersConfig } from "../Components/Video/utils";
import { isMobile } from "../Enum/EnvironmentVariable";
import { highlightedEmbedScreen } from "../Stores/EmbedScreensStore";
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
@ -43,7 +43,10 @@ export class ScreenSharingPeer extends Peer {
this.streamStore = readable<MediaStream | null>(null, (set) => {
const onStream = (stream: MediaStream | null) => {
videoFocusStore.focus(this);
highlightedEmbedScreen.highlight({
type: "streamable",
embed: this,
});
set(stream);
};
const onData = (chunk: Buffer) => {
@ -177,7 +180,13 @@ export class ScreenSharingPeer extends Peer {
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
this.removeStream(stream);
this.write(
new Buffer(JSON.stringify({ type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true, isMobile: isMobile() }))
new Buffer(
JSON.stringify({
type: MESSAGE_TYPE_CONSTRAINT,
streamEnded: true,
isMobile: isMediaBreakpointUp("md"),
})
)
);
}
}

View file

@ -86,7 +86,7 @@ export class SimplePeer {
}
);
mediaManager.showGameOverlay();
mediaManager.showMyCamera();
//receive message start
this.Connection.webRtcStartMessageStream.subscribe((message: UserSimplePeerInterface) => {

View file

@ -9,7 +9,7 @@ import { localStreamStore, obtainedMediaConstraintStore, ObtainedMediaStreamCons
import { playersStore } from "../Stores/PlayersStore";
import { chatMessagesStore, newChatMessageSubject } from "../Stores/ChatStore";
import { getIceServersConfig } from "../Components/Video/utils";
import { isMobile } from "../Enum/EnvironmentVariable";
import { isMediaBreakpointUp } from "../Utils/BreakpointsUtils";
const Peer: SimplePeerNamespace.SimplePeer = require("simple-peer");
@ -202,7 +202,7 @@ export class VideoPeer extends Peer {
JSON.stringify({
type: MESSAGE_TYPE_CONSTRAINT,
...constraints,
isMobile: isMobile(),
isMobile: isMediaBreakpointUp("md"),
})
)
);

View file

@ -2,7 +2,7 @@ import "phaser";
import GameConfig = Phaser.Types.Core.GameConfig;
import "../style/index.scss";
import { DEBUG_MODE, isMobile } from "./Enum/EnvironmentVariable";
import { DEBUG_MODE } from "./Enum/EnvironmentVariable";
import { LoginScene } from "./Phaser/Login/LoginScene";
import { ReconnectingScene } from "./Phaser/Reconnecting/ReconnectingScene";
import { SelectCharacterScene } from "./Phaser/Login/SelectCharacterScene";
@ -24,6 +24,7 @@ import App from "./Components/App.svelte";
import { HtmlUtils } from "./WebRtc/HtmlUtils";
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
import { analyticsClient } from "./Administration/AnalyticsClient";
import { isMediaBreakpointUp } from "./Utils/BreakpointsUtils";
const { width, height } = coWebsiteManager.getGameSize();
const valueGameQuality = localUserStore.getGameQualityValue();
@ -90,7 +91,7 @@ const config: GameConfig = {
scene: [
EntryScene,
LoginScene,
isMobile() ? SelectCharacterMobileScene : SelectCharacterScene,
isMediaBreakpointUp("md") ? SelectCharacterMobileScene : SelectCharacterScene,
SelectCompanionScene,
EnableCameraScene,
ReconnectingScene,
@ -136,7 +137,6 @@ const config: GameConfig = {
},
};
//const game = new Phaser.Game(config);
const game = new Game(config);
waScaleManager.setGame(game);
@ -156,7 +156,7 @@ coWebsiteManager.onResize.subscribe(() => {
iframeListener.init();
const app = new App({
target: HtmlUtils.getElementByIdOrFail("svelte-overlay"),
target: HtmlUtils.getElementByIdOrFail("game-overlay"),
props: {
game: game,
},

View file

@ -30,7 +30,7 @@ section.section-input-send-text {
}
}
@media only screen and (max-width: 800px), only screen and (max-height: 800px) {
@include media-breakpoint-up(md) {
section.section-input-send-text {
--height-toolbar: 30%;

View file

@ -0,0 +1,102 @@
@use "sass:map";
// If you modify this breakpoints don't forget to change it also in TS utils.
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);
@function get-upper-breakpoint($breakpoint) {
@return map-get((
xs: map.get($grid-breakpoints, "sm"),
sm: map.get($grid-breakpoints, "md"),
md: map.get($grid-breakpoints, "lg"),
lg: map.get($grid-breakpoints, "xl"),
xl: map.get($grid-breakpoints, "xxl"),
xxl: null,
), $breakpoint);
}
@function get-under-breakpoint($breakpoint) {
@return map-get((
xs: null,
sm: map.get($grid-breakpoints, "xs"),
md: map.get($grid-breakpoints, "sm"),
lg: map.get($grid-breakpoints, "md"),
xl: map.get($grid-breakpoints, "lg"),
xxl: map.get($grid-breakpoints, "xl"),
), $breakpoint);
}
@mixin media-breakpoint-up($upTo) {
@if $upTo == 'xxl' {
@media only screen {
@content;
}
} @else {
$value: get-upper-breakpoint($upTo);
@if $value != null {
$value: $value - 1px;
@media only screen and (max-width: $value) {
@content;
}
}
}
}
@mixin media-breakpoint-down($downFrom) {
@if $downFrom == 'xs' {
@media only screen {
@content;
}
} @else {
$value: map.get($grid-breakpoints, $downFrom);
@if $value != null {
@media only screen and (min-width: $value) {
@content;
}
}
}
}
@mixin media-breakpoint-only($only) {
$maxValue: get-upper-breakpoint($only);
$minValue: map.get($grid-breakpoints, $only);
@if $minValue == null {
} @else if $maxValue != null {
$maxValue: $maxValue - 1px;
@media only screen and (min-width: $minValue) and (max-width: $maxValue) {
@content;
}
} @else {
@media only screen and (min-width: $minValue) {
@content;
}
}
}
@mixin media-breakpoint-between($start, $end) {
$maxValue: get-upper-breakpoint($end);
$minValue: map.get($grid-breakpoints, $start);
@if $minValue == null {
} @else if $maxValue != null {
$maxValue: $maxValue - 1px;
@media only screen and (min-width: $minValue) and (max-width: $maxValue) {
@content;
}
} @else {
@media only screen and (min-width: $minValue) {
@content;
}
}
}

View file

@ -1,136 +0,0 @@
@media (max-aspect-ratio: 1/1) {
#main-container {
display: flex;
flex-direction: column-reverse;
}
#cowebsite {
left: 0;
top: 0;
width: 100%;
height: 50%;
display: flex;
flex-direction: column-reverse;
&.loading {
transform: translateY(-90%);
}
&.hidden {
transform: translateY(-100%);
}
main {
height: 100%;
}
&-container {
display: none;
}
aside {
height: 50px;
cursor: ns-resize;
flex-direction: row-reverse;
align-items: center;
display: flex;
justify-content: space-between;
#cowebsite-aside-holder {
pointer-events: none;
height: 100%;
img {
height: 80%;
}
}
#cowebsite-sub-icons {
display: inline-flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-top: 0;
height: 100%;
visibility: visible;
img {
height: 30px;
width: 30px;
cursor: pointer !important;
border-radius: 50%;
background-color: whitesmoke;
}
&>div {
display: flex;
margin-left: 2px;
margin-right: 2px;
}
}
#cowebsite-aside-buttons {
flex-direction: row-reverse;
margin-left: auto;
margin-bottom: 0;
justify-content: end;
}
img {
cursor: ns-resize;
}
#cowebsite-fullscreen {
padding-top: 0;
}
.top-right-btn {
img {
width: 15px;
}
}
}
}
}
@media (max-width:960px) and (max-height:768px) {
#cowebsite {
&-container {
display: none;
}
aside {
#cowebsite-sub-icons {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 0;
height: 100%;
visibility: visible;
img {
height: 28px;
width: 28px;
cursor: pointer !important;
border-radius: 50%;
background-color: whitesmoke;
}
&>div {
display: flex;
margin-top: 2px;
margin-bottom: 2px;
transform: rotate(-90deg);
}
}
img {
cursor: ns-resize;
}
}
}
}

View file

@ -1,220 +1,3 @@
/* A potentially shared website could appear in an iframe in the cowebsite space. */
#cowebsite {
position: fixed;
z-index: 200;
transition: transform 0.5s;
background-color: whitesmoke;
display: none;
&.loading {
background-color: gray;
}
main {
iframe {
width: 100%;
height: 100%;
max-width: 100vw;
max-height: 100vh;
}
}
aside {
background: gray;
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-between;
#cowebsite-aside-holder {
background: gray;
height: 20px;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
img {
width: 80%;
pointer-events: none;
}
}
#cowebsite-aside-buttons {
display: flex;
flex-direction: column;
margin-bottom: auto;
flex: 1;
justify-content: start;
}
.top-right-btn{
transform: scale(0.5);
cursor: url('./images/cursor_pointer.png'), pointer;
}
#cowebsite-sub-icons {
display: flex;
margin-top: auto;
visibility: hidden;
justify-content: end;
flex: 1;
}
}
&-container {
position: absolute;
display: none;
height: 100%;
width: 100%;
&-main {
padding: 2% 5%;
height: 50%;
}
&-sub {
position: absolute !important;
display: inline-flex;
justify-content: center;
align-items: center;
bottom: 23%;
height: 20% !important;
width: 100%;
}
}
&-slot-0 {
z-index: 70 !important;
background-color: whitesmoke;
}
@for $i from 1 through 4 {
&-slot-#{$i} {
transition: transform 0.5s;
position: relative;
height: 100%;
display: none;
background-color: #333333;
@if $i == 1 {
width: 100%;
} @else {
width: 33%;
margin: 5px;
}
.overlay {
width: 100%;
height: 100%;
z-index: 50;
position: absolute;
display: flex;
flex-direction: column;
.actions-move {
display: none;
flex-direction: row;
justify-content: center;
align-items: center;
position: absolute;
height: 100%;
width: 100%;
gap: 10%;
}
&:hover {
background-color: rgba($color: #333333, $alpha: 0.6);
.actions-move {
display: flex;
}
}
}
.actions {
pointer-events: all !important;
margin: 3% 2%;
display: flex;
flex-direction: row;
justify-content: end;
position: relative;
z-index: 50;
button {
width: 32px;
height: 32px;
margin: 8px;
display: flex;
justify-content: center;
align-items: center;
}
}
}
}
&-buffer {
iframe {
z-index: 45 !important;
pointer-events: none !important;
overflow: hidden;
border: 0;
position: absolute;
}
.main {
pointer-events: all !important;
z-index: 205 !important;
}
.sub-main {
pointer-events: all !important;
}
.thumbnail {
transform: scale(0.5, 0.5);
}
}
.pixel {
visibility: hidden;
height: 1px;
width: 1px;
}
}
@media (min-aspect-ratio: 1/1) {
#cowebsite {
right: 0;
top: 0;
width: 50%;
height: 100vh;
display: none;
&.loading {
transform: translateX(90%);
}
&.hidden {
transform: translateX(100%);
}
main {
width: 100%;
}
aside {
width: 30px;
img {
transform: rotate(90deg);
}
}
&-aside-holder {
cursor: ew-resize;
}
}
}
@import "cowebsite/global";
@import "cowebsite/short-screens";
@import "cowebsite/wide-screens";

View file

@ -0,0 +1,127 @@
#cowebsite {
position: fixed;
z-index: 820;
transition: transform 0.5s;
background-color: rgba(10, 9, 9, 0.8);
display: none;
main {
iframe {
width: 100%;
height: 100%;
max-width: 100vw;
max-height: 100vh;
}
}
aside {
background: gray;
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-between;
#cowebsite-aside-holder {
background: gray;
height: 20px;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
img {
width: 80%;
pointer-events: none;
}
}
#cowebsite-aside-buttons {
display: flex;
flex-direction: column;
margin-bottom: auto;
flex: 1;
justify-content: start;
#cowebsite-swipe {
display: none;
img {
transform: rotate(0deg) !important;
transform: scale(0.5);
}
}
}
.top-right-btn {
transform: scale(0.5);
cursor: url("./images/cursor_pointer.png"), pointer;
}
#cowebsite-other-actions {
display: flex;
margin-top: auto;
visibility: hidden;
justify-content: end;
flex: 1;
}
}
&-loader {
width: 20%;
#smoke {
@for $i from 1 through 3 {
#trail-#{$i} {
@for $y from 1 through 3 {
#trail-#{$i}-state-#{$y} {
visibility: hidden;
}
}
}
}
}
}
&-slot-main {
z-index: 70 !important;
background-color: rgba(10, 9, 9, 0);
display: flex;
justify-content: center;
align-items: center;
}
&-buffer {
iframe {
z-index: 45 !important;
pointer-events: none !important;
overflow: hidden;
border: 0;
position: absolute;
&.pixel {
height: 1px !important;
width: 1px !important;
}
}
.main {
pointer-events: all !important;
z-index: 821 !important;
}
.highlighted {
pointer-events: all !important;
padding: 4px;
}
.thumbnail {
transform: scale(0.5, 0.5);
}
}
.pixel {
visibility: hidden;
height: 1px;
width: 1px;
}
}

View file

@ -0,0 +1,84 @@
@include media-breakpoint-up(md) {
#main-container {
display: flex;
flex-direction: column-reverse;
}
#cowebsite {
left: 0;
top: 0;
width: 100%;
height: 50%;
display: flex;
flex-direction: column-reverse;
visibility: collapse;
transform: translateY(-100%);
&.loading {
visibility: visible;
transform: translateY(0%);
}
&.opened {
visibility: visible;
transform: translateY(0%);
}
&.closing {
visibility: visible;
}
&-loader {
height: 20%;
}
main {
height: 100%;
}
aside {
height: 50px;
flex-direction: row-reverse;
align-items: center;
display: flex;
justify-content: space-between;
#cowebsite-aside-holder {
height: 100%;
cursor: ns-resize;
img {
height: 100%;
}
}
#cowebsite-aside-buttons {
flex-direction: row-reverse;
margin-left: auto;
margin-bottom: 0;
justify-content: end;
}
#cowebsite-fullscreen {
padding-top: 0;
}
#cowebsite-other-actions {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 0;
height: 100%;
visibility: visible;
}
.top-right-btn {
img {
width: 15px;
}
}
}
}
}

View file

@ -0,0 +1,41 @@
@include media-breakpoint-down(lg) {
#cowebsite {
right: 0;
top: 0;
width: 50%;
height: 100vh;
display: flex;
visibility: collapse;
transform: translateX(100%);
&.loading {
visibility: visible;
transform: translateX(0%);
}
&.opened {
visibility: visible;
transform: translateX(0%);
}
&.closing {
visibility: visible;
}
main {
width: 100%;
}
aside {
width: 30px;
img {
transform: rotate(90deg);
}
}
&-aside-holder {
cursor: ew-resize;
}
}
}

View file

@ -1,10 +1,5 @@
@import "~@fontsource/press-start-2p/index.css";
@font-face {
font-family: "Twemoji Mozilla";
src: url("./fonts/TwemojiMozilla.ttf") format('truetype');
}
*{
font-family: PixelFont-7,monospace;
}

Binary file not shown.

View file

@ -1,6 +1,6 @@
@charset 'UTF-8';
@import "breakpoints";
@import "cowebsite.scss";
@import "cowebsite-mobile.scss";
@import "style";
@import "mobile-style.scss";
@import "fonts.scss";
@import "style";
@import "TextGlobalMessageSvelte-Style";

View file

@ -1,48 +0,0 @@
@media (hover: none) {
/**
* If we cannot hover over elements, let's display camera button in full.
*/
.btn-cam-action {
div {
transform: translateY(0px);
}
}
}
@media screen and (max-width: 700px),
screen and (max-height: 700px){
video.myCamVideo {
width: 150px;
}
.div-myCamVideo.hide {
right: -160px;
}
.sidebar {
width: 20%;
min-width: 200px;
position: absolute;
display: block;
right: 0;
height: 80%;
&> div {
min-width: 200px;
max-height: 21vh;
}
.video-container{
min-width: 200px;
}
}
.main-section {
position: absolute;
width: 100%;
& > div {
z-index: 2;
}
}
}

View file

@ -36,15 +36,22 @@ body .message-info.warning{
}
.video-container {
position: relative;
display: flex;
transition: all 0.2s ease;
background-color: #00000099;
cursor: url('./images/cursor_pointer.png'), pointer;
width: 100%;
&.no-clikable {
video {
cursor: url('./images/cursor_normal.png'), pointer;
}
}
video {
height: fit-content;
max-height: 100%;
max-width: 100%;
width: 100%;
height: 100%;
object-fit: cover;
cursor: url('./images/cursor_pointer.png'), pointer;
&.mobile{
@ -146,35 +153,6 @@ body .message-info.warning{
}
}
.video-container.div-myCamVideo{
border: none;
background-color: transparent;
}
.div-myCamVideo {
position: absolute;
right: 15px;
bottom: 30px;
border-radius: 15px 15px 15px 15px;
max-height: 20%;
transition: right 350ms;
}
.div-myCamVideo.hide {
right: -20vw;
}
video.myCamVideo{
background-color: #00000099;
max-height: 20vh;
width: 15vw;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
border-radius: 15px 15px 15px 15px;
/*width: 200px;*/
/*height: 113px;*/
}
.sound-progress{
display: none;
position: absolute;
@ -211,94 +189,6 @@ video.myCamVideo{
top: calc(50% - 20px);
}
.btn-cam-action {
pointer-events: all;
position: absolute;
display: inline-flex;
bottom: 10px;
right: 15px;
width: 240px;
height: 40px;
text-align: center;
align-content: center;
align-items: center;
justify-content: flex-end;
justify-items: center;
}
/*btn animation*/
.btn-cam-action div{
cursor: url('./images/cursor_pointer.png'), pointer;
display: flex;
align-items: center;
justify-content: center;
border: solid 0px black;
width: 44px;
height: 44px;
background: #666;
box-shadow: 2px 2px 24px #444;
border-radius: 48px;
transform: translateY(15px);
transition-timing-function: ease-in-out;
margin: 0 4%;
}
.btn-cam-action div.disabled {
background: #d75555;
}
.btn-cam-action div.enabled {
background: #73c973;
}
.btn-cam-action:hover div{
transform: translateY(0);
}
.btn-cam-action div:hover{
background: #407cf7;
box-shadow: 4px 4px 48px #666;
transition: 120ms;
}
.btn-micro{
pointer-events: auto;
transition: all .3s;
/*right: 44px;*/
}
.btn-video{
pointer-events: auto;
transition: all .25s;
/*right: 134px;*/
}
.btn-monitor{
pointer-events: auto;
transition: all .2s;
/*right: 224px;*/
}
.btn-monitor.hide {
transform: translateY(60px);
}
.btn-cam-action:hover .btn-monitor.hide{
transform: translateY(60px);
}
.btn-layout{
pointer-events: auto;
transition: all .15s;
}
.btn-layout.hide {
transform: translateY(60px);
}
.btn-cam-action:hover .btn-layout.hide{
transform: translateY(60px);
}
.btn-copy{
pointer-events: auto;
transition: all .3s;
right: 44px;
opacity: 1;
}
.btn-cam-action div img{
height: 22px;
width: 30px;
position: relative;
cursor: url('./images/cursor_pointer.png'), pointer;
}
/* Spinner */
.connecting-spinner {
/*display: inline-block;*/
@ -369,26 +259,6 @@ body {
position: absolute;
}
@media (min-aspect-ratio: 1/1) {
.game-overlay {
flex-direction: row;
}
.sidebar {
flex-direction: column;
}
.sidebar > div {
max-height: 21%;
}
.sidebar > div:hover {
max-height: 25%;
}
}
#game {
position: relative; /* Position relative is needed for the game-overlay. */
@ -486,96 +356,6 @@ input[type=range]:focus::-ms-fill-upper {
background: #FFFFFF;
}
.game-overlay {
display: none;
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
/* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */
}
.game-overlay + div {
pointer-events: none;
}
.game-overlay + div > div {
pointer-events: auto;
}
.game-overlay.active {
display: flex;
}
.game-overlay video {
width: 100%;
}
.main-section {
flex: 0 0 75%;
display: flex;
justify-content: start;
align-items: flex-start;
flex-wrap: wrap;
}
.main-section > div {
margin: 2%;
flex-basis: 96%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, flex-basis 0.2s;
cursor: url('./images/cursor_pointer.png'), pointer;
pointer-events: auto;
/*flex-shrink: 2;*/
}
.main-section > div:hover {
margin: 0%;
flex-basis: 100%;
}
.sidebar {
flex: 0 0 25%;
display: flex;
}
.sidebar > div {
margin: 2%;
transition: margin-left 0.2s, margin-right 0.2s, margin-bottom 0.2s, margin-top 0.2s, max-height 0.2s, max-width 0.2s;
cursor: url('./images/cursor_pointer.png'), pointer;
border-radius: 15px 15px 15px 15px;
pointer-events: auto;
video {
max-height: 21vh;
}
}
.sidebar > div:hover {
margin: 0%;
}
.sidebar > div video {
cursor: url('./images/cursor_pointer.png'), pointer;
}
/* Let's make sure videos are vertically centered if they need to be cropped */
.media-container {
display: flex;
flex-direction: column;
overflow: hidden;
border-radius: 15px;
}
.chat-mode {
display: grid;
width: 100%;
align-items: flex-start;
padding: 1%;
}
.chat-mode > div {
margin: 1%;
max-height: 96%;
@ -1061,13 +841,22 @@ div.action.danger p.action-body{
}
#svelte-overlay {
#game-overlay {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
user-select: none;
& + div {
pointer-events: none;
z-index: 2;
}
& + div > div {
pointer-events: auto;
}
& > div {
position: relative;
width: 100%;
@ -1085,22 +874,10 @@ div.action.danger p.action-body{
pointer-events: auto;
}
}
}
div.is-silent {
position: absolute;
bottom: 40px;
border-radius: 15px 15px 15px 15px;
max-height: 20%;
transition: right 350ms;
right: -300px;
background-color: black;
font-size: 20px;
color: white;
padding: 30px 20px;
}
div.is-silent.hide {
right: 15px;
&.hide {
visibility: hidden;
}
}
div.emoji-picker {
@ -1121,8 +898,3 @@ div.emoji-picker {
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
.pixel {
height: 1px !important;
width: 1px !important;
}

View file

@ -2,6 +2,7 @@ import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import fs from 'fs/promises';
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
import path from "path";
import sveltePreprocess from "svelte-preprocess";
@ -79,33 +80,9 @@ module.exports = {
},
},
{
test: /\.scss$/,
test: /\.(sc|c)ss$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
//url: false,
sourceMap: true,
},
},
"sass-loader",
],
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
//url: false,
sourceMap: true,
},
},
],
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
{
test: /\.(html|svelte)$/,
@ -185,6 +162,10 @@ module.exports = {
extensions: [".tsx", ".ts", ".js", ".svelte"],
mainFields: ["svelte", "browser", "module", "main"],
},
optimization: {
minimize: isProduction,
minimizer: [new CssMinimizerPlugin(), "..."],
},
output: {
filename: (pathData) => {
// Add a content hash only for the main bundle.

View file

@ -208,6 +208,11 @@
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.12.0.tgz#b7395688a79403c6df8d8bb8d81deb8222519853"
integrity sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA==
"@trysound/sax@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
@ -774,11 +779,25 @@ ajv-errors@^1.0.0:
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==
ajv-formats@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
dependencies:
ajv "^8.0.0"
ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv-keywords@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16"
integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
dependencies:
fast-deep-equal "^3.1.3"
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@ -789,6 +808,21 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^8.0.0, ajv@^8.8.0:
version "8.8.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb"
integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
uri-js "^4.2.2"
alphanum-sort@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
ansi-colors@^3.0.0:
version "3.2.4"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
@ -1212,6 +1246,17 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
browserslist@^4.0.0, browserslist@^4.16.0, browserslist@^4.16.6:
version "4.19.1"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3"
integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==
dependencies:
caniuse-lite "^1.0.30001286"
electron-to-chromium "^1.4.17"
escalade "^3.1.1"
node-releases "^2.0.1"
picocolors "^1.0.0"
browserslist@^4.14.5:
version "4.16.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
@ -1307,6 +1352,21 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
caniuse-api@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
dependencies:
browserslist "^4.0.0"
caniuse-lite "^1.0.0"
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286:
version "1.0.30001292"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz#4a55f61c06abc9595965cfd77897dc7bc1cdc456"
integrity sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==
caniuse-lite@^1.0.30001219:
version "1.0.30001228"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa"
@ -1483,6 +1543,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
colord@^2.9.1:
version "2.9.2"
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1"
integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==
colorette@^1.2.1, colorette@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
@ -1692,6 +1757,13 @@ crypto-browserify@^3.12.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
css-declaration-sorter@^6.0.3:
version "6.1.3"
resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz#e9852e4cf940ba79f509d9425b137d1f94438dc2"
integrity sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA==
dependencies:
timsort "^0.3.0"
css-loader@^5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.4.tgz#e985dcbce339812cb6104ef3670f08f9893a1536"
@ -1709,6 +1781,18 @@ css-loader@^5.2.4:
schema-utils "^3.0.0"
semver "^7.3.5"
css-minimizer-webpack-plugin@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.3.1.tgz#5afc4507a4ec13dd223f043cda8953ee0bf6ecfa"
integrity sha512-SHA7Hu/EiF0dOwdmV2+agvqYpG+ljlUa7Dvn1AVOmSH3N8KOERoaM9lGpstz9nGsoTjANGyUXdrxl/EwdMScRg==
dependencies:
cssnano "^5.0.6"
jest-worker "^27.0.2"
postcss "^8.3.5"
schema-utils "^4.0.0"
serialize-javascript "^6.0.0"
source-map "^0.6.1"
css-select@^2.0.2:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
@ -1719,16 +1803,96 @@ css-select@^2.0.2:
domutils "^1.7.0"
nth-check "^1.0.2"
css-select@^4.1.3:
version "4.2.1"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd"
integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==
dependencies:
boolbase "^1.0.0"
css-what "^5.1.0"
domhandler "^4.3.0"
domutils "^2.8.0"
nth-check "^2.0.1"
css-tree@^1.1.2, css-tree@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
dependencies:
mdn-data "2.0.14"
source-map "^0.6.1"
css-what@^3.2.1:
version "3.4.2"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
css-what@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe"
integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
cssnano-preset-default@^5.1.9:
version "5.1.9"
resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.9.tgz#79628ac48eccbdad570f70b4018cc38d43d1b7df"
integrity sha512-RhkEucqlQ+OxEi14K1p8gdXcMQy1mSpo7P1oC44oRls7BYIj8p+cht4IFBFV3W4iOjTP8EUB33XV1fX9KhDzyA==
dependencies:
css-declaration-sorter "^6.0.3"
cssnano-utils "^2.0.1"
postcss-calc "^8.0.0"
postcss-colormin "^5.2.2"
postcss-convert-values "^5.0.2"
postcss-discard-comments "^5.0.1"
postcss-discard-duplicates "^5.0.1"
postcss-discard-empty "^5.0.1"
postcss-discard-overridden "^5.0.1"
postcss-merge-longhand "^5.0.4"
postcss-merge-rules "^5.0.3"
postcss-minify-font-values "^5.0.1"
postcss-minify-gradients "^5.0.3"
postcss-minify-params "^5.0.2"
postcss-minify-selectors "^5.1.0"
postcss-normalize-charset "^5.0.1"
postcss-normalize-display-values "^5.0.1"
postcss-normalize-positions "^5.0.1"
postcss-normalize-repeat-style "^5.0.1"
postcss-normalize-string "^5.0.1"
postcss-normalize-timing-functions "^5.0.1"
postcss-normalize-unicode "^5.0.1"
postcss-normalize-url "^5.0.4"
postcss-normalize-whitespace "^5.0.1"
postcss-ordered-values "^5.0.2"
postcss-reduce-initial "^5.0.2"
postcss-reduce-transforms "^5.0.1"
postcss-svgo "^5.0.3"
postcss-unique-selectors "^5.0.2"
cssnano-utils@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-2.0.1.tgz#8660aa2b37ed869d2e2f22918196a9a8b6498ce2"
integrity sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==
cssnano@^5.0.6:
version "5.0.14"
resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.14.tgz#99bc550f663b48c38e9b8e0ae795697c9de84b47"
integrity sha512-qzhRkFvBhv08tbyKCIfWbxBXmkIpLl1uNblt8SpTHkgLfON5OCPX/CCnkdNmEosvo8bANQYmTTMEgcVBlisHaw==
dependencies:
cssnano-preset-default "^5.1.9"
lilconfig "^2.0.3"
yaml "^1.10.2"
csso@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
dependencies:
css-tree "^1.1.2"
dataloader@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8"
@ -1952,6 +2116,15 @@ dom-serializer@0:
domelementtype "^2.0.1"
entities "^2.0.0"
dom-serializer@^1.0.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.2.0"
entities "^2.0.0"
domain-browser@^4.19.0:
version "4.19.0"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1"
@ -1962,7 +2135,7 @@ domelementtype@1, domelementtype@^1.3.1:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
domelementtype@^2.0.1:
domelementtype@^2.0.1, domelementtype@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
@ -1974,6 +2147,13 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
domhandler@^4.2.0, domhandler@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626"
integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==
dependencies:
domelementtype "^2.2.0"
domutils@^1.5.1, domutils@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
@ -1982,6 +2162,15 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0"
domelementtype "1"
domutils@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
@ -2007,6 +2196,11 @@ electron-to-chromium@^1.3.723:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz#dbedd6373f595ae10a13d146b66bece4c1afa5bd"
integrity sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA==
electron-to-chromium@^1.4.17:
version "1.4.28"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz#fef0e92e281df6d568f482d8d53c34ca5374de48"
integrity sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg==
elliptic@^6.5.3:
version "6.5.4"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
@ -3541,6 +3735,15 @@ jest-worker@^26.6.2:
merge-stream "^2.0.0"
supports-color "^7.0.0"
jest-worker@^27.0.2:
version "27.4.5"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.5.tgz#d696e3e46ae0f24cff3fa7195ffba22889262242"
integrity sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg==
dependencies:
"@types/node" "*"
merge-stream "^2.0.0"
supports-color "^8.0.0"
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -3568,6 +3771,11 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
@ -3659,6 +3867,11 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
lilconfig@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==
lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
@ -3747,11 +3960,21 @@ lodash.isequal@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@ -3830,6 +4053,11 @@ md5.js@^1.3.4:
inherits "^2.0.1"
safe-buffer "^5.1.2"
mdn-data@2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@ -4036,6 +4264,11 @@ nanoid@^3.1.23:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
nanoid@^3.1.30:
version "3.1.30"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@ -4121,6 +4354,11 @@ node-releases@^1.1.71:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
node-releases@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@ -4143,6 +4381,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
normalize-url@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
npm-run-all@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba"
@ -4179,6 +4422,13 @@ nth-check@^1.0.2:
dependencies:
boolbase "~1.0.0"
nth-check@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==
dependencies:
boolbase "^1.0.0"
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@ -4549,6 +4799,11 @@ phaser@^3.54.0:
eventemitter3 "^4.0.7"
path "^0.12.7"
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d"
@ -4621,6 +4876,103 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
postcss-calc@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.0.0.tgz#a05b87aacd132740a5db09462a3612453e5df90a"
integrity sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==
dependencies:
postcss-selector-parser "^6.0.2"
postcss-value-parser "^4.0.2"
postcss-colormin@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.2.tgz#019cd6912bef9e7e0924462c5e4ffae241e2f437"
integrity sha512-tSEe3NpqWARUTidDlF0LntPkdlhXqfDFuA1yslqpvvGAfpZ7oBaw+/QXd935NKm2U9p4PED0HDZlzmMk7fVC6g==
dependencies:
browserslist "^4.16.6"
caniuse-api "^3.0.0"
colord "^2.9.1"
postcss-value-parser "^4.2.0"
postcss-convert-values@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz#879b849dc3677c7d6bc94b6a2c1a3f0808798059"
integrity sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==
dependencies:
postcss-value-parser "^4.1.0"
postcss-discard-comments@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz#9eae4b747cf760d31f2447c27f0619d5718901fe"
integrity sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==
postcss-discard-duplicates@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz#68f7cc6458fe6bab2e46c9f55ae52869f680e66d"
integrity sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==
postcss-discard-empty@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz#ee136c39e27d5d2ed4da0ee5ed02bc8a9f8bf6d8"
integrity sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==
postcss-discard-overridden@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz#454b41f707300b98109a75005ca4ab0ff2743ac6"
integrity sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==
postcss-merge-longhand@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.4.tgz#41f4f3270282ea1a145ece078b7679f0cef21c32"
integrity sha512-2lZrOVD+d81aoYkZDpWu6+3dTAAGkCKbV5DoRhnIR7KOULVrI/R7bcMjhrH9KTRy6iiHKqmtG+n/MMj1WmqHFw==
dependencies:
postcss-value-parser "^4.1.0"
stylehacks "^5.0.1"
postcss-merge-rules@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.3.tgz#b5cae31f53129812a77e3eb1eeee448f8cf1a1db"
integrity sha512-cEKTMEbWazVa5NXd8deLdCnXl+6cYG7m2am+1HzqH0EnTdy8fRysatkaXb2dEnR+fdaDxTvuZ5zoBdv6efF6hg==
dependencies:
browserslist "^4.16.6"
caniuse-api "^3.0.0"
cssnano-utils "^2.0.1"
postcss-selector-parser "^6.0.5"
postcss-minify-font-values@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz#a90cefbfdaa075bd3dbaa1b33588bb4dc268addf"
integrity sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA==
dependencies:
postcss-value-parser "^4.1.0"
postcss-minify-gradients@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.3.tgz#f970a11cc71e08e9095e78ec3a6b34b91c19550e"
integrity sha512-Z91Ol22nB6XJW+5oe31+YxRsYooxOdFKcbOqY/V8Fxse1Y3vqlNRpi1cxCqoACZTQEhl+xvt4hsbWiV5R+XI9Q==
dependencies:
colord "^2.9.1"
cssnano-utils "^2.0.1"
postcss-value-parser "^4.1.0"
postcss-minify-params@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.2.tgz#1b644da903473fbbb18fbe07b8e239883684b85c"
integrity sha512-qJAPuBzxO1yhLad7h2Dzk/F7n1vPyfHfCCh5grjGfjhi1ttCnq4ZXGIW77GSrEbh9Hus9Lc/e/+tB4vh3/GpDg==
dependencies:
alphanum-sort "^1.0.2"
browserslist "^4.16.6"
cssnano-utils "^2.0.1"
postcss-value-parser "^4.1.0"
postcss-minify-selectors@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz#4385c845d3979ff160291774523ffa54eafd5a54"
integrity sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og==
dependencies:
alphanum-sort "^1.0.2"
postcss-selector-parser "^6.0.5"
postcss-modules-extract-imports@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
@ -4649,6 +5001,96 @@ postcss-modules-values@^4.0.0:
dependencies:
icss-utils "^5.0.0"
postcss-normalize-charset@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz#121559d1bebc55ac8d24af37f67bd4da9efd91d0"
integrity sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==
postcss-normalize-display-values@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz#62650b965981a955dffee83363453db82f6ad1fd"
integrity sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ==
dependencies:
cssnano-utils "^2.0.1"
postcss-value-parser "^4.1.0"
postcss-normalize-positions@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz#868f6af1795fdfa86fbbe960dceb47e5f9492fe5"
integrity sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg==
dependencies:
postcss-value-parser "^4.1.0"
postcss-normalize-repeat-style@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz#cbc0de1383b57f5bb61ddd6a84653b5e8665b2b5"
integrity sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w==
dependencies:
cssnano-utils "^2.0.1"
postcss-value-parser "^4.1.0"
postcss-normalize-string@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz#d9eafaa4df78c7a3b973ae346ef0e47c554985b0"
integrity sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA==
dependencies:
postcss-value-parser "^4.1.0"
postcss-normalize-timing-functions@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz#8ee41103b9130429c6cbba736932b75c5e2cb08c"
integrity sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q==
dependencies:
cssnano-utils "^2.0.1"
postcss-value-parser "^4.1.0"
postcss-normalize-unicode@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz#82d672d648a411814aa5bf3ae565379ccd9f5e37"
integrity sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA==
dependencies:
browserslist "^4.16.0"
postcss-value-parser "^4.1.0"
postcss-normalize-url@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz#3b0322c425e31dd275174d0d5db0e466f50810fb"
integrity sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg==
dependencies:
normalize-url "^6.0.1"
postcss-value-parser "^4.2.0"
postcss-normalize-whitespace@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz#b0b40b5bcac83585ff07ead2daf2dcfbeeef8e9a"
integrity sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA==
dependencies:
postcss-value-parser "^4.1.0"
postcss-ordered-values@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz#1f351426977be00e0f765b3164ad753dac8ed044"
integrity sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ==
dependencies:
cssnano-utils "^2.0.1"
postcss-value-parser "^4.1.0"
postcss-reduce-initial@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz#fa424ce8aa88a89bc0b6d0f94871b24abe94c048"
integrity sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw==
dependencies:
browserslist "^4.16.6"
caniuse-api "^3.0.0"
postcss-reduce-transforms@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz#93c12f6a159474aa711d5269923e2383cedcf640"
integrity sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA==
dependencies:
cssnano-utils "^2.0.1"
postcss-value-parser "^4.1.0"
postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
version "6.0.6"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea"
@ -4657,6 +5099,35 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss-selector-parser@^6.0.5:
version "6.0.8"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.8.tgz#f023ed7a9ea736cd7ef70342996e8e78645a7914"
integrity sha512-D5PG53d209Z1Uhcc0qAZ5U3t5HagH3cxu+WLZ22jt3gLUpXM4eXXfiO14jiDWST3NNooX/E8wISfOhZ9eIjGTQ==
dependencies:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss-svgo@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.3.tgz#d945185756e5dfaae07f9edb0d3cae7ff79f9b30"
integrity sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==
dependencies:
postcss-value-parser "^4.1.0"
svgo "^2.7.0"
postcss-unique-selectors@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.2.tgz#5d6893daf534ae52626708e0d62250890108c0c1"
integrity sha512-w3zBVlrtZm7loQWRPVC0yjUwwpty7OM6DnEHkxcSQXO1bMS3RJ+JUS5LFMSDZHJcvGsRwhZinCWVqn8Kej4EDA==
dependencies:
alphanum-sort "^1.0.2"
postcss-selector-parser "^6.0.5"
postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss-value-parser@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
@ -4671,6 +5142,15 @@ postcss@^8.2.10:
nanoid "^3.1.23"
source-map "^0.6.1"
postcss@^8.3.5:
version "8.4.5"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95"
integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==
dependencies:
nanoid "^3.1.30"
picocolors "^1.0.0"
source-map-js "^1.0.1"
posthog-js@^1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.14.1.tgz#68553b9074c686784b994b3f433ad035b241deaa"
@ -4998,6 +5478,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
@ -5190,6 +5675,16 @@ schema-utils@^3.0.0:
ajv "^6.12.5"
ajv-keywords "^3.5.2"
schema-utils@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7"
integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==
dependencies:
"@types/json-schema" "^7.0.9"
ajv "^8.8.0"
ajv-formats "^2.1.1"
ajv-keywords "^5.0.0"
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@ -5250,6 +5745,13 @@ serialize-javascript@^5.0.1:
dependencies:
randombytes "^2.1.0"
serialize-javascript@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
dependencies:
randombytes "^2.1.0"
serve-index@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
@ -5470,6 +5972,11 @@ source-list-map@^2.0.0, source-list-map@^2.0.1:
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
source-map-js@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
source-map-resolve@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@ -5565,6 +6072,11 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
stable@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
standardized-audio-context@^25.2.4:
version "25.2.4"
resolved "https://registry.yarnpkg.com/standardized-audio-context/-/standardized-audio-context-25.2.4.tgz#d64dbdd70615171ec90d1b7151a0d945af94cf3d"
@ -5731,6 +6243,14 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
stylehacks@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.1.tgz#323ec554198520986806388c7fdaebc38d2c06fb"
integrity sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA==
dependencies:
browserslist "^4.16.0"
postcss-selector-parser "^6.0.4"
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@ -5752,6 +6272,13 @@ supports-color@^7.0.0, supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
supports-color@^8.0.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
svelte-check@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-2.1.0.tgz#3ee8ad86068256346ebca862bbee8417a757fc53"
@ -5801,6 +6328,19 @@ svelte@^3.38.2:
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.38.2.tgz#55e5c681f793ae349b5cc2fe58e5782af4275ef5"
integrity sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==
svgo@^2.7.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
dependencies:
"@trysound/sax" "0.2.0"
commander "^7.2.0"
css-select "^4.1.3"
css-tree "^1.1.3"
csso "^4.2.0"
picocolors "^1.0.0"
stable "^0.1.8"
tabbable@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261"
@ -5868,6 +6408,11 @@ timers-browserify@^2.0.12:
dependencies:
setimmediate "^1.0.4"
timsort@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tiny-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
@ -6496,7 +7041,7 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml@^1.10.0, yaml@^1.7.2:
yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==

View file

@ -0,0 +1,194 @@
{ "compressionlevel":-1,
"height":10,
"infinite":false,
"layers":[
{
"data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"height":10,
"id":1,
"name":"floor",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
"height":10,
"id":2,
"name":"start",
"opacity":1,
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34],
"height":10,
"id":8,
"name":"firstCoWebsite",
"opacity":1,
"properties":[
{
"name":"openWebsite",
"type":"string",
"value":"https:\/\/workadventu.re"
},
{
"name":"openWebsiteClosable",
"type":"bool",
"value":true
},
{
"name":"openWebsiteTrigger",
"type":"string",
"value":"onaction"
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23],
"height":10,
"id":11,
"name":"secondCoWebsite",
"opacity":1,
"properties":[
{
"name":"openWebsite",
"type":"string",
"value":"https:\/\/wikipedia.org"
},
{
"name":"openWebsiteClosable",
"type":"bool",
"value":true
},
{
"name":"openWebsiteTrigger",
"type":"string",
"value":"onaction"
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12],
"height":10,
"id":10,
"name":"terCoWebsite",
"opacity":1,
"properties":[
{
"name":"openWebsite",
"type":"string",
"value":"https:\/\/www.typescriptlang.org\/"
},
{
"name":"openWebsiteClosable",
"type":"bool",
"value":true
},
{
"name":"openWebsiteTrigger",
"type":"string",
"value":"onaction"
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 111, 111, 0, 0, 0, 0, 0, 111, 111, 111, 111, 111, 0, 0, 0, 0, 0, 111, 111, 111, 111, 111, 0, 0, 0, 0, 0, 111, 111, 111, 111, 111],
"height":10,
"id":12,
"name":"quatCoWebsite",
"opacity":1,
"properties":[
{
"name":"openWebsite",
"type":"string",
"value":"https:\/\/svelte.dev\/"
},
{
"name":"openWebsiteClosable",
"type":"bool",
"value":true
},
{
"name":"openWebsiteTrigger",
"type":"string",
"value":"onaction"
}],
"type":"tilelayer",
"visible":true,
"width":10,
"x":0,
"y":0
},
{
"draworder":"topdown",
"id":3,
"name":"floorLayer",
"objects":[
{
"height":141,
"id":1,
"name":"Tests",
"rotation":0,
"text":
{
"fontfamily":"Sans Serif",
"pixelsize":11,
"text":"Test:\nWalk on the blue carpet, an iframe open, walk on the white carpet another one to open another one\nResult:\n2 co-websites must be opened\n\nTest:\nGo outside of carpets\nResult:\nAll co-websites must disapeared",
"wrap":true
},
"type":"",
"visible":true,
"width":316.770833333333,
"x":0.28125,
"y":187.833333333333
}],
"opacity":1,
"type":"objectgroup",
"visible":true,
"x":0,
"y":0
}],
"nextlayerid":13,
"nextobjectid":3,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.7.2",
"tileheight":32,
"tilesets":[
{
"columns":11,
"firstgid":1,
"image":"tileset1.png",
"imageheight":352,
"imagewidth":352,
"margin":0,
"name":"tileset1",
"spacing":0,
"tilecount":121,
"tileheight":32,
"tilewidth":32
}],
"tilewidth":32,
"type":"map",
"version":"1.6",
"width":10
}

View file

@ -326,6 +326,14 @@
<a href="#" class="testLink" data-testmap="CoWebsite/cowebsite_property.json" target="_blank">Open co-websites by map property</a>
</td>
</tr>
<tr>
<td>
<input type="radio" name="test-cowebsite-property-trigger"> Success <input type="radio" name="test-cowebsite-property-trigger"> Failure <input type="radio" name="test-cowebsite-property-trigger" checked> Pending
</td>
<td>
<a href="#" class="testLink" data-testmap="CoWebsite/cowebsite_property_trigger.json" target="_blank">Open co-websites by map property trigger</a>
</td>
</tr>
<tr>
<td>
<input type="radio" name="test-cowebsite-api"> Success <input type="radio" name="test-cowebsite-api"> Failure <input type="radio" name="test-cowebsite-api" checked> Pending