diff --git a/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx b/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
index 66b92458e..5b20b545e 100644
--- a/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
+++ b/nerfstudio/viewer/app/src/modules/ConfigPanel/ConfigPanel.jsx
@@ -1,6 +1,8 @@
-import { buttonGroup, useControls } from 'leva';
+import * as React from 'react';
+import { Leva, buttonGroup, useControls } from 'leva';
import { useContext, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
+import LevaTheme from '../../themes/leva_theme.json';
import { WebSocketContext } from '../WebSocket/WebSocket';
@@ -22,7 +24,7 @@ function dispatch_and_send(websocket, dispatch, path, data) {
}
}
-export function RenderControls() {
+function ControlsLeva() {
// connection status indicators
const websocket = useContext(WebSocketContext).socket;
const outputOptions = useSelector(
@@ -326,3 +328,18 @@ export function RenderControls() {
return null;
}
+
+export function RenderControls() {
+ return (
+
+
+
+
+ );
+}
diff --git a/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx b/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx
index 51617dfc8..30e390b34 100644
--- a/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx
+++ b/nerfstudio/viewer/app/src/modules/SidePanel/ExportPanel/ExportPanel.jsx
@@ -49,7 +49,7 @@ const CLIPPING_BOX_NAME = 'Clipping Box';
export default function ExportPanel(props) {
// unpack relevant information
const sceneTree = props.sceneTree;
- const showExportBox = props.showExportBox;
+ const showExportBox = useSelector((state) => state.show_export_box);
const [type_value, setTypeValue] = React.useState(false);
diff --git a/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx b/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx
index 35dd126d6..292c88a85 100644
--- a/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx
+++ b/nerfstudio/viewer/app/src/modules/SidePanel/SidePanel.jsx
@@ -5,19 +5,16 @@ import * as THREE from 'three';
import Box from '@mui/material/Box';
import Divider from '@mui/material/Divider';
-import { Leva } from 'leva';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
-import Typography from '@mui/material/Typography';
import {
CameraAltRounded,
TuneRounded,
WidgetsRounded,
ImportExportRounded,
} from '@mui/icons-material/';
-import { useSelector } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import StatusPanel from './StatusPanel';
-import LevaTheme from '../../themes/leva_theme.json';
import CameraPanel from './CameraPanel';
import ScenePanel from './ScenePanel';
import { RenderControls } from '../ConfigPanel/ConfigPanel';
@@ -39,31 +36,6 @@ export const snap_to_camera = (sceneTree, camera, matrix) => {
);
};
-interface TabPanelProps {
- children: React.ReactNode;
- index: number;
- value: number;
-}
-
-function TabPanel(props: TabPanelProps) {
- const { children, value, index, ...other } = props;
-
- return (
-
-
- {children}
-
-
- );
-}
-
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
@@ -71,6 +43,86 @@ function a11yProps(index: number) {
};
}
+interface PanelTabContentsProps {
+ children: React.ReactNode;
+ index: number;
+ value: number;
+}
+
+/** One tab in the control panel. */
+function PanelTabContents(props: PanelTabContentsProps) {
+ const { children, value, index, ...other } = props;
+
+ return (
+
+ {children}
+
+ );
+}
+
+interface PanelContentsProps {
+ children: React.ReactNode;
+}
+
+function PanelContents(props: PanelContentsProps) {
+ const dispatch = useDispatch();
+ const [tabState, setTabState] = React.useState(0);
+ const handleChange = (_event: React.SyntheticEvent, newValue: number) => {
+ setTabState(newValue);
+ dispatch({
+ type: 'write',
+ path: 'show_export_box',
+ data: newValue === 3,
+ });
+ };
+ const camera_choice = useSelector(
+ (state) => state.renderingState.camera_choice,
+ );
+ const arrayChildren = React.Children.toArray(props.children);
+
+ return (
+ <>
+
+
+ } label="Controls" {...a11yProps(0)} />
+ } label="Render" {...a11yProps(1)} />
+ }
+ label="Scene"
+ disabled={camera_choice === 'Render Camera'}
+ {...a11yProps(2)}
+ />
+ }
+ label="Export"
+ disabled={camera_choice === 'Render Camera'}
+ {...a11yProps(3)}
+ />
+
+
+
+ {arrayChildren.map((child, index) => (
+ // eslint-disable-next-line react/no-array-index-key
+
+ {child}
+
+ ))}
+ >
+ );
+}
+
interface BasicTabsProps {
sceneTree: object;
}
@@ -78,78 +130,17 @@ interface BasicTabsProps {
export function BasicTabs(props: BasicTabsProps) {
const sceneTree = props.sceneTree;
- const [value, setValue] = React.useState(0);
- const [showExportBox, setShowExportBox] = React.useState(false);
-
- const handleChange = (event: React.SyntheticEvent, newValue: number) => {
- setValue(newValue);
- setShowExportBox(newValue === 3);
- };
- const camera_choice = useSelector(
- (state) => state.renderingState.camera_choice,
- );
-
- React.useEffect(() => {
- if (camera_choice === 'Render Camera') {
- setValue(1);
- }
- }, [camera_choice]);
-
return (
-
-
- } label="Controls" {...a11yProps(0)} />
- } label="Render" {...a11yProps(1)} />
- }
- label="Scene"
- disabled={camera_choice === 'Render Camera'}
- {...a11yProps(2)}
- />
- }
- label="Export"
- disabled={camera_choice === 'Render Camera'}
- {...a11yProps(3)}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
);
diff --git a/nerfstudio/viewer/app/src/reducer.js b/nerfstudio/viewer/app/src/reducer.js
index 5aefe9c8a..21279a547 100644
--- a/nerfstudio/viewer/app/src/reducer.js
+++ b/nerfstudio/viewer/app/src/reducer.js
@@ -13,6 +13,8 @@ const initialState = {
render_img: null, // The rendered images
+ show_export_box: false, // whether to show the export box
+
// the rendering state
renderingState: {
// cameras