diff --git a/CMakeLists.txt b/CMakeLists.txt index 19b6ca6..29f851e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ enable_testing() add_subdirectory("PiPedalCommon") -add_subdirectory("react") +add_subdirectory("vite") add_subdirectory("src") diff --git a/react/.gitignore b/react/.gitignore deleted file mode 100644 index 6362cab..0000000 --- a/react/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -/package-lock.json -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/react/CMakeLists.txt b/react/CMakeLists.txt deleted file mode 100644 index 2c84502..0000000 --- a/react/CMakeLists.txt +++ /dev/null @@ -1,208 +0,0 @@ - - -find_program(NPM_COMMAND npm) - - -if (CMAKE_BUILD_TYPE MATCHES Debug) - set(BUILD_REACT "echo") - set(BUILD_REACT_ARGS Skipping react debug build) -else() - set(BUILD_REACT npm) - set(BUILD_REACT_ARGS run build) -endif() - -set (BUILD_DIRECTORY ${PROJECT_SOURCE_DIR}/build/react/build) - - -add_custom_command( - OUTPUT ${BUILD_DIRECTORY}/index.html - COMMAND ${BUILD_REACT} - ARGS ${BUILD_REACT_ARGS} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react - - DEPENDS - src/PiPedalError.tsx - src/OldDeleteIcon.tsx - src/MaterialColors.tsx - src/Pedalboard.tsx - src/SystemMidiBindingView.tsx - src/GovernorSettings.tsx - src/ObservableProperty.tsx - src/SnapshotEditor.tsx - src/SelectMidiChannelsDialog.tsx - src/AlsaMidiDeviceInfo.tsx - src/WifiDirectConfigSettings.tsx - src/WifiConfigSettings.tsx - src/SnapshotButton.tsx - src/WindowScale.tsx - src/JsonAtom.tsx - src/PiPedalSocket.tsx - src/TemporaryDrawer.tsx - src/DarkMode.tsx - src/MidiBindingsDialog.tsx - src/MidiBinding.tsx - src/SnapshotDialog.tsx - src/SettingsDialog.tsx - src/PedalboardView.tsx - src/reportWebVitals.ts - src/OptionsDialog.tsx - src/SelectChannelsDialog.tsx - src/PluginPresetSelector.tsx - src/VuMeter.tsx - src/ToobFrequencyResponseView.tsx - src/SystemMidiBindingsDialog.tsx - src/FullScreenIME.tsx - src/App.tsx - src/ToobInputStageView.tsx - src/XxxSnippet.tsx - src/JackServerSettings.tsx - src/ToobWaveShapeView.tsx - src/ToobPowerStage2View.tsx - src/PluginPreset.tsx - src/logo.svg - src/Banks.tsx - src/NoChangePassword.tsx - src/SearchFilter.tsx - src/FilePropertyDirectorySelectDialog.tsx - src/GxTunerView.tsx - src/UpdateDialog.tsx - src/svg/file_download_black_24dp.svg - src/svg/fx_filter.svg - src/svg/fx_pitch.svg - src/svg/fx_distortion.svg - src/svg/fx_split_b.svg - src/svg/fx_chorus.svg - src/svg/fx_limiter.svg - src/svg/fx_phaser.svg - src/svg/snapshot_0.svg - src/svg/snapshot_3.svg - src/svg/file_upload_black_24dp.svg - src/svg/ic_bank.svg - src/svg/fx_function.svg - src/svg/fx_dial.svg - src/svg/ic_settings.svg - src/svg/fx_split_a.svg - src/svg/fx_empty.svg - src/svg/fx_amplifier.svg - src/svg/fx_spectral.svg - src/svg/fx_eq.svg - src/svg/ic_edit_banks.svg - src/svg/ic_help_outline.svg - src/svg/ic_save_bank_as.svg - src/svg/ic_pluginpreset2.svg - src/svg/fx_flanger2.svg - src/svg/snapshot_6.svg - src/svg/fx_lr.svg - src/svg/fx_generator.svg - src/svg/snapshot_1.svg - src/svg/fx_reverb.svg - src/svg/fx_plugin.svg - src/svg/snapshot_4.svg - src/svg/drive_file_rename_outline_black_24dp.svg - src/svg/snapshot_2.svg - src/svg/fx_instrument.svg - src/svg/fx_modulator.svg - src/svg/fx_error.svg - src/svg/fx_simulator.svg - src/svg/fx_filter_hp.svg - src/svg/fx_compressor.svg - src/svg/fx_gate.svg - src/svg/fx_terminal.svg - src/svg/ic_midi.svg - src/svg/fx_oscillator.svg - src/svg/fx_analyzer.svg - src/svg/fx_flanger.svg - src/svg/old_delete_outline_24dp.svg - src/svg/ic_presets.svg - src/svg/fx_delay.svg - src/svg/fx_parametric_eq.svg - src/svg/fx_spatial.svg - src/svg/fx_converter.svg - src/svg/fx_constant.svg - src/svg/snapshot_5.svg - src/svg/ic_pluginpreset.svg - src/svg/fx_mixer.svg - src/svg/fx_utility.svg - src/Updater.tsx - src/UploadPresetDialog.tsx - src/RadioSelectDialog.tsx - src/setupTests.ts - src/ToobCabSimView.tsx - src/RenameDialog.tsx - src/SplitControlView.tsx - src/SelectHoverBackground.tsx - src/IControlViewFactory.tsx - src/OkCancelDialog.tsx - src/BankDialog.tsx - src/PluginPresetsDialog.tsx - src/App.test.tsx - src/ControlViewFactory.tsx - src/DraggableGrid.tsx - src/ZoomedDial.tsx - src/index.css - src/Pedal.tsx - src/Draggable.tsx - src/PluginIcon.tsx - src/react-app-env.d.ts - src/TextFieldEx.tsx - src/FilePropertyDialog.tsx - src/FilePropertyDirectoryTree.tsx - src/StringBuilder.tsx - src/MainPage.tsx - src/PluginInfoDialog.tsx - src/AppThemed.tsx - src/Lv2Plugin.tsx - src/ToobSpectrumResponseView.tsx - src/PerformanceView.tsx - src/JackServerSettingsDialog.tsx - src/Utility.tsx - src/JackStatusView.tsx - src/ToobSpectrumAnalyzerView.tsx - src/SvgPathBuilder.tsx - src/PresetSelector.tsx - src/PresetDialog.tsx - src/WifiChannel.tsx - src/AboutDialog.tsx - src/GxTunerControl.tsx - src/VirtualKeyboardHandler.tsx - src/PluginControl.tsx - src/LoadPluginDialog.tsx - src/ZoomedUiControl.tsx - src/AlsaDeviceInfo.tsx - src/ToobToneStackView.tsx - src/UploadFileDialog.tsx - src/PluginOutputControl.tsx - src/SelectThemeDialog.tsx - src/Rect.tsx - src/AppThemed.css - src/ColorDropdownButton.tsx - src/AndroidHost.tsx - src/SnapshotPanel.tsx - src/ToobMLView.tsx - src/WifiConfigDialog.tsx - src/Rectangle.tsx - src/SnapshotColors.tsx - src/PiPedalModel.tsx - src/ObservableEvent.tsx - src/JackHostStatus.tsx - src/index.tsx - src/FilePropertyControl.tsx - src/WifiDirectConfigDialog.tsx - src/MidiBindingView.tsx - src/PluginControlView.tsx - src/SplitUiControls.tsx - src/PluginClass.tsx - src/DialogEx.tsx - src/Jack.tsx - src/ResizeResponsiveComponent.tsx - src/NumericInput.tsx - src/SnapshotPropertiesDialog.tsx - src/SearchControl.tsx - src/Units.tsx - -) - -add_custom_target ( - ReactBuild ALL - DEPENDS ${BUILD_DIRECTORY}/index.html -) diff --git a/react/MiniSpinner.tsx b/react/MiniSpinner.tsx deleted file mode 100644 index 74bd742..0000000 --- a/react/MiniSpinner.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2024 Robin E. R. Davies - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - -import * as React from 'react'; -import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; - - -const styles = (theme: Theme) => createStyles({ - frame: { - position: "relative", - margin: "12px" - } -}); - - -export interface MiniSpinnerProps extends WithStyles { - theme: Theme -} - -export interface MiniSpinnerProps { - size?: number; -}; - - -const MiniSpinner = withStyles(styles)((props: MiniSpinnerProps) => { - const { classes } = props; - - let size: number = 24; - if (props.size) - { - size = props.size; - } - let thickness = 3; - let r = size-thickness/2; - return ( -
- width={size} height={size} - - -
- ); -}); diff --git a/react/README.md b/react/README.md deleted file mode 100644 index b87cb00..0000000 --- a/react/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Getting Started with Create React App - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.\ -You will also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** - -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. - -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). diff --git a/react/build.sh b/react/build.sh deleted file mode 100755 index 10da9ff..0000000 --- a/react/build.sh +++ /dev/null @@ -1 +0,0 @@ -npm run build \ No newline at end of file diff --git a/react/package.json b/react/package.json deleted file mode 100644 index 477237d..0000000 --- a/react/package.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "name": "pipedal", - "version": "0.1.0", - "private": true, - "dependencies": { - "@emotion/react": "^11.7.1", - "@emotion/styled": "^11.6.0", - "@mui/icons-material": "^5.4.1", - "@mui/material": "^5.4.1", - "@mui/styles": "^5.4.1", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/react": "^11.2.7", - "@testing-library/user-event": "^12.8.3", - "@types/jest": "^26.0.23", - "@types/node": "^12.20.15", - "@types/react": "^17.0.11", - "@types/react-dom": "^17.0.8", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-remark": "^2.1.0", - "react-scripts": "^5.0.1", - "react-virtualized-auto-sizer": "^1.0.6", - "react-window": "^1.8.6", - "typescript": "^4.3.4", - "web-vitals": "^1.1.2", - - "@babel/plugin-proposal-private-property-in-object": "^7.2.11" - - }, - "scripts": { - "start": "react-scripts start", - "build": "BUILD_PATH='../build/react/build' react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ], - "rules": { - "eqeqeq": 2, - "no-unused-expressions": 2, - "@typescript-eslint/no-unused-vars": 2 - } - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "@types/react-virtualized-auto-sizer": "^1.0.1", - "@types/react-window": "^1.8.5", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2" - } -} diff --git a/react/public/logo192x.png b/react/public/logo192x.png deleted file mode 100644 index d483c7b..0000000 Binary files a/react/public/logo192x.png and /dev/null differ diff --git a/react/react_info.txt b/react/react_info.txt deleted file mode 100644 index 5771b93..0000000 --- a/react/react_info.txt +++ /dev/null @@ -1,28 +0,0 @@ -found 8 vulnerabilities (4 moderate, 4 high) - run `npm audit fix` to fix them, or `npm audit` for details - -Created git commit. - -Success! Created my-app at /home/patch/src/piddle/react/my-app -Inside that directory, you can run several commands: - - npm start - Starts the development server. - - npm run build - Bundles the app into static files for production. - - npm test - Starts the test runner. - - npm run eject - Removes this tool and copies build dependencies, configuration files - and scripts into the app directory. If you do this, you canât go back! - -We suggest that you begin by typing: - - cd my-app - npm start - -Happy hacking! - diff --git a/react/src/AboutDialog.tsx b/react/src/AboutDialog.tsx deleted file mode 100644 index 5a81b9f..0000000 --- a/react/src/AboutDialog.tsx +++ /dev/null @@ -1,298 +0,0 @@ - -import React, { SyntheticEvent, Component } from 'react'; -import IconButton from '@mui/material/IconButton'; -import Typography from '@mui/material/Typography'; -import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel'; -import AppBar from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; -import Divider from '@mui/material/Divider'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; - -import JackHostStatus from './JackHostStatus'; -import { PiPedalError } from './PiPedalError'; -import DialogEx from './DialogEx'; - -import createStyles from '@mui/styles/createStyles'; - -import { Theme } from '@mui/material/styles'; -import { withStyles, WithStyles } from '@mui/styles'; -import Slide, { SlideProps } from '@mui/material/Slide'; - -interface AboutDialogProps extends WithStyles { - open: boolean; - onClose: () => void; -}; - -interface AboutDialogState { - jackStatus?: JackHostStatus; - - openSourceNotices: string; -}; - - -const styles = (theme: Theme) => createStyles({ - dialogAppBar: { - position: 'relative', - top: 0, left: 0 - }, - dialogTitle: { - marginLeft: theme.spacing(2), - flex: 1, - }, - sectionHead: { - marginLeft: 24, - marginRight: 24, - marginTop: 16, - paddingBottom: 12 - - }, - textBlock: { - marginLeft: 24, - marginRight: 24, - marginTop: 16, - paddingBottom: 0, - opacity: 0.95 - - }, - textBlockIndented: { - marginLeft: 40, - marginRight: 24, - marginTop: 16, - paddingBottom: 0, - opacity: 0.95 - - }, - - setting: { - minHeight: 64, - width: "100%", - textAlign: "left", - paddingLeft: 22, - paddingRight: 22 - }, - primaryItem: { - - }, - secondaryItem: { - - }, - can_select: { - userSelect: "text", - cursor: "text" - }, - no_select: { - userSelect: "none", - cursor: "text" - - } - - -}); - - -const Transition = React.forwardRef(function Transition( - props: SlideProps, ref: React.Ref -) { - return (); -}); - - -const AboutDialog = withStyles(styles, { withTheme: true })( - - class extends Component { - - model: PiPedalModel; - refNotices: React.RefObject; - - - constructor(props: AboutDialogProps) { - super(props); - this.model = PiPedalModelFactory.getInstance(); - this.refNotices = React.createRef(); - this.handleDialogClose = this.handleDialogClose.bind(this); - this.state = { - jackStatus: undefined, - openSourceNotices: "" - - }; - } - - mounted: boolean = false; - subscribed: boolean = false; - - tick() { - if (this.model.state.get() === State.Ready) { - this.model.getJackStatus() - .then(jackStatus => { - this.setState({ jackStatus: jackStatus }); - }) - .catch(error => { /* ignore*/ }); - } - } - - timerHandle?: NodeJS.Timeout; - - updateNotifications() { - let subscribed = this.mounted && this.props.open; - if (subscribed !== this.subscribed) { - if (subscribed) { - this.timerHandle = setInterval(() => this.tick(), 1000); - } else { - if (this.timerHandle) { - clearInterval(this.timerHandle); - this.timerHandle = undefined; - } - } - this.subscribed = subscribed; - } - } - - startFossRequest() { - if (this.state.openSourceNotices === "") { - fetch("var/notices.txt") - .then((request) => { - if (!request.ok) { - throw new PiPedalError("notices request failed."); - } - return request.text(); - }) - .then((text) => { - if (this.mounted) { - this.setState({ openSourceNotices: text }); - } - - }) - .catch((err) => { - // ok in debug builds. File doesn't get placed until install time. - console.log("Failed to fetch open-source notices. " + err.toString()); - }); - } - } - - componentDidMount() { - super.componentDidMount?.(); - this.mounted = true; - this.updateNotifications(); - this.startFossRequest(); - } - componentWillUnmount() { - super.componentWillUnmount?.(); - this.mounted = false; - this.updateNotifications(); - } - componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot: any): void { - super.componentDidUpdate?.(prevProps, prevState, snapshot); - this.updateNotifications(); - } - - handleDialogClose(e: SyntheticEvent) { - this.props.onClose(); - } - - - render() { - let classes = this.props.classes; - let addressKey = 0; - let serverVersion = this.model.serverVersion?.serverVersion ?? ""; - let nPos = serverVersion.indexOf(' '); - if (nPos !== -1) { - serverVersion = serverVersion.substring(nPos + 1); - } - return ( - { this.props.onClose() }} TransitionComponent={Transition} - onEnterKey={() => { this.props.onClose() }} - style={{ userSelect: "none" }} - > - -
-
- - - - - - - About - - - -
-
-
-
- - PiPedal - {serverVersion - + (this.model.serverVersion?.debug ? " (Debug)" : "")} - - - -
- - Copyright © 2022-2024 Robin Davies. - - {this.model.isAndroidHosted() && ( - - {this.model.getAndroidHostVersion()} - - )} - - {this.model.isAndroidHosted() && ( - - Copyright © 2022-2024 Robin Davies. - - )} - - - ADDRESSES - -
- { - this.model.serverVersion?.webAddresses.map((address) => - ( - - {address} - - )) - - } -
- - - - SERVER OS - -
- - {this.model.serverVersion?.osVersion ?? ""} - -
-
- - - - LEGAL NOTICES - -
-
- -
-
-
-
- - ); - - } - - } - -); - -export default AboutDialog; \ No newline at end of file diff --git a/react/src/App.test.tsx b/react/src/App.test.tsx deleted file mode 100644 index 2a68616..0000000 --- a/react/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/react/src/AppThemed.tsx b/react/src/AppThemed.tsx deleted file mode 100644 index 2dd578e..0000000 --- a/react/src/AppThemed.tsx +++ /dev/null @@ -1,1121 +0,0 @@ -// Copyright (c) 2022 Robin Davies -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import { WithStyles } from '@mui/styles'; - -import './AppThemed.css'; -//import {alpha} from '@mui/material/styles'; -import AppBar from '@mui/material/AppBar'; -import Modal from '@mui/material/Modal'; -import Toolbar from '@mui/material/Toolbar'; -import CssBaseline from '@mui/material/CssBaseline'; -import Typography from '@mui/material/Typography'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; -import IconButton from '@mui/material/IconButton'; -import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'; -import MenuButton from '@mui/icons-material/Menu'; -import { TemporaryDrawer } from './TemporaryDrawer'; -import FullscreenIcon from '@mui/icons-material/Fullscreen'; -import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'; -import List from '@mui/material/List'; -import Divider from '@mui/material/Divider'; -// import ListItem from '@mui/material/ListItem'; -import ListItemButton from '@mui/material/ListItemButton'; - -import ListItemIcon from '@mui/material/ListItemIcon'; -import ListItemText from '@mui/material/ListItemText'; -import CircularProgress from '@mui/material/CircularProgress'; -import { OnChangedHandler } from './ObservableProperty'; -import ErrorOutlineIcon from '@mui/icons-material/Error'; -import ResizeResponsiveComponent from './ResizeResponsiveComponent'; -import Button from '@mui/material/Button'; -import PresetSelector from './PresetSelector'; -import SettingsDialog from './SettingsDialog'; -import AboutDialog from './AboutDialog'; -import BankDialog from './BankDialog'; - -import { PiPedalModelFactory, PiPedalModel, State, ZoomedControlInfo, wantsReloadingScreen } from './PiPedalModel'; -import ZoomedUiControl from './ZoomedUiControl' -import MainPage from './MainPage'; -import DialogContent from '@mui/material/DialogContent'; -import DialogContentText from '@mui/material/DialogContentText'; -import DialogActions from '@mui/material/DialogActions'; -import ListSubheader from '@mui/material/ListSubheader'; -import { BankIndex, BankIndexEntry } from './Banks'; -import RenameDialog from './RenameDialog'; -import JackStatusView from './JackStatusView'; -import { Theme } from '@mui/material/styles'; -import { isDarkMode } from './DarkMode'; -import UpdateDialog from './UpdateDialog'; - -import { ReactComponent as RenameOutlineIcon } from './svg/drive_file_rename_outline_black_24dp.svg'; -import { ReactComponent as SaveBankAsIcon } from './svg/ic_save_bank_as.svg'; -import { ReactComponent as EditBanksIcon } from './svg/ic_edit_banks.svg'; -import { ReactComponent as SettingsIcon } from './svg/ic_settings.svg'; -import { ReactComponent as HelpOutlineIcon } from './svg/ic_help_outline.svg'; -import { ReactComponent as FxAmplifierIcon } from './svg/fx_amplifier.svg'; -import { PerformanceView } from './PerformanceView'; - -import DialogEx, { DialogStackState } from './DialogEx'; - - -const appStyles = (theme: Theme) => createStyles({ - "&": { // :root - colorScheme: (isDarkMode() ? "dark" : "light") - }, - menuListItem: { - color: "#FE8!important" as any, //theme.palette.text.primary, - fill: "#FE8!important" as any, //theme.palette.text.primary, - }, - menuIcon: { - fill: (theme.palette.text.primary + "!important") as any, //theme.palette.text.primary, - opacity: 0.6 - }, - toolBar: { - color: "white" - }, - listSubheader: { - backgroundImage: "linear-gradient(255,255,255,0.15),rgba(255,255,255,0.15)" - }, - loadingMask: { - position: "absolute", - minHeight: "10em", - left: "0px", - top: "0px", - right: "0px", - bottom: "0px", - opacity: 0.8, - background: - isDarkMode() ? "#222" : "#EEE", - }, - loadingContent: { - display: "block", - position: "absolute", - minHeight: "10em", - left: "0px", - top: "0px", - width: "100%", - height: "100%", - opacity: "0.95", - justifyContent: "center", - textAlign: "center", - zIndex: 2010 - }, - errorContent: { - - display: "flex", - flexDirection: "column", - justifyContent: "center", - flexWrap: "nowrap", - alignItems: "center", - position: "fixed", - minHeight: "10em", - left: "0px", - top: "0px", - width: "100%", - height: "100%", - color: isDarkMode() ? "#CCC" : "#444", - zIndex: 2000 - }, - errorContentMask: { - position: "absolute", left: 0, top: 0, - width: "100%", height: "100%", - background: isDarkMode() ? "#121212" : "#BBB", - opacity: 0.95, - zIndex: 1999 - }, - errorText: { - marginTop: 0, - padding: 0, - marginBottom: 12, - fontWeight: 500, - fontSize: "13pt", - maxWidth: 250, - opacity: 1, - zIndex: 2010, - }, - progressText: { - marginTop: 0, - fontWeight: 500, - fontSize: "13pt", - opacity: 1, - zIndex: 2010, - paddingTop: 12, - gravity: "center", - textAlign: "center" - }, - - errorMessageBox: { - flex: "0 0 auto", - width: 300, - marginLeft: "auto", - marginRight: "auto", - zIndex: 2010 - - - }, - errorMessage: { - color: theme.palette.text.secondary, - textAlign: "left", - zIndex: 2010 - - }, - loadingBox: { - display: "flex", - flexFlow: "column nowrap", - position: "absolute", - top: "20%", - left: 0, right: 0, - color: isDarkMode() ? theme.palette.text.secondary : "#888", - // border: "3px solid #888", - alignItems: "center", - textAlign: "center", - opacity: 0.8, - - }, - loadingBoxItem: { - display: "flex", flexFlow: "row nowrap", - alignItems: "center", - textAlign: "center", - - }, - - - toolBarContent: - { - position: "absolute", top: 0, width: "100%" - }, - - toolBarSpacer: - { - position: "relative", flex: "0 0 auto", - }, - - - mainFrame: { - overflow: "hidden", - display: "flex", - flexFlow: "column", - flex: "1 1 100%" - }, - - toolbarSizingPosition: { - flexGrow: 0, - flexShrink: 1, - width: "100%", - flexBasis: "auto" - }, - mainSizingPosition: { - overflow: "hidden", - flex: "1 1 auto" - }, - - heroContent: { - backgroundColor: theme.mainBackground, - position: "relative", - height: "100%", - width: "100%" - }, - - drawerItem: { - width: 350, - }, - drawerItemFullWidth: { - width: 'auto', - }, - -}); - - - -function supportsFullScreen(): boolean { - let doc: any = window.document; - let docEl: any = doc.documentElement; - var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullscreen || docEl.msRequestFullScreen; - return (!!requestFullScreen); - -} - -function setFullScreen(value: boolean) { - let doc: any = window.document; - let docEl: any = doc.documentElement; - - if (docEl.requestFullscren) // the latest offical api. - { - if (value) { - window.document.documentElement.requestFullscreen({ navigationUI: "show" }); - } else { - window.document.exitFullscreen(); - } - } - - var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullscreen || docEl.msRequestFullScreen; - var cancelFullScreen = docEl.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; - - if (value) { - requestFullScreen.call(docEl); - } - else { - cancelFullScreen.call(doc); - } -} - - -// function preventDefault(e: SyntheticEvent): void { -// e.stopPropagation(); -// e.preventDefault(); -// } - -type AppState = { - zoomedControlInfo: ZoomedControlInfo | undefined; - - isDrawerOpen: boolean; - errorMessage: string; - displayState: State; - - performanceView: boolean; - - canFullScreen: boolean; - isFullScreen: boolean; - tinyToolBar: boolean; - alertDialogOpen: boolean; - alertDialogMessage: string; - updateDialogOpen: boolean; - isSettingsDialogOpen: boolean; - onboarding: boolean; - isDebug: boolean; - - renameBankDialogOpen: boolean; - saveBankAsDialogOpen: boolean; - - aboutDialogOpen: boolean; - bankDialogOpen: boolean; - editBankDialogOpen: boolean; - zoomedControlOpen: boolean; - - - presetName: string; - presetChanged: boolean; - banks: BankIndex; - bankDisplayItems: number; - showStatusMonitor: boolean; -}; -interface AppProps extends WithStyles { -} - -const AppThemed = withStyles(appStyles)(class extends ResizeResponsiveComponent { - // Before the component mounts, we initialise our state - - model_: PiPedalModel; - errorChangeHandler_: OnChangedHandler; - stateChangeHandler_: OnChangedHandler; - - constructor(props: AppProps) { - super(props); - this.model_ = PiPedalModelFactory.getInstance(); - - this.model_.zoomedUiControl.addOnChangedHandler( - () => { - this.setState({ - zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined, - zoomedControlInfo: this.model_.zoomedUiControl.get() - }); - } - ); - - this.state = { - performanceView: false, - zoomedControlInfo: this.model_.zoomedUiControl.get(), - isDrawerOpen: false, - errorMessage: this.model_.errorMessage.get(), - displayState: this.model_.state.get(), - canFullScreen: supportsFullScreen() && !this.model_.isAndroidHosted(), - isFullScreen: !!document.fullscreenElement, - tinyToolBar: false, - alertDialogOpen: false, - alertDialogMessage: "", - presetName: this.model_.presets.get().getSelectedText(), - isSettingsDialogOpen: false, - updateDialogOpen: false, - onboarding: false, - isDebug: true, - presetChanged: this.model_.presets.get().presetChanged, - banks: this.model_.banks.get(), - renameBankDialogOpen: false, - saveBankAsDialogOpen: false, - aboutDialogOpen: false, - bankDialogOpen: false, - editBankDialogOpen: false, - zoomedControlOpen: false, - bankDisplayItems: 5, - showStatusMonitor: this.model_.showStatusMonitor.get() - - }; - - this.promptForUpdateHandler = this.promptForUpdateHandler.bind(this); - this.errorChangeHandler_ = this.setErrorMessage.bind(this); - this.beforeUnloadListener = this.beforeUnloadListener.bind(this); - this.unloadListener = this.unloadListener.bind(this); - this.stateChangeHandler_ = this.setDisplayState.bind(this); - this.presetChangedHandler = this.presetChangedHandler.bind(this); - this.alertMessageChangedHandler = this.alertMessageChangedHandler.bind(this); - this.handleCloseAlert = this.handleCloseAlert.bind(this); - this.banksChangedHandler = this.banksChangedHandler.bind(this); - this.showStatusMonitorHandler = this.showStatusMonitorHandler.bind(this); - this.onPopStateHandler = this.onPopStateHandler.bind(this); - } - - showDrawer() { - if (!this.state.isDrawerOpen) { - this.setState({ isDrawerOpen: true }) - this.pushOpenMenuState(); - } - } - hideDrawer(loadingDialog: boolean = false) { - if (!loadingDialog && window.location.hash === "#menu") { - window.history.back(); - } else { - this.setState({ isDrawerOpen: false }) - } - } - - pushOpenMenuState() { - let stackState = { tag: "OpenMenu", previousState: null }; - window.history.replaceState(stackState, "", window.location.href); - window.history.pushState({}, "", "#menu"); - } - onPopStateHandler(ev: PopStateEvent) { - let stackState: DialogStackState | null = ev.state as (DialogStackState | null); - if (stackState && stackState.tag === "OpenMenu") { - if (this.state.isDrawerOpen) { - this.setState({ isDrawerOpen: false }); - } - ev.stopPropagation(); - window.history.replaceState(stackState.previousState, "", window.location.href); - return true; - } - return false; - } - - handleSpecificBank(bankId: number) { - - this.model_.openBank(bankId) - .catch((error) => this.model_.showAlert(error.toString())); - - } - - handleSaveBankAsOk(newName: string) { - let currentName = this.model_.banks.get().getSelectedEntryName(); - if (currentName === newName) { - this.setState({ - renameBankDialogOpen: false, - saveBankAsDialogOpen: false - }); - return; - - } - - if (this.model_.banks.get().nameExists(newName)) { - this.model_.showAlert("A bank by that name already exists."); - return; - } - this.setState({ - renameBankDialogOpen: false, - saveBankAsDialogOpen: false - }); - this.model_.saveBankAs(this.model_.banks.get().selectedBank, newName) - .catch((error) => { - this.model_.showAlert(error.toString()); - }); - - } - handleBankRenameOk(newName: string) { - let currentName = this.model_.banks.get().getSelectedEntryName(); - if (currentName === newName) { - this.setState({ - renameBankDialogOpen: false, - saveBankAsDialogOpen: false - }); - return; - - } - - if (this.model_.banks.get().nameExists(newName)) { - this.model_.showAlert("A bank by that name already exists."); - return; - } - this.setState({ - renameBankDialogOpen: false - }); - this.model_.renameBank(this.model_.banks.get().selectedBank, newName) - .catch((error) => { - this.model_.showAlert(error.toString()); - }); - - } - handleSettingsDialogClose() { - this.setState({ - isSettingsDialogOpen: false, - onboarding: false - }); - } - handleDrawerSettingsClick() { - this.setState({ - isDrawerOpen: false, - isSettingsDialogOpen: true, - onboarding: false - }); - - } - - handleDisplayOnboarding() { - this.setState({ - isDrawerOpen: false, - isSettingsDialogOpen: true, - onboarding: true - }); - - } - - handleDrawerManageBanks() { - this.setState({ - isDrawerOpen: false, - bankDialogOpen: true, - editBankDialogOpen: true - }); - - } - handleDrawerSelectBank() { - - this.setState({ - bankDialogOpen: true, - editBankDialogOpen: false - }); - - } - handleDrawerDonationClick() { - this.hideDrawer(false); - if (this.model_.isAndroidHosted()) { - this.model_.showAndroidDonationActivity(); - } else { - if (window) { - window.open("https://github.com/sponsors/rerdavies", '_blank'); - } - } - - } - - handleDrawerAboutClick() { - this.setState({ - aboutDialogOpen: true - }); - - } - handleDrawerRenameBank() { - this.setState({ - renameBankDialogOpen: true, - saveBankAsDialogOpen: false - }); - - } - handleDrawerSaveBankAs() { - this.setState({ - renameBankDialogOpen: false, - saveBankAsDialogOpen: true - }); - } - - - handleCloseAlert(e?: any, reason?: any) { - this.model_.alertMessage.set(""); - } - showStatusMonitorHandler() { - this.setState({ - showStatusMonitor: this.model_.showStatusMonitor.get() - }); - } - banksChangedHandler() { - this.setState({ - banks: this.model_.banks.get() - }); - } - presetChangedHandler() { - let presets = this.model_.presets.get(); - - this.setState({ - presetName: presets.getSelectedText(), - presetChanged: presets.presetChanged - }); - } - toggleFullScreen(): void { - setFullScreen(!this.state.isFullScreen); - this.setState({ isFullScreen: !this.state.isFullScreen }); - } - - private unloadListener(e: Event) { - this.model_.close(); - return undefined; - } - - private beforeUnloadListener(e: Event) { - this.model_.close(); - return undefined; - } - promptForUpdateHandler(newValue: boolean) { - if (this.model_.enableAutoUpdate) { - if (this.state.updateDialogOpen !== newValue) { - this.setState({ updateDialogOpen: newValue }); - } - } - } - componentDidMount() { - - super.componentDidMount(); - window.addEventListener("beforeunload", this.beforeUnloadListener); - window.addEventListener("unload", this.unloadListener); - window.addEventListener("popstate", this.onPopStateHandler); - - this.model_.errorMessage.addOnChangedHandler(this.errorChangeHandler_); - this.model_.state.addOnChangedHandler(this.stateChangeHandler_); - this.model_.pedalboard.addOnChangedHandler(this.presetChangedHandler); - this.model_.alertMessage.addOnChangedHandler(this.alertMessageChangedHandler); - this.model_.banks.addOnChangedHandler(this.banksChangedHandler); - this.model_.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler); - this.model_.promptForUpdate.addOnChangedHandler(this.promptForUpdateHandler); - this.alertMessageChangedHandler(); - - } - - - updateOverscroll(): void { - if (this.model_.serverVersion) { - // no pull-down refresh on android devices once we're ready (unless we're debug) - let preventOverscroll = - this.model_.state.get() === State.Ready - && !this.model_.debug; - - let overscrollBehavior = preventOverscroll ? "none" : "auto"; - document.body.style.overscrollBehavior = overscrollBehavior; - } - } - - componentDidUpdate() { - } - - componentWillUnmount() { - super.componentWillUnmount(); - window.removeEventListener("beforeunload", this.beforeUnloadListener); - - window.removeEventListener("popstate", this.onPopStateHandler); - - - this.model_.promptForUpdate.removeOnChangedHandler(this.promptForUpdateHandler); - this.model_.errorMessage.removeOnChangedHandler(this.errorChangeHandler_); - this.model_.state.removeOnChangedHandler(this.stateChangeHandler_); - this.model_.pedalboard.removeOnChangedHandler(this.presetChangedHandler); - this.model_.banks.removeOnChangedHandler(this.banksChangedHandler); - this.model_.banks.removeOnChangedHandler(this.showStatusMonitorHandler); - - this.model_.close(); - - } - - alertMessageChangedHandler() { - let message = this.model_.alertMessage.get(); - if (message === "") { - this.setState({ alertDialogOpen: false }); - // leave the message intact so the dialog can fade. - } else { - this.setState({ - alertDialogOpen: true, - alertDialogMessage: message - }); - } - - } - updateResponsive() { - // functional, but disabled. - // let tinyToolBar_ = this.windowSize.height < 600; - // this.setState({ tinyToolBar: tinyToolBar_ }); - - let height = this.windowSize.height; - - const ENTRY_HEIGHT = 48; - // ENTRY_HEIGHT*6 +K = 727 from observation. - const K = 450; - - let bankEntries = Math.floor((height - K) / ENTRY_HEIGHT); - if (bankEntries < 1) bankEntries = 1; - if (bankEntries > 7) bankEntries = 7; - if (bankEntries === 2) bankEntries = 1; - - this.setState({ bankDisplayItems: bankEntries }); - - } - onWindowSizeChanged(width: number, height: number): void { - super.onWindowSizeChanged(width, height); - this.updateResponsive(); - } - - - setErrorMessage(message: string): void { - this.setState({ errorMessage: message }); - } - - - setDisplayState(newState: State): void { - this.updateOverscroll(); - - this.setState({ - displayState: newState, - canFullScreen: supportsFullScreen() && !this.model_.isAndroidHosted() - }); - if (newState === State.Ready) { - if (this.model_.isOnboarding()) { - this.handleDisplayOnboarding(); - } - } - } - - shortBankList(banks: BankIndex): BankIndexEntry[] { - let nDisplayEntries = this.state.bankDisplayItems; - let entries = banks.entries; - let nListEntries = entries.length; - - let result: BankIndexEntry[] = []; - - if (nListEntries <= nDisplayEntries) { - for (let i = 0; i < nListEntries; ++i) { - result.push(entries[i]); - } - } else { - // a subset of the list CENTERED on the currently selected entry. - let selectedIndex = 0; - for (let i = 0; i < nListEntries; ++i) { - if (entries[i].instanceId === banks.selectedBank) { - selectedIndex = i; - break; - } - } - - let minN = selectedIndex - Math.floor(nDisplayEntries / 2); - if (minN < 0) minN = 0; - let maxN = minN + nDisplayEntries; - if (maxN > entries.length) { - maxN = entries.length; - } - for (let i = minN; i < maxN; ++i) { - result.push(entries[i]); - } - } - return result; - } - handleReload() { - if (this.model_.isAndroidHosted()) { - this.model_.chooseNewDevice(); - } else { - window.location.reload(); - } - } - getReloadingMessage(): string { - switch (this.state.displayState) { - case State.ApplyingChanges: - return "Applying\u00A0changes..."; - case State.ReloadingPlugins: - return "Reloading\u00A0plugins..."; - case State.DownloadingUpdate: - return "Downloading update..."; - case State.InstallingUpdate: - return "Installing update...."; - case State.HotspotChanging: - return "Network connection changing..." - default: - return "Reconnecting..."; - } - } - render() { - - const { classes } = this.props; - - let shortBankList = this.shortBankList(this.state.banks); - let showBankSelectDialog = shortBankList.length !== this.state.banks.entries.length; - - - return ( -
{ - if (!this.model_.debug) { - e.preventDefault(); e.stopPropagation(); - } - }} - > - - {this.state.performanceView ? ( - { this.setState({ performanceView: false }); }} - /> - ) : ( -
- - - {(!this.state.tinyToolBar) && !this.state.performanceView ? - ( - - - { this.showDrawer() }} - size="large"> - - -
- -
-
- {this.state.canFullScreen && - { this.toggleFullScreen(); }} - color="inherit" - size="large"> - {this.state.isFullScreen ? ( - - ) : ( - - - )} - - - } - - - ) : ( -
- { this.showDrawer() }} - color="inherit" - size="large"> - - - {this.state.canFullScreen && ( - { this.toggleFullScreen(); }} - size="large"> - {this.state.isFullScreen ? ( - - ) : ( - - - )} - - - )} -
- )} - { this.hideDrawer(false); }} > - - - { - ev.stopPropagation(); - this.hideDrawer(true); - this.setState({ performanceView: true }); - }}> - - - - - - - - - Banks - - - { - shortBankList.map((bank) => { - return ( - { - ev.stopPropagation(); - this.hideDrawer(false); - this.handleSpecificBank(bank.instanceId); - }} - > - - - - - ); - }) - } - { - showBankSelectDialog && ( - { - ev.stopPropagation(); - this.hideDrawer(true); - this.handleDrawerSelectBank(); - }} - > - - - - - - ) - } - - - - { - ev.stopPropagation(); - this.hideDrawer(true); - this.handleDrawerRenameBank() - }}> - - - - - - { - ev.stopPropagation(); - this.hideDrawer(true); - this.handleDrawerSaveBankAs(); - }} > - - - - - - { - ev.stopPropagation(); - this.hideDrawer(true); - this.handleDrawerManageBanks(); - }}> - - - - - - - - - { - ev.stopPropagation(); - this.hideDrawer(true); - this.handleDrawerSettingsClick() - }}> - - - - - - { - ev.stopPropagation(); - this.hideDrawer(true); - this.handleDrawerAboutClick(); - }}> - - - - - - { - ev.stopPropagation(); - this.handleDrawerDonationClick(); - }}> - - - - - - - - - {!this.state.tinyToolBar && ( - - )} -
-
-
- {(this.state.displayState !== State.Loading) && - ( - - ) - } -
- -
-
- this.setState({ bankDialogOpen: false })} /> - {(this.state.aboutDialogOpen) && - ( - this.setState({ aboutDialogOpen: false })} /> - )} - this.handleSettingsDialogClose()} /> - { - this.setState({ - renameBankDialogOpen: false, - saveBankAsDialogOpen: false - }) - }} - onOk={(text: string) => { - if (this.state.renameBankDialogOpen) { - this.handleBankRenameOk(text); - } else if (this.state.saveBankAsDialogOpen) { - this.handleSaveBankAsOk(text); - } - } - } - /> - - { this.setState({ zoomedControlOpen: false }); }} - onDialogClosed={() => { this.model_.zoomedUiControl.set(undefined); } - } - /> - - {this.state.showStatusMonitor && ()} -
- ) - } - - - - - - - - { - this.state.alertDialogMessage - } - - - - - - - - - - -
-
-
 
-
-
- -
-
-

- Error: {this.state.errorMessage} -

-
-
- - -
-
-
 
-
- - - {/* Reloading mask */} - < Modal - open={wantsReloadingScreen(this.state.displayState) || this.state.displayState === State.Loading} - aria-label="loading" - aria-describedby="reloading-modal-description" - > -
-
-
-
- -
- - { - this.state.displayState === State.Loading ? - "Loading..." - :this.getReloadingMessage() - } - -
-
- -
- - ); - } -}); - -export default AppThemed; diff --git a/react/src/ChannelBindingsHelpDialog.tsx b/react/src/ChannelBindingsHelpDialog.tsx deleted file mode 100644 index 66e1087..0000000 --- a/react/src/ChannelBindingsHelpDialog.tsx +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2022 Robin Davies -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import React from 'react'; -import Button from '@mui/material/Button'; -import DialogEx from './DialogEx'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import Typography from '@mui/material/Typography'; -import ResizeResponsiveComponent from './ResizeResponsiveComponent'; -import Divider from '@mui/material/Divider'; -//import TextFieldEx from './TextFieldEx'; - - -export interface ChannelBindingHelpDialogProps { - open: boolean, - onClose: () => void, -}; - -export interface ChannelBindingHelpDialogState { - fullScreen: boolean; -}; - -export default class ChannelBindingHelpDialog extends ResizeResponsiveComponent { - - refText: React.RefObject; - - constructor(props: ChannelBindingHelpDialogProps) { - super(props); - this.state = { - fullScreen: false - }; - this.refText = React.createRef(); - } - mounted: boolean = false; - - - - onWindowSizeChanged(width: number, height: number): void - { - this.setState({fullScreen: height < 200}) - } - - - componentDidMount() - { - super.componentDidMount(); - this.mounted = true; - } - componentWillUnmount() - { - super.componentWillUnmount(); - this.mounted = false; - } - - componentDidUpdate() - { - } - render() { - let props = this.props; - let { open, onClose } = props; - - const handleClose = () => { - onClose(); - }; - - return ( - {}} - > - - - MIDI Control Binding Priority - -
-

MIDI Channel Filters determine which MIDI messages get sent to a plugin that accepts MIDI messages. Note that the - MIDI Channel Filters do NOT affect system MIDI bindings or control bindings. -

-

- MIDI message processing occurs in the following order -

-
    -
  • If the message is a Program Change message, the message is first offered to any MIDI plugins in the currently loaded preset (Message Filters permitting). - The message is sent to each MIDI plugin in the current preset that wants it. If any MIDI plugin accepts the program change, - no futher processing occurs. Specifically, the program change will not change the currently selected PiPedal preset. -
    -
  • -
  • - If the message is a Program Change message, and no MIDI plugin has accepted the message, PiPedal selects the PiPedal preset that corresponds - to the requested program. -
    -
  • -
  • - The message is then checked to see if it has been bound to a Pipedal feature using the System Midi Bindings settings. If it has, - Pipedal processes the message, and it is not forwarded to MIDI plugins. -
    -
  • -
  • - Otherwise, the message is sent to each MIDI plugin in the currently loaded Pipedal preset (channel filters permitting).) -
    -
  • -
-
- -
- - - - -
- ); - } -} \ No newline at end of file diff --git a/react/src/DialogEx.tsx b/react/src/DialogEx.tsx deleted file mode 100644 index 70c765a..0000000 --- a/react/src/DialogEx.tsx +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) 2022 Robin Davies -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import React from 'react'; - -import Dialog, {DialogProps} from '@mui/material/Dialog'; - - -interface DialogExProps extends DialogProps { - tag: string; - fullwidth?: boolean; - onEnterKey: () => void; -} - -interface DialogExState { -} - -export interface IDialogStackable { - getTag: ()=> string; - isOpen: ()=> boolean; - onDialogStackClose(): void; -} - -class DialogStackEntry { - constructor(dialog: IDialogStackable) - { - this.tag = dialog.getTag(); - this.dialog = dialog; - } - tag: string; - dialog: IDialogStackable; -}; - -let dialogStack: DialogStackEntry[] = []; - - -export function popDialogStack(): void { - if (dialogStack.length !== 0) - { - dialogStack.splice(dialogStack.length-1,1); - } -} -export function pushDialogStack(dialog: IDialogStackable): void { - dialogStack.push(new DialogStackEntry(dialog)); -} - -export interface DialogStackState{ - tag: String; - previousState: DialogStackState | null; -}; - - -// Close all dialogs higher in the dialog stack than "tag". -export function removeDialogStackEntriesAbove(tag: string) -{ - for (let i = dialogStack.length-1; i >= 0; --i) - { - let entry = dialogStack[i]; - if (entry.dialog.getTag() === tag) - { - return; - } - popDialogStack(); - if (entry.dialog.isOpen()) - { - entry.dialog.onDialogStackClose(); - } - - } -} -class DialogEx extends React.Component implements IDialogStackable { - constructor(props: DialogExProps) - { - super(props); - this.state = { - - }; - this.handlePopState = this.handlePopState.bind(this); - - } - - mounted: boolean = false; - - hasHooks: boolean = false; - stateWasPopped: boolean = false; - - getTag() { - return this.props.tag; - } - isOpen(): boolean { - return this.props.open; - } - onDialogStackClose() { - this.props.onClose?.({},"backdropClick"); - } - - handlePopState(ev: PopStateEvent): any - { - let evTag: DialogStackState | null = ev.state as DialogStackState | null; - if (evTag && evTag.tag === this.props.tag) { - removeDialogStackEntriesAbove(this.props.tag); - - if (!this.stateWasPopped) - { - this.stateWasPopped = true; - popDialogStack(); - if (this.props.open) { - this.props.onClose?.(ev,"backdropClick"); - } - } - window.history.replaceState(evTag.previousState,"",window.location.href); - window.removeEventListener("popstate",this.handlePopState); - - if (window.location.hash === "#menu") // The menu popstate is next? - { - window.history.back(); // then clear off the menu popstate too. - } - ev.stopPropagation(); - } - } - - updateHooks() : void { - let wantHooks = this.mounted && this.props.open; - if (wantHooks !== this.hasHooks) - { - this.hasHooks = wantHooks; - - if (this.hasHooks) - { - this.stateWasPopped = false; - window.addEventListener("popstate",this.handlePopState); - - pushDialogStack(this); - let dialogStackState: DialogStackState = {tag: this.props.tag,previousState: window.history.state as DialogStackState | null}; - window.history.replaceState(dialogStackState,"",window.location.href); - window.history.pushState( - null, - "", - "#" + this.props.tag - ); - } else { - if (!this.stateWasPopped) - { - window.history.back(); - } - } - } - } - - componentDidMount() - { - super.componentDidMount?.(); - - this.mounted = true; - this.updateHooks(); - } - componentWillUnmount() - { - super.componentWillUnmount?.(); - this.mounted = false; - this.updateHooks(); - } - - componentDidUpdate() - { - this.updateHooks(); - } - myOnClose(event:{}, reason: "backdropClick" | "escapeKeyDown") - { - if (this.props.onClose) this.props.onClose(event,reason); - } - - onEnterKey() { - if (this.props.onEnterKey) - { - this.props.onEnterKey(); - } - } - onKeyDown(evt: React.KeyboardEvent) - { - if (evt.key === 'Enter') - { - this.onEnterKey(); - } - evt.stopPropagation(); - } - render() { - let { tag,onClose,...extra} = this.props; - return ( - { this.myOnClose(event,reason);}} - onKeyDown={(evt)=>{ this.onKeyDown(evt); }} - > - {this.props.children} - - ); - } - -}; - -export default DialogEx; \ No newline at end of file diff --git a/react/src/FilePropertyControl.tsx b/react/src/FilePropertyControl.tsx deleted file mode 100644 index 8b0ff4b..0000000 --- a/react/src/FilePropertyControl.tsx +++ /dev/null @@ -1,265 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2023 Robin E. R. Davies - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import React, { Component, SyntheticEvent } from 'react'; -import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; -import { UiFileProperty } from './Lv2Plugin'; -import Typography from '@mui/material/Typography'; -import { PiPedalModel, PiPedalModelFactory, ListenHandle,State} from './PiPedalModel'; -import ButtonBase from '@mui/material/ButtonBase' -import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; -import {PedalboardItem} from './Pedalboard'; -import {isDarkMode} from './DarkMode'; -import Tooltip from "@mui/material/Tooltip" - -export const StandardItemSize = { width: 80, height: 140 } - - - -const styles = (theme: Theme) => createStyles({ - frame: { - position: "relative", - margin: "12px" - }, - switchTrack: { - height: '100%', - width: '100%', - borderRadius: 14 / 2, - zIndex: -1, - transition: theme.transitions.create(['opacity', 'background-color'], { - duration: theme.transitions.duration.shortest - }), - backgroundColor: theme.palette.secondary.main, - opacity: theme.palette.mode === 'light' ? 0.38 : 0.3 - }, - controlFrame: { - display: "flex", flexDirection: "column", alignItems: "start", justifyContent: "space-between", height: 116 - }, - displayValue: { - position: "absolute", - top: 0, - left: 0, - right: 0, - bottom: 4, - textAlign: "center", - background: theme.mainBackground, - color: theme.palette.text.secondary, - // zIndex: -1, - }, - titleSection: { - flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 8, marginRight: 0 - }, - midSection: { - flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center" - }, - editSection: { - flex: "0 0 0", position: "relative", width: 60, height: 28,minHeight: 28 - } - -}); - - -export interface FilePropertyControlProps extends WithStyles { - fileProperty: UiFileProperty; - pedalboardItem: PedalboardItem; - onFileClick: (fileProperty: UiFileProperty, value: string) => void; - theme: Theme; -} -type FilePropertyControlState = { - error: boolean; - hasValue: boolean; - value: string; -}; - -const FilePropertyControl = - withStyles(styles, { withTheme: true })( - class extends Component - { - - model: PiPedalModel; - - - constructor(props: FilePropertyControlProps) { - super(props); - - this.state = { - error: false, - hasValue: false, - value: "", - }; - this.model = PiPedalModelFactory.getInstance(); - this.onStateChanged = this.onStateChanged.bind(this); - } - - - onStateChanged(state: State) { - if (this.mounted) - { - if (state === State.Ready) { - this.subscribeToPatchProperty(); - } - } - } - private propertyGetHandle?: ListenHandle; - - monitorPropertyHandle?: ListenHandle; - - subscribeToPatchProperty() { - // this.propertyGetHandle = this.model.listenForAtomOutput(this.props.instanceId,(instanceId,atomOutput)=>{ - // // PARSE THE OBJECT! - // // this.setState({value: atomOutput?.value as string}); - // }); - this.unsubscribeToPatchProperty(); - this.monitorPropertyHandle = this.model.monitorPatchProperty(this.props.pedalboardItem.instanceId, - this.props.fileProperty.patchProperty, - (instanceId,property,propertyValue) => { - if (propertyValue.otype_ === "Path") - { - this.setState({value: propertyValue.value as string,hasValue: true}); - } - } - ); - - } - unsubscribeToPatchProperty() { - if (this.monitorPropertyHandle !== undefined) - { - this.model.cancelMonitorPatchProperty(this.monitorPropertyHandle); - this.monitorPropertyHandle = undefined; - } - - } - private mounted: boolean = false; - componentDidMount() { - this.model.state.addOnChangedHandler(this.onStateChanged); - this.mounted = true; - this.subscribeToPatchProperty(); - } - componentWillUnmount() { - this.unsubscribeToPatchProperty(); - this.mounted = false; - this.model.state.removeOnChangedHandler(this.onStateChanged); - } - componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { - if (prevProps.fileProperty.patchProperty !== this.props.fileProperty.patchProperty - || prevProps.pedalboardItem.instanceId !== this.props.pedalboardItem.instanceId - || prevProps.pedalboardItem.stateUpdateCount !== this.props.pedalboardItem.stateUpdateCount - - ) { - this.unsubscribeToPatchProperty(); - this.subscribeToPatchProperty(); - } - - } - - - inputChanged: boolean = false; - - onDrag(e: SyntheticEvent) { - e.preventDefault(); - } - - onFileClick() { - this.props.onFileClick(this.props.fileProperty, this.state.value); - } - private fileNameOnly(path: string): string { - let slashPos = path.lastIndexOf('/'); - if (slashPos < 0) { - slashPos = 0; - } else { - ++slashPos; - } - let extPos = path.lastIndexOf('.'); - if (extPos < 0 || extPos < slashPos) { - extPos = path.length; - } - - return path.substring(slashPos, extPos); - - } - render() { - //let classes = this.props.classes; - let fileProperty = this.props.fileProperty; - let classes = this.props.classes; - - let value = "\u00A0"; - if (this.state.hasValue) - { - if (this.state.value.length === 0) - { - value = ""; - } else { - value = this.fileNameOnly(this.state.value); - } - } - - let item_width = 264; - - return ( -
- {/* TITLE SECTION */} -
- - {fileProperty.label} - -
- {/* CONTROL SECTION */} - -
- - { this.onFileClick() }} > -
-
- {value} - -
-
 
-
-
-
- - {/* LABEL/EDIT SECTION*/} -
-
-
- ); - } - } - ); - -export default FilePropertyControl; \ No newline at end of file diff --git a/react/src/PedalboardView.tsx b/react/src/PedalboardView.tsx deleted file mode 100644 index 4099320..0000000 --- a/react/src/PedalboardView.tsx +++ /dev/null @@ -1,1287 +0,0 @@ -// Copyright (c) 2022 Robin Davies -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import React, { ReactNode, Component, SyntheticEvent } from 'react'; -import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; -import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; -import { PluginType } from './Lv2Plugin'; -import ButtonBase from '@mui/material/ButtonBase'; -import Typography from '@mui/material/Typography'; -import PluginIcon, { SelectIconUri } from './PluginIcon'; -import { SelectHoverBackground } from './SelectHoverBackground'; -import SvgPathBuilder from './SvgPathBuilder'; -import Draggable from './Draggable' -import Rect from './Rect'; -import { PiPedalStateError } from './PiPedalError'; -import Utility from './Utility'; -import { isDarkMode } from './DarkMode'; -import { - Pedalboard, PedalboardItem, PedalboardSplitItem, SplitType -} from './Pedalboard'; - -import { ReactComponent as MidiIcon } from "./svg/ic_midi.svg"; - - -const START_CONTROL = Pedalboard.START_CONTROL; -const END_CONTROL = Pedalboard.END_CONTROL; - - -const START_PEDALBOARD_ITEM_URI = Pedalboard.START_PEDALBOARD_ITEM_URI; -const END_PEDALBOARD_ITEM_URI = Pedalboard.END_PEDALBOARD_ITEM_URI; - -const ENABLED_CONNECTOR_COLOR = isDarkMode() ? "#CCC" : "#666"; -const DISABLED_CONNECTOR_COLOR = isDarkMode() ? "#666" : "#CCC"; - - - -const CELL_WIDTH: number = 96; -const CELL_HEIGHT: number = 64; -const FRAME_SIZE: number = 36; - -const STROKE_WIDTH = 3; -const STEREO_STROKE_WIDTH = 6; - - -const I_SVG_STROKE_WIDTH = 3; -const I_SVG_STEREO_STROKE_WIDTH = 6; - -const SVG_STROKE_WIDTH = I_SVG_STROKE_WIDTH.toString(); -const SVG_STEREO_STROKE_WIDTH = I_SVG_STEREO_STROKE_WIDTH.toString(); - - - -const EMPTY_ICON_URL = "img/fx_empty.svg"; -const ERROR_ICON_URL = "img/fx_error.svg"; -const TERMINAL_ICON_URL = "img/fx_terminal.svg"; - -function CalculateConnection(numberOfInputs: number, numberOfOutputs: number) { - if (numberOfInputs === 0) { - return numberOfOutputs; - } - if (numberOfOutputs === 0) { - return numberOfInputs; - } - let result = Math.min(numberOfInputs, numberOfOutputs); - if (result > 2) result = 2; - return result; -} - -const pedalboardStyles = (theme: Theme) => createStyles({ - scrollContainer: { - }, - - container: { - position: "relative", - overflow: "visible", - - }, - splitItem: { - position: "absolute", - display: "flex", - alignItems: "center", - justifyContent: "center", - - - width: CELL_WIDTH, - height: CELL_HEIGHT - - }, - splitStart: { - position: "absolute", - display: "flex", - width: CELL_WIDTH, - height: CELL_HEIGHT, - left: 0, - top: 0 - }, - splitEnd: { - position: "absolute", - display: "flex", - width: CELL_WIDTH, - height: CELL_HEIGHT, - right: 0, - top: 0 - }, - buttonDraggable: { - display: "flex", - alignItem: "center", - justifyContent: "center", - width: "100%", - height: "100%" - }, - pedalItem: { - position: "absolute", - width: CELL_WIDTH, - height: CELL_HEIGHT, - display: "flex", - alignItems: "center", - justifyContent: "center", - }, - midiConnectorDecoration: { - position: "absolute", - left: -20, - top: -6, - fill: theme.palette.text.secondary - }, - iconFrame: { - - display: "flex", - alignItems: "center", - justifyContent: "center", - position: "relative", - - background: theme.palette.background.default, - marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, - marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, - marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2, - marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2, - width: FRAME_SIZE, - height: FRAME_SIZE, - border: isDarkMode() ? "1pt #AAA solid" : "1pt #666 solid", - borderRadius: 6 - }, - borderlessIconFrame: { - - display: "flex", - alignItems: "center", - justifyContent: "center", - - background: "transparent", - marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, - marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, - marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2, - marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2, - width: FRAME_SIZE, - height: FRAME_SIZE, - border: "0pt #666 solid", - borderRadius: 6 - }, - - pedalIcon: { - width: 24, - height: 24, - opacity: 0.8 - }, - connector: { - position: "absolute", - width: CELL_WIDTH, - height: CELL_HEIGHT - }, - stroke: { - position: "absolute", - background: "#888" - }, - stereoStrokeOuter: { - position: "absolute", - background: "#888" - }, - stereoStrokeInner: { - position: "absolute", - background: "#888" - }, - -}); - -export type OnSelectHandler = (selectedPedal: number) => void; - -interface PedalboardProps extends WithStyles { - theme: Theme; - selectedId?: number; - onSelectionChanged?: OnSelectHandler; - onDoubleClick?: OnSelectHandler; - hasTinyToolBar: boolean; - enableStructureEditing: boolean; - -} -interface LayoutSize { - width: number; - height: number; -} - -type PedalboardState = { - pedalboard?: Pedalboard; -}; - -const EMPTY_PEDALS: PedalLayout[] = []; - - -function makeChain(model: PiPedalModel, uiItems?: PedalboardItem[]): PedalLayout[] { - let result: PedalLayout[] = []; - if (uiItems) { - for (let i = 0; i < uiItems.length; ++i) { - let item = uiItems[i]; - result.push(new PedalLayout(model, item)); - } - } - return result; -} - -class PedalLayout { - uri: string = ""; - name: string = ""; - pluginType: PluginType = PluginType.Plugin; - iconUrl: string = ""; - - bounds: Rect = new Rect(); - - numberOfInputs: number = 2; - numberOfOutputs: number = 2; - originalInputs: number = 2; - originalOutputs: number = 2; - - pedalItem?: PedalboardItem; - - // Split Layout only. - topChildren: PedalLayout[] = EMPTY_PEDALS; - bottomChildren: PedalLayout[] = EMPTY_PEDALS; - topConnectorY: number = 0; - bottomConnectorY: number = 0; - - - static Start(): PedalLayout { - let t: PedalLayout = new PedalLayout(); - t.uri = START_PEDALBOARD_ITEM_URI; - t.pluginType = PluginType.Terminal; - t.iconUrl = TERMINAL_ICON_URL; - t.numberOfInputs = 0; - t.numberOfOutputs = 2; - return t; - } - static End(): PedalLayout { - let t: PedalLayout = new PedalLayout(); - t.pluginType = PluginType.Terminal; - t.uri = END_PEDALBOARD_ITEM_URI; - t.iconUrl = TERMINAL_ICON_URL; - t.numberOfInputs = 2; - t.numberOfOutputs = 0; - return t; - } - constructor(model?: PiPedalModel, pedalItem?: PedalboardItem) { - if (model === undefined && pedalItem === undefined) { - return; - } - if (model === undefined || pedalItem === undefined) { - throw new Error("Invalid arguments."); - } - this.pedalItem = pedalItem; - this.uri = pedalItem.uri; - if (pedalItem.isSplit()) { - let splitter = pedalItem as PedalboardSplitItem; - - this.pluginType = PluginType.UtilityPlugin; - this.topChildren = makeChain(model, splitter.topChain); - this.bottomChildren = makeChain(model, splitter.bottomChain); - - this.numberOfInputs = 2; - this.numberOfOutputs = 2; - - } else if (pedalItem.isEmpty()) { - - this.pluginType = PluginType.None; - this.iconUrl = EMPTY_ICON_URL; - this.numberOfInputs = 2; - this.numberOfOutputs = 2; - - } else if (pedalItem.uri === START_PEDALBOARD_ITEM_URI) { - this.pluginType = PluginType.UtilityPlugin; - this.iconUrl = TERMINAL_ICON_URL; - this.numberOfInputs = 0; - this.numberOfOutputs = PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length; - if (this.numberOfOutputs === 0) { - this.numberOfOutputs = 1; - } - - } else if (pedalItem.uri === END_PEDALBOARD_ITEM_URI) { - this.pluginType = PluginType.UtilityPlugin; - this.iconUrl = TERMINAL_ICON_URL; - this.numberOfInputs = PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length; - if (this.numberOfInputs === 0) { - this.numberOfInputs = 1; - } - this.numberOfOutputs = 0; - } - else { - let uiPlugin = model.getUiPlugin(pedalItem.uri); - if (uiPlugin != null) { - this.pluginType = uiPlugin.plugin_type; - this.iconUrl = SelectIconUri(uiPlugin.plugin_type); - this.name = uiPlugin.label; - this.numberOfInputs = Math.min(uiPlugin.audio_inputs, 2); - this.numberOfOutputs = Math.min(uiPlugin.audio_outputs, 2); - } else { - // default to empty plugin. - this.pluginType = PluginType.ErrorPlugin; - this.name = pedalItem.pluginName ?? "#error"; - this.iconUrl = ERROR_ICON_URL; - this.numberOfInputs = 2; - this.numberOfOutputs = 2; - } - } - this.originalInputs = this.numberOfInputs; - this.originalOutputs = this.numberOfOutputs; - } - isEmpty(): boolean { - return (!this.pedalItem) || this.pedalItem.isEmpty(); - } - isSplitter(): boolean { - return this.pedalItem !== undefined && this.pedalItem.isSplit(); - } - isStart() { - return this.uri === START_PEDALBOARD_ITEM_URI; - } - isEnd() { - return this.uri === END_PEDALBOARD_ITEM_URI; - } -} - -function* chainIterator(layoutChain: PedalLayout[]): Generator { - for (let i = 0; i < layoutChain.length; ++i) { - let item = layoutChain[i]; - yield item; - if (item.isSplitter()) { - let g = chainIterator(item.topChildren); - while (true) { - let v = g.next(); - if (v.done) { - break; - } - yield v.value; - } - g = chainIterator(item.bottomChildren); - while (true) { - let v = g.next(); - if (v.done) { - break; - } - yield v.value; - } - } - } - return; -} - - - -class LayoutParams { - nextId: number = 1; - cx: number = 0; - cy: number = 0; -} - - -const PedalboardView = - withStyles(pedalboardStyles, { withTheme: true })( - class extends Component { - model: PiPedalModel; - - frameRef: React.RefObject; - scrollRef: React.RefObject; - private bgColor: string; - - constructor(props: PedalboardProps) { - super(props); - this.model = PiPedalModelFactory.getInstance(); - - this.bgColor = props.theme.palette.background.default - - if (!props.selectedId) props.selectedId = -1; - this.state = { - pedalboard: this.model.pedalboard.get(), - }; - this.onPedalboardChanged = this.onPedalboardChanged.bind(this); - this.frameRef = React.createRef(); - this.scrollRef = React.createRef(); - this.handleTouchStart = this.handleTouchStart.bind(this); - } - - handleTouchStart(e: any) { - // just has to exist to allow Draggable to receive - // touchyMove. :-/ - } - - onDragEnd(instanceId: number, clientX: number, clientY: number) { - if (!this.props.enableStructureEditing) { - return; - } - if (!this.currentLayout) return; - - if (!this.frameRef.current) return; - - let currentLayout: PedalLayout[] = this.currentLayout; - let frameElement = this.frameRef.current; - - let rc = frameElement.getBoundingClientRect(); - clientX -= rc.left; - clientY -= rc.top; - - let it = chainIterator(currentLayout); - - while (true) { - let v = it.next(); - if (v.done) break; - let item = v.value; - - if (item.isSplitter() && item.pedalItem) { - if (item.bounds.contains(clientX, clientY)) { - if (clientX < item.bounds.x + CELL_WIDTH / 2) { - this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); - this.setSelection(instanceId); - return; - } else if (clientX > item.bounds.right - CELL_WIDTH / 2) { - this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); - this.setSelection(instanceId); - return; - - } - } - let yMid = (item.bounds.y + item.bounds.bottom) / 2; - if (clientX >= item.bounds.x - && clientY < yMid && clientY >= item.topChildren[0].bounds.y - - ) { - if (clientX < item.topChildren[0].bounds.x) { - let topPedalItem = item.topChildren[0].pedalItem; - if (topPedalItem) { - this.model.movePedalboardItemBefore(instanceId, topPedalItem.instanceId); - this.setSelection(instanceId); - return; - } - } - let lastTop = item.topChildren[item.topChildren.length - 1]; - if (clientX >= lastTop.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { - if (lastTop.pedalItem) { - this.model.movePedalboardItemAfter(instanceId, lastTop.pedalItem.instanceId); - this.setSelection(instanceId); - return; - } - } - - } - if (clientX >= item.bounds.x - && clientY > yMid && clientY < item.bottomChildren[0].bounds.bottom - ) { - if (clientX < item.bottomChildren[0].bounds.x) { - let bottomPedalItem = item.bottomChildren[0].pedalItem; - if (bottomPedalItem) { - this.model.movePedalboardItemBefore(instanceId, bottomPedalItem.instanceId); - this.setSelection(instanceId); - return; - } - } - let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; - if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { - if (lastBottom.pedalItem) { - this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId); - this.setSelection(instanceId); - return; - } - - } - - } - - } else if (item.bounds.contains(clientX, clientY)) { - if (item.isStart()) { - this.model.movePedalboardItemToStart(instanceId); - this.setSelection(instanceId); - return; - } else if (item.isEnd()) { - this.model.movePedalboardItemToEnd(instanceId); - this.setSelection(instanceId); - return; - } else { - if (item.pedalItem) { - let margin = (CELL_WIDTH - FRAME_SIZE) / 2; - if (clientX < item.bounds.x + margin) { - this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); - } else if (clientX > item.bounds.right - margin) { - this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); - } else { - this.model.movePedalboardItem(instanceId, item.pedalItem.instanceId); - } - this.setSelection(instanceId); - return; - } - } - - } - } - // delete the plugin. - let newId = this.model.setPedalboardItemEmpty(instanceId); - this.setSelection(newId); - - } - - onPedalboardChanged(value?: Pedalboard) { - this.setState({ pedalboard: value }); - } - - componentDidMount() { - this.scrollRef.current!.addEventListener("touchstart", this.handleTouchStart, { passive: false }); - this.model.pedalboard.addOnChangedHandler(this.onPedalboardChanged); - - } - componentWillUnmount() { - this.scrollRef.current!.removeEventListener("touchstart", this.handleTouchStart); - this.model.pedalboard.removeOnChangedHandler(this.onPedalboardChanged); - } - - offsetLayout_(layoutItems: PedalLayout[], offset: number): void { - for (let i = 0; i < layoutItems.length; ++i) { - let layoutItem = layoutItems[i]; - layoutItem.bounds.y += offset; - if (layoutItem.isSplitter()) { - layoutItem.topConnectorY += offset; - layoutItem.bottomConnectorY += offset; - this.offsetLayout_(layoutItem.topChildren, offset); - this.offsetLayout_(layoutItem.bottomChildren, offset); - } - } - } - - getSplitterIcon(layoutItem: PedalLayout): PluginType { - if (layoutItem.pedalItem === undefined) { - throw new Error("Invalid splitter"); - } - let split = layoutItem.pedalItem as PedalboardSplitItem; - if (split.getSplitType() === SplitType.Ab) { - if (split.isASelected()) { - return PluginType.SplitA; - } else { - return PluginType.SplitB; - - } - } else if (split.getSplitType() === SplitType.Mix) { - return PluginType.SplitMix; //"img/fx_dial.svg"; - } else { - return PluginType.SplitLR; //"img/fx_lr.svg"; - } - } - - doLayout2_(lp: LayoutParams, layoutItems: PedalLayout[]): Rect { - let bounds = new Rect(); - for (let i = 0; i < layoutItems.length; ++i) { - let layoutItem = layoutItems[i]; - if (layoutItem.isSplitter()) { - let x0 = lp.cx; - let y0 = lp.cy; - - layoutItem.bounds.x = x0; - layoutItem.bounds.y = y0; - layoutItem.bounds.height = CELL_HEIGHT; - layoutItem.bounds.width = CELL_WIDTH; - - lp.cx += CELL_WIDTH; - - - let topBounds = this.doLayout2_(lp, layoutItem.topChildren); - if (topBounds.isEmpty()) { - topBounds.x = lp.cx; - topBounds.width = 0; - topBounds.y = y0 - CELL_HEIGHT / 2; - topBounds.height = CELL_HEIGHT; - - } - - - let dyTop = (lp.cy + CELL_HEIGHT / 2) - (topBounds.y + topBounds.height); - - - - this.offsetLayout_(layoutItem.topChildren, dyTop); - topBounds.offset(0, dyTop); - bounds.accumulate(topBounds); - - let topCx = lp.cx; - lp.cx = x0; - lp.cx += CELL_WIDTH; - - let bottomBounds = this.doLayout2_(lp, layoutItem.bottomChildren); - if (bottomBounds.isEmpty()) { - bottomBounds.x = lp.cx; bottomBounds.width = 0; - bottomBounds.y = lp.cy; bottomBounds.height = CELL_HEIGHT; - } - - let dyBottom = (lp.cy + CELL_HEIGHT / 2) - bottomBounds.y; - this.offsetLayout_(layoutItem.bottomChildren, dyBottom) - bottomBounds.offset(0, dyBottom); - bounds.accumulate(bottomBounds); - - lp.cx = Math.max(lp.cx, topCx) + CELL_WIDTH; - lp.cy = y0; - - layoutItem.bounds.width = lp.cx - layoutItem.bounds.x; - bounds.accumulate(layoutItem.bounds); - - if (layoutItem.topChildren.length === 0) { - layoutItem.topConnectorY = bounds.y + CELL_HEIGHT / 2; - } else { - layoutItem.topConnectorY = layoutItem.topChildren[0].bounds.y + CELL_HEIGHT / 2; - } - if (layoutItem.bottomChildren.length === 0) { - layoutItem.bottomConnectorY = bounds.y + bounds.height - CELL_HEIGHT / 2; - } else { - layoutItem.bottomConnectorY = layoutItem.bottomChildren[0].bounds.y + CELL_HEIGHT / 2; - } - } else { - layoutItem.bounds.x = lp.cx; - layoutItem.bounds.y = lp.cy; - lp.cx += CELL_WIDTH; - layoutItem.bounds.width = CELL_WIDTH; - layoutItem.bounds.height = CELL_HEIGHT; - bounds.accumulate(layoutItem.bounds); - - } - } - return bounds; - } - doLayout(layoutItems: PedalLayout[]): LayoutSize { - const TWO_ROW_HEIGHT = 142 - 14; - - if (layoutItems.length === 0) { - // if the current pedalboard is empty, reserve display space anyway. - return { width: 1, height: TWO_ROW_HEIGHT }; - } - - let lp = new LayoutParams(); - - let bounds = this.doLayout2_(lp, layoutItems); - // shift everything down so there are no negative y coordinates. - - if (bounds.height < TWO_ROW_HEIGHT) { - - let extra = Math.floor((TWO_ROW_HEIGHT - Math.ceil(bounds.height)) / 2); - this.offsetLayout_(layoutItems, Math.floor(-bounds.y + extra / 2)); - bounds.height += extra; - - } else { - this.offsetLayout_(layoutItems, -bounds.y); - } - - bounds.height += 14; // for labels that aren't accounted for. - return { width: bounds.width, height: bounds.height }; - - } - - onItemClick(e: SyntheticEvent, instanceId?: number): void { - if (instanceId) { - this.setSelection(instanceId); - } - } - setSelection(instanceId: number) { - if (this.props.onSelectionChanged) { - this.props.onSelectionChanged(instanceId); - } - } - - onItemDoubleClick(event: SyntheticEvent, instanceId?: number): void { - event.preventDefault(); - event.stopPropagation(); - - if (this.props.onDoubleClick && instanceId && this.props.enableStructureEditing) { - this.props.onDoubleClick(instanceId); - } - - } - - onItemLongClick(event: SyntheticEvent, instanceId?: number): void { - if (!instanceId) { - return; - } - event.preventDefault(); - event.stopPropagation(); - - if (!this.props.enableStructureEditing) { - this.setSelection(instanceId); - return; - } - if (!Utility.needsZoomedControls()) { - if (this.props.onDoubleClick && this.props.enableStructureEditing && instanceId) { - this.props.onDoubleClick(instanceId); - } - } - - } - - strokeConnector(output: ReactNode[],channels: number,enabled: Boolean,svgPath: string) - { - let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; - - if (channels === 2) { - output.push(( - - )); - output.push(( - - )); - } else if (channels === 1) { - output.push(( - - )); - } - } - - renderConnector(output: ReactNode[], item: PedalLayout, enabled: boolean): void { - // let classes = this.props.classes; - let x_ = item.bounds.x + CELL_WIDTH / 2; - let y_ = item.bounds.y + CELL_HEIGHT / 2; - let numberOfOutputs = item.numberOfOutputs; - let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; - let stereoCenterColor = this.bgColor; - - if (item.originalInputs === 0) { - // break the input paths. - let rx = item.bounds.x + CELL_WIDTH / 2 - FRAME_SIZE / 2 - 4; - let ry = y_ - 4; - - output.push(( - - )); - } - let svgPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_ + CELL_WIDTH, y_).toString(); - - - if (numberOfOutputs === 2) { - output.push(( - - )); - output.push(( - - )); - } else if (numberOfOutputs === 1) { - output.push(( - - )); - } else { - output.push(( - - )); - - } - } - renderSplitConnectors(output: ReactNode[], item: PedalLayout, enabled: boolean, shortSplitOutput: boolean,): void { - //let classes = this.props.classes; - let x_ = item.bounds.x + CELL_WIDTH / 2; - let y_ = item.bounds.y + CELL_HEIGHT / 2; - let yTop = item.topConnectorY; - let yBottom = item.bottomConnectorY; - //let isStereo = item.stereoOutput; - let split = item.pedalItem as PedalboardSplitItem; - - let topEnabled = enabled && split.isASelected(); - let bottomEnabled = enabled && split.isBSelected(); - let topColor = topEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; - let bottomColor = bottomEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; - - - let topStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yTop).lineTo(x_ + CELL_WIDTH, yTop).toString(); - let bottomStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yBottom).lineTo(x_ + CELL_WIDTH, yBottom).toString(); - - if (item.numberOfInputs === 2 && item.topChildren[0].numberOfInputs === 2) { - output.push(()); - output.push(()); - } else if (item.numberOfInputs !== 0 && item.topChildren[0].numberOfInputs !== 0) { - output.push(()); - } - - if (item.numberOfInputs === 2 && item.bottomChildren[0].numberOfInputs === 2) { - output.push(()); - output.push(()); - - } else if (item.numberOfInputs !== 0 && item.bottomChildren[0].numberOfInputs !== 0) { - output.push(()); - } - - let lastTop = item.topChildren[item.topChildren.length - 1]; - let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; - - let xTop = lastTop.bounds.right - CELL_WIDTH / 2; - let xBottom = lastBottom.bounds.right - CELL_WIDTH / 2; - - let xEnd = shortSplitOutput ? item.bounds.right : item.bounds.right + CELL_WIDTH / 2; - let xTee0 = item.bounds.right - CELL_WIDTH / 2; - - let firstPath: string; // top or bottom depending on draw order. - let secondPath: string; // top or bottom depending on draw order. - - let firstPathStereo: boolean; - let secondPathStereo: boolean; - let firstPathAbsent: boolean; - let secondPathAbsent: boolean; - let firstPathEnabled: boolean; - let secondPathEnabled: boolean; - let xTee: number; - - let monoAdjustment = (STEREO_STROKE_WIDTH - STROKE_WIDTH) / 2; - - let bottomPathFirst = topEnabled && !bottomEnabled; - let topPathFirst = bottomEnabled && !topEnabled; - - - // Third case: L/R stereo output, when both outputs are mono, requires a third stroke. - let thirdPath: string | null = null; // for L/R stereo output (which can be stereo even if both outputs are mono) - let hasThirdPath = item.numberOfOutputs === 2 && (lastTop.numberOfOutputs !== 2) && (lastBottom.numberOfOutputs !== 2); - - if (hasThirdPath) { - firstPathStereo = false; - secondPathStereo = false; - firstPathAbsent = lastTop.numberOfOutputs === 0 || item.numberOfOutputs === 0; - secondPathAbsent = lastBottom.numberOfOutputs === 0 || item.numberOfOutputs === 0; - xTee = xTee0 - monoAdjustment; - firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString(); - - secondPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString(); - - hasThirdPath = true; - thirdPath = new SvgPathBuilder().moveTo(xTee0, y_).lineTo(xEnd, y_).toString(); - firstPathEnabled = bottomEnabled; - secondPathEnabled = topEnabled; - } else if (bottomPathFirst || (topEnabled && lastTop.numberOfOutputs === 2)) { - // draw the bottom path first. - firstPathStereo = item.numberOfOutputs === 2 && lastBottom.numberOfOutputs === 2; - secondPathStereo = item.numberOfOutputs === 2 && lastTop.numberOfOutputs === 2; - firstPathAbsent = item.numberOfOutputs === 0 || lastBottom.numberOfOutputs === 0; - secondPathAbsent = item.numberOfOutputs === 0 || lastTop.numberOfOutputs === 0; - - xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment; - firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString(); - xTee = secondPathStereo ? xTee0 : xTee0 - monoAdjustment; - - secondPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).lineTo(xEnd, y_).toString(); - firstPathEnabled = bottomEnabled; - secondPathEnabled = topEnabled; - } else { - // draw the top path first. - firstPathStereo = item.numberOfOutputs === 2 && lastTop.numberOfOutputs === 2; - secondPathStereo = item.numberOfOutputs === 2 && lastBottom.numberOfOutputs === 2; - firstPathAbsent = item.numberOfOutputs === 0 || lastTop.numberOfOutputs === 0; - secondPathAbsent = item.numberOfOutputs === 0 || lastBottom.numberOfOutputs === 0; - - xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment; - firstPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString(); - - xTee = secondPathStereo ? xTee0 : xTee0 - monoAdjustment; - secondPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).lineTo(xEnd, y_).toString(); - - firstPathEnabled = topEnabled; - secondPathEnabled = bottomEnabled; - } - let firstPathColor = firstPathEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; - let secondPathColor = secondPathEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; - - if (bottomPathFirst || topPathFirst) { - // display stereo strokes with cutoff line. - if (firstPathStereo) { - output.push(( - - )); - output.push(( - - )); - } else if (!firstPathAbsent) { - output.push(( - - )); - } - if (secondPathStereo) { - output.push(( - - )); - output.push(( - - )); - } else if (!secondPathAbsent) { - output.push(( - - )); - } - - } else { - // stereo strokes merge. - if (firstPathStereo) { - output.push(( - - )); - } else if (!firstPathAbsent) { - output.push(( - - )); - } - if (secondPathStereo) { - output.push(( - - )); - } else if (!secondPathAbsent) { - output.push(( - - )); - } - - // draw stereo inner lines. - if (firstPathStereo) { - output.push(( - - )); - } - if (secondPathStereo) { - output.push(( - - )); - - } - } - if (thirdPath != null) { - // stereo output of L/R splitter - output.push(( - - )); - output.push(( - - )); - - - } - } - getScrollContainer() { - let el: HTMLElement | undefined | null = this.scrollRef.current; - // actually not here anymore. :-/ It has a reactive definition in MainPage.tsx now. - while (el) { - if (el.id === "pedalboardScroll") { - return el as HTMLDivElement; - } - el = el.parentElement; - } - throw new PiPedalStateError("scroll container not found."); - } - - pedalButton( - instanceId: number, - iconType: PluginType, - draggable: boolean, - enabled: boolean, - hasBorder: boolean = true, - pluginNotFound: boolean, - hasMidiConnector: boolean - ) - : ReactNode { - let classes = this.props.classes; - return ( -
{ e.preventDefault(); }}> - {hasMidiConnector && ( -
- -
- - )} - - { this.onItemClick(e, instanceId); }} - onDoubleClick={(e: SyntheticEvent) => { this.onItemDoubleClick(e, instanceId); }} - onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }} - > - - this.getScrollContainer()} - onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} - > - - - - -
- ); - - } - renderConnectors(output: ReactNode[], layoutChain: PedalLayout[], enabled: boolean, shortSplitOutput: boolean): void { - let length = layoutChain.length - 1; - if (layoutChain.length > 0 && layoutChain[layoutChain.length - 1].isSplitter()) { - ++length; - } - for (let i = 0; i < length; ++i) { - let item = layoutChain[i]; - if (item.isSplitter()) { - let splitter = item.pedalItem as PedalboardSplitItem; - this.renderSplitConnectors(output, item, enabled, i === length - 1 && shortSplitOutput,); - this.renderConnectors(output, item.topChildren, enabled && splitter.isASelected(), false); - this.renderConnectors(output, item.bottomChildren, enabled && splitter.isBSelected(), false); - - } else if (item.uri !== END_PEDALBOARD_ITEM_URI) { - this.renderConnector(output, item, enabled); - - } - } - } - renderConnectorFrame(layoutChain: PedalLayout[], layoutSize: LayoutSize): ReactNode { - let outputs: ReactNode[] = []; - this.renderConnectors(outputs, layoutChain, true, false); - return ( -
- - - { - outputs - } - - - -
- ); - - } - - renderChain(layoutChain: PedalLayout[], layoutSize: LayoutSize): ReactNode { - - let classes = this.props.classes; - - let result: ReactNode[] = []; - - result.push(this.renderConnectorFrame(layoutChain, layoutSize)); - - let it = chainIterator(layoutChain); - while (true) { - let v = it.next(); - if (v.done) break; - let item = v.value; - switch (item.uri) { - case START_PEDALBOARD_ITEM_URI: - result.push(
-
- - {this.pedalButton(START_CONTROL, item.pluginType, false, true, false, false, false)} -
-
); - break; - case END_PEDALBOARD_ITEM_URI: - result.push(
-
- - {this.pedalButton(END_CONTROL, item.pluginType, false, true, false, false, false)} -
-
); - break; - default: - if (item.isSplitter()) { - - result.push(
-
- {this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), false, true, true, false, false)} -
-
); - - } else { - result.push( -
- {item.name} -
- ) - let uiPlugin = this.model.getUiPlugin(item.pedalItem?.uri ?? ""); - let pluginMissing = uiPlugin == null; - - result.push(
- {this.pedalButton( - item.pedalItem?.instanceId ?? -1, - item.pluginType, - !item.isEmpty(), - item.pedalItem?.isEnabled ?? false, - true, - pluginMissing, - uiPlugin ? ((uiPlugin.has_midi_input !== 0) || (uiPlugin.has_midi_output !== 0)) : false)} - -
); - - } - break; - } - - } - return result; - } - - canInputStero(item: PedalLayout): boolean { - if (item.pedalItem) { - let plugin = this.model.getUiPlugin(item.pedalItem.uri); - if (plugin) { - return plugin.audio_inputs === 2; - } - } - return true; - } - getNumberOfInputs(item: PedalLayout): number { - if (item.pedalItem) { - let plugin = this.model.getUiPlugin(item.pedalItem.uri); - if (plugin) { - return plugin.audio_inputs; - } - } - return 1; - } - getNumberOfOutputs(item: PedalLayout): number { - if (item.pedalItem) { - let plugin = this.model.getUiPlugin(item.pedalItem.uri); - if (plugin) { - return plugin.audio_outputs; - } - } - return 1; - } - - markStereoOutputs(layoutChain: PedalLayout[], numberOfInputs: number, numberOfOutputs: number) { - // analyze forward flow. - this.markStereoForward(layoutChain, numberOfInputs); - // mark items that feed a mono effect as mono. - this.markStereoBackward(layoutChain, numberOfOutputs); - } - - markStereoBackward(layoutChain: PedalLayout[], numberOfOutputs: number): number { - for (let i = layoutChain.length - 1; i >= 0; --i) { - let item = layoutChain[i]; - if (item.isSplitter()) { - item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs) - - this.markStereoBackward(item.topChildren, numberOfOutputs); - this.markStereoBackward(item.bottomChildren, numberOfOutputs); - let topInputs = item.topChildren[0].numberOfInputs; - let bottomInputs = item.bottomChildren[0].numberOfInputs; - - let splitItem = item.pedalItem as PedalboardSplitItem; - if (splitItem.getSplitType() !== SplitType.Lr) { - item.numberOfInputs = CalculateConnection(item.numberOfInputs, Math.max(topInputs, bottomInputs)); - } - } else if (item.isEnd()) { - - } else if (item.isStart()) { - item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); - return item.numberOfOutputs; - - } else if (item.isEmpty()) { - if (numberOfOutputs === 0) { - item.numberOfOutputs = 0; - item.numberOfInputs = CalculateConnection(item.numberOfInputs, 2); - } else { - item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); - item.numberOfInputs = CalculateConnection(item.numberOfInputs, numberOfOutputs); - } - } else { - item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); - } - numberOfOutputs = item.numberOfInputs; - } - return numberOfOutputs; - } - markStereoForward(layoutChain: PedalLayout[], numberOfInputs: number): number { - if (layoutChain.length === 0) { - return numberOfInputs; - } - for (let i = 0; i < layoutChain.length; ++i) { - let item = layoutChain[i]; - if (item.isSplitter()) { - let splitter = item.pedalItem as PedalboardSplitItem; - item.numberOfInputs = numberOfInputs; - - let chainInputs = numberOfInputs; - if (splitter.getSplitType() === SplitType.Lr) { - chainInputs = CalculateConnection(numberOfInputs, 1); - } - let topOutputs = this.markStereoForward(item.topChildren, chainInputs); - let bottomOutputs = this.markStereoForward(item.bottomChildren, chainInputs); - - - if (splitter.getSplitType() === SplitType.Ab) { - if (splitter.isASelected()) { - item.numberOfOutputs = topOutputs; - } else { - item.numberOfOutputs = bottomOutputs; - } - } else { - item.numberOfOutputs = (topOutputs >= 1 || bottomOutputs >= 1) ? 2 : 1; - } - } else if (item.isStart()) { - item.numberOfOutputs = Math.min(PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length, 2); - } else if (item.isEnd()) { - item.numberOfInputs = - CalculateConnection( - Math.min(PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length, 2), - numberOfInputs); - return item.numberOfInputs; - } else if (item.isEmpty()) { - item.numberOfInputs = numberOfInputs; - if (numberOfInputs === 0) { - item.numberOfOutputs = 2; - } else { - item.numberOfOutputs = item.numberOfInputs; - } - } else { - if (item.numberOfInputs === 0) // zero-input plugins merge their output with the input. - { - item.numberOfInputs = numberOfInputs; - item.numberOfOutputs = Math.max(item.numberOfOutputs, numberOfInputs); - } else { - item.numberOfInputs = CalculateConnection(numberOfInputs, this.getNumberOfInputs(item)); - item.numberOfOutputs = this.getNumberOfOutputs(item); - } - } - numberOfInputs = item.numberOfOutputs; - } - return numberOfInputs; - } - - currentLayout?: PedalLayout[]; - private renderKey: number = 0; - render() { - const { classes } = this.props; - this.renderKey = 0; - let layoutChain = makeChain(this.model, this.state.pedalboard?.items); - let start = PedalLayout.Start(); - let end = PedalLayout.End(); - if (layoutChain.length !== 0) { - layoutChain.splice(0, 0, start); - layoutChain.splice(layoutChain.length, 0, end); - this.markStereoOutputs(layoutChain, 2, 2); - } - - let layoutSize = this.doLayout(layoutChain); - - - this.currentLayout = layoutChain; // save for mouse processing &c. - - return ( -
-
- {this.renderChain(layoutChain, layoutSize)} -
-
- ); - } - - } - ); - -export default PedalboardView \ No newline at end of file diff --git a/react/src/SnapshotEditor.tsx b/react/src/SnapshotEditor.tsx deleted file mode 100644 index d078ff7..0000000 --- a/react/src/SnapshotEditor.tsx +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (c) 2024 Robin Davies -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import { WithStyles } from '@mui/styles'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import CloseIcon from '@mui/icons-material/Close'; -import AppBar from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; -import CssBaseline from '@mui/material/CssBaseline'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; -import IconButton from '@mui/material/IconButton'; -import FullscreenIcon from '@mui/icons-material/Fullscreen'; -import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'; -import ResizeResponsiveComponent from './ResizeResponsiveComponent'; -import { PiPedalModelFactory, PiPedalModel, State, ZoomedControlInfo } from './PiPedalModel'; -import ZoomedUiControl from './ZoomedUiControl' -import MainPage from './MainPage'; -import JackStatusView from './JackStatusView'; -import { Theme } from '@mui/material/styles'; -import { isDarkMode } from './DarkMode'; -import Typography from '@mui/material/Typography'; -import TextField from '@mui/material/TextField'; -import { Snapshot } from './Pedalboard'; -import ColorDropdownButton, { DropdownAlignment } from './ColorDropdownButton'; -import { getBackgroundColor } from './MaterialColors'; - - -const selectColor = isDarkMode() ? "#888" : "#FFFFFF"; - - -const appStyles = (theme: Theme) => createStyles({ - "&": { // :root - colorScheme: (isDarkMode() ? "dark" : "light") - }, - - - mainFrame: { - overflow: "hidden", - display: "flex", - flexFlow: "column", - marginTop: 8, - flex: "1 1 100%" - }, - heroContent: { - backgroundColor: theme.mainBackground, - position: "relative", - height: "100%", - width: "100%" - }, - shadowCatcher: { - backgroundColor: theme.mainBackground - }, - - select: { // fu fu fu.Overrides for white selector on dark background. - '&:before': { - borderColor: selectColor, - }, - '&:after': { - borderColor: selectColor, - }, - '&:hover:not(.Mui-disabled):before': { - borderColor: selectColor, - } - }, - select_icon: { - fill: selectColor, - } - - -}); - - -function supportsFullScreen(): boolean { - let doc: any = window.document; - let docEl: any = doc.documentElement; - var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; - return (!!requestFullScreen); - -} - -function setFullScreen(value: boolean) { - let doc: any = window.document; - let docEl: any = doc.documentElement; - - var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; - var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; - - if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { - requestFullScreen.call(docEl); - } - else { - cancelFullScreen.call(doc); - } -} - -type SnapshotEditorState = { - zoomedControlInfo: ZoomedControlInfo | undefined; - zoomedControlOpen: boolean; - - canFullScreen: boolean; - isFullScreen: boolean; - isDebug: boolean; - collapseLabel: boolean; - - showStatusMonitor: boolean; - name: string, - color: string - -}; -interface SnapshotEditorProps extends WithStyles { - onClose: () => void; - onOk: (snapshotIndex: number, name: string, color: string, newSnapshots: (Snapshot | null)[]) => void; - snapshotIndex: number; -} - -const SnapshotEditor = withStyles(appStyles)(class extends ResizeResponsiveComponent { - // Before the component mounts, we initialise our state - - model_: PiPedalModel; - - getCollapseLabel() { - return this.windowSize.width < 500; - } - - constructor(props: SnapshotEditorProps) { - super(props); - this.model_ = PiPedalModelFactory.getInstance(); - - this.model_.zoomedUiControl.addOnChangedHandler( - () => { - this.setState({ - zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined, - zoomedControlInfo: this.model_.zoomedUiControl.get() - }); - } - ); - - let snapshot = this.model_.pedalboard.get().snapshots[this.props.snapshotIndex] ?? new Snapshot(); - this.state = { - zoomedControlOpen: false, - zoomedControlInfo: this.model_.zoomedUiControl.get(), - canFullScreen: supportsFullScreen() && !this.model_.isAndroidHosted(), - isFullScreen: !!document.fullscreenElement, - showStatusMonitor: this.model_.showStatusMonitor.get(), - isDebug: true, - name: snapshot.name, - color: snapshot.color, - collapseLabel: this.getCollapseLabel() - - }; - - this.showStatusMonitorHandler = this.showStatusMonitorHandler.bind(this); - } - - showStatusMonitorHandler() { - this.setState({ - showStatusMonitor: this.model_.showStatusMonitor.get() - }); - } - toggleFullScreen(): void { - setFullScreen(this.state.isFullScreen); - this.setState({ isFullScreen: !this.state.isFullScreen }); - } - - componentDidMount() { - - super.componentDidMount(); - this.model_.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler); - - } - - - updateOverscroll(): void { - if (this.model_.serverVersion) { - // no pull-down refresh on android devices once we're ready (unless we're debug) - let preventOverscroll = - this.model_.state.get() === State.Ready - && !this.model_.debug; - - let overscrollBehavior = preventOverscroll ? "none" : "auto"; - document.body.style.overscrollBehavior = overscrollBehavior; - } - } - - componentDidUpdate() { - } - - componentWillUnmount() { - super.componentWillUnmount(); - this.model_.banks.removeOnChangedHandler(this.showStatusMonitorHandler); - } - - onWindowSizeChanged(width: number, height: number): void { - super.onWindowSizeChanged(width, height); - this.setState({ - collapseLabel: this.getCollapseLabel() - }); - - } - - handleOk() { - let currentPedalboard = this.model_.pedalboard.get(); - let selectedSnapshot = this.props.snapshotIndex; - let currentSnapshot: Snapshot | null = currentPedalboard.snapshots[selectedSnapshot]; - if (!currentSnapshot) - { - this.props.onClose(); - return; - } - - let changed = this.state.name !== currentSnapshot.name - || this.state.color !== currentSnapshot.color - || currentSnapshot.isModified; - if (!changed) - { - this.props.onClose(); - return; - } - let newSnapshots = Snapshot.cloneSnapshots(currentPedalboard.snapshots); - let newSnapshot = this.model_.pedalboard.get().makeSnapshot(); - newSnapshot.name = this.state.name; - newSnapshot.color = this.state.color; - newSnapshots[selectedSnapshot] = newSnapshot; - this.model_.setSnapshots(newSnapshots, selectedSnapshot); - - this.props.onOk(selectedSnapshot, this.state.name, this.state.color, newSnapshots); - - - } - - render() { - - const { classes } = this.props; - - return ( - -
{ - if (!this.model_.debug) { - e.preventDefault(); e.stopPropagation(); - } - }} - > - - - - { this.handleOk(); }} - size="large"> - - - {!this.state.collapseLabel && ( - - {'Snapshot ' + (this.props.snapshotIndex + 1)} - - )} - -
- { - this.setState({ name: ev.target.value }); - }} - /> -
- { - this.setState({ color: newColor }); - }} /> - {this.state.canFullScreen && - { this.toggleFullScreen(); }} - color="inherit" - size="medium"> - {this.state.isFullScreen ? ( - - ) : ( - - - )} - - - } - { this.props.onClose(); }} - color="inherit" - size="medium"> - - - -
-
-
-
-
- -
- -
-
- - { this.setState({ zoomedControlOpen: false }); }} - onDialogClosed={() => { this.model_.zoomedUiControl.set(undefined); } - } - /> - { this.state.showStatusMonitor && ()} -
- ); - } -} -); - -export default SnapshotEditor; diff --git a/react/src/XxxSnippet.tsx b/react/src/XxxSnippet.tsx deleted file mode 100644 index 1e6b4ea..0000000 --- a/react/src/XxxSnippet.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2022 Robin Davies -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import React from 'react'; -import { Theme } from '@mui/material/styles'; - - -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; - - -const styles = (theme: Theme) => createStyles({ -}); - - -interface XxxProps extends WithStyles { - theme: Theme, -} - -interface XxxState { - -} - -const Xxx = withStyles(styles, { withTheme: true })( - - class extends React.Component { - constructor(props: XxxProps) - { - super(props); - this.state = { - - }; - } - - render() { - return ( -
- ); - } - } -); - -export default Xxx; diff --git a/react/src/index.css b/react/src/index.css deleted file mode 100644 index ec2585e..0000000 --- a/react/src/index.css +++ /dev/null @@ -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; -} diff --git a/react/src/index.tsx b/react/src/index.tsx deleted file mode 100644 index ef2edf8..0000000 --- a/react/src/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -ReactDOM.render( - - - , - document.getElementById('root') -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/react/src/reportWebVitals.ts b/react/src/reportWebVitals.ts deleted file mode 100644 index 49a2a16..0000000 --- a/react/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/react/src/setupTests.ts b/react/src/setupTests.ts deleted file mode 100644 index 8f2609b..0000000 --- a/react/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/react/start b/react/start deleted file mode 100755 index 0186425..0000000 --- a/react/start +++ /dev/null @@ -1 +0,0 @@ -npm start diff --git a/react/tmp.txt b/react/tmp.txt deleted file mode 100644 index 3a7e78a..0000000 --- a/react/tmp.txt +++ /dev/null @@ -1,178 +0,0 @@ -src/PiPedalError.tsx -src/OldDeleteIcon.tsx -src/MaterialColors.tsx -src/Pedalboard.tsx -src/SystemMidiBindingView.tsx -src/GovernorSettings.tsx -src/ObservableProperty.tsx -src/SnapshotEditor.tsx -src/SelectMidiChannelsDialog.tsx -src/AlsaMidiDeviceInfo.tsx -src/WifiDirectConfigSettings.tsx -src/WifiConfigSettings.tsx -src/SnapshotButton.tsx -src/WindowScale.tsx -src/JsonAtom.tsx -src/PiPedalSocket.tsx -src/TemporaryDrawer.tsx -src/DarkMode.tsx -src/MidiBindingsDialog.tsx -src/MidiBinding.tsx -src/SnapshotDialog.tsx -src/SettingsDialog.tsx -src/PedalboardView.tsx -src/reportWebVitals.ts -src/OptionsDialog.tsx -src/SelectChannelsDialog.tsx -src/PluginPresetSelector.tsx -src/VuMeter.tsx -src/ToobFrequencyResponseView.tsx -src/SystemMidiBindingsDialog.tsx -src/FullScreenIME.tsx -src/App.tsx -src/ToobInputStageView.tsx -src/XxxSnippet.tsx -src/JackServerSettings.tsx -src/ToobWaveShapeView.tsx -src/ToobPowerStage2View.tsx -src/PluginPreset.tsx -src/logo.svg -src/Banks.tsx -src/NoChangePassword.tsx -src/SearchFilter.tsx -src/FilePropertyDirectorySelectDialog.tsx -src/GxTunerView.tsx -src/UpdateDialog.tsx -src/svg/file_download_black_24dp.svg -src/svg/fx_filter.svg -src/svg/fx_pitch.svg -src/svg/fx_distortion.svg -src/svg/fx_split_b.svg -src/svg/fx_chorus.svg -src/svg/fx_limiter.svg -src/svg/fx_phaser.svg -src/svg/snapshot_0.svg -src/svg/snapshot_3.svg -src/svg/file_upload_black_24dp.svg -src/svg/ic_bank.svg -src/svg/fx_function.svg -src/svg/fx_dial.svg -src/svg/ic_settings.svg -src/svg/fx_split_a.svg -src/svg/fx_empty.svg -src/svg/fx_amplifier.svg -src/svg/fx_spectral.svg -src/svg/fx_eq.svg -src/svg/ic_edit_banks.svg -src/svg/ic_help_outline.svg -src/svg/ic_save_bank_as.svg -src/svg/ic_pluginpreset2.svg -src/svg/fx_flanger2.svg -src/svg/snapshot_6.svg -src/svg/fx_lr.svg -src/svg/fx_generator.svg -src/svg/snapshot_1.svg -src/svg/fx_reverb.svg -src/svg/fx_plugin.svg -src/svg/snapshot_4.svg -src/svg/drive_file_rename_outline_black_24dp.svg -src/svg/snapshot_2.svg -src/svg/fx_instrument.svg -src/svg/fx_modulator.svg -src/svg/fx_error.svg -src/svg/fx_simulator.svg -src/svg/fx_filter_hp.svg -src/svg/fx_compressor.svg -src/svg/fx_gate.svg -src/svg/fx_terminal.svg -src/svg/ic_midi.svg -src/svg/fx_oscillator.svg -src/svg/fx_analyzer.svg -src/svg/fx_flanger.svg -src/svg/old_delete_outline_24dp.svg -src/svg/ic_presets.svg -src/svg/fx_delay.svg -src/svg/fx_parametric_eq.svg -src/svg/fx_spatial.svg -src/svg/fx_converter.svg -src/svg/fx_constant.svg -src/svg/snapshot_5.svg -src/svg/ic_pluginpreset.svg -src/svg/fx_mixer.svg -src/svg/fx_utility.svg -src/Updater.tsx -src/UploadPresetDialog.tsx -src/RadioSelectDialog.tsx -src/setupTests.ts -src/ToobCabSimView.tsx -src/RenameDialog.tsx -src/SplitControlView.tsx -src/SelectHoverBackground.tsx -src/IControlViewFactory.tsx -src/OkCancelDialog.tsx -src/BankDialog.tsx -src/PluginPresetsDialog.tsx -src/App.test.tsx -src/ControlViewFactory.tsx -src/DraggableGrid.tsx -src/ZoomedDial.tsx -src/index.css -src/Pedal.tsx -src/Draggable.tsx -src/PluginIcon.tsx -src/react-app-env.d.ts -src/TextFieldEx.tsx -src/FilePropertyDialog.tsx -src/FilePropertyDirectoryTree.tsx -src/StringBuilder.tsx -src/MainPage.tsx -src/PluginInfoDialog.tsx -src/AppThemed.tsx -src/Lv2Plugin.tsx -src/ToobSpectrumResponseView.tsx -src/PerformanceView.tsx -src/JackServerSettingsDialog.tsx -src/Utility.tsx -src/JackStatusView.tsx -src/ToobSpectrumAnalyzerView.tsx -src/SvgPathBuilder.tsx -src/PresetSelector.tsx -src/PresetDialog.tsx -src/WifiChannel.tsx -src/AboutDialog.tsx -src/GxTunerControl.tsx -src/VirtualKeyboardHandler.tsx -src/PluginControl.tsx -src/LoadPluginDialog.tsx -src/ZoomedUiControl.tsx -src/AlsaDeviceInfo.tsx -src/ToobToneStackView.tsx -src/UploadFileDialog.tsx -src/PluginOutputControl.tsx -src/SelectThemeDialog.tsx -src/Rect.tsx -src/AppThemed.css -src/ColorDropdownButton.tsx -src/AndroidHost.tsx -src/SnapshotPanel.tsx -src/ToobMLView.tsx -src/WifiConfigDialog.tsx -src/Rectangle.tsx -src/SnapshotColors.tsx -src/PiPedalModel.tsx -src/ObservableEvent.tsx -src/JackHostStatus.tsx -src/index.tsx -src/FilePropertyControl.tsx -src/WifiDirectConfigDialog.tsx -src/MidiBindingView.tsx -src/PluginControlView.tsx -src/SplitUiControls.tsx -src/PluginClass.tsx -src/DialogEx.tsx -src/Jack.tsx -src/ResizeResponsiveComponent.tsx -src/NumericInput.tsx -src/SnapshotPropertiesDialog.tsx -src/SearchControl.tsx -src/Units.tsx diff --git a/react/tsconfig.json b/react/tsconfig.json deleted file mode 100644 index a191348..0000000 --- a/react/tsconfig.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext", - "es2020" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx" - }, - "include": [ - "src" - ] -} diff --git a/vite/.gitignore b/vite/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/vite/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/vite/CMakeLists.txt b/vite/CMakeLists.txt new file mode 100644 index 0000000..a4b8cdb --- /dev/null +++ b/vite/CMakeLists.txt @@ -0,0 +1,312 @@ + + +find_program(NPM_COMMAND npm) + + +if (CMAKE_BUILD_TYPE MATCHES Debug) + set(BUILD_REACT "echo") + set(BUILD_REACT_ARGS Skipping react debug build) +else() + set(BUILD_REACT npm) + set(BUILD_REACT_ARGS run build) +endif() + +set (BUILD_DIRECTORY ${PROJECT_SOURCE_DIR}/dist) + + +add_custom_command( + OUTPUT ${BUILD_DIRECTORY}/index.html + COMMAND ${BUILD_REACT} + ARGS ${BUILD_REACT_ARGS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/vite + + DEPENDS + # find public src *.json *.ts -type f >/tmp/tmp.txt + public/vite.svg + public/robots.txt + public/css/roboto.css + public/iso_codes.json + public/serve.json + public/var/current_pedalboard.json + public/var/config.json + public/var/uiplugins.json + public/var/plugin_classes.json + public/logo512.png + public/img/fx_delay.svg + public/img/fx_error.svg + public/img/fx_parametric_eq.svg + public/img/fx_simulator.svg + public/img/fx_lr.svg + public/img/fx_plugin.svg + public/img/fx_flanger2.svg + public/img/fx_phaser.svg + public/img/ic_bank.svg + public/img/ic_drawer.svg + public/img/Pi-Logo-3.png + public/img/fx_constant.svg + public/img/ic_drawer_2.png + public/img/Pi-Logo-3.svg + public/img/fx_split_a.svg + public/img/fx_chorus.svg + public/img/fx_split_b.svg + public/img/fx_terminal.svg + public/img/fx_spectral.svg + public/img/settings_black_24dp.svg + public/img/fx_analyzer.svg + public/img/fx_oscillator.svg + public/img/fx_generator.svg + public/img/edit_banks.svg + public/img/delete_outline_black_24dp.svg + public/img/old_delete_outline_white_24dp.svg + public/img/fx_converter.svg + public/img/settings_white_24dp.svg + public/img/ic_drawer_2.svg + public/img/vst.png + public/img/edit_banks_white.svg + public/img/fx_pitch.svg + public/img/fx_spatial.svg + public/img/old_delete_outline_black_24dp.svg + public/img/fx_limiter.svg + public/img/vst.svg + public/img/ic_navigate_before.svg + public/img/cc-by.png + public/img/fx_gate.svg + public/img/ic_presets.svg + public/img/fx_modulator.svg + public/img/fx_filter_hp.svg + public/img/VST_Logo_Steinberg.png + public/img/fx_dial.svg + public/img/VST_Logo_Steinberg.ico + public/img/cx_mono.svg + public/img/ic_navigate_next.svg + public/img/fx_instrument.svg + public/img/fx_flanger.svg + public/img/fx_empty.svg + public/img/fx_mixer.svg + public/img/cx_stereo.svg + public/img/ic_logo.svg + public/img/fx_compressor.svg + public/img/fx_amplifier.svg + public/img/fx_filter.svg + public/img/fx_eq.svg + public/img/fx_utility.svg + public/img/fx_function.svg + public/img/fx_reverb.svg + public/img/fx_distortion.svg + public/img/ic_presets_white.svg + public/manifest.json + public/logo192.png + public/sample_lv2_plugins.json + public/fonts/Roboto-Thin.woff2 + public/fonts/LICENSE.txt + public/fonts/Roboto-Italic.woff2 + public/fonts/Roboto-BoldItalic.woff2 + public/fonts/Roboto-Black.woff2 + public/fonts/Roboto-ThinItalic.woff2 + public/fonts/Roboto-BlackItalic.woff2 + public/fonts/Roboto-MediumItalic.woff2 + public/fonts/Roboto-Medium.woff2 + public/fonts/Roboto-LightItalic.woff2 + public/fonts/Roboto-Regular.woff2 + public/fonts/Roboto-Bold.woff2 + public/fonts/Roboto-Light.woff2 + public/favicon.ico + public/index.html + src/main.tsx + src/index.css + src/assets/react.svg + src/vite-env.d.ts + src/App.tsx + src/pipedal/MainPage.tsx + src/pipedal/ColorDropdownButton.tsx + src/pipedal/AppThemed.tsx + src/pipedal/ModFileTypes.tsx + src/pipedal/ChannelBindingsHelpDialog.tsx + src/pipedal/SplitUiControls.tsx + src/pipedal/RadioSelectDialog.tsx + src/pipedal/Pedal.tsx + src/pipedal/WindowScale.tsx + src/pipedal/MidiChannelBindingDialog.tsx + src/pipedal/BankDialog.tsx + src/pipedal/IControlViewFactory.tsx + src/pipedal/Pedalboard.tsx + src/pipedal/Lv2Plugin.tsx + src/pipedal/WifiConfigDialog.tsx + src/pipedal/GxTunerControl.tsx + src/pipedal/ControlTooltip.tsx + src/pipedal/StringBuilder.tsx + src/pipedal/UploadFileDialog.tsx + src/pipedal/FilePropertyDirectorySelectDialog.tsx + src/pipedal/ObservableProperty.tsx + src/pipedal/OldDeleteIcon.tsx + src/pipedal/OkCancelDialog.tsx + src/pipedal/PluginControl.tsx + src/pipedal/WifiDirectConfigSettings.tsx + src/pipedal/FullScreenIME.tsx + src/pipedal/WifiConfigSettings.tsx + src/pipedal/JackServerSettingsDialog.tsx + src/pipedal/Utility.tsx + src/pipedal/SelectChannelsDialog.tsx + src/pipedal/AlsaDeviceInfo.tsx + src/pipedal/LoadPluginDialog.tsx + src/pipedal/PiPedalModel.tsx + src/pipedal/SelectThemeDialog.tsx + src/pipedal/ResizeResponsiveComponent.tsx + src/pipedal/FilePropertyControl.tsx + src/pipedal/NumericInput.tsx + src/pipedal/MidiBinding.tsx + src/pipedal/GovernorSettings.tsx + src/pipedal/SnapshotPanel.tsx + src/pipedal/PedalboardView.tsx + src/pipedal/ToobSpectrumAnalyzerView.tsx + src/pipedal/ToobMLView.tsx + src/pipedal/MaterialColors.tsx + src/pipedal/ZoomedUiControl.tsx + src/pipedal/TemporaryDrawer.tsx + src/pipedal/WifiChannel.tsx + src/pipedal/MidiChannelBinding.tsx + src/pipedal/SystemMidiBindingView.tsx + src/pipedal/SelectMidiChannelsDialog.tsx + src/pipedal/Banks.tsx + src/pipedal/SelectHoverBackground.tsx + src/pipedal/ToobWaveShapeView.tsx + src/pipedal/PluginPresetSelector.tsx + src/pipedal/PluginOutputControl.tsx + src/pipedal/Rect.tsx + src/pipedal/JackStatusView.tsx + src/pipedal/JackHostStatus.tsx + src/pipedal/PluginClass.tsx + src/pipedal/VuMeter.tsx + src/pipedal/svg/fx_delay.svg + src/pipedal/svg/fx_error.svg + src/pipedal/svg/old_delete_outline_24dp.svg + src/pipedal/svg/drive_file_rename_outline_black_24dp.svg + src/pipedal/svg/fx_parametric_eq.svg + src/pipedal/svg/fx_simulator.svg + src/pipedal/svg/fx_lr.svg + src/pipedal/svg/fx_plugin.svg + src/pipedal/svg/fx_flanger2.svg + src/pipedal/svg/fx_phaser.svg + src/pipedal/svg/ic_bank.svg + src/pipedal/svg/fx_constant.svg + src/pipedal/svg/snapshot_3.svg + src/pipedal/svg/snapshot_0.svg + src/pipedal/svg/fx_split_a.svg + src/pipedal/svg/fx_chorus.svg + src/pipedal/svg/fx_split_b.svg + src/pipedal/svg/fx_terminal.svg + src/pipedal/svg/ic_save_bank_as.svg + src/pipedal/svg/fx_spectral.svg + src/pipedal/svg/ic_settings.svg + src/pipedal/svg/ic_pluginpreset2.svg + src/pipedal/svg/fx_analyzer.svg + src/pipedal/svg/fx_oscillator.svg + src/pipedal/svg/fx_generator.svg + src/pipedal/svg/snapshot_2.svg + src/pipedal/svg/fx_converter.svg + src/pipedal/svg/ic_midi.svg + src/pipedal/svg/fx_pitch.svg + src/pipedal/svg/fx_spatial.svg + src/pipedal/svg/fx_limiter.svg + src/pipedal/svg/fx_gate.svg + src/pipedal/svg/ic_presets.svg + src/pipedal/svg/snapshot_5.svg + src/pipedal/svg/fx_modulator.svg + src/pipedal/svg/snapshot_6.svg + src/pipedal/svg/fx_filter_hp.svg + src/pipedal/svg/fx_dial.svg + src/pipedal/svg/ic_pluginpreset.svg + src/pipedal/svg/fx_instrument.svg + src/pipedal/svg/fx_flanger.svg + src/pipedal/svg/file_download_black_24dp.svg + src/pipedal/svg/snapshot_1.svg + src/pipedal/svg/fx_empty.svg + src/pipedal/svg/fx_mixer.svg + src/pipedal/svg/ic_edit_banks.svg + src/pipedal/svg/file_upload_black_24dp.svg + src/pipedal/svg/fx_compressor.svg + src/pipedal/svg/fx_amplifier.svg + src/pipedal/svg/fx_filter.svg + src/pipedal/svg/fx_eq.svg + src/pipedal/svg/fx_utility.svg + src/pipedal/svg/fx_function.svg + src/pipedal/svg/snapshot_4.svg + src/pipedal/svg/fx_reverb.svg + src/pipedal/svg/fx_distortion.svg + src/pipedal/svg/ic_help_outline.svg + src/pipedal/AppThemed.css + src/pipedal/AboutDialog.tsx + src/pipedal/SettingsDialog.tsx + src/pipedal/PluginControlView.tsx + src/pipedal/VirtualKeyboardHandler.tsx + src/pipedal/react-app-env.d.ts + src/pipedal/DialogEx.tsx + src/pipedal/ToobInputStageView.tsx + src/pipedal/UpdateDialog.tsx + src/pipedal/UploadPresetDialog.tsx + src/pipedal/SnapshotButton.tsx + src/pipedal/GxTunerView.tsx + src/pipedal/SnapshotColors.tsx + src/pipedal/MidiChannelBindingControl.tsx + src/pipedal/DarkMode.tsx + src/pipedal/PluginInfoDialog.tsx + src/pipedal/ToobCabSimView.tsx + src/pipedal/Jack.tsx + src/pipedal/SnapshotEditor.tsx + src/pipedal/ControlViewFactory.tsx + src/pipedal/MidiBindingsDialog.tsx + src/pipedal/SplitControlView.tsx + src/pipedal/PiPedalError.tsx + src/pipedal/XxxSnippet.tsx + src/pipedal/SearchControl.tsx + src/pipedal/PluginPreset.tsx + src/pipedal/AlsaMidiDeviceInfo.tsx + src/pipedal/PiPedalSocket.tsx + src/pipedal/NoChangePassword.tsx + src/pipedal/App.tsx + src/pipedal/ObservableEvent.tsx + src/pipedal/JackServerSettings.tsx + src/pipedal/SnapshotPropertiesDialog.tsx + src/pipedal/TextFieldEx.tsx + src/pipedal/FilePropertyDialog.tsx + src/pipedal/PresetDialog.tsx + src/pipedal/SearchFilter.tsx + src/pipedal/Units.tsx + src/pipedal/AndroidHost.tsx + src/pipedal/ToobPowerStage2View.tsx + src/pipedal/Rectangle.tsx + src/pipedal/PluginPresetsDialog.tsx + src/pipedal/FilePropertyDirectoryTree.tsx + src/pipedal/RenameDialog.tsx + src/pipedal/PerformanceView.tsx + src/pipedal/ToobFrequencyResponseView.tsx + src/pipedal/MidiBindingView.tsx + src/pipedal/SnapshotDialog.tsx + src/pipedal/PluginIcon.tsx + src/pipedal/JsonAtom.tsx + src/pipedal/ToobToneStackView.tsx + src/pipedal/logo.svg + src/pipedal/ToobSpectrumResponseView.tsx + src/pipedal/ZoomedDial.tsx + src/pipedal/Draggable.tsx + src/pipedal/DraggableGrid.tsx + src/pipedal/WifiDirectConfigDialog.tsx + src/pipedal/SvgPathBuilder.tsx + src/pipedal/PresetSelector.tsx + src/pipedal/SystemMidiBindingsDialog.tsx + src/pipedal/OptionsDialog.tsx + src/pipedal/Updater.tsx + src/App.css + package.json + package-lock.json + tsconfig.app.json + tsconfig.json + tsconfig.node.json + vite.config.ts + +) + +add_custom_target ( + ReactBuild ALL + DEPENDS ${BUILD_DIRECTORY}/index.html +) diff --git a/vite/README.md b/vite/README.md new file mode 100644 index 0000000..74872fd --- /dev/null +++ b/vite/README.md @@ -0,0 +1,50 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/vite/eslint.config.js b/vite/eslint.config.js new file mode 100644 index 0000000..adef530 --- /dev/null +++ b/vite/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist', 'build'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/vite/index.html b/vite/index.html new file mode 100644 index 0000000..b44998e --- /dev/null +++ b/vite/index.html @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + PiPedal + + +
+ + + diff --git a/vite/package-lock.json b/vite/package-lock.json new file mode 100644 index 0000000..58d5300 --- /dev/null +++ b/vite/package-lock.json @@ -0,0 +1,5152 @@ +{ + "name": "pipedal", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pipedal", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@fontsource/roboto": "^5.1.1", + "@mui/icons-material": "^6.4.4", + "@mui/material": "^6.4.4", + "@mui/styles": "^6.4.4", + "@types/react-window": "^1.8.8", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-remark": "^2.1.0", + "react-virtualized-auto-sizer": "^1.0.25", + "react-window": "^1.8.11", + "tss-react": "^4.9.15" + }, + "devDependencies": { + "@eslint/js": "^9.19.0", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.19.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.18", + "globals": "^15.14.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.22.0", + "vite": "^6.1.0", + "vite-plugin-svgr": "^4.3.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.9" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", + "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "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.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", + "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.20.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", + "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.10.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@fontsource/roboto": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.1.1.tgz", + "integrity": "sha512-XwVVXtERDQIM7HPUIbyDe0FP4SRovpjF7zMI8M7pbqFp3ahLJsJTd18h+E6pkar6UbV3btbwkKjYARr5M+SQow==", + "license": "Apache-2.0" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/hast-util-table-cell-style": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@mapbox/hast-util-table-cell-style/-/hast-util-table-cell-style-0.2.1.tgz", + "integrity": "sha512-LyQz4XJIdCdY/+temIhD/Ed0x/p4GAOUycpFSEK2Ads1CPKZy6b7V/2ROEtQiLLQ8soIs0xe/QAoR6kwpyW/yw==", + "license": "BSD-2-Clause", + "dependencies": { + "unist-util-visit": "^1.4.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.4.tgz", + "integrity": "sha512-r+J0EditrekkTtO2CnCBCOGpNaDYwJqz8lH4rj6o/anDcskZFJodBlG8aCJkS8DL/CF/9EHS+Gz53EbmYEnQbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.4.tgz", + "integrity": "sha512-uF1chGaoFmYdRUomK6f8kgJfWosk9A3HXWiVD0vQm+2mE7f25eTQ1E8RRO11LXpnUBqu8Rbv/uGlpnjT/u1Ksg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^6.4.4", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.4.tgz", + "integrity": "sha512-ISVPrIsPQsxnwvS40C4u03AuNSPigFeS2+n1qpuEZ94hDsdMi19dQM2JcC9CHEhXecSIQjP1RTyY0mPiSpSrFQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.4.4", + "@mui/system": "^6.4.3", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.3", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^6.4.3", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.3.tgz", + "integrity": "sha512-7x9HaNwDCeoERc4BoEWLieuzKzXu5ZrhRnEM6AUcRXUScQLvF1NFkTlP59+IJfTbEMgcGg1wWHApyoqcksrBpQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.3.tgz", + "integrity": "sha512-OC402VfK+ra2+f12Gef8maY7Y9n7B6CZcoQ9u7mIkh/7PKwW/xH81xwX+yW+Ak1zBT3HYcVjh2X82k5cKMFGoQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/styles": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-6.4.4.tgz", + "integrity": "sha512-QjB9jhjVTPR7+icGQQzjTUPAu/LodnwcRV3HNDqO5sr1HJhZOqd8V8TF/8ArXWwVIm0yw6E/MTvnvhJB82LUPQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/hash": "^0.9.2", + "@mui/private-theming": "^6.4.3", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.3", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.10.0", + "jss-plugin-camel-case": "^10.10.0", + "jss-plugin-default-unit": "^10.10.0", + "jss-plugin-global": "^10.10.0", + "jss-plugin-nested": "^10.10.0", + "jss-plugin-props-sort": "^10.10.0", + "jss-plugin-rule-value-function": "^10.10.0", + "jss-plugin-vendor-prefixer": "^10.10.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.3.tgz", + "integrity": "sha512-Q0iDwnH3+xoxQ0pqVbt8hFdzhq1g2XzzR4Y5pVcICTNtoCLJmpJS3vI4y/OIM1FHFmpfmiEC2IRIq7YcZ8nsmg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.4.3", + "@mui/styled-engine": "^6.4.3", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.3", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.21", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", + "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.3.tgz", + "integrity": "sha512-jxHRHh3BqVXE9ABxDm+Tc3wlBooYz/4XPa0+4AI+iF38rV1/+btJmSUgG4shDtSWVs/I97aDn5jBCt6SF2Uq2A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", + "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", + "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", + "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", + "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", + "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", + "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", + "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", + "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", + "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", + "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", + "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", + "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", + "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", + "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", + "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", + "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", + "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", + "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", + "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", + "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", + "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/type-utils": "8.24.1", + "@typescript-eslint/utils": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz", + "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz", + "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz", + "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.24.1", + "@typescript-eslint/utils": "8.24.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz", + "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz", + "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/visitor-keys": "8.24.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz", + "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.24.1", + "@typescript-eslint/types": "8.24.1", + "@typescript-eslint/typescript-estree": "8.24.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz", + "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.24.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.102", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", + "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", + "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.11.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.20.0", + "@eslint/plugin-kit": "^0.2.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", + "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-to-hyperscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", + "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.3", + "comma-separated-tokens": "^1.0.0", + "property-information": "^5.3.0", + "space-separated-tokens": "^1.0.0", + "style-to-object": "^0.3.0", + "unist-util-is": "^4.0.0", + "web-namespaces": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "license": "MIT" + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "license": "MIT", + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions/node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions/node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz", + "integrity": "sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "license": "MIT" + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "license": "MIT", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remark": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-remark/-/react-remark-2.1.0.tgz", + "integrity": "sha512-7dEPxRGQ23sOdvteuRGaQAs9cEOH/BOeCN4CqsJdk3laUDIDYRCWnM6a3z92PzXHUuxIRLXQNZx7SiO0ijUcbw==", + "license": "MIT", + "dependencies": { + "rehype-react": "^6.0.0", + "remark-parse": "^9.0.0", + "remark-rehype": "^8.0.0", + "unified": "^9.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-virtualized-auto-sizer": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.25.tgz", + "integrity": "sha512-YHsksEGDfsHbHuaBVDYwJmcktblcHGafz4ZVuYPQYuSHMUGjpwmUCrAOcvMSGMwwk1eFWj1M/1GwYpNPuyhaBg==", + "license": "MIT", + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-window": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz", + "integrity": "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/rehype-react": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/rehype-react/-/rehype-react-6.2.1.tgz", + "integrity": "sha512-f9KIrjktvLvmbGc7si25HepocOg4z0MuNOtweigKzBcDjiGSTGhyz6VSgaV5K421Cq1O+z4/oxRJ5G9owo0KVg==", + "license": "MIT", + "dependencies": { + "@mapbox/hast-util-table-cell-style": "^0.2.0", + "hast-to-hyperscript": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-8.1.0.tgz", + "integrity": "sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==", + "license": "MIT", + "dependencies": { + "mdast-util-to-hast": "^10.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", + "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.34.8", + "@rollup/rollup-android-arm64": "4.34.8", + "@rollup/rollup-darwin-arm64": "4.34.8", + "@rollup/rollup-darwin-x64": "4.34.8", + "@rollup/rollup-freebsd-arm64": "4.34.8", + "@rollup/rollup-freebsd-x64": "4.34.8", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", + "@rollup/rollup-linux-arm-musleabihf": "4.34.8", + "@rollup/rollup-linux-arm64-gnu": "4.34.8", + "@rollup/rollup-linux-arm64-musl": "4.34.8", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", + "@rollup/rollup-linux-riscv64-gnu": "4.34.8", + "@rollup/rollup-linux-s390x-gnu": "4.34.8", + "@rollup/rollup-linux-x64-gnu": "4.34.8", + "@rollup/rollup-linux-x64-musl": "4.34.8", + "@rollup/rollup-win32-arm64-msvc": "4.34.8", + "@rollup/rollup-win32-ia32-msvc": "4.34.8", + "@rollup/rollup-win32-x64-msvc": "4.34.8", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-object": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", + "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tss-react": { + "version": "4.9.15", + "resolved": "https://registry.npmjs.org/tss-react/-/tss-react-4.9.15.tgz", + "integrity": "sha512-rLiEmDwUtln9RKTUR/ZPYBrufF0Tq/PFggO1M7P8M3/FAcodPQ746Ug9MCEFkURKDlntN17+Oja0DMMz5yBnsQ==", + "license": "MIT", + "dependencies": { + "@emotion/cache": "*", + "@emotion/serialize": "*", + "@emotion/utils": "*" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/server": "^11.4.0", + "@mui/material": "^5.0.0 || ^6.0.0", + "@types/react": "^16.8.0 || ^17.0.2 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/server": { + "optional": true + }, + "@mui/material": { + "optional": true + } + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.24.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.24.1.tgz", + "integrity": "sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.24.1", + "@typescript-eslint/parser": "8.24.1", + "@typescript-eslint/utils": "8.24.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "license": "MIT", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-generated": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "license": "MIT", + "dependencies": { + "unist-util-visit-parents": "^2.0.0" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "license": "MIT", + "dependencies": { + "unist-util-is": "^3.0.0" + } + }, + "node_modules/unist-util-visit-parents/node_modules/unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz", + "integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.24.2", + "postcss": "^8.5.1", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-svgr": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz", + "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.3", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": ">=2.6.0" + } + }, + "node_modules/web-namespaces": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", + "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/vite/package.json b/vite/package.json new file mode 100644 index 0000000..bcf96f2 --- /dev/null +++ b/vite/package.json @@ -0,0 +1,41 @@ +{ + "name": "pipedal", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@fontsource/roboto": "^5.1.1", + "@mui/icons-material": "^6.4.4", + "@mui/material": "^6.4.4", + "@mui/styles": "^6.4.4", + "@types/react-window": "^1.8.8", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-remark": "^2.1.0", + "react-virtualized-auto-sizer": "^1.0.25", + "react-window": "^1.8.11", + "tss-react": "^4.9.15" + }, + "devDependencies": { + "@eslint/js": "^9.19.0", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.19.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.18", + "globals": "^15.14.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.22.0", + "vite": "^6.1.0", + "vite-plugin-svgr": "^4.3.0" + } +} diff --git a/react/public/css/roboto.css b/vite/public/css/roboto.css similarity index 100% rename from react/public/css/roboto.css rename to vite/public/css/roboto.css diff --git a/react/public/favicon.ico b/vite/public/favicon.ico similarity index 100% rename from react/public/favicon.ico rename to vite/public/favicon.ico diff --git a/react/public/fonts/LICENSE.txt b/vite/public/fonts/LICENSE.txt similarity index 100% rename from react/public/fonts/LICENSE.txt rename to vite/public/fonts/LICENSE.txt diff --git a/react/public/fonts/Roboto-Black.woff2 b/vite/public/fonts/Roboto-Black.woff2 similarity index 100% rename from react/public/fonts/Roboto-Black.woff2 rename to vite/public/fonts/Roboto-Black.woff2 diff --git a/react/public/fonts/Roboto-BlackItalic.woff2 b/vite/public/fonts/Roboto-BlackItalic.woff2 similarity index 100% rename from react/public/fonts/Roboto-BlackItalic.woff2 rename to vite/public/fonts/Roboto-BlackItalic.woff2 diff --git a/react/public/fonts/Roboto-Bold.woff2 b/vite/public/fonts/Roboto-Bold.woff2 similarity index 100% rename from react/public/fonts/Roboto-Bold.woff2 rename to vite/public/fonts/Roboto-Bold.woff2 diff --git a/react/public/fonts/Roboto-BoldItalic.woff2 b/vite/public/fonts/Roboto-BoldItalic.woff2 similarity index 100% rename from react/public/fonts/Roboto-BoldItalic.woff2 rename to vite/public/fonts/Roboto-BoldItalic.woff2 diff --git a/react/public/fonts/Roboto-Italic.woff2 b/vite/public/fonts/Roboto-Italic.woff2 similarity index 100% rename from react/public/fonts/Roboto-Italic.woff2 rename to vite/public/fonts/Roboto-Italic.woff2 diff --git a/react/public/fonts/Roboto-Light.woff2 b/vite/public/fonts/Roboto-Light.woff2 similarity index 100% rename from react/public/fonts/Roboto-Light.woff2 rename to vite/public/fonts/Roboto-Light.woff2 diff --git a/react/public/fonts/Roboto-LightItalic.woff2 b/vite/public/fonts/Roboto-LightItalic.woff2 similarity index 100% rename from react/public/fonts/Roboto-LightItalic.woff2 rename to vite/public/fonts/Roboto-LightItalic.woff2 diff --git a/react/public/fonts/Roboto-Medium.woff2 b/vite/public/fonts/Roboto-Medium.woff2 similarity index 100% rename from react/public/fonts/Roboto-Medium.woff2 rename to vite/public/fonts/Roboto-Medium.woff2 diff --git a/react/public/fonts/Roboto-MediumItalic.woff2 b/vite/public/fonts/Roboto-MediumItalic.woff2 similarity index 100% rename from react/public/fonts/Roboto-MediumItalic.woff2 rename to vite/public/fonts/Roboto-MediumItalic.woff2 diff --git a/react/public/fonts/Roboto-Regular.woff2 b/vite/public/fonts/Roboto-Regular.woff2 similarity index 100% rename from react/public/fonts/Roboto-Regular.woff2 rename to vite/public/fonts/Roboto-Regular.woff2 diff --git a/react/public/fonts/Roboto-Thin.woff2 b/vite/public/fonts/Roboto-Thin.woff2 similarity index 100% rename from react/public/fonts/Roboto-Thin.woff2 rename to vite/public/fonts/Roboto-Thin.woff2 diff --git a/react/public/fonts/Roboto-ThinItalic.woff2 b/vite/public/fonts/Roboto-ThinItalic.woff2 similarity index 100% rename from react/public/fonts/Roboto-ThinItalic.woff2 rename to vite/public/fonts/Roboto-ThinItalic.woff2 diff --git a/react/public/img/Pi-Logo-3.png b/vite/public/img/Pi-Logo-3.png similarity index 100% rename from react/public/img/Pi-Logo-3.png rename to vite/public/img/Pi-Logo-3.png diff --git a/react/public/img/Pi-Logo-3.svg b/vite/public/img/Pi-Logo-3.svg similarity index 100% rename from react/public/img/Pi-Logo-3.svg rename to vite/public/img/Pi-Logo-3.svg diff --git a/react/public/img/VST_Logo_Steinberg.ico b/vite/public/img/VST_Logo_Steinberg.ico similarity index 100% rename from react/public/img/VST_Logo_Steinberg.ico rename to vite/public/img/VST_Logo_Steinberg.ico diff --git a/react/public/img/VST_Logo_Steinberg.png b/vite/public/img/VST_Logo_Steinberg.png similarity index 100% rename from react/public/img/VST_Logo_Steinberg.png rename to vite/public/img/VST_Logo_Steinberg.png diff --git a/react/public/img/cc-by.png b/vite/public/img/cc-by.png similarity index 100% rename from react/public/img/cc-by.png rename to vite/public/img/cc-by.png diff --git a/react/public/img/cx_mono.svg b/vite/public/img/cx_mono.svg similarity index 100% rename from react/public/img/cx_mono.svg rename to vite/public/img/cx_mono.svg diff --git a/react/public/img/cx_stereo.svg b/vite/public/img/cx_stereo.svg similarity index 100% rename from react/public/img/cx_stereo.svg rename to vite/public/img/cx_stereo.svg diff --git a/react/public/img/delete_outline_black_24dp.svg b/vite/public/img/delete_outline_black_24dp.svg similarity index 100% rename from react/public/img/delete_outline_black_24dp.svg rename to vite/public/img/delete_outline_black_24dp.svg diff --git a/react/public/img/edit_banks.svg b/vite/public/img/edit_banks.svg similarity index 100% rename from react/public/img/edit_banks.svg rename to vite/public/img/edit_banks.svg diff --git a/react/public/img/edit_banks_white.svg b/vite/public/img/edit_banks_white.svg similarity index 100% rename from react/public/img/edit_banks_white.svg rename to vite/public/img/edit_banks_white.svg diff --git a/react/public/img/fx_amplifier.svg b/vite/public/img/fx_amplifier.svg similarity index 100% rename from react/public/img/fx_amplifier.svg rename to vite/public/img/fx_amplifier.svg diff --git a/react/public/img/fx_analyzer.svg b/vite/public/img/fx_analyzer.svg similarity index 100% rename from react/public/img/fx_analyzer.svg rename to vite/public/img/fx_analyzer.svg diff --git a/react/public/img/fx_chorus.svg b/vite/public/img/fx_chorus.svg similarity index 100% rename from react/public/img/fx_chorus.svg rename to vite/public/img/fx_chorus.svg diff --git a/react/public/img/fx_compressor.svg b/vite/public/img/fx_compressor.svg similarity index 100% rename from react/public/img/fx_compressor.svg rename to vite/public/img/fx_compressor.svg diff --git a/react/public/img/fx_constant.svg b/vite/public/img/fx_constant.svg similarity index 100% rename from react/public/img/fx_constant.svg rename to vite/public/img/fx_constant.svg diff --git a/react/public/img/fx_converter.svg b/vite/public/img/fx_converter.svg similarity index 100% rename from react/public/img/fx_converter.svg rename to vite/public/img/fx_converter.svg diff --git a/react/public/img/fx_delay.svg b/vite/public/img/fx_delay.svg similarity index 100% rename from react/public/img/fx_delay.svg rename to vite/public/img/fx_delay.svg diff --git a/react/public/img/fx_dial.svg b/vite/public/img/fx_dial.svg similarity index 100% rename from react/public/img/fx_dial.svg rename to vite/public/img/fx_dial.svg diff --git a/react/public/img/fx_distortion.svg b/vite/public/img/fx_distortion.svg similarity index 100% rename from react/public/img/fx_distortion.svg rename to vite/public/img/fx_distortion.svg diff --git a/react/public/img/fx_empty.svg b/vite/public/img/fx_empty.svg similarity index 100% rename from react/public/img/fx_empty.svg rename to vite/public/img/fx_empty.svg diff --git a/react/public/img/fx_eq.svg b/vite/public/img/fx_eq.svg similarity index 100% rename from react/public/img/fx_eq.svg rename to vite/public/img/fx_eq.svg diff --git a/react/public/img/fx_error.svg b/vite/public/img/fx_error.svg similarity index 100% rename from react/public/img/fx_error.svg rename to vite/public/img/fx_error.svg diff --git a/react/public/img/fx_filter.svg b/vite/public/img/fx_filter.svg similarity index 100% rename from react/public/img/fx_filter.svg rename to vite/public/img/fx_filter.svg diff --git a/react/public/img/fx_filter_hp.svg b/vite/public/img/fx_filter_hp.svg similarity index 100% rename from react/public/img/fx_filter_hp.svg rename to vite/public/img/fx_filter_hp.svg diff --git a/react/public/img/fx_flanger.svg b/vite/public/img/fx_flanger.svg similarity index 100% rename from react/public/img/fx_flanger.svg rename to vite/public/img/fx_flanger.svg diff --git a/react/public/img/fx_flanger2.svg b/vite/public/img/fx_flanger2.svg similarity index 100% rename from react/public/img/fx_flanger2.svg rename to vite/public/img/fx_flanger2.svg diff --git a/react/public/img/fx_function.svg b/vite/public/img/fx_function.svg similarity index 100% rename from react/public/img/fx_function.svg rename to vite/public/img/fx_function.svg diff --git a/react/public/img/fx_gate.svg b/vite/public/img/fx_gate.svg similarity index 100% rename from react/public/img/fx_gate.svg rename to vite/public/img/fx_gate.svg diff --git a/react/public/img/fx_generator.svg b/vite/public/img/fx_generator.svg similarity index 100% rename from react/public/img/fx_generator.svg rename to vite/public/img/fx_generator.svg diff --git a/react/public/img/fx_instrument.svg b/vite/public/img/fx_instrument.svg similarity index 100% rename from react/public/img/fx_instrument.svg rename to vite/public/img/fx_instrument.svg diff --git a/react/public/img/fx_limiter.svg b/vite/public/img/fx_limiter.svg similarity index 100% rename from react/public/img/fx_limiter.svg rename to vite/public/img/fx_limiter.svg diff --git a/react/public/img/fx_lr.svg b/vite/public/img/fx_lr.svg similarity index 100% rename from react/public/img/fx_lr.svg rename to vite/public/img/fx_lr.svg diff --git a/react/public/img/fx_mixer.svg b/vite/public/img/fx_mixer.svg similarity index 100% rename from react/public/img/fx_mixer.svg rename to vite/public/img/fx_mixer.svg diff --git a/react/public/img/fx_modulator.svg b/vite/public/img/fx_modulator.svg similarity index 100% rename from react/public/img/fx_modulator.svg rename to vite/public/img/fx_modulator.svg diff --git a/react/public/img/fx_oscillator.svg b/vite/public/img/fx_oscillator.svg similarity index 100% rename from react/public/img/fx_oscillator.svg rename to vite/public/img/fx_oscillator.svg diff --git a/react/public/img/fx_parametric_eq.svg b/vite/public/img/fx_parametric_eq.svg similarity index 100% rename from react/public/img/fx_parametric_eq.svg rename to vite/public/img/fx_parametric_eq.svg diff --git a/react/public/img/fx_phaser.svg b/vite/public/img/fx_phaser.svg similarity index 100% rename from react/public/img/fx_phaser.svg rename to vite/public/img/fx_phaser.svg diff --git a/react/public/img/fx_pitch.svg b/vite/public/img/fx_pitch.svg similarity index 100% rename from react/public/img/fx_pitch.svg rename to vite/public/img/fx_pitch.svg diff --git a/react/public/img/fx_plugin.svg b/vite/public/img/fx_plugin.svg similarity index 100% rename from react/public/img/fx_plugin.svg rename to vite/public/img/fx_plugin.svg diff --git a/react/public/img/fx_reverb.svg b/vite/public/img/fx_reverb.svg similarity index 100% rename from react/public/img/fx_reverb.svg rename to vite/public/img/fx_reverb.svg diff --git a/react/public/img/fx_simulator.svg b/vite/public/img/fx_simulator.svg similarity index 100% rename from react/public/img/fx_simulator.svg rename to vite/public/img/fx_simulator.svg diff --git a/react/public/img/fx_spatial.svg b/vite/public/img/fx_spatial.svg similarity index 100% rename from react/public/img/fx_spatial.svg rename to vite/public/img/fx_spatial.svg diff --git a/react/public/img/fx_spectral.svg b/vite/public/img/fx_spectral.svg similarity index 100% rename from react/public/img/fx_spectral.svg rename to vite/public/img/fx_spectral.svg diff --git a/react/public/img/fx_split_a.svg b/vite/public/img/fx_split_a.svg similarity index 100% rename from react/public/img/fx_split_a.svg rename to vite/public/img/fx_split_a.svg diff --git a/react/public/img/fx_split_b.svg b/vite/public/img/fx_split_b.svg similarity index 100% rename from react/public/img/fx_split_b.svg rename to vite/public/img/fx_split_b.svg diff --git a/react/public/img/fx_terminal.svg b/vite/public/img/fx_terminal.svg similarity index 100% rename from react/public/img/fx_terminal.svg rename to vite/public/img/fx_terminal.svg diff --git a/react/public/img/fx_utility.svg b/vite/public/img/fx_utility.svg similarity index 100% rename from react/public/img/fx_utility.svg rename to vite/public/img/fx_utility.svg diff --git a/react/public/img/ic_bank.svg b/vite/public/img/ic_bank.svg similarity index 100% rename from react/public/img/ic_bank.svg rename to vite/public/img/ic_bank.svg diff --git a/react/public/img/ic_drawer.svg b/vite/public/img/ic_drawer.svg similarity index 100% rename from react/public/img/ic_drawer.svg rename to vite/public/img/ic_drawer.svg diff --git a/react/public/img/ic_drawer_2.png b/vite/public/img/ic_drawer_2.png similarity index 100% rename from react/public/img/ic_drawer_2.png rename to vite/public/img/ic_drawer_2.png diff --git a/react/public/img/ic_drawer_2.svg b/vite/public/img/ic_drawer_2.svg similarity index 100% rename from react/public/img/ic_drawer_2.svg rename to vite/public/img/ic_drawer_2.svg diff --git a/react/public/img/ic_logo.svg b/vite/public/img/ic_logo.svg similarity index 100% rename from react/public/img/ic_logo.svg rename to vite/public/img/ic_logo.svg diff --git a/react/public/img/ic_navigate_before.svg b/vite/public/img/ic_navigate_before.svg similarity index 100% rename from react/public/img/ic_navigate_before.svg rename to vite/public/img/ic_navigate_before.svg diff --git a/react/public/img/ic_navigate_next.svg b/vite/public/img/ic_navigate_next.svg similarity index 100% rename from react/public/img/ic_navigate_next.svg rename to vite/public/img/ic_navigate_next.svg diff --git a/react/public/img/ic_presets.svg b/vite/public/img/ic_presets.svg similarity index 100% rename from react/public/img/ic_presets.svg rename to vite/public/img/ic_presets.svg diff --git a/react/public/img/ic_presets_white.svg b/vite/public/img/ic_presets_white.svg similarity index 100% rename from react/public/img/ic_presets_white.svg rename to vite/public/img/ic_presets_white.svg diff --git a/react/public/img/old_delete_outline_black_24dp.svg b/vite/public/img/old_delete_outline_black_24dp.svg similarity index 100% rename from react/public/img/old_delete_outline_black_24dp.svg rename to vite/public/img/old_delete_outline_black_24dp.svg diff --git a/react/public/img/old_delete_outline_white_24dp.svg b/vite/public/img/old_delete_outline_white_24dp.svg similarity index 100% rename from react/public/img/old_delete_outline_white_24dp.svg rename to vite/public/img/old_delete_outline_white_24dp.svg diff --git a/react/public/img/settings_black_24dp.svg b/vite/public/img/settings_black_24dp.svg similarity index 100% rename from react/public/img/settings_black_24dp.svg rename to vite/public/img/settings_black_24dp.svg diff --git a/react/public/img/settings_white_24dp.svg b/vite/public/img/settings_white_24dp.svg similarity index 100% rename from react/public/img/settings_white_24dp.svg rename to vite/public/img/settings_white_24dp.svg diff --git a/react/public/img/vst.png b/vite/public/img/vst.png similarity index 100% rename from react/public/img/vst.png rename to vite/public/img/vst.png diff --git a/react/public/img/vst.svg b/vite/public/img/vst.svg similarity index 100% rename from react/public/img/vst.svg rename to vite/public/img/vst.svg diff --git a/react/public/index.html b/vite/public/index.html similarity index 100% rename from react/public/index.html rename to vite/public/index.html diff --git a/react/public/iso_codes.json b/vite/public/iso_codes.json similarity index 100% rename from react/public/iso_codes.json rename to vite/public/iso_codes.json diff --git a/react/public/logo192.png b/vite/public/logo192.png similarity index 100% rename from react/public/logo192.png rename to vite/public/logo192.png diff --git a/react/public/logo512.png b/vite/public/logo512.png similarity index 100% rename from react/public/logo512.png rename to vite/public/logo512.png diff --git a/react/public/manifest.json b/vite/public/manifest.json similarity index 100% rename from react/public/manifest.json rename to vite/public/manifest.json diff --git a/react/public/robots.txt b/vite/public/robots.txt similarity index 100% rename from react/public/robots.txt rename to vite/public/robots.txt diff --git a/react/public/sample_lv2_plugins.json b/vite/public/sample_lv2_plugins.json similarity index 100% rename from react/public/sample_lv2_plugins.json rename to vite/public/sample_lv2_plugins.json diff --git a/react/public/serve.json b/vite/public/serve.json similarity index 100% rename from react/public/serve.json rename to vite/public/serve.json diff --git a/react/public/var/config.json b/vite/public/var/config.json similarity index 100% rename from react/public/var/config.json rename to vite/public/var/config.json diff --git a/react/public/var/current_pedalboard.json b/vite/public/var/current_pedalboard.json similarity index 100% rename from react/public/var/current_pedalboard.json rename to vite/public/var/current_pedalboard.json diff --git a/react/public/var/plugin_classes.json b/vite/public/var/plugin_classes.json similarity index 100% rename from react/public/var/plugin_classes.json rename to vite/public/var/plugin_classes.json diff --git a/react/public/var/uiplugins.json b/vite/public/var/uiplugins.json similarity index 100% rename from react/public/var/uiplugins.json rename to vite/public/var/uiplugins.json diff --git a/vite/public/vite.svg b/vite/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/vite/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/vite/src/App.css b/vite/src/App.css new file mode 100644 index 0000000..e69de29 diff --git a/vite/src/App.tsx b/vite/src/App.tsx new file mode 100644 index 0000000..eabe4ed --- /dev/null +++ b/vite/src/App.tsx @@ -0,0 +1,36 @@ +import { useState } from 'react' +import ReactLogo from './assets/react.svg?react' +//import ViteLogo from '/vite.svg?react' +import './App.css' + + +function App() { + const [count, setCount] = useState(0) + + return ( + <> + +

Vite + React

+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+

+ Click on the Vite and React logos to learn more +

+ + ) +} + +export default App diff --git a/vite/src/assets/react.svg b/vite/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/vite/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/vite/src/index.css b/vite/src/index.css new file mode 100644 index 0000000..e69de29 diff --git a/vite/src/main.tsx b/vite/src/main.tsx new file mode 100644 index 0000000..6b0cb78 --- /dev/null +++ b/vite/src/main.tsx @@ -0,0 +1,14 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './pipedal/App.tsx' + +// createRoot(document.getElementById('root')!).render( +// +// +// , +// ) + +createRoot(document.getElementById('root')!).render( + +) diff --git a/vite/src/pipedal/AboutDialog.tsx b/vite/src/pipedal/AboutDialog.tsx new file mode 100644 index 0000000..e8dce53 --- /dev/null +++ b/vite/src/pipedal/AboutDialog.tsx @@ -0,0 +1,240 @@ + +import React, { SyntheticEvent, Component } from 'react'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import Divider from '@mui/material/Divider'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; + +import JackHostStatus from './JackHostStatus'; +import { PiPedalError } from './PiPedalError'; +import DialogEx from './DialogEx'; + + + +import Slide, { SlideProps } from '@mui/material/Slide'; + +interface AboutDialogProps { + open: boolean; + onClose: () => void; +}; + +interface AboutDialogState { + jackStatus?: JackHostStatus; + + openSourceNotices: string; +}; + + +const Transition = React.forwardRef(function Transition( + props: SlideProps, ref: React.Ref +) { + return (); +}); + + +const AboutDialog = class extends Component { + + model: PiPedalModel; + refNotices: React.RefObject; + + + constructor(props: AboutDialogProps) { + super(props); + this.model = PiPedalModelFactory.getInstance(); + this.refNotices = React.createRef(); + this.handleDialogClose = this.handleDialogClose.bind(this); + this.state = { + jackStatus: undefined, + openSourceNotices: "" + + }; + } + + mounted: boolean = false; + subscribed: boolean = false; + + tick() { + if (this.model.state.get() === State.Ready) { + this.model.getJackStatus() + .then(jackStatus => { + this.setState({ jackStatus: jackStatus }); + }) + .catch(_error => { /* ignore*/ }); + } + } + + timerHandle?: number; + + updateNotifications() { + let subscribed = this.mounted && this.props.open; + if (subscribed !== this.subscribed) { + if (subscribed) { + this.timerHandle = setInterval(() => this.tick(), 1000); + } else { + if (this.timerHandle) { + clearInterval(this.timerHandle); + this.timerHandle = undefined; + } + } + this.subscribed = subscribed; + } + } + + startFossRequest() { + if (this.state.openSourceNotices === "") { + fetch("var/notices.txt") + .then((request) => { + if (!request.ok) { + throw new PiPedalError("notices request failed."); + } + return request.text(); + }) + .then((text) => { + if (this.mounted) { + this.setState({ openSourceNotices: text }); + } + + }) + .catch((err) => { + // ok in debug builds. File doesn't get placed until install time. + console.log("Failed to fetch open-source notices. " + err.toString()); + }); + } + } + + componentDidMount() { + super.componentDidMount?.(); + this.mounted = true; + this.updateNotifications(); + this.startFossRequest(); + } + componentWillUnmount() { + super.componentWillUnmount?.(); + this.mounted = false; + this.updateNotifications(); + } + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot: any): void { + super.componentDidUpdate?.(prevProps, prevState, snapshot); + this.updateNotifications(); + } + + handleDialogClose(_e: SyntheticEvent) { + this.props.onClose(); + } + + + render() { + let addressKey = 0; + let serverVersion = this.model.serverVersion?.serverVersion ?? ""; + let nPos = serverVersion.indexOf(' '); + if (nPos !== -1) { + serverVersion = serverVersion.substring(nPos + 1); + } + return ( + { this.props.onClose() }} TransitionComponent={Transition} + onEnterKey={() => { this.props.onClose() }} + style={{ userSelect: "none" }} + > + +
+
+ + + + + + + About + + + +
+
+
+
+ + PiPedal + {serverVersion + + (this.model.serverVersion?.debug ? " (Debug)" : "")} + + + +
+ + Copyright © 2022-2024 Robin Davies. + + {this.model.isAndroidHosted() && ( + + {this.model.getAndroidHostVersion()} + + )} + + {this.model.isAndroidHosted() && ( + + Copyright © 2022-2024 Robin Davies. + + )} + + + ADDRESSES + +
+ { + this.model.serverVersion?.webAddresses.map((address) => + ( + + {address} + + )) + + } +
+ + + + SERVER OS + +
+ + {this.model.serverVersion?.osVersion ?? ""} + +
+
+ + + + LEGAL NOTICES + +
+
+ +
+
+
+
+ + ); + + } + +}; + + +export default AboutDialog; \ No newline at end of file diff --git a/react/src/AlsaDeviceInfo.tsx b/vite/src/pipedal/AlsaDeviceInfo.tsx similarity index 100% rename from react/src/AlsaDeviceInfo.tsx rename to vite/src/pipedal/AlsaDeviceInfo.tsx diff --git a/react/src/AlsaMidiDeviceInfo.tsx b/vite/src/pipedal/AlsaMidiDeviceInfo.tsx similarity index 100% rename from react/src/AlsaMidiDeviceInfo.tsx rename to vite/src/pipedal/AlsaMidiDeviceInfo.tsx diff --git a/react/src/AndroidHost.tsx b/vite/src/pipedal/AndroidHost.tsx similarity index 95% rename from react/src/AndroidHost.tsx rename to vite/src/pipedal/AndroidHost.tsx index b6b0f25..5fe7aa4 100644 --- a/react/src/AndroidHost.tsx +++ b/vite/src/pipedal/AndroidHost.tsx @@ -48,12 +48,12 @@ export class FakeAndroidHost implements AndroidHostInterface isDarkTheme(): boolean { return true; } - setDisconnected(isDisconnected: boolean): void { + setDisconnected(_isDisconnected: boolean): void { } showSponsorship() : void { } - launchExternalUrl(url:string): boolean + launchExternalUrl(_url:string): boolean { return false; } diff --git a/react/src/App.tsx b/vite/src/pipedal/App.tsx similarity index 93% rename from react/src/App.tsx rename to vite/src/pipedal/App.tsx index 6738e2d..cb08a14 100644 --- a/react/src/App.tsx +++ b/vite/src/pipedal/App.tsx @@ -18,7 +18,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React from 'react'; -import { ThemeProvider, createTheme, StyledEngineProvider, Theme } from '@mui/material/styles'; +import { ThemeProvider, createTheme, StyledEngineProvider } from '@mui/material/styles'; +import { CssBaseline } from '@mui/material'; import VirtualKeyboardHandler from './VirtualKeyboardHandler'; import AppThemed from "./AppThemed"; @@ -44,10 +45,10 @@ declare module '@mui/material/Button' { } -declare module '@mui/styles/defaultTheme' { - // eslint-disable-next-line @typescript-eslint/no-empty-interface - interface DefaultTheme extends Theme { } -} +// declare module '@mui/styles/defaultTheme' { +// // eslint-disable-next-line @typescript-eslint/no-empty-interface +// interface DefaultTheme extends Theme { } +// } @@ -55,6 +56,7 @@ declare module '@mui/styles/defaultTheme' { const theme = createTheme( isDarkMode() ? { + cssVariables: true, components: { MuiButton: { styleOverrides: { @@ -101,6 +103,7 @@ const theme = createTheme( } : { + cssVariables: true, components: { /* make the selection state for MuiListItemButtons a smidgen darker (light theme only) */ MuiListItemButton: { @@ -187,6 +190,8 @@ const App = (class extends React.Component { return ( + + diff --git a/react/src/AppThemed.css b/vite/src/pipedal/AppThemed.css similarity index 99% rename from react/src/AppThemed.css rename to vite/src/pipedal/AppThemed.css index e007307..4ea4273 100644 --- a/react/src/AppThemed.css +++ b/vite/src/pipedal/AppThemed.css @@ -16,3 +16,4 @@ div { min-width: 0; } + diff --git a/vite/src/pipedal/AppThemed.tsx b/vite/src/pipedal/AppThemed.tsx new file mode 100644 index 0000000..8461fa9 --- /dev/null +++ b/vite/src/pipedal/AppThemed.tsx @@ -0,0 +1,1158 @@ +// Copyright (c) 2022 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +import './AppThemed.css'; +//import {alpha} from '@mui/material/styles'; +import AppBar from '@mui/material/AppBar'; + +import Modal from '@mui/material/Modal'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import { withStyles } from "tss-react/mui"; +import WithStyles from './WithStyles'; +import { css } from '@emotion/react'; + +import IconButton from '@mui/material/IconButton'; +import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'; +import MenuButton from '@mui/icons-material/Menu'; +import { TemporaryDrawer } from './TemporaryDrawer'; +import FullscreenIcon from '@mui/icons-material/Fullscreen'; +import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'; +import List from '@mui/material/List'; +import Divider from '@mui/material/Divider'; +// import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; + +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import CircularProgress from '@mui/material/CircularProgress'; +import { type OnChangedHandler } from './ObservableProperty'; +import ErrorOutlineIcon from '@mui/icons-material/Error'; +import ResizeResponsiveComponent from './ResizeResponsiveComponent'; +import Button from '@mui/material/Button'; +import PresetSelector from './PresetSelector'; +import SettingsDialog from './SettingsDialog'; +import AboutDialog from './AboutDialog'; +import BankDialog from './BankDialog'; + +import { PiPedalModelFactory, PiPedalModel, State, ZoomedControlInfo, wantsReloadingScreen } from './PiPedalModel'; +import ZoomedUiControl from './ZoomedUiControl' +import MainPage from './MainPage'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogActions from '@mui/material/DialogActions'; +import ListSubheader from '@mui/material/ListSubheader'; +import { BankIndex, BankIndexEntry } from './Banks'; +import RenameDialog from './RenameDialog'; +import JackStatusView from './JackStatusView'; +import { type Theme } from '@mui/material/styles'; +import { isDarkMode } from './DarkMode'; +import UpdateDialog from './UpdateDialog'; + +import RenameOutlineIcon from './svg/drive_file_rename_outline_black_24dp.svg?react'; +import SaveBankAsIcon from './svg/ic_save_bank_as.svg?react'; +import EditBanksIcon from './svg/ic_edit_banks.svg?react'; +import SettingsIcon from './svg/ic_settings.svg?react'; +import HelpOutlineIcon from './svg/ic_help_outline.svg?react'; +import FxAmplifierIcon from './svg/fx_amplifier.svg?react'; +import { PerformanceView } from './PerformanceView'; + +import DialogEx from './DialogEx'; + +import { IDialogStackable, popDialogStack, pushDialogStack } from './DialogStack'; + + + +interface AppProps extends WithStyles { +}; + +const appStyles = ((theme: Theme) => ( + { + root: css({ // :root + colorScheme: (isDarkMode() ? "dark" : "light") + }), + menuListItem: css({ + color: "#FE8!important", //theme.palette.text.primary, + fill: "#FE8!important", //theme.palette.text.primary, + }), + menuIcon: css({ + fill: (theme.palette.text.primary + "!important"), //theme.palette.text.primary, + opacity: 0.6 + }), + toolBar: css({ + color: "white" + }), + listSubheader: css({ + backgroundImage: "linear-gradient(255,255,255,0.15),rgba(255,255,255,0.15)" + }), + loadingContent: css({ + display: "block", + position: "absolute", + minHeight: "10em", + left: "0px", + top: "0px", + width: "100%", + height: "100%", + opacity: "0.95", + justifyContent: "center", + textAlign: "center", + zIndex: 2010 + }), + errorContent: css({ + + display: "flex", + flexDirection: "column", + justifyContent: "center", + flexWrap: "nowrap", + alignItems: "center", + position: "fixed", + minHeight: "10em", + left: "0px", + top: "0px", + width: "100%", + height: "100%", + color: isDarkMode() ? "#CCC" : "#444", + zIndex: 2000 + }), + errorContentMask: css({ + position: "absolute", left: 0, top: 0, + width: "100%", height: "100%", + background: isDarkMode() ? "#121212" : "#BBB", + opacity: 0.95, + zIndex: 1999 + }), + errorText: css({ + marginTop: 0, + padding: 0, + marginBottom: 12, + fontWeight: 500, + fontSize: "13pt", + maxWidth: 250, + opacity: 1, + zIndex: 2010, + }), + progressText: css({ + marginTop: 0, + fontWeight: 500, + fontSize: "13pt", + opacity: 1, + zIndex: 2010, + paddingTop: 12, + gravity: "center", + textAlign: "center" + }), + + errorMessageBox: css({ + flex: "0 0 auto", + width: 300, + marginLeft: "auto", + marginRight: "auto", + zIndex: 2010 + + + }), + errorMessage: css({ + color: theme.palette.text.secondary, + textAlign: "start", + zIndex: 2010 + + }), + loadingBox: css({ + display: "flex", + flexFlow: "column nowrap", + position: "absolute", + top: "20%", + left: 0, right: 0, + color: isDarkMode() ? theme.palette.text.secondary : "#888", + // border: "3px solid #888", + alignItems: "center", + textAlign: "center", + opacity: 0.8, + + }), + loadingBoxItem: css({ + display: "flex", flexFlow: "row nowrap", + alignItems: "center", + textAlign: "center", + + }), + + + toolBarContent: + css({ + position: "absolute", top: 0, width: "100%" + }), + + toolBarSpacer: + css({ + position: "relative", flex: "0 0 auto", + }), + + + mainFrame: css({ + overflow: "hidden", + display: "flex", + flexFlow: "column", + flex: "1 1 100%" + }), + + toolbarSizingPosition: css({ + flexGrow: 0, + flexShrink: 1, + width: "100%", + flexBasis: "auto" + }), + mainSizingPosition: css({ + overflow: "hidden", + flex: "1 1 auto" + }), + + heroContent: css({ + backgroundColor: theme.mainBackground, + position: "relative", + height: "100%", + width: "100%" + }), + + drawerItem: css({ + width: 350, + }), + drawerItemFullWidth: css({ + width: 'auto', + }), + + } +)); + + + + +function supportsFullScreen(): boolean { + let doc: any = window.document; + let docEl: any = doc.documentElement; + var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullscreen || docEl.msRequestFullScreen; + return (!!requestFullScreen); + +} + +function setFullScreen(value: boolean) { + let doc: any = window.document; + let docEl: any = doc.documentElement; + + if (docEl.requestFullscren) // the latest offical api. + { + if (value) { + window.document.documentElement.requestFullscreen({ navigationUI: "show" }); + } else { + window.document.exitFullscreen(); + } + } + + var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullscreen || docEl.msRequestFullScreen; + var cancelFullScreen = docEl.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; + + if (value) { + requestFullScreen.call(docEl); + } + else { + cancelFullScreen.call(doc); + } +} + + +// function preventDefault(e: SyntheticEvent): void { +// e.stopPropagation(); +// e.preventDefault(); +// } + +type AppState = { + zoomedControlInfo: ZoomedControlInfo | undefined; + + isDrawerOpen: boolean; + errorMessage: string; + displayState: State; + + performanceView: boolean; + + canFullScreen: boolean; + isFullScreen: boolean; + tinyToolBar: boolean; + alertDialogOpen: boolean; + alertDialogMessage: string; + updateDialogOpen: boolean; + isSettingsDialogOpen: boolean; + onboarding: boolean; + isDebug: boolean; + + renameBankDialogOpen: boolean; + saveBankAsDialogOpen: boolean; + + aboutDialogOpen: boolean; + bankDialogOpen: boolean; + editBankDialogOpen: boolean; + zoomedControlOpen: boolean; + + + presetName: string; + presetChanged: boolean; + banks: BankIndex; + bankDisplayItems: number; + showStatusMonitor: boolean; +}; +class MenuStackHandler implements IDialogStackable { + constructor(app: AppThemedBase) { + this.app = app; + } + getTag() { + return "Menu"; + } + isOpen(): boolean { + return this.app.state.isDrawerOpen; + } + onDialogStackClose() { + this.app.menuStackHandler = null; + this.app.hideDrawer(); + } + private app: AppThemedBase; +}; + + + +export + class AppThemedBase extends ResizeResponsiveComponent { + // Before the component mounts, we initialise our state + + model_: PiPedalModel; + errorChangeHandler_: OnChangedHandler; + stateChangeHandler_: OnChangedHandler; + + constructor(props: AppProps) { + super(props); + this.model_ = PiPedalModelFactory.getInstance(); + + + this.state = { + performanceView: false, + zoomedControlInfo: this.model_.zoomedUiControl.get(), + isDrawerOpen: false, + errorMessage: this.model_.errorMessage.get(), + displayState: this.model_.state.get(), + canFullScreen: supportsFullScreen() && !this.model_.isAndroidHosted(), + isFullScreen: !!document.fullscreenElement, + tinyToolBar: false, + alertDialogOpen: false, + alertDialogMessage: "", + presetName: this.model_.presets.get().getSelectedText(), + isSettingsDialogOpen: false, + updateDialogOpen: false, + onboarding: false, + isDebug: true, + presetChanged: this.model_.presets.get().presetChanged, + banks: this.model_.banks.get(), + renameBankDialogOpen: false, + saveBankAsDialogOpen: false, + aboutDialogOpen: false, + bankDialogOpen: false, + editBankDialogOpen: false, + zoomedControlOpen: false, + bankDisplayItems: 5, + showStatusMonitor: this.model_.showStatusMonitor.get() + + }; + + this.promptForUpdateHandler = this.promptForUpdateHandler.bind(this); + this.errorChangeHandler_ = this.setErrorMessage.bind(this); + this.beforeUnloadListener = this.beforeUnloadListener.bind(this); + this.unloadListener = this.unloadListener.bind(this); + this.stateChangeHandler_ = this.setDisplayState.bind(this); + this.presetChangedHandler = this.presetChangedHandler.bind(this); + this.alertMessageChangedHandler = this.alertMessageChangedHandler.bind(this); + this.handleCloseAlert = this.handleCloseAlert.bind(this); + this.banksChangedHandler = this.banksChangedHandler.bind(this); + this.showStatusMonitorHandler = this.showStatusMonitorHandler.bind(this); + this.handleZoomedUiControlChanged = this.handleZoomedUiControlChanged.bind(this); + } + + showDrawer() { + if (!this.state.isDrawerOpen) { + this.setState({ isDrawerOpen: true }) + this.pushOpenMenuState(); + } + } + hideDrawer(loadingDialog: boolean = false) { + window.setTimeout(() => { + this.popOpenMenuState(); + this.setState({ isDrawerOpen: false }) + }); + } + + + menuStackHandler: MenuStackHandler | null = null; + + popOpenMenuState() { + if (this.menuStackHandler) { + let t = this.menuStackHandler; + this.menuStackHandler = null; + popDialogStack(t); + } + + } + pushOpenMenuState() { + this.menuStackHandler = new MenuStackHandler(this); + pushDialogStack(this.menuStackHandler); + } + + handleSpecificBank(bankId: number) { + + this.model_.openBank(bankId) + .catch((error) => this.model_.showAlert(error.toString())); + + } + + handleSaveBankAsOk(newName: string) { + let currentName = this.model_.banks.get().getSelectedEntryName(); + if (currentName === newName) { + this.setState({ + renameBankDialogOpen: false, + saveBankAsDialogOpen: false + }); + return; + + } + + if (this.model_.banks.get().nameExists(newName)) { + this.model_.showAlert("A bank by that name already exists."); + return; + } + this.setState({ + renameBankDialogOpen: false, + saveBankAsDialogOpen: false + }); + this.model_.saveBankAs(this.model_.banks.get().selectedBank, newName) + .catch((error) => { + this.model_.showAlert(error.toString()); + }); + + } + handleBankRenameOk(newName: string) { + let currentName = this.model_.banks.get().getSelectedEntryName(); + if (currentName === newName) { + this.setState({ + renameBankDialogOpen: false, + saveBankAsDialogOpen: false + }); + return; + + } + + if (this.model_.banks.get().nameExists(newName)) { + this.model_.showAlert("A bank by that name already exists."); + return; + } + this.setState({ + renameBankDialogOpen: false + }); + this.model_.renameBank(this.model_.banks.get().selectedBank, newName) + .catch((error) => { + this.model_.showAlert(error.toString()); + }); + + } + handleSettingsDialogClose() { + this.setState({ + isSettingsDialogOpen: false, + onboarding: false + }); + } + handleDrawerSettingsClick() { + this.setState({ + isDrawerOpen: false, + isSettingsDialogOpen: true, + onboarding: false + }); + + } + + handleDisplayOnboarding() { + this.setState({ + isDrawerOpen: false, + isSettingsDialogOpen: true, + onboarding: true + }); + + } + + handleDrawerManageBanks() { + this.setState({ + isDrawerOpen: false, + bankDialogOpen: true, + editBankDialogOpen: true + }); + + } + handleDrawerSelectBank() { + + this.setState({ + bankDialogOpen: true, + editBankDialogOpen: false + }); + + } + handleDrawerDonationClick() { + this.hideDrawer(false); + if (this.model_.isAndroidHosted()) { + this.model_.showAndroidDonationActivity(); + } else { + if (window) { + window.open("https://github.com/sponsors/rerdavies", '_blank'); + } + } + + } + + handleDrawerAboutClick() { + this.setState({ + aboutDialogOpen: true + }); + + } + handleDrawerRenameBank() { + this.setState({ + renameBankDialogOpen: true, + saveBankAsDialogOpen: false + }); + + } + handleDrawerSaveBankAs() { + this.setState({ + renameBankDialogOpen: false, + saveBankAsDialogOpen: true + }); + } + + + handleCloseAlert(e?: any, reason?: any) { + this.model_.alertMessage.set(""); + } + showStatusMonitorHandler() { + this.setState({ + showStatusMonitor: this.model_.showStatusMonitor.get() + }); + } + banksChangedHandler() { + this.setState({ + banks: this.model_.banks.get() + }); + } + presetChangedHandler() { + let presets = this.model_.presets.get(); + + this.setState({ + presetName: presets.getSelectedText(), + presetChanged: presets.presetChanged + }); + } + toggleFullScreen(): void { + setFullScreen(!this.state.isFullScreen); + this.setState({ isFullScreen: !this.state.isFullScreen }); + } + + private unloadListener(e: Event) { + this.model_.close(); + return undefined; + } + + private beforeUnloadListener(e: Event) { + this.model_.close(); + return undefined; + } + promptForUpdateHandler(newValue: boolean) { + if (this.model_.enableAutoUpdate) { + if (this.state.updateDialogOpen !== newValue) { + this.setState({ updateDialogOpen: newValue }); + } + } + } + componentDidMount() { + + super.componentDidMount(); + window.addEventListener("beforeunload", this.beforeUnloadListener); + window.addEventListener("unload", this.unloadListener); + + this.model_.errorMessage.addOnChangedHandler(this.errorChangeHandler_); + this.model_.state.addOnChangedHandler(this.stateChangeHandler_); + this.model_.pedalboard.addOnChangedHandler(this.presetChangedHandler); + this.model_.alertMessage.addOnChangedHandler(this.alertMessageChangedHandler); + this.model_.banks.addOnChangedHandler(this.banksChangedHandler); + this.model_.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler); + this.model_.promptForUpdate.addOnChangedHandler(this.promptForUpdateHandler); + this.alertMessageChangedHandler(); + + this.model_.zoomedUiControl.addOnChangedHandler( + this.handleZoomedUiControlChanged + ); + } + handleZoomedUiControlChanged() { + this.setState({ + zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined, + zoomedControlInfo: this.model_.zoomedUiControl.get() + }); + + } + + updateOverscroll(): void { + if (this.model_.serverVersion) { + // no pull-down refresh on android devices once we're ready (unless we're debug) + let preventOverscroll = + this.model_.state.get() === State.Ready + && !this.model_.debug; + + let overscrollBehavior = preventOverscroll ? "none" : "auto"; + document.body.style.overscrollBehavior = overscrollBehavior; + } + } + + componentDidUpdate() { + } + + componentWillUnmount() { + super.componentWillUnmount(); + + this.model_.zoomedUiControl.removeOnChangedHandler( + this.handleZoomedUiControlChanged + ); + + window.removeEventListener("beforeunload", this.beforeUnloadListener); + + this.model_.promptForUpdate.removeOnChangedHandler(this.promptForUpdateHandler); + this.model_.errorMessage.removeOnChangedHandler(this.errorChangeHandler_); + this.model_.state.removeOnChangedHandler(this.stateChangeHandler_); + this.model_.pedalboard.removeOnChangedHandler(this.presetChangedHandler); + this.model_.banks.removeOnChangedHandler(this.banksChangedHandler); + this.model_.banks.removeOnChangedHandler(this.showStatusMonitorHandler); + + this.model_.close(); + + } + + alertMessageChangedHandler() { + let message = this.model_.alertMessage.get(); + if (message === "") { + this.setState({ alertDialogOpen: false }); + // leave the message intact so the dialog can fade. + } else { + this.setState({ + alertDialogOpen: true, + alertDialogMessage: message + }); + } + + } + updateResponsive() { + // functional, but disabled. + // let tinyToolBar_ = this.windowSize.height < 600; + // this.setState({ tinyToolBar: tinyToolBar_ }); + + let height = this.windowSize.height; + + const ENTRY_HEIGHT = 48; + // ENTRY_HEIGHT*6 +K = 727 from observation. + const K = 450; + + let bankEntries = Math.floor((height - K) / ENTRY_HEIGHT); + if (bankEntries < 1) bankEntries = 1; + if (bankEntries > 7) bankEntries = 7; + if (bankEntries === 2) bankEntries = 1; + + this.setState({ bankDisplayItems: bankEntries }); + + } + onWindowSizeChanged(width: number, height: number): void { + super.onWindowSizeChanged(width, height); + this.updateResponsive(); + } + + + setErrorMessage(message: string): void { + this.setState({ errorMessage: message }); + } + + + setDisplayState(newState: State): void { + this.updateOverscroll(); + + this.setState({ + displayState: newState, + canFullScreen: supportsFullScreen() && !this.model_.isAndroidHosted() + }); + if (newState === State.Ready) { + if (this.model_.isOnboarding()) { + this.handleDisplayOnboarding(); + } + } + } + + shortBankList(banks: BankIndex): BankIndexEntry[] { + let nDisplayEntries = this.state.bankDisplayItems; + let entries = banks.entries; + let nListEntries = entries.length; + + let result: BankIndexEntry[] = []; + + if (nListEntries <= nDisplayEntries) { + for (let i = 0; i < nListEntries; ++i) { + result.push(entries[i]); + } + } else { + // a subset of the list CENTERED on the currently selected entry. + let selectedIndex = 0; + for (let i = 0; i < nListEntries; ++i) { + if (entries[i].instanceId === banks.selectedBank) { + selectedIndex = i; + break; + } + } + + let minN = selectedIndex - Math.floor(nDisplayEntries / 2); + if (minN < 0) minN = 0; + let maxN = minN + nDisplayEntries; + if (maxN > entries.length) { + maxN = entries.length; + } + for (let i = minN; i < maxN; ++i) { + result.push(entries[i]); + } + } + return result; + } + handleReload() { + if (this.model_.isAndroidHosted()) { + this.model_.chooseNewDevice(); + } else { + window.location.reload(); + } + } + getReloadingMessage(): string { + switch (this.state.displayState) { + case State.ApplyingChanges: + return "Applying\u00A0changes..."; + case State.ReloadingPlugins: + return "Reloading\u00A0plugins..."; + case State.DownloadingUpdate: + return "Downloading update..."; + case State.InstallingUpdate: + return "Installing update...."; + case State.HotspotChanging: + return "Network connection changing..." + default: + return "Reconnecting..."; + } + } + render() { + + const classes = withStyles.getClasses(this.props); + + let shortBankList = this.shortBankList(this.state.banks); + let showBankSelectDialog = shortBankList.length !== this.state.banks.entries.length; + + + return ( +
{ + if (!this.model_.debug) { + e.preventDefault(); e.stopPropagation(); + } + }} + > + {this.state.performanceView ? ( + { this.setState({ performanceView: false }); }} + /> + ) : ( +
+ + + {(!this.state.tinyToolBar) && !this.state.performanceView ? + ( + + + { this.showDrawer() }} + size="large"> + + +
+ +
+
+ {this.state.canFullScreen && + { this.toggleFullScreen(); }} + color="inherit" + size="large"> + {this.state.isFullScreen ? ( + + ) : ( + + + )} + + + } + + + ) : ( +
+ { this.showDrawer() }} + color="inherit" + size="large"> + + + {this.state.canFullScreen && ( + { this.toggleFullScreen(); }} + size="large"> + {this.state.isFullScreen ? ( + + ) : ( + + + )} + + + )} +
+ )} + { this.hideDrawer(false); }} > + + + { + ev.stopPropagation(); + this.hideDrawer(true); + this.setState({ performanceView: true }); + }}> + + + + + + + + + Banks + + + { + shortBankList.map((bank) => { + return ( + { + ev.stopPropagation(); + this.hideDrawer(false); + this.handleSpecificBank(bank.instanceId); + }} + > + + + + + ); + }) + } + { + showBankSelectDialog && ( + { + ev.stopPropagation(); + this.hideDrawer(true); + this.handleDrawerSelectBank(); + }} + > + + + + + + ) + } + + + + { + ev.stopPropagation(); + this.hideDrawer(true); + this.handleDrawerRenameBank() + }}> + + + + + + { + ev.stopPropagation(); + this.hideDrawer(true); + this.handleDrawerSaveBankAs(); + }} > + + + + + + { + ev.stopPropagation(); + this.hideDrawer(true); + this.handleDrawerManageBanks(); + }}> + + + + + + + + + { + ev.stopPropagation(); + this.hideDrawer(true); + this.handleDrawerSettingsClick() + }}> + + + + + + { + ev.stopPropagation(); + this.hideDrawer(true); + this.handleDrawerAboutClick(); + }}> + + + + + + { + ev.stopPropagation(); + this.handleDrawerDonationClick(); + }}> + + + + + + + + + {!this.state.tinyToolBar && ( + + )} +
+
+
+ {(this.state.displayState !== State.Loading) && + ( + + ) + } +
+ +
+
+ this.setState({ bankDialogOpen: false })} /> + {(this.state.aboutDialogOpen) && + ( + this.setState({ aboutDialogOpen: false })} /> + )} + this.handleSettingsDialogClose()} /> + { + this.setState({ + renameBankDialogOpen: false, + saveBankAsDialogOpen: false + }) + }} + onOk={(text: string) => { + if (this.state.renameBankDialogOpen) { + this.handleBankRenameOk(text); + } else if (this.state.saveBankAsDialogOpen) { + this.handleSaveBankAsOk(text); + } + } + } + /> + + { this.setState({ zoomedControlOpen: false }); }} + onDialogClosed={() => { this.model_.zoomedUiControl.set(undefined); } + } + /> + + {this.state.showStatusMonitor && ()} +
+ ) + } + + + + + + + + { + this.state.alertDialogMessage + } + + + + + + + + + + +
+
+
 
+
+
+ +
+
+

+ Error: {this.state.errorMessage} +

+
+
+ + +
+
+
 
+
+ + + {/* Reloading mask */} + < Modal + open={wantsReloadingScreen(this.state.displayState) || this.state.displayState === State.Loading} + aria-label="loading" + aria-describedby="reloading-modal-description" + > +
+
+
+
+ +
+ + { + this.state.displayState === State.Loading ? + "Loading..." + : this.getReloadingMessage() + } + +
+
+ +
+ + ); + } + }; + + +const AppThemed = withStyles(AppThemedBase, appStyles); +export default AppThemed; diff --git a/react/src/BankDialog.tsx b/vite/src/pipedal/BankDialog.tsx similarity index 96% rename from react/src/BankDialog.tsx rename to vite/src/pipedal/BankDialog.tsx index 197250a..ea12606 100644 --- a/react/src/BankDialog.tsx +++ b/vite/src/pipedal/BankDialog.tsx @@ -27,10 +27,13 @@ import ButtonBase from "@mui/material/ButtonBase"; import Slide, { SlideProps } from '@mui/material/Slide'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; -import createStyles from '@mui/styles/createStyles'; +import {createStyles} from './WithStyles'; + import { Theme } from '@mui/material/styles'; -import { WithStyles, withStyles } from "@mui/styles"; +import { withStyles } from "tss-react/mui"; +import WithStyles from './WithStyles'; + import DraggableGrid, { ScrollDirection } from './DraggableGrid'; import Fade from '@mui/material/Fade'; @@ -48,9 +51,11 @@ import MenuItem from '@mui/material/MenuItem'; import DialogEx from './DialogEx'; import DialogContent from '@mui/material/DialogContent'; import DialogActions from '@mui/material/DialogActions'; -import { ReactComponent as DownloadIcon } from './svg/file_download_black_24dp.svg'; -import { ReactComponent as UploadIcon } from './svg/file_upload_black_24dp.svg'; -import { ReactComponent as BankIcon } from './svg/ic_bank.svg'; +import DownloadIcon from './svg/file_download_black_24dp.svg?react'; +import UploadIcon from './svg/file_upload_black_24dp.svg?react'; +import BankIcon from './svg/ic_bank.svg?react'; +import { css } from '@emotion/react'; + //import PublishIcon from '@mui/icons-material/Publish'; // import FileDownloadIcon from '@mui/icons-material/FileDownload'; // import AppsIcon from '@mui/icons-material/Apps'; @@ -78,24 +83,24 @@ interface BankDialogState { const styles = (theme: Theme) => createStyles({ - listIcon: { + listIcon: css({ width: 24, height: 24, opacity: 0.6, fill: theme.palette.text.primary - }, - dialogAppBar: { + }), + dialogAppBar: css({ position: 'relative', top: 0, left: 0 - }, - dialogActionBar: { + }), + dialogActionBar: css({ position: 'relative', top: 0, left: 0, background: "black" - }, - dialogTitle: { + }), + dialogTitle: css({ marginLeft: theme.spacing(2), flex: 1, - }, - itemFrame: { + }), + itemFrame: css({ display: "flex", flexDirection: "row", flexWrap: "nowrap", @@ -105,18 +110,18 @@ const styles = (theme: Theme) => createStyles({ textAlign: "left", justifyContent: "center", paddingLeft: 8 - }, - iconFrame: { + }), + iconFrame: css({ flex: "0 0 auto", - }, - itemIcon: { + }), + itemIcon: css({ width: 24, height: 24, margin: 12, opacity: 0.6 - }, - itemLabel: { + }), + itemLabel: css({ flex: "1 1 1px", marginLeft: 8 - } + }) }); @@ -128,13 +133,12 @@ const Transition = React.forwardRef(function Transition( }); -const BankDialog = withStyles(styles, { withTheme: true })( - +const BankDialog = withStyles( class extends Component { model: PiPedalModel; - refUpload: React.RefObject; + refUpload: React.RefObject; constructor(props: BankDialogProps) { super(props); @@ -296,7 +300,7 @@ const BankDialog = withStyles(styles, { withTheme: true })( mapElement(el: any): React.ReactNode { let bankEntry = el as BankIndexEntry; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let selectedItem = this.isEditMode() ? this.state.selectedItem : this.state.banks.selectedBank; return (
@@ -403,7 +407,7 @@ const BankDialog = withStyles(styles, { withTheme: true })( render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar; let defaultSelectedIndex = this.getSelectedIndex(); @@ -570,8 +574,8 @@ const BankDialog = withStyles(styles, { withTheme: true })( } - } - + }, + styles ); export default BankDialog; \ No newline at end of file diff --git a/react/src/Banks.tsx b/vite/src/pipedal/Banks.tsx similarity index 100% rename from react/src/Banks.tsx rename to vite/src/pipedal/Banks.tsx diff --git a/vite/src/pipedal/ChannelBindingsHelpDialog.tsx b/vite/src/pipedal/ChannelBindingsHelpDialog.tsx new file mode 100644 index 0000000..0829af4 --- /dev/null +++ b/vite/src/pipedal/ChannelBindingsHelpDialog.tsx @@ -0,0 +1,131 @@ +// Copyright (c) 2022 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import React from 'react'; +import Button from '@mui/material/Button'; +import DialogEx from './DialogEx'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import Typography from '@mui/material/Typography'; +import ResizeResponsiveComponent from './ResizeResponsiveComponent'; +import Divider from '@mui/material/Divider'; +//import TextFieldEx from './TextFieldEx'; + + +export interface ChannelBindingHelpDialogProps { + open: boolean, + onClose: () => void, +}; + +export interface ChannelBindingHelpDialogState { + fullScreen: boolean; +}; + +export default class ChannelBindingHelpDialog extends ResizeResponsiveComponent { + + refText: React.RefObject; + + constructor(props: ChannelBindingHelpDialogProps) { + super(props); + this.state = { + fullScreen: false + }; + this.refText = React.createRef(); + } + mounted: boolean = false; + + + + onWindowSizeChanged(width: number, height: number): void { + this.setState({ fullScreen: height < 200 }) + } + + + componentDidMount() { + super.componentDidMount(); + this.mounted = true; + } + componentWillUnmount() { + super.componentWillUnmount(); + this.mounted = false; + } + + componentDidUpdate() { + } + render() { + let props = this.props; + let { open, onClose } = props; + + const handleClose = () => { + onClose(); + }; + + return ( + { }} + > + + + MIDI Control Binding Priority + +
+

MIDI Channel Filters determine which MIDI messages get sent to a plugin that accepts MIDI messages. Note that the + MIDI Channel Filters do NOT affect system MIDI bindings or control bindings. +

+

+ MIDI message processing occurs in the following order +

+
    +
  • If the message is a Program Change message, the message is first offered to any MIDI plugins in the currently loaded preset (Message Filters permitting). + The message is sent to each MIDI plugin in the current preset that wants it. If any MIDI plugin accepts the program change, + no futher processing occurs. Specifically, the program change will not change the currently selected PiPedal preset. +
    +
  • +
  • + If the message is a Program Change message, and no MIDI plugin has accepted the message, PiPedal selects the PiPedal preset that corresponds + to the requested program. +
    +
  • +
  • + The message is then checked to see if it has been bound to a Pipedal feature using the System Midi Bindings settings. If it has, + Pipedal processes the message, and it is not forwarded to MIDI plugins. +
    +
  • +
  • + Otherwise, the message is sent to each MIDI plugin in the currently loaded Pipedal preset (channel filters permitting).) +
    +
  • +
+
+ +
+ + + + +
+ ); + } +} \ No newline at end of file diff --git a/react/src/ColorDropdownButton.tsx b/vite/src/pipedal/ColorDropdownButton.tsx similarity index 98% rename from react/src/ColorDropdownButton.tsx rename to vite/src/pipedal/ColorDropdownButton.tsx index 3d9dace..64af2dc 100644 --- a/react/src/ColorDropdownButton.tsx +++ b/vite/src/pipedal/ColorDropdownButton.tsx @@ -3,7 +3,7 @@ import ButtonBase from '@mui/material/ButtonBase'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; import { colorKeys, getBackgroundColor } from './MaterialColors'; -import { useTheme } from '@mui/styles'; +import { useTheme } from '@mui/material/styles'; const colors: string[] = colorKeys; diff --git a/react/src/ControlTooltip.tsx b/vite/src/pipedal/ControlTooltip.tsx similarity index 100% rename from react/src/ControlTooltip.tsx rename to vite/src/pipedal/ControlTooltip.tsx diff --git a/react/src/ControlViewFactory.tsx b/vite/src/pipedal/ControlViewFactory.tsx similarity index 100% rename from react/src/ControlViewFactory.tsx rename to vite/src/pipedal/ControlViewFactory.tsx diff --git a/react/src/DarkMode.tsx b/vite/src/pipedal/DarkMode.tsx similarity index 100% rename from react/src/DarkMode.tsx rename to vite/src/pipedal/DarkMode.tsx diff --git a/vite/src/pipedal/DialogEx.tsx b/vite/src/pipedal/DialogEx.tsx new file mode 100644 index 0000000..39de5ae --- /dev/null +++ b/vite/src/pipedal/DialogEx.tsx @@ -0,0 +1,116 @@ +// Copyright (c) 2022 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import React from 'react'; +import { IDialogStackable, pushDialogStack, popDialogStack } from './DialogStack'; +import Dialog, { DialogProps } from '@mui/material/Dialog'; + + +interface DialogExState { +} + + +interface DialogExProps extends DialogProps { + tag: string; + fullwidth?: boolean; + onEnterKey: () => void; +} + +class DialogEx extends React.Component implements IDialogStackable { + constructor(props: DialogExProps) { + super(props); + this.state = { + + }; + } + + mounted: boolean = false; + + hasHooks: boolean = false; + stateWasPopped: boolean = false; + + getTag() { + return this.props.tag; + } + isOpen(): boolean { + return this.props.open; + } + onDialogStackClose() { + this.props.onClose?.({}, "backdropClick"); + } + updateHooks(): void { + let wantHooks = this.mounted && this.props.open; + if (wantHooks !== this.hasHooks) { + this.hasHooks = wantHooks; + + if (this.hasHooks) { + this.stateWasPopped = false; + pushDialogStack(this); + } else { + popDialogStack(this); + } + } + } + + componentDidMount() { + super.componentDidMount?.(); + + this.mounted = true; + this.updateHooks(); + } + componentWillUnmount() { + super.componentWillUnmount?.(); + this.mounted = false; + this.updateHooks(); + } + + componentDidUpdate() { + this.updateHooks(); + } + myOnClose(event: {}, reason: "backdropClick" | "escapeKeyDown") { + if (this.props.onClose) this.props.onClose(event, reason); + } + + handleEnterKey() { + if (this.props.onEnterKey) { + this.props.onEnterKey(); + } + } + onKeyDown(evt: React.KeyboardEvent) { + if (evt.key === 'Enter') { + this.handleEnterKey(); + } + evt.stopPropagation(); + } + render() { + let { tag, onClose, fullWidth, onEnterKey, ...extra } = this.props; + return ( + { this.myOnClose(event, reason); }} + onKeyDown={(evt) => { this.onKeyDown(evt); }} + > + {this.props.children} + + ); + } + +}; + +export default DialogEx; \ No newline at end of file diff --git a/vite/src/pipedal/DialogStack.tsx b/vite/src/pipedal/DialogStack.tsx new file mode 100644 index 0000000..80b3a1d --- /dev/null +++ b/vite/src/pipedal/DialogStack.tsx @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2025 Robin E. R. Davies + * All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +export interface IDialogStackable { + getTag: () => string; + isOpen: () => boolean; + onDialogStackClose(): void; +} + +interface DialogStackEntry { + dialog: IDialogStackable | null; + historyState: DialogStackHistoryState +}; + +let dialogStack: DialogStackEntry[] = []; + + +const DLG_STATE_MAGIC = "DlgState438327" +export interface DialogStackHistoryState { + magic: string; + topmost: boolean; + stackDepth: number; + tag: string; + id: string; + previousState: DialogStackHistoryState | null; +}; + +export function pushDialogStack(dialog: IDialogStackable): void { + let stackId = Math.random().toString(36).substring(7); + let dialogStackState: DialogStackHistoryState = { + magic: DLG_STATE_MAGIC, + topmost: false, + stackDepth: dialogStack.length + 1, + tag: dialog.getTag(), + id: stackId, previousState: + window.history.state as DialogStackHistoryState | null + }; + window.history.replaceState( + dialogStackState, + "", + "#" + dialog.getTag()); + + // refresh reloads the page. + let topState: DialogStackHistoryState = { + topmost: true, + magic: DLG_STATE_MAGIC, + previousState: dialogStackState, + tag: dialog.getTag(), + id: stackId, + stackDepth: dialogStack.length + 1 + }; + + dialogStack.push( + { + dialog: dialog, + historyState: dialogStackState + }); + + console.log("pushDialogStack " + stackId + " #" + dialog.getTag()); + window.history.pushState( + topState, + "", + "#" + dialog.getTag()); + + if (window.history.state.id != stackId) + { + console.log("pushDialogStack: nav deferred."); + } + if (!gWatchingHistory) { + gWatchingHistory = true; + window.addEventListener("popstate", handlePopState); + } +} + +let gWatchingHistory = false; +let gPopFromBack = true; + +let handlePopState = (ev: PopStateEvent) => { + let evTag: DialogStackHistoryState | null = ev.state as DialogStackHistoryState | null; + if (!evTag) return; + if (evTag.magic !== DLG_STATE_MAGIC) return; + + ev.stopPropagation(); + + console.log("handlePopState: " + evTag?.tag + " " + evTag?.id + " " + gPopFromBack); + let id = evTag.id; + let ix = dialogStack.length-1; + while (ix >= 0) { + let entry = dialogStack[ix]; + if (entry.historyState.id === id) { + break; + } + ix--; + } + if (ix < 0) { + // user must have navigated directly. + // so just reload the page. + dialogStack = []; + window.location.reload(); + ev.stopPropagation(); + return; + } + + while (dialogStack.length > ix) { + let topEntry = dialogStack[dialogStack.length - 1]; + if (topEntry.dialog) { + try { + + if (topEntry.dialog.isOpen()) { + topEntry.dialog.onDialogStackClose(); + } + } catch (_e) { + + } + topEntry.dialog = null; + } + dialogStack.pop(); + } + let previousState = evTag.previousState; + if (previousState) + { + let newState = {...previousState, topmost: true}; + window.history.replaceState(newState,"","#"+previousState.tag); + } else { + window.history.replaceState(null,"",window.location.pathname); + } + + if (dialogStack.length != 0 && gPopFromBack) { + if (dialogStack[dialogStack.length -1].dialog === null) + { + if (window.history.state != null) + { + console.log("handlePopState: nav back"); + window.setTimeout(() => { window.history.back(); }, 0); + console.log("handlePopState: id= "+ (window.history?.state?.id??"null")); + } else { + dialogStack.pop(); + } + } + } + ev.stopPropagation(); +} + + +export function popDialogStack(dialog: IDialogStackable): void { + let ix = dialogStack.length-1; + + while (ix >= 0) { + let entry = dialogStack[ix]; + if (entry.dialog === dialog) { + console.log("popDialogStack " + entry.historyState.id + " #" + dialog.getTag()); + break; + } + ix--; + } + if (ix < 0) + { + return; + } + dialogStack[ix].dialog = null; + if (dialogStack.length != 0 && dialogStack[dialogStack.length - 1].dialog === null) + { + console.log("popDialogStack nav back: " + dialogStack[ix].historyState.id ); + window.history.back(); + } +} + + + + + + + + + +// Close all dialogs higher in the dialog stack than "tag". +export function removeDialogStackEntriesAbove(tag: string) { + while (dialogStack.length > 0) { + let entry = dialogStack[dialogStack.length - 1]; + if (entry.dialog && entry.dialog.getTag() === tag) { + return; + } + + dialogStack.pop(); + + if (entry.dialog && entry.dialog.isOpen()) { + entry.dialog.onDialogStackClose(); + } + } +} + diff --git a/react/src/Draggable.tsx b/vite/src/pipedal/Draggable.tsx similarity index 97% rename from react/src/Draggable.tsx rename to vite/src/pipedal/Draggable.tsx index 81751dc..ca2931e 100644 --- a/react/src/Draggable.tsx +++ b/vite/src/pipedal/Draggable.tsx @@ -19,10 +19,12 @@ import { MouseEvent, PointerEvent, ReactNode, Component } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalStateError } from './PiPedalError'; +import { css } from '@emotion/react'; const SELECT_SCALE = 1.5; @@ -35,14 +37,14 @@ const AUTOSCROLL_SCROLL_RATE = AUTOSCROLL_SCROLL_PER_SECOND * AUTOSCROLL_TICK_DE const LONG_PRESS_TIME_MS = 250; const styles = (theme: Theme) => createStyles({ - frame: { + frame: css({ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", position: "relative" - } + }) }); type Point = { x: number, y: number }; @@ -64,7 +66,7 @@ type DraggableState = { const Draggable = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { @@ -158,7 +160,7 @@ const Draggable = e.preventDefault(); e.stopPropagation(); } - longPressTimer?: NodeJS.Timeout = undefined; + longPressTimer?: number = undefined; longPressTimerTick() { if (this.pointerType === "touch") { @@ -482,8 +484,9 @@ const Draggable = render() { + const classes = withStyles.getClasses(this.props); return ( -
createStyles({ - frame: { - position: "relative", - margin: "12px", - background: theme.palette.background.paper - } -}); type Point = { x: number, y: number }; @@ -113,8 +103,8 @@ const DraggableGrid = ( class extends Component { - refScrollContainer: React.RefObject; - refGrid: React.RefObject; + refScrollContainer: React.RefObject; + refGrid: React.RefObject; constructor(props: DraggableGridProps) { super(props); @@ -653,7 +643,7 @@ const DraggableGrid = itemOffsetX: number = 0; itemOffsetY: number = 0; - longPressTimer?: NodeJS.Timeout = undefined; + longPressTimer?: number = undefined; longPressTimerTick() { @@ -792,7 +782,7 @@ const DraggableGrid = } } } - autoScrollTimer?: NodeJS.Timeout; + autoScrollTimer?: number; getElementKeys(): any[] { diff --git a/vite/src/pipedal/FilePropertyControl.tsx b/vite/src/pipedal/FilePropertyControl.tsx new file mode 100644 index 0000000..04a4f49 --- /dev/null +++ b/vite/src/pipedal/FilePropertyControl.tsx @@ -0,0 +1,268 @@ +/* + * MIT License + * + * Copyright (c) 2023 Robin E. R. Davies + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { Component, SyntheticEvent } from 'react'; +import { Theme } from '@mui/material/styles'; +import { css } from '@emotion/react'; + +import { UiFileProperty } from './Lv2Plugin'; +import Typography from '@mui/material/Typography'; +import { PiPedalModel, PiPedalModelFactory, ListenHandle, State } from './PiPedalModel'; +import ButtonBase from '@mui/material/ButtonBase' +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import { PedalboardItem } from './Pedalboard'; +import { isDarkMode } from './DarkMode'; +import Tooltip from "@mui/material/Tooltip" + +export const StandardItemSize = { width: 80, height: 140 } + + +import { withStyles } from "tss-react/mui"; +import WithStyles from './WithStyles'; +import { withTheme } from './WithStyles'; + +const styles = (theme: Theme) => { + return { + frame: css({ + position: "relative", + margin: "12px" + }), + switchTrack: css({ + height: '100%', + width: '100%', + borderRadius: 14 / 2, + zIndex: -1, + transition: theme.transitions.create(['opacity', 'background-color'], { + duration: theme.transitions.duration.shortest + }), + backgroundColor: theme.palette.secondary.main, + opacity: theme.palette.mode === 'light' ? 0.38 : 0.3 + }), + controlFrame: css({ + display: "flex", flexDirection: "column", alignItems: "start", justifyContent: "space-between", height: 116 + }), + displayValue: css({ + position: "absolute", + top: 0, + left: 0, + right: 0, + bottom: 4, + textAlign: "center", + background: theme.mainBackground, + color: theme.palette.text.secondary, + // zIndex: -1, + }), + titleSection: css({ + flex: "0 0 auto", alignSelf: "stretch", marginBottom: 8, marginLeft: 8, marginRight: 0 + }), + midSection: css({ + flex: "1 1 1", display: "flex", flexFlow: "column nowrap", alignContent: "center", justifyContent: "center" + }), + editSection: css({ + flex: "0 0 0", position: "relative", width: 60, height: 28, minHeight: 28 + }) + } +} + ; + + +export interface FilePropertyControlProps extends WithStyles { + fileProperty: UiFileProperty; + pedalboardItem: PedalboardItem; + onFileClick: (fileProperty: UiFileProperty, value: string) => void; + theme: Theme; +} +type FilePropertyControlState = { + error: boolean; + hasValue: boolean; + value: string; +}; + +const FilePropertyControl = + withTheme(withStyles( + class extends Component { + + model: PiPedalModel; + + + constructor(props: FilePropertyControlProps) { + super(props); + + this.state = { + error: false, + hasValue: false, + value: "", + }; + this.model = PiPedalModelFactory.getInstance(); + this.onStateChanged = this.onStateChanged.bind(this); + } + + + onStateChanged(state: State) { + if (this.mounted) { + if (state === State.Ready) { + this.subscribeToPatchProperty(); + } + } + } + // private propertyGetHandle?: ListenHandle; + + monitorPropertyHandle?: ListenHandle; + + subscribeToPatchProperty() { + // this.propertyGetHandle = this.model.listenForAtomOutput(this.props.instanceId,(instanceId,atomOutput)=>{ + // // PARSE THE OBJECT! + // // this.setState({value: atomOutput?.value as string}); + // }); + this.unsubscribeToPatchProperty(); + this.monitorPropertyHandle = this.model.monitorPatchProperty(this.props.pedalboardItem.instanceId, + this.props.fileProperty.patchProperty, + (instanceId, property, propertyValue) => { + if (propertyValue.otype_ === "Path") { + this.setState({ value: propertyValue.value as string, hasValue: true }); + } + } + ); + + } + unsubscribeToPatchProperty() { + if (this.monitorPropertyHandle !== undefined) { + this.model.cancelMonitorPatchProperty(this.monitorPropertyHandle); + this.monitorPropertyHandle = undefined; + } + + } + private mounted: boolean = false; + componentDidMount() { + this.model.state.addOnChangedHandler(this.onStateChanged); + this.mounted = true; + this.subscribeToPatchProperty(); + } + componentWillUnmount() { + this.unsubscribeToPatchProperty(); + this.mounted = false; + this.model.state.removeOnChangedHandler(this.onStateChanged); + } + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { + if (prevProps.fileProperty.patchProperty !== this.props.fileProperty.patchProperty + || prevProps.pedalboardItem.instanceId !== this.props.pedalboardItem.instanceId + || prevProps.pedalboardItem.stateUpdateCount !== this.props.pedalboardItem.stateUpdateCount + + ) { + this.unsubscribeToPatchProperty(); + this.subscribeToPatchProperty(); + } + + } + + + inputChanged: boolean = false; + + onDrag(e: SyntheticEvent) { + e.preventDefault(); + } + + onFileClick() { + this.props.onFileClick(this.props.fileProperty, this.state.value); + } + private fileNameOnly(path: string): string { + let slashPos = path.lastIndexOf('/'); + if (slashPos < 0) { + slashPos = 0; + } else { + ++slashPos; + } + let extPos = path.lastIndexOf('.'); + if (extPos < 0 || extPos < slashPos) { + extPos = path.length; + } + + return path.substring(slashPos, extPos); + + } + render() { + //const classes = withStyles.getClasses(this.props); + + const classes = withStyles.getClasses(this.props); + + let fileProperty = this.props.fileProperty; + + let value = "\u00A0"; + if (this.state.hasValue) { + if (this.state.value.length === 0) { + value = ""; + } else { + value = this.fileNameOnly(this.state.value); + } + } + + let item_width = 264; + + return ( +
+ {/* TITLE SECTION */} +
+ + {fileProperty.label} + +
+ {/* CONTROL SECTION */} + +
+ + { this.onFileClick() }} > +
+
+ {value} + +
+
 
+
+
+
+ + {/* LABEL/EDIT SECTION*/} +
+
+
+ ); + } + } + ,styles) +) + ; +export default FilePropertyControl; \ No newline at end of file diff --git a/react/src/FilePropertyDialog.tsx b/vite/src/pipedal/FilePropertyDialog.tsx similarity index 99% rename from react/src/FilePropertyDialog.tsx rename to vite/src/pipedal/FilePropertyDialog.tsx index 3f46507..4a02bd7 100644 --- a/react/src/FilePropertyDialog.tsx +++ b/vite/src/pipedal/FilePropertyDialog.tsx @@ -19,7 +19,8 @@ import React from 'react'; -import createStyles from '@mui/styles/createStyles'; +import {createStyles} from './WithStyles'; + import { Theme } from '@mui/material/styles'; import CreateNewFolderIcon from '@mui/icons-material/CreateNewFolder'; @@ -43,7 +44,9 @@ import DialogContent from '@mui/material/DialogContent'; import IconButton from '@mui/material/IconButton'; import OldDeleteIcon from './OldDeleteIcon'; import Toolbar from '@mui/material/Toolbar'; -import { withStyles, WithStyles } from '@mui/styles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; + import ResizeResponsiveComponent from './ResizeResponsiveComponent'; @@ -175,7 +178,7 @@ export function pathFileName(path: string): string { } -export default withStyles(styles, { withTheme: true })( +export default withStyles( class FilePropertyDialog extends ResizeResponsiveComponent { getNavDirectoryFromFile(selectedFile: string, fileProperty: UiFileProperty): string { @@ -448,7 +451,6 @@ export default withStyles(styles, { withTheme: true })( } } - private wantsScrollRef: boolean = true; navigate(relativeDirectory: string) { this.requestFiles(relativeDirectory); @@ -570,7 +572,7 @@ export default withStyles(styles, { withTheme: true })( } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let columnWidth = this.state.columnWidth; let okButtonText = "Select"; if (this.state.hasSelection && this.state.selectedFileIsDirectory) { @@ -908,4 +910,5 @@ export default withStyles(styles, { withTheme: true })( } ); } - }); \ No newline at end of file + }, + styles); \ No newline at end of file diff --git a/react/src/FilePropertyDirectorySelectDialog.tsx b/vite/src/pipedal/FilePropertyDirectorySelectDialog.tsx similarity index 99% rename from react/src/FilePropertyDirectorySelectDialog.tsx rename to vite/src/pipedal/FilePropertyDirectorySelectDialog.tsx index 3954fcf..e923223 100644 --- a/react/src/FilePropertyDirectorySelectDialog.tsx +++ b/vite/src/pipedal/FilePropertyDirectorySelectDialog.tsx @@ -17,7 +17,6 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -import React from 'react'; import Button from '@mui/material/Button'; import DialogEx from './DialogEx'; import DialogActions from '@mui/material/DialogActions'; @@ -143,7 +142,6 @@ export interface FilePropertyDirectorySelectDialogState { export default class FilePropertyDirectorySelectDialog extends ResizeResponsiveComponent { private model: PiPedalModel; - private refSelected: React.Ref; constructor(props: FilePropertyDirectorySelectDialogProps) { super(props); @@ -157,7 +155,6 @@ export default class FilePropertyDirectorySelectDialog extends ResizeResponsiveC directoryTreeInvalidatecount: 0 }; - this.refSelected = React.createRef(); this.requestDirectoryTree(); } diff --git a/react/src/FilePropertyDirectoryTree.tsx b/vite/src/pipedal/FilePropertyDirectoryTree.tsx similarity index 100% rename from react/src/FilePropertyDirectoryTree.tsx rename to vite/src/pipedal/FilePropertyDirectoryTree.tsx diff --git a/react/src/FullScreenIME.tsx b/vite/src/pipedal/FullScreenIME.tsx similarity index 95% rename from react/src/FullScreenIME.tsx rename to vite/src/pipedal/FullScreenIME.tsx index f3ec69a..a9b3930 100644 --- a/react/src/FullScreenIME.tsx +++ b/vite/src/pipedal/FullScreenIME.tsx @@ -19,9 +19,10 @@ import React, { ReactNode } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles, {withTheme} from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import { UiControl } from './Lv2Plugin'; @@ -29,10 +30,10 @@ import DialogEx from './DialogEx'; import Input from '@mui/material/Input'; import { nullCast } from './Utility'; import Typography from '@mui/material/Typography'; - +import { css } from '@emotion/react'; const styles = (theme: Theme) => createStyles({ - frame: { + frame: css({ display: "block", position: "relative", flexDirection: "row", @@ -42,7 +43,7 @@ const styles = (theme: Theme) => createStyles({ height: "100%", overflowX: "auto", overflowY: "auto" - }, + }), }); @@ -60,11 +61,11 @@ type FullScreenIMEState = { }; const FullScreenIME = - withStyles(styles, { withTheme: true })( + withTheme(withStyles( class extends ResizeResponsiveComponent { model: PiPedalModel; - inputRef: React.RefObject; + inputRef: React.RefObject; constructor(props: FullScreenIMEProps) { super(props); @@ -187,7 +188,7 @@ const FullScreenIME = currentWidth?: number; render(): ReactNode { - //let classes = this.props.classes; + //const classes = withStyles.getClasses(this.props); let control = this.props.uiControl; if (!control) { this.currentWidth = undefined; @@ -233,8 +234,9 @@ const FullScreenIME = } - } - ); + }, + styles + )); export default FullScreenIME; diff --git a/react/src/GovernorSettings.tsx b/vite/src/pipedal/GovernorSettings.tsx similarity index 100% rename from react/src/GovernorSettings.tsx rename to vite/src/pipedal/GovernorSettings.tsx diff --git a/react/src/GxTunerControl.tsx b/vite/src/pipedal/GxTunerControl.tsx similarity index 93% rename from react/src/GxTunerControl.tsx rename to vite/src/pipedal/GxTunerControl.tsx index 9f997f2..e73e127 100644 --- a/react/src/GxTunerControl.tsx +++ b/vite/src/pipedal/GxTunerControl.tsx @@ -19,9 +19,10 @@ import React, {Component} from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { MonitorPortHandle, PiPedalModel, State, PiPedalModelFactory } from "./PiPedalModel"; import SvgPathBuilder from './SvgPathBuilder' import {isDarkMode} from './DarkMode'; @@ -62,8 +63,8 @@ interface PitchInfo { const FLAT = "\u{266d}"; const SHARP = "#"; -const DOUBLE_FLAT = "\uD834\uDD2B"; -const DOUBLE_SHARP = "\uD834\uDD2A"; +// const DOUBLE_FLAT = "\uD834\uDD2B"; +// const DOUBLE_SHARP = "\uD834\uDD2A"; const HALF_FLAT = "\uD834\uDD33"; const HALF_SHARP = "\uD834\uDD32"; @@ -78,16 +79,15 @@ const NOTES_24TET: string[] = [ "C","C"+HALF_SHARP, "C" + SHARP,"D"+HALF_FLAT, "D", "D"+HALF_SHARP, "E"+FLAT, "E"+HALF_FLAT, "E","E"+HALF_SHARP,"F", "F"+HALF_SHARP, "F" + SHARP, "G"+HALF_FLAT,"G","G"+HALF_SHARP, "A"+FLAT,"A"+HALF_FLAT,"A","A"+HALF_SHARP,"B"+FLAT,"B"+HALF_FLAT,"B","C"+HALF_FLAT ]; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const NOTES_31TET: string[] = [ - "C", "D"+DOUBLE_FLAT, "C" + SHARP,"Db","C"+DOUBLE_SHARP,"D","E"+DOUBLE_FLAT,"D" + SHARP,"Eb","D"+DOUBLE_SHARP,"E",'Fb","E" + SHARP,"F","G"+DOUBLE_FLAT,"F" + SHARP,"Gb""Fx","G","A"+DOUBLE_FLAT,"G"+DOUBLE_SHARP,"A"' -]; +// // eslint-disable-next-line @typescript-eslint/no-unused-vars +// const NOTES_31TET: string[] = [ +// "C", "D"+DOUBLE_FLAT, "C" + SHARP,"Db","C"+DOUBLE_SHARP,"D","E"+DOUBLE_FLAT,"D" + SHARP,"Eb","D"+DOUBLE_SHARP,"E",'Fb","E" + SHARP,"F","G"+DOUBLE_FLAT,"F" + SHARP,"Gb""Fx","G","A"+DOUBLE_FLAT,"G"+DOUBLE_SHARP,"A"' +// ]; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const NOTES_53TET = ["la","laa","lo","law","ta","teh","te","tu","tuh","ti","tih","to","taw","da","do","di","daw","ro","rih","ra","ru","ruh","reh","re ","ri","raw","ma","meh","me","mu","muh","mi","maa","mo","maw","fe","fa","fih","fu","fuh","fi","se","suh","su","sih","sol","si","saw","lo","leh","le","lu","luh"]; +// // eslint-disable-next-line @typescript-eslint/no-unused-vars +// const NOTES_53TET = ["la","laa","lo","law","ta","teh","te","tu","tuh","ti","tih","to","taw","da","do","di","daw","ro","rih","ra","ru","ruh","reh","re ","ri","raw","ma","meh","me","mu","muh","mi","maa","mo","maw","fe","fa","fih","fu","fuh","fi","se","suh","su","sih","sol","si","saw","lo","leh","le","lu","luh"]; interface GxTunerControlProps extends WithStyles { - theme: Theme; instanceId: number; valueIsMidi: boolean; symbolName?: string; @@ -100,13 +100,13 @@ type GxTunerControlState = { }; const GxTunerControl = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { model: PiPedalModel; - refRoot: React.RefObject; + refRoot: React.RefObject; constructor(props: GxTunerControlProps) { super(props); @@ -236,7 +236,7 @@ const GxTunerControl = } } - animationTimer?: NodeJS.Timeout = undefined; + animationTimer?: number = undefined; cancelAnimationTimer(): void { if (this.animationTimer) @@ -430,7 +430,8 @@ const GxTunerControl = { this.renderDial(this.state.pitchInfo) }
); } - } + }, + styles ); diff --git a/react/src/GxTunerView.tsx b/vite/src/pipedal/GxTunerView.tsx similarity index 95% rename from react/src/GxTunerView.tsx rename to vite/src/pipedal/GxTunerView.tsx index 0f313cd..c4c9b7e 100644 --- a/react/src/GxTunerView.tsx +++ b/vite/src/pipedal/GxTunerView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import IControlViewFactory from './IControlViewFactory'; import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel"; @@ -49,7 +50,7 @@ interface GxTunerState { } const GxTunerView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component implements ControlViewCustomization { @@ -107,7 +108,8 @@ const GxTunerView = customizationId={this.customizationId} />); } - } + }, + styles ); diff --git a/react/src/IControlViewFactory.tsx b/vite/src/pipedal/IControlViewFactory.tsx similarity index 100% rename from react/src/IControlViewFactory.tsx rename to vite/src/pipedal/IControlViewFactory.tsx diff --git a/react/src/Jack.tsx b/vite/src/pipedal/Jack.tsx similarity index 100% rename from react/src/Jack.tsx rename to vite/src/pipedal/Jack.tsx diff --git a/react/src/JackHostStatus.tsx b/vite/src/pipedal/JackHostStatus.tsx similarity index 100% rename from react/src/JackHostStatus.tsx rename to vite/src/pipedal/JackHostStatus.tsx diff --git a/react/src/JackServerSettings.tsx b/vite/src/pipedal/JackServerSettings.tsx similarity index 100% rename from react/src/JackServerSettings.tsx rename to vite/src/pipedal/JackServerSettings.tsx diff --git a/react/src/JackServerSettingsDialog.tsx b/vite/src/pipedal/JackServerSettingsDialog.tsx similarity index 98% rename from react/src/JackServerSettingsDialog.tsx rename to vite/src/pipedal/JackServerSettingsDialog.tsx index aa9530d..b933d79 100644 --- a/react/src/JackServerSettingsDialog.tsx +++ b/vite/src/pipedal/JackServerSettingsDialog.tsx @@ -18,13 +18,14 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -import React, { Component } from 'react'; +import { Component } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import Button from '@mui/material/Button'; import DialogActions from '@mui/material/DialogActions'; @@ -220,7 +221,7 @@ function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaD } -const JackServerSettingsDialog = withStyles(styles)( +const JackServerSettingsDialog = withStyles( class extends Component { model: PiPedalModel; @@ -400,7 +401,7 @@ const JackServerSettingsDialog = withStyles(styles)( }; render() { - const classes = this.props.classes; + const classes = withStyles.getClasses(this.props); const { onClose, open } = this.props; @@ -541,6 +542,7 @@ const JackServerSettingsDialog = withStyles(styles)( ); } - }); + }, + styles); export default JackServerSettingsDialog; diff --git a/react/src/JackStatusView.tsx b/vite/src/pipedal/JackStatusView.tsx similarity index 98% rename from react/src/JackStatusView.tsx rename to vite/src/pipedal/JackStatusView.tsx index b8a40fa..b47d96a 100644 --- a/react/src/JackStatusView.tsx +++ b/vite/src/pipedal/JackStatusView.tsx @@ -56,7 +56,7 @@ export default class JackStatusView extends React.Component void; @@ -60,14 +63,14 @@ const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter"; const pluginGridStyles = (theme: Theme) => createStyles({ - frame: { + frame: css({ position: "relative", width: "100%", height: "100%" - }, + }), - top: { + top: css({ top: "0px", right: "0px", left: "0px", @@ -76,8 +79,8 @@ const pluginGridStyles = (theme: Theme) => createStyles({ flexGrow: 1, overflowX: "hidden", overflowY: "visible" - }, - bottom: { + }), + bottom: css({ position: "relative", bottom: "0px", height: "64px", @@ -89,20 +92,20 @@ const pluginGridStyles = (theme: Theme) => createStyles({ paddingLeft: "24px", paddingRight: "48px", - }, - paper: { + }), + paper: css({ position: "relative", overflow: "hidden", height: "56px", width: "100%", background: theme.palette.background.paper - }, - buttonBase: { + }), + buttonBase: css({ width: "100%", height: "100%'" - }, - content: { + }), + content: css({ marginTop: "4px", marginBottom: "4px", marginLeft: "8px", @@ -116,42 +119,42 @@ const pluginGridStyles = (theme: Theme) => createStyles({ height: 48, alignItems: "start", flexWrap: "nowrap" - }, - content2: { + }), + content2: css({ display: "flex", flexDirection: "column", flex: "1 1 auto", width: "100%", paddingLeft: 12, whiteSpace: "nowrap", textOverflow: "ellipsis" - }, - favoriteDecoration: { + }), + favoriteDecoration: css({ flex: "0 0 auto" - }, - table: { + }), + table: css({ borderCollapse: "collapse", - }, - icon: { + }), + icon: css({ width: "24px", height: "24px", margin: "0px", opacity: "0.6" - }, - iconBorder: { + }), + iconBorder: css({ flex: "0 0 auto", paddingTop: "4px" - }, - label: { + }), + label: css({ width: "100%" - }, - label2: { + }), + label2: css({ marginTop: 8, color: theme.palette.text.secondary - }, - control: { + }), + control: css({ padding: theme.spacing(1), - }, - tdText: { + }), + tdText: css({ padding: "0px" - } + }) }) ; @@ -183,10 +186,11 @@ type PluginGridState = { export const LoadPluginDialog = - withStyles(pluginGridStyles, { withTheme: true })( + withTheme( + withStyles( class extends ResizeResponsiveComponent { model: PiPedalModel; - searchInputRef: React.RefObject; + searchInputRef: React.RefObject; constructor(props: PluginGridProps) { super(props); @@ -373,13 +377,9 @@ export const LoadPluginDialog = handleCancel(e: SyntheticEvent): void { - e.preventDefault(); this.cancel(); } handleOk(e?: SyntheticEvent): void { - if (e) { - e.preventDefault(); - } let selectedPlugin: UiPlugin | undefined = undefined; if (this.state.selected_uri) { @@ -564,7 +564,7 @@ export const LoadPluginDialog = } changedSearchString?: string = undefined; - hSearchTimeout?: NodeJS.Timeout; + hSearchTimeout?: number; private scrollToRequested = false; requestScrollTo() { @@ -637,7 +637,7 @@ export const LoadPluginDialog = } let value = gridItems[item]; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let isFavorite: boolean = this.state.favoritesList[value.uri] ?? false; return ( @@ -690,7 +690,7 @@ export const LoadPluginDialog = render() { - const { classes } = this.props; + const classes= withStyles.getClasses(this.props); let selectedPlugin: UiPlugin | undefined = undefined; if (this.state.selected_uri) { @@ -911,6 +911,8 @@ export const LoadPluginDialog = ); } } - ); + , + pluginGridStyles + )); export default LoadPluginDialog; diff --git a/react/src/Lv2Plugin.tsx b/vite/src/pipedal/Lv2Plugin.tsx similarity index 100% rename from react/src/Lv2Plugin.tsx rename to vite/src/pipedal/Lv2Plugin.tsx diff --git a/react/src/MainPage.tsx b/vite/src/pipedal/MainPage.tsx similarity index 94% rename from react/src/MainPage.tsx rename to vite/src/pipedal/MainPage.tsx index 1fc1607..6a41c96 100644 --- a/react/src/MainPage.tsx +++ b/vite/src/pipedal/MainPage.tsx @@ -19,9 +19,9 @@ import { SyntheticEvent } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles, {withTheme} from './WithStyles'; +import { withStyles } from "tss-react/mui"; + import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { Pedalboard, PedalboardItem, PedalboardSplitItem, SplitType @@ -45,18 +45,19 @@ import PluginInfoDialog from './PluginInfoDialog'; import { GetControlView } from './ControlViewFactory'; import MidiBindingsDialog from './MidiBindingsDialog'; import PluginPresetSelector from './PluginPresetSelector'; -import { ReactComponent as OldDeleteIcon } from "./svg/old_delete_outline_24dp.svg"; -import { ReactComponent as MidiIcon } from "./svg/ic_midi.svg"; +import OldDeleteIcon from "./svg/old_delete_outline_24dp.svg?react"; +import MidiIcon from "./svg/ic_midi.svg?react"; import { isDarkMode } from './DarkMode'; -import { ReactComponent as Snapshot0Icon } from "./svg/snapshot_0.svg"; -import { ReactComponent as Snapshot1Icon } from "./svg/snapshot_1.svg"; -import { ReactComponent as Snapshot2Icon } from "./svg/snapshot_2.svg"; -import { ReactComponent as Snapshot3Icon } from "./svg/snapshot_3.svg"; -import { ReactComponent as Snapshot4Icon } from "./svg/snapshot_4.svg"; -import { ReactComponent as Snapshot5Icon } from "./svg/snapshot_5.svg"; -import { ReactComponent as Snapshot6Icon } from "./svg/snapshot_6.svg"; +import Snapshot0Icon from "./svg/snapshot_0.svg?react"; +import Snapshot1Icon from "./svg/snapshot_1.svg?react"; +import Snapshot2Icon from "./svg/snapshot_2.svg?react"; +import Snapshot3Icon from "./svg/snapshot_3.svg?react"; +import Snapshot4Icon from "./svg/snapshot_4.svg?react"; +import Snapshot5Icon from "./svg/snapshot_5.svg?react"; +import Snapshot6Icon from "./svg/snapshot_6.svg?react"; import SnapshotDialog from './SnapshotDialog'; +import { css } from '@emotion/react'; const SPLIT_CONTROLBAR_THRESHHOLD = 750; @@ -65,41 +66,42 @@ const DISPLAY_AUTHOR_SPLIT_THRESHOLD = 500; const HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK = 500; // eslint-disable-next-line @typescript-eslint/no-unused-vars -const HORIZONTAL_LAYOUT_MQ = "@media (max-height: " + HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK + "px)"; +// const HORIZONTAL_LAYOUT_MQ = "@media (max-height: " + HORIZONTAL_CONTROL_SCROLL_HEIGHT_BREAK + "px)"; -const styles = ({ palette }: Theme) => createStyles({ - frame: { +const styles = ({ palette }: Theme) => { return { + frame: css({ position: "absolute", display: "flex", flexDirection: "column", flexWrap: "nowrap", justifyContent: "flex-start", left: "0px", top: "0px", bottom: "0px", right: "0px", overflow: "hidden" - }, - pedalboardScroll: { + }), + pedalboardScroll: css({ position: "relative", width: "100%", flex: "0 0 auto", overflow: "auto", maxHeight: 220 - }, - pedalboardScrollSmall: { + }), + pedalboardScrollSmall: css({ position: "relative", width: "100%", flex: "1 1 1px", overflow: "auto" - }, - separator: { + }), + separator: css({ width: "100%", height: "1px", background: "#888", opacity: "0.5", flex: "0 0 1px" - }, + }), - controlToolBar: { + controlToolBar: css({ flex: "0 0 auto", width: "100%", height: 48 - }, - splitControlBar: { + }), + splitControlBar: css({ flex: "0 0 64px", width: "100%", paddingLeft: 24, paddingRight: 16, paddingBottom: 16 - }, - controlContent: { + }), + controlContent: css({ flex: "1 1 auto", width: "100%", overflowY: "auto", minHeight: 240 - }, - controlContentSmall: { + }), + controlContentSmall: css({ flex: "0 0 162px", width: "100%", height: 162, overflowY: "hidden", - }, - title: { fontSize: "1.1em", fontWeight: 700, marginRight: 8, textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }, - author: { fontWeight: 500, marginRight: 8, textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 } -}); + }), + title: css({ fontSize: "1.1em", fontWeight: 700, marginRight: 8, textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }), + author: css({ fontWeight: 500, marginRight: 8, textOverflow: "ellipsis", whiteSpace: "nowrap", opacity: 0.75 }) +} +}; @@ -126,7 +128,8 @@ interface MainState { export const MainPage = - withStyles(styles, { withTheme: true })( + withTheme( + withStyles( class extends ResizeResponsiveComponent { model: PiPedalModel; @@ -318,6 +321,8 @@ export const MainPage = onLoadClick(e: SyntheticEvent) { this.setState({ loadDialogOpen: true }); + e.preventDefault(); + e.stopPropagation(); } onSnapshotClick() { this.setState({ snapshotDialogOpen: true }); @@ -395,7 +400,7 @@ export const MainPage = } } } - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); if (missing) { return (
); } - } - ); + }, + styles + )); export default MainPage \ No newline at end of file diff --git a/react/src/MaterialColors.tsx b/vite/src/pipedal/MaterialColors.tsx similarity index 100% rename from react/src/MaterialColors.tsx rename to vite/src/pipedal/MaterialColors.tsx diff --git a/react/src/MidiBinding.tsx b/vite/src/pipedal/MidiBinding.tsx similarity index 100% rename from react/src/MidiBinding.tsx rename to vite/src/pipedal/MidiBinding.tsx diff --git a/react/src/MidiBindingView.tsx b/vite/src/pipedal/MidiBindingView.tsx similarity index 98% rename from react/src/MidiBindingView.tsx rename to vite/src/pipedal/MidiBindingView.tsx index cab3a34..0387b44 100644 --- a/react/src/MidiBindingView.tsx +++ b/vite/src/pipedal/MidiBindingView.tsx @@ -20,9 +20,10 @@ import { Component } from 'react'; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import {createStyles} from './WithStyles'; + import { UiPlugin } from './Lv2Plugin'; import MenuItem from '@mui/material/MenuItem'; @@ -71,7 +72,7 @@ interface MidiBindingViewState { const MidiBindingView = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { model: PiPedalModel; @@ -200,7 +201,7 @@ const MidiBindingView = } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let midiBinding = this.props.midiBinding; let uiPlugin = this.props.uiPlugin; if (!uiPlugin) { @@ -400,6 +401,7 @@ const MidiBindingView = ); } - }); + }, + styles); export default MidiBindingView; \ No newline at end of file diff --git a/react/src/MidiBindingsDialog.tsx b/vite/src/pipedal/MidiBindingsDialog.tsx similarity index 96% rename from react/src/MidiBindingsDialog.tsx rename to vite/src/pipedal/MidiBindingsDialog.tsx index 1fe9991..6810438 100644 --- a/react/src/MidiBindingsDialog.tsx +++ b/vite/src/pipedal/MidiBindingsDialog.tsx @@ -23,9 +23,10 @@ import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import { PiPedalModel, PiPedalModelFactory, ListenHandle } from './PiPedalModel'; import Typography from '@mui/material/Typography'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; @@ -34,33 +35,34 @@ import MidiBinding from './MidiBinding'; import MidiBindingView from './MidiBindingView'; import Snackbar from '@mui/material/Snackbar'; import { UiPlugin,makeSplitUiPlugin } from './Lv2Plugin'; +import { css } from '@emotion/react'; const styles = (theme: Theme) => createStyles({ - dialogAppBar: { + dialogAppBar: css({ position: 'relative', top: 0, left: 0 - }, - dialogTitle: { + }), + dialogTitle: css({ marginLeft: theme.spacing(2), flex: "1 1 auto", - }, - pluginTable: { + }), + pluginTable: css({ border: "collapse", width: "100%", - }, - pluginHead: { + }), + pluginHead: css({ borderTop: "1pt #DDD solid", paddingLeft: 22, paddingRight: 22 - }, - bindingTd: { + }), + bindingTd: css({ verticalAlign: "top", paddingLeft: 12, paddingBottom: 8 - }, - nameTd: { + }), + nameTd: css({ paddingLeft: 48, verticalAlign: "top", paddingTop: 12 - }, + }), }); function not_null(value: T | null) { @@ -81,7 +83,7 @@ export interface MidiBindingDialogState { } export const MidiBindingDialog = - withStyles(styles, { withTheme: true })( + withStyles( class extends ResizeResponsiveComponent { model: PiPedalModel; @@ -105,7 +107,7 @@ export const MidiBindingDialog = this.props.onClose(); } - listenTimeoutHandle?: NodeJS.Timeout; + listenTimeoutHandle?: number; listenHandle?: ListenHandle; @@ -183,7 +185,7 @@ export const MidiBindingDialog = this.model.setMidiBinding(instanceId, newBinding); } generateTable(): React.ReactNode[] { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let result: React.ReactNode[] = []; let pedalboard = this.model.pedalboard.get(); @@ -317,7 +319,8 @@ export const MidiBindingDialog = render() { let props = this.props; - let { open, classes } = props; + let { open} = props; + const classes = withStyles.getClasses(props); if (!open) { return (
); } @@ -374,7 +377,8 @@ export const MidiBindingDialog = ); } - }); + }, + styles); export default MidiBindingDialog; \ No newline at end of file diff --git a/react/src/MidiChannelBinding.tsx b/vite/src/pipedal/MidiChannelBinding.tsx similarity index 100% rename from react/src/MidiChannelBinding.tsx rename to vite/src/pipedal/MidiChannelBinding.tsx diff --git a/react/src/MidiChannelBindingControl.tsx b/vite/src/pipedal/MidiChannelBindingControl.tsx similarity index 91% rename from react/src/MidiChannelBindingControl.tsx rename to vite/src/pipedal/MidiChannelBindingControl.tsx index 85501a2..db7b086 100644 --- a/react/src/MidiChannelBindingControl.tsx +++ b/vite/src/pipedal/MidiChannelBindingControl.tsx @@ -21,8 +21,8 @@ import React from 'react'; import { Component } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles, {withTheme} from './WithStyles'; +import { withStyles } from "tss-react/mui"; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { pluginControlStyles } from './PluginControl'; import MidiChannelBinding from './MidiChannelBinding'; @@ -30,7 +30,7 @@ import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import Divider from '@mui/material/Divider'; import ButtonBase from '@mui/material/ButtonBase'; -import { ReactComponent as MidiIcon } from "./svg/ic_midi.svg"; +import MidiIcon from './svg/ic_midi.svg?react'; import MidiChannelBindingDialog from './MidiChannelBindingDialog'; @@ -48,7 +48,8 @@ type MidiChannelBindingControlState = { }; const MidiChannelBindingControl = - withStyles(pluginControlStyles, { withTheme: true })( + withTheme( + withStyles( class extends Component { model: PiPedalModel; @@ -63,7 +64,7 @@ const MidiChannelBindingControl = } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let item_width = 80; return (
{/* TITLE SECTION */} -
{/* CONTROL SECTION */} -
+
{ this.setState({dialogOpen: true}); @@ -111,7 +112,7 @@ const MidiChannelBindingControl =
-
); } - }); + }, + pluginControlStyles + )); export default MidiChannelBindingControl; diff --git a/react/src/MidiChannelBindingDialog.tsx b/vite/src/pipedal/MidiChannelBindingDialog.tsx similarity index 83% rename from react/src/MidiChannelBindingDialog.tsx rename to vite/src/pipedal/MidiChannelBindingDialog.tsx index f804697..e3ba3d5 100644 --- a/react/src/MidiChannelBindingDialog.tsx +++ b/vite/src/pipedal/MidiChannelBindingDialog.tsx @@ -19,14 +19,13 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + import { useState } from 'react'; import Button from '@mui/material/Button'; import DialogTitle from '@mui/material/DialogTitle'; import DialogActions from '@mui/material/DialogActions'; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import FormControlLabel from '@mui/material/FormControlLabel'; -import FormControl from '@mui/material/FormControl'; -import InputLabel from '@mui/material/InputLabel'; import ChannelBindingHelpDialog from './ChannelBindingsHelpDialog'; import IconButton from '@mui/material/IconButton'; @@ -40,9 +39,7 @@ import DialogContent from '@mui/material/DialogContent'; import Typography from '@mui/material/Typography'; import Select, { SelectChangeEvent } from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; -import { styled, Theme } from '@mui/material/styles'; -import createStyles from '@mui/styles/createStyles'; -import { makeStyles } from '@mui/styles'; + export interface MidiChannelBindingDialogProps { @@ -54,48 +51,9 @@ export interface MidiChannelBindingDialogProps { } -function isChecked(selectedChannels: AlsaMidiDeviceInfo[], channel: AlsaMidiDeviceInfo): boolean { - for (let i = 0; i < selectedChannels.length; ++i) { - if (selectedChannels[i].equals(channel)) return true; - } - return false; -} -function addPort(availableChannels: AlsaMidiDeviceInfo[], selectedChannels: AlsaMidiDeviceInfo[], newChannel: AlsaMidiDeviceInfo) - : AlsaMidiDeviceInfo[] { - let result: AlsaMidiDeviceInfo[] = []; - for (let i = 0; i < availableChannels.length; ++i) { - let channel = availableChannels[i]; - if (isChecked(selectedChannels, channel) || channel.equals(newChannel)) { - result.push(channel); - } - } - return result; -} - -function removePort(selectedChannels: AlsaMidiDeviceInfo[], channel: AlsaMidiDeviceInfo) - : AlsaMidiDeviceInfo[] { - let result: AlsaMidiDeviceInfo[] = []; - for (let i = 0; i < selectedChannels.length; ++i) { - if (!selectedChannels[i].equals(channel)) { - result.push(selectedChannels[i]); - } - } - return result; -} - -const usesStyles = makeStyles((theme: Theme) => - createStyles({ - icon: { - fill: theme.palette.text.primary, - opacity: 0.6 - } - }) -); - function MidiChannelBindingDialog(props: MidiChannelBindingDialogProps) { - const classes = usesStyles(); - const { onClose, midiChannelBinding, open, onChanged } = props; + const { onClose, midiChannelBinding, open } = props; const [currentChannel, setCurrentChannel] = useState(midiChannelBinding.channel); const [allowProgramChanges, setAllowProgramChanges] = useState(midiChannelBinding.acceptCommonMessages); @@ -213,7 +171,10 @@ function MidiChannelBindingDialog(props: MidiChannelBindingDialogProps) { onClick={() => { setHelpDialog(true); }} size="large"> )} diff --git a/react/src/ModFileTypes.tsx b/vite/src/pipedal/ModFileTypes.tsx similarity index 100% rename from react/src/ModFileTypes.tsx rename to vite/src/pipedal/ModFileTypes.tsx diff --git a/react/src/NoChangePassword.tsx b/vite/src/pipedal/NoChangePassword.tsx similarity index 100% rename from react/src/NoChangePassword.tsx rename to vite/src/pipedal/NoChangePassword.tsx diff --git a/react/src/NumericInput.tsx b/vite/src/pipedal/NumericInput.tsx similarity index 95% rename from react/src/NumericInput.tsx rename to vite/src/pipedal/NumericInput.tsx index 961ef4a..73e50d6 100644 --- a/react/src/NumericInput.tsx +++ b/vite/src/pipedal/NumericInput.tsx @@ -19,9 +19,10 @@ import React, { Component } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import Input from '@mui/material/Input'; @@ -45,7 +46,7 @@ interface NumericInputState { } export const NumericInput = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { @@ -129,7 +130,7 @@ export const NumericInput = render() { - //let classes = this.props.classes; + //const classes = withStyles.getClasses(this.props); return ( createStyles({ -}); -interface PedalProps extends WithStyles { +interface PedalProps { plugin: Lv2Plugin; - children?: React.ReactChild | React.ReactChild[]; + children?: React.ReactNode; } @@ -39,7 +33,7 @@ type PedalState = { }; -export const TemporaryDrawer = withStyles(pedalStyles)( +export const TemporaryDrawer = class extends Component { state: PedalState; @@ -57,5 +51,4 @@ export const TemporaryDrawer = withStyles(pedalStyles)( ); } - } -); \ No newline at end of file + }; \ No newline at end of file diff --git a/react/src/Pedalboard.tsx b/vite/src/pipedal/Pedalboard.tsx similarity index 100% rename from react/src/Pedalboard.tsx rename to vite/src/pipedal/Pedalboard.tsx diff --git a/vite/src/pipedal/PedalboardView.tsx b/vite/src/pipedal/PedalboardView.tsx new file mode 100644 index 0000000..c1fa5fe --- /dev/null +++ b/vite/src/pipedal/PedalboardView.tsx @@ -0,0 +1,1292 @@ +// Copyright (c) 2022 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import React, { ReactNode, Component, SyntheticEvent } from 'react'; +import { css } from '@emotion/react'; +import {createStyles} from './WithStyles'; + +import WithStyles, {withTheme} from './WithStyles'; +import { withStyles } from "tss-react/mui"; + + +import { Theme } from '@mui/material/styles'; +import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; +import { PluginType } from './Lv2Plugin'; +import ButtonBase from '@mui/material/ButtonBase'; +import Typography from '@mui/material/Typography'; +import PluginIcon, { SelectIconUri } from './PluginIcon'; +import { SelectHoverBackground } from './SelectHoverBackground'; +import SvgPathBuilder from './SvgPathBuilder'; +import Draggable from './Draggable' +import Rect from './Rect'; +import { PiPedalStateError } from './PiPedalError'; +import Utility from './Utility'; +import { isDarkMode } from './DarkMode'; +import { + Pedalboard, PedalboardItem, PedalboardSplitItem, SplitType +} from './Pedalboard'; + +import MidiIcon from './svg/ic_midi.svg?react'; + + +const START_CONTROL = Pedalboard.START_CONTROL; +const END_CONTROL = Pedalboard.END_CONTROL; + + +const START_PEDALBOARD_ITEM_URI = Pedalboard.START_PEDALBOARD_ITEM_URI; +const END_PEDALBOARD_ITEM_URI = Pedalboard.END_PEDALBOARD_ITEM_URI; + +const ENABLED_CONNECTOR_COLOR = isDarkMode() ? "#CCC" : "#666"; +const DISABLED_CONNECTOR_COLOR = isDarkMode() ? "#666" : "#CCC"; + + + +const CELL_WIDTH: number = 96; +const CELL_HEIGHT: number = 64; +const FRAME_SIZE: number = 36; + +const STROKE_WIDTH = 3; +const STEREO_STROKE_WIDTH = 6; + + +const I_SVG_STROKE_WIDTH = 3; +const I_SVG_STEREO_STROKE_WIDTH = 6; + +const SVG_STROKE_WIDTH = I_SVG_STROKE_WIDTH.toString(); +const SVG_STEREO_STROKE_WIDTH = I_SVG_STEREO_STROKE_WIDTH.toString(); + + + +const EMPTY_ICON_URL = "img/fx_empty.svg"; +const ERROR_ICON_URL = "img/fx_error.svg"; +const TERMINAL_ICON_URL = "img/fx_terminal.svg"; + +function CalculateConnection(numberOfInputs: number, numberOfOutputs: number) { + if (numberOfInputs === 0) { + return numberOfOutputs; + } + if (numberOfOutputs === 0) { + return numberOfInputs; + } + let result = Math.min(numberOfInputs, numberOfOutputs); + if (result > 2) result = 2; + return result; +} + +const pedalboardStyles = (theme: Theme) => createStyles({ + scrollContainer: { + }, + + container: css({ + position: "relative", + overflow: "visible", + + }), + splitItem: css({ + position: "absolute", + display: "flex", + alignItems: "center", + justifyContent: "center", + + + width: CELL_WIDTH, + height: CELL_HEIGHT + + }), + splitStart: css({ + position: "absolute", + display: "flex", + width: CELL_WIDTH, + height: CELL_HEIGHT, + left: 0, + top: 0 + }), + splitEnd: css({ + position: "absolute", + display: "flex", + width: CELL_WIDTH, + height: CELL_HEIGHT, + right: 0, + top: 0 + }), + buttonDraggable: css({ + display: "flex", + alignItem: "center", + justifyContent: "center", + width: "100%", + height: "100%" + }), + pedalItem: css({ + position: "absolute", + width: CELL_WIDTH, + height: CELL_HEIGHT, + display: "flex", + alignItems: "center", + justifyContent: "center", + }), + midiConnectorDecoration: css({ + position: "absolute", + left: -20, + top: -6, + fill: theme.palette.text.secondary + }), + iconFrame: css({ + + display: "flex", + alignItems: "center", + justifyContent: "center", + position: "relative", + + background: theme.palette.background.default, + marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, + marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, + marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2, + marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2, + width: FRAME_SIZE, + height: FRAME_SIZE, + border: isDarkMode() ? "1pt #AAA solid" : "1pt #666 solid", + borderRadius: 6 + }), + borderlessIconFrame: css({ + + display: "flex", + alignItems: "center", + justifyContent: "center", + + background: "transparent", + marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, + marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, + marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2, + marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2, + width: FRAME_SIZE, + height: FRAME_SIZE, + border: "0pt #666 solid", + borderRadius: 6 + }), + + pedalIcon: css({ + width: 24, + height: 24, + opacity: 0.8 + }), + connector: css({ + position: "absolute", + width: CELL_WIDTH, + height: CELL_HEIGHT + }), + stroke: css({ + position: "absolute", + background: "#888" + }), + stereoStrokeOuter: css({ + position: "absolute", + background: "#888" + }), + stereoStrokeInner: css({ + position: "absolute", + background: "#888" + }), + +}); + +export type OnSelectHandler = (selectedPedal: number) => void; + +interface PedalboardProps extends WithStyles { + theme: Theme; + selectedId?: number; + onSelectionChanged?: OnSelectHandler; + onDoubleClick?: OnSelectHandler; + hasTinyToolBar: boolean; + enableStructureEditing: boolean; + +} +interface LayoutSize { + width: number; + height: number; +} + +type PedalboardState = { + pedalboard?: Pedalboard; +}; + +const EMPTY_PEDALS: PedalLayout[] = []; + + +function makeChain(model: PiPedalModel, uiItems?: PedalboardItem[]): PedalLayout[] { + let result: PedalLayout[] = []; + if (uiItems) { + for (let i = 0; i < uiItems.length; ++i) { + let item = uiItems[i]; + result.push(new PedalLayout(model, item)); + } + } + return result; +} + +class PedalLayout { + uri: string = ""; + name: string = ""; + pluginType: PluginType = PluginType.Plugin; + iconUrl: string = ""; + + bounds: Rect = new Rect(); + + numberOfInputs: number = 2; + numberOfOutputs: number = 2; + originalInputs: number = 2; + originalOutputs: number = 2; + + pedalItem?: PedalboardItem; + + // Split Layout only. + topChildren: PedalLayout[] = EMPTY_PEDALS; + bottomChildren: PedalLayout[] = EMPTY_PEDALS; + topConnectorY: number = 0; + bottomConnectorY: number = 0; + + + static Start(): PedalLayout { + let t: PedalLayout = new PedalLayout(); + t.uri = START_PEDALBOARD_ITEM_URI; + t.pluginType = PluginType.Terminal; + t.iconUrl = TERMINAL_ICON_URL; + t.numberOfInputs = 0; + t.numberOfOutputs = 2; + return t; + } + static End(): PedalLayout { + let t: PedalLayout = new PedalLayout(); + t.pluginType = PluginType.Terminal; + t.uri = END_PEDALBOARD_ITEM_URI; + t.iconUrl = TERMINAL_ICON_URL; + t.numberOfInputs = 2; + t.numberOfOutputs = 0; + return t; + } + constructor(model?: PiPedalModel, pedalItem?: PedalboardItem) { + if (model === undefined && pedalItem === undefined) { + return; + } + if (model === undefined || pedalItem === undefined) { + throw new Error("Invalid arguments."); + } + this.pedalItem = pedalItem; + this.uri = pedalItem.uri; + if (pedalItem.isSplit()) { + let splitter = pedalItem as PedalboardSplitItem; + + this.pluginType = PluginType.UtilityPlugin; + this.topChildren = makeChain(model, splitter.topChain); + this.bottomChildren = makeChain(model, splitter.bottomChain); + + this.numberOfInputs = 2; + this.numberOfOutputs = 2; + + } else if (pedalItem.isEmpty()) { + + this.pluginType = PluginType.None; + this.iconUrl = EMPTY_ICON_URL; + this.numberOfInputs = 2; + this.numberOfOutputs = 2; + + } else if (pedalItem.uri === START_PEDALBOARD_ITEM_URI) { + this.pluginType = PluginType.UtilityPlugin; + this.iconUrl = TERMINAL_ICON_URL; + this.numberOfInputs = 0; + this.numberOfOutputs = PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length; + if (this.numberOfOutputs === 0) { + this.numberOfOutputs = 1; + } + + } else if (pedalItem.uri === END_PEDALBOARD_ITEM_URI) { + this.pluginType = PluginType.UtilityPlugin; + this.iconUrl = TERMINAL_ICON_URL; + this.numberOfInputs = PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length; + if (this.numberOfInputs === 0) { + this.numberOfInputs = 1; + } + this.numberOfOutputs = 0; + } + else { + let uiPlugin = model.getUiPlugin(pedalItem.uri); + if (uiPlugin != null) { + this.pluginType = uiPlugin.plugin_type; + this.iconUrl = SelectIconUri(uiPlugin.plugin_type); + this.name = uiPlugin.label; + this.numberOfInputs = Math.min(uiPlugin.audio_inputs, 2); + this.numberOfOutputs = Math.min(uiPlugin.audio_outputs, 2); + } else { + // default to empty plugin. + this.pluginType = PluginType.ErrorPlugin; + this.name = pedalItem.pluginName ?? "#error"; + this.iconUrl = ERROR_ICON_URL; + this.numberOfInputs = 2; + this.numberOfOutputs = 2; + } + } + this.originalInputs = this.numberOfInputs; + this.originalOutputs = this.numberOfOutputs; + } + isEmpty(): boolean { + return (!this.pedalItem) || this.pedalItem.isEmpty(); + } + isSplitter(): boolean { + return this.pedalItem !== undefined && this.pedalItem.isSplit(); + } + isStart() { + return this.uri === START_PEDALBOARD_ITEM_URI; + } + isEnd() { + return this.uri === END_PEDALBOARD_ITEM_URI; + } +} + +function* chainIterator(layoutChain: PedalLayout[]): Generator { + for (let i = 0; i < layoutChain.length; ++i) { + let item = layoutChain[i]; + yield item; + if (item.isSplitter()) { + let g = chainIterator(item.topChildren); + while (true) { + let v = g.next(); + if (v.done) { + break; + } + yield v.value; + } + g = chainIterator(item.bottomChildren); + while (true) { + let v = g.next(); + if (v.done) { + break; + } + yield v.value; + } + } + } + return; +} + + + +class LayoutParams { + nextId: number = 1; + cx: number = 0; + cy: number = 0; +} + + +const PedalboardView = +withTheme( +withStyles( + class extends Component { + model: PiPedalModel; + + frameRef: React.RefObject; + scrollRef: React.RefObject; + private bgColor: string; + + constructor(props: PedalboardProps) { + super(props); + this.model = PiPedalModelFactory.getInstance(); + + this.bgColor = props.theme.palette.background.default + + if (!props.selectedId) props.selectedId = -1; + this.state = { + pedalboard: this.model.pedalboard.get(), + }; + this.onPedalboardChanged = this.onPedalboardChanged.bind(this); + this.frameRef = React.createRef(); + this.scrollRef = React.createRef(); + this.handleTouchStart = this.handleTouchStart.bind(this); + } + + handleTouchStart(e: any) { + // just has to exist to allow Draggable to receive + // touchyMove. :-/ + } + + onDragEnd(instanceId: number, clientX: number, clientY: number) { + if (!this.props.enableStructureEditing) { + return; + } + if (!this.currentLayout) return; + + if (!this.frameRef.current) return; + + let currentLayout: PedalLayout[] = this.currentLayout; + let frameElement = this.frameRef.current; + + let rc = frameElement.getBoundingClientRect(); + clientX -= rc.left; + clientY -= rc.top; + + let it = chainIterator(currentLayout); + + while (true) { + let v = it.next(); + if (v.done) break; + let item = v.value; + + if (item.isSplitter() && item.pedalItem) { + if (item.bounds.contains(clientX, clientY)) { + if (clientX < item.bounds.x + CELL_WIDTH / 2) { + this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); + this.setSelection(instanceId); + return; + } else if (clientX > item.bounds.right - CELL_WIDTH / 2) { + this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); + this.setSelection(instanceId); + return; + + } + } + let yMid = (item.bounds.y + item.bounds.bottom) / 2; + if (clientX >= item.bounds.x + && clientY < yMid && clientY >= item.topChildren[0].bounds.y + + ) { + if (clientX < item.topChildren[0].bounds.x) { + let topPedalItem = item.topChildren[0].pedalItem; + if (topPedalItem) { + this.model.movePedalboardItemBefore(instanceId, topPedalItem.instanceId); + this.setSelection(instanceId); + return; + } + } + let lastTop = item.topChildren[item.topChildren.length - 1]; + if (clientX >= lastTop.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { + if (lastTop.pedalItem) { + this.model.movePedalboardItemAfter(instanceId, lastTop.pedalItem.instanceId); + this.setSelection(instanceId); + return; + } + } + + } + if (clientX >= item.bounds.x + && clientY > yMid && clientY < item.bottomChildren[0].bounds.bottom + ) { + if (clientX < item.bottomChildren[0].bounds.x) { + let bottomPedalItem = item.bottomChildren[0].pedalItem; + if (bottomPedalItem) { + this.model.movePedalboardItemBefore(instanceId, bottomPedalItem.instanceId); + this.setSelection(instanceId); + return; + } + } + let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; + if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { + if (lastBottom.pedalItem) { + this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId); + this.setSelection(instanceId); + return; + } + + } + + } + + } else if (item.bounds.contains(clientX, clientY)) { + if (item.isStart()) { + this.model.movePedalboardItemToStart(instanceId); + this.setSelection(instanceId); + return; + } else if (item.isEnd()) { + this.model.movePedalboardItemToEnd(instanceId); + this.setSelection(instanceId); + return; + } else { + if (item.pedalItem) { + let margin = (CELL_WIDTH - FRAME_SIZE) / 2; + if (clientX < item.bounds.x + margin) { + this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); + } else if (clientX > item.bounds.right - margin) { + this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); + } else { + this.model.movePedalboardItem(instanceId, item.pedalItem.instanceId); + } + this.setSelection(instanceId); + return; + } + } + + } + } + // delete the plugin. + let newId = this.model.setPedalboardItemEmpty(instanceId); + this.setSelection(newId); + + } + + onPedalboardChanged(value?: Pedalboard) { + this.setState({ pedalboard: value }); + } + + componentDidMount() { + this.scrollRef.current!.addEventListener("touchstart", this.handleTouchStart, { passive: false }); + this.model.pedalboard.addOnChangedHandler(this.onPedalboardChanged); + + } + componentWillUnmount() { + this.scrollRef.current!.removeEventListener("touchstart", this.handleTouchStart); + this.model.pedalboard.removeOnChangedHandler(this.onPedalboardChanged); + } + + offsetLayout_(layoutItems: PedalLayout[], offset: number): void { + for (let i = 0; i < layoutItems.length; ++i) { + let layoutItem = layoutItems[i]; + layoutItem.bounds.y += offset; + if (layoutItem.isSplitter()) { + layoutItem.topConnectorY += offset; + layoutItem.bottomConnectorY += offset; + this.offsetLayout_(layoutItem.topChildren, offset); + this.offsetLayout_(layoutItem.bottomChildren, offset); + } + } + } + + getSplitterIcon(layoutItem: PedalLayout): PluginType { + if (layoutItem.pedalItem === undefined) { + throw new Error("Invalid splitter"); + } + let split = layoutItem.pedalItem as PedalboardSplitItem; + if (split.getSplitType() === SplitType.Ab) { + if (split.isASelected()) { + return PluginType.SplitA; + } else { + return PluginType.SplitB; + + } + } else if (split.getSplitType() === SplitType.Mix) { + return PluginType.SplitMix; //"img/fx_dial.svg"; + } else { + return PluginType.SplitLR; //"img/fx_lr.svg"; + } + } + + doLayout2_(lp: LayoutParams, layoutItems: PedalLayout[]): Rect { + let bounds = new Rect(); + for (let i = 0; i < layoutItems.length; ++i) { + let layoutItem = layoutItems[i]; + if (layoutItem.isSplitter()) { + let x0 = lp.cx; + let y0 = lp.cy; + + layoutItem.bounds.x = x0; + layoutItem.bounds.y = y0; + layoutItem.bounds.height = CELL_HEIGHT; + layoutItem.bounds.width = CELL_WIDTH; + + lp.cx += CELL_WIDTH; + + + let topBounds = this.doLayout2_(lp, layoutItem.topChildren); + if (topBounds.isEmpty()) { + topBounds.x = lp.cx; + topBounds.width = 0; + topBounds.y = y0 - CELL_HEIGHT / 2; + topBounds.height = CELL_HEIGHT; + + } + + + let dyTop = (lp.cy + CELL_HEIGHT / 2) - (topBounds.y + topBounds.height); + + + + this.offsetLayout_(layoutItem.topChildren, dyTop); + topBounds.offset(0, dyTop); + bounds.accumulate(topBounds); + + let topCx = lp.cx; + lp.cx = x0; + lp.cx += CELL_WIDTH; + + let bottomBounds = this.doLayout2_(lp, layoutItem.bottomChildren); + if (bottomBounds.isEmpty()) { + bottomBounds.x = lp.cx; bottomBounds.width = 0; + bottomBounds.y = lp.cy; bottomBounds.height = CELL_HEIGHT; + } + + let dyBottom = (lp.cy + CELL_HEIGHT / 2) - bottomBounds.y; + this.offsetLayout_(layoutItem.bottomChildren, dyBottom) + bottomBounds.offset(0, dyBottom); + bounds.accumulate(bottomBounds); + + lp.cx = Math.max(lp.cx, topCx) + CELL_WIDTH; + lp.cy = y0; + + layoutItem.bounds.width = lp.cx - layoutItem.bounds.x; + bounds.accumulate(layoutItem.bounds); + + if (layoutItem.topChildren.length === 0) { + layoutItem.topConnectorY = bounds.y + CELL_HEIGHT / 2; + } else { + layoutItem.topConnectorY = layoutItem.topChildren[0].bounds.y + CELL_HEIGHT / 2; + } + if (layoutItem.bottomChildren.length === 0) { + layoutItem.bottomConnectorY = bounds.y + bounds.height - CELL_HEIGHT / 2; + } else { + layoutItem.bottomConnectorY = layoutItem.bottomChildren[0].bounds.y + CELL_HEIGHT / 2; + } + } else { + layoutItem.bounds.x = lp.cx; + layoutItem.bounds.y = lp.cy; + lp.cx += CELL_WIDTH; + layoutItem.bounds.width = CELL_WIDTH; + layoutItem.bounds.height = CELL_HEIGHT; + bounds.accumulate(layoutItem.bounds); + + } + } + return bounds; + } + doLayout(layoutItems: PedalLayout[]): LayoutSize { + const TWO_ROW_HEIGHT = 142 - 14; + + if (layoutItems.length === 0) { + // if the current pedalboard is empty, reserve display space anyway. + return { width: 1, height: TWO_ROW_HEIGHT }; + } + + let lp = new LayoutParams(); + + let bounds = this.doLayout2_(lp, layoutItems); + // shift everything down so there are no negative y coordinates. + + if (bounds.height < TWO_ROW_HEIGHT) { + + let extra = Math.floor((TWO_ROW_HEIGHT - Math.ceil(bounds.height)) / 2); + this.offsetLayout_(layoutItems, Math.floor(-bounds.y + extra / 2)); + bounds.height += extra; + + } else { + this.offsetLayout_(layoutItems, -bounds.y); + } + + bounds.height += 14; // for labels that aren't accounted for. + return { width: bounds.width, height: bounds.height }; + + } + + onItemClick(e: SyntheticEvent, instanceId?: number): void { + if (instanceId) { + this.setSelection(instanceId); + } + } + setSelection(instanceId: number) { + if (this.props.onSelectionChanged) { + this.props.onSelectionChanged(instanceId); + } + } + + onItemDoubleClick(event: SyntheticEvent, instanceId?: number): void { + event.preventDefault(); + event.stopPropagation(); + + if (this.props.onDoubleClick && instanceId && this.props.enableStructureEditing) { + this.props.onDoubleClick(instanceId); + } + + } + + onItemLongClick(event: SyntheticEvent, instanceId?: number): void { + if (!instanceId) { + return; + } + event.preventDefault(); + event.stopPropagation(); + + if (!this.props.enableStructureEditing) { + this.setSelection(instanceId); + return; + } + if (!Utility.needsZoomedControls()) { + if (this.props.onDoubleClick && this.props.enableStructureEditing && instanceId) { + this.props.onDoubleClick(instanceId); + } + } + + } + + strokeConnector(output: ReactNode[], channels: number, enabled: Boolean, svgPath: string) { + let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + + if (channels === 2) { + output.push(( + + )); + output.push(( + + )); + } else if (channels === 1) { + output.push(( + + )); + } + } + + renderConnector(output: ReactNode[], item: PedalLayout, enabled: boolean): void { + // const classes = withStyles.getClasses(this.props); + let x_ = item.bounds.x + CELL_WIDTH / 2; + let y_ = item.bounds.y + CELL_HEIGHT / 2; + let numberOfOutputs = item.numberOfOutputs; + let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + let stereoCenterColor = this.bgColor; + + if (item.originalInputs === 0) { + // break the input paths. + let rx = item.bounds.x + CELL_WIDTH / 2 - FRAME_SIZE / 2 - 4; + let ry = y_ - 4; + + output.push(( + + )); + } + let svgPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_ + CELL_WIDTH, y_).toString(); + + + if (numberOfOutputs === 2) { + output.push(( + + )); + output.push(( + + )); + } else if (numberOfOutputs === 1) { + output.push(( + + )); + } else { + output.push(( + + )); + + } + } + renderSplitConnectors(output: ReactNode[], item: PedalLayout, enabled: boolean, shortSplitOutput: boolean,): void { + //const classes = withStyles.getClasses(this.props); + let x_ = item.bounds.x + CELL_WIDTH / 2; + let y_ = item.bounds.y + CELL_HEIGHT / 2; + let yTop = item.topConnectorY; + let yBottom = item.bottomConnectorY; + //let isStereo = item.stereoOutput; + let split = item.pedalItem as PedalboardSplitItem; + + let topEnabled = enabled && split.isASelected(); + let bottomEnabled = enabled && split.isBSelected(); + let topColor = topEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + let bottomColor = bottomEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + + + let topStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yTop).lineTo(x_ + CELL_WIDTH, yTop).toString(); + let bottomStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yBottom).lineTo(x_ + CELL_WIDTH, yBottom).toString(); + + if (item.numberOfInputs === 2 && item.topChildren[0].numberOfInputs === 2) { + output.push(()); + output.push(()); + } else if (item.numberOfInputs !== 0 && item.topChildren[0].numberOfInputs !== 0) { + output.push(()); + } + + if (item.numberOfInputs === 2 && item.bottomChildren[0].numberOfInputs === 2) { + output.push(()); + output.push(()); + + } else if (item.numberOfInputs !== 0 && item.bottomChildren[0].numberOfInputs !== 0) { + output.push(()); + } + + let lastTop = item.topChildren[item.topChildren.length - 1]; + let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; + + let xTop = lastTop.bounds.right - CELL_WIDTH / 2; + let xBottom = lastBottom.bounds.right - CELL_WIDTH / 2; + + let xEnd = shortSplitOutput ? item.bounds.right : item.bounds.right + CELL_WIDTH / 2; + let xTee0 = item.bounds.right - CELL_WIDTH / 2; + + let firstPath: string; // top or bottom depending on draw order. + let secondPath: string; // top or bottom depending on draw order. + + let firstPathStereo: boolean; + let secondPathStereo: boolean; + let firstPathAbsent: boolean; + let secondPathAbsent: boolean; + let firstPathEnabled: boolean; + let secondPathEnabled: boolean; + let xTee: number; + + let monoAdjustment = (STEREO_STROKE_WIDTH - STROKE_WIDTH) / 2; + + let bottomPathFirst = topEnabled && !bottomEnabled; + let topPathFirst = bottomEnabled && !topEnabled; + + + // Third case: L/R stereo output, when both outputs are mono, requires a third stroke. + let thirdPath: string | null = null; // for L/R stereo output (which can be stereo even if both outputs are mono) + let hasThirdPath = item.numberOfOutputs === 2 && (lastTop.numberOfOutputs !== 2) && (lastBottom.numberOfOutputs !== 2); + + if (hasThirdPath) { + firstPathStereo = false; + secondPathStereo = false; + firstPathAbsent = lastTop.numberOfOutputs === 0 || item.numberOfOutputs === 0; + secondPathAbsent = lastBottom.numberOfOutputs === 0 || item.numberOfOutputs === 0; + xTee = xTee0 - monoAdjustment; + firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString(); + + secondPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString(); + + hasThirdPath = true; + thirdPath = new SvgPathBuilder().moveTo(xTee0, y_).lineTo(xEnd, y_).toString(); + firstPathEnabled = bottomEnabled; + secondPathEnabled = topEnabled; + } else if (bottomPathFirst || (topEnabled && lastTop.numberOfOutputs === 2)) { + // draw the bottom path first. + firstPathStereo = item.numberOfOutputs === 2 && lastBottom.numberOfOutputs === 2; + secondPathStereo = item.numberOfOutputs === 2 && lastTop.numberOfOutputs === 2; + firstPathAbsent = item.numberOfOutputs === 0 || lastBottom.numberOfOutputs === 0; + secondPathAbsent = item.numberOfOutputs === 0 || lastTop.numberOfOutputs === 0; + + xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment; + firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString(); + xTee = secondPathStereo ? xTee0 : xTee0 - monoAdjustment; + + secondPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).lineTo(xEnd, y_).toString(); + firstPathEnabled = bottomEnabled; + secondPathEnabled = topEnabled; + } else { + // draw the top path first. + firstPathStereo = item.numberOfOutputs === 2 && lastTop.numberOfOutputs === 2; + secondPathStereo = item.numberOfOutputs === 2 && lastBottom.numberOfOutputs === 2; + firstPathAbsent = item.numberOfOutputs === 0 || lastTop.numberOfOutputs === 0; + secondPathAbsent = item.numberOfOutputs === 0 || lastBottom.numberOfOutputs === 0; + + xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment; + firstPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString(); + + xTee = secondPathStereo ? xTee0 : xTee0 - monoAdjustment; + secondPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).lineTo(xEnd, y_).toString(); + + firstPathEnabled = topEnabled; + secondPathEnabled = bottomEnabled; + } + let firstPathColor = firstPathEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + let secondPathColor = secondPathEnabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + + if (bottomPathFirst || topPathFirst) { + // display stereo strokes with cutoff line. + if (firstPathStereo) { + output.push(( + + )); + output.push(( + + )); + } else if (!firstPathAbsent) { + output.push(( + + )); + } + if (secondPathStereo) { + output.push(( + + )); + output.push(( + + )); + } else if (!secondPathAbsent) { + output.push(( + + )); + } + + } else { + // stereo strokes merge. + if (firstPathStereo) { + output.push(( + + )); + } else if (!firstPathAbsent) { + output.push(( + + )); + } + if (secondPathStereo) { + output.push(( + + )); + } else if (!secondPathAbsent) { + output.push(( + + )); + } + + // draw stereo inner lines. + if (firstPathStereo) { + output.push(( + + )); + } + if (secondPathStereo) { + output.push(( + + )); + + } + } + if (thirdPath != null) { + // stereo output of L/R splitter + output.push(( + + )); + output.push(( + + )); + + + } + } + getScrollContainer() { + let el: HTMLElement | undefined | null = this.scrollRef.current; + // actually not here anymore. :-/ It has a reactive definition in MainPage.tsx now. + while (el) { + if (el.id === "pedalboardScroll") { + return el as HTMLDivElement; + } + el = el.parentElement; + } + throw new PiPedalStateError("scroll container not found."); + } + + pedalButton( + instanceId: number, + iconType: PluginType, + draggable: boolean, + enabled: boolean, + hasBorder: boolean = true, + pluginNotFound: boolean, + hasMidiConnector: boolean + ) + : ReactNode { + const classes = withStyles.getClasses(this.props); + return ( +
{ e.preventDefault(); }}> + {hasMidiConnector && ( +
+ +
+ + )} + + { this.onItemClick(e, instanceId); }} + onDoubleClick={(e: SyntheticEvent) => { this.onItemDoubleClick(e, instanceId); }} + onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }} + > + + this.getScrollContainer()} + onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} + > + + + + +
+ ); + + } + renderConnectors(output: ReactNode[], layoutChain: PedalLayout[], enabled: boolean, shortSplitOutput: boolean): void { + let length = layoutChain.length - 1; + if (layoutChain.length > 0 && layoutChain[layoutChain.length - 1].isSplitter()) { + ++length; + } + for (let i = 0; i < length; ++i) { + let item = layoutChain[i]; + if (item.isSplitter()) { + let splitter = item.pedalItem as PedalboardSplitItem; + this.renderSplitConnectors(output, item, enabled, i === length - 1 && shortSplitOutput,); + this.renderConnectors(output, item.topChildren, enabled && splitter.isASelected(), false); + this.renderConnectors(output, item.bottomChildren, enabled && splitter.isBSelected(), false); + + } else if (item.uri !== END_PEDALBOARD_ITEM_URI) { + this.renderConnector(output, item, enabled); + + } + } + } + renderConnectorFrame(layoutChain: PedalLayout[], layoutSize: LayoutSize): ReactNode { + let outputs: ReactNode[] = []; + this.renderConnectors(outputs, layoutChain, true, false); + return ( +
+ + + { + outputs + } + + + +
+ ); + + } + + renderChain(layoutChain: PedalLayout[], layoutSize: LayoutSize): ReactNode { + + const classes = withStyles.getClasses(this.props); + + let result: ReactNode[] = []; + + result.push(this.renderConnectorFrame(layoutChain, layoutSize)); + + let it = chainIterator(layoutChain); + while (true) { + let v = it.next(); + if (v.done) break; + let item = v.value; + switch (item.uri) { + case START_PEDALBOARD_ITEM_URI: + result.push(
+
+ + {this.pedalButton(START_CONTROL, item.pluginType, false, true, false, false, false)} +
+
); + break; + case END_PEDALBOARD_ITEM_URI: + result.push(
+
+ + {this.pedalButton(END_CONTROL, item.pluginType, false, true, false, false, false)} +
+
); + break; + default: + if (item.isSplitter()) { + + result.push(
+
+ {this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), false, true, true, false, false)} +
+
); + + } else { + result.push( +
+ {item.name} +
+ ) + let uiPlugin = this.model.getUiPlugin(item.pedalItem?.uri ?? ""); + let pluginMissing = uiPlugin == null; + + result.push(
+ {this.pedalButton( + item.pedalItem?.instanceId ?? -1, + item.pluginType, + !item.isEmpty(), + item.pedalItem?.isEnabled ?? false, + true, + pluginMissing, + uiPlugin ? ((uiPlugin.has_midi_input !== 0) || (uiPlugin.has_midi_output !== 0)) : false)} + +
); + + } + break; + } + + } + return result; + } + + canInputStero(item: PedalLayout): boolean { + if (item.pedalItem) { + let plugin = this.model.getUiPlugin(item.pedalItem.uri); + if (plugin) { + return plugin.audio_inputs === 2; + } + } + return true; + } + getNumberOfInputs(item: PedalLayout): number { + if (item.pedalItem) { + let plugin = this.model.getUiPlugin(item.pedalItem.uri); + if (plugin) { + return plugin.audio_inputs; + } + } + return 1; + } + getNumberOfOutputs(item: PedalLayout): number { + if (item.pedalItem) { + let plugin = this.model.getUiPlugin(item.pedalItem.uri); + if (plugin) { + return plugin.audio_outputs; + } + } + return 1; + } + + markStereoOutputs(layoutChain: PedalLayout[], numberOfInputs: number, numberOfOutputs: number) { + // analyze forward flow. + this.markStereoForward(layoutChain, numberOfInputs); + // mark items that feed a mono effect as mono. + this.markStereoBackward(layoutChain, numberOfOutputs); + } + + markStereoBackward(layoutChain: PedalLayout[], numberOfOutputs: number): number { + for (let i = layoutChain.length - 1; i >= 0; --i) { + let item = layoutChain[i]; + if (item.isSplitter()) { + item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs) + + this.markStereoBackward(item.topChildren, numberOfOutputs); + this.markStereoBackward(item.bottomChildren, numberOfOutputs); + let topInputs = item.topChildren[0].numberOfInputs; + let bottomInputs = item.bottomChildren[0].numberOfInputs; + + let splitItem = item.pedalItem as PedalboardSplitItem; + if (splitItem.getSplitType() !== SplitType.Lr) { + item.numberOfInputs = CalculateConnection(item.numberOfInputs, Math.max(topInputs, bottomInputs)); + } + } else if (item.isEnd()) { + + } else if (item.isStart()) { + item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); + return item.numberOfOutputs; + + } else if (item.isEmpty()) { + if (numberOfOutputs === 0) { + item.numberOfOutputs = 0; + item.numberOfInputs = CalculateConnection(item.numberOfInputs, 2); + } else { + item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); + item.numberOfInputs = CalculateConnection(item.numberOfInputs, numberOfOutputs); + } + } else { + item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs); + } + numberOfOutputs = item.numberOfInputs; + } + return numberOfOutputs; + } + markStereoForward(layoutChain: PedalLayout[], numberOfInputs: number): number { + if (layoutChain.length === 0) { + return numberOfInputs; + } + for (let i = 0; i < layoutChain.length; ++i) { + let item = layoutChain[i]; + if (item.isSplitter()) { + let splitter = item.pedalItem as PedalboardSplitItem; + item.numberOfInputs = numberOfInputs; + + let chainInputs = numberOfInputs; + if (splitter.getSplitType() === SplitType.Lr) { + chainInputs = CalculateConnection(numberOfInputs, 1); + } + let topOutputs = this.markStereoForward(item.topChildren, chainInputs); + let bottomOutputs = this.markStereoForward(item.bottomChildren, chainInputs); + + + if (splitter.getSplitType() === SplitType.Ab) { + if (splitter.isASelected()) { + item.numberOfOutputs = topOutputs; + } else { + item.numberOfOutputs = bottomOutputs; + } + } else { + item.numberOfOutputs = (topOutputs >= 1 || bottomOutputs >= 1) ? 2 : 1; + } + } else if (item.isStart()) { + item.numberOfOutputs = Math.min(PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length, 2); + } else if (item.isEnd()) { + item.numberOfInputs = + CalculateConnection( + Math.min(PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length, 2), + numberOfInputs); + return item.numberOfInputs; + } else if (item.isEmpty()) { + item.numberOfInputs = numberOfInputs; + if (numberOfInputs === 0) { + item.numberOfOutputs = 2; + } else { + item.numberOfOutputs = item.numberOfInputs; + } + } else { + if (item.numberOfInputs === 0) // zero-input plugins merge their output with the input. + { + item.numberOfInputs = numberOfInputs; + item.numberOfOutputs = Math.max(item.numberOfOutputs, numberOfInputs); + } else { + item.numberOfInputs = CalculateConnection(numberOfInputs, this.getNumberOfInputs(item)); + item.numberOfOutputs = this.getNumberOfOutputs(item); + } + } + numberOfInputs = item.numberOfOutputs; + } + return numberOfInputs; + } + + currentLayout?: PedalLayout[]; + private renderKey: number = 0; + render() { + const classes = withStyles.getClasses(this.props); + this.renderKey = 0; + let layoutChain = makeChain(this.model, this.state.pedalboard?.items); + let start = PedalLayout.Start(); + let end = PedalLayout.End(); + if (layoutChain.length !== 0) { + layoutChain.splice(0, 0, start); + layoutChain.splice(layoutChain.length, 0, end); + this.markStereoOutputs(layoutChain, 2, 2); + } + + let layoutSize = this.doLayout(layoutChain); + + + this.currentLayout = layoutChain; // save for mouse processing &c. + + return ( +
+
+ {this.renderChain(layoutChain, layoutSize)} +
+
+ ); + } + + }, + pedalboardStyles +)); + +export default PedalboardView \ No newline at end of file diff --git a/react/src/PerformanceView.tsx b/vite/src/pipedal/PerformanceView.tsx similarity index 87% rename from react/src/PerformanceView.tsx rename to vite/src/pipedal/PerformanceView.tsx index e89d776..e521535 100644 --- a/react/src/PerformanceView.tsx +++ b/vite/src/pipedal/PerformanceView.tsx @@ -20,9 +20,10 @@ import { Theme } from '@mui/material/styles'; import SaveIconOutline from '@mui/icons-material/Save'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; @@ -40,17 +41,18 @@ import { BankIndex } from './Banks'; import SnapshotEditor from './SnapshotEditor'; import { Snapshot } from './Pedalboard'; import JackStatusView from './JackStatusView'; -import {IDialogStackable, DialogStackState, removeDialogStackEntriesAbove, popDialogStack, pushDialogStack} from './DialogEx'; +import {IDialogStackable, popDialogStack, pushDialogStack} from './DialogStack'; +import { css } from '@emotion/react'; const selectColor = isDarkMode() ? "#888" : "#FFFFFF"; const styles = (theme: Theme) => createStyles({ - frame: { + frame: css({ position: "absolute", display: "flex", flexDirection: "column", flexWrap: "nowrap", justifyContent: "flex-start", left: "0px", top: "0px", bottom: "0px", right: "0px", overflow: "hidden" - }, - select: { // fu fu fu.Overrides for white selector on dark background. + }), + select: css({ // fu fu fu.Overrides for white selector on dark background. '&:before': { borderColor: selectColor, }, @@ -60,17 +62,16 @@ const styles = (theme: Theme) => createStyles({ '&:hover:not(.Mui-disabled):before': { borderColor: selectColor, } - }, - select_icon: { + }), + select_icon: css({ fill: selectColor, - }, + }), }); interface PerformanceViewProps extends WithStyles { open: boolean, onClose: () => void, - theme: Theme } interface PerformanceViewState { @@ -84,7 +85,7 @@ interface PerformanceViewState { export const PerformanceView = - withStyles(styles, { withTheme: true })( + withStyles( class extends ResizeResponsiveComponent implements IDialogStackable { model: PiPedalModel; @@ -104,7 +105,6 @@ export const PerformanceView = }; this.onPresetsChanged = this.onPresetsChanged.bind(this); this.onBanksChanged = this.onBanksChanged.bind(this); - this.handlePopState = this.handlePopState.bind(this); this.onPresetChangedChanged = this.onPresetChangedChanged.bind(this); } @@ -115,8 +115,6 @@ export const PerformanceView = this.props.onClose(); } - private stateWasPopped: boolean = false; - private hasHooks: boolean = false; @@ -129,52 +127,14 @@ export const PerformanceView = if (this.hasHooks) { - this.stateWasPopped = false; - window.addEventListener("popstate",this.handlePopState); - - pushDialogStack(this); - let dialogStackState: DialogStackState = {tag: this.getTag(),previousState: window.history.state as DialogStackState | null}; - window.history.replaceState(dialogStackState,"",window.location.href); - window.history.pushState( - null, - "", - "#" + this.getTag() - ); - } else { - if (!this.stateWasPopped) - { - window.history.back(); - } - } - } - } - - handlePopState(ev: PopStateEvent): any - { - let evTag: DialogStackState | null = ev.state as DialogStackState | null; - if (evTag && evTag.tag === this.getTag()) { - removeDialogStackEntriesAbove(this.getTag()); - - if (!this.stateWasPopped) - { - this.stateWasPopped = true; - popDialogStack (); - if (this.props.open) { - this.props.onClose(); - } - } - window.history.replaceState(evTag.previousState,"",window.location.href); - window.removeEventListener("popstate",this.handlePopState); - - if (window.location.hash === "#menu") // The menu popstate is next? - { - window.history.back(); // then clear off the menu popstate too. - } - ev.stopPropagation(); - } - } - + pushDialogStack(this); + } else { + popDialogStack(this); + } + } + } + onWindowSizeChanged(width: number, height: number): void { this.setState({ wrapSelects: width < 700 }); } @@ -268,13 +228,13 @@ export const PerformanceView = } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let wrapSelects = this.state.wrapSelects; let presets = this.state.presets; let banks = this.state.banks; return ( -
-
+
+
) } - } + }, + styles ); diff --git a/react/src/PiPedalError.tsx b/vite/src/pipedal/PiPedalError.tsx similarity index 100% rename from react/src/PiPedalError.tsx rename to vite/src/pipedal/PiPedalError.tsx diff --git a/react/src/PiPedalModel.tsx b/vite/src/pipedal/PiPedalModel.tsx similarity index 99% rename from react/src/PiPedalModel.tsx rename to vite/src/pipedal/PiPedalModel.tsx index b648e2b..b791956 100644 --- a/react/src/PiPedalModel.tsx +++ b/vite/src/pipedal/PiPedalModel.tsx @@ -370,12 +370,6 @@ interface ControlChangedBody { symbol: string; value: number; }; -interface PatchPropertyChangedBody { - clientId: number; - instanceId: number; - propertyUri: string; - value: any; -}; interface Vst3ControlChangedBody { clientId: number; instanceId: number; @@ -483,7 +477,7 @@ export class PiPedalModel //implements PiPedalModel } - private expectDisconnectTimer?: NodeJS.Timeout = undefined; + private expectDisconnectTimer?: number = undefined; cancelExpectDisconnectTimer() { @@ -762,7 +756,7 @@ export class PiPedalModel //implements PiPedalModel } - private updateLaterTimeout?: NodeJS.Timeout = undefined; + private updateLaterTimeout?: number = undefined; private clearPromptForUpdateTimer() { if (this.updateLaterTimeout) { @@ -896,7 +890,7 @@ export class PiPedalModel //implements PiPedalModel return this.webSocket; } - androidReconnectTimeout?: NodeJS.Timeout = undefined; + androidReconnectTimeout?: number = undefined; cancelAndroidReconnectTimer() { if (this.androidReconnectTimeout) { @@ -1119,7 +1113,7 @@ export class PiPedalModel //implements PiPedalModel } - backgroundStateTimeout?: NodeJS.Timeout = undefined; + backgroundStateTimeout?: number = undefined; exitBackgroundState() { if (this.backgroundStateTimeout) { @@ -1361,15 +1355,6 @@ export class PiPedalModel //implements PiPedalModel this.webSocket?.send("setSnapshots", { snapshots: snapshots, selectedSnapshot: selectedSnapshot }); } - private _setPedalboardPropertyValue(instanceId: number, propertyUri: string, value: any): void { - let body: PatchPropertyChangedBody = { - clientId: this.clientId, - instanceId: instanceId, - propertyUri: propertyUri, - value: value - }; - this.webSocket?.send("setPatchProperty", body); - } setInputVolume(volume_db: number): void { this._setInputVolume(volume_db, true); @@ -2994,10 +2979,9 @@ export class PiPedalModel //implements PiPedalModel //window.location.reload(); } - private networkChanging: boolean = false; private networkChanging_expectHotspot = false; - private expectNetworkChangeTimeout?: NodeJS.Timeout = undefined; - private hotspotReconnectTimer?: NodeJS.Timeout = undefined; + private expectNetworkChangeTimeout?: number = undefined; + private hotspotReconnectTimer?: number = undefined; async detectServer(address: string) { let port = window.location.port; @@ -3056,13 +3040,11 @@ export class PiPedalModel //implements PiPedalModel clearTimeout(this.expectNetworkChangeTimeout); this.expectNetworkChangeTimeout = undefined; } - this.networkChanging = false; this.expectDisconnect(ReconnectReason.Disconnected); } onNetworkChanging(hotspotConnected: boolean) { this.cancelOnNetworkChanging(); - this.networkChanging = true; this.networkChanging_expectHotspot = hotspotConnected; this.expectDisconnect(ReconnectReason.HotspotChanging); diff --git a/react/src/PiPedalSocket.tsx b/vite/src/pipedal/PiPedalSocket.tsx similarity index 100% rename from react/src/PiPedalSocket.tsx rename to vite/src/pipedal/PiPedalSocket.tsx diff --git a/react/src/PluginClass.tsx b/vite/src/pipedal/PluginClass.tsx similarity index 100% rename from react/src/PluginClass.tsx rename to vite/src/pipedal/PluginClass.tsx diff --git a/react/src/PluginControl.tsx b/vite/src/pipedal/PluginControl.tsx similarity index 96% rename from react/src/PluginControl.tsx rename to vite/src/pipedal/PluginControl.tsx index 69c3240..d421348 100644 --- a/react/src/PluginControl.tsx +++ b/vite/src/pipedal/PluginControl.tsx @@ -18,11 +18,13 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React, { TouchEvent, PointerEvent, ReactNode, Component, SyntheticEvent } from 'react'; +import { css } from '@emotion/react'; import Button from '@mui/material/Button'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles, {withTheme} from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { UiControl, ScalePoint } from './Lv2Plugin'; import Typography from '@mui/material/Typography'; import Input from '@mui/material/Input'; @@ -31,7 +33,7 @@ import Switch from '@mui/material/Switch'; import Utility, { nullCast } from './Utility'; import MenuItem from '@mui/material/MenuItem'; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; -import { ReactComponent as DialIcon } from './svg/fx_dial.svg'; +import DialIcon from './svg/fx_dial.svg?react'; import { isDarkMode } from './DarkMode'; import ControlTooltip from './ControlTooltip'; @@ -53,11 +55,11 @@ export const StandardItemSize = { width: 80, height: 140 } export const pluginControlStyles = (theme: Theme) => createStyles({ - frame: { + frame: css({ position: "relative", margin: "12px" - }, - switchTrack: { + }), + switchTrack: css({ height: '100%', width: '100%', borderRadius: 14 / 2, @@ -67,15 +69,15 @@ export const pluginControlStyles = (theme: Theme) => createStyles({ }), backgroundColor: theme.palette.primary.main, opacity: theme.palette.mode === 'light' ? 0.38 : 0.3 - }, - controlFrame: { + }), + controlFrame: css({ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between", height: 116 - }, + }), - titleSection: { + titleSection: css({ flex: "0 0 auto", alignSelf: "stretch", marginBottom: 8, marginLeft: 0, marginRight: 0 - }, - displayValue: { + }), + displayValue: css({ position: "absolute", top: 0, left: 0, @@ -85,13 +87,13 @@ export const pluginControlStyles = (theme: Theme) => createStyles({ background: theme.mainBackground, color: theme.palette.text.secondary, // zIndex: -1, - }, - midSection: { + }), + midSection: css({ flex: "1 1 1", display: "flex", flexFlow: "column nowrap", alignContent: "center", justifyContent: "center" - }, - editSection: { + }), + editSection: css({ flex: "0 0 0", display: "flex", flexFlow: "column nowrap", justifyContent: "center", position: "relative", width: 60, height: 28, minHeight: 28 - } + }) }); @@ -110,15 +112,15 @@ type PluginControlState = { }; const PluginControl = - withStyles(pluginControlStyles, { withTheme: true })( + withTheme(withStyles( class extends Component { - frameRef: React.RefObject; - imgRef: React.RefObject; - inputRef: React.RefObject; - selectRef: React.RefObject; + frameRef: React.RefObject; + imgRef: React.RefObject; + inputRef: React.RefObject; + selectRef: React.RefObject; - displayValueRef: React.RefObject; + displayValueRef: React.RefObject; model: PiPedalModel; @@ -267,7 +269,6 @@ const PluginControl = touchDown: boolean = false; touchIdentifier: number = -1; - private lastclickTimeMs: number = 0; private isTap: boolean = false; onTouchStart(e: TouchEvent) { @@ -598,6 +599,7 @@ const PluginControl = ); } if (control.isAbToggle()) { + let classes = withStyles.getClasses(this.props); // unchecked color is not gray. return ( @@ -742,7 +744,7 @@ const PluginControl = uiControl?: UiControl = undefined; render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let t = this.props.uiControl; if (!t) { return (
#Error
); @@ -772,11 +774,11 @@ const PluginControl = return (
{/* TITLE SECTION */} -
{/* CONTROL SECTION */} -
+
{isTrigger ? ( @@ -862,7 +864,7 @@ const PluginControl =
{/* LABEL/EDIT SECTION*/} -
+
{(!(isSelect || isOnOffSwitch || isTrigger)) && ( (isAbSwitch) ? ( @@ -909,7 +911,8 @@ const PluginControl = touchPointerId: number = 0; - } - ); + }, + pluginControlStyles + )); export default PluginControl; \ No newline at end of file diff --git a/react/src/PluginControlView.tsx b/vite/src/pipedal/PluginControlView.tsx similarity index 93% rename from react/src/PluginControlView.tsx rename to vite/src/pipedal/PluginControlView.tsx index 0eb74ab..7d07a58 100644 --- a/react/src/PluginControlView.tsx +++ b/vite/src/pipedal/PluginControlView.tsx @@ -19,9 +19,11 @@ import { ReactNode } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles, {withTheme} from './WithStyles'; +import {createStyles} from './WithStyles'; +import { css } from '@emotion/react'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { UiPlugin, UiControl, UiFileProperty, UiFrequencyPlot, ScalePoint } from './Lv2Plugin'; import { @@ -84,7 +86,7 @@ let endPluginInfo: UiPlugin = const styles = (theme: Theme) => createStyles({ - frame: { + frame: css({ display: "block", position: "relative", flexDirection: "row", @@ -94,8 +96,8 @@ const styles = (theme: Theme) => createStyles({ height: "100%", overflowX: "hidden", overflowY: "hidden" - }, - frameScrollLandscape: { + }), + frameScrollLandscape: css({ display: "block", position: "absolute", left: 0, top: 0, right: 0, bottom:0, @@ -105,8 +107,8 @@ const styles = (theme: Theme) => createStyles({ paddingBottom: "0px", overflowX: "auto", overflowY: "hidden" - }, - frameScrollPortrait: { + }), + frameScrollPortrait: css({ display: "block", position: "absolute", left: 0, top: 0, right: 0, bottom:0, @@ -116,8 +118,8 @@ const styles = (theme: Theme) => createStyles({ paddingBottom: "0px", overflowX: "hidden", overflowY: "auto" - }, - vuMeterL: { + }), + vuMeterL: css({ position: "absolute", left: 0, top: 0, paddingLeft: 6, @@ -126,8 +128,8 @@ const styles = (theme: Theme) => createStyles({ background: theme.mainBackground, zIndex: 3 - }, - vuMeterR: { + }), + vuMeterR: css({ position: "absolute", right: 0, top: 0, marginRight: 20, // has to potentially clear a scrollbar. @@ -136,8 +138,8 @@ const styles = (theme: Theme) => createStyles({ background: theme.mainBackground, zIndex: 3 - }, - vuMeterRLandscape: { + }), + vuMeterRLandscape: css({ position: "absolute", right: 0, top: 0, paddingRight: 6, @@ -146,9 +148,9 @@ const styles = (theme: Theme) => createStyles({ background: theme.mainBackground, zIndex: 3 - }, + }), - normalGrid: { + normalGrid: css({ position: "relative", paddingLeft: 30, paddingRight: 30, @@ -161,8 +163,8 @@ const styles = (theme: Theme) => createStyles({ - }, - landscapeGrid: { + }), + landscapeGrid: css({ paddingLeft: 40, paddingRight: 40, paddingTop: 8, // marginRight: 40, : bug in chrome layout engine wrt/ right margin/padding. // See the spacer div added after all controls in render() with provides the same effect. @@ -172,34 +174,34 @@ const styles = (theme: Theme) => createStyles({ overflowY: "hidden", flex: "0 0 auto", width: "fit-content" - }, - portgroupControlPadding: { + }), + portgroupControlPadding: css({ flex: "0 0 auto", marginTop: 12, marginBottom: 8 - }, + }), - controlPadding: { + controlPadding: css({ flex: "0 0 auto", marginTop: 0, marginBottom: 0, height: 116 - }, - controlPair: { + }), + controlPair: css({ display: "flex", flexFlow: "row nowrap", flex: "0 0 auto", height: 116 - }, - controlSpacer: { + }), + controlSpacer: css({ display: "none", minWidth: 0, width: 0, height: 116 - }, + }), - portGroup: { + portGroup: css({ marginLeft: 8, marginTop: 0, marginRight: 8, @@ -215,8 +217,8 @@ const styles = (theme: Theme) => createStyles({ display: "flex", flexDirection: "row", flexWrap: "wrap", flex: "0 1 auto", - }, - portGroupLandscape: { + }), + portGroupLandscape: css({ marginLeft: 8, marginTop: 0, marginRight: 8, @@ -235,8 +237,8 @@ const styles = (theme: Theme) => createStyles({ flex: "0 0 auto", width: "fit-content", minWidth: "max-content" - }, - portGroupTitle: { + }), + portGroupTitle: css({ position: "absolute", top: -15, background: theme.mainBackground, @@ -246,20 +248,20 @@ const styles = (theme: Theme) => createStyles({ paddingLeft: 8, paddingRight: 8, margin_right: 28 - }, - portGroupControls: { + }), + portGroupControls: css({ display: "flex", flexDirection: "row", flexWrap: "wrap", paddingTop: 6, paddingBottom: 8 - }, - portGroupControlsLandscape: { + }), + portGroupControlsLandscape: css({ display: "flex", flexFlow: "row nowrap", width: "fit-content", paddingTop: 6, paddingBottom: 8 - } + }) }); @@ -302,7 +304,7 @@ type PluginControlViewState = { }; const PluginControlView = - withStyles(styles, { withTheme: true })( + withTheme(withStyles( class extends ResizeResponsiveComponent { model: PiPedalModel; @@ -423,33 +425,38 @@ const PluginControlView = { this.onControlValueChanged(controlValue!.key, value) }} onPreviewChange={(value: number) => { this.onPreviewChange(controlValue!.key, value) }} - requestIMEEdit={(uiControl, value) => this.requestImeEdit(uiControl, value)} + requestIMEEdit={(uiControl: any, value: any) => this.requestImeEdit(uiControl, value)} /> )); } - push_control(controls: ReactNode[], pluginControl: UiControl,controlValues: ControlValue[]) + push_control(controls:ControlNodes, pluginControl: UiControl,controlValues: ControlValue[]) { // combine lamps with their previous control if ((pluginControl.isLamp() || pluginControl.isDbVu()) && controls.length !== 0) { + const classes = withStyles.getClasses(this.props); let newControl = this.makeStandardControl(pluginControl,controlValues); let previousControl = controls[controls.length-1]; - let pair = ( -
- {previousControl} - {newControl} -
- ); - controls[controls.length-1] = pair; + if (!(previousControl instanceof ControlGroup)) { + let pair = ( +
+ {previousControl as ReactNode} + {newControl} +
+ ); + controls[controls.length-1] = pair; // push a spacer control in order to make placing of extended controls predictable. - // (e.g.. inserting at position 4 still places the extended control after four previous controls - controls.push(( + // (e.g.. inserting at position 4 still places the extended control after four previous controls + controls.push(( -
- )); +
+ )); + } else { + controls.push(newControl); + } } else { controls.push( @@ -608,7 +615,7 @@ const PluginControlView = controlKeyIndex: number = 0; controlNodesToNodes(nodes: (ReactNode | ControlGroup)[]): ReactNode[] { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let isLandscapeGrid = this.state.landscapeGrid; let hasGroups = this.hasGroups(nodes); @@ -684,7 +691,7 @@ const PluginControlView = render(): ReactNode { this.controlKeyIndex = 0; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let pedalboardItem: PedalboardItem; let pedalboard = this.model.pedalboard.get(); if (this.props.instanceId === Pedalboard.START_CONTROL) { @@ -803,8 +810,9 @@ const PluginControlView = } - } - ); + }, + styles + )); export default PluginControlView; diff --git a/react/src/PluginIcon.tsx b/vite/src/pipedal/PluginIcon.tsx similarity index 73% rename from react/src/PluginIcon.tsx rename to vite/src/pipedal/PluginIcon.tsx index 99f550b..a273a61 100644 --- a/react/src/PluginIcon.tsx +++ b/vite/src/pipedal/PluginIcon.tsx @@ -19,52 +19,53 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModelFactory } from "./PiPedalModel"; import { PluginType } from './Lv2Plugin'; -import { ReactComponent as FxSimulatorIcon } from './svg/fx_simulator.svg'; -import { ReactComponent as FxPhaserIcon } from './svg/fx_phaser.svg'; -import { ReactComponent as FxFilterIcon } from './svg/fx_filter.svg'; -import { ReactComponent as FxDelayIcon } from './svg/fx_delay.svg'; +import FxSimulatorIcon from './svg/fx_simulator.svg?react'; +import FxPhaserIcon from './svg/fx_phaser.svg?react'; +import FxFilterIcon from './svg/fx_filter.svg?react'; +import FxDelayIcon from './svg/fx_delay.svg?react'; -import { ReactComponent as FxAmplifierIcon } from './svg/fx_amplifier.svg'; -import { ReactComponent as FxChorusIcon } from './svg/fx_chorus.svg'; -import { ReactComponent as FxModulatorIcon } from './svg/fx_modulator.svg'; -import { ReactComponent as FxReverbIcon } from './svg/fx_reverb.svg'; -import { ReactComponent as FxDistortionIcon } from './svg/fx_distortion.svg'; -import { ReactComponent as FxFlangerIcon } from './svg/fx_flanger.svg'; -import { ReactComponent as FxFilterHpIcon } from './svg/fx_filter_hp.svg'; -import { ReactComponent as FxParametricEqIcon } from './svg/fx_parametric_eq.svg'; -import { ReactComponent as FxEqIcon } from './svg/fx_eq.svg'; -import { ReactComponent as FxUtilityIcon } from './svg/fx_utility.svg'; -import { ReactComponent as FxCompressorIcon } from './svg/fx_compressor.svg'; -import { ReactComponent as FxLimiterIcon } from './svg/fx_limiter.svg'; -import { ReactComponent as FxConstantIcon } from './svg/fx_constant.svg'; -import { ReactComponent as FxFunctionIcon } from './svg/fx_function.svg'; -import { ReactComponent as FxGateIcon } from './svg/fx_gate.svg'; -import { ReactComponent as FxConverterIcon } from './svg/fx_converter.svg'; -import { ReactComponent as FxGeneratorIcon } from './svg/fx_generator.svg'; -import { ReactComponent as FxOscillatorIcon } from './svg/fx_oscillator.svg'; -import { ReactComponent as FxInstrumentIcon } from './svg/fx_instrument.svg'; -import { ReactComponent as FxMixerIcon } from './svg/fx_mixer.svg'; -import { ReactComponent as FxPitchIcon } from './svg/fx_pitch.svg'; -import { ReactComponent as FxSpatialIcon } from './svg/fx_spatial.svg'; -import { ReactComponent as FxSpectralIcon } from './svg/fx_spectral.svg'; -import { ReactComponent as FxAnalyzerIcon } from './svg/fx_analyzer.svg'; -import { ReactComponent as FxPluginIcon } from './svg/fx_plugin.svg'; -import { ReactComponent as SplitAIcon } from './svg/fx_split_a.svg'; -import { ReactComponent as SplitBIcon } from './svg/fx_split_b.svg'; -import { ReactComponent as SplitMixIcon } from './svg/fx_dial.svg'; -import { ReactComponent as SplitLrIcon } from './svg/fx_lr.svg'; -import { ReactComponent as FxEmptyIcon } from './svg/fx_empty.svg'; +import FxAmplifierIcon from './svg/fx_amplifier.svg?react'; +import FxChorusIcon from './svg/fx_chorus.svg?react'; +import FxModulatorIcon from './svg/fx_modulator.svg?react'; +import FxReverbIcon from './svg/fx_reverb.svg?react'; +import FxDistortionIcon from './svg/fx_distortion.svg?react'; +import FxFlangerIcon from './svg/fx_flanger.svg?react'; +import FxFilterHpIcon from './svg/fx_filter_hp.svg?react'; +import FxParametricEqIcon from './svg/fx_parametric_eq.svg?react'; +import FxEqIcon from './svg/fx_eq.svg?react'; +import FxUtilityIcon from './svg/fx_utility.svg?react'; +import FxCompressorIcon from './svg/fx_compressor.svg?react'; +import FxLimiterIcon from './svg/fx_limiter.svg?react'; +import FxConstantIcon from './svg/fx_constant.svg?react'; +import FxFunctionIcon from './svg/fx_function.svg?react'; +import FxGateIcon from './svg/fx_gate.svg?react'; +import FxConverterIcon from './svg/fx_converter.svg?react'; +import FxGeneratorIcon from './svg/fx_generator.svg?react'; +import FxOscillatorIcon from './svg/fx_oscillator.svg?react'; +import FxInstrumentIcon from './svg/fx_instrument.svg?react'; +import FxMixerIcon from './svg/fx_mixer.svg?react'; +import FxPitchIcon from './svg/fx_pitch.svg?react'; +import FxSpatialIcon from './svg/fx_spatial.svg?react'; +import FxSpectralIcon from './svg/fx_spectral.svg?react'; +import FxAnalyzerIcon from './svg/fx_analyzer.svg?react'; +import FxPluginIcon from './svg/fx_plugin.svg?react'; +import SplitAIcon from './svg/fx_split_a.svg?react'; +import SplitBIcon from './svg/fx_split_b.svg?react'; +import SplitMixIcon from './svg/fx_dial.svg?react'; +import SplitLrIcon from './svg/fx_lr.svg?react'; +import FxEmptyIcon from './svg/fx_empty.svg?react'; -import { ReactComponent as FxTerminalIcon } from './svg/fx_terminal.svg'; +import FxTerminalIcon from './svg/fx_terminal.svg?react'; -import {isDarkMode} from './DarkMode'; +import { isDarkMode } from './DarkMode'; @@ -91,9 +92,8 @@ export interface PluginIconProps extends WithStyles { export function SelectSvgIcon(plugin_type: PluginType, className: string, size: number = 24, opacity: number = 1.0, missingPlugin: boolean = false) { let color = ""; - if (missingPlugin) - { - color = isDarkMode()? "#C00000": "#B00000"; + if (missingPlugin) { + color = isDarkMode() ? "#C00000" : "#B00000"; } let myStyle = { width: size, height: size, opacity: opacity, color: color, fill: color }; switch (plugin_type) { @@ -175,8 +175,8 @@ export function SelectSvgIcon(plugin_type: PluginType, className: string, size: case PluginType.Terminal: return ; - default: - case PluginType.Plugin: + default: + case PluginType.Plugin: return ; } } @@ -251,50 +251,23 @@ export function SelectBaseIcon(plugin_type: PluginType): string { } } -var colors: string[] = [ - "#14003d", - "#630651", - "#005010", - "#281431", - "#5E0012", - "#401222", - "#3B1502" -]; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -function colorFromHash(uri: string) { - let hash = hashString(uri); - - return colors[hash % colors.length]; - -} - -function hashString(str: string): number { - var hash = 0, i, chr; - for (i = 0; i < str.length; i++) { - chr = str.charCodeAt(i); - hash = ((hash << 5) - hash) + chr; - hash |= 0; // Convert to 32bit integer - } - if (hash < 0) return -hash; - return hash; -} export function SelectIconUri(plugin_type: PluginType) { let icon = SelectBaseIcon(plugin_type); return PiPedalModelFactory.getInstance().svgImgUrl(icon); } -const PluginIcon = withStyles(styles)((props: PluginIconProps) => { - const { classes, pluginType, opacity,pluginMissing } = props; +const PluginIcon = withStyles((props: PluginIconProps) => { + const { pluginType, opacity, pluginMissing } = props; + const classes = withStyles.getClasses(props); - let pluginMissing_ : boolean = pluginMissing??false; + let pluginMissing_: boolean = pluginMissing ?? false; let size: number = 24; if (props.size) size = props.size; let topVal: number = (props.offsetY ?? 0); - let svgIcon = SelectSvgIcon(pluginType, classes.icon, size, opacity,pluginMissing_); + let svgIcon = SelectSvgIcon(pluginType, classes.icon, size, opacity, pluginMissing_); if (svgIcon) { return svgIcon; } @@ -311,6 +284,7 @@ const PluginIcon = withStyles(styles)((props: PluginIconProps) => { // mask: maskText, WebkitMask: maskText, WebkitMaskSize: size + "px", maskSize: size + "px" }} // > 
// ); -}); +}, + styles); export default PluginIcon; \ No newline at end of file diff --git a/react/src/PluginInfoDialog.tsx b/vite/src/pipedal/PluginInfoDialog.tsx similarity index 94% rename from react/src/PluginInfoDialog.tsx rename to vite/src/pipedal/PluginInfoDialog.tsx index fb5d79d..ce184d9 100644 --- a/react/src/PluginInfoDialog.tsx +++ b/vite/src/pipedal/PluginInfoDialog.tsx @@ -19,9 +19,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import Button from '@mui/material/Button'; import DialogEx from './DialogEx'; import MuiDialogTitle from '@mui/material/DialogTitle'; @@ -36,23 +37,22 @@ import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; import { UiPlugin, UiControl } from './Lv2Plugin'; import PluginIcon from './PluginIcon'; import { Remark } from 'react-remark'; +import { css } from '@emotion/react'; + -/* eslint-disable */ -let myTheme: Theme | undefined = undefined; const styles = (theme: Theme) => { - myTheme = theme; return createStyles({ root: { margin: 0, padding: theme.spacing(2), }, - closeButton: { + closeButton: css({ position: 'absolute', right: theme.spacing(1), top: theme.spacing(1), color: theme.palette.grey[500], - }, + }), icon: { fill: theme.palette.text.primary, opacity: 0.6 @@ -83,18 +83,23 @@ export interface PluginInfoDialogTitleProps extends WithStyles { // ); // }); -const PluginInfoDialogContent = withStyles((theme: Theme) => ({ - root: { - padding: theme.spacing(2), - }, -}))(MuiDialogContent); +const PluginInfoDialogContent = withStyles( + MuiDialogContent, + (theme: Theme) => ({ + root: { + padding: theme.spacing(2), + } + }) +); -const PluginInfoDialogActions = withStyles((theme: Theme) => ({ - root: { - margin: 0, - padding: theme.spacing(1), - }, -}))(MuiDialogActions); +const PluginInfoDialogActions = withStyles(MuiDialogActions, + (theme: Theme) => ({ + root: { + margin: 0, + padding: theme.spacing(1), + }, + }) +); export interface PluginInfoProps extends WithStyles { plugin_uri: string @@ -200,11 +205,12 @@ function makeControls(controls: UiControl[]) { -const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => { +const PluginInfoDialog = withStyles((props: PluginInfoProps) => { let model = PiPedalModelFactory.getInstance(); const [open, setOpen] = React.useState(false); - let { classes, plugin_uri } = props; + let { plugin_uri } = props; + let classes = withStyles.getClasses(props); const handleClickOpen = () => { setOpen(true); @@ -331,6 +337,7 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => { )}
); -}); +}, +styles); export default PluginInfoDialog; diff --git a/react/src/PluginOutputControl.tsx b/vite/src/pipedal/PluginOutputControl.tsx similarity index 95% rename from react/src/PluginOutputControl.tsx rename to vite/src/pipedal/PluginOutputControl.tsx index c5de88e..d4e8642 100644 --- a/react/src/PluginOutputControl.tsx +++ b/vite/src/pipedal/PluginOutputControl.tsx @@ -19,9 +19,10 @@ import React, { Component } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { UiControl } from './Lv2Plugin'; import Typography from '@mui/material/Typography'; import { PiPedalModel, PiPedalModelFactory, MonitorPortHandle, State } from './PiPedalModel'; @@ -29,6 +30,7 @@ import { isDarkMode } from './DarkMode'; import GxTunerControl from './GxTunerControl'; import Units from './Units'; import ControlTooltip from './ControlTooltip'; +import { css } from '@emotion/react'; @@ -65,11 +67,11 @@ function makeLedGradient(color: string) { const styles = (theme: Theme) => createStyles({ - frame: { + frame: css({ position: "relative", margin: "12px" - }, - switchTrack: { + }), + switchTrack: css({ height: '100%', width: '100%', borderRadius: 14 / 2, @@ -79,8 +81,8 @@ const styles = (theme: Theme) => createStyles({ }), backgroundColor: theme.palette.secondary.main, opacity: theme.palette.mode === 'light' ? 0.38 : 0.3 - }, - displayValue: { + }), + displayValue: css({ position: "absolute", top: 0, left: 0, @@ -90,25 +92,25 @@ const styles = (theme: Theme) => createStyles({ background: "white", color: "#666", // zIndex: -1, - }, - controlFrame: { + }), + controlFrame: css({ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between", height: 116 - }, + }), - titleSection: { + titleSection: css({ flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 0, marginRight: 0 - }, + }), - midSection: { + midSection: css({ flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center" - }, + }), - editSection: { + editSection: css({ flex: "0 0 28", position: "relative", width: 60, height: 28, minHeight: 28 - }, - editSectionNoContent: { + }), + editSectionNoContent: css({ flex: "0 0 28", position: "relative", width: 1, height: 28, minHeight: 28 - } + }) }); @@ -123,14 +125,14 @@ type PluginOutputControlState = { }; const PluginOutputControl = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { private model: PiPedalModel; - private vuRef: React.RefObject; - private dbVuRef: React.RefObject; - private dbVuTelltaleRef: React.RefObject; - private lampRef: React.RefObject; + private vuRef: React.RefObject; + private dbVuRef: React.RefObject; + private dbVuTelltaleRef: React.RefObject; + private lampRef: React.RefObject; constructor(props: PluginOutputControlProps) { @@ -330,7 +332,7 @@ const PluginOutputControl = } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let control: UiControl = this.props.uiControl; @@ -432,7 +434,7 @@ const PluginOutputControl =
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
+
@@ -482,7 +484,7 @@ const PluginOutputControl =
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
+
); @@ -514,7 +516,7 @@ const PluginOutputControl =
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
+
); @@ -543,13 +545,14 @@ const PluginOutputControl =
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
+
); } } - } + }, + styles ); export default PluginOutputControl; \ No newline at end of file diff --git a/react/src/PluginPreset.tsx b/vite/src/pipedal/PluginPreset.tsx similarity index 100% rename from react/src/PluginPreset.tsx rename to vite/src/pipedal/PluginPreset.tsx diff --git a/react/src/PluginPresetSelector.tsx b/vite/src/pipedal/PluginPresetSelector.tsx similarity index 96% rename from react/src/PluginPresetSelector.tsx rename to vite/src/pipedal/PluginPresetSelector.tsx index 6a1f0f4..8aa2f2a 100644 --- a/react/src/PluginPresetSelector.tsx +++ b/vite/src/pipedal/PluginPresetSelector.tsx @@ -21,9 +21,10 @@ import { SyntheticEvent, Component } from 'react'; import IconButton from '@mui/material/IconButton'; import { PiPedalModel, PiPedalModelFactory, PluginPresetsChangedHandle } from './PiPedalModel'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import {createStyles} from './WithStyles'; + import PluginPresetsDialog from './PluginPresetsDialog'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; @@ -33,8 +34,8 @@ import {PluginUiPresets} from './PluginPreset'; import Divider from "@mui/material/Divider"; -import {ReactComponent as PluginPresetsIcon} from "./svg/ic_pluginpreset.svg"; -import {ReactComponent as PluginPresetIcon} from "./svg/ic_pluginpreset2.svg"; +import PluginPresetsIcon from "./svg/ic_pluginpreset.svg?react"; +import PluginPresetIcon from "./svg/ic_pluginpreset2.svg?react"; interface PluginPresetSelectorProps extends WithStyles { pluginUri?: string; @@ -74,7 +75,7 @@ const styles = (theme: Theme) => createStyles({ const PluginPresetSelector = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { model: PiPedalModel; @@ -282,10 +283,10 @@ const PluginPresetSelector = } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); - //let classes = this.props.classes; - //let classes = this.props.classes; + //const classes = withStyles.getClasses(this.props); + //const classes = withStyles.getClasses(this.props); if ((!this.props.pluginUri)) { return (
); @@ -340,6 +341,7 @@ const PluginPresetSelector = ); } - }); + }, + styles); export default PluginPresetSelector; diff --git a/react/src/PluginPresetsDialog.tsx b/vite/src/pipedal/PluginPresetsDialog.tsx similarity index 95% rename from react/src/PluginPresetsDialog.tsx rename to vite/src/pipedal/PluginPresetsDialog.tsx index 4dbfb1d..2573c78 100644 --- a/react/src/PluginPresetsDialog.tsx +++ b/vite/src/pipedal/PluginPresetsDialog.tsx @@ -27,7 +27,7 @@ import DialogEx from './DialogEx'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import DraggableGrid, { ScrollDirection } from './DraggableGrid'; -import MoreVertIcon from '@mui/icons-material//MoreVert'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import Menu from '@mui/material/Menu'; @@ -37,20 +37,23 @@ import UploadPresetDialog from './UploadPresetDialog'; import {PluginUiPresets,PluginUiPreset} from './PluginPreset'; import SelectHoverBackground from './SelectHoverBackground'; -import CloseIcon from '@mui/icons-material//Close'; -import ArrowBackIcon from '@mui/icons-material//ArrowBack'; -import EditIcon from '@mui/icons-material//Edit'; +import CloseIcon from '@mui/icons-material/Close'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import EditIcon from '@mui/icons-material/Edit'; import RenameDialog from './RenameDialog'; import Slide, {SlideProps} from '@mui/material/Slide'; -import createStyles from '@mui/styles/createStyles'; +import {createStyles} from './WithStyles'; + import { Theme } from '@mui/material/styles'; -import { WithStyles, withStyles} from '@mui/styles'; -import {ReactComponent as PluginPresetIcon} from "./svg/ic_pluginpreset2.svg"; -import { ReactComponent as DownloadIcon} from './svg/file_download_black_24dp.svg'; -import { ReactComponent as UploadIcon} from './svg/file_upload_black_24dp.svg'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import PluginPresetIcon from "./svg/ic_pluginpreset2.svg?react"; +import DownloadIcon from './svg/file_download_black_24dp.svg?react'; +import UploadIcon from './svg/file_upload_black_24dp.svg?react'; +import { css } from '@emotion/react'; interface PluginPresetsDialogProps extends WithStyles { @@ -79,27 +82,27 @@ interface PluginPresetsDialogState { const styles = (theme: Theme) => createStyles({ - listIcon: { + listIcon: css({ width: 24, height: 24, opacity: 0.6,fill: theme.palette.text.primary - }, - dialogAppBar: { + }), + dialogAppBar: css({ position: 'relative', top: 0, left: 0 - }, - itemBackground: { + }), + itemBackground: css({ background: theme.palette.background.paper - }, - dialogActionBar: { + }), + dialogActionBar: css({ position: 'relative', top: 0, left: 0, background: "black" - }, - dialogTitle: { + }), + dialogTitle: css({ marginLeft: theme.spacing(2), textOverflow: "ellipsis", flex: "1 1", - }, - itemFrame: { + }), + itemFrame: css({ display: "flex", flexDirection: "row", flexWrap: "nowrap", @@ -109,19 +112,19 @@ const styles = (theme: Theme) => createStyles({ textAlign: "left", justifyContent: "center", paddingLeft: 8 - }, - iconFrame: { + }), + iconFrame: css({ flex: "0 0 auto", - }, - itemIcon: { + }), + itemIcon: css({ width: 24, height: 24, margin: 12, opacity: 0.6, fill: theme.palette.text.primary - }, - itemLabel: { + }), + itemLabel: css({ flex: "1 1 1px", marginLeft: 8 - } + }) }); @@ -133,8 +136,7 @@ const Transition = React.forwardRef(function Transition( }); -const PluginPresetsDialog = withStyles(styles, { withTheme: true })( - +const PluginPresetsDialog = withStyles( class extends Component { model: PiPedalModel; @@ -258,7 +260,7 @@ const PluginPresetsDialog = withStyles(styles, { withTheme: true })( mapElement(el: any): React.ReactNode { let presetEntry = el as PluginUiPreset; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let selectedItem = this.state.selectedItem; return (
@@ -347,7 +349,7 @@ const PluginPresetsDialog = withStyles(styles, { withTheme: true })( render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar; let defaultSelectedIndex = this.getSelectedIndex(); @@ -496,8 +498,8 @@ const PluginPresetsDialog = withStyles(styles, { withTheme: true })( } - } - + }, + styles ); export default PluginPresetsDialog; \ No newline at end of file diff --git a/react/src/PresetDialog.tsx b/vite/src/pipedal/PresetDialog.tsx similarity index 95% rename from react/src/PresetDialog.tsx rename to vite/src/pipedal/PresetDialog.tsx index f9f3531..5c7e5f8 100644 --- a/react/src/PresetDialog.tsx +++ b/vite/src/pipedal/PresetDialog.tsx @@ -18,6 +18,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React, { SyntheticEvent,Component } from 'react'; +import { css } from '@emotion/react'; import {isDarkMode} from './DarkMode'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; @@ -28,7 +29,7 @@ import DialogEx from './DialogEx'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import DraggableGrid, { ScrollDirection } from './DraggableGrid'; -import MoreVertIcon from '@mui/icons-material//MoreVert'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import Menu from '@mui/material/Menu'; @@ -37,17 +38,20 @@ import Fade from '@mui/material/Fade'; import UploadPresetDialog from './UploadPresetDialog'; import SelectHoverBackground from './SelectHoverBackground'; -import CloseIcon from '@mui/icons-material//Close'; -import ArrowBackIcon from '@mui/icons-material//ArrowBack'; -import EditIcon from '@mui/icons-material//Edit'; +import CloseIcon from '@mui/icons-material/Close'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import EditIcon from '@mui/icons-material/Edit'; import RenameDialog from './RenameDialog'; import Slide, {SlideProps} from '@mui/material/Slide'; -import createStyles from '@mui/styles/createStyles'; +import {createStyles} from './WithStyles'; + import { Theme } from '@mui/material/styles'; -import { WithStyles, withStyles} from '@mui/styles'; -import { ReactComponent as DownloadIcon} from './svg/file_download_black_24dp.svg'; -import { ReactComponent as UploadIcon} from './svg/file_upload_black_24dp.svg'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; + +import DownloadIcon from './svg/file_download_black_24dp.svg?react'; +import UploadIcon from './svg/file_upload_black_24dp.svg?react'; interface PresetDialogProps extends WithStyles { @@ -74,26 +78,26 @@ interface PresetDialogState { const styles = (theme: Theme) => createStyles({ - listIcon: { + listIcon: css({ width: 24, height: 24, opacity: 0.6, fill: theme.palette.text.primary - }, - dialogAppBar: { + }), + dialogAppBar: css({ position: 'relative', top: 0, left: 0 - }, - dialogActionBar: { + }), + dialogActionBar: css({ position: 'relative', top: 0, left: 0, background: "black" - }, - dialogTitle: { + }), + dialogTitle: css({ marginLeft: theme.spacing(2), flex: 1, - }, - itemBackground: { + }), + itemBackground: css({ background: theme.palette.background.paper - }, - itemFrame: { + }), + itemFrame: css({ display: "flex", flexDirection: "row", flexWrap: "nowrap", @@ -103,18 +107,18 @@ const styles = (theme: Theme) => createStyles({ textAlign: "left", justifyContent: "center", paddingLeft: 8 - }, - iconFrame: { + }), + iconFrame: css({ flex: "0 0 auto", - }, - itemIcon: { + }), + itemIcon: css({ width: 24, height: 24, margin: 12, opacity: 0.6 - }, - itemLabel: { + }), + itemLabel: css({ flex: "1 1 1px", marginLeft: 8 - } + }) }); @@ -126,8 +130,7 @@ const Transition = React.forwardRef(function Transition( }); -const PresetDialog = withStyles(styles, { withTheme: true })( - +const PresetDialog = withStyles( class extends Component { model: PiPedalModel; @@ -247,7 +250,7 @@ const PresetDialog = withStyles(styles, { withTheme: true })( mapElement(el: any): React.ReactNode { let presetEntry = el as PresetIndexEntry; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let selectedItem = this.isEditMode() ? this.state.selectedItem : this.state.presets.selectedInstanceId; return (
@@ -333,7 +336,7 @@ const PresetDialog = withStyles(styles, { withTheme: true })( render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar; let defaultSelectedIndex = this.getSelectedIndex(); @@ -473,8 +476,8 @@ const PresetDialog = withStyles(styles, { withTheme: true })( } - } - + }, + styles ); export default PresetDialog; \ No newline at end of file diff --git a/react/src/PresetSelector.tsx b/vite/src/pipedal/PresetSelector.tsx similarity index 98% rename from react/src/PresetSelector.tsx rename to vite/src/pipedal/PresetSelector.tsx index b9776a2..05cf62e 100644 --- a/react/src/PresetSelector.tsx +++ b/vite/src/pipedal/PresetSelector.tsx @@ -23,9 +23,10 @@ import { PiPedalModel, PiPedalModelFactory, PresetIndex } from './PiPedalModel'; import SaveIconOutline from '@mui/icons-material/Save'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import {createStyles} from './WithStyles'; + import PresetDialog from './PresetDialog'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; @@ -78,7 +79,7 @@ const styles = (theme: Theme) => createStyles({ const PresetSelector = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { model: PiPedalModel; @@ -290,9 +291,9 @@ const PresetSelector = } render() { - //let classes = this.props.classes; + //const classes = withStyles.getClasses(this.props); let presets = this.state.presets; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); return (
{ - refText: React.RefObject; + refText: React.RefObject; constructor(props: RadioSelectDialogProps) { super(props); diff --git a/react/src/Rect.tsx b/vite/src/pipedal/Rect.tsx similarity index 100% rename from react/src/Rect.tsx rename to vite/src/pipedal/Rect.tsx diff --git a/react/src/Rectangle.tsx b/vite/src/pipedal/Rectangle.tsx similarity index 100% rename from react/src/Rectangle.tsx rename to vite/src/pipedal/Rectangle.tsx diff --git a/react/src/RenameDialog.tsx b/vite/src/pipedal/RenameDialog.tsx similarity index 95% rename from react/src/RenameDialog.tsx rename to vite/src/pipedal/RenameDialog.tsx index cafe277..c795c60 100644 --- a/react/src/RenameDialog.tsx +++ b/vite/src/pipedal/RenameDialog.tsx @@ -43,7 +43,7 @@ export interface RenameDialogState { export default class RenameDialog extends ResizeResponsiveComponent { - refText: React.RefObject; + refText: React.RefObject; constructor(props: RenameDialogProps) { super(props); @@ -125,8 +125,10 @@ export default class RenameDialog extends ResizeResponsiveComponent { onClick: () => void; onClearFilterClick: () => void; onApplyFilter: () => void; - inputRef?: React.RefObject; + inputRef?: React.RefObject; showSearchIcon?: boolean } @@ -48,11 +49,10 @@ interface SearchControlProps extends WithStyles { interface SearchControlState { } -const SearchControl = withStyles(styles, { withTheme: true })( - +const SearchControl = withTheme(withStyles( class extends React.Component { - defaultInputRef: React.RefObject; + defaultInputRef: React.RefObject; constructor(props: SearchControlProps) { super(props); @@ -61,7 +61,7 @@ const SearchControl = withStyles(styles, { withTheme: true })( }; this.defaultInputRef = React.createRef(); } - getInputRef(): React.RefObject { + getInputRef(): React.RefObject { if (this.props.inputRef) { return this.props.inputRef; } @@ -146,7 +146,8 @@ const SearchControl = withStyles(styles, { withTheme: true })(
); } - } -); + }, + styles +)); export default SearchControl; diff --git a/react/src/SearchFilter.tsx b/vite/src/pipedal/SearchFilter.tsx similarity index 100% rename from react/src/SearchFilter.tsx rename to vite/src/pipedal/SearchFilter.tsx diff --git a/react/src/SelectChannelsDialog.tsx b/vite/src/pipedal/SelectChannelsDialog.tsx similarity index 90% rename from react/src/SelectChannelsDialog.tsx rename to vite/src/pipedal/SelectChannelsDialog.tsx index c79f7e4..e274adc 100644 --- a/react/src/SelectChannelsDialog.tsx +++ b/vite/src/pipedal/SelectChannelsDialog.tsx @@ -20,7 +20,6 @@ import { useState } from 'react'; import Button from '@mui/material/Button'; import List from '@mui/material/List'; -import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import DialogTitle from '@mui/material/DialogTitle'; import DialogActions from '@mui/material/DialogActions'; @@ -28,6 +27,7 @@ import DialogEx from './DialogEx'; import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; +import { ListItemButton } from '@mui/material'; @@ -108,14 +108,14 @@ function SelectChannelsDialog(props: SelectChannelsDialogProps) { Select Channels {availableChannels.map((channel) => ( - + toggleSelect(channel)} style={{marginLeft: 24}} /> } label={makeChannelName(channel)} /> - + ) )} @@ -173,15 +173,15 @@ function SelectChannelsDialog(props: SelectChannelsDialogProps) { > - handleListItemClick("Stereo")} key={"Stereo"} selected={selectionKey === "Stereo"} > + handleListItemClick("Stereo")} key={"Stereo"} selected={selectionKey === "Stereo"} > - - handleListItemClick("Left")} key={"LeftChannel"} selected={selectionKey === "Left"} > + + handleListItemClick("Left")} key={"LeftChannel"} selected={selectionKey === "Left"} > - - handleListItemClick("Right")} key={"RightChannel"} selected={selectionKey === "Right"}> + + handleListItemClick("Right")} key={"RightChannel"} selected={selectionKey === "Right"}> - + ); diff --git a/react/src/SelectHoverBackground.tsx b/vite/src/pipedal/SelectHoverBackground.tsx similarity index 89% rename from react/src/SelectHoverBackground.tsx rename to vite/src/pipedal/SelectHoverBackground.tsx index 1285f44..060136c 100644 --- a/react/src/SelectHoverBackground.tsx +++ b/vite/src/pipedal/SelectHoverBackground.tsx @@ -21,31 +21,33 @@ import React,{ Component, SyntheticEvent } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; + +import {createStyles} from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import { css } from '@emotion/react'; const styles = ({ palette }: Theme) => createStyles({ - frame: { + frame: css({ position: "absolute", width: "100%", height: "100%" - }, - hover: { + }), + hover: css({ position: "absolute", width: "100%", height: "100%", background: palette.action.hover, //opacity: palette.action.hoverOpacity - }, - selected: { + }), + selected: css({ position: "absolute", width: "100%", height: "100%", background: palette.action.selected, //opacity: palette.action.selectedOpacity - } + }) }); @@ -55,7 +57,6 @@ interface HoverProps extends WithStyles { selected: boolean; showHover?: boolean; borderRadius?: number; - theme: Theme; children?: React.ReactNode; } @@ -64,7 +65,7 @@ interface HoverState { export const SelectHoverBackground = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { constructor(props: HoverProps) { @@ -98,11 +99,11 @@ export const SelectHoverBackground = } - private selfRef: React.RefObject; - private hoverElementRef: React.RefObject; + private selfRef: React.RefObject; + private hoverElementRef: React.RefObject; render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let isSelected = this.props.selected; return (
{ this.handleMouseOver(e); }} onMouseLeave={(e) => { this.handleMouseLeave(e) }} @@ -115,7 +116,8 @@ export const SelectHoverBackground =
); } - } + }, + styles ); export default SelectHoverBackground; diff --git a/react/src/SelectMidiChannelsDialog.tsx b/vite/src/pipedal/SelectMidiChannelsDialog.tsx similarity index 97% rename from react/src/SelectMidiChannelsDialog.tsx rename to vite/src/pipedal/SelectMidiChannelsDialog.tsx index 23b74a5..22a57e6 100644 --- a/react/src/SelectMidiChannelsDialog.tsx +++ b/vite/src/pipedal/SelectMidiChannelsDialog.tsx @@ -20,7 +20,7 @@ import { useState } from 'react'; import Button from '@mui/material/Button'; import List from '@mui/material/List'; -import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; import DialogTitle from '@mui/material/DialogTitle'; import DialogActions from '@mui/material/DialogActions'; import FormControlLabel from '@mui/material/FormControlLabel'; @@ -99,14 +99,14 @@ function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) { Select MIDI Device {availableChannels.map((channel) => ( - + toggleSelect(channel)} key={channel.name} /> } label={channel.description} /> - + ) )} diff --git a/react/src/SelectThemeDialog.tsx b/vite/src/pipedal/SelectThemeDialog.tsx similarity index 100% rename from react/src/SelectThemeDialog.tsx rename to vite/src/pipedal/SelectThemeDialog.tsx diff --git a/react/src/SettingsDialog.tsx b/vite/src/pipedal/SettingsDialog.tsx similarity index 98% rename from react/src/SettingsDialog.tsx rename to vite/src/pipedal/SettingsDialog.tsx index cdde593..3cca4ef 100644 --- a/react/src/SettingsDialog.tsx +++ b/vite/src/pipedal/SettingsDialog.tsx @@ -29,7 +29,7 @@ import ButtonBase from "@mui/material/ButtonBase"; import AppBar from '@mui/material/AppBar'; import Button from '@mui/material/Button'; import Toolbar from '@mui/material/Toolbar'; -import ArrowBackIcon from '@mui/icons-material//ArrowBack'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import JackConfiguration, { JackChannelSelection } from './Jack'; import Divider from '@mui/material/Divider'; import SelectChannelsDialog from './SelectChannelsDialog'; @@ -49,12 +49,14 @@ import SystemMidiBindingsDialog from './SystemMidiBindingsDialog'; import SelectThemeDialog from './SelectThemeDialog'; import Slide, { SlideProps } from '@mui/material/Slide'; -import createStyles from '@mui/styles/createStyles'; +import {createStyles} from './WithStyles'; + import { Theme } from '@mui/material/styles'; -import { WithStyles, withStyles } from '@mui/styles'; +import { withStyles } from "tss-react/mui"; +import WithStyles from './WithStyles'; import { canScaleWindow, getWindowScaleOptions, getWindowScaleText, setWindowScale, getWindowScale } from './WindowScale'; import OptionsDialog from './OptionsDialog'; - +import { css } from '@emotion/react'; interface SettingsDialogProps extends WithStyles { @@ -99,56 +101,56 @@ interface SettingsDialogState { const styles = (theme: Theme) => createStyles({ - dialogAppBar: { + dialogAppBar: css({ position: 'relative', top: 0, left: 0 - }, - dialogTitle: { + }), + dialogTitle: css({ marginLeft: theme.spacing(2), flex: 1, - }, - cpuStatusColor: { + }), + cpuStatusColor: css({ color: theme.palette.text.secondary, opacity: 0.7 - }, - sectionHead: { + }), + sectionHead: css({ marginLeft: 24, marginRight: 24, marginTop: 16, paddingBottom: 12 - }, - textBlock: { + }), + textBlock: css({ marginLeft: 24, marginRight: 24, marginTop: 16, paddingBottom: 0, opacity: 0.95 - }, - textBlockIndented: { + }), + textBlockIndented: css({ marginLeft: 40, marginRight: 24, marginTop: 16, paddingBottom: 0, opacity: 0.95 - }, + }), - setting: { + setting: css({ minHeight: 64, width: "100%", textAlign: "left", paddingLeft: 22, paddingRight: 22 - }, - primaryItem: { + }), + primaryItem: css({ - }, - secondaryItem: { + }), + secondaryItem: css({ - } + }) }); @@ -162,8 +164,7 @@ const Transition = React.forwardRef(function Transition( -const SettingsDialog = withStyles(styles, { withTheme: true })( - +const SettingsDialog = withStyles( class extends Component { model: PiPedalModel; @@ -309,7 +310,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( mounted: boolean = false; active: boolean = false; - timerHandle?: NodeJS.Timeout; + timerHandle?: number; tick() { this.model.getJackStatus() @@ -540,7 +541,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( this.props.onClose(); } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); + let isConfigValid = this.state.jackConfiguration.isValid; let selectedChannels: string[] = this.state.showInputSelectDialog ? this.state.jackSettings.inputAudioPorts : this.state.jackSettings.outputAudioPorts; let disableShutdown = this.state.shuttingDown || this.state.restarting; @@ -1003,7 +1005,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( } - } + }, + styles ); diff --git a/react/src/SnapshotButton.tsx b/vite/src/pipedal/SnapshotButton.tsx similarity index 99% rename from react/src/SnapshotButton.tsx rename to vite/src/pipedal/SnapshotButton.tsx index 2348450..5c656ac 100644 --- a/react/src/SnapshotButton.tsx +++ b/vite/src/pipedal/SnapshotButton.tsx @@ -22,7 +22,6 @@ * SOFTWARE. */ -import React from 'react'; import Typography from '@mui/material/Typography'; import IconButton from '@mui/material/IconButton'; import ButtonBase from '@mui/material/ButtonBase'; diff --git a/react/src/SnapshotColors.tsx b/vite/src/pipedal/SnapshotColors.tsx similarity index 84% rename from react/src/SnapshotColors.tsx rename to vite/src/pipedal/SnapshotColors.tsx index 3908c9b..f20721f 100644 --- a/react/src/SnapshotColors.tsx +++ b/vite/src/pipedal/SnapshotColors.tsx @@ -22,14 +22,14 @@ * SOFTWARE. */ -const darkcolors: string[] = -[ - "#b71C1C","#880E4F","#4A148c","#311B92","#1A237E","#0D47A1","#01579B","#006064","#004D40" -]; +// const darkcolors: string[] = +// [ +// "#b71C1C","#880E4F","#4A148c","#311B92","#1A237E","#0D47A1","#01579B","#006064","#004D40" +// ]; -const lighcolors: string[] = [ - "#FFcDD2","#F8BBd0","#E1BEE7","#D1c4E9","#C5CAE9","#BBDEFB","#B3E5FC","#B2EbF2","B2DFDB" -]; +// const lighcolors: string[] = [ +// "#FFcDD2","#F8BBd0","#E1BEE7","#D1c4E9","#C5CAE9","#BBDEFB","#B3E5FC","#B2EbF2","B2DFDB" +// ]; export function getLightSnapshotcolors() { let result:string[] = []; for (let h = 0; h < 360; h += 30) diff --git a/react/src/SnapshotDialog.tsx b/vite/src/pipedal/SnapshotDialog.tsx similarity index 94% rename from react/src/SnapshotDialog.tsx rename to vite/src/pipedal/SnapshotDialog.tsx index 9207334..f1a725b 100644 --- a/react/src/SnapshotDialog.tsx +++ b/vite/src/pipedal/SnapshotDialog.tsx @@ -30,7 +30,6 @@ import DialogContent from '@mui/material/DialogContent'; import DialogTitle from '@mui/material/DialogTitle'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import IconButton from '@mui/material/IconButton'; -import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import { TransitionProps } from '@mui/material/transitions'; @@ -38,12 +37,6 @@ import Fade from '@mui/material/Fade'; import Slide from '@mui/material/Slide'; import SnapshotPanel from './SnapshotPanel'; -const styles = { - dialogPaper: { - minHeight: '80vh', - maxHeight: '80vh', - }, -}; const FadeTransition = React.forwardRef(function Transition( @@ -78,11 +71,9 @@ export interface SnapshotDialogState { }; export default class SnapshotDialog extends ResizeResponsiveComponent { - private model: PiPedalModel; constructor(props: SnapshotDialogProps) { super(props); - this.model = PiPedalModelFactory.getInstance(); this.state = { fullScreen: this.getFullScreen(), }; diff --git a/vite/src/pipedal/SnapshotEditor.tsx b/vite/src/pipedal/SnapshotEditor.tsx new file mode 100644 index 0000000..63fd2e1 --- /dev/null +++ b/vite/src/pipedal/SnapshotEditor.tsx @@ -0,0 +1,364 @@ +// Copyright (c) 2024 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import WithStyles from './WithStyles'; +import { css } from '@emotion/react'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import CloseIcon from '@mui/icons-material/Close'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import CssBaseline from '@mui/material/CssBaseline'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; +import IconButton from '@mui/material/IconButton'; +import FullscreenIcon from '@mui/icons-material/Fullscreen'; +import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'; +import ResizeResponsiveComponent from './ResizeResponsiveComponent'; +import { PiPedalModelFactory, PiPedalModel, State, ZoomedControlInfo } from './PiPedalModel'; +import ZoomedUiControl from './ZoomedUiControl' +import MainPage from './MainPage'; +import JackStatusView from './JackStatusView'; +import { Theme } from '@mui/material/styles'; +import { isDarkMode } from './DarkMode'; +import Typography from '@mui/material/Typography'; +import TextField from '@mui/material/TextField'; +import { Snapshot } from './Pedalboard'; +import ColorDropdownButton, { DropdownAlignment } from './ColorDropdownButton'; +import { getBackgroundColor } from './MaterialColors'; + + +const selectColor = isDarkMode() ? "#888" : "#FFFFFF"; + + +const appStyles = (theme: Theme) => createStyles({ + "&": css({ // :root + colorScheme: (isDarkMode() ? "dark" : "light") + }), + + + mainFrame: css({ + overflow: "hidden", + display: "flex", + flexFlow: "column", + marginTop: 8, + flex: "1 1 100%" + }), + heroContent: css({ + backgroundColor: theme.mainBackground, + position: "relative", + height: "100%", + width: "100%" + }), + shadowCatcher: css({ + backgroundColor: theme.mainBackground + }), + + select: css({ // fu fu fu.Overrides for white selector on dark background. + '&:before': { + borderColor: selectColor, + }, + '&:after': { + borderColor: selectColor, + }, + '&:hover:not(.Mui-disabled):before': { + borderColor: selectColor, + } + }), + select_icon: css({ + fill: selectColor, + }) + + +}); + + +function supportsFullScreen(): boolean { + let doc: any = window.document; + let docEl: any = doc.documentElement; + var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; + return (!!requestFullScreen); + +} + +function setFullScreen(value: boolean) { + let doc: any = window.document; + let docEl: any = doc.documentElement; + + var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen; + var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen; + + if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) { + requestFullScreen.call(docEl); + } + else { + cancelFullScreen.call(doc); + } +} + +type SnapshotEditorState = { + zoomedControlInfo: ZoomedControlInfo | undefined; + zoomedControlOpen: boolean; + + canFullScreen: boolean; + isFullScreen: boolean; + isDebug: boolean; + collapseLabel: boolean; + + showStatusMonitor: boolean; + name: string, + color: string + +}; +interface SnapshotEditorProps extends WithStyles { + onClose: () => void; + onOk: (snapshotIndex: number, name: string, color: string, newSnapshots: (Snapshot | null)[]) => void; + snapshotIndex: number; +} + +const SnapshotEditor = withStyles( + class extends ResizeResponsiveComponent { + // Before the component mounts, we initialise our state + + model_: PiPedalModel; + + getCollapseLabel() { + return this.windowSize.width < 500; + } + + constructor(props: SnapshotEditorProps) { + super(props); + this.model_ = PiPedalModelFactory.getInstance(); + + this.model_.zoomedUiControl.addOnChangedHandler( + () => { + this.setState({ + zoomedControlOpen: this.model_.zoomedUiControl.get() !== undefined, + zoomedControlInfo: this.model_.zoomedUiControl.get() + }); + } + ); + + let snapshot = this.model_.pedalboard.get().snapshots[this.props.snapshotIndex] ?? new Snapshot(); + this.state = { + zoomedControlOpen: false, + zoomedControlInfo: this.model_.zoomedUiControl.get(), + canFullScreen: supportsFullScreen() && !this.model_.isAndroidHosted(), + isFullScreen: !!document.fullscreenElement, + showStatusMonitor: this.model_.showStatusMonitor.get(), + isDebug: true, + name: snapshot.name, + color: snapshot.color, + collapseLabel: this.getCollapseLabel() + + }; + + this.showStatusMonitorHandler = this.showStatusMonitorHandler.bind(this); + } + + showStatusMonitorHandler() { + this.setState({ + showStatusMonitor: this.model_.showStatusMonitor.get() + }); + } + toggleFullScreen(): void { + setFullScreen(this.state.isFullScreen); + this.setState({ isFullScreen: !this.state.isFullScreen }); + } + + componentDidMount() { + + super.componentDidMount(); + this.model_.showStatusMonitor.addOnChangedHandler(this.showStatusMonitorHandler); + + } + + + updateOverscroll(): void { + if (this.model_.serverVersion) { + // no pull-down refresh on android devices once we're ready (unless we're debug) + let preventOverscroll = + this.model_.state.get() === State.Ready + && !this.model_.debug; + + let overscrollBehavior = preventOverscroll ? "none" : "auto"; + document.body.style.overscrollBehavior = overscrollBehavior; + } + } + + componentDidUpdate() { + } + + componentWillUnmount() { + super.componentWillUnmount(); + this.model_.banks.removeOnChangedHandler(this.showStatusMonitorHandler); + } + + onWindowSizeChanged(width: number, height: number): void { + super.onWindowSizeChanged(width, height); + this.setState({ + collapseLabel: this.getCollapseLabel() + }); + + } + + handleOk() { + let currentPedalboard = this.model_.pedalboard.get(); + let selectedSnapshot = this.props.snapshotIndex; + let currentSnapshot: Snapshot | null = currentPedalboard.snapshots[selectedSnapshot]; + if (!currentSnapshot) { + this.props.onClose(); + return; + } + + let changed = this.state.name !== currentSnapshot.name + || this.state.color !== currentSnapshot.color + || currentSnapshot.isModified; + if (!changed) { + this.props.onClose(); + return; + } + let newSnapshots = Snapshot.cloneSnapshots(currentPedalboard.snapshots); + let newSnapshot = this.model_.pedalboard.get().makeSnapshot(); + newSnapshot.name = this.state.name; + newSnapshot.color = this.state.color; + newSnapshots[selectedSnapshot] = newSnapshot; + this.model_.setSnapshots(newSnapshots, selectedSnapshot); + + this.props.onOk(selectedSnapshot, this.state.name, this.state.color, newSnapshots); + + + } + + render() { + + const classes = withStyles.getClasses(this.props); + + return ( + +
{ + if (!this.model_.debug) { + e.preventDefault(); e.stopPropagation(); + } + }} + > + + + + { this.handleOk(); }} + size="large"> + + + {!this.state.collapseLabel && ( + + {'Snapshot ' + (this.props.snapshotIndex + 1)} + + )} + +
+ { + this.setState({ name: ev.target.value }); + }} + /> +
+ { + this.setState({ color: newColor }); + }} /> + {this.state.canFullScreen && + { this.toggleFullScreen(); }} + color="inherit" + size="medium"> + {this.state.isFullScreen ? ( + + ) : ( + + + )} + + + } + { this.props.onClose(); }} + color="inherit" + size="medium"> + + + +
+
+
+
+
+ +
+ +
+
+ + { this.setState({ zoomedControlOpen: false }); }} + onDialogClosed={() => { this.model_.zoomedUiControl.set(undefined); } + } + /> + {this.state.showStatusMonitor && ()} +
+ ); + } + }, + appStyles +); + +export default SnapshotEditor; diff --git a/react/src/SnapshotPanel.tsx b/vite/src/pipedal/SnapshotPanel.tsx similarity index 100% rename from react/src/SnapshotPanel.tsx rename to vite/src/pipedal/SnapshotPanel.tsx diff --git a/react/src/SnapshotPropertiesDialog.tsx b/vite/src/pipedal/SnapshotPropertiesDialog.tsx similarity index 99% rename from react/src/SnapshotPropertiesDialog.tsx rename to vite/src/pipedal/SnapshotPropertiesDialog.tsx index f828742..a70f205 100644 --- a/react/src/SnapshotPropertiesDialog.tsx +++ b/vite/src/pipedal/SnapshotPropertiesDialog.tsx @@ -22,7 +22,7 @@ * SOFTWARE. */ -import React from 'react'; + import Button from '@mui/material/Button'; import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; diff --git a/react/src/SplitControlView.tsx b/vite/src/pipedal/SplitControlView.tsx similarity index 96% rename from react/src/SplitControlView.tsx rename to vite/src/pipedal/SplitControlView.tsx index 45a560b..2ff7930 100644 --- a/react/src/SplitControlView.tsx +++ b/vite/src/pipedal/SplitControlView.tsx @@ -19,9 +19,10 @@ import { ReactNode } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { UiPlugin } from './Lv2Plugin'; import { @@ -31,12 +32,13 @@ import PluginControl from './PluginControl'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import VuMeter from './VuMeter'; import { SplitAbControl, SplitTypeControl, SplitMixControl, SplitPanLeftControl,SplitVolLeftControl,SplitPanRightControl,SplitVolRightControl } from './SplitUiControls'; +import { css } from '@emotion/react'; const LANDSCAPE_HEIGHT_BREAK = 500; const styles = (theme: Theme) => createStyles({ - frame: { + frame: css({ display: "block", position: "relative", flexDirection: "row", @@ -46,8 +48,8 @@ const styles = (theme: Theme) => createStyles({ height: "100%", overflowX: "auto", overflowY: "auto" - }, - vuMeterL: { + }), + vuMeterL: css({ position: "fixed", paddingLeft: 12, paddingRight: 12, @@ -55,8 +57,8 @@ const styles = (theme: Theme) => createStyles({ background: theme.mainBackground, zIndex: 3 - }, - vuMeterR: { + }), + vuMeterR: css({ position: "fixed", right: 0, marginRight: 22, @@ -64,8 +66,8 @@ const styles = (theme: Theme) => createStyles({ background: theme.mainBackground, zIndex: 3 - }, - vuMeterRLandscape: { + }), + vuMeterRLandscape: css({ position: "fixed", right: 0, paddingRight: 22, @@ -73,9 +75,9 @@ const styles = (theme: Theme) => createStyles({ background: theme.mainBackground, zIndex: 3 - }, + }), - normalGrid: { + normalGrid: css({ position: "relative", paddingLeft: 40, paddingRight: 40, @@ -83,19 +85,18 @@ const styles = (theme: Theme) => createStyles({ display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "flex-start", alignItems: "flex_start" - }, - landscapeGrid: { + }), + landscapeGrid: css({ paddingLeft: 40, marginRight: 40, display: "inline-flex", flexDirection: "row", flexWrap: "nowrap", justifyContent: "flex-start", alignItems: "flex_start", height: "100%" - } + }) }); interface SplitControlViewProps extends WithStyles { - theme: Theme; instanceId: number; item: PedalboardSplitItem; } @@ -104,7 +105,7 @@ type SplitControlViewState = { }; const SplitControlView = - withStyles(styles, { withTheme: true })( + withStyles( class extends ResizeResponsiveComponent { model: PiPedalModel; @@ -161,7 +162,7 @@ const SplitControlView = render(): ReactNode { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let pedalboardItem = this.model.pedalboard.get().getItem(this.props.instanceId); if (!pedalboardItem) @@ -277,7 +278,8 @@ const SplitControlView = } - } + }, + styles ); export default SplitControlView; diff --git a/react/src/SplitUiControls.tsx b/vite/src/pipedal/SplitUiControls.tsx similarity index 100% rename from react/src/SplitUiControls.tsx rename to vite/src/pipedal/SplitUiControls.tsx diff --git a/react/src/StringBuilder.tsx b/vite/src/pipedal/StringBuilder.tsx similarity index 100% rename from react/src/StringBuilder.tsx rename to vite/src/pipedal/StringBuilder.tsx diff --git a/react/src/SvgPathBuilder.tsx b/vite/src/pipedal/SvgPathBuilder.tsx similarity index 100% rename from react/src/SvgPathBuilder.tsx rename to vite/src/pipedal/SvgPathBuilder.tsx diff --git a/react/src/SystemMidiBindingView.tsx b/vite/src/pipedal/SystemMidiBindingView.tsx similarity index 97% rename from react/src/SystemMidiBindingView.tsx rename to vite/src/pipedal/SystemMidiBindingView.tsx index a0e2b9f..0d3ab11 100644 --- a/react/src/SystemMidiBindingView.tsx +++ b/vite/src/pipedal/SystemMidiBindingView.tsx @@ -26,9 +26,10 @@ import { Component } from 'react'; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import {createStyles} from './WithStyles'; + import MenuItem from '@mui/material/MenuItem'; import Select from '@mui/material/Select'; import MidiBinding from './MidiBinding'; @@ -63,7 +64,7 @@ interface SystemMidiBindingViewState { const SystemMidiBindingView = - withStyles(styles, { withTheme: true })( + withStyles( class extends Component { model: PiPedalModel; @@ -143,7 +144,7 @@ const SystemMidiBindingView = render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let midiBinding = this.props.midiBinding; return ( @@ -213,6 +214,8 @@ const SystemMidiBindingView = ); } - }); + }, + styles + ); export default SystemMidiBindingView; \ No newline at end of file diff --git a/react/src/SystemMidiBindingsDialog.tsx b/vite/src/pipedal/SystemMidiBindingsDialog.tsx similarity index 95% rename from react/src/SystemMidiBindingsDialog.tsx rename to vite/src/pipedal/SystemMidiBindingsDialog.tsx index 52ad0e0..736f4d5 100644 --- a/react/src/SystemMidiBindingsDialog.tsx +++ b/vite/src/pipedal/SystemMidiBindingsDialog.tsx @@ -24,14 +24,16 @@ import React, { SyntheticEvent } from 'react'; +import { css } from '@emotion/react'; import DialogEx from './DialogEx'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import { PiPedalModel, PiPedalModelFactory, ListenHandle } from './PiPedalModel'; import Typography from '@mui/material/Typography'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; @@ -41,37 +43,37 @@ import SystemMidiBindingView from './SystemMidiBindingView'; import Snackbar from '@mui/material/Snackbar'; const styles = (theme: Theme) => createStyles({ - dialogAppBar: { + dialogAppBar: css({ position: 'relative', top: 0, left: 0 - }, - dialogTitle: { + }), + dialogTitle: css({ marginLeft: theme.spacing(2), flex: "1 1 auto", - }, - pluginTable: { + }), + pluginTable: css({ borderCollapse: "collapse", width: "100%", - }, - pluginHead: { + }), + pluginHead: css({ borderTop: "1pt #DDD solid", paddingLeft: 22, paddingRight: 22 - }, - bindingTd: { + }), + bindingTd: css({ verticalAlign: "top", paddingLeft: 12, paddingBottom: 8 - }, - nameTd: { + }), + nameTd: css({ paddingLeft: 16, verticalAlign: "top", paddingTop: 12 - }, - plainRow: { + }), + plainRow: css({ borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: "transparent" - }, - dividerRow: { + }), + dividerRow: css({ borderWidth: "1px 0px 0px 0px", borderStyle: "solid", borderColor: theme.palette.divider - } + }) }); @@ -105,7 +107,7 @@ export interface SystemMidiBindingDialogState { } export const SystemMidiBindingDialog = - withStyles(styles, { withTheme: true })( + withStyles( class extends ResizeResponsiveComponent { model: PiPedalModel; @@ -185,7 +187,7 @@ export const SystemMidiBindingDialog = this.props.onClose(); } - listenTimeoutHandle?: NodeJS.Timeout; + listenTimeoutHandle?: number; listenHandle?: ListenHandle; @@ -268,7 +270,7 @@ export const SystemMidiBindingDialog = this.model.setSystemMidiBinding(instanceId, newBinding); } generateTable(): React.ReactNode[] { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let result: React.ReactNode[] = []; let items = this.state.systemMidiBindings; @@ -319,7 +321,9 @@ export const SystemMidiBindingDialog = render() { let props = this.props; - let { open, classes } = props; + let { open} = props; + + const classes = withStyles.getClasses(this.props); if (!open) { return (
); } @@ -375,7 +379,9 @@ export const SystemMidiBindingDialog = ); } - }); + }, + styles + ); export default SystemMidiBindingDialog; \ No newline at end of file diff --git a/react/src/TemporaryDrawer.tsx b/vite/src/pipedal/TemporaryDrawer.tsx similarity index 92% rename from react/src/TemporaryDrawer.tsx rename to vite/src/pipedal/TemporaryDrawer.tsx index ffcca26..6c87b0f 100644 --- a/react/src/TemporaryDrawer.tsx +++ b/vite/src/pipedal/TemporaryDrawer.tsx @@ -18,9 +18,10 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React, { Component } from 'react'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import {createStyles} from './WithStyles'; + import IconButton from '@mui/material/Toolbar'; import Drawer from '@mui/material/Drawer'; @@ -55,14 +56,14 @@ interface DrawerProps extends WithStyles { position: Anchor; is_open: boolean; onClose?: CloseEventHandler; - children?: React.ReactChild | React.ReactChild[]; + children?: React.ReactNode; } type DrawerState = { is_open: boolean; } -export const TemporaryDrawer = withStyles(drawerStyles)( +export const TemporaryDrawer = withStyles( class extends Component { constructor(props: DrawerProps) { @@ -84,7 +85,7 @@ export const TemporaryDrawer = withStyles(drawerStyles)( } render() { - const { classes } = this.props; + const classes = withStyles.getClasses(this.props); return ( @@ -111,5 +112,6 @@ export const TemporaryDrawer = withStyles(drawerStyles)(
); } - } + }, + drawerStyles ); diff --git a/react/src/TextFieldEx.tsx b/vite/src/pipedal/TextFieldEx.tsx similarity index 99% rename from react/src/TextFieldEx.tsx rename to vite/src/pipedal/TextFieldEx.tsx index 4e7705e..9facc9d 100644 --- a/react/src/TextFieldEx.tsx +++ b/vite/src/pipedal/TextFieldEx.tsx @@ -3,7 +3,8 @@ import React from 'react'; import TextField, { TextFieldProps } from '@mui/material/TextField'; import { withStyles, WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; +import {createStyles} from './WithStyles'; + import { Theme } from '@mui/material/styles'; import Modal from '@mui/material/Modal'; diff --git a/react/src/ToobCabSimView.tsx b/vite/src/pipedal/ToobCabSimView.tsx similarity index 94% rename from react/src/ToobCabSimView.tsx rename to vite/src/pipedal/ToobCabSimView.tsx index 02dda9e..97e936e 100644 --- a/react/src/ToobCabSimView.tsx +++ b/vite/src/pipedal/ToobCabSimView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import IControlViewFactory from './IControlViewFactory'; import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel"; @@ -45,7 +46,7 @@ interface ToobCabSimState { } const ToobCabSimView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component implements ControlViewCustomization { @@ -75,7 +76,8 @@ const ToobCabSimView = customizationId={this.customizationId} />); } - } + }, + styles ); diff --git a/react/src/ToobFrequencyResponseView.tsx b/vite/src/pipedal/ToobFrequencyResponseView.tsx similarity index 97% rename from react/src/ToobFrequencyResponseView.tsx rename to vite/src/pipedal/ToobFrequencyResponseView.tsx index 554972b..4a07ed3 100644 --- a/react/src/ToobFrequencyResponseView.tsx +++ b/vite/src/pipedal/ToobFrequencyResponseView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModelFactory, PiPedalModel, State } from "./PiPedalModel"; import Utility from './Utility'; @@ -60,13 +61,13 @@ interface ToobFrequencyResponseState { } const ToobFrequencyResponseView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component { model: PiPedalModel; customizationId: number = 1; - pathRef: React.RefObject; + pathRef: React.RefObject; constructor(props: ToobFrequencyResponseProps) { super(props); @@ -286,7 +287,7 @@ const ToobFrequencyResponseView = this.logMax = Math.log(this.fMax); - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); return (
@@ -296,7 +297,8 @@ const ToobFrequencyResponseView =
); } - } + }, + styles ); diff --git a/react/src/ToobInputStageView.tsx b/vite/src/pipedal/ToobInputStageView.tsx similarity index 94% rename from react/src/ToobInputStageView.tsx rename to vite/src/pipedal/ToobInputStageView.tsx index 66838fe..8f4f601 100644 --- a/react/src/ToobInputStageView.tsx +++ b/vite/src/pipedal/ToobInputStageView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import IControlViewFactory from './IControlViewFactory'; import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel"; @@ -45,7 +46,7 @@ interface ToobInputStageState { } const ToobInputStageView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component implements ControlViewCustomization { @@ -77,7 +78,8 @@ const ToobInputStageView = customizationId={this.customizationId} />); } - } + }, + styles ); diff --git a/react/src/ToobMLView.tsx b/vite/src/pipedal/ToobMLView.tsx similarity index 96% rename from react/src/ToobMLView.tsx rename to vite/src/pipedal/ToobMLView.tsx index dc69c76..b33f18f 100644 --- a/react/src/ToobMLView.tsx +++ b/vite/src/pipedal/ToobMLView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import IControlViewFactory from './IControlViewFactory'; import { PiPedalModelFactory, PiPedalModel, MonitorPortHandle } from "./PiPedalModel"; @@ -43,11 +44,11 @@ interface ToobMLState { } const ToobMLView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component implements ControlViewCustomization { model: PiPedalModel; - gainRef: React.RefObject; + gainRef: React.RefObject; customizationId: number = 1; @@ -142,7 +143,8 @@ const ToobMLView = customizationId={this.customizationId} />); } - } + }, + styles ); diff --git a/react/src/ToobPowerStage2View.tsx b/vite/src/pipedal/ToobPowerStage2View.tsx similarity index 97% rename from react/src/ToobPowerStage2View.tsx rename to vite/src/pipedal/ToobPowerStage2View.tsx index 9fe1641..868f694 100644 --- a/react/src/ToobPowerStage2View.tsx +++ b/vite/src/pipedal/ToobPowerStage2View.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import IControlViewFactory from './IControlViewFactory'; import { PiPedalModelFactory, PiPedalModel, State,ListenHandle } from "./PiPedalModel"; @@ -46,7 +47,7 @@ interface ToobPowerstage2State { } const ToobPowerstage2View = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component implements ControlViewCustomization { @@ -177,7 +178,8 @@ const ToobPowerstage2View = customizationId={this.customizationId} />); } - } + }, + styles ); diff --git a/react/src/ToobSpectrumAnalyzerView.tsx b/vite/src/pipedal/ToobSpectrumAnalyzerView.tsx similarity index 94% rename from react/src/ToobSpectrumAnalyzerView.tsx rename to vite/src/pipedal/ToobSpectrumAnalyzerView.tsx index 9391f9a..0e600ba 100644 --- a/react/src/ToobSpectrumAnalyzerView.tsx +++ b/vite/src/pipedal/ToobSpectrumAnalyzerView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import IControlViewFactory from './IControlViewFactory'; import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel"; @@ -46,7 +47,7 @@ interface ToobSpectrumAnalyzerState { } const ToobSpectrumAnalyzerView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component implements ControlViewCustomization { @@ -76,7 +77,8 @@ const ToobSpectrumAnalyzerView = customizationId={this.customizationId} />); } - } + }, + styles ); diff --git a/react/src/ToobSpectrumResponseView.tsx b/vite/src/pipedal/ToobSpectrumResponseView.tsx similarity index 97% rename from react/src/ToobSpectrumResponseView.tsx rename to vite/src/pipedal/ToobSpectrumResponseView.tsx index 424f97d..62b78e4 100644 --- a/react/src/ToobSpectrumResponseView.tsx +++ b/vite/src/pipedal/ToobSpectrumResponseView.tsx @@ -21,9 +21,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModelFactory, PiPedalModel, State, ListenHandle } from "./PiPedalModel"; import { StandardItemSize } from './PluginControlView'; @@ -65,7 +66,7 @@ interface ToobSpectrumResponseState { } const ToobSpectrumResponseView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component { model: PiPedalModel; @@ -162,7 +163,6 @@ const ToobSpectrumResponseView = } private isMonitoring: boolean = false; - private hClock?: NodeJS.Timeout; private hAtomOutput?: ListenHandle; private maybeStartMonitoring(wantsMonitoring: boolean = true) { @@ -276,7 +276,7 @@ const ToobSpectrumResponseView = render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); return (
{/*
@@ -312,7 +312,8 @@ const ToobSpectrumResponseView =
); } - } + }, + styles ); diff --git a/react/src/ToobToneStackView.tsx b/vite/src/pipedal/ToobToneStackView.tsx similarity index 95% rename from react/src/ToobToneStackView.tsx rename to vite/src/pipedal/ToobToneStackView.tsx index 41eea1d..99d4ee0 100644 --- a/react/src/ToobToneStackView.tsx +++ b/vite/src/pipedal/ToobToneStackView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import IControlViewFactory from './IControlViewFactory'; import { PiPedalModelFactory, PiPedalModel,ControlValueChangedHandle } from "./PiPedalModel"; @@ -45,7 +46,7 @@ interface ToobToneStackState { } const ToobToneStackView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component implements ControlViewCustomization { @@ -109,7 +110,8 @@ const ToobToneStackView = customizationId={this.customizationId} />); } - } + }, + styles ); diff --git a/react/src/ToobWaveShapeView.tsx b/vite/src/pipedal/ToobWaveShapeView.tsx similarity index 98% rename from react/src/ToobWaveShapeView.tsx rename to vite/src/pipedal/ToobWaveShapeView.tsx index 715291c..822050f 100644 --- a/react/src/ToobWaveShapeView.tsx +++ b/vite/src/pipedal/ToobWaveShapeView.tsx @@ -20,9 +20,10 @@ import React from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import WithStyles from './WithStyles'; +import {createStyles} from './WithStyles'; + +import { withStyles } from "tss-react/mui"; import { PiPedalModelFactory, PiPedalModel, State, ControlValueChangedHandle } from "./PiPedalModel"; import { StandardItemSize } from './PluginControlView'; @@ -114,7 +115,7 @@ export class BidirectionalVuPeak { const ToobWaveShapeView = - withStyles(styles, { withTheme: true })( + withStyles( class extends React.Component { model: PiPedalModel; @@ -409,7 +410,7 @@ const ToobWaveShapeView = } render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let data = this.state.data; @@ -442,7 +443,8 @@ const ToobWaveShapeView =
); } - } + }, + styles ); diff --git a/react/src/Units.tsx b/vite/src/pipedal/Units.tsx similarity index 100% rename from react/src/Units.tsx rename to vite/src/pipedal/Units.tsx diff --git a/react/src/UpdateDialog.tsx b/vite/src/pipedal/UpdateDialog.tsx similarity index 99% rename from react/src/UpdateDialog.tsx rename to vite/src/pipedal/UpdateDialog.tsx index 43e7f58..6e53948 100644 --- a/react/src/UpdateDialog.tsx +++ b/vite/src/pipedal/UpdateDialog.tsx @@ -22,7 +22,6 @@ * SOFTWARE. */ -import React from 'react'; import Button from '@mui/material/Button'; import Typography from '@mui/material/Typography'; import Divider from '@mui/material/Divider'; @@ -76,17 +75,14 @@ export default class UpdateDialog extends ResizeResponsiveComponent createStyles({ - frame: { +const styles = (theme: Theme) => ({ + frame: css({ position: "relative", width: CHANNEL_WIDTH + 2*BORDER_THICKNESS, borderTop: BORDER_THICKNESS + "px black solid", @@ -106,8 +108,8 @@ const styles = (theme: Theme) => createStyles({ overflow: "hidden", marginLeft: (CHANNEL_WIDTH+BORDER_THICKNESS)/2, marginRight: (CHANNEL_WIDTH+BORDER_THICKNESS)/2 - }, - frameStereo: { + }), + frameStereo: css({ position: "relative", width: 2*CHANNEL_WIDTH + 3*BORDER_THICKNESS, borderTop: BORDER_THICKNESS + "px black solid", @@ -115,14 +117,14 @@ const styles = (theme: Theme) => createStyles({ height: DISPLAY_HEIGHT, background: "black", overflow: "hidden", - }, - monoTextFrame: { + }), + monoTextFrame: css({ display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center" - }, - stereoTextFrame: { + }), + stereoTextFrame: css({ display: "flex",flexFlow: "column nowrap", alignItems: "center", textAlign: "center" - }, - vuTextFrame: { + }), + vuTextFrame: css({ color: theme.palette.text.secondary, display: "table-cell", position: "relative", @@ -132,32 +134,32 @@ const styles = (theme: Theme) => createStyles({ fontSize: "11px", justifyContent: "center", alignItems: "center" - }, - redFrameL: { + }), + redFrameL: css({ position: "absolute", top: "100%", left: LEFT_X, width: CHANNEL_WIDTH, height: DISPLAY_HEIGHT - 2, background: "#F00" - }, - yellowFrameL: { + }), + yellowFrameL: css({ top: "100%", left: LEFT_X, width: CHANNEL_WIDTH, position: "absolute", height: DISPLAY_HEIGHT - 2, background: "#CC0" - }, - greenFrameL: { + }), + greenFrameL: css({ top: "100%", left: LEFT_X, width: CHANNEL_WIDTH, position: "absolute", height: DISPLAY_HEIGHT - 2, background: "#0C2" - }, - indicatorL: { + }), + indicatorL: css({ position: "absolute", left: LEFT_X, width: CHANNEL_WIDTH, @@ -165,32 +167,32 @@ const styles = (theme: Theme) => createStyles({ top: "100%", background: "#F00" - }, - redFrameR: { + }), + redFrameR: css({ position: "absolute", top: "100%", left: RIGHT_X, width: CHANNEL_WIDTH, height: DISPLAY_HEIGHT - 2, background: "#F00" - }, - yellowFrameR: { + }), + yellowFrameR: css({ top: "100%", left: RIGHT_X, width: CHANNEL_WIDTH, position: "absolute", height: DISPLAY_HEIGHT - 2, background: "#CC0" - }, - greenFrameR: { + }), + greenFrameR: css({ top: "100%", left: RIGHT_X, width: CHANNEL_WIDTH, position: "absolute", height: DISPLAY_HEIGHT - 2, background: "#0C2" - }, - indicatorR: { + }), + indicatorR: css({ position: "absolute", left: RIGHT_X, width: CHANNEL_WIDTH, @@ -198,7 +200,7 @@ const styles = (theme: Theme) => createStyles({ top: "100%", background: "#F00" - } + }) }); @@ -229,11 +231,11 @@ function dbToY(db: number): number { } export const VuMeter = - withStyles(styles, { withTheme: true })( + withTheme(withStyles( class extends Component { - divRef: React.RefObject = React.createRef(); - textRef: React.RefObject = React.createRef(); + divRef: React.RefObject = React.createRef(); + textRef: React.RefObject = React.createRef(); model: PiPedalModel; yZero: number; @@ -441,7 +443,7 @@ export const VuMeter = render() { let displayText = this.props.displayText??false; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); if (displayText) { return ( @@ -461,7 +463,7 @@ export const VuMeter = renderVus() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); if (!this.state.isStereo) { return (
@@ -507,8 +509,9 @@ export const VuMeter = this.removeVuSubscription(); this.model.state.removeOnChangedHandler(this.onStateChanged); } - } - ); + }, + styles + )); export default VuMeter; diff --git a/react/src/WifiChannel.tsx b/vite/src/pipedal/WifiChannel.tsx similarity index 100% rename from react/src/WifiChannel.tsx rename to vite/src/pipedal/WifiChannel.tsx diff --git a/react/src/WifiConfigDialog.tsx b/vite/src/pipedal/WifiConfigDialog.tsx similarity index 99% rename from react/src/WifiConfigDialog.tsx rename to vite/src/pipedal/WifiConfigDialog.tsx index 7e35386..e90aafe 100644 --- a/react/src/WifiConfigDialog.tsx +++ b/vite/src/pipedal/WifiConfigDialog.tsx @@ -33,9 +33,10 @@ import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import WifiConfigSettings from './WifiConfigSettings'; import Typography from '@mui/material/Typography'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import {createStyles} from './WithStyles'; + import Select from '@mui/material/Select'; import FormControl from '@mui/material/FormControl'; import InputLabel from '@mui/material/InputLabel'; @@ -91,11 +92,11 @@ export interface WifiConfigState { let gCountryCodeOptions: { id: string, label: string }[] | undefined = undefined; -const WifiConfigDialog = withStyles(styles, { withTheme: true })( +const WifiConfigDialog = withStyles( class extends ResizeResponsiveComponent { - refName: React.RefObject; - refPassword: React.RefObject; + refName: React.RefObject; + refPassword: React.RefObject; model: PiPedalModel; constructor(props: WifiConfigProps) { @@ -373,7 +374,7 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })( render() { let props = this.props; - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); let { open, onClose } = props; const handleClose = () => { @@ -701,6 +702,7 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })( ); } - }); + }, + styles); export default WifiConfigDialog; \ No newline at end of file diff --git a/react/src/WifiConfigSettings.tsx b/vite/src/pipedal/WifiConfigSettings.tsx similarity index 100% rename from react/src/WifiConfigSettings.tsx rename to vite/src/pipedal/WifiConfigSettings.tsx diff --git a/react/src/WifiDirectConfigDialog.tsx b/vite/src/pipedal/WifiDirectConfigDialog.tsx similarity index 97% rename from react/src/WifiDirectConfigDialog.tsx rename to vite/src/pipedal/WifiDirectConfigDialog.tsx index 679a2ab..90909e6 100644 --- a/react/src/WifiDirectConfigDialog.tsx +++ b/vite/src/pipedal/WifiDirectConfigDialog.tsx @@ -35,25 +35,18 @@ import Switch from '@mui/material/Switch'; import FormControlLabel from '@mui/material/FormControlLabel'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; import WifiDirectConfigSettings from './WifiDirectConfigSettings'; -import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; + +//import { withStyles } from "tss-react/mui"; import MenuItem from '@mui/material/MenuItem'; import WifiChannel from './WifiChannel'; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; -const styles = (theme: Theme) => createStyles({ - pgraph: { - paddingBottom: 16 - } -}); declare let crypto: any; const NBSP = "\u00A0"; -export interface WifiDirectConfigProps extends WithStyles { +export interface WifiDirectConfigProps { open: boolean, wifiDirectConfigSettings: WifiDirectConfigSettings, onOk: (wifiDirectConfigSettings: WifiDirectConfigSettings) => void, @@ -79,11 +72,11 @@ export interface WifiDirectConfigState { -const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })( +const WifiDirectConfigDialog = class extends ResizeResponsiveComponent { - refName: React.RefObject; - refPassword: React.RefObject; + refName: React.RefObject; + refPassword: React.RefObject; model: PiPedalModel; constructor(props: WifiDirectConfigProps) { @@ -516,6 +509,6 @@ const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })( ); } - }); + }; export default WifiDirectConfigDialog; \ No newline at end of file diff --git a/react/src/WifiDirectConfigSettings.tsx b/vite/src/pipedal/WifiDirectConfigSettings.tsx similarity index 100% rename from react/src/WifiDirectConfigSettings.tsx rename to vite/src/pipedal/WifiDirectConfigSettings.tsx diff --git a/react/src/WindowScale.tsx b/vite/src/pipedal/WindowScale.tsx similarity index 100% rename from react/src/WindowScale.tsx rename to vite/src/pipedal/WindowScale.tsx diff --git a/vite/src/pipedal/WithStyles.tsx b/vite/src/pipedal/WithStyles.tsx new file mode 100644 index 0000000..19a1e87 --- /dev/null +++ b/vite/src/pipedal/WithStyles.tsx @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025 Robin E. R. Davies + * All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react'; + +import { Theme, useTheme } from '@mui/material/styles'; + +export function createStyles(style: T): T { + return style; +} + +export default + interface WithStyles any> { + className?: string; + classes?: Partial, string>>; + +} + +export interface WithTheme { + theme: Theme; +} + + +// export function XXwithTheme(Component: React.ComponentType>) { +// return function ( +// Component: React.ComponentType +// ) { +// return function (props: Omit): React.JSX.Element { + +// const theme = useTheme(); +// const newProps = { ...props, theme: theme } as T; +// return +// } +// }; +// }; + + + + +export function withTheme(Component: React.ComponentType) { + return function (props: Omit): React.JSX.Element { + + const theme = useTheme(); + const newProps = {theme: theme, ...props} as PROPS; + return React.createElement(Component,newProps); + } +}; + + + \ No newline at end of file diff --git a/vite/src/pipedal/XxxSnippet.tsx b/vite/src/pipedal/XxxSnippet.tsx new file mode 100644 index 0000000..6a037fb --- /dev/null +++ b/vite/src/pipedal/XxxSnippet.tsx @@ -0,0 +1,58 @@ + +import React from 'react'; +import { withStyles } from "tss-react/mui"; +import { css } from '@emotion/react'; + +import { Theme } from "@mui/material"; + + + + +// const styles : (theme: Theme) => Record +const styles + = ((theme: Theme) => { + return { + root: { + color: theme.palette.primary.main + }, + frame: css({ + margin: "12px", + color: "red", + position: "absolute", + height: '100%', + width: '100%', + borderRadius: 14 / 2, + zIndex: -1, + transition: theme.transitions.create(['opacity', 'background-color'], { + duration: theme.transitions.duration.shortest + }), + backgroundColor: theme.palette.secondary.main, + opacity: theme.palette.mode === 'light' ? 0.38 : 0.3, + //position: "absolute" + + }) + + } +}); + +export interface DummyProps { + className?: string; + classes?: Partial, string>>; + +}; + + +class DummyBase extends React.Component { + render() { + const classes = withStyles.getClasses(this.props); + return (
); + } +} + +const Dummy = withStyles( + DummyBase, + styles +); + +export default Dummy; + diff --git a/react/src/ZoomedDial.tsx b/vite/src/pipedal/ZoomedDial.tsx similarity index 97% rename from react/src/ZoomedDial.tsx rename to vite/src/pipedal/ZoomedDial.tsx index 4d168d6..e657d65 100644 --- a/react/src/ZoomedDial.tsx +++ b/vite/src/pipedal/ZoomedDial.tsx @@ -18,11 +18,12 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React, { TouchEvent, PointerEvent, SyntheticEvent } from 'react'; import { Theme } from '@mui/material/styles'; -import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles from './WithStyles'; +import { withStyles } from "tss-react/mui"; +import {createStyles} from './WithStyles'; + import { PiPedalModel, PiPedalModelFactory, ZoomedControlInfo } from './PiPedalModel'; -import { ReactComponent as DialIcon } from './svg/fx_dial.svg'; +import DialIcon from './svg/fx_dial.svg?react'; const SELECTED_OPACITY = 0.8; const DEFAULT_OPACITY = 0.6; @@ -45,7 +46,6 @@ const styles = (theme: Theme) => createStyles({ interface ZoomedDialProps extends WithStyles { - theme: Theme, size: number, controlInfo: ZoomedControlInfo | undefined, @@ -58,12 +58,12 @@ interface ZoomedDialState { } -const ZoomedDial = withStyles(styles, { withTheme: true })( +const ZoomedDial = withStyles( class extends React.Component { model: PiPedalModel = PiPedalModelFactory.getInstance(); - imgRef: React.RefObject = React.createRef(); + imgRef: React.RefObject = React.createRef(); constructor(props: ZoomedDialProps) { super(props); @@ -395,7 +395,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( render() { - let classes = this.props.classes; + const classes = withStyles.getClasses(this.props); return ( ); } - } + }, + styles ); export default ZoomedDial; diff --git a/react/src/ZoomedUiControl.tsx b/vite/src/pipedal/ZoomedUiControl.tsx similarity index 97% rename from react/src/ZoomedUiControl.tsx rename to vite/src/pipedal/ZoomedUiControl.tsx index 06d71b7..b8b1d57 100644 --- a/react/src/ZoomedUiControl.tsx +++ b/vite/src/pipedal/ZoomedUiControl.tsx @@ -23,8 +23,11 @@ import Select from '@mui/material/Select'; import Switch from '@mui/material/Switch'; import { UiControl, ScalePoint } from './Lv2Plugin'; import { Theme } from '@mui/material/styles'; -import { WithStyles, withStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; +import WithStyles, {withTheme} from './WithStyles'; +import { withStyles } from "tss-react/mui"; + +import {createStyles} from './WithStyles'; + import { ZoomedControlInfo } from './PiPedalModel'; import DialogEx from './DialogEx'; import Typography from '@mui/material/Typography'; @@ -73,7 +76,7 @@ const Transition = React.forwardRef(function Transition( */ -const ZoomedUiControl = withStyles(styles, { withTheme: true })( +const ZoomedUiControl = withTheme(withStyles( class extends React.Component { @@ -152,6 +155,8 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })( } makeSelect(control: UiControl, value: number): ReactNode { + const classes = withStyles.getClasses(this.props); + if (control.isOnOffSwitch()) { // normal gray unchecked state. return ( @@ -170,7 +175,7 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })( this.onCheckChanged(event.target.checked); }} classes={{ - track: this.props.classes.switchTrack + track: classes.switchTrack }} style={{ color: this.props.theme.palette.primary.main }} /> @@ -289,7 +294,7 @@ const ZoomedUiControl = withStyles(styles, { withTheme: true })( ); } - } -); + }, + styles)); export default ZoomedUiControl; diff --git a/react/src/logo.svg b/vite/src/pipedal/logo.svg similarity index 100% rename from react/src/logo.svg rename to vite/src/pipedal/logo.svg diff --git a/react/src/react-app-env.d.ts b/vite/src/pipedal/react-app-env.d.ts similarity index 100% rename from react/src/react-app-env.d.ts rename to vite/src/pipedal/react-app-env.d.ts diff --git a/react/src/svg/drive_file_rename_outline_black_24dp.svg b/vite/src/pipedal/svg/drive_file_rename_outline_black_24dp.svg similarity index 100% rename from react/src/svg/drive_file_rename_outline_black_24dp.svg rename to vite/src/pipedal/svg/drive_file_rename_outline_black_24dp.svg diff --git a/react/src/svg/file_download_black_24dp.svg b/vite/src/pipedal/svg/file_download_black_24dp.svg similarity index 100% rename from react/src/svg/file_download_black_24dp.svg rename to vite/src/pipedal/svg/file_download_black_24dp.svg diff --git a/react/src/svg/file_upload_black_24dp.svg b/vite/src/pipedal/svg/file_upload_black_24dp.svg similarity index 100% rename from react/src/svg/file_upload_black_24dp.svg rename to vite/src/pipedal/svg/file_upload_black_24dp.svg diff --git a/react/src/svg/fx_amplifier.svg b/vite/src/pipedal/svg/fx_amplifier.svg similarity index 100% rename from react/src/svg/fx_amplifier.svg rename to vite/src/pipedal/svg/fx_amplifier.svg diff --git a/react/src/svg/fx_analyzer.svg b/vite/src/pipedal/svg/fx_analyzer.svg similarity index 100% rename from react/src/svg/fx_analyzer.svg rename to vite/src/pipedal/svg/fx_analyzer.svg diff --git a/react/src/svg/fx_chorus.svg b/vite/src/pipedal/svg/fx_chorus.svg similarity index 100% rename from react/src/svg/fx_chorus.svg rename to vite/src/pipedal/svg/fx_chorus.svg diff --git a/react/src/svg/fx_compressor.svg b/vite/src/pipedal/svg/fx_compressor.svg similarity index 100% rename from react/src/svg/fx_compressor.svg rename to vite/src/pipedal/svg/fx_compressor.svg diff --git a/react/src/svg/fx_constant.svg b/vite/src/pipedal/svg/fx_constant.svg similarity index 100% rename from react/src/svg/fx_constant.svg rename to vite/src/pipedal/svg/fx_constant.svg diff --git a/react/src/svg/fx_converter.svg b/vite/src/pipedal/svg/fx_converter.svg similarity index 100% rename from react/src/svg/fx_converter.svg rename to vite/src/pipedal/svg/fx_converter.svg diff --git a/react/src/svg/fx_delay.svg b/vite/src/pipedal/svg/fx_delay.svg similarity index 100% rename from react/src/svg/fx_delay.svg rename to vite/src/pipedal/svg/fx_delay.svg diff --git a/react/src/svg/fx_dial.svg b/vite/src/pipedal/svg/fx_dial.svg similarity index 100% rename from react/src/svg/fx_dial.svg rename to vite/src/pipedal/svg/fx_dial.svg diff --git a/react/src/svg/fx_distortion.svg b/vite/src/pipedal/svg/fx_distortion.svg similarity index 100% rename from react/src/svg/fx_distortion.svg rename to vite/src/pipedal/svg/fx_distortion.svg diff --git a/react/src/svg/fx_empty.svg b/vite/src/pipedal/svg/fx_empty.svg similarity index 100% rename from react/src/svg/fx_empty.svg rename to vite/src/pipedal/svg/fx_empty.svg diff --git a/react/src/svg/fx_eq.svg b/vite/src/pipedal/svg/fx_eq.svg similarity index 100% rename from react/src/svg/fx_eq.svg rename to vite/src/pipedal/svg/fx_eq.svg diff --git a/react/src/svg/fx_error.svg b/vite/src/pipedal/svg/fx_error.svg similarity index 100% rename from react/src/svg/fx_error.svg rename to vite/src/pipedal/svg/fx_error.svg diff --git a/react/src/svg/fx_filter.svg b/vite/src/pipedal/svg/fx_filter.svg similarity index 100% rename from react/src/svg/fx_filter.svg rename to vite/src/pipedal/svg/fx_filter.svg diff --git a/react/src/svg/fx_filter_hp.svg b/vite/src/pipedal/svg/fx_filter_hp.svg similarity index 100% rename from react/src/svg/fx_filter_hp.svg rename to vite/src/pipedal/svg/fx_filter_hp.svg diff --git a/react/src/svg/fx_flanger.svg b/vite/src/pipedal/svg/fx_flanger.svg similarity index 100% rename from react/src/svg/fx_flanger.svg rename to vite/src/pipedal/svg/fx_flanger.svg diff --git a/react/src/svg/fx_flanger2.svg b/vite/src/pipedal/svg/fx_flanger2.svg similarity index 100% rename from react/src/svg/fx_flanger2.svg rename to vite/src/pipedal/svg/fx_flanger2.svg diff --git a/react/src/svg/fx_function.svg b/vite/src/pipedal/svg/fx_function.svg similarity index 100% rename from react/src/svg/fx_function.svg rename to vite/src/pipedal/svg/fx_function.svg diff --git a/react/src/svg/fx_gate.svg b/vite/src/pipedal/svg/fx_gate.svg similarity index 100% rename from react/src/svg/fx_gate.svg rename to vite/src/pipedal/svg/fx_gate.svg diff --git a/react/src/svg/fx_generator.svg b/vite/src/pipedal/svg/fx_generator.svg similarity index 100% rename from react/src/svg/fx_generator.svg rename to vite/src/pipedal/svg/fx_generator.svg diff --git a/react/src/svg/fx_instrument.svg b/vite/src/pipedal/svg/fx_instrument.svg similarity index 100% rename from react/src/svg/fx_instrument.svg rename to vite/src/pipedal/svg/fx_instrument.svg diff --git a/react/src/svg/fx_limiter.svg b/vite/src/pipedal/svg/fx_limiter.svg similarity index 100% rename from react/src/svg/fx_limiter.svg rename to vite/src/pipedal/svg/fx_limiter.svg diff --git a/react/src/svg/fx_lr.svg b/vite/src/pipedal/svg/fx_lr.svg similarity index 100% rename from react/src/svg/fx_lr.svg rename to vite/src/pipedal/svg/fx_lr.svg diff --git a/react/src/svg/fx_mixer.svg b/vite/src/pipedal/svg/fx_mixer.svg similarity index 100% rename from react/src/svg/fx_mixer.svg rename to vite/src/pipedal/svg/fx_mixer.svg diff --git a/react/src/svg/fx_modulator.svg b/vite/src/pipedal/svg/fx_modulator.svg similarity index 100% rename from react/src/svg/fx_modulator.svg rename to vite/src/pipedal/svg/fx_modulator.svg diff --git a/react/src/svg/fx_oscillator.svg b/vite/src/pipedal/svg/fx_oscillator.svg similarity index 100% rename from react/src/svg/fx_oscillator.svg rename to vite/src/pipedal/svg/fx_oscillator.svg diff --git a/react/src/svg/fx_parametric_eq.svg b/vite/src/pipedal/svg/fx_parametric_eq.svg similarity index 100% rename from react/src/svg/fx_parametric_eq.svg rename to vite/src/pipedal/svg/fx_parametric_eq.svg diff --git a/react/src/svg/fx_phaser.svg b/vite/src/pipedal/svg/fx_phaser.svg similarity index 100% rename from react/src/svg/fx_phaser.svg rename to vite/src/pipedal/svg/fx_phaser.svg diff --git a/react/src/svg/fx_pitch.svg b/vite/src/pipedal/svg/fx_pitch.svg similarity index 100% rename from react/src/svg/fx_pitch.svg rename to vite/src/pipedal/svg/fx_pitch.svg diff --git a/react/src/svg/fx_plugin.svg b/vite/src/pipedal/svg/fx_plugin.svg similarity index 100% rename from react/src/svg/fx_plugin.svg rename to vite/src/pipedal/svg/fx_plugin.svg diff --git a/react/src/svg/fx_reverb.svg b/vite/src/pipedal/svg/fx_reverb.svg similarity index 100% rename from react/src/svg/fx_reverb.svg rename to vite/src/pipedal/svg/fx_reverb.svg diff --git a/react/src/svg/fx_simulator.svg b/vite/src/pipedal/svg/fx_simulator.svg similarity index 100% rename from react/src/svg/fx_simulator.svg rename to vite/src/pipedal/svg/fx_simulator.svg diff --git a/react/src/svg/fx_spatial.svg b/vite/src/pipedal/svg/fx_spatial.svg similarity index 100% rename from react/src/svg/fx_spatial.svg rename to vite/src/pipedal/svg/fx_spatial.svg diff --git a/react/src/svg/fx_spectral.svg b/vite/src/pipedal/svg/fx_spectral.svg similarity index 100% rename from react/src/svg/fx_spectral.svg rename to vite/src/pipedal/svg/fx_spectral.svg diff --git a/react/src/svg/fx_split_a.svg b/vite/src/pipedal/svg/fx_split_a.svg similarity index 100% rename from react/src/svg/fx_split_a.svg rename to vite/src/pipedal/svg/fx_split_a.svg diff --git a/react/src/svg/fx_split_b.svg b/vite/src/pipedal/svg/fx_split_b.svg similarity index 100% rename from react/src/svg/fx_split_b.svg rename to vite/src/pipedal/svg/fx_split_b.svg diff --git a/react/src/svg/fx_terminal.svg b/vite/src/pipedal/svg/fx_terminal.svg similarity index 100% rename from react/src/svg/fx_terminal.svg rename to vite/src/pipedal/svg/fx_terminal.svg diff --git a/react/src/svg/fx_utility.svg b/vite/src/pipedal/svg/fx_utility.svg similarity index 100% rename from react/src/svg/fx_utility.svg rename to vite/src/pipedal/svg/fx_utility.svg diff --git a/react/src/svg/ic_bank.svg b/vite/src/pipedal/svg/ic_bank.svg similarity index 100% rename from react/src/svg/ic_bank.svg rename to vite/src/pipedal/svg/ic_bank.svg diff --git a/react/src/svg/ic_edit_banks.svg b/vite/src/pipedal/svg/ic_edit_banks.svg similarity index 100% rename from react/src/svg/ic_edit_banks.svg rename to vite/src/pipedal/svg/ic_edit_banks.svg diff --git a/react/src/svg/ic_help_outline.svg b/vite/src/pipedal/svg/ic_help_outline.svg similarity index 100% rename from react/src/svg/ic_help_outline.svg rename to vite/src/pipedal/svg/ic_help_outline.svg diff --git a/react/src/svg/ic_midi.svg b/vite/src/pipedal/svg/ic_midi.svg similarity index 100% rename from react/src/svg/ic_midi.svg rename to vite/src/pipedal/svg/ic_midi.svg diff --git a/react/src/svg/ic_pluginpreset.svg b/vite/src/pipedal/svg/ic_pluginpreset.svg similarity index 100% rename from react/src/svg/ic_pluginpreset.svg rename to vite/src/pipedal/svg/ic_pluginpreset.svg diff --git a/react/src/svg/ic_pluginpreset2.svg b/vite/src/pipedal/svg/ic_pluginpreset2.svg similarity index 100% rename from react/src/svg/ic_pluginpreset2.svg rename to vite/src/pipedal/svg/ic_pluginpreset2.svg diff --git a/react/src/svg/ic_presets.svg b/vite/src/pipedal/svg/ic_presets.svg similarity index 100% rename from react/src/svg/ic_presets.svg rename to vite/src/pipedal/svg/ic_presets.svg diff --git a/react/src/svg/ic_save_bank_as.svg b/vite/src/pipedal/svg/ic_save_bank_as.svg similarity index 100% rename from react/src/svg/ic_save_bank_as.svg rename to vite/src/pipedal/svg/ic_save_bank_as.svg diff --git a/react/src/svg/ic_settings.svg b/vite/src/pipedal/svg/ic_settings.svg similarity index 100% rename from react/src/svg/ic_settings.svg rename to vite/src/pipedal/svg/ic_settings.svg diff --git a/react/src/svg/old_delete_outline_24dp.svg b/vite/src/pipedal/svg/old_delete_outline_24dp.svg similarity index 100% rename from react/src/svg/old_delete_outline_24dp.svg rename to vite/src/pipedal/svg/old_delete_outline_24dp.svg diff --git a/react/src/svg/snapshot_0.svg b/vite/src/pipedal/svg/snapshot_0.svg similarity index 100% rename from react/src/svg/snapshot_0.svg rename to vite/src/pipedal/svg/snapshot_0.svg diff --git a/react/src/svg/snapshot_1.svg b/vite/src/pipedal/svg/snapshot_1.svg similarity index 100% rename from react/src/svg/snapshot_1.svg rename to vite/src/pipedal/svg/snapshot_1.svg diff --git a/react/src/svg/snapshot_2.svg b/vite/src/pipedal/svg/snapshot_2.svg similarity index 100% rename from react/src/svg/snapshot_2.svg rename to vite/src/pipedal/svg/snapshot_2.svg diff --git a/react/src/svg/snapshot_3.svg b/vite/src/pipedal/svg/snapshot_3.svg similarity index 100% rename from react/src/svg/snapshot_3.svg rename to vite/src/pipedal/svg/snapshot_3.svg diff --git a/react/src/svg/snapshot_4.svg b/vite/src/pipedal/svg/snapshot_4.svg similarity index 100% rename from react/src/svg/snapshot_4.svg rename to vite/src/pipedal/svg/snapshot_4.svg diff --git a/react/src/svg/snapshot_5.svg b/vite/src/pipedal/svg/snapshot_5.svg similarity index 100% rename from react/src/svg/snapshot_5.svg rename to vite/src/pipedal/svg/snapshot_5.svg diff --git a/react/src/svg/snapshot_6.svg b/vite/src/pipedal/svg/snapshot_6.svg similarity index 100% rename from react/src/svg/snapshot_6.svg rename to vite/src/pipedal/svg/snapshot_6.svg diff --git a/vite/src/vite-env.d.ts b/vite/src/vite-env.d.ts new file mode 100644 index 0000000..ee9857a --- /dev/null +++ b/vite/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/vite/tsconfig.app.json b/vite/tsconfig.app.json new file mode 100644 index 0000000..6440f1e --- /dev/null +++ b/vite/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + // "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/vite/tsconfig.json b/vite/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/vite/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/vite/tsconfig.node.json b/vite/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/vite/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite/vite.config.ts b/vite/vite.config.ts new file mode 100644 index 0000000..8a12749 --- /dev/null +++ b/vite/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import svgr from "vite-plugin-svgr" + +// https://vite.dev/config/ +export default defineConfig({ + build: { + chunkSizeWarningLimit: 2000 + }, + plugins: [react(),svgr()], +})