mirror of
https://github.com/nerfstudio-project/nerfstudio.git
synced 2026-11-13 11:24:00 +08:00
Improve the web viewer UI [WIP] (#333)
* functional but style is bad * banner. sidebar. tabs for controls/scene/camera_path/logs. websocket text field * moved window. toggle scene button. next step: lint/clean code * websocket and webrtc reconnection on url change * reconnection on websocket change seems robust * cleaned up errors and organized code * fixing lint
This commit is contained in:
1 parent
32517f4587
commit
4c77c4e7c0
28 files changed
+1136
-294
No files matched your search
@@ -17,7 +17,11 @@
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "@typescript-eslint"],
|
||||
"plugins": [
|
||||
"react",
|
||||
"@typescript-eslint",
|
||||
"unused-imports"
|
||||
],
|
||||
"rules": {
|
||||
"arrow-body-style": "off",
|
||||
"camelcase": "off",
|
||||
@@ -25,6 +29,7 @@
|
||||
"no-alert": "off",
|
||||
"no-console": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"react/destructuring-assignment": "off"
|
||||
"react/destructuring-assignment": "off",
|
||||
"unused-imports/no-unused-imports-ts": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.4",
|
||||
"@emotion/styled": "^11.10.4",
|
||||
"@mui/material": "^5.10.3",
|
||||
"@reduxjs/toolkit": "^1.8.3",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
@@ -12,14 +15,20 @@
|
||||
"classnames": "^2.3.1",
|
||||
"dat.gui": "^0.7.9",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"leva": "^0.9.29",
|
||||
"msgpack-lite": "^0.1.26",
|
||||
"prop-types": "^15.8.1",
|
||||
"re-resizable": "^6.9.9",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-pro-sidebar": "^0.7.1",
|
||||
"react-redux": "^8.0.2",
|
||||
"redux": "^4.2.0",
|
||||
"sass": "^1.54.8",
|
||||
"socket.io-client": "^4.5.1",
|
||||
"stats.js": "^0.17.0",
|
||||
"three": "^0.142.0",
|
||||
"three-wtm": "^1.0",
|
||||
"websocket": "^1.0.34",
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
@@ -1,21 +1,34 @@
|
||||
import './App.css';
|
||||
|
||||
import React from 'react';
|
||||
import { Leva } from 'leva';
|
||||
import { RenderControls } from './modules/ConfigPanel/ConfigPanel';
|
||||
|
||||
import LevaTheme from './modules/ConfigPanel/leva_theme.json';
|
||||
import SetupScene from './modules/Scene/Scene';
|
||||
import { BasicTabs } from './modules/SidePanel/SidePanel';
|
||||
import {
|
||||
SceneTreeWebSocketListener,
|
||||
get_scene_tree,
|
||||
} from './modules/Scene/Scene';
|
||||
import { RenderControls } from './modules/ConfigPanel/ConfigPanel';
|
||||
import ViewerWindow from './modules/ViewerWindow/ViewerWindow';
|
||||
import Banner from './modules/Banner';
|
||||
|
||||
export default function App() {
|
||||
const scene = SetupScene();
|
||||
// The scene tree won't rerender but it will listener to changes
|
||||
// from the redux store and draw three.js objects.
|
||||
const sceneTree = get_scene_tree();
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<Leva theme={LevaTheme} titleBar={false} />
|
||||
<RenderControls />
|
||||
<ViewerWindow scene={scene} />
|
||||
{/*
|
||||
Code that listens for websocket 'write' messages and updates the redux store.
|
||||
*/}
|
||||
<SceneTreeWebSocketListener />
|
||||
{/* ----- */}
|
||||
<Banner />
|
||||
<div className="App-body">
|
||||
<div className="SidePanel">
|
||||
<BasicTabs sceneTree={sceneTree} />
|
||||
</div>
|
||||
<RenderControls />
|
||||
<ViewerWindow scene={sceneTree.object} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,17 +2,6 @@
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import * as THREE from 'three';
|
||||
|
||||
const dat = require('dat.gui');
|
||||
|
||||
function remove_folders(gui) {
|
||||
for (const name of Object.keys(gui.__folders)) {
|
||||
const folder = gui.__folders[name];
|
||||
remove_folders(folder);
|
||||
dat.dom.dom.unbind(window, 'resize', folder.__resizeHandler);
|
||||
gui.removeFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
function dispose(object) {
|
||||
if (!object) {
|
||||
return;
|
||||
@@ -38,19 +27,16 @@ function dispose(object) {
|
||||
}
|
||||
|
||||
export default class SceneNode {
|
||||
constructor(object, folder) {
|
||||
constructor(object) {
|
||||
this.object = object;
|
||||
this.folder = folder;
|
||||
this.children = {};
|
||||
this.create_controls();
|
||||
for (const c of this.object.children) {
|
||||
this.add_child(c);
|
||||
}
|
||||
}
|
||||
|
||||
add_child(object) {
|
||||
const f = this.folder.addFolder(object.name);
|
||||
const node = new SceneNode(object, f);
|
||||
const node = new SceneNode(object);
|
||||
this.children[object.name] = node;
|
||||
return node;
|
||||
}
|
||||
@@ -74,19 +60,6 @@ export default class SceneNode {
|
||||
return child.find(path.slice(1));
|
||||
}
|
||||
|
||||
create_controls() {
|
||||
if (this.vis_controller !== undefined) {
|
||||
this.folder.domElement.removeChild(this.vis_controller.domElement);
|
||||
}
|
||||
this.vis_controller = new dat.controllers.BooleanController(
|
||||
this.object,
|
||||
'visible',
|
||||
);
|
||||
this.folder.domElement.prepend(this.vis_controller.domElement);
|
||||
this.vis_controller.domElement.style.height = '0';
|
||||
this.vis_controller.domElement.style.float = 'right';
|
||||
}
|
||||
|
||||
set_property(property, value) {
|
||||
if (property === 'position') {
|
||||
this.object.position.set(value[0], value[1], value[2]);
|
||||
@@ -116,7 +89,6 @@ export default class SceneNode {
|
||||
this.object.parent.remove(this.object);
|
||||
this.object = object;
|
||||
parent.add(object);
|
||||
this.create_controls();
|
||||
}
|
||||
|
||||
dispose_recursive() {
|
||||
@@ -136,8 +108,6 @@ export default class SceneNode {
|
||||
if (child !== undefined) {
|
||||
child.dispose_recursive();
|
||||
parent.object.remove(child.object);
|
||||
remove_folders(child.folder);
|
||||
parent.folder.removeFolder(child.folder);
|
||||
delete parent.children[name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import './index.scss';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
.App {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
$banner-color: #84c3ff4d;
|
||||
|
||||
.banner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
background-color: $banner-color;
|
||||
|
||||
.banner-button {
|
||||
margin: 10px;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.App-body {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
$SidePanel-width: 500px;
|
||||
$custom-border: solid 1px #ddd;
|
||||
|
||||
.SidePanel {
|
||||
position: absolute;
|
||||
background-color: #f0f0f080;
|
||||
top: 0;
|
||||
width: $SidePanel-width;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: $custom-border;
|
||||
z-index: 999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.Leva-container > div {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.Scene-container {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
// Your variable overrides
|
||||
$sidebar-color: #b71f0b !default;
|
||||
$sidebar-width: 100% !default;
|
||||
@import '~react-pro-sidebar/dist/scss/styles.scss';
|
||||
|
||||
// ------------------------------------
|
||||
// Status Panel styling
|
||||
.StatusPanel {
|
||||
display: block;
|
||||
// height: 170px;
|
||||
}
|
||||
|
||||
.StatusPanel-title {
|
||||
margin: 5px;
|
||||
text-align: center;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.StatusPanel .StatusPanel-button {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.StatusPanel .WebSocketUrlField {
|
||||
// display: block;
|
||||
// float: left;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.StatusPanel-metrics {
|
||||
background-color: $banner-color;
|
||||
}
|
||||
|
||||
.StatusPanel-metrics div {
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// Landing Modal styling
|
||||
// .LandingModal {
|
||||
// display: block;
|
||||
// }
|
||||
|
||||
$background-paper-color: #fff;
|
||||
|
||||
.LandingModal-box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 400px; // 400
|
||||
background-color: $background-paper-color;
|
||||
border: 2px solid #000;
|
||||
// box-shadow: 24px 24px;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.LandingModal-box .WebSocketUrlField {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.LandingModal-box .LandingModal-button {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// Camera Vie
|
||||
// }
|
||||
|
||||
.canvas-container-main,
|
||||
.WebRTCVideo {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: $SidePanel-width;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
border: $custom-border;
|
||||
}
|
||||
|
||||
.WebRTCVideo-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: fill;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import Button from '@mui/material/Button';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import LandingModal from '../LandingModal';
|
||||
|
||||
function getParam(param_name) {
|
||||
// https://stackoverflow.com/questions/831030/how-to-get-get-request-parameters-in-javascript
|
||||
const params = new RegExp(
|
||||
`[?&]${encodeURIComponent(param_name)}=([^&]*)`,
|
||||
).exec(window.location.href);
|
||||
if (params === null) {
|
||||
return undefined;
|
||||
}
|
||||
return decodeURIComponent(params[1]);
|
||||
}
|
||||
|
||||
function getWebsocketEndpoint() {
|
||||
const endpoint = getParam('websocket_url');
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
export default function Banner() {
|
||||
// const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const [anchorEl, setAnchorEl] = React.useState(0);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
let open_modal = true;
|
||||
const websocket_url_from_argument = getWebsocketEndpoint();
|
||||
if (websocket_url_from_argument !== undefined) {
|
||||
open_modal = false;
|
||||
dispatch({
|
||||
type: 'write',
|
||||
path: 'websocketState/websocket_url',
|
||||
data: websocket_url_from_argument,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="banner">
|
||||
<LandingModal initial_state={open_modal} />
|
||||
<Button
|
||||
id="basic-button"
|
||||
className="banner-button"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
aria-controls={open ? 'basic-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
onClick={handleClick}
|
||||
>
|
||||
Options
|
||||
</Button>
|
||||
<Menu
|
||||
id="basic-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleClose}>Load Training Config</MenuItem>
|
||||
<MenuItem onClick={handleClose}>Export Training Config</MenuItem>
|
||||
<MenuItem onClick={handleClose}>Dark Mode</MenuItem>
|
||||
</Menu>
|
||||
<Button className="banner-button" variant="outlined" size="small">
|
||||
Download Desktop App
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import Banner from './Banner';
|
||||
|
||||
export default Banner;
|
||||
Whitespace-only changes.
@@ -9,12 +9,6 @@ const msgpack = require('msgpack-lite');
|
||||
export function RenderControls() {
|
||||
// connection status indicators
|
||||
const websocket = useContext(WebSocketContext).socket;
|
||||
const isWebsocketConnected = useSelector(
|
||||
(state) => state.websocketState.isConnected,
|
||||
);
|
||||
const isWebrtcConnected = useSelector(
|
||||
(state) => state.webrtcState.isConnected,
|
||||
);
|
||||
const isTraining = useSelector((state) => state.renderingState.isTraining);
|
||||
const outputOptions = useSelector(
|
||||
(state) => state.renderingState.output_options,
|
||||
@@ -37,11 +31,6 @@ export function RenderControls() {
|
||||
const field_of_view = useSelector(
|
||||
(state) => state.renderingState.field_of_view,
|
||||
);
|
||||
let eval_fps = useSelector((state) => state.renderingState.eval_fps);
|
||||
let train_eta = useSelector((state) => state.renderingState.train_eta);
|
||||
let vis_train_ratio = useSelector(
|
||||
(state) => state.renderingState.vis_train_ratio,
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -265,44 +254,7 @@ export function RenderControls() {
|
||||
min_resolution,
|
||||
max_resolution,
|
||||
field_of_view,
|
||||
],
|
||||
);
|
||||
|
||||
const [, setState] = useControls(
|
||||
'Rendering Monitor',
|
||||
() => ({
|
||||
// WebSocket isConnected
|
||||
// button does nothing except be an indicator
|
||||
'WebSocket Connected': button(() => {}, {
|
||||
disabled: !isWebsocketConnected,
|
||||
}),
|
||||
// webRTC isConnected
|
||||
// button does nothing except be an indicator
|
||||
'WebRTC Connected': button(() => {}, {
|
||||
disabled: !isWebrtcConnected,
|
||||
}),
|
||||
eval_fps: {
|
||||
label: 'Eval FPS',
|
||||
value: eval_fps,
|
||||
disabled: true,
|
||||
},
|
||||
train_eta: {
|
||||
label: 'Train ETA',
|
||||
value: train_eta,
|
||||
disabled: true,
|
||||
},
|
||||
vis_train_ratio: {
|
||||
label: 'Time Allocation',
|
||||
value: vis_train_ratio,
|
||||
disabled: true,
|
||||
},
|
||||
}),
|
||||
[
|
||||
isWebsocketConnected,
|
||||
isWebrtcConnected,
|
||||
eval_fps,
|
||||
train_eta,
|
||||
vis_train_ratio,
|
||||
websocket // need to re-render when websocket changes to use the new websocket
|
||||
],
|
||||
);
|
||||
|
||||
@@ -324,26 +276,5 @@ export function RenderControls() {
|
||||
field_of_view,
|
||||
]);
|
||||
|
||||
useEffect(() => {}, [setState, isWebsocketConnected, isWebrtcConnected]);
|
||||
|
||||
useEffect(() => {
|
||||
websocket.addEventListener('message', (originalCmd) => {
|
||||
// set the remote description when the offer is received
|
||||
const cmd = msgpack.decode(new Uint8Array(originalCmd.data));
|
||||
if (cmd.path === '/renderingState/eval_fps') {
|
||||
eval_fps = cmd.data;
|
||||
setState({ eval_fps });
|
||||
}
|
||||
if (cmd.path === '/renderingState/train_eta') {
|
||||
train_eta = cmd.data;
|
||||
setState({ train_eta });
|
||||
}
|
||||
if (cmd.path === '/renderingState/vis_train_ratio') {
|
||||
vis_train_ratio = cmd.data;
|
||||
setState({ vis_train_ratio });
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Box, Button, Modal, Typography } from '@mui/material';
|
||||
|
||||
import WebSocketUrlField from '../WebSocketUrlField';
|
||||
|
||||
|
||||
interface LandingModalProps {
|
||||
initial_state: object
|
||||
}
|
||||
|
||||
export default function LandingModel(props: LandingModalProps) {
|
||||
const [open, setOpen] = React.useState(props.initial_state);
|
||||
const handleOpen = () => setOpen(true);
|
||||
const handleClose = () => setOpen(false);
|
||||
|
||||
const text_1 = `At the top right is the Status Panel where you can connect to a WebSocket and see the train/eval status of your NeRF model. The WebSocket URL takes the form <domain-or-ip_address>:<port>.`;
|
||||
const text_2 =
|
||||
'If the WebSocket is not connecting, make sure your port is being forwarded properly!';
|
||||
|
||||
return (
|
||||
<div className="LandingModal">
|
||||
<Button
|
||||
className="banner-button"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={handleOpen}
|
||||
>
|
||||
Getting Started
|
||||
</Button>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box className="LandingModal-box">
|
||||
<Typography id="modal-modal-title" component="div">
|
||||
Welcome to the Nerfactory Viewer!{' '}
|
||||
<img style={{ height: 37, margin: 'auto' }} src="/favicon.png" alt="The favicon."/>
|
||||
</Typography>
|
||||
<Typography
|
||||
id="modal-modal-description"
|
||||
component="div"
|
||||
sx={{ mt: 2 }}
|
||||
>
|
||||
<div className="LandingModel-content">
|
||||
<b>Getting Started</b>
|
||||
<p>{text_1}</p>
|
||||
<p>
|
||||
You can enter your WebSocket URL in the Status Panel or here:
|
||||
</p>
|
||||
<WebSocketUrlField />
|
||||
<p>{text_2}</p>
|
||||
<b>Other Resources:</b>
|
||||
<Button
|
||||
className="LandingModal-button"
|
||||
variant="outlined"
|
||||
href="https://github.com/plenoptix/nerfactory"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
GitHub
|
||||
</Button>
|
||||
<Button
|
||||
className="LandingModal-button"
|
||||
variant="outlined"
|
||||
href="https://plenoptix-nerfactory.readthedocs-hosted.com/en/latest/index.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</Button>
|
||||
</div>
|
||||
</Typography>
|
||||
</Box>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import LandingModel from './LandingModal';
|
||||
|
||||
export default LandingModel;
|
||||
@@ -1,5 +0,0 @@
|
||||
#datgui {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
}
|
||||
@@ -1,38 +1,24 @@
|
||||
import './Scene.css';
|
||||
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import * as THREE from 'three';
|
||||
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { GUI } from 'dat.gui';
|
||||
import SceneNode from '../../SceneNode';
|
||||
import { subscribe_to_changes } from '../../subscriber';
|
||||
import { WebSocketContext } from '../WebSocket/WebSocket';
|
||||
import { drawCamera, drawSceneBounds } from './drawing';
|
||||
|
||||
import SceneNode from '../../SceneNode';
|
||||
import { WebSocketContext } from '../WebSocket/WebSocket';
|
||||
import { subscribe_to_changes } from '../../subscriber';
|
||||
|
||||
const msgpack = require('msgpack-lite');
|
||||
|
||||
// manages setting up the scene and other logic for keeping state in sync with the server
|
||||
export default function SetupScene() {
|
||||
const SCENE_BOUNDS_NAME = 'Scene Bounds';
|
||||
const CAMERAS_NAME = 'Training Cameras';
|
||||
|
||||
export function get_scene_tree() {
|
||||
let scene = null;
|
||||
let gui = null;
|
||||
let sceneTree = null;
|
||||
|
||||
// the websocket context
|
||||
const socket = useContext(WebSocketContext).socket;
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Scene
|
||||
scene = new THREE.Scene();
|
||||
|
||||
// GUI
|
||||
gui = new GUI();
|
||||
gui.domElement.id = 'datgui';
|
||||
const sceneFolder = gui.addFolder('Scene');
|
||||
sceneFolder.open();
|
||||
sceneTree = new SceneNode(scene, sceneFolder);
|
||||
sceneTree = new SceneNode(scene);
|
||||
|
||||
// add objects to the the scene tree
|
||||
const setObject = (path, object) => {
|
||||
@@ -64,9 +50,9 @@ export default function SetupScene() {
|
||||
const fn_value_scene_bounds = (previous, current) => {
|
||||
if (current !== null) {
|
||||
const line = drawSceneBounds(current);
|
||||
setObject(['Scene Bounds'], line);
|
||||
setObject([SCENE_BOUNDS_NAME], line);
|
||||
} else {
|
||||
deleteObject(['Scene Bounds']);
|
||||
deleteObject([SCENE_BOUNDS_NAME]);
|
||||
}
|
||||
};
|
||||
subscribe_to_changes(selector_fn_scene_bounds, fn_value_scene_bounds);
|
||||
@@ -92,22 +78,30 @@ export default function SetupScene() {
|
||||
// keys_valid.push(key);
|
||||
const json = current[key];
|
||||
const camera = drawCamera(json);
|
||||
setObject(['Cameras', key], camera);
|
||||
setObject([CAMERAS_NAME, key], camera);
|
||||
}
|
||||
}
|
||||
for (const key of prev) {
|
||||
// invalid so delete
|
||||
if (!curr.has(key) || current[key] === null) {
|
||||
// keys_invalid.push(key);
|
||||
deleteObject(['Cameras', key]);
|
||||
deleteObject([CAMERAS_NAME, key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
deleteObject(['Cameras']);
|
||||
deleteObject([CAMERAS_NAME]);
|
||||
}
|
||||
};
|
||||
subscribe_to_changes(selector_fn_cameras, fn_value_cameras);
|
||||
|
||||
return sceneTree;
|
||||
}
|
||||
|
||||
// manages setting up the scene and other logic for keeping state in sync with the server
|
||||
export function SceneTreeWebSocketListener() {
|
||||
const socket = useContext(WebSocketContext).socket;
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
socket.addEventListener('message', (originalCmd) => {
|
||||
// set the remote description when the offer is received
|
||||
@@ -121,7 +115,5 @@ export default function SetupScene() {
|
||||
});
|
||||
}
|
||||
});
|
||||
}, []); // empty dependency array means only run once
|
||||
|
||||
return scene;
|
||||
}, [socket]); // dependency to call this whenever the websocket changes
|
||||
}
|
||||
@@ -122,7 +122,6 @@ function transpose(matrix) {
|
||||
export function drawCamera(camera): THREE.Object3D {
|
||||
const group = new THREE.Group();
|
||||
|
||||
// console.log(camera);
|
||||
console.assert(
|
||||
camera.type === 'PinholeCamera',
|
||||
'The camera should be a PinholeCamera',
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Button } from '@mui/material';
|
||||
|
||||
export default function CameraPanel() {
|
||||
return (
|
||||
<div className="CameraPanel">
|
||||
TODO: create a camera path panel list of cameras
|
||||
<Button id="CameraPanel-add-camera-button" variant="outlined">
|
||||
Add Camera
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import CameraPanel from './CameraPanel';
|
||||
|
||||
export default CameraPanel;
|
||||
@@ -0,0 +1,163 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
import { FaLightbulb, FaTractor } from 'react-icons/fa';
|
||||
import { Menu, MenuItem, ProSidebar, SubMenu } from 'react-pro-sidebar';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import { Leva } from 'leva';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useEffect } from 'react';
|
||||
import StatusPanel from './StatusPanel';
|
||||
import SceneNode from '../../SceneNode';
|
||||
import LevaTheme from '../ConfigPanel/leva_theme.json';
|
||||
import CameraPanel from './CameraPanel';
|
||||
|
||||
interface TabPanelProps {
|
||||
children: React.ReactNode;
|
||||
index: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
function TabPanel(props: TabPanelProps) {
|
||||
const { children, value, index, ...other } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
role="tabpanel"
|
||||
hidden={value !== index}
|
||||
id={`simple-tabpanel-${index}`}
|
||||
aria-labelledby={`simple-tab-${index}`}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...other}
|
||||
>
|
||||
{value === index && (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Typography component="div">{children}</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function a11yProps(index: number) {
|
||||
return {
|
||||
id: `simple-tab-${index}`,
|
||||
'aria-controls': `simple-tabpanel-${index}`,
|
||||
};
|
||||
}
|
||||
|
||||
interface ListItemProps {
|
||||
name: object;
|
||||
object: object;
|
||||
}
|
||||
|
||||
function ListItem(props: ListItemProps) {
|
||||
const name = props.name;
|
||||
const object = props.object;
|
||||
|
||||
const [value, setValue] = React.useState(1);
|
||||
object.visible = value;
|
||||
|
||||
function handleClick() {
|
||||
setValue(!value);
|
||||
}
|
||||
|
||||
return <button type="button" onClick={handleClick}>{name}</button>;
|
||||
}
|
||||
|
||||
interface ClickableListProps {
|
||||
sceneTree: object
|
||||
}
|
||||
|
||||
function ClickableList(props: ClickableListProps) {
|
||||
const sceneTree = props.sceneTree;
|
||||
|
||||
const get_menu_items = (name: String, scene_node: SceneNode) => {
|
||||
// TODO: sort the keys by string
|
||||
const num_children = Object.keys(scene_node.children).length;
|
||||
if (num_children === 0) {
|
||||
return (
|
||||
<MenuItem icon={<FaLightbulb />}>
|
||||
<ListItem name={name} object={scene_node.object} />
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SubMenu title={name} icon={<FaTractor />} defaultOpen>
|
||||
{Object.keys(scene_node.children).map((key) =>
|
||||
get_menu_items(key, scene_node.children[key]),
|
||||
)}
|
||||
</SubMenu>
|
||||
);
|
||||
};
|
||||
|
||||
const menu_items = get_menu_items('Scene', sceneTree);
|
||||
|
||||
return (
|
||||
<ProSidebar>
|
||||
<Menu iconShape="square">{menu_items}</Menu>
|
||||
</ProSidebar>
|
||||
);
|
||||
}
|
||||
|
||||
interface BasicTabsProps {
|
||||
sceneTree: object
|
||||
}
|
||||
|
||||
export function BasicTabs(props: BasicTabsProps) {
|
||||
const sceneTree = props.sceneTree;
|
||||
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
// set the panel pane index
|
||||
useEffect(() => {
|
||||
const panel_index = 0;
|
||||
setValue(panel_index);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StatusPanel sceneTree={sceneTree} />
|
||||
<hr />
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
<Tabs
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
aria-label="basic tabs example"
|
||||
>
|
||||
<Tab label="Controls" {...a11yProps(0)} />
|
||||
<Tab label="Scene" {...a11yProps(1)} />
|
||||
<Tab label="Camera Path" {...a11yProps(2)} />
|
||||
<Tab label="Logs" {...a11yProps(3)} />
|
||||
</Tabs>
|
||||
</Box>
|
||||
<TabPanel value={value} index={0}>
|
||||
<div className="Leva-container">
|
||||
<Leva className="Leva-panel" theme={LevaTheme} titleBar={false} />
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={1}>
|
||||
<div className="Scene-container">
|
||||
<ClickableList sceneTree={sceneTree} />
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={2}>
|
||||
<CameraPanel />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={value} index={3}>
|
||||
TODO: add a logging panel
|
||||
</TabPanel>
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import Button from '@mui/material/Button';
|
||||
import { useSelector } from 'react-redux';
|
||||
import WebSocketUrlField from '../../WebSocketUrlField';
|
||||
|
||||
|
||||
interface StatusPanelProps {
|
||||
sceneTree: object
|
||||
}
|
||||
|
||||
export default function StatusPanel(props: StatusPanelProps) {
|
||||
|
||||
const sceneTree = props.sceneTree;
|
||||
|
||||
const isWebsocketConnected = useSelector(
|
||||
(state) => state.websocketState.isConnected,
|
||||
);
|
||||
const isWebrtcConnected = useSelector(
|
||||
(state) => state.webrtcState.isConnected,
|
||||
);
|
||||
const eval_fps = useSelector((state) => state.renderingState.eval_fps);
|
||||
const train_eta = useSelector((state) => state.renderingState.train_eta);
|
||||
const vis_train_ratio = useSelector(
|
||||
(state) => state.renderingState.vis_train_ratio,
|
||||
);
|
||||
|
||||
// simple toggle button
|
||||
const [value, setValue] = React.useState(1);
|
||||
const handleChange = () => {
|
||||
setValue(!value);
|
||||
};
|
||||
const scene_button = value ? "Hide Scene" : "Show Scene";
|
||||
sceneTree.object.visible = value;
|
||||
|
||||
return (
|
||||
<div className="StatusPanel">
|
||||
<div className="StatusPanel-title">Status Panel</div>
|
||||
<Button
|
||||
className="StatusPanel-button"
|
||||
variant="contained"
|
||||
disabled={!isWebsocketConnected}
|
||||
style={{ textTransform: 'none' }}
|
||||
>
|
||||
Websocket Connected
|
||||
</Button>
|
||||
<Button
|
||||
className="StatusPanel-button"
|
||||
variant="contained"
|
||||
disabled={!isWebrtcConnected}
|
||||
style={{ textTransform: 'none' }}
|
||||
>
|
||||
WebRTC Connected
|
||||
</Button>
|
||||
<Button
|
||||
className="StatusPanel-button"
|
||||
variant="outlined"
|
||||
onClick={handleChange}
|
||||
style={{ textTransform: 'none' }}
|
||||
>
|
||||
{scene_button}
|
||||
</Button>
|
||||
<WebSocketUrlField />
|
||||
<div className="StatusPanel-metrics">
|
||||
<div>
|
||||
<b>Eval FPS:</b> {eval_fps}
|
||||
</div>
|
||||
<div>
|
||||
<b>Train ETA:</b> {train_eta}
|
||||
</div>
|
||||
<div>
|
||||
<b>Time Allocation:</b> {vis_train_ratio}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import StatusPanel from './StatusPanel';
|
||||
|
||||
export default StatusPanel;
|
||||
@@ -1,27 +0,0 @@
|
||||
.WebRTCVideo {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.WebRTCVideo-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: fill;
|
||||
}
|
||||
|
||||
.canvas-container-main {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.canvas-container-main canvas {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,62 +1,57 @@
|
||||
import './ViewerWindow.css';
|
||||
|
||||
import * as THREE from 'three';
|
||||
|
||||
import React, { useContext, useEffect, useRef } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
|
||||
import WebRtcWindow from '../WebRtcWindow/WebRtcWindow';
|
||||
import Stats from 'stats.js';
|
||||
import { useSelector } from 'react-redux';
|
||||
import React, { useContext, useEffect, useRef } from 'react';
|
||||
import { WebSocketContext } from '../WebSocket/WebSocket';
|
||||
import WebRtcWindow from '../WebRtcWindow/WebRtcWindow';
|
||||
|
||||
|
||||
const msgpack = require('msgpack-lite');
|
||||
|
||||
function createStats() {
|
||||
const stats = new Stats();
|
||||
stats.setMode(0);
|
||||
stats.domElement.style.position = 'absolute';
|
||||
stats.domElement.style.left = '0';
|
||||
stats.domElement.style.top = '0';
|
||||
return stats;
|
||||
}
|
||||
|
||||
// manages a camera and the web rtc stream...
|
||||
export default function ViewerWindow(props) {
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const scene = props.scene;
|
||||
let cameraControls = null;
|
||||
|
||||
const myRef = useRef(null);
|
||||
const websocket = useContext(WebSocketContext).socket;
|
||||
const field_of_view = useSelector(
|
||||
(state) => state.renderingState.field_of_view,
|
||||
);
|
||||
const field_of_view_ref = useRef(field_of_view);
|
||||
|
||||
const camera = useRef(null);
|
||||
let cameraControls = null;
|
||||
let renderer = null;
|
||||
let viewportWidth = null;
|
||||
let viewportHeight = null;
|
||||
let stats = null;
|
||||
|
||||
const getViewportWidth = () => {
|
||||
return window.innerWidth - (window.innerWidth % 2);
|
||||
const width = myRef.current.clientWidth;
|
||||
return width - (width % 2);
|
||||
};
|
||||
|
||||
const getViewportHeight = () => {
|
||||
return window.innerHeight;
|
||||
return myRef.current.clientHeight;
|
||||
};
|
||||
|
||||
let viewportWidth = getViewportWidth();
|
||||
let viewportHeight = getViewportHeight();
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
field_of_view,
|
||||
viewportWidth / viewportHeight,
|
||||
0.01,
|
||||
100,
|
||||
);
|
||||
camera.position.x = 5;
|
||||
camera.position.y = -5;
|
||||
camera.position.z = 5;
|
||||
camera.up = new THREE.Vector3(0, 0, 1);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
});
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(viewportWidth, viewportHeight);
|
||||
renderer.domElement.style.border = '1px solid black';
|
||||
|
||||
const handleResize = () => {
|
||||
viewportWidth = getViewportWidth();
|
||||
viewportHeight = getViewportHeight();
|
||||
camera.aspect = viewportWidth / viewportHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
camera.current.aspect = viewportWidth / viewportHeight;
|
||||
camera.current.updateProjectionMatrix();
|
||||
renderer.setSize(viewportWidth, viewportHeight);
|
||||
};
|
||||
|
||||
@@ -68,27 +63,62 @@ export default function ViewerWindow(props) {
|
||||
const data = {
|
||||
type: cmd,
|
||||
path,
|
||||
data: camera.toJSON(),
|
||||
data: camera.current.toJSON(),
|
||||
};
|
||||
const message = msgpack.encode(data);
|
||||
websocket.send(message);
|
||||
}
|
||||
};
|
||||
|
||||
// keep sending the camera often
|
||||
useEffect(() => {
|
||||
const fps = 60;
|
||||
const interval = 1000 / fps;
|
||||
const refreshIntervalId = setInterval(sendCamera, interval);
|
||||
return () => {
|
||||
clearInterval(refreshIntervalId);
|
||||
};
|
||||
}, [websocket]);
|
||||
|
||||
const update = () => {
|
||||
handleResize();
|
||||
camera.updateProjectionMatrix();
|
||||
cameraControls.update();
|
||||
requestAnimationFrame(update);
|
||||
renderer.render(scene, camera);
|
||||
sendCamera();
|
||||
handleResize();
|
||||
camera.current.fov = field_of_view_ref.current;
|
||||
camera.current.updateProjectionMatrix();
|
||||
cameraControls.update();
|
||||
renderer.render(scene, camera.current);
|
||||
stats.update();
|
||||
};
|
||||
|
||||
// similar to componentDidMount
|
||||
// this is run once
|
||||
useEffect(() => {
|
||||
viewportWidth = getViewportWidth();
|
||||
viewportHeight = getViewportHeight();
|
||||
|
||||
stats = createStats();
|
||||
myRef.current.append(stats.domElement);
|
||||
|
||||
camera.current = new THREE.PerspectiveCamera(
|
||||
field_of_view_ref.current,
|
||||
viewportWidth / viewportHeight,
|
||||
0.01,
|
||||
100,
|
||||
);
|
||||
camera.current.position.x = 5;
|
||||
camera.current.position.y = -5;
|
||||
camera.current.position.z = 5;
|
||||
camera.current.up = new THREE.Vector3(0, 0, 1);
|
||||
|
||||
renderer = new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
});
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.setSize(viewportWidth, viewportHeight);
|
||||
|
||||
myRef.current.append(renderer.domElement);
|
||||
// add camera controls
|
||||
cameraControls = new OrbitControls(camera, renderer.domElement);
|
||||
cameraControls = new OrbitControls(camera.current, renderer.domElement);
|
||||
cameraControls.rotateSpeed = 2.0;
|
||||
cameraControls.zoomSpeed = 0.3;
|
||||
cameraControls.panSpeed = 0.2;
|
||||
@@ -98,19 +128,19 @@ export default function ViewerWindow(props) {
|
||||
cameraControls.dampingFactor = 1.0;
|
||||
cameraControls.update();
|
||||
update();
|
||||
|
||||
}, []);
|
||||
|
||||
// useEffect(() => {
|
||||
// camera.fov = field_of_view;
|
||||
// console.log(field_of_view);
|
||||
// camera.updateProjectionMatrix();
|
||||
// }, [field_of_view]);
|
||||
// updates the field of view inside the ref to avoid rerendering so often
|
||||
useEffect(() => {
|
||||
field_of_view_ref.current = field_of_view;
|
||||
}, [field_of_view]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{/* the webrtc viewer needs to know the camera pose */}
|
||||
<WebRtcWindow />
|
||||
<div className="canvas-container-main" ref={myRef} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export default function WebRtcWindow() {
|
||||
username: 'openrelayproject',
|
||||
credential: 'openrelayproject',
|
||||
},
|
||||
// extra STUN server(s):
|
||||
// Extra STUN and TURN server(s):
|
||||
// {
|
||||
// urls: 'stun:stun.l.google.com:19302',
|
||||
// },
|
||||
@@ -83,6 +83,15 @@ export default function WebRtcWindow() {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pc.onclose = () => {
|
||||
dispatch({
|
||||
type: 'write',
|
||||
path: 'webrtcState/isConnected',
|
||||
data: false,
|
||||
});
|
||||
};
|
||||
|
||||
return pc;
|
||||
};
|
||||
|
||||
@@ -161,7 +170,19 @@ export default function WebRtcWindow() {
|
||||
console.log('[webrtc] starting process');
|
||||
sendOffer();
|
||||
});
|
||||
}, []); // empty dependency array means only run once
|
||||
|
||||
// kill the webrtc connection on dismount
|
||||
return () => {
|
||||
if (pcRef.current !== null) {
|
||||
dispatch({
|
||||
type: 'write',
|
||||
path: 'webrtcState/isConnected',
|
||||
data: false,
|
||||
});
|
||||
pcRef.current.close();
|
||||
}
|
||||
};
|
||||
}, [websocket]); // dependency to call this whenever the websocket changes
|
||||
|
||||
return (
|
||||
<div className="WebRTCVideo">
|
||||
|
||||
@@ -1,49 +1,29 @@
|
||||
// Much of this code comes from or is inspired by:
|
||||
// https://www.pluralsight.com/guides/using-web-sockets-in-your-reactredux-app
|
||||
|
||||
import React, { createContext } from 'react';
|
||||
import React, { createContext, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
const WebSocketContext = createContext(null);
|
||||
|
||||
export { WebSocketContext };
|
||||
|
||||
function getParam(param_name) {
|
||||
// https://stackoverflow.com/questions/831030/how-to-get-get-request-parameters-in-javascript
|
||||
const params = new RegExp(
|
||||
`[?&]${encodeURIComponent(param_name)}=([^&]*)`,
|
||||
).exec(window.location.href);
|
||||
if (params === null) {
|
||||
return undefined;
|
||||
}
|
||||
return decodeURIComponent(params[1]);
|
||||
}
|
||||
|
||||
function getWebsocketEndpoint() {
|
||||
const endpoint = getParam('websocket_url');
|
||||
console.log('websocket url: ', endpoint);
|
||||
if (endpoint === undefined) {
|
||||
const message =
|
||||
'Please set the websocket endpoint. The format should look like "<viewer_url>?websocket_url=localhost:<port>". E.g., "https://viewer.nerfactory.com/branch/master/?websocket_url=localhost:7007".';
|
||||
window.alert(message);
|
||||
return '';
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
export default function WebSocketContextFunction({ children }) {
|
||||
const dispatch = useDispatch();
|
||||
let ws = null;
|
||||
let socket = null;
|
||||
|
||||
let socket;
|
||||
let ws;
|
||||
|
||||
// should look like e.g., "ws://<localhost:port>"
|
||||
const websocketEndpoint = getWebsocketEndpoint();
|
||||
// this code will rerender anytime the webosocket changes now
|
||||
const websocket_url = useSelector(
|
||||
(state) => state.websocketState.websocket_url,
|
||||
);
|
||||
console.log(websocket_url);
|
||||
|
||||
const connect = () => {
|
||||
socket = new WebSocket(`ws://${websocketEndpoint}/`);
|
||||
const url = `ws://${websocket_url}/`;
|
||||
socket = new WebSocket(url);
|
||||
socket.binaryType = 'arraybuffer';
|
||||
socket.onopen = () => {
|
||||
dispatch({
|
||||
@@ -60,9 +40,6 @@ export default function WebSocketContextFunction({ children }) {
|
||||
path: 'websocketState/isConnected',
|
||||
data: false,
|
||||
});
|
||||
setTimeout(() => {
|
||||
connect();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
socket.onerror = (err) => {
|
||||
@@ -73,14 +50,20 @@ export default function WebSocketContextFunction({ children }) {
|
||||
);
|
||||
socket.close();
|
||||
};
|
||||
return socket;
|
||||
};
|
||||
|
||||
if (!socket) {
|
||||
connect();
|
||||
ws = {
|
||||
socket,
|
||||
useEffect(() => {
|
||||
// cleanup function to close the websocket on rerender
|
||||
return () => {
|
||||
socket.close();
|
||||
};
|
||||
}
|
||||
}, [websocket_url]);
|
||||
|
||||
connect();
|
||||
ws = {
|
||||
socket
|
||||
};
|
||||
|
||||
return (
|
||||
<WebSocketContext.Provider value={ws}>{children}</WebSocketContext.Provider>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { TextField } from '@mui/material';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
export default function WebSocketUrlField() {
|
||||
|
||||
const websocket_url = useSelector(
|
||||
(state) => state.websocketState.websocket_url,
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
const websocket_url_onchange = (event) => {
|
||||
const value = event.target.value;
|
||||
dispatch({
|
||||
type: 'write',
|
||||
path: 'websocketState/websocket_url',
|
||||
data: value,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<TextField
|
||||
className="WebSocketUrlField"
|
||||
label="WebSocket URL"
|
||||
variant="outlined"
|
||||
value={websocket_url}
|
||||
onChange={websocket_url_onchange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { split_path } from './utils';
|
||||
const initialState = {
|
||||
// the websocket connection state
|
||||
websocketState: {
|
||||
websocket_url: 'localhost:7007',
|
||||
isConnected: false,
|
||||
},
|
||||
// the webrtc connection state
|
||||
|
||||
+327
-13
@@ -162,7 +162,7 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.18.9"
|
||||
|
||||
"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6":
|
||||
"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
|
||||
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
|
||||
@@ -513,7 +513,7 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.8.0"
|
||||
|
||||
"@babel/plugin-syntax-jsx@^7.18.6":
|
||||
"@babel/plugin-syntax-jsx@^7.17.12", "@babel/plugin-syntax-jsx@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
|
||||
integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==
|
||||
@@ -1025,7 +1025,7 @@
|
||||
core-js-pure "^3.20.2"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
version "7.18.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
|
||||
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
|
||||
@@ -1167,6 +1167,114 @@
|
||||
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36"
|
||||
integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==
|
||||
|
||||
"@emotion/babel-plugin@^11.10.0":
|
||||
version "11.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz#879db80ba622b3f6076917a1e6f648b1c7d008c7"
|
||||
integrity sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.16.7"
|
||||
"@babel/plugin-syntax-jsx" "^7.17.12"
|
||||
"@babel/runtime" "^7.18.3"
|
||||
"@emotion/hash" "^0.9.0"
|
||||
"@emotion/memoize" "^0.8.0"
|
||||
"@emotion/serialize" "^1.1.0"
|
||||
babel-plugin-macros "^3.1.0"
|
||||
convert-source-map "^1.5.0"
|
||||
escape-string-regexp "^4.0.0"
|
||||
find-root "^1.1.0"
|
||||
source-map "^0.5.7"
|
||||
stylis "4.0.13"
|
||||
|
||||
"@emotion/cache@^11.10.0", "@emotion/cache@^11.10.3":
|
||||
version "11.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.3.tgz#c4f67904fad10c945fea5165c3a5a0583c164b87"
|
||||
integrity sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.8.0"
|
||||
"@emotion/sheet" "^1.2.0"
|
||||
"@emotion/utils" "^1.2.0"
|
||||
"@emotion/weak-memoize" "^0.3.0"
|
||||
stylis "4.0.13"
|
||||
|
||||
"@emotion/hash@^0.9.0":
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7"
|
||||
integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==
|
||||
|
||||
"@emotion/is-prop-valid@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83"
|
||||
integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.8.0"
|
||||
|
||||
"@emotion/memoize@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
|
||||
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
|
||||
|
||||
"@emotion/react@^11.10.4":
|
||||
version "11.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.4.tgz#9dc6bccbda5d70ff68fdb204746c0e8b13a79199"
|
||||
integrity sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.3"
|
||||
"@emotion/babel-plugin" "^11.10.0"
|
||||
"@emotion/cache" "^11.10.0"
|
||||
"@emotion/serialize" "^1.1.0"
|
||||
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
|
||||
"@emotion/utils" "^1.2.0"
|
||||
"@emotion/weak-memoize" "^0.3.0"
|
||||
hoist-non-react-statics "^3.3.1"
|
||||
|
||||
"@emotion/serialize@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.0.tgz#b1f97b1011b09346a40e9796c37a3397b4ea8ea8"
|
||||
integrity sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.9.0"
|
||||
"@emotion/memoize" "^0.8.0"
|
||||
"@emotion/unitless" "^0.8.0"
|
||||
"@emotion/utils" "^1.2.0"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@emotion/sheet@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.0.tgz#771b1987855839e214fc1741bde43089397f7be5"
|
||||
integrity sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==
|
||||
|
||||
"@emotion/styled@^11.10.4":
|
||||
version "11.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.4.tgz#e93f84a4d54003c2acbde178c3f97b421fce1cd4"
|
||||
integrity sha512-pRl4R8Ez3UXvOPfc2bzIoV8u9P97UedgHS4FPX594ntwEuAMA114wlaHvOK24HB48uqfXiGlYIZYCxVJ1R1ttQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.3"
|
||||
"@emotion/babel-plugin" "^11.10.0"
|
||||
"@emotion/is-prop-valid" "^1.2.0"
|
||||
"@emotion/serialize" "^1.1.0"
|
||||
"@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
|
||||
"@emotion/utils" "^1.2.0"
|
||||
|
||||
"@emotion/unitless@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db"
|
||||
integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==
|
||||
|
||||
"@emotion/use-insertion-effect-with-fallbacks@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df"
|
||||
integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==
|
||||
|
||||
"@emotion/utils@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
|
||||
integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
|
||||
|
||||
"@emotion/weak-memoize@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
|
||||
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
||||
|
||||
"@eslint/eslintrc@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
|
||||
@@ -1492,6 +1600,92 @@
|
||||
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
|
||||
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
|
||||
|
||||
"@mui/base@5.0.0-alpha.95":
|
||||
version "5.0.0-alpha.95"
|
||||
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.95.tgz#eac88ddb6ded633a73cd3c19638241f0f9fa274f"
|
||||
integrity sha512-fcxnDeO7rBwzP0buVdI5fn0aA7NQ/AeUV5RzIIH0kOXVVT21HB4JFf41Qhwd0PIq63PXxmc6Fs2mdlzMYuPo9g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.9"
|
||||
"@emotion/is-prop-valid" "^1.2.0"
|
||||
"@mui/types" "^7.2.0"
|
||||
"@mui/utils" "^5.10.3"
|
||||
"@popperjs/core" "^2.11.6"
|
||||
clsx "^1.2.1"
|
||||
prop-types "^15.8.1"
|
||||
react-is "^18.2.0"
|
||||
|
||||
"@mui/core-downloads-tracker@^5.10.3":
|
||||
version "5.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.3.tgz#e17a3cd87c7814ff35592284b19ae39ad52b85ac"
|
||||
integrity sha512-mX2S0d1oboKBbWQqWIgRmyALAEzh37yiknpD3mKx8bcoMKbp1VtqzIt0aeHP16Uhsd0eValDFILxLNHWi0oddQ==
|
||||
|
||||
"@mui/material@^5.10.3":
|
||||
version "5.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.10.3.tgz#5497128ea7c8de1d40ba9bd95437d46d441fa1d1"
|
||||
integrity sha512-g0lzHcqWHYeOEAxTzcwpM1I7b+wyiRTeXkEdRsspnOpZtb0H/1xg386tMFRGbxBJ4zfVGT+TWublofw7pyQkqw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.9"
|
||||
"@mui/base" "5.0.0-alpha.95"
|
||||
"@mui/core-downloads-tracker" "^5.10.3"
|
||||
"@mui/system" "^5.10.3"
|
||||
"@mui/types" "^7.2.0"
|
||||
"@mui/utils" "^5.10.3"
|
||||
"@types/react-transition-group" "^4.4.5"
|
||||
clsx "^1.2.1"
|
||||
csstype "^3.1.0"
|
||||
prop-types "^15.8.1"
|
||||
react-is "^18.2.0"
|
||||
react-transition-group "^4.4.5"
|
||||
|
||||
"@mui/private-theming@^5.10.3":
|
||||
version "5.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.10.3.tgz#7325eef3e480caaaa2d866b9057943ec4fbcb8ce"
|
||||
integrity sha512-LCYIKlkGz2BTSng2BFzzwSJBRZbChIUri2x2Nh8ryk2B1Ho7zpvE7ex6y39LlStG2Frf92NFC/V4YQbmMAjD5A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.9"
|
||||
"@mui/utils" "^5.10.3"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/styled-engine@^5.10.3":
|
||||
version "5.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.10.3.tgz#c3e061548951568936b749a58531fb269af48948"
|
||||
integrity sha512-9Uz7eB8xXoiDvpJ9qBxZ/2xGO8xKfA2T23dw4AsQ69SQtGatrOLAapzP2lNr0tfB9xvKucclPFhRO5aLhDFOVQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.9"
|
||||
"@emotion/cache" "^11.10.3"
|
||||
csstype "^3.1.0"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/system@^5.10.3":
|
||||
version "5.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.10.3.tgz#26f82e506af506f4fddde265c20e1100baaca03e"
|
||||
integrity sha512-uLW/CIz3zk1jr5zH0ahOUqJIrpWP02Mv4emfrplh7Mh5JCb/oumhYaC/ALJJEjzUHKg9wwiyuM0pCwK/kSf1jQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.9"
|
||||
"@mui/private-theming" "^5.10.3"
|
||||
"@mui/styled-engine" "^5.10.3"
|
||||
"@mui/types" "^7.2.0"
|
||||
"@mui/utils" "^5.10.3"
|
||||
clsx "^1.2.1"
|
||||
csstype "^3.1.0"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
"@mui/types@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.0.tgz#91380c2d42420f51f404120f7a9270eadd6f5c23"
|
||||
integrity sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA==
|
||||
|
||||
"@mui/utils@^5.10.3":
|
||||
version "5.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.10.3.tgz#ce2a96f31de2a5e717f507b5383dbabbddbc4dfc"
|
||||
integrity sha512-4jXMDPfx6bpMVuheLaOpKTjpzw39ogAZLeaLj5+RJec3E37/hAZMYjURfblLfTWMMoGoqkY03mNsZaEwNobBow==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.9"
|
||||
"@types/prop-types" "^15.7.5"
|
||||
"@types/react-is" "^16.7.1 || ^17.0.0"
|
||||
prop-types "^15.8.1"
|
||||
react-is "^18.2.0"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
@@ -1528,6 +1722,11 @@
|
||||
schema-utils "^3.0.0"
|
||||
source-map "^0.7.3"
|
||||
|
||||
"@popperjs/core@^2.11.6", "@popperjs/core@^2.4.0":
|
||||
version "2.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
|
||||
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
|
||||
|
||||
"@radix-ui/popper@0.1.0":
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/popper/-/popper-0.1.0.tgz#c387a38f31b7799e1ea0d2bb1ca0c91c2931b063"
|
||||
@@ -2193,7 +2392,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed"
|
||||
integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw==
|
||||
|
||||
"@types/prop-types@*":
|
||||
"@types/prop-types@*", "@types/prop-types@^15.7.5":
|
||||
version "15.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||
@@ -2220,6 +2419,20 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-is@^16.7.1 || ^17.0.0":
|
||||
version "17.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a"
|
||||
integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.4.5":
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
||||
integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "18.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe"
|
||||
@@ -3254,7 +3467,7 @@ check-types@^11.1.1:
|
||||
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f"
|
||||
integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==
|
||||
|
||||
chokidar@^3.4.2, chokidar@^3.5.3:
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
@@ -3284,7 +3497,7 @@ cjs-module-lexer@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
|
||||
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
||||
|
||||
classnames@^2.3.1:
|
||||
classnames@^2.2.6, classnames@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||
integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
|
||||
@@ -3305,6 +3518,11 @@ cliui@^7.0.2:
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
clsx@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||
|
||||
co@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
@@ -3457,7 +3675,7 @@ content-type@~1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
|
||||
convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
|
||||
convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
|
||||
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
|
||||
@@ -3732,7 +3950,7 @@ cssstyle@^2.3.0:
|
||||
dependencies:
|
||||
cssom "~0.3.6"
|
||||
|
||||
csstype@^3.0.2, csstype@^3.0.4:
|
||||
csstype@^3.0.2, csstype@^3.0.4, csstype@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
|
||||
integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
|
||||
@@ -3957,6 +4175,14 @@ dom-converter@^0.2.0:
|
||||
dependencies:
|
||||
utila "~0.4"
|
||||
|
||||
dom-helpers@^5.0.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.8.7"
|
||||
csstype "^3.0.2"
|
||||
|
||||
dom-serializer@0:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
|
||||
@@ -4391,6 +4617,18 @@ eslint-plugin-testing-library@^5.0.1:
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "^5.13.0"
|
||||
|
||||
eslint-plugin-unused-imports@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz#d8db8c4d0cfa0637a8b51ce3fd7d1b6bc3f08520"
|
||||
integrity sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==
|
||||
dependencies:
|
||||
eslint-rule-composer "^0.3.0"
|
||||
|
||||
eslint-rule-composer@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
|
||||
integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
|
||||
|
||||
eslint-scope@5.1.1, eslint-scope@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
@@ -4747,6 +4985,11 @@ find-cache-dir@^3.3.1:
|
||||
make-dir "^3.0.2"
|
||||
pkg-dir "^4.1.0"
|
||||
|
||||
find-root@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
||||
integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
|
||||
|
||||
find-up@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
||||
@@ -5093,7 +5336,7 @@ he@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
@@ -5285,6 +5528,11 @@ immer@^9.0.7:
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.15.tgz#0b9169e5b1d22137aba7d43f8a81a495dd1b62dc"
|
||||
integrity sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef"
|
||||
integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==
|
||||
|
||||
import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||
@@ -7664,7 +7912,7 @@ prompts@^2.0.1, prompts@^2.4.2:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.8.1:
|
||||
prop-types@^15.6.2, prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
@@ -7742,6 +7990,11 @@ raw-body@2.5.1:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
re-resizable@^6.9.9:
|
||||
version "6.9.9"
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.9.tgz#99e8b31c67a62115dc9c5394b7e55892265be216"
|
||||
integrity sha512-l+MBlKZffv/SicxDySKEEh42hR6m5bAHfNu3Tvxks2c4Ah+ldnWjfnVRwxo/nxF27SsUsxDS0raAzFuJNKABXA==
|
||||
|
||||
react-app-polyfill@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz#95221e0a9bd259e5ca6b177c7bb1cb6768f68fd7"
|
||||
@@ -7811,6 +8064,11 @@ react-error-overlay@^6.0.11:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
|
||||
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
|
||||
|
||||
react-icons@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703"
|
||||
integrity sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
@@ -7821,11 +8079,21 @@ react-is@^17.0.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
|
||||
react-is@^18.0.0:
|
||||
react-is@^18.0.0, react-is@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
react-pro-sidebar@^0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/react-pro-sidebar/-/react-pro-sidebar-0.7.1.tgz#0b5edca0809ff6a23bda188c5db370f880690ee7"
|
||||
integrity sha512-Iy1X8ce4t5Vqz4CsyzjwokGUE3/IObgmYzS0ins7/2eWKle0SMUPaWdgMKFIVjtVrMr5vmjPbRicq8FxnVaf8A==
|
||||
dependencies:
|
||||
"@popperjs/core" "^2.4.0"
|
||||
classnames "^2.2.6"
|
||||
react-slidedown "^2.4.5"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
react-redux@^8.0.2:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad"
|
||||
@@ -7898,6 +8166,23 @@ react-scripts@^5.0.1:
|
||||
optionalDependencies:
|
||||
fsevents "^2.3.2"
|
||||
|
||||
react-slidedown@^2.4.5:
|
||||
version "2.4.7"
|
||||
resolved "https://registry.yarnpkg.com/react-slidedown/-/react-slidedown-2.4.7.tgz#c09e72bba8aac25018fd644ece041da771854589"
|
||||
integrity sha512-HGDfrqo70r1WVE0DwrySPdCT27/2wcZaJYh5kOnmuPSCtjDDJrNkDdn4Ep/cma2VVfwupeAGhbc2pbrGThU6VQ==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
react-transition-group@^4.4.5:
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
||||
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
dom-helpers "^5.0.1"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react@^18.1.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
@@ -8071,6 +8356,11 @@ reselect@^4.1.5:
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.6.tgz#19ca2d3d0b35373a74dc1c98692cdaffb6602656"
|
||||
integrity sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ==
|
||||
|
||||
resize-observer-polyfill@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
@@ -8198,6 +8488,15 @@ sass-loader@^12.3.0:
|
||||
klona "^2.0.4"
|
||||
neo-async "^2.6.2"
|
||||
|
||||
sass@^1.54.8:
|
||||
version "1.54.8"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.54.8.tgz#4adef0dd86ea2b1e4074f551eeda4fc5f812a996"
|
||||
integrity sha512-ib4JhLRRgbg6QVy6bsv5uJxnJMTS2soVcCp9Y88Extyy13A8vV0G1fAwujOzmNkFQbR3LvedudAMbtuNRPbQww==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
source-map-js ">=0.6.2 <2.0.0"
|
||||
|
||||
sax@~1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
@@ -8437,7 +8736,7 @@ 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, source-map-js@^1.0.2:
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
@@ -8472,6 +8771,11 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, sourc
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
source-map@^0.5.7:
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
|
||||
|
||||
source-map@^0.7.3:
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||
@@ -8546,6 +8850,11 @@ stackframe@^1.3.4:
|
||||
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
|
||||
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
|
||||
|
||||
stats.js@^0.17.0:
|
||||
version "0.17.0"
|
||||
resolved "https://registry.yarnpkg.com/stats.js/-/stats.js-0.17.0.tgz#b1c3dc46d94498b578b7fd3985b81ace7131cc7d"
|
||||
integrity sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==
|
||||
|
||||
statuses@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
||||
@@ -8700,6 +9009,11 @@ stylehacks@^5.1.0:
|
||||
browserslist "^4.16.6"
|
||||
postcss-selector-parser "^6.0.4"
|
||||
|
||||
stylis@4.0.13:
|
||||
version "4.0.13"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
|
||||
integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
@@ -8969,7 +9283,7 @@ tslib@^1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.3, tslib@^2.1.0:
|
||||
tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
||||
Reference in new issue
Block a user