diff --git a/.env.example b/.env.example index 72d072c..87098aa 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ -# Dev server port (default 5000). Use http://localhost:5000 if VITE_HTTPS is false. -VITE_PORT=5000 +# Dev server port (default 4004). Use http://localhost:4004 if VITE_HTTPS is false. +VITE_PORT=4004 VITE_OVERRIDE_ROS_BRIDGE_SERVER_URL= # Set false for local dev without SSL (avoids ERR_SSL_PROTOCOL_ERROR). Set true and add certs for HTTPS. diff --git a/.gitignore b/.gitignore index fb5188b..0c3bdfe 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ pnpm-debug.log* lerna-debug.log* node_modules +.yarn/ +.pnp.cjs +.pnp.loader.mjs dist dist-ssr *.local @@ -32,7 +35,6 @@ dist-ssr .env.test.local .env.production.local -yarn.lock package-lock.json todo.md diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/README.md b/README.md index 7db0df7..bec0c15 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The application features a distinctive green-on-black cyberpunk aesthetic remini > **Quick Node upgrade with nvm** > -> If you have Node < 22 (e.g. the Ubuntu 22.04 default 18.x), install nvm and switch: +> If you have Node < 22 (e.g. the Ubuntu 24.04 Noble default 18.x), install nvm and switch: > ```sh > # Install nvm (skip if already installed) > curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash @@ -140,7 +140,7 @@ VITE_OVERRIDE_ROS_BRIDGE_SERVER_URL=https://100.100.100.100:5000/rosbridge VITE_ENABLE_LOGS=true -# Hardware YAML editor (`/configuration`): header shows `robot_name` from loaded YAML (via ROS config/get). +# Hardware YAML editor (`/robot-configuration`): header shows `robot_name` from loaded YAML (via ROS config/get). ``` ### Authentication Setup diff --git a/docs/getting_started.md b/docs/getting_started.md new file mode 100644 index 0000000..e1c4e1d --- /dev/null +++ b/docs/getting_started.md @@ -0,0 +1,92 @@ +<-- Welcome --> + +## Welcome to the Lucy control panel. + +This short getting-started guide will walk you through the basics of the control panel and help you get familiar with the main features. + +> You can skip this guide with the button below. +> If you want to replay the guide, you can find it in the setting popup at the top right corner. + +--- + +If you need more in-depth information, you can click the 'Documentation' button at any time or go to [this address](https://docs.sentience-robotics.fr/share/p1x9ikjkhf/p/public-documentation-EExgMX2REV). + +Additionally, if you need specific information or want to exchange on the project, you can join our [community discord server](https://discord.gg/g4KNZ3eeBd), we'll be more than happy to exchange with you! + +--- + +Thanks for using Lucy, it means a lot to us ❤️. + +With that being said, let's get started! + +<-- ROS 2 --> + +Let's have a quick word about ROS 2. + +> [ROS](https://www.ros.org/) (Robot Operating System) is a standard in the field of robotic. + +Lucy use ROS 2 internaly to manage everything, from connecting to the physical actuators to displaying the number of connected controller. + +--- + +The control panel has to connect to this bridge everytime, which is made automatically for you by default. +> If at any point you need to update the connection URL, or toggle the auto-connect feature, you can visit the setting popup. + +<-- Control --> + +> This is where you'll control your robot, and it's full of stuff, so let's go over the basics! + +First thing first, you will need to take control over the robot. This can be achieved by using the 'Control robot' toggle. +> This security system ensure that no more than one controller is sending instruction to your robot. When activated, this will take control from other control applications. + +--- + +## Sliders + +The main part of the screen is occupied by the 'actuators categories', each of them contains multiple actuators. + +> You will be able to assign each & every actuator to a category in the configuration page. + +Each of these actuators has its own control box, letting you update its position in real time. +Every slider comes with two indicators: +- The top green one indicates the default position for this actuator. +- The bottom blue one indicates the actual position. It will move more or less slowly depending on the physical configuration of the actuator. + +<-- Poses & Animations --> + +## Poses + +In the top bar controls, you can find the 'Save pose' & 'Load pose' popups. + +They will let you save every actuators position and load them at a later time when needed. + +> Every pose is saved locally in your browser's localStorage. + +--- + +## Animations + +The animation system uses the saved poses. + +> Once at least two poses are created, you will be able to select them in the 'Manage animation' popup and create an animation with them. + +<-- Sensors --> + +For this part, we will need to travel a bit. + +- On the bottom right corner, you will find the navigation bar, select the second option 'Sensors'. + +> It sure look empty, let's fix it! + +Using the drop-down menu, you can select every sensors you want to watch. + +--- + +> We currently support direct data display & temperatures, with a graph. +> If you have sensors whose data cannot be displayed, feel free to create an [issue](https://github.com/Sentience-Robotics/lucy_control_panel/issues) or to [contact us](https://discord.gg/g4KNZ3eeBd) directly. + +<-- Configuration --> + +The configuration page is undergoing a complete refactor, usage guide will be updated soon. + +In the meantime, if you need more information, please refer to our [online documentation](https://docs.sentience-robotics.fr/share/p1x9ikjkhf/p/public-documentation-EExgMX2REV). \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index a85d95a..abdc340 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,10 +7,14 @@ import { RobotControlPanel } from './Pages/RobotControlPanel'; import { Navigation } from './Components/Navigation'; import { NotFound } from './Pages/NotFound'; import { ActiveHardwareRosProvider } from './contexts/ActiveHardwareRosContext'; +import { GettingStartedModal } from './Components/GettingStartedModal'; /* Components */ import { AuthForm } from './Components/AuthForm'; import { LucyLoader } from './Components/LucyLoader'; +import { Page } from './Components/Page'; +import { ROUTES } from './Constants/routes.ts'; import { + PAGE_CONTENT_STYLE, UI_ACCENT_GREEN, UI_BG_BLACK, UI_BORDER_STRONG, @@ -20,21 +24,21 @@ import { UI_TEXT_SUBTLE, } from './Constants/uiTheme.ts'; -const { Content } = Layout; const { useBreakpoint } = Grid; const Configuration = lazy(() => import('./Pages/Configuration').then(module => ({ default: module.default }))); const SensorDisplay = lazy(() => import('./Pages/SensorDisplay').then(module => ({ default: module.default }))); -const CONTROL_PATH = '/'; -const CONFIG_PATH = '/configuration'; -const SENSORS_PATH = '/sensors'; -const KNOWN_PATHS = new Set([CONTROL_PATH, CONFIG_PATH, SENSORS_PATH]); +const KNOWN_PATHS = new Set([ + ROUTES.control, + ROUTES.robotConfiguration, + ROUTES.sensors, +]); /** * Render all main pages once and toggle visibility with `display: none`. * Keeps page-local state (sliders, edits, expanded panels) alive when the - * user switches between CONTROL / CONFIGURATION. Lazy pages are only mounted + * user switches between CONTROL / ROBOT CONFIGURATION. Lazy pages are only mounted * after their first visit so the initial chunk stays small. */ const PersistentPages: FC = () => { @@ -43,8 +47,8 @@ const PersistentPages: FC = () => { const [sensorsVisited, setSensorsVisited] = useState(false); useEffect(() => { - if (pathname === CONFIG_PATH) setConfigVisited(true); - if (pathname === SENSORS_PATH) setSensorsVisited(true); + if (pathname === ROUTES.robotConfiguration) setConfigVisited(true); + if (pathname === ROUTES.sensors) setSensorsVisited(true); }, [pathname]); const isKnown = KNOWN_PATHS.has(pathname); @@ -55,16 +59,16 @@ const PersistentPages: FC = () => { return ( <> -
+
{configVisited ? ( -
+
} > @@ -73,7 +77,7 @@ const PersistentPages: FC = () => {
) : null} {sensorsVisited ? ( -
+
- + - + + diff --git a/src/Components/AnimationManager.tsx b/src/Components/AnimationManager.tsx index 1501140..b2d8cbf 100644 --- a/src/Components/AnimationManager.tsx +++ b/src/Components/AnimationManager.tsx @@ -1,6 +1,5 @@ import React, { useState, useCallback, useEffect } from 'react'; import { - Modal, Button, Input, Space, @@ -16,16 +15,16 @@ import { DeleteOutlined } from '@ant-design/icons'; import { storageService, type SavedPose, type SavedAnimation } from '../Services/storage.service.ts'; +import { MovableModal } from './MovableModal'; import { UI_ACCENT_GREEN, UI_BORDER_SOFT, UI_COLOR_TRANSPARENT, UI_INPUT_SURFACE, - UI_MODAL_MASK_BG, UI_TEXT_PRIMARY_ON_DARK, } from '../Constants/uiTheme.ts'; -const { Text, Title } = Typography; +const { Text } = Typography; const { Option } = Select; interface AnimationManagerProps { @@ -168,16 +167,16 @@ export const AnimationManager: React.FC = ({ onPlayAnimat {/* Manage Animation Modal */} - - Manage Animations - - } - open={manageModalVisible} - onCancel={() => setManageModalVisible(false)} - footer={[ - editingAnimId ? ( + setManageModalVisible(false)} + initialPosition={{ x: 760, y: 120 }} + initialSize={{ w: 350, h: 650 }} + header={} + footer={ + <> + {editingAnimId ? ( = ({ onPlayAnimat > - ) : null, + ) : null} , - + - ]} - style={{ top: 100 }} - styles={{ mask: { backgroundColor: UI_MODAL_MASK_BG } }} - className="dark-modal" + + } >
@@ -255,7 +261,7 @@ export const AnimationManager: React.FC = ({ onPlayAnimat
-
+ ); }; diff --git a/src/Components/AppHeader.tsx b/src/Components/AppHeader.tsx index aab50d9..24272c2 100644 --- a/src/Components/AppHeader.tsx +++ b/src/Components/AppHeader.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState, useRef } from 'react'; import { Button, Tooltip, Typography, Grid } from 'antd'; -import { SettingOutlined, ReadOutlined } from '@ant-design/icons'; +import { SettingOutlined, ReadOutlined, BugOutlined, NodeIndexOutlined } from '@ant-design/icons'; import { useRosConnection } from '../hooks/useRosConnection.hook'; import { ConnectedClientsHandler } from '../Services/ros/handlers/ConnectedClients.handler'; import { ControlModeHandler } from '../Services/ros/handlers/ControlMode.handler'; @@ -13,6 +13,7 @@ import { UI_TEXT_SECONDARY_MUTED, } from '../Constants/uiTheme'; import { SettingsModal, isAutoConnectEnabled } from './SettingsModal'; +import { ConnectionDebugModal, CommandDebugModal } from './PipelineDebugModal'; const { Text } = Typography; const { useBreakpoint } = Grid; @@ -21,6 +22,8 @@ export const AppHeader: React.FC = () => { const { connectionStatus, isConnected, connect, currentUrl } = useRosConnection(); const [countState, setCountState] = useState(0); const [isSettingsModalVisible, setIsSettingsModalVisible] = useState(false); + const [isConnectionDebugVisible, setIsConnectionDebugVisible] = useState(false); + const [isCommandDebugVisible, setIsCommandDebugVisible] = useState(false); const [activeControllerId, setActiveControllerId] = useState( () => ControlModeHandler.getInstance().currentControllerId ); @@ -180,6 +183,12 @@ export const AppHeader: React.FC = () => { {!isMobile && 'Documentation'} + +
); }; diff --git a/src/Components/DraggableCategory.tsx b/src/Components/DraggableCategory.tsx index d58f275..22d8a33 100644 --- a/src/Components/DraggableCategory.tsx +++ b/src/Components/DraggableCategory.tsx @@ -12,6 +12,7 @@ interface DraggableCategoryProps { joints: JointControlState[]; onJointValueChange: (name: string, value: number) => void; onResetCategory: (category: string) => void; + onResetJoint?: (name: string) => void; showDegrees: boolean; disabled?: boolean; } @@ -22,6 +23,7 @@ export const DraggableCategory: React.FC = ({ joints, onJointValueChange, onResetCategory, + onResetJoint, showDegrees, disabled = false, }) => { @@ -87,6 +89,7 @@ export const DraggableCategory: React.FC = ({ joints={joints} onJointValueChange={onJointValueChange} onResetCategory={onResetCategory} + onResetJoint={onResetJoint} showDegrees={showDegrees} disabled={disabled} /> diff --git a/src/Components/GettingStartedModal.tsx b/src/Components/GettingStartedModal.tsx new file mode 100644 index 0000000..bc159a4 --- /dev/null +++ b/src/Components/GettingStartedModal.tsx @@ -0,0 +1,275 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { Button, Divider, Space, Typography } from 'antd'; +import { + UI_ACCENT_GREEN, + UI_BORDER_MUTED, + UI_BORDER_SOFT, + UI_CHROME_SURFACE, + UI_COLOR_TRANSPARENT, + UI_PANEL_BG, + UI_TEXT_ON_ACCENT, + UI_TEXT_PRIMARY_ON_DARK, + UI_TEXT_SUBTLE, +} from '../Constants/uiTheme.ts'; +import { useRosConnection } from '../hooks/useRosConnection.hook'; +import { MovableModal } from './MovableModal.tsx'; +import gettingStartedMarkdown from '../../docs/getting_started.md?raw'; + +const { Paragraph, Text, Title } = Typography; + +export const GETTING_STARTED_COMPLETED_KEY = 'gettingStartedCompleted'; +export const REDO_GETTING_STARTED_EVENT = 'redoGettingStarted'; + +export interface GettingStartedSection { + name: string; + content: string; +} + +export function parseGettingStartedSections(markdown: string): GettingStartedSection[] { + const sections: GettingStartedSection[] = []; + let currentSection: GettingStartedSection | null = null; + + for (const line of markdown.split(/\r?\n/)) { + const marker = line.match(/^<--\s*(.*?)\s*-->$/); + if (marker) { + if (currentSection) sections.push({ ...currentSection, content: currentSection.content.trim() }); + currentSection = { name: marker[1].trim(), content: '' }; + } else if (currentSection) { + currentSection.content += `${line}\n`; + } + } + + if (currentSection) sections.push({ ...currentSection, content: currentSection.content.trim() }); + return sections.filter(section => section.name.length > 0); +} + +function renderInline(text: string): React.ReactNode[] { + return text.split(/(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\(https?:\/\/[^)\s]+\))/g).filter(Boolean).map((part, index) => { + if (part.startsWith('`') && part.endsWith('`')) { + return {part.slice(1, -1)}; + } + if (part.startsWith('**') && part.endsWith('**')) { + return {part.slice(2, -2)}; + } + const link = part.match(/^\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)$/); + if (link) { + return ( + + {link[1]} + + ); + } + return {part}; + }); +} + +function MarkdownContent({ content }: { content: string }) { + const lines = content.split(/\r?\n/); + const blocks: React.ReactNode[] = []; + let paragraphLines: string[] = []; + let listItems: string[] = []; + + const flushParagraph = () => { + if (paragraphLines.length > 0) { + blocks.push({renderInline(paragraphLines.join(' '))}); + paragraphLines = []; + } + }; + const flushList = () => { + if (listItems.length > 0) { + blocks.push( +
    + {listItems.map((item, index) =>
  • {renderInline(item)}
  • )} +
, + ); + listItems = []; + } + }; + + lines.forEach((line, index) => { + const trimmed = line.trim(); + if (!trimmed) { + flushParagraph(); + flushList(); + } else if (/^-{3,}$/.test(trimmed)) { + flushParagraph(); + flushList(); + blocks.push( + , + ); + } else if (trimmed.startsWith('- ')) { + flushParagraph(); + listItems.push(trimmed.slice(2)); + } else if (trimmed.startsWith('> ')) { + flushParagraph(); + flushList(); + blocks.push( +
+ {renderInline(trimmed.slice(2))} +
, + ); + } else if (trimmed.startsWith('### ')) { + flushParagraph(); + flushList(); + blocks.push({renderInline(trimmed.slice(4))}); + } else if (trimmed.startsWith('## ')) { + flushParagraph(); + flushList(); + blocks.push({renderInline(trimmed.slice(3))}); + } else { + flushList(); + paragraphLines.push(trimmed); + } + }); + + flushParagraph(); + flushList(); + return <>{blocks}; +} + +export const isGettingStartedCompleted = () => + localStorage.getItem(GETTING_STARTED_COMPLETED_KEY) === 'true'; + +export const GettingStartedModal: React.FC = () => { + const sections = useMemo(() => parseGettingStartedSections(gettingStartedMarkdown), []); + const { isConnected } = useRosConnection(); + const [visible, setVisible] = useState(false); + const [sectionIndex, setSectionIndex] = useState(0); + + useEffect(() => { + if (isConnected && !isGettingStartedCompleted()) setVisible(true); + }, [isConnected]); + + useEffect(() => { + const redo = () => { + localStorage.removeItem(GETTING_STARTED_COMPLETED_KEY); + setSectionIndex(0); + if (isConnected) setVisible(true); + }; + window.addEventListener(REDO_GETTING_STARTED_EVENT, redo); + return () => window.removeEventListener(REDO_GETTING_STARTED_EVENT, redo); + }, [isConnected]); + + const complete = () => { + localStorage.setItem(GETTING_STARTED_COMPLETED_KEY, 'true'); + setVisible(false); + }; + const section = sections[sectionIndex]; + if (!section) return null; + + return ( + + SECTION {sectionIndex + 1}/{sections.length} + + } + > +
+
event.stopPropagation()} + > + + {section.name} + + +
+
+ + + + {sectionIndex === sections.length - 1 ? ( + + ) : ( + + )} + +
+
+
+ ); +}; diff --git a/src/Components/HardwareYamlConfigManager.tsx b/src/Components/HardwareYamlConfigManager.tsx index bb49431..d834f2a 100644 --- a/src/Components/HardwareYamlConfigManager.tsx +++ b/src/Components/HardwareYamlConfigManager.tsx @@ -5,7 +5,6 @@ import { Col, Input, List, - Modal, Popconfirm, Row, Space, @@ -31,13 +30,13 @@ import { UI_COLOR_TRANSPARENT, UI_INPUT_SURFACE, UI_LIST_ROW_BG, - UI_MODAL_MASK_BG, UI_PRIMARY_GREEN_BUTTON_STYLE, UI_TEXT_PRIMARY_ON_DARK, UI_TEXT_SUBTLE, } from '../Constants/uiTheme.ts'; +import { MovableModal } from './MovableModal'; -const { Text, Title } = Typography; +const { Text } = Typography; const QUICK_CONFIG_NAMES = ['default', 'staging', 'test_bench', 'production', 'minimal']; @@ -125,10 +124,6 @@ export const HardwareYamlConfigManager: React.FC [onLoadConfig], ); - const modalStyles = { - mask: { backgroundColor: UI_MODAL_MASK_BG }, - }; - const inputDarkStyle = { backgroundColor: UI_INPUT_SURFACE, borderColor: UI_BORDER_SOFT, @@ -194,29 +189,22 @@ export const HardwareYamlConfigManager: React.FC - - SAVE HARDWARE CONFIGURATION - - } - open={saveModalVisible} - onOk={handleSaveConfig} - onCancel={() => { + { setSaveModalVisible(false); setConfigNameInput(''); }} - confirmLoading={saving} - okText="SAVE CONFIGURATION" - cancelText="CANCEL" - okButtonProps={{ - disabled: !yamlDoc || !isDirty || !configNameInput.trim(), - style: UI_PRIMARY_GREEN_BUTTON_STYLE, - }} - style={{ top: 100 }} - styles={modalStyles} - className="dark-modal" - destroyOnClose + initialPosition={{ x: 120, y: 80 }} + initialSize={{ w: 680, h: 520 }} + header={} + footer={ + <> + + + + } > {!isDirty && !saving ? ( @@ -279,25 +267,16 @@ export const HardwareYamlConfigManager: React.FC - + - - LOAD HARDWARE CONFIGURATION - - } - open={loadModalVisible} - footer={ - - } - onCancel={() => setLoadModalVisible(false)} - width={720} - style={{ top: 50 }} - styles={modalStyles} - className="dark-modal" + setLoadModalVisible(false)} + initialPosition={{ x: 100, y: 60 }} + initialSize={{ w: 720, h: 600 }} + header={} + footer={} >
diff --git a/src/Components/MovableModal.tsx b/src/Components/MovableModal.tsx index 7d311b6..960ffbf 100644 --- a/src/Components/MovableModal.tsx +++ b/src/Components/MovableModal.tsx @@ -4,45 +4,154 @@ import { UI_ACCENT_GREEN, UI_BORDER_DIM, UI_BORDER_MUTED, - UI_GRADIENT_MODAL_HEADER, + UI_CHROME_SURFACE, UI_MODAL_SURFACE, UI_SHADOW_ELEVATED, + UI_TEXT_PRIMARY_ON_DARK, + UI_TEXT_SUBTLE, } from '../Constants/uiTheme.ts'; const { useBreakpoint } = Grid; -interface MediapipeHandTrackerModalProps { +interface ModalPosition { + x: number; + y: number; +} + +interface ModalSize { + w: number; + h: number; +} + +interface RegisteredModal { + position: ModalPosition; + size: ModalSize; +} + +const registeredModals = new Map(); +const VIEWPORT_MARGIN = 8; +const PLACEMENT_STEP = 24; + +const clampPosition = (position: ModalPosition, size: ModalSize): ModalPosition => { + const maxX = Math.max(VIEWPORT_MARGIN, window.innerWidth - size.w - VIEWPORT_MARGIN); + const maxY = Math.max(VIEWPORT_MARGIN, window.innerHeight - size.h - VIEWPORT_MARGIN); + + return { + x: Math.min(Math.max(VIEWPORT_MARGIN, position.x), maxX), + y: Math.min(Math.max(VIEWPORT_MARGIN, position.y), maxY), + }; +}; + +const overlaps = (first: RegisteredModal, second: RegisteredModal) => ( + first.position.x < second.position.x + second.size.w + && first.position.x + first.size.w > second.position.x + && first.position.y < second.position.y + second.size.h + && first.position.y + first.size.h > second.position.y +); + +const findAvailablePosition = (preferredPosition: ModalPosition, size: ModalSize, id: symbol): ModalPosition => { + const preferred = clampPosition(preferredPosition, size); + const occupied = Array.from(registeredModals.entries()) + .filter(([registeredId]) => registeredId !== id) + .map(([, modal]) => modal); + const fits = (position: ModalPosition) => { + const candidate = { position, size }; + return occupied.every(modal => !overlaps(candidate, modal)); + }; + + if (fits(preferred)) { + return preferred; + } + + const maxX = Math.max(VIEWPORT_MARGIN, window.innerWidth - size.w - VIEWPORT_MARGIN); + const maxY = Math.max(VIEWPORT_MARGIN, window.innerHeight - size.h - VIEWPORT_MARGIN); + for (let y = VIEWPORT_MARGIN; y <= maxY; y += PLACEMENT_STEP) { + for (let x = VIEWPORT_MARGIN; x <= maxX; x += PLACEMENT_STEP) { + const candidate = { x, y }; + if (fits(candidate)) { + return candidate; + } + } + } + + // The viewport is fully occupied; retaining the preferred position is the least surprising fallback. + return preferred; +}; + +interface MovableModalProps { children: ReactNode; header?: ReactNode; + footer?: ReactNode; modalName: string; isVisible: boolean; onClose: () => void; initialPosition?: { x: number; y: number }; initialSize?: { w: number; h: number }; - aspectRatio?: number; + contentPadding?: number | string; mobileFixedTop?: boolean; mobileTopOffset?: number; + footerWrap?: boolean; + minWidth?: number; } export function MovableModal({ children, header, + footer, modalName, isVisible, onClose, - initialPosition = { x: 100, y: 100 }, - initialSize = { w: 480, h: 320 }, - aspectRatio = 4 / 3, + initialPosition = { x: 100, y: 120 }, + initialSize = { w: 350, h: 650 }, + contentPadding = 24, mobileFixedTop = false, mobileTopOffset = 0, -}: MediapipeHandTrackerModalProps) { - const [{ x, y }, setPos] = useState(initialPosition); + footerWrap = true, + minWidth = 260, +}: MovableModalProps) { + const screens = useBreakpoint(); + const isMobile = !screens.md; + const hasResolvedBreakpoint = screens.md !== undefined; + const hasInitializedPositionRef = useRef(hasResolvedBreakpoint); + const [{ x, y }, setPos] = useState(isMobile ? { x: 20, y: 120 } : initialPosition); const [{ w, h }, setSize] = useState(initialSize); + const modalIdRef = useRef(Symbol(modalName)); + const wasVisibleRef = useRef(false); + const hasOpenedRef = useRef(false); const draggingRef = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null); const resizingRef = useRef<{ startX: number; startY: number; origW: number; origH: number } | null>(null); - const screens = useBreakpoint(); const isLocked = mobileFixedTop && !screens.md; + React.useEffect(() => { + if (!hasResolvedBreakpoint || hasInitializedPositionRef.current) return; + hasInitializedPositionRef.current = true; + setPos(isMobile ? { x: 20, y: 120 } : initialPosition); + }, [hasResolvedBreakpoint, initialPosition, isMobile]); + + React.useEffect(() => { + const modalId = modalIdRef.current; + if (!hasResolvedBreakpoint || !isVisible || isLocked) { + registeredModals.delete(modalId); + wasVisibleRef.current = false; + return; + } + + if (!wasVisibleRef.current) { + const preferredPosition = hasOpenedRef.current || isMobile ? { x, y } : initialPosition; + const position = findAvailablePosition(preferredPosition, { w, h }, modalId); + setPos(position); + registeredModals.set(modalId, { position, size: { w, h } }); + wasVisibleRef.current = true; + hasOpenedRef.current = true; + } else { + registeredModals.set(modalId, { position: { x, y }, size: { w, h } }); + } + + return () => { + registeredModals.delete(modalId); + }; + }, [h, hasResolvedBreakpoint, initialPosition, isLocked, isMobile, isVisible, w, x, y]); + if (!isVisible) { return null; } const handleDragStart = (e: React.MouseEvent) => { @@ -86,11 +195,8 @@ export function MovableModal({ if (!resizingRef.current) { return; } const dw = ev.clientX - resizingRef.current.startX; const dh = ev.clientY - resizingRef.current.startY; - - // Use the larger delta to maintain aspect ratio - const delta = Math.max(dw, dh); - const newW = Math.max(260, resizingRef.current.origW + delta); - const newH = Math.max(195, newW / aspectRatio); + const newW = Math.max(minWidth, resizingRef.current.origW + dw); + const newH = Math.max(195, resizingRef.current.origH + dh); setSize({ w: newW, h: newH }); }; @@ -113,44 +219,109 @@ export function MovableModal({ left: isLocked ? undefined : x, top: isLocked ? mobileTopOffset : y, width: isLocked ? '100%' : w, + minWidth: isLocked ? undefined : minWidth, height: isLocked ? '33.333vh' : h, marginBottom: isLocked ? 12 : undefined, zIndex: isLocked ? 1 : 1000, backgroundColor: UI_MODAL_SURFACE, border: `1px solid ${UI_BORDER_MUTED}`, - borderRadius: 8, + borderRadius: 0, boxShadow: UI_SHADOW_ELEVATED, overflow: 'hidden', + overscrollBehavior: 'contain', userSelect: 'none', + display: 'flex', + flexDirection: 'column', + boxSizing: 'border-box', }} > {/* Header Bar */}
-
- +
+ {modalName} {header}
- -
- {children} +
+ {children} +
+ {footer ? ( +
+ {footer} +
+ ) : null} {/* Resize Handle */} {!isLocked && ( diff --git a/src/Components/Navigation.tsx b/src/Components/Navigation.tsx index aa9b41d..529f29d 100644 --- a/src/Components/Navigation.tsx +++ b/src/Components/Navigation.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Link, useLocation } from 'react-router-dom'; import { Button, Space, Grid } from 'antd'; import { ControlOutlined, SettingOutlined, FundProjectionScreenOutlined } from '@ant-design/icons'; +import { ROUTES } from '../Constants/routes.ts'; import { UI_ACCENT_GREEN, UI_BORDER_MUTED, @@ -15,9 +16,9 @@ import { const { useBreakpoint } = Grid; const navigationItems = [ - { to: '/', label: 'CONTROL', icon: }, - { to: '/sensors', label: 'SENSORS', icon: }, - { to: '/configuration', label: 'CONFIGURATION', icon: }, + { to: ROUTES.control, label: 'CONTROL', icon: }, + { to: ROUTES.sensors, label: 'SENSORS', icon: }, + { to: ROUTES.robotConfiguration, label: 'ROBOT CONFIGURATION', icon: }, ]; export const Navigation: React.FC = () => { diff --git a/src/Components/Page.tsx b/src/Components/Page.tsx index b3c946d..3d71fd3 100644 --- a/src/Components/Page.tsx +++ b/src/Components/Page.tsx @@ -11,6 +11,7 @@ import { UI_INPUT_SURFACE, UI_PANEL_BG, UI_PAGE_HEADER_BORDER_BOTTOM, + PAGE_CONTENT_STYLE, UI_TEXT_ON_ACCENT, UI_TEXT_PRIMARY_ON_DARK, UI_TEXT_SECONDARY_MUTED, @@ -59,7 +60,7 @@ export const Page: React.FC = ({ const defaultContentStyle: React.CSSProperties = { backgroundColor: UI_BG_BLACK, minHeight: showHeader ? 'calc(100vh - 70px)' : '100vh', - padding: isMobile ? '12px' : '24px', + ...PAGE_CONTENT_STYLE, ...contentStyle, }; @@ -315,7 +316,12 @@ export const Page: React.FC = ({ text-shadow: 0 0 8px ${uiAccentRgba(0.6)}; } - .tui-toggle-button.active { + .tui-toggle-button.off { + background-color: ${UI_ERROR}; + color: ${UI_TEXT_ON_ACCENT}; + } + + .tui-toggle-button.on { background-color: ${UI_ACCENT_GREEN}; color: ${UI_TEXT_ON_ACCENT}; box-shadow: 0 0 15px ${uiAccentRgba(0.6)}; @@ -391,25 +397,29 @@ export const Page: React.FC = ({ {showHeader && (
{title && ( = { + ok: UI_ACCENT_GREEN, + warn: UI_WARNING, + error: UI_ERROR, + pending: UI_TEXT_SUBTLE, +}; + +const STATUS_GLYPH: Record<StageStatus, string> = { + ok: '●', + warn: '▲', + error: '✕', + pending: '○', +}; + +function ago(at?: number): string { + if (!at) return ''; + const seconds = Math.round((Date.now() - at) / 1000); + if (seconds < 1) return 'just now'; + if (seconds < 60) return `${seconds}s ago`; + return `${Math.round(seconds / 60)}m ago`; +} + +/** Re-render on every diagnostics update, plus a tick so "ago" stays honest. */ +export function useDiagnosticsTick(): number { + const [tick, setTick] = useState(0); + useEffect(() => { + const bump = () => setTick((t) => t + 1); + const unsubscribe = Diagnostics.subscribe(bump); + const timer = setInterval(bump, 1000); + return () => { + unsubscribe(); + clearInterval(timer); + }; + }, []); + return tick; +} + +export function StageRow({ stage, index }: { stage: Stage; index: number }) { + return ( + <div + style={{ + display: 'flex', + gap: 10, + padding: '8px 10px', + background: UI_LIST_ROW_BG, + borderBottom: `1px solid ${UI_BORDER_DIM}`, + alignItems: 'flex-start', + }} + > + <span style={{ color: UI_TEXT_SUBTLE, fontFamily: 'monospace', minWidth: 18 }}> + {index + 1} + </span> + <span style={{ color: STATUS_COLOR[stage.status], minWidth: 14 }}> + {STATUS_GLYPH[stage.status]} + </span> + <div style={{ flex: 1, minWidth: 0 }}> + <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}> + <Text style={{ color: UI_TEXT_PRIMARY_ON_DARK, fontWeight: 600 }}> + {stage.label} + </Text> + <Text style={{ color: UI_TEXT_SUBTLE, fontSize: 11, whiteSpace: 'nowrap' }}> + {stage.count != null ? `${stage.count} msg · ` : ''} + {ago(stage.at)} + </Text> + </div> + <div style={{ color: UI_TEXT_SUBTLE, fontSize: 11 }}>{stage.hint}</div> + {stage.detail && ( + <div + style={{ + color: STATUS_COLOR[stage.status], + fontSize: 11, + fontFamily: 'monospace', + wordBreak: 'break-all', + marginTop: 2, + }} + > + {stage.detail} + </div> + )} + </div> + </div> + ); +} + + +/** Fallback publisher used on platforms where ros2_control cannot run. */ +const JOINT_STATE_FALLBACK = 'joint_state_publisher'; + +/** + * Distinguish real ros2_control from the stand-in. + * + * joint_command_echo mirrors commands straight back onto /joint_states, so the + * command pipeline goes green whether or not anything is actually driving the + * robot. Checking for controller_manager is what tells the two apart. + */ +async function probeController(): Promise<{ status: StageStatus; detail: string }> { + const ros = RosBridgeService.getInstance(); + const [nodes, publishers] = await Promise.all([ + ros.getNodes(), + ros.getPublishers('/joint_states'), + ]); + const hasManager = nodes.some((n) => n.includes('controller_manager')); + const fallback = publishers.some((p) => p.includes(JOINT_STATE_FALLBACK)); + + if (hasManager) { + return { status: 'ok', detail: `controller_manager running; /joint_states from ${publishers.join(', ') || 'nobody'}` }; + } + if (fallback) { + return { + status: 'warn', + detail: + 'no controller_manager — commands are mirrored back by joint_command_echo. ' + + 'Motion is instant and open loop: no trajectory interpolation, no hardware.', + }; + } + return { + status: 'error', + detail: `no controller_manager and no known publisher (${publishers.join(', ') || 'none'}) — nothing executes commands`, + }; +} + +/** + * Connect button -> rosbridge -> ROS graph -> robot configuration. + * + * The rosapi probe is on demand: it asks who publishes /joint_states, which + * separates "rosbridge is up" from "rosbridge is attached to a live robot". + */ +export function ConnectionDebugModal(props: { isVisible: boolean; onClose: () => void }) { + useDiagnosticsTick(); + const [probing, setProbing] = useState(false); + + const probe = async () => { + setProbing(true); + Diagnostics.record('connection', 'rosapi', 'pending', 'asking rosapi...'); + try { + const publishers = await RosBridgeService.getInstance().getPublishers('/joint_states'); + Diagnostics.record( + 'connection', + 'rosapi', + publishers.length ? 'ok' : 'warn', + publishers.length + ? `/joint_states published by ${publishers.join(', ')}` + + (publishers.some((p) => p.includes(JOINT_STATE_FALLBACK)) + ? ' (stand-in, not ros2_control)' + : '') + : 'rosapi answered, but nothing publishes /joint_states', + ); + } catch (error) { + Diagnostics.record( + 'connection', + 'rosapi', + 'error', + error instanceof Error ? error.message : String(error), + ); + } finally { + setProbing(false); + } + }; + + // Costs one service call, so run it on open rather than making it a chore. + useEffect(() => { + if (props.isVisible) void probe(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.isVisible]); + + return ( + <MovableModal + modalName="Connection pipeline" + isVisible={props.isVisible} + onClose={props.onClose} + initialSize={{ w: 460, h: 520 }} + header={ + <Button size="small" onClick={probe} loading={probing}> + Re-probe + </Button> + } + > + <div style={{ overflowY: 'auto', height: '100%' }}> + {Diagnostics.getStages('connection').map((stage, i) => ( + <StageRow key={stage.id} stage={stage} index={i} /> + ))} + </div> + </MovableModal> + ); +} + +/** Slider -> JointTrajectory -> /joint_states -> rendered pose, with the joints you moved. */ +export function CommandDebugModal(props: { isVisible: boolean; onClose: () => void }) { + useDiagnosticsTick(); + const [checking, setChecking] = useState(false); + + const checkController = async () => { + setChecking(true); + Diagnostics.record('command', 'controller', 'pending', 'checking for controller_manager...'); + try { + const { status, detail } = await probeController(); + Diagnostics.record('command', 'controller', status, detail); + } catch (error) { + Diagnostics.record( + 'command', 'controller', 'error', + error instanceof Error ? error.message : String(error), + ); + } finally { + setChecking(false); + } + }; + + // Answer "is ros2_control actually running?" without being asked. + useEffect(() => { + if (props.isVisible) void checkController(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.isVisible]); + + return ( + <MovableModal + modalName="Command pipeline" + isVisible={props.isVisible} + onClose={props.onClose} + initialSize={{ w: 520, h: 560 }} + header={ + <Button size="small" onClick={checkController} loading={checking}> + Re-check + </Button> + } + > + <div style={{ overflowY: 'auto', height: '100%' }}> + {Diagnostics.getStages('command').map((stage, i) => ( + <StageRow key={stage.id} stage={stage} index={i} /> + ))} + + </div> + </MovableModal> + ); +} diff --git a/src/Components/PoseManager.tsx b/src/Components/PoseManager.tsx index 6a9ed81..bf505fb 100644 --- a/src/Components/PoseManager.tsx +++ b/src/Components/PoseManager.tsx @@ -29,15 +29,15 @@ import { UI_COLOR_TRANSPARENT, UI_INPUT_SURFACE, UI_LIST_ROW_BG, - UI_MODAL_MASK_BG, UI_PANEL_BG, UI_PRIMARY_GREEN_BUTTON_STYLE, UI_TEXT_PRIMARY_ON_DARK, UI_TEXT_SUBTLE, } from '../Constants/uiTheme.ts'; import { storageService, type SavedPose, PoseInUseError } from '../Services/storage.service.ts'; +import { MovableModal } from './MovableModal'; -const { Text, Title } = Typography; +const { Text } = Typography; interface PoseManagerProps { joints: JointControlState[]; @@ -202,26 +202,22 @@ export const PoseManager: React.FC<PoseManagerProps> = ({ joints, onLoadPose }) </Space> {/* Save Pose Modal */} - <Modal - title={ - <Title level={4} style={{ color: UI_ACCENT_GREEN, margin: 0 }}> - <SaveOutlined /> Save Current Pose - - } - open={saveModalVisible} - onOk={() => void handleSavePose()} - onCancel={() => { + { setSaveModalVisible(false); setPoseName(''); }} - confirmLoading={loading} - okText="Save Pose" - cancelText="Cancel" - style={{ top: 100 }} - styles={{ - mask: { backgroundColor: UI_MODAL_MASK_BG } - }} - className="dark-modal" + initialPosition={{ x: 20, y: 120 }} + initialSize={{ w: 350, h: 650 }} + header={} + footer={ + <> + + + + } > @@ -282,36 +278,17 @@ export const PoseManager: React.FC = ({ joints, onLoadPose }) - + {/* Load Pose Modal */} - - Load Saved Pose - - } - open={loadModalVisible} - footer={[ - - ]} - onCancel={() => setLoadModalVisible(false)} - width={700} - style={{ top: 50 }} - styles={{ - mask: { backgroundColor: UI_MODAL_MASK_BG } - }} - className="dark-modal" + setLoadModalVisible(false)} + initialPosition={{ x: 390, y: 120 }} + initialSize={{ w: 350, h: 650 }} + header={} + footer={} > {savedPoses.length === 0 ? ( @@ -384,7 +361,7 @@ export const PoseManager: React.FC = ({ joints, onLoadPose }) )} /> )} - + ); }; diff --git a/src/Components/Sensors/FloatGraph.tsx b/src/Components/Sensors/FloatGraph.tsx index 704eb57..3db6be1 100644 --- a/src/Components/Sensors/FloatGraph.tsx +++ b/src/Components/Sensors/FloatGraph.tsx @@ -1,110 +1,203 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useRef } from 'react'; import { Button } from 'antd'; +import type { SensorSource } from '../../Constants/rosConfig'; import { UI_ACCENT_GREEN, UI_BG_BLACK, UI_BORDER_SOFT } from '../../Constants/uiTheme'; +import { useSensorStream } from '../../hooks/useSensorStream'; interface FloatGraphProps { - dataSourceId: string; - sourceName: string; + source: SensorSource; } -export const FloatGraph: React.FC = ({ dataSourceId, sourceName }) => { - const [sourceData, setSourceData] = useState<{ time: number, value: number }[]>([]); - const [displayData, setDisplayData] = useState<{ time: number, value: number }[]>([]); - const [isPaused, setIsPaused] = useState(false); +function resolveYAxisBounds( + historicalMin: number | null, + historicalMax: number | null, +): { minValue: number; maxValue: number } { + if (historicalMin === null || historicalMax === null) { + return { minValue: 0, maxValue: 100 }; + } + if (historicalMin === historicalMax) { + const pad = Math.max(Math.abs(historicalMin) * 0.1, 1); + return { minValue: historicalMin - pad, maxValue: historicalMax + pad }; + } + const pad = (historicalMax - historicalMin) * 0.05; + return { minValue: historicalMin - pad, maxValue: historicalMax + pad }; +} + +/** Live time-series graph for a single float value on a `sensors/` Float32Array topic. */ +export const FloatGraph: React.FC = ({ source }) => { const graphRef = useRef(null); - - // Data source always runs in the background - useEffect(() => { - let startTime = Date.now(); - const interval = setInterval(() => { - const now = Date.now(); - const timeElapsed = (now - startTime) / 1000; - const value = 20 + Math.sin(timeElapsed) * 5 + (Math.random() * 2 - 1); - - setSourceData(prevData => { - const newData = [...prevData, { time: now, value }]; - return newData.filter(d => now - d.time < 60000); - }); - }, 1000); - - return () => clearInterval(interval); - }, [dataSourceId]); + const { + displaySamples, + currentValue, + historicalMin, + historicalMax, + isPaused, + setPaused, + } = useSensorStream(source); + const { minValue, maxValue } = resolveYAxisBounds(historicalMin, historicalMax); - // Sync display with source only when not paused - useEffect(() => { - if (!isPaused) { - setDisplayData(sourceData); - } - }, [sourceData, isPaused]); - - // Simple SVG rendering const renderGraph = () => { - if (displayData.length < 2) return null; - const width = graphRef.current?.clientWidth || 300; const height = 280; - const padding = 20; + const paddingLeft = 36; + const paddingRight = 88; + const paddingY = 20; const now = Date.now(); - const minTime = now - 60000; + const minTime = now - 60_000; const maxTime = now; - const values = displayData.map(d => d.value); - let minValue = Math.min(...values) - 2; - let maxValue = Math.max(...values) + 2; - - if (minValue === maxValue) { - minValue -= 10; - maxValue += 10; - } - - const mapX = (time: number) => padding + ((time - minTime) / (maxTime - minTime)) * (width - padding * 2); - const mapY = (val: number) => height - padding - ((val - minValue) / (maxValue - minValue)) * (height - padding * 2); - - const pathData = displayData.map((d, i) => { - const x = mapX(d.time); - const y = mapY(d.value); - return `${i === 0 ? 'M' : 'L'} ${x} ${y}`; - }).join(' '); + const mapX = (time: number) => + paddingLeft + + ((time - minTime) / Math.max(maxTime - minTime, 1)) * + (width - paddingLeft - paddingRight); + const mapY = (val: number) => + height - + paddingY - + ((val - minValue) / Math.max(maxValue - minValue, 1)) * + (height - paddingY * 2); const midValue = (minValue + maxValue) / 2; + const latest = displaySamples[displaySamples.length - 1]; + + const pathData = + displaySamples.length >= 2 + ? displaySamples + .map((sample, index) => { + const x = mapX(sample.time); + const y = mapY(sample.value); + return `${index === 0 ? 'M' : 'L'} ${x} ${y}`; + }) + .join(' ') + : ''; return ( - {maxValue.toFixed(1)} - {minValue.toFixed(1)} - - {midValue.toFixed(1)} - - {displayData.length > 0 && ( - + + {maxValue.toFixed(1)} + + + {minValue.toFixed(1)} + + + + {midValue.toFixed(1)} + + + {pathData ? ( + + ) : null} + + {latest ? ( + <> + + + {latest.value.toFixed(0)} + + + ) : currentValue !== null ? ( + + {currentValue.toFixed(0)} + + ) : ( + + Waiting for sensor data... + )} ); }; return ( -
-
- {sourceName} - {displayData.length > 0 && ( - - CURRENT: {displayData[displayData.length - 1].value.toFixed(2)} - - )} -
- {isPaused ? '[PAUSED]' : '[ACTIVE]'} - +
+
+
+ + {source.name} + +
+ {source.topic} +
+
+
+ + {isPaused ? '[PAUSED]' : '[LIVE]'} + + +
+
+
+ {renderGraph()}
-
- {renderGraph()} -
-
); - }; +}; diff --git a/src/Components/SettingsModal.tsx b/src/Components/SettingsModal.tsx index a9beab3..7b9fec6 100644 --- a/src/Components/SettingsModal.tsx +++ b/src/Components/SettingsModal.tsx @@ -1,19 +1,22 @@ import React, { useEffect, useState } from 'react'; -import { Modal, Input, Button, Form, Typography, Space } from 'antd'; +import { Input, Button, Form, Typography, Space } from 'antd'; import { InfoCircleOutlined, SettingOutlined } from '@ant-design/icons'; import { useRosConnection } from '../hooks/useRosConnection.hook'; +import { getOriginRosUrl } from '../Services/ros/ros.service'; import { useActiveHardwareRos } from '../contexts/ActiveHardwareRosContext'; import { + UI_ACCENT_BLUE, UI_ACCENT_GREEN, UI_BORDER_SOFT, UI_COLOR_TRANSPARENT, - UI_MODAL_MASK_BG, UI_TEXT_ON_ACCENT, UI_TEXT_PRIMARY_ON_DARK, } from '../Constants/uiTheme'; import { ToggleSwitch } from './ToggleSwitch'; +import { GETTING_STARTED_COMPLETED_KEY, REDO_GETTING_STARTED_EVENT } from './GettingStartedModal'; +import { MovableModal } from './MovableModal'; -const { Text, Title } = Typography; +const { Text } = Typography; interface SettingsModalProps { visible: boolean; @@ -28,6 +31,9 @@ export const SHOW_DEGREES_KEY = 'showDegreesEnabled'; export const isShowDegreesEnabled = () => localStorage.getItem(SHOW_DEGREES_KEY) !== 'false'; +/** Fixed hint for the URL field: the bridge served alongside this page. */ +const originRosUrl = getOriginRosUrl(); + export const SettingsModal: React.FC = ({ visible, onClose }) => { const { connect, disconnect, isConnected, currentUrl, connectionStatus } = useRosConnection(); const { activeHardwareConfigName, controllerConfigsFromActive } = useActiveHardwareRos(); @@ -54,7 +60,7 @@ export const SettingsModal: React.FC = ({ visible, onClose } try { await connect(rosUrl); onClose(); - } catch (error) { + } catch { // Error is already logged in the hook } }; @@ -74,15 +80,17 @@ export const SettingsModal: React.FC = ({ visible, onClose } : 0; return ( - - Settings - - } - open={visible} - onCancel={onClose} - footer={[ + } + footer={ + <> , + , + , - ]} - styles={{ mask: { backgroundColor: UI_MODAL_MASK_BG } }} - className="dark-modal" + + + } >
- ROS Bridge URL}> + ROS Bridge URL} + tooltip={{ + title: 'WebSocket address of the rosbridge server (e.g. ws://host:port/rosbridge). Saved locally and reused on next launch.', + icon: , + }} + > setRosUrl(e.target.value)} - placeholder="ws://localhost:9090" + placeholder={originRosUrl} /> = ({ visible, onClose } Active Configuration: {activeHardwareConfigName || 'N/A'} + + + -
+ ); }; diff --git a/src/Components/StreamPlayerModal.tsx b/src/Components/StreamPlayerModal.tsx index c202f22..d7b55f3 100644 --- a/src/Components/StreamPlayerModal.tsx +++ b/src/Components/StreamPlayerModal.tsx @@ -52,7 +52,6 @@ interface StreamPlayerModalProps { onClose: () => void; initialPosition?: { x: number; y: number }; initialSize?: { w: number; h: number }; - aspectRatio?: number; } export function StreamPlayerModal({ @@ -60,7 +59,6 @@ export function StreamPlayerModal({ onClose, initialPosition = { x: 100, y: 100 }, initialSize = { w: 480, h: 320 }, - aspectRatio = 4.5 / 3, }: StreamPlayerModalProps) { const [frameDelay, setFrameDelay] = useState(0); const [fps, setFps] = useState(0); @@ -181,7 +179,7 @@ export function StreamPlayerModal({ onClose={onClose} initialPosition={initialPosition} initialSize={initialSize} - aspectRatio={aspectRatio} + contentPadding={0} mobileFixedTop mobileTopOffset={headerHeight} > diff --git a/src/Components/StreamSwitch.tsx b/src/Components/StreamSwitch.tsx index 22ef8bd..9881f0b 100644 --- a/src/Components/StreamSwitch.tsx +++ b/src/Components/StreamSwitch.tsx @@ -12,7 +12,7 @@ interface StreamSwitchProps { export const StreamSwitch: React.FC = ({ labelA = 'OFF', labelB = 'ON', value, onChange }) => (
+
+ +
+ ); + + if (titlePlacement === 'inline') { + return ( +
+ {title} + {toggle} +
+ ); + } + return (
- - {title} - -
- -
- -
+ {title} + {toggle}
); }; \ No newline at end of file diff --git a/src/Constants/rosConfig.ts b/src/Constants/rosConfig.ts index 687ab67..6bba0c7 100644 --- a/src/Constants/rosConfig.ts +++ b/src/Constants/rosConfig.ts @@ -62,3 +62,18 @@ export const STREAM_SOURCE_3D_VIEW: StreamSource = { /** Initial placeholder until a camera from the active config is selected. */ export const DEFAULT_STREAM_SOURCE = STREAM_SOURCE_3D_VIEW; + +/** Per-sensor stream entry derived from hardware YAML (pressure sensors on `sensors/` topics). */ +export interface SensorSource { + id: string; + name: string; + /** ROS topic for the board's Float32Array payload (e.g. `/sensors/left_arm`). */ + topic: string; + messageType: string; + /** Index into the Float32Array for this sensor (board sensors sorted by `virtual_pin`). */ + arrayIndex: number; + type: string; + boardId: string; +} + +export const SENSOR_FLOAT32_ARRAY_MESSAGE_TYPE = 'ros_gz_interfaces/msg/Float32Array' as const; diff --git a/src/Constants/routes.ts b/src/Constants/routes.ts new file mode 100644 index 0000000..3c6d8a9 --- /dev/null +++ b/src/Constants/routes.ts @@ -0,0 +1,5 @@ +export const ROUTES = { + control: '/', + sensors: '/sensors', + robotConfiguration: '/robot-configuration', +} as const; diff --git a/src/Constants/uiTheme.ts b/src/Constants/uiTheme.ts index c1bf033..5ae4be6 100644 --- a/src/Constants/uiTheme.ts +++ b/src/Constants/uiTheme.ts @@ -26,7 +26,7 @@ export const UI_BORDER_STRONG = '#333333'; export const UI_BORDER_SOFT = '#444444'; export const UI_BORDER_DIM = '#222222'; export const UI_TEXT_ON_ACCENT = '#000000'; -export const UI_TEXT_PRIMARY_ON_DARK = '#ffffff'; +export const UI_TEXT_PRIMARY_ON_DARK = '#F7F1E5'; export const UI_TEXT_SECONDARY_MUTED = '#666666'; /** Muted labels / secondary lines (Ant `colorTextSecondary` parity). */ export const UI_TEXT_SUBTLE = '#888888'; @@ -73,6 +73,12 @@ export const UI_ACCENT_BOX_SHADOW_SOFT = `0 0 8px ${UI_ACCENT_GREEN}`; export const UI_ACCENT_BOX_SHADOW_STRONG = `0 0 10px ${UI_ACCENT_GREEN}`; export const UI_PAGE_HEADER_BORDER_BOTTOM = `2px solid ${UI_BORDER_MUTED}`; +/** Shared shell padding for main app pages (control, sensors, robot configuration). */ +export const PAGE_CONTENT_STYLE = { + padding: 12, + position: 'relative', +} as const; + export const UI_CARD_SURFACE_STYLE = { background: UI_PANEL_BG, borderColor: UI_BORDER_MUTED, diff --git a/src/Pages/NotFound.tsx b/src/Pages/NotFound.tsx index 5b402cc..e7208cc 100644 --- a/src/Pages/NotFound.tsx +++ b/src/Pages/NotFound.tsx @@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react'; import { Typography, Button, Space } from 'antd'; import { HomeOutlined, ReloadOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; -import { Page } from '../Components/Page'; import { UI_ACCENT_GREEN, UI_BORDER_MUTED, @@ -98,13 +97,13 @@ export const NotFound: React.FC = () => { }; return ( - {/* Header */} @@ -293,6 +292,6 @@ export const NotFound: React.FC = () => {
- +
); }; diff --git a/src/Pages/RobotControlPanel.tsx b/src/Pages/RobotControlPanel.tsx index bf8fd60..bee500b 100644 --- a/src/Pages/RobotControlPanel.tsx +++ b/src/Pages/RobotControlPanel.tsx @@ -3,17 +3,17 @@ import React, { useRef, useEffect, useCallback, + useContext, useMemo, lazy, Suspense, } from 'react'; -import { Typography, Space, Button, Row, Col, Alert, Spin, Tooltip, Modal, message, Dropdown, Grid } from 'antd'; +import { Typography, Space, Button, Row, Col, Alert, Spin, Tooltip, message, Dropdown, Grid } from 'antd'; import type { MenuProps } from 'antd'; import { ReloadOutlined, ThunderboltOutlined, StopOutlined, - InfoCircleOutlined, MenuOutlined, VideoCameraOutlined, EyeOutlined, @@ -52,13 +52,13 @@ import { jointRadToActuatorDeg, type ActuatorMapping, } from '../Utils/actuatorJointMapping'; +import { describeClient } from '../Utils/clientIdentity'; import { DEFAULT_JOINT_SLIDER_BOUNDS_DEG, DEFAULT_JOINT_SLIDER_VALUE_DEG, } from '../Constants/hardwareConfigDefaults'; /* Components */ -import { Page } from '../Components/Page'; import { LucyLoader } from '../Components/LucyLoader'; import { JointCategory } from '../Components/JointCategory'; import { DraggableCategory } from '../Components/DraggableCategory'; @@ -75,7 +75,6 @@ import { UI_BORDER_SOFT, UI_COLOR_TRANSPARENT, UI_ERROR, - UI_MODAL_MASK_BG, UI_TEXT_ON_ACCENT, UI_TEXT_PRIMARY_ON_DARK, UI_TEXT_SUBTLE, @@ -86,16 +85,82 @@ import { UI_GRADIENT_MODAL_HEADER, UI_MODAL_SURFACE, UI_SHADOW_ELEVATED, + UI_BG_BLACK, + PAGE_CONTENT_STYLE, } from '../Constants/uiTheme.ts'; +import { HeaderHeightContext } from '../contexts/HeaderHeightContext.ts'; const MediapipeHandTracker = lazy(() => import('../Components/MediapipeHandTracker').then(module => ({ default: module.default }))); -const { Text, Title } = Typography; +const { Text } = Typography; const { useBreakpoint } = Grid; const REFRESH_RATE = 300; const BASE_ANIMATION_INTERVAL = 1000; // ms per keyframe at 1x speed +interface ControlTakenModalProps { + isVisible: boolean; + fighting: boolean; + onClose: () => void; + onRetake: () => void; +} + +const ControlTakenModal: React.FC = ({ + isVisible, + fighting, + onClose, + onRetake, +}) => ( + } + footer={ + <> + + + + } + > + + + {fighting + ? 'You and another client keep taking control from each other. Maybe… talk it out?' + : <>Another connected client turned Control Robot ON and now has exclusive control. + } + + + {fighting + ? 'The robot is confused. You should be too.' + : <>Your Control Robot was automatically turned OFF. Use Retake Control to reclaim it. + } + + + +); + export const RobotControlPanel: React.FC = () => { const { isConnected, isConnecting } = useRosConnection(); @@ -122,10 +187,15 @@ export const RobotControlPanel: React.FC = () => { const animationTimeoutRef = useRef | null>(null); const [showControlTakenModal, setShowControlTakenModal] = useState(false); const retakeCountRef = useRef(0); + const [controllerToPreempt, setControllerToPreempt] = useState(''); + const [showConfirmTakeControlModal, setShowConfirmTakeControlModal] = useState(false); const screens = useBreakpoint(); const isMobile = !screens.md; + // The page header is `position: sticky; top: 0`, so the control toolbar has to park below it. + const headerHeight = useContext(HeaderHeightContext); + // Floating stream window state const STREAM_VISIBLE_KEY = 'lucy_stream_visible'; @@ -355,16 +425,30 @@ export const RobotControlPanel: React.FC = () => { return () => clearInterval(interval); }, [isSending]); - const handleControlRobotToggle = useCallback((next: boolean) => { - setIsSending(next); + /** Function responsible of flipping control without asking. Callers own the decision to preempt another client. */ + const applyControlToggle = useCallback((shouldControl: boolean) => { + setIsSending(shouldControl); setShowControlTakenModal(false); - if (next) { + setShowConfirmTakeControlModal(false); + if (shouldControl) { ControlModeHandler.getInstance().takeControl(); } else { ControlModeHandler.getInstance().releaseControl(); } }, []); + const handleControlRobotToggle = useCallback((shouldControl: boolean) => { + const handler = ControlModeHandler.getInstance(); + const otherHasControl = + handler.currentControllerId !== '' && handler.currentControllerId !== handler.clientId; + if (shouldControl && otherHasControl) { + setControllerToPreempt(handler.currentControllerId); + setShowConfirmTakeControlModal(true); + return; + } + applyControlToggle(shouldControl); + }, [applyControlToggle]); + const handleJointValueChange = useCallback((name: string, value: number) => { setJoints((prevJoints) => prevJoints.map((joint) => @@ -388,6 +472,19 @@ export const RobotControlPanel: React.FC = () => { ); } + const handleResetJoint = useCallback((name: string) => { + setJoints((prevJoints) => + prevJoints.map((joint) => { + if (joint.name === name) { + const rest = joint.restValue ?? 0; + const clamped = Math.max(joint.minValue, Math.min(joint.maxValue, rest)); + return { ...joint, currentValue: clamped, targetValue: clamped }; + } + return joint; + }) + ); + }, []); + const handleResetCategory = useCallback((category: string) => { setJoints((prevJoints) => prevJoints.map((joint) => { @@ -510,18 +607,33 @@ export const RobotControlPanel: React.FC = () => { if (isConnected && loading) { return ( - - - - Loading robot configuration... - - + <> +
+ + + Loading robot configuration... + +
+ = 3} + onClose={() => { + retakeCountRef.current = 0; + setShowControlTakenModal(false); + }} + onRetake={() => { + retakeCountRef.current += 1; + setShowControlTakenModal(false); + applyControlToggle(true); + }} + /> + ); } if (isConnected && error) { return ( - + <> { } /> - + = 3} + onClose={() => { + retakeCountRef.current = 0; + setShowControlTakenModal(false); + }} + onRetake={() => { + retakeCountRef.current += 1; + setShowControlTakenModal(false); + applyControlToggle(true); + }} + /> + ); } @@ -543,6 +668,7 @@ export const RobotControlPanel: React.FC = () => { label: 'RESET ALL', icon: , onClick: handleResetAll, + disabled: !isSending, style: { color: UI_TEXT_PRIMARY_ON_DARK } }, { @@ -577,33 +703,23 @@ export const RobotControlPanel: React.FC = () => { borderRadius: 4, }; - const switches = () => { - return( - <> -
- } - width={180} - height={32} - /> - - - -
- - ) - } + const switches = () => ( + + + } + width={180} + /> + + + ); return ( - + <> setIsStreamVisible(false)} @@ -623,38 +739,63 @@ export const RobotControlPanel: React.FC = () => { /> ) : (
- - - {isMobile ? ( - - ( -
{menu}
- )}> +
+ {isMobile ? ( + + + + ( +
{menu}
+ )}> + +
- - -
- ) : ( + + + + {switches()} + +
+ ) : ( +
@@ -705,24 +847,10 @@ export const RobotControlPanel: React.FC = () => { {isWebcamActive ? 'HIDE HAND TRACKER' : 'SHOW HAND TRACKER'} - )} - { isMobile ? null : ( - - - {switches()} - - - )} - - - { isMobile ? ( - - - {switches()} - - - ) : null } - + {switches()} +
+ )} +
{/* Mobile webcam sits inline under Control Robot and scrolls with the joint boxes. */} {isMobile && isWebcamActive && ( @@ -793,6 +921,7 @@ export const RobotControlPanel: React.FC = () => { joints={categorizedJoints[category]} onJointValueChange={handleJointValueChange} onResetCategory={handleResetCategory} + onResetJoint={handleResetJoint} showDegrees={showDegrees} disabled={!isSending} /> @@ -836,72 +965,69 @@ export const RobotControlPanel: React.FC = () => { )} - {/* Another client took control */} - {(() => { - const fighting = retakeCountRef.current >= 3; - return ( - - {fighting ? 'Stop fighting!' : 'Someone else took control'} - - } - open={showControlTakenModal} - onCancel={() => { retakeCountRef.current = 0; setShowControlTakenModal(false); }} - footer={[ - , - , - ]} - style={{ top: 200 }} - styles={{ mask: { backgroundColor: UI_MODAL_MASK_BG } }} - className="dark-modal" - > - - - {fighting - ? 'You and another client keep taking control from each other. Maybe… talk it out?' - : <>Another connected client turned Control Robot ON and now has exclusive control. - } - - - {fighting - ? 'The robot is confused. You should be too.' - : <>Your Control Robot was automatically turned OFF. Use Retake Control to reclaim it. - } - - - - ); - })()} + = 3} + onClose={() => { + retakeCountRef.current = 0; + setShowControlTakenModal(false); + }} + onRetake={() => { + retakeCountRef.current += 1; + setShowControlTakenModal(false); + applyControlToggle(true); + }} + /> - + {/* We are about to take control away from another client */} + setShowConfirmTakeControlModal(false)} + initialPosition={{ x: 100, y: 200 }} + initialSize={{ w: 500, h: 300 }} + header={ + + TAKE CONTROL FROM ANOTHER CLIENT? + + } + footer={[ + , + , + ]} + > + + + The robot is currently controlled by {describeClient(controllerToPreempt)}. + + + Control is exclusive: their Control Robot switches to OFF immediately, + and they are told you took over. + + + + + ); }; diff --git a/src/Pages/SensorDisplay.tsx b/src/Pages/SensorDisplay.tsx index 015f457..d3542a5 100644 --- a/src/Pages/SensorDisplay.tsx +++ b/src/Pages/SensorDisplay.tsx @@ -1,61 +1,107 @@ -import React, { useState } from 'react'; -import { Select, Row, Col } from 'antd'; -import { Page } from '../Components/Page'; -import { Terminal, FloatGraph } from '../Components/Sensors'; +import React, { useMemo, useState } from 'react'; +import { Alert, Col, Row, Select } from 'antd'; +import { FloatGraph } from '../Components/Sensors'; +import { useActiveHardwareRos } from '../contexts/ActiveHardwareRosContext'; +import { useRosConnection } from '../hooks/useRosConnection.hook'; +import { useSensorSources } from '../hooks/useSensorSources'; const { Option } = Select; -const mockDataSources = [ - { id: '1', name: 'Sensor Alpha', type: 'text' }, - { id: '2', name: 'Sensor Beta', type: 'text' }, - { id: '3', name: 'Sensor Gamma', type: 'text' }, - { id: '4', name: 'Sensor Delta', type: 'text' }, - { id: '5', name: 'Sensor Epsilon', type: 'text' }, - { id: '6', name: 'Temperature Sensor', type: 'float' }, -]; - const SensorDisplay: React.FC = () => { - const [selectedSources, setSelectedSources] = useState([]); + const [selectedSensorIds, setSelectedSensorIds] = useState([]); + const sensorSources = useSensorSources(); + const { isConnected } = useRosConnection(); + const { activeHardwareDoc, activeHardwareLoading, activeHardwareError } = useActiveHardwareRos(); - const handleSelectionChange = (selected: string[]) => { - setSelectedSources(selected); - }; + const selectedSources = useMemo( + () => + selectedSensorIds + .map((id) => sensorSources.find((source) => source.id === id)) + .filter((source): source is NonNullable => Boolean(source)), + [selectedSensorIds, sensorSources], + ); return ( - -
-
-
- SELECT DATA SOURCES TO MONITOR: -
- -
- - - {selectedSources.map(sourceId => { - const source = mockDataSources.find(s => s.id === sourceId); - return ( - 1 ? 12 : 24}> - {source?.type === 'float' ? ( - - ) : ( - - )} - - ); - })} - + <> + {!isConnected ? ( + + ) : null} + + {activeHardwareLoading ? ( + + ) : null} + + {activeHardwareError ? ( + + ) : null} + + {!activeHardwareLoading && isConnected && activeHardwareDoc && sensorSources.length === 0 ? ( + + ) : null} + +
+ + SELECT PRESSURE SENSORS TO MONITOR: + +
- + + + {selectedSources.map((source) => ( + + + + ))} + + ); }; diff --git a/src/Services/diagnostics.service.ts b/src/Services/diagnostics.service.ts new file mode 100644 index 0000000..d799513 --- /dev/null +++ b/src/Services/diagnostics.service.ts @@ -0,0 +1,111 @@ +/** + * Passive recorder for the two debug pipelines. + * + * Services and handlers report what they observe; nothing here changes their + * behaviour, so instrumentation cannot break the paths it is meant to explain. + * + * connection: Connect button -> rosbridge -> ROS graph -> robot configuration. + * command: slider -> JointTrajectory -> /joint_states -> rendered pose. + */ + +export type StageStatus = 'pending' | 'ok' | 'warn' | 'error'; +export type PipelineId = 'connection' | 'command'; + +export interface Stage { + id: string; + label: string; + /** What this step does, shown under the label. */ + hint: string; + status: StageStatus; + /** Latest observation: a URL, a topic, a count, an error. */ + detail?: string; + /** epoch ms of the last update */ + at?: number; + /** messages seen, for streaming stages */ + count?: number; +} + +const CONNECTION_STAGES: Array> = [ + { id: 'url', label: 'Endpoint resolved', hint: 'Which rosbridge URL the panel will dial' }, + { id: 'socket', label: 'WebSocket open', hint: 'TCP + upgrade to the rosbridge server' }, + { id: 'rosapi', label: 'Attached to a live robot', hint: 'Which nodes publish /joint_states, via rosapi' }, + { id: 'description', label: 'Robot description', hint: '/robot_description from robot_state_publisher (the URDF)' }, + { id: 'hardware', label: 'Robot configuration', hint: 'Hardware YAML: joints, limits, calibration' }, + { id: 'jointstates', label: 'Joint states streaming', hint: '/joint_states — drives the blue dots' }, +]; + +const COMMAND_STAGES: Array> = [ + { id: 'slider', label: 'Slider moved', hint: 'Panel control changed, in actuator degrees' }, + { id: 'converted', label: 'Converted to radians', hint: 'Actuator degrees mapped to URDF radians' }, + { id: 'published', label: 'JointTrajectory published', hint: 'Sent to the controller command topic' }, + { id: 'controller', label: 'Executed by a controller', hint: 'A ros2_control controller_manager is driving the joints' }, + { id: 'echoed', label: 'Echoed on /joint_states', hint: 'Something reported the joint actually moved' }, + { id: 'rendered', label: 'Pose rendered', hint: 'Forward kinematics ran and the model redrew' }, +]; + +function seed(defs: Array>): Map { + return new Map(defs.map((d) => [d.id, { ...d, status: 'pending' as StageStatus }])); +} + +class DiagnosticsService { + private static _instance: DiagnosticsService | null = null; + + private stages: Record> = { + connection: seed(CONNECTION_STAGES), + command: seed(COMMAND_STAGES), + }; + private listeners = new Set<() => void>(); + + static getInstance(): DiagnosticsService { + if (!DiagnosticsService._instance) { + DiagnosticsService._instance = new DiagnosticsService(); + } + return DiagnosticsService._instance; + } + + subscribe(listener: () => void): () => void { + this.listeners.add(listener); + return () => this.listeners.delete(listener); + } + + private emit() { + this.listeners.forEach((l) => l()); + } + + /** Update one stage. `count` accumulates when passed as true. */ + record( + pipeline: PipelineId, + id: string, + status: StageStatus, + detail?: string, + countUp = false, + ): void { + const stage = this.stages[pipeline].get(id); + if (!stage) return; + stage.status = status; + stage.at = Date.now(); + if (detail !== undefined) stage.detail = detail; + if (countUp) stage.count = (stage.count ?? 0) + 1; + this.emit(); + } + + /** Drop everything downstream of a lost connection. */ + resetConnection(): void { + for (const id of ['socket', 'rosapi', 'description', 'hardware', 'jointstates']) { + const stage = this.stages.connection.get(id); + if (stage) { + stage.status = 'pending'; + stage.detail = undefined; + stage.count = undefined; + } + } + this.emit(); + } + + getStages(pipeline: PipelineId): Stage[] { + return Array.from(this.stages[pipeline].values()); + } + +} + +export const Diagnostics = DiagnosticsService.getInstance(); diff --git a/src/Services/ros/handlers/ConnectedClients.handler.ts b/src/Services/ros/handlers/ConnectedClients.handler.ts index 0546ab9..89c5b19 100644 --- a/src/Services/ros/handlers/ConnectedClients.handler.ts +++ b/src/Services/ros/handlers/ConnectedClients.handler.ts @@ -51,12 +51,20 @@ export class ConnectedClientsHandler { serviceType: 'lucy_msgs/srv/GetInt', }); const request = new ROSLIB.ServiceRequest({}); - this.connectedClientsService.callService(request, (result: ROSLIB.ServiceResponse) => { - const value = (result as unknown as { value?: number }).value; - if (value !== undefined && value !== null) { - callback(value); - } - }); + this.connectedClientsService.callService( + request, + (result: ROSLIB.ServiceResponse) => { + const value = (result as unknown as { value?: number }).value; + if (value !== undefined && value !== null) { + callback(value); + } + }, + // Without this callback roslib drops rosbridge's error, so a client + // registry that never advertised looks identical to one still starting. + (error: string) => { + console.warn(`Cannot read client count: ${error}`); + }, + ); } static cleanup(): void { diff --git a/src/Services/ros/handlers/HardwareConfig.handler.ts b/src/Services/ros/handlers/HardwareConfig.handler.ts index 28585f0..b0f96a4 100644 --- a/src/Services/ros/handlers/HardwareConfig.handler.ts +++ b/src/Services/ros/handlers/HardwareConfig.handler.ts @@ -43,10 +43,19 @@ function callService, TRes>( const timer = window.setTimeout(() => { reject(new Error(`Service ${name} timed out after ${SERVICE_TIMEOUT_MS}ms`)); }, SERVICE_TIMEOUT_MS); - svc.callService(req, (result: TRes) => { - window.clearTimeout(timer); - resolve(result); - }); + svc.callService( + req, + (result: TRes) => { + window.clearTimeout(timer); + resolve(result); + }, + // Without this callback roslib drops rosbridge's failure message, + // so the promise hangs until the timeout above misreports it. + (error: string) => { + window.clearTimeout(timer); + reject(new Error(error || `Service ${name} failed.`)); + }, + ); }); } diff --git a/src/Services/ros/handlers/JointState.handler.ts b/src/Services/ros/handlers/JointState.handler.ts index 428a412..23056b3 100644 --- a/src/Services/ros/handlers/JointState.handler.ts +++ b/src/Services/ros/handlers/JointState.handler.ts @@ -7,6 +7,7 @@ import { } from '../../../Utils/jointConfigLookup.ts'; import { actuatorDegToJointRad } from '../../../Utils/actuatorJointMapping.ts'; import { RosBridgeService } from '../ros.service.ts'; +import { Diagnostics } from '../../diagnostics.service.ts'; /** ROS time (sec + nsec) for trajectory header; from /clock when use_sim_time, else wall clock. */ function getRosTimeNow(rosTime: { sec: number; nanosec: number } | null): { sec: number; nanosec: number } { @@ -142,6 +143,8 @@ export class JointStateHandler { const js = msg as unknown as { name: string[]; position: number[] }; if (!js.name?.length || !js.position?.length) return; const positions = js.name.map((name, i) => ({ name, value: js.position[i] })); + Diagnostics.record('connection', 'jointstates', 'ok', `${positions.length} joints`, true); + Diagnostics.record('command', 'echoed', 'ok', `${positions.length} joints reported`, true); callback(positions); }); return () => sub.unsubscribe(); @@ -217,6 +220,8 @@ export class JointStateHandler { if (names.length === 0) continue; const topic = this.topicByTopicName.get(cfg.topic); if (!topic) continue; + Diagnostics.record('command', 'slider', 'ok', `${names.length} joint(s) on ${cfg.defaultCategory}`); + Diagnostics.record('command', 'converted', 'ok', 'actuator deg -> URDF rad'); const stamp = getRosTimeNow(this.lastClock); const message = new ROSLIB.Message({ header: { stamp, frame_id: '' }, @@ -224,6 +229,7 @@ export class JointStateHandler { points: [{ positions, time_from_start: { sec: 0, nanosec: 0.8e9 } }], }); topic.publish(message); + Diagnostics.record('command', 'published', 'ok', `${names.length} joint(s) -> ${cfg.topic}`, true); } } } diff --git a/src/Services/ros/handlers/RobotDescription.handler.ts b/src/Services/ros/handlers/RobotDescription.handler.ts index b37100b..64fb462 100644 --- a/src/Services/ros/handlers/RobotDescription.handler.ts +++ b/src/Services/ros/handlers/RobotDescription.handler.ts @@ -1,13 +1,26 @@ import ROSLIB from 'roslib'; import { RosBridgeService } from '../ros.service.ts'; +import { Diagnostics } from '../../diagnostics.service.ts'; /** - * Subscribes to the latched `/robot_description` topic published by - * `robot_state_publisher` (`std_msgs/String`, transient_local QoS) and exposes - * the full URDF XML as a string, or `null` when nothing has been received yet - * (disconnected, or robot_state_publisher not up). + * Exposes the robot's URDF XML, or `null` when nothing has been received yet. + * + * Read from robot_state_publisher's `robot_description` parameter rather than + * the latched topic: rosbridge fixes a subscription's durability when it is + * created and its subscribe op takes no QoS field, so subscribing before + * robot_state_publisher exists yields a VOLATILE subscription that can never + * receive the latched message. The topic stays subscribed for republishes. */ const ROBOT_DESCRIPTION_TOPIC = '/robot_description'; +const PARAM_SERVICE = '/robot_state_publisher/get_parameters'; +const PARAM_SERVICE_TYPE = 'rcl_interfaces/srv/GetParameters'; +const PARAM_NAME = 'robot_description'; +/** rcl_interfaces/msg/ParameterType.PARAMETER_STRING */ +const PARAMETER_STRING = 4; + +/** First retry delay; doubles up to FETCH_MAX_DELAY_MS. */ +const FETCH_DELAY_MS = 2000; +const FETCH_MAX_DELAY_MS = 15000; type Listener = (urdf: string | null) => void; @@ -18,12 +31,17 @@ export class RobotDescriptionHandler { private unsubscribeStatus: (() => void) | null = null; private listeners: Set = new Set(); private _urdf: string | null = null; + private retryTimer: ReturnType | null = null; + private retryDelay: number = FETCH_DELAY_MS; private constructor() { this.unsubscribeStatus = RosBridgeService.getInstance().onStatusChange((status) => { if (status === 'connected') { this.initTopic(); + void this.fetchUrdf(); } else if (status === 'disconnected') { + this.cancelRetry(); + this.retryDelay = FETCH_DELAY_MS; this.topic?.unsubscribe(); this.topic = null; this.setUrdf(null); @@ -31,6 +49,7 @@ export class RobotDescriptionHandler { }); if (RosBridgeService.getInstance().isConnected) { this.initTopic(); + void this.fetchUrdf(); } } @@ -54,12 +73,72 @@ export class RobotDescriptionHandler { }; } + /** Fetch now rather than waiting out the backoff (the Retry button). */ + resubscribe() { + this.cancelRetry(); + this.retryDelay = FETCH_DELAY_MS; + this.initTopic(); + void this.fetchUrdf(); + } + private setUrdf(v: string | null) { + if (v !== null) this.cancelRetry(); if (v === this._urdf) return; this._urdf = v; + Diagnostics.record( + 'connection', 'description', + v ? 'ok' : 'pending', + v ? `${v.length} bytes of URDF` : 'not received', + ); this.listeners.forEach((cb) => cb(v)); } + private cancelRetry() { + if (this.retryTimer !== null) { + clearTimeout(this.retryTimer); + this.retryTimer = null; + } + } + + private scheduleRetry() { + this.cancelRetry(); + const delay = this.retryDelay; + this.retryDelay = Math.min(this.retryDelay * 2, FETCH_MAX_DELAY_MS); + this.retryTimer = setTimeout(() => { + this.retryTimer = null; + if (this._urdf === null) void this.fetchUrdf(); + }, delay); + } + + /** Read robot_state_publisher's `robot_description` parameter. */ + private fetchUrdf(): Promise { + const ros = RosBridgeService.getInstance().rosConnection; + if (!ros) return Promise.resolve(); + return new Promise((resolve) => { + const svc = new ROSLIB.Service({ + ros, + name: PARAM_SERVICE, + serviceType: PARAM_SERVICE_TYPE, + }); + const req = new ROSLIB.ServiceRequest({ names: [PARAM_NAME] }); + svc.callService( + req, + (result: { values?: Array<{ type?: number; string_value?: string }> }) => { + // `values` here is GetParameters.Response.values. + const value = result?.values?.[0]; + const xml = value?.type === PARAMETER_STRING ? value.string_value : ''; + if (xml) this.setUrdf(xml); + else this.scheduleRetry(); + resolve(); + }, + () => { + this.scheduleRetry(); + resolve(); + }, + ); + }); + } + private initTopic() { const ros = RosBridgeService.getInstance().rosConnection; if (!ros) return; @@ -71,12 +150,13 @@ export class RobotDescriptionHandler { }); this.topic.subscribe((msg: ROSLIB.Message) => { const data = (msg as unknown as { data?: string }).data; - this.setUrdf(typeof data === 'string' && data.length > 0 ? data : null); + if (typeof data === 'string' && data.length > 0) this.setUrdf(data); }); } static cleanup() { if (RobotDescriptionHandler._instance) { + RobotDescriptionHandler._instance.cancelRetry(); RobotDescriptionHandler._instance.topic?.unsubscribe(); RobotDescriptionHandler._instance.unsubscribeStatus?.(); RobotDescriptionHandler._instance = null; diff --git a/src/Services/ros/handlers/Sensor.handler.ts b/src/Services/ros/handlers/Sensor.handler.ts new file mode 100644 index 0000000..9290038 --- /dev/null +++ b/src/Services/ros/handlers/Sensor.handler.ts @@ -0,0 +1,126 @@ +import ROSLIB from 'roslib'; +import { SENSOR_FLOAT32_ARRAY_MESSAGE_TYPE } from '../../../Constants/rosConfig'; +import { RosBridgeService } from '../ros.service.ts'; + +export type SensorSampleCallback = (value: number, timestampMs: number) => void; + +interface SensorListener { + sensorId: string; + arrayIndex: number; + callback: SensorSampleCallback; +} + +interface TopicState { + topic: ROSLIB.Topic; + listeners: SensorListener[]; +} + +/** + * Subscribes to per-board `sensors/` Float32Array topics and fans out + * values to individual sensor listeners by array index. + */ +export class SensorHandler { + private static instance: SensorHandler; + private topics = new Map(); + private unsubscribeFromStatus: (() => void) | null = null; + + private constructor() { + this.unsubscribeFromStatus = RosBridgeService.getInstance().onStatusChange((status) => { + if (status === 'disconnected') { + this.teardownAll(); + } + }); + } + + static getInstance(): SensorHandler { + if (!SensorHandler.instance) { + SensorHandler.instance = new SensorHandler(); + } + return SensorHandler.instance; + } + + static cleanup(): void { + if (SensorHandler.instance?.unsubscribeFromStatus) { + SensorHandler.instance.unsubscribeFromStatus(); + } + SensorHandler.instance?.teardownAll(); + } + + private teardownAll(): void { + for (const state of this.topics.values()) { + state.topic.unsubscribe(); + } + this.topics.clear(); + } + + private ensureTopic(topicName: string): TopicState | null { + const existing = this.topics.get(topicName); + if (existing) return existing; + + const ros = RosBridgeService.getInstance().rosConnection; + if (!ros) { + console.warn('[SensorHandler] Cannot subscribe: ROS connection not available'); + return null; + } + + const topic = new ROSLIB.Topic({ + ros, + name: topicName, + messageType: SENSOR_FLOAT32_ARRAY_MESSAGE_TYPE, + queue_length: 1, + throttle_rate: 0, + }); + + const state: TopicState = { topic, listeners: [] }; + topic.subscribe((message: ROSLIB.Message) => { + const data = (message as unknown as { data?: number[] }).data; + if (!Array.isArray(data)) return; + + const timestampMs = Date.now(); + for (const listener of state.listeners) { + if (listener.arrayIndex < 0 || listener.arrayIndex >= data.length) continue; + listener.callback(data[listener.arrayIndex], timestampMs); + } + }); + + this.topics.set(topicName, state); + return state; + } + + subscribe( + sensorId: string, + topicName: string, + arrayIndex: number, + callback: SensorSampleCallback, + ): void { + const state = this.ensureTopic(topicName); + if (!state) return; + + const exists = state.listeners.some( + (listener) => + listener.sensorId === sensorId && listener.callback === callback, + ); + if (exists) return; + + state.listeners.push({ sensorId, arrayIndex, callback }); + } + + unsubscribe( + sensorId: string, + topicName: string, + callback: SensorSampleCallback, + ): void { + const state = this.topics.get(topicName); + if (!state) return; + + state.listeners = state.listeners.filter( + (listener) => + !(listener.sensorId === sensorId && listener.callback === callback), + ); + + if (state.listeners.length === 0) { + state.topic.unsubscribe(); + this.topics.delete(topicName); + } + } +} diff --git a/src/Services/ros/ros.service.ts b/src/Services/ros/ros.service.ts index ef70b18..98fa259 100644 --- a/src/Services/ros/ros.service.ts +++ b/src/Services/ros/ros.service.ts @@ -2,6 +2,7 @@ import ROSLIB from 'roslib'; import { logger } from "../../Utils/logger.utils.ts"; +import { Diagnostics } from "../diagnostics.service.ts"; export type ConnectionStatus = 'disconnected' | 'connecting' | 'connected'; @@ -29,6 +30,13 @@ function describeRosBridgeFailure(error: unknown, url: string, opts?: { timeout? ].join('\n'); } +export function getOriginRosUrl(): string { + if (typeof window === 'undefined') { + throw new Error('Window is undefined'); + } + return `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/rosbridge`; +} + export function getDefaultRosUrl(): string { if (typeof window === 'undefined') { throw new Error('Window is undefined'); @@ -43,9 +51,7 @@ export function getDefaultRosUrl(): string { return stored; } - return ( - `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.hostname}:${window.location.port}/rosbridge` - ); + return getOriginRosUrl(); } class RosBridgeService { @@ -77,18 +83,22 @@ class RosBridgeService { ros.on('connection', () => { logger('Connected to ROS bridge.'); + Diagnostics.record('connection', 'socket', 'ok', `open to ${this.url}`); this.clearConnectionTimeout(); this.setConnectionStatus('connected'); }); ros.on('error', (error: any) => { logger(`Error connecting to ROS bridge: ${error}`); + Diagnostics.record('connection', 'socket', 'error', describeRosBridgeFailure(error, this.url)); this.clearConnectionTimeout(); this.setConnectionStatus('disconnected'); }); ros.on('close', () => { logger('ROS bridge connection closed.'); + Diagnostics.resetConnection(); + Diagnostics.record('connection', 'socket', 'error', 'connection closed'); this.clearConnectionTimeout(); this.setConnectionStatus('disconnected'); }); @@ -183,6 +193,30 @@ class RosBridgeService { }); } + /** Node names currently in the graph, via `rosapi/nodes`. Bounded like getPublishers. */ + async getNodes(timeoutMs = 5000): Promise { + return new Promise((resolve, reject) => { + if (!this.ros || this._connectionStatus !== 'connected') { + reject(new Error('ROS bridge is not connected')); + return; + } + const service = new ROSLIB.Service({ + ros: this.ros, + name: '/rosapi/nodes', + serviceType: 'rosapi/Nodes', + }); + const timer = setTimeout( + () => reject(new Error('rosapi/nodes did not respond — is the rosapi node running?')), + timeoutMs, + ); + service.callService( + new ROSLIB.ServiceRequest({}), + (result: { nodes: string[] }) => { clearTimeout(timer); resolve(result.nodes); }, + (error: string) => { clearTimeout(timer); reject(new Error(error)); }, + ); + }); + } + async connect(url: string): Promise { return new Promise((resolve, reject) => { logger(`Attempting to connect to ROS Bridge at ${url}...`); @@ -197,6 +231,8 @@ class RosBridgeService { this.url = url; localStorage.setItem(ROS_URL_KEY, url); + Diagnostics.record('connection', 'url', 'ok', url); + Diagnostics.record('connection', 'socket', 'pending', 'dialling...'); if (this.ros) { this.ros.close(); diff --git a/src/Utils/clientIdentity.ts b/src/Utils/clientIdentity.ts new file mode 100644 index 0000000..388cdcb --- /dev/null +++ b/src/Utils/clientIdentity.ts @@ -0,0 +1,40 @@ +/** + * Human-readable descriptions for `lucy_client_registry` client IDs. + * + * The registry treats IDs as opaque strings, so everything we can say about a + * peer has to be read back out of the ID itself. Both known clients encode the + * same shape — `__`: + * - web control panel: `cp__<4 base36 chars>` + * - Lucy CLI: `cli__<4 digits>` + */ + +const CLIENT_KINDS: Record = { + cp: 'another control panel', + cli: 'the Lucy CLI', +}; + +/** Milliseconds above which a `cp_` style timestamp is assumed; below it, seconds. */ +const EPOCH_MS_THRESHOLD = 1e11; + +/** Parses the session-start timestamp out of a client ID, or null when absent/implausible. */ +export function clientSessionStart(clientId: string): Date | null { + const stamp = Number(clientId.split('_')[1]); + if (!Number.isFinite(stamp) || stamp <= 0) return null; + const date = new Date(stamp < EPOCH_MS_THRESHOLD ? stamp * 1000 : stamp); + return Number.isNaN(date.getTime()) ? null : date; +} + +/** + * Short phrase naming a client, e.g. `another control panel (since 14:32)`. + * Falls back to the raw ID for clients we don't know the prefix of. + */ +export function describeClient(clientId: string): string { + if (clientId === '') return 'nobody'; + + const kind = CLIENT_KINDS[clientId.split('_')[0]] ?? `an unknown client (${clientId})`; + const startedAt = clientSessionStart(clientId); + if (!startedAt) return kind; + + const time = startedAt.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); + return `${kind} (connected since ${time})`; +} diff --git a/src/Utils/sensorSourcesFromHardwareYaml.ts b/src/Utils/sensorSourcesFromHardwareYaml.ts new file mode 100644 index 0000000..634b496 --- /dev/null +++ b/src/Utils/sensorSourcesFromHardwareYaml.ts @@ -0,0 +1,76 @@ +import { + SENSOR_FLOAT32_ARRAY_MESSAGE_TYPE, + type SensorSource, +} from '../Constants/rosConfig'; + +function rosTopic(topic: string): string { + const trimmed = topic.trim(); + if (!trimmed) return ''; + return trimmed.startsWith('/') ? trimmed : `/${trimmed}`; +} + +/** Human-readable label from a sensor id such as `left_thumb_pressure`. */ +export function formatSensorDisplayName(sensorId: string): string { + return sensorId + .split('_') + .filter(Boolean) + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(' '); +} + +/** + * Pressure sensor entries from hardware YAML `sensors` + `boards[].topic_sensors`. + * Array index matches firmware / mock_sensor ordering: sensors on a board sorted by `virtual_pin`. + */ +export function sensorSourcesFromHardwareYaml(doc: Record): SensorSource[] { + const boards = doc.boards; + const sensors = doc.sensors; + if (!boards || typeof boards !== 'object' || Array.isArray(boards) || !Array.isArray(sensors)) { + return []; + } + + const boardsMap = boards as Record>; + const byBoard = new Map[]>(); + + for (const row of sensors) { + if (!row || typeof row !== 'object' || Array.isArray(row)) continue; + const entry = row as Record; + if (String(entry.type ?? '').trim() !== 'pressure') continue; + + const boardId = String(entry.board ?? '').trim(); + const id = String(entry.id ?? '').trim(); + if (!boardId || !id) continue; + + const bucket = byBoard.get(boardId) ?? []; + bucket.push(entry); + byBoard.set(boardId, bucket); + } + + const out: SensorSource[] = []; + for (const [boardId, entries] of byBoard) { + const board = boardsMap[boardId]; + if (!board) continue; + + const topic = rosTopic(String(board.topic_sensors ?? '')); + if (!topic) continue; + + entries.sort( + (a, b) => Number(a.virtual_pin ?? 0) - Number(b.virtual_pin ?? 0), + ); + + entries.forEach((entry, arrayIndex) => { + const id = String(entry.id).trim(); + out.push({ + id, + name: formatSensorDisplayName(id), + topic, + messageType: SENSOR_FLOAT32_ARRAY_MESSAGE_TYPE, + arrayIndex, + type: 'pressure', + boardId, + }); + }); + } + + return out.sort((a, b) => a.name.localeCompare(b.name)); +} diff --git a/src/contexts/ActiveHardwareRosContext.tsx b/src/contexts/ActiveHardwareRosContext.tsx index 9fc95b9..eb6b4c7 100644 --- a/src/contexts/ActiveHardwareRosContext.tsx +++ b/src/contexts/ActiveHardwareRosContext.tsx @@ -9,6 +9,7 @@ import React, { import type { ControllerJointConfig } from '../Constants/rosConfig'; import { useRosConnection } from '../hooks/useRosConnection.hook'; import { HardwareConfigHandler } from '../Services/ros/handlers/HardwareConfig.handler'; +import { Diagnostics } from '../Services/diagnostics.service'; import { parseHardwareConfigYaml } from '../Utils/hardwareConfigYaml'; import { controllerJointConfigsFromHardwareYaml } from '../Utils/hardwareControllersFromYaml.ts'; @@ -80,6 +81,7 @@ export function ActiveHardwareRosProvider({ children }: { children: React.ReactN const res = await HardwareConfigHandler.getConfig(''); if (!res.success) { const msg = res.message || 'Failed to load active hardware config'; + Diagnostics.record('connection', 'hardware', 'error', msg); setActiveHardwareError(msg); setActiveHardwareDoc(null); setControllerConfigsFromActive(null); @@ -93,6 +95,11 @@ export function ActiveHardwareRosProvider({ children }: { children: React.ReactN } const doc = parseHardwareConfigYaml(res.config_yaml || ''); const ctrls = controllerJointConfigsFromHardwareYaml(doc); + Diagnostics.record( + 'connection', 'hardware', 'ok', + `${res.config_name || 'active'}: ${ctrls.length} controllers, ` + + `${ctrls.reduce((n, c) => n + c.joints.length, 0)} joints`, + ); setActiveHardwareDoc(doc); setActiveHardwareConfigName(res.config_name || ''); setServerFlashedConfigName((res.flashed_config_name || '').trim()); diff --git a/src/features/hardwareConfiguration/ConfigurationPage.tsx b/src/features/hardwareConfiguration/ConfigurationPage.tsx index ed3fca0..ac8ad39 100644 --- a/src/features/hardwareConfiguration/ConfigurationPage.tsx +++ b/src/features/hardwareConfiguration/ConfigurationPage.tsx @@ -1,11 +1,12 @@ +import { useContext } from 'react'; import { Alert, Button, Card, Input, Select, Space, Table, Tag, Tooltip, Typography, Grid } from 'antd'; import { PlusOutlined, ReloadOutlined, ThunderboltOutlined } from '@ant-design/icons'; -import { Page } from '../../Components/Page.tsx'; import { HardwareConfigPresetHeaderTag } from '../../Components/HardwareConfigPresetTag.tsx'; import { HardwareYamlConfigManager } from '../../Components/HardwareYamlConfigManager.tsx'; import { LucyLoader } from '../../Components/LucyLoader.tsx'; import { UNPARSED_VALIDATION_KEY, GENERAL_VALIDATION_KEY } from '../../Utils/hardwareConfigServerErrors.ts'; import { UI_CARD_SURFACE_STYLE, UI_PRIMARY_GREEN_BUTTON_STYLE } from '../../Constants/uiTheme.ts'; +import { HeaderHeightContext } from '../../contexts/HeaderHeightContext.ts'; import './configuration.switch.css'; import { ActivateConfigureWorkflowModal } from './components/ActivateConfigureWorkflowModal.tsx'; import { useHardwareConfiguration } from './hooks/useHardwareConfiguration.tsx'; @@ -16,31 +17,26 @@ const { useBreakpoint } = Grid; const ConfigurationPage = () => { const hw = useHardwareConfiguration(); const editorLocked = hw.editorLocked; + // The page header is `position: sticky; top: 0`, so the actuator table header has to park below it. + const headerHeight = useContext(HeaderHeightContext); const screens = useBreakpoint(); const isMobile = !screens.lg; if (isMobile) { return ( - -
- - Unsupported Screen Size - - The hardware configuration page is not available on small screens. Please use a tablet or computer for a better experience. - - -
-
+
+ + Unsupported Screen Size + + The robot configuration page is not available on small screens. Please use a tablet or computer for a better experience. + + +
); } return ( - + <> {hw.contextHolderMessage}
{ {!hw.yamlDoc ? ( { { ) : null} - + ); }; diff --git a/src/features/hardwareConfiguration/components/ActivateConfigureWorkflowModal.tsx b/src/features/hardwareConfiguration/components/ActivateConfigureWorkflowModal.tsx index 1313e8f..e03f7c8 100644 --- a/src/features/hardwareConfiguration/components/ActivateConfigureWorkflowModal.tsx +++ b/src/features/hardwareConfiguration/components/ActivateConfigureWorkflowModal.tsx @@ -6,7 +6,7 @@ import { ThunderboltOutlined, WarningOutlined, } from '@ant-design/icons'; -import { Alert, Button, Card, Checkbox, Divider, Modal, Progress, Space, Switch, Tag, Typography } from 'antd'; +import { Alert, Button, Card, Checkbox, Divider, Progress, Space, Switch, Tag, Typography } from 'antd'; import type { HardwareConfigDiff } from '../model/hardwareConfigDiff.ts'; import type { GeneratedFileNames } from '../../../Utils/generatedFiles.ts'; import { HardwareConfigPresetHeaderTag } from '../../../Components/HardwareConfigPresetTag.tsx'; @@ -16,7 +16,6 @@ import { UI_BORDER_SOFT, UI_CARD_SURFACE_STYLE, UI_ERROR, - UI_MODAL_MASK_BG, UI_PRIMARY_GREEN_BUTTON_STYLE, UI_TEXT_PRIMARY_ON_DARK, UI_TEXT_SECONDARY_MUTED, @@ -25,12 +24,9 @@ import { } from '../../../Constants/uiTheme.ts'; import type { WorkflowStepRuntimeStatus, WorkflowStepSlice } from '../activateWorkflowStepTypes.ts'; import '../configuration.switch.css'; +import { MovableModal } from '../../../Components/MovableModal.tsx'; -const { Text, Title } = Typography; - -const modalStyles = { - mask: { backgroundColor: UI_MODAL_MASK_BG }, -}; +const { Text } = Typography; function stepIcon(status: WorkflowStepRuntimeStatus) { if (status === 'done') return ; @@ -132,23 +128,13 @@ export function ActivateConfigureWorkflowModal(props: ActivateConfigureWorkflowM gazeboRunning === true; return ( - - ACTIVATE & CONFIGURE - - } - open={open} - onCancel={workflowRunning ? () => { } : onClose} - footer={null} - keyboard={!workflowRunning} - width={720} - style={{ top: 48 }} - styles={modalStyles} - className="dark-modal" - maskClosable={!workflowRunning} - closable={!workflowRunning} - destroyOnClose={false} + { } : onClose} + initialPosition={{ x: 100, y: 48 }} + initialSize={{ w: 720, h: 680 }} + header={} > {serverActiveConfigName && serverActiveConfigName !== selectedTargetConfigName ? ( @@ -374,7 +360,7 @@ export function ActivateConfigureWorkflowModal(props: ActivateConfigureWorkflowM - + ); } diff --git a/src/hooks/useRobotModel.ts b/src/hooks/useRobotModel.ts index 056e7ce..e19b951 100644 --- a/src/hooks/useRobotModel.ts +++ b/src/hooks/useRobotModel.ts @@ -207,6 +207,8 @@ export function useRobotModel(): UseRobotModelReturn { }, [parseAndLoad, reloadToken]); const reload = useCallback(() => { + // Re-running the hook alone would not touch the rosbridge subscription. + RobotDescriptionHandler.getInstance().resubscribe(); globalCache = { ready: false }; loadedUrdfRef.current = null; setRobot(null); diff --git a/src/hooks/useSensorSources.ts b/src/hooks/useSensorSources.ts new file mode 100644 index 0000000..7eebd28 --- /dev/null +++ b/src/hooks/useSensorSources.ts @@ -0,0 +1,15 @@ +import { useMemo } from 'react'; +import { useActiveHardwareRos } from '../contexts/ActiveHardwareRosContext'; +import { sensorSourcesFromHardwareYaml } from '../Utils/sensorSourcesFromHardwareYaml'; +import type { SensorSource } from '../Constants/rosConfig'; + +/** Pressure sensor stream entries from the active hardware config. */ +export function useSensorSources(): SensorSource[] { + const { activeHardwareDoc, activeHardwareFetchEpoch } = useActiveHardwareRos(); + + return useMemo(() => { + if (!activeHardwareDoc) return []; + return sensorSourcesFromHardwareYaml(activeHardwareDoc); + // eslint-disable-next-line react-hooks/exhaustive-deps -- rebuild when pipeline activates a new preset + }, [activeHardwareDoc, activeHardwareFetchEpoch]); +} diff --git a/src/hooks/useSensorStream.ts b/src/hooks/useSensorStream.ts new file mode 100644 index 0000000..aca4d78 --- /dev/null +++ b/src/hooks/useSensorStream.ts @@ -0,0 +1,84 @@ +import { useCallback, useEffect, useState } from 'react'; +import type { SensorSource } from '../Constants/rosConfig'; +import { SensorHandler } from '../Services/ros/handlers/Sensor.handler'; + +const HISTORY_MS = 60_000; + +export interface SensorSample { + time: number; + value: number; +} + +export interface SensorStreamState { + displaySamples: SensorSample[]; + currentValue: number | null; + historicalMin: number | null; + historicalMax: number | null; + isPaused: boolean; + setPaused: (paused: boolean) => void; +} + +/** Live samples for one pressure sensor on a `sensors/` topic. */ +export function useSensorStream(source: SensorSource | null): SensorStreamState { + const [sourceSamples, setSourceSamples] = useState([]); + const [displaySamples, setDisplaySamples] = useState([]); + const [currentValue, setCurrentValue] = useState(null); + const [historicalMin, setHistoricalMin] = useState(null); + const [historicalMax, setHistoricalMax] = useState(null); + const [isPaused, setIsPaused] = useState(false); + + const onSample = useCallback((value: number, timestampMs: number) => { + setSourceSamples((prev) => { + const next = [...prev, { time: timestampMs, value }]; + return next.filter((sample) => timestampMs - sample.time < HISTORY_MS); + }); + setHistoricalMin((prev) => (prev === null ? value : Math.min(prev, value))); + setHistoricalMax((prev) => (prev === null ? value : Math.max(prev, value))); + }, []); + + useEffect(() => { + if (!source) return undefined; + + const handler = SensorHandler.getInstance(); + handler.subscribe(source.id, source.topic, source.arrayIndex, onSample); + return () => { + handler.unsubscribe(source.id, source.topic, onSample); + }; + }, [source, onSample]); + + useEffect(() => { + setSourceSamples([]); + setDisplaySamples([]); + setCurrentValue(null); + setHistoricalMin(null); + setHistoricalMax(null); + if (!source) return; + }, [source]); + + useEffect(() => { + if (!isPaused) { + setDisplaySamples(sourceSamples); + } + }, [sourceSamples, isPaused]); + + useEffect(() => { + const latest = displaySamples[displaySamples.length - 1]; + setCurrentValue(latest?.value ?? null); + }, [displaySamples]); + + const setPaused = useCallback((paused: boolean) => { + setIsPaused(paused); + if (!paused) { + setDisplaySamples(sourceSamples); + } + }, [sourceSamples]); + + return { + displaySamples, + currentValue, + historicalMin, + historicalMax, + isPaused, + setPaused, + }; +} diff --git a/src/hooks/useThrottledJointAngles.ts b/src/hooks/useThrottledJointAngles.ts index 44523c5..8962bfb 100644 --- a/src/hooks/useThrottledJointAngles.ts +++ b/src/hooks/useThrottledJointAngles.ts @@ -8,6 +8,7 @@ import { useState, useRef, useEffect } from 'react'; import { JointStateHandler } from '../Services/ros/handlers/JointState.handler'; +import { Diagnostics } from '../Services/diagnostics.service'; export function useThrottledJointAngles(isConnected: boolean, hz = 10): Map { const [jointAngles, setJointAngles] = useState>(new Map()); @@ -48,6 +49,7 @@ export function useThrottledJointAngles(isConnected: boolean, hz = 10): Map clearInterval(interval); }, [isConnected, hz]); diff --git a/vite.config.ts b/vite.config.ts index 5752b5f..39ed554 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -41,9 +41,9 @@ export default defineConfig(({ mode }) => { https, host: true, allowedHosts, - port: parseInt(env.VITE_PORT || "5000"), + port: parseInt(env.VITE_PORT || "4004"), hmr: https - ? { protocol: "wss", clientPort: parseInt(env.VITE_PORT || "5000") } + ? { protocol: "wss", clientPort: parseInt(env.VITE_PORT || "4004") } : undefined, proxy: { "^/rosbridge": { diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fcbc555 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4583 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 10 + cacheKey: 10c0 + +"@ant-design/colors@npm:^7.0.0, @ant-design/colors@npm:^7.2.1": + version: 7.2.1 + resolution: "@ant-design/colors@npm:7.2.1" + dependencies: + "@ant-design/fast-color": "npm:^2.0.6" + checksum: 10c0/4748a0bfb1ea98e08e29dcd4f7afd2781ae2119f783e6e9f80e889fd15fc19f7137e2a3d91f26bae2ab1ee76c04d520cc35f2bb0a708cd71e463f4d9deb4192d + languageName: node + linkType: hard + +"@ant-design/colors@npm:^8.0.1": + version: 8.0.1 + resolution: "@ant-design/colors@npm:8.0.1" + dependencies: + "@ant-design/fast-color": "npm:^3.0.0" + checksum: 10c0/6108c2204ce98dbf68682fcd04c3cba087f5316f01c6a28928eb8d7b291c23b6a5fcc9cd74af6211f7984d3ccc7e26be16ec664a9380737b46ee24fc78222e30 + languageName: node + linkType: hard + +"@ant-design/cssinjs-utils@npm:^1.1.3": + version: 1.1.3 + resolution: "@ant-design/cssinjs-utils@npm:1.1.3" + dependencies: + "@ant-design/cssinjs": "npm:^1.21.0" + "@babel/runtime": "npm:^7.23.2" + rc-util: "npm:^5.38.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/e8a443a613689c4e984f5cf44b799f6288a6debf9a35cafb27a0411ef77ae335ba5ace7a38efd8e04f04ac897ddf24c81f0ad6615ac586f2616cb7f2b72f6176 + languageName: node + linkType: hard + +"@ant-design/cssinjs@npm:^1.21.0, @ant-design/cssinjs@npm:^1.23.0": + version: 1.24.0 + resolution: "@ant-design/cssinjs@npm:1.24.0" + dependencies: + "@babel/runtime": "npm:^7.11.1" + "@emotion/hash": "npm:^0.8.0" + "@emotion/unitless": "npm:^0.7.5" + classnames: "npm:^2.3.1" + csstype: "npm:^3.1.3" + rc-util: "npm:^5.35.0" + stylis: "npm:^4.3.4" + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 10c0/e84bc33bd74d386f87813641287ad3ba7494adcde944277bbec3745ea14cc19bb430c0723d8e86058d23c450a4c22e7fe63281e0d5e50d3a0e94ea55185305b4 + languageName: node + linkType: hard + +"@ant-design/fast-color@npm:^2.0.6": + version: 2.0.6 + resolution: "@ant-design/fast-color@npm:2.0.6" + dependencies: + "@babel/runtime": "npm:^7.24.7" + checksum: 10c0/8d30649bd8d4e56d5c48393fcf0ad5c24d1099ec4cbf88f55bd9f4489e61efc30087d301da384c4ed21f2d5597087c8ba27dfbcc7693915310c26d307f5a8276 + languageName: node + linkType: hard + +"@ant-design/fast-color@npm:^3.0.0": + version: 3.0.1 + resolution: "@ant-design/fast-color@npm:3.0.1" + checksum: 10c0/bc6535351f855b1af777b9d18c5b5ca7eeb8769c1dc9cd8579408ed5284e4d6dd2d9cd192ff8110dc7a82e9dc9bc2e5c51ceeff3237c88c5464aca0e4d70eb9a + languageName: node + linkType: hard + +"@ant-design/icons-svg@npm:^4.4.0, @ant-design/icons-svg@npm:^4.5.0": + version: 4.5.0 + resolution: "@ant-design/icons-svg@npm:4.5.0" + checksum: 10c0/6d8c15ffc43c7c39560e075682da5b177462d42ca92ac227702c9ce267fea688025449bc6528d7b64f1bc88408384af5d6c52464afa47a01b5394894816d7f9e + languageName: node + linkType: hard + +"@ant-design/icons@npm:^5.6.1": + version: 5.6.1 + resolution: "@ant-design/icons@npm:5.6.1" + dependencies: + "@ant-design/colors": "npm:^7.0.0" + "@ant-design/icons-svg": "npm:^4.4.0" + "@babel/runtime": "npm:^7.24.8" + classnames: "npm:^2.2.6" + rc-util: "npm:^5.31.1" + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 10c0/7a9d9fd388c5c66d92818fd0eb794a54ef0b0dc3d75f15ac24c7cfde21c5c836c220cf35423768fd1faa28d55443a6917bf4260469c1485be83f799daa351976 + languageName: node + linkType: hard + +"@ant-design/icons@npm:^6.0.0": + version: 6.3.2 + resolution: "@ant-design/icons@npm:6.3.2" + dependencies: + "@ant-design/colors": "npm:^8.0.1" + "@ant-design/icons-svg": "npm:^4.5.0" + "@rc-component/util": "npm:^1.11.0" + clsx: "npm:^2.1.1" + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 10c0/3f7dcfda65d22f43f78173de60ac47fd5ffcb54a9db4dcaa2fd8380aa424facb8c51edc9c7a9afeaab8ca012c07827edfe73bb51e7a33891fed90ce1da692620 + languageName: node + linkType: hard + +"@ant-design/react-slick@npm:~1.1.2": + version: 1.1.2 + resolution: "@ant-design/react-slick@npm:1.1.2" + dependencies: + "@babel/runtime": "npm:^7.10.4" + classnames: "npm:^2.2.5" + json2mq: "npm:^0.2.0" + resize-observer-polyfill: "npm:^1.5.1" + throttle-debounce: "npm:^5.0.0" + peerDependencies: + react: ">=16.9.0" + checksum: 10c0/4f758e28cf8418e9f1a9b03da3814b2342fcee8a4039cae2fe6f77c01e9c8b7ea78a7e10961128a5ccba4992f520e88cb72c4f7fa1bd22314ce628b0d9fb3f5c + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/code-frame@npm:7.29.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.29.7" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/169fc2080169a40c1760155eaaaf739bcb882df0bec76a83adbda5493645bc17270a3434b8848c494b1933e96fe1d147370001e3cda09a39f43ae30f08ef2069 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/compat-data@npm:7.29.7" + checksum: 10c0/47913f05e08a45a1c9df38c02b4b49e391005085b489432647a1abe112e5d9c75e3be8ea5972b7f6da4ec5d1339922ceb9ea02b8a25d4ed1cb8636e5261f344e + languageName: node + linkType: hard + +"@babel/core@npm:^7.28.0": + version: 7.29.7 + resolution: "@babel/core@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.7" + "@babel/helper-compilation-targets": "npm:^7.29.7" + "@babel/helper-module-transforms": "npm:^7.29.7" + "@babel/helpers": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/template": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/112fb09c24de7a1de64d1de2c31fe65c4e6af4cb2fb6e6d99ea5373e6fc51e75b88581c0efae4c4c68f119a02a988c7106e95011a41530a2fb8ed793c7eaa07b + languageName: node + linkType: hard + +"@babel/generator@npm:^7.29.7, @babel/generator@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/generator@npm:7.29.8" + dependencies: + "@babel/parser": "npm:^7.29.8" + "@babel/types": "npm:^7.29.8" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/7b896696314a659652393b76d78276e236acd0f7fae40a9a1af7f01c76aeafc630dd0966aad6f9386d35d7c674a8e6e2d8e217c44d25fb11460e68afa9ba8441 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-compilation-targets@npm:7.29.7" + dependencies: + "@babel/compat-data": "npm:^7.29.7" + "@babel/helper-validator-option": "npm:^7.29.7" + browserslist: "npm:^4.24.0" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/4c15fd4c69a0a7047799a28a88460c19cede0a0ee8af994ea169114986f4af48b92c7393a4a3fee0456c11a656eece3448a6ed06354453d6c27cccf17195453b + languageName: node + linkType: hard + +"@babel/helper-globals@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-globals@npm:7.29.7" + checksum: 10c0/f38417c40b1129a1b2b519ca961b9040c8827d1444fd74068702286b91b77089431dc76b6b9d5c1496e5da2a4f3ad329c6946e688ba3fa0d1d0b3d2b4f34f36a + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-imports@npm:7.29.7" + dependencies: + "@babel/traverse": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/6adf60d97356027413342a092f818d9678c4f5caff716a33e3284b5ae14e47a9e88059d421dde4ee4894691260039a12602c0e7becadc175602194b40dfa345d + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-module-transforms@npm:7.29.7" + dependencies: + "@babel/helper-module-imports": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + "@babel/traverse": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/ee5a2172c24a42be696836f4b0d947489c9729d8adf5821885cf77d1ad5333e3c447368e9a71f67df1099570490553dccf9f888ef0a92a48aa63cb086bd8c7e1 + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-plugin-utils@npm:7.29.7" + checksum: 10c0/380477a06133274a2759f9355929cb60a95e8b8fee624a1ae1fa349e1d1645b89daca456f72833f6d1062bffa12ee4271c5bf0cc5a61c0166cdc24c7591e2408 + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-string-parser@npm:7.29.7" + checksum: 10c0/194bc0f1716e396d5ffde56ad6119745fb9557662c98611590e5e454906783a4ccb21ce93056b8eb69a4909044834e45d96e50ac695bbe9e3221648fe033c06c + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-identifier@npm:7.29.7" + checksum: 10c0/4795354e7ae0dcafa72de1cd04ec51252dc1498517170beaf019e03effc5b7bf13c6b21a3949a77e07b8125be7f106ed1131350d8ebd4566ae874094a726d62b + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helper-validator-option@npm:7.29.7" + checksum: 10c0/d2a06c6d0ac40ba4a2f219fc2cab249c7a94bacdb2686273b7f9598571c908809b48468ff588915a346e6cc7296f60b581023d1d498b747fed06f779d335c2cc + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/helpers@npm:7.29.7" + dependencies: + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/218e8d10953647c9f44775f5a022b227a182674853b5ea8631889deb7e1a3e4bc870388aaecf59bb8bd92a87f9a96220ed3f70a35bffec6bcf9169ecb67891ac + languageName: node + linkType: hard + +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.29.7, @babel/parser@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/parser@npm:7.29.8" + dependencies: + "@babel/types": "npm:^7.29.8" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/acc890c5e6a6dd40863a47b50bac111d7185ee6fbbe163ebe11d5214854ca2adb901462ad4d718a65090ef84bd2230e9e8ab45a2e0caccc685f1f57ab0bb1e28 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-self@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx-self@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/288995f0fd0d61ab740a315fb56c8255eb87dd4a4ac2ac7d0fdd4ce173c3878200141e80da2db0e598c7b2a71e74e604afdbb4c8e14ae6e0527ce0b6294c03da + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-source@npm:^7.27.1": + version: 7.29.7 + resolution: "@babel/plugin-transform-react-jsx-source@npm:7.29.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.29.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/a121899631e6d99b9e1b276acf736dbb77948a31f8eeeae67b89c8a4ab0f05e51ba64544baa06c286a2b9944f227244e15aac464e2313d286d0511fe51e27975 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0": + version: 7.29.7 + resolution: "@babel/runtime@npm:7.29.7" + checksum: 10c0/ca11572f7146b21e0bde6a9ed4bb6a89eafbee5f0944c7eb54d0d8a2dac962c33638a1d611e14faa71dfbb92b4b5f9236232208568a6b7d5c6f3f39ddb91771e + languageName: node + linkType: hard + +"@babel/template@npm:^7.29.7": + version: 7.29.7 + resolution: "@babel/template@npm:7.29.7" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.7" + "@babel/types": "npm:^7.29.7" + checksum: 10c0/8bb7f900dcab0e9e1c5ffbc33ca10e0d26b7b2e2ca804becb73ee771b9c4ed6e2908a4ae4a14c08560febb45d2b6b9a173955e42ad404d05f8b04840a14d9c58 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.29.7": + version: 7.29.8 + resolution: "@babel/traverse@npm:7.29.8" + dependencies: + "@babel/code-frame": "npm:^7.29.7" + "@babel/generator": "npm:^7.29.8" + "@babel/helper-globals": "npm:^7.29.7" + "@babel/parser": "npm:^7.29.8" + "@babel/template": "npm:^7.29.7" + "@babel/types": "npm:^7.29.8" + debug: "npm:^4.3.1" + checksum: 10c0/87a28989c434add26d787776ac6d30f749b89cb030f2a605c89f671a516a6fa165ac0476f07e2b69feed70128b2734cae1cbbf41dfe95ccf22081bd8f8b91923 + languageName: node + linkType: hard + +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.28.2, @babel/types@npm:^7.29.7, @babel/types@npm:^7.29.8": + version: 7.29.8 + resolution: "@babel/types@npm:7.29.8" + dependencies: + "@babel/helper-string-parser": "npm:^7.29.7" + "@babel/helper-validator-identifier": "npm:^7.29.7" + checksum: 10c0/be7c279f0abf2a086c633e21b49c7ca80275d05283cc5a268b67a708c9914bd0c944f1422b3eb3cb37682a2af5d560abf520ccf9b01b53ecbfe6b71fbc3fdde6 + languageName: node + linkType: hard + +"@dimforge/rapier3d-compat@npm:~0.12.0": + version: 0.12.0 + resolution: "@dimforge/rapier3d-compat@npm:0.12.0" + checksum: 10c0/c66c24f90649c0fc870679c12e7fec1a111080d44450169b57561f957d7b6b284ad8a3ceeba95533e213176ea171351acebd3dd43885fafb33f18bfbd9d507db + languageName: node + linkType: hard + +"@dnd-kit/accessibility@npm:^3.1.1": + version: 3.1.1 + resolution: "@dnd-kit/accessibility@npm:3.1.1" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/be0bf41716dc58f9386bc36906ec1ce72b7b42b6d1d0e631d347afe9bd8714a829bd6f58a346dd089b1519e93918ae2f94497411a61a4f5e4d9247c6cfd1fef8 + languageName: node + linkType: hard + +"@dnd-kit/core@npm:^6.3.1": + version: 6.3.1 + resolution: "@dnd-kit/core@npm:6.3.1" + dependencies: + "@dnd-kit/accessibility": "npm:^3.1.1" + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/196db95d81096d9dc248983533eab91ba83591770fa5c894b1ac776f42af0d99522b3fd5bb3923411470e4733fcfa103e6ee17adc17b9b7eb54c7fbec5ff7c52 + languageName: node + linkType: hard + +"@dnd-kit/sortable@npm:^10.0.0": + version: 10.0.0 + resolution: "@dnd-kit/sortable@npm:10.0.0" + dependencies: + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@dnd-kit/core": ^6.3.0 + react: ">=16.8.0" + checksum: 10c0/37ee48bc6789fb512dc0e4c374a96d19abe5b2b76dc34856a5883aaa96c3297891b94cc77bbc409e074dcce70967ebcb9feb40cd9abadb8716fc280b4c7f99af + languageName: node + linkType: hard + +"@dnd-kit/utilities@npm:^3.2.2": + version: 3.2.2 + resolution: "@dnd-kit/utilities@npm:3.2.2" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/9aa90526f3e3fd567b5acc1b625a63177b9e8d00e7e50b2bd0e08fa2bf4dba7e19529777e001fdb8f89a7ce69f30b190c8364d390212634e0afdfa8c395e85a0 + languageName: node + linkType: hard + +"@emotion/hash@npm:^0.8.0": + version: 0.8.0 + resolution: "@emotion/hash@npm:0.8.0" + checksum: 10c0/706303d35d416217cd7eb0d36dbda4627bb8bdf4a32ea387e8dd99be11b8e0a998e10af21216e8a5fade518ad955ff06aa8890f20e694ce3a038ae7fc1000556 + languageName: node + linkType: hard + +"@emotion/unitless@npm:^0.7.5": + version: 0.7.5 + resolution: "@emotion/unitless@npm:0.7.5" + checksum: 10c0/4d0d94f53cb97b4481bbfa394953e1899a0b877644642ba9dd7247c27eb8c48e14e22aeb11411d7d9874685ad85dd5fb5b50eb78c6d8840eb56a84b92dcef2f4 + languageName: node + linkType: hard + +"@esbuild/aix-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/aix-ppc64@npm:0.21.5" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm64@npm:0.21.5" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm@npm:0.21.5" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-x64@npm:0.21.5" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-arm64@npm:0.21.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-x64@npm:0.21.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-arm64@npm:0.21.5" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-x64@npm:0.21.5" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm64@npm:0.21.5" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm@npm:0.21.5" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ia32@npm:0.21.5" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-loong64@npm:0.21.5" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-mips64el@npm:0.21.5" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ppc64@npm:0.21.5" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-riscv64@npm:0.21.5" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-s390x@npm:0.21.5" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-x64@npm:0.21.5" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/netbsd-x64@npm:0.21.5" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/openbsd-x64@npm:0.21.5" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/sunos-x64@npm:0.21.5" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-arm64@npm:0.21.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-ia32@npm:0.21.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-x64@npm:0.21.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": + version: 4.10.1 + resolution: "@eslint-community/eslint-utils@npm:4.10.1" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/b514586655698bc6b74db72a496c77e813c78b63e36a83429845ac7057dd77113b0b6c31e6e590d5baaeee2abae6ea22363a41209bcafa078d895ab83ce83011 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": + version: 4.12.2 + resolution: "@eslint-community/regexpp@npm:4.12.2" + checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d + languageName: node + linkType: hard + +"@eslint/config-array@npm:^0.21.2": + version: 0.21.2 + resolution: "@eslint/config-array@npm:0.21.2" + dependencies: + "@eslint/object-schema": "npm:^2.1.7" + debug: "npm:^4.3.1" + minimatch: "npm:^3.1.5" + checksum: 10c0/89dfe815d18456177c0a1f238daf4593107fd20298b3598e0103054360d3b8d09d967defd8318f031185d68df1f95cfa68becf1390a9c5c6887665f1475142e3 + languageName: node + linkType: hard + +"@eslint/config-helpers@npm:^0.4.2": + version: 0.4.2 + resolution: "@eslint/config-helpers@npm:0.4.2" + dependencies: + "@eslint/core": "npm:^0.17.0" + checksum: 10c0/92efd7a527b2d17eb1a148409d71d80f9ac160b565ac73ee092252e8bf08ecd08670699f46b306b94f13d22e88ac88a612120e7847570dd7cdc72f234d50dcb4 + languageName: node + linkType: hard + +"@eslint/core@npm:^0.17.0": + version: 0.17.0 + resolution: "@eslint/core@npm:0.17.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10c0/9a580f2246633bc752298e7440dd942ec421860d1946d0801f0423830e67887e4aeba10ab9a23d281727a978eb93d053d1922a587d502942a713607f40ed704e + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^3.3.6": + version: 3.3.6 + resolution: "@eslint/eslintrc@npm:3.3.6" + dependencies: + ajv: "npm:^6.14.0" + debug: "npm:^4.3.2" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.3.0" + minimatch: "npm:^3.1.5" + strip-json-comments: "npm:^3.1.1" + checksum: 10c0/349697171ee116f501a2f483bf47cd38937a61880aeb1c23481a69afc95e95859430a0947acbe1f5507f223180bf57530ecf2989e73bcbea763a6dcffdf1d010 + languageName: node + linkType: hard + +"@eslint/js@npm:9.39.5, @eslint/js@npm:^9.25.0": + version: 9.39.5 + resolution: "@eslint/js@npm:9.39.5" + checksum: 10c0/49894e98ba313a7d3bfb1b20d55c0f9826be45a7db876fd84e533ac7f101b1d95b51cd22c6a6db3a45066b9c0d068c31b4a22fa0db8a6cc8744a25540efdb824 + languageName: node + linkType: hard + +"@eslint/object-schema@npm:^2.1.7": + version: 2.1.7 + resolution: "@eslint/object-schema@npm:2.1.7" + checksum: 10c0/936b6e499853d1335803f556d526c86f5fe2259ed241bc665000e1d6353828edd913feed43120d150adb75570cae162cf000b5b0dfc9596726761c36b82f4e87 + languageName: node + linkType: hard + +"@eslint/plugin-kit@npm:^0.4.1": + version: 0.4.1 + resolution: "@eslint/plugin-kit@npm:0.4.1" + dependencies: + "@eslint/core": "npm:^0.17.0" + levn: "npm:^0.4.1" + checksum: 10c0/51600f78b798f172a9915dffb295e2ffb44840d583427bc732baf12ecb963eb841b253300e657da91d890f4b323d10a1bd12934bf293e3018d8bb66fdce5217b + languageName: node + linkType: hard + +"@humanfs/core@npm:^0.19.2": + version: 0.19.2 + resolution: "@humanfs/core@npm:0.19.2" + dependencies: + "@humanfs/types": "npm:^0.15.0" + checksum: 10c0/d0a1d52d7b30c27d49475a53072d1510b81c5803e44b342fb8faf3887f1aa27593a1e6dc76a45268e7892d3f4e198146659281f6b6d55eacf3fd5a38bac30c5c + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.6": + version: 0.16.8 + resolution: "@humanfs/node@npm:0.16.8" + dependencies: + "@humanfs/core": "npm:^0.19.2" + "@humanfs/types": "npm:^0.15.0" + "@humanwhocodes/retry": "npm:^0.4.0" + checksum: 10c0/56140579db811af4e160b195d45d0f29acf644d192c93fe24c9e594ebf06f19dfc157494a07c84540b8a071c0e4b37209c2362765d31734f4d0be869c2422e25 + languageName: node + linkType: hard + +"@humanfs/types@npm:^0.15.0": + version: 0.15.0 + resolution: "@humanfs/types@npm:0.15.0" + checksum: 10c0/fc26b9a024b0e55f7eaf64036df94345bf5d36d6a41ef80ef38e78f1f7430ce26cf435af736adae58913baae18eac3f38c18739054a3d379102015978eae862e + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 10c0/909b69c3b86d482c26b3359db16e46a32e0fb30bd306a3c176b8313b9e7313dba0f37f519de6aa8b0a1921349e505f259d19475e123182416a506d7f87e7f529 + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2": + version: 0.4.3 + resolution: "@humanwhocodes/retry@npm:0.4.3" + checksum: 10c0/3775bb30087d4440b3f7406d5a057777d90e4b9f435af488a4923ef249e93615fb78565a85f173a186a076c7706a81d0d57d563a2624e4de2c5c9c66c486ce42 + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.13 + resolution: "@jridgewell/gen-mapping@npm:0.3.13" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.0" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/9a7d65fb13bd9aec1fbab74cda08496839b7e2ceb31f5ab922b323e94d7c481ce0fc4fd7e12e2610915ed8af51178bdc61e168e92a8c8b8303b030b03489b13b + languageName: node + linkType: hard + +"@jridgewell/remapping@npm:^2.3.5": + version: 2.3.5 + resolution: "@jridgewell/remapping@npm:2.3.5" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/3de494219ffeb2c5c38711d0d7bb128097edf91893090a2dbc8ee0b55d092bb7347b1fd0f478486c5eab010e855c73927b1666f2107516d472d24a73017d1194 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": + version: 1.6.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.6.0" + checksum: 10c0/b5be700e45a775f218589c3466c4ffea630582b4988657652da464e5ab8a7d18bf928ee4fd2363fb346ede4bea9c6ff0bae05a538358c4565ece6199531b5f72 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.31 + resolution: "@jridgewell/trace-mapping@npm:0.3.31" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/4b30ec8cd56c5fd9a661f088230af01e0c1a3888d11ffb6b47639700f71225be21d1f7e168048d6d4f9449207b978a235c07c8f15c07705685d16dc06280e9d9 + languageName: node + linkType: hard + +"@mediapipe/camera_utils@npm:^0.3.1675466862": + version: 0.3.1675466862 + resolution: "@mediapipe/camera_utils@npm:0.3.1675466862" + checksum: 10c0/beb200f14a942ccffcd6867b8877e2a74ae39af830b2e81eef14a5f9fb4ce04205aa10094ca0b1b4ded7b2475f777030f82993189864238b9c5ac743a53ccfce + languageName: node + linkType: hard + +"@mediapipe/drawing_utils@npm:^0.3.1675466124": + version: 0.3.1675466124 + resolution: "@mediapipe/drawing_utils@npm:0.3.1675466124" + checksum: 10c0/286a8d77b39e6420c2bda2c4b85a87711749584953645e43215d7c0c5367455e5af417c65fb9313f6ebd0ba9cf6fe08be96a9b0696298a2ae503476afde0d406 + languageName: node + linkType: hard + +"@mediapipe/hands@npm:^0.4.1675469240": + version: 0.4.1675469240 + resolution: "@mediapipe/hands@npm:0.4.1675469240" + checksum: 10c0/f80c2cabaf66aa79bca097cdbcf315b1e9185fc8b2b5c4060bde93faaeeccfa7dadb77f9be47b2c57a8be8da49a972de63106d743a78a31f214a158b2426a059 + languageName: node + linkType: hard + +"@mediapipe/tasks-vision@npm:0.10.17": + version: 0.10.17 + resolution: "@mediapipe/tasks-vision@npm:0.10.17" + checksum: 10c0/f2f5dd9ca39d562b902a6d964f8b786f17a143f42776d0d26b8c79632b10cf210e28d98ec92de757b7bb2e6595ed4f9bbf5e49f1709b09dc08709bd276ced442 + languageName: node + linkType: hard + +"@mediapipe/tasks-vision@npm:^0.10.22-rc.20250304": + version: 0.10.35 + resolution: "@mediapipe/tasks-vision@npm:0.10.35" + checksum: 10c0/82a7ce07f70811251f594aa9edfbb4c1a7491b7b31665d008c53076757ce901706b9fa71af31b14c8c0211a82056d2c11118b5114dc55856b479cae3a633a697 + languageName: node + linkType: hard + +"@monogrid/gainmap-js@npm:^3.0.6": + version: 3.4.0 + resolution: "@monogrid/gainmap-js@npm:3.4.0" + dependencies: + promise-worker-transferable: "npm:^1.0.4" + peerDependencies: + three: ">= 0.159.0" + checksum: 10c0/06104767a0f542b36e1e203fe569f78996babcdd52661f42db4f079f372b272e0849b86f84f7690b2c165a8ba931f77ad324fe82a09c9b23e26d20e76cabd3e7 + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-gnu@npm:1.5.1": + version: 1.5.1 + resolution: "@napi-rs/lzma-linux-x64-gnu@npm:1.5.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rc-component/async-validator@npm:^5.0.3": + version: 5.1.2 + resolution: "@rc-component/async-validator@npm:5.1.2" + dependencies: + "@babel/runtime": "npm:^7.24.4" + checksum: 10c0/9a91822000cdd5501987eef9209fd11b2e367037b4b6751168858b89468e0aebc1a80f6b5e2de214ff6045a652057b7e42ef5ec06e8aad64755808ba09197c9d + languageName: node + linkType: hard + +"@rc-component/color-picker@npm:~2.0.1": + version: 2.0.1 + resolution: "@rc-component/color-picker@npm:2.0.1" + dependencies: + "@ant-design/fast-color": "npm:^2.0.6" + "@babel/runtime": "npm:^7.23.6" + classnames: "npm:^2.2.6" + rc-util: "npm:^5.38.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/be0b851a609fdb360367f38765b568c52f935dde4e70f7b832146a921eb221ddd51735144a35ce36c55810f47f830078e271508920585e3ab789e47c4f824775 + languageName: node + linkType: hard + +"@rc-component/context@npm:^1.4.0": + version: 1.4.0 + resolution: "@rc-component/context@npm:1.4.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + rc-util: "npm:^5.27.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/4da3773deca15107adccf6635c703663f5a9202c09d8877ee003ccf144de9991c5eefbca4458f31d95d234e57babf0f8f1926e0b887f8a503a43119a39a653aa + languageName: node + linkType: hard + +"@rc-component/mini-decimal@npm:^1.0.1": + version: 1.1.4 + resolution: "@rc-component/mini-decimal@npm:1.1.4" + dependencies: + "@babel/runtime": "npm:^7.18.0" + checksum: 10c0/32215c6a71ea0dba03fd9a7769f0fd23008d40d3c0c494180acc3d50746f3f8cc122ab2c0e07664271f21123455d5f1552f951357b288f19f2e0a06d95a4174e + languageName: node + linkType: hard + +"@rc-component/mutate-observer@npm:^1.1.0": + version: 1.1.0 + resolution: "@rc-component/mutate-observer@npm:1.1.0" + dependencies: + "@babel/runtime": "npm:^7.18.0" + classnames: "npm:^2.3.2" + rc-util: "npm:^5.24.4" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/90159acd831ed04b7d2d412354892fd042ad51e63bf17a9fcfa84f3b799e23479617fcc7404566a5b97e5b512b6097b87b8eb36e0c419d803692796a39186a2e + languageName: node + linkType: hard + +"@rc-component/portal@npm:^1.0.0-8, @rc-component/portal@npm:^1.0.0-9, @rc-component/portal@npm:^1.0.2, @rc-component/portal@npm:^1.1.0, @rc-component/portal@npm:^1.1.1": + version: 1.1.2 + resolution: "@rc-component/portal@npm:1.1.2" + dependencies: + "@babel/runtime": "npm:^7.18.0" + classnames: "npm:^2.3.2" + rc-util: "npm:^5.24.4" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/3c0297356635d47f364be79de02bb16009f06b1ce82124c3e63da9a71b8e7d3ea2c147e4703ead9cae0f662435a21e9feb30d2edf7197108bc3dbf969f3ca51f + languageName: node + linkType: hard + +"@rc-component/qrcode@npm:~1.1.0": + version: 1.1.3 + resolution: "@rc-component/qrcode@npm:1.1.3" + dependencies: + "@babel/runtime": "npm:^7.24.7" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/004ecd66c9f62edee0ede502235cf155a6da90b0cd52a836852cab84c795074a895cca6a3ea56692b32b538fcbf90533ac1ac2c7a89190228c2522c7ffe0e2bc + languageName: node + linkType: hard + +"@rc-component/tour@npm:~1.15.1": + version: 1.15.1 + resolution: "@rc-component/tour@npm:1.15.1" + dependencies: + "@babel/runtime": "npm:^7.18.0" + "@rc-component/portal": "npm:^1.0.0-9" + "@rc-component/trigger": "npm:^2.0.0" + classnames: "npm:^2.3.2" + rc-util: "npm:^5.24.4" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/8324edb96bbca2838e9bdcca5ac02e615596593d0e79ee74ad2e7d3a4801975f205907170bb16f92303b22701bc730c34337cb10270281cea36659bac10193b5 + languageName: node + linkType: hard + +"@rc-component/trigger@npm:^2.0.0, @rc-component/trigger@npm:^2.1.1, @rc-component/trigger@npm:^2.3.0": + version: 2.3.1 + resolution: "@rc-component/trigger@npm:2.3.1" + dependencies: + "@babel/runtime": "npm:^7.23.2" + "@rc-component/portal": "npm:^1.1.0" + classnames: "npm:^2.3.2" + rc-motion: "npm:^2.0.0" + rc-resize-observer: "npm:^1.3.1" + rc-util: "npm:^5.44.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/6baf0b0c22cea1a3869f931948145d32c6a3dfb711d022d290453685e79a4f06fedaf9e250e60082506ce3dee5a861ef5b78b337b05a2fa5a9a77cf0b4d97ec4 + languageName: node + linkType: hard + +"@rc-component/util@npm:^1.11.0": + version: 1.12.0 + resolution: "@rc-component/util@npm:1.12.0" + dependencies: + is-mobile: "npm:^5.0.0" + react-is: "npm:^19.2.7" + peerDependencies: + react: ">=18.0.0" + react-dom: ">=18.0.0" + checksum: 10c0/357aa5d88dc2662d81aa9d84c94ba0349589b53c89ad46989bdaf08840902186d4970f35b1f62cdf1fb0be2632323f3c56f2eb5e5ac961e0107333e34e7a34ab + languageName: node + linkType: hard + +"@react-three/drei@npm:^10.1.2": + version: 10.7.8 + resolution: "@react-three/drei@npm:10.7.8" + dependencies: + "@babel/runtime": "npm:^7.26.0" + "@mediapipe/tasks-vision": "npm:0.10.17" + "@monogrid/gainmap-js": "npm:^3.0.6" + "@use-gesture/react": "npm:^10.3.1" + camera-controls: "npm:^3.1.0" + cross-env: "npm:^7.0.3" + detect-gpu: "npm:^5.0.56" + glsl-noise: "npm:^0.0.0" + hls.js: "npm:^1.5.17" + maath: "npm:^0.10.8" + meshline: "npm:^3.3.1" + stats-gl: "npm:^2.2.8" + stats.js: "npm:^0.17.0" + suspend-react: "npm:^0.1.3" + three-mesh-bvh: "npm:^0.8.3" + three-stdlib: "npm:^2.35.6" + troika-three-text: "npm:^0.52.4" + tunnel-rat: "npm:^0.1.2" + use-sync-external-store: "npm:^1.4.0" + utility-types: "npm:^3.11.0" + zustand: "npm:^5.0.1" + peerDependencies: + "@react-three/fiber": ^9.0.0 + react: ^19 + react-dom: ^19 + three: ">=0.159" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10c0/b4103a79f2cebfd6ff00a869de38e602ad670df14bd08c2fa0d44b71ebebe81b000b523077df01df8c358fe406a4e1ff75c44eb727bca216d3407c1d7e91192e + languageName: node + linkType: hard + +"@react-three/fiber@npm:^9.1.2": + version: 9.7.0 + resolution: "@react-three/fiber@npm:9.7.0" + dependencies: + "@babel/runtime": "npm:^7.17.8" + "@types/webxr": "npm:*" + base64-js: "npm:^1.5.1" + buffer: "npm:^6.0.3" + its-fine: "npm:^2.0.0" + react-use-measure: "npm:^2.1.7" + scheduler: "npm:^0.27.0" + suspend-react: "npm:^0.1.3" + use-sync-external-store: "npm:^1.4.0" + zustand: "npm:^5.0.3" + peerDependencies: + expo: ">=43.0" + expo-asset: ">=8.4" + expo-file-system: ">=11.0" + expo-gl: ">=11.0" + react: ">=19 <19.3" + react-dom: ">=19 <19.3" + react-native: ">=0.78" + three: ">=0.156" + peerDependenciesMeta: + expo: + optional: true + expo-asset: + optional: true + expo-file-system: + optional: true + expo-gl: + optional: true + react-dom: + optional: true + react-native: + optional: true + checksum: 10c0/7ad36da135b5105e379c4984455dab1bd7cfabfad7548e4ff87ee161f8265be5d9e9af7f00a761c7f3a4252dc2db7c7ea8b9bfd9db7f601dd9759ee1179dc533 + languageName: node + linkType: hard + +"@rolldown/pluginutils@npm:1.0.0-beta.27": + version: 1.0.0-beta.27 + resolution: "@rolldown/pluginutils@npm:1.0.0-beta.27" + checksum: 10c0/9658f235b345201d4f6bfb1f32da9754ca164f892d1cb68154fe5f53c1df42bd675ecd409836dff46884a7847d6c00bdc38af870f7c81e05bba5c2645eb4ab9c + languageName: node + linkType: hard + +"@rollup/rollup-android-arm-eabi@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.63.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@rollup/rollup-android-arm64@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-android-arm64@npm:4.63.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-arm64@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.63.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-darwin-x64@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.63.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.63.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-freebsd-x64@npm:4.63.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-gnueabihf@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.63.1" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm-musleabihf@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.63.1" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.63.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-musl@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.63.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.63.1" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.63.1" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-gnu@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.63.1" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.63.1" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.63.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.63.1" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-s390x-gnu@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.63.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.63.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-musl@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.63.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-openbsd-x64@npm:4.63.1" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.63.1" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-arm64-msvc@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.63.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-ia32-msvc@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.63.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.63.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-msvc@npm:4.63.1": + version: 4.63.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.63.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@socket.io/component-emitter@npm:~3.1.0": + version: 3.1.2 + resolution: "@socket.io/component-emitter@npm:3.1.2" + checksum: 10c0/c4242bad66f67e6f7b712733d25b43cbb9e19a595c8701c3ad99cbeb5901555f78b095e24852f862fffb43e96f1d8552e62def885ca82ae1bb05da3668fd87d7 + languageName: node + linkType: hard + +"@tweenjs/tween.js@npm:~23.1.3": + version: 23.1.3 + resolution: "@tweenjs/tween.js@npm:23.1.3" + checksum: 10c0/811b30f5f0e7409fb41833401c501c2d6f600eb5f43039dd9067a7f70aff6dad5f5ce1233848e13f0b33a269a160d9c133f344d986cbff4f1f6b72ddecd06c89 + languageName: node + linkType: hard + +"@types/babel__core@npm:^7.20.5": + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" + dependencies: + "@babel/parser": "npm:^7.20.7" + "@babel/types": "npm:^7.20.7" + "@types/babel__generator": "npm:*" + "@types/babel__template": "npm:*" + "@types/babel__traverse": "npm:*" + checksum: 10c0/bdee3bb69951e833a4b811b8ee9356b69a61ed5b7a23e1a081ec9249769117fa83aaaf023bb06562a038eb5845155ff663e2d5c75dd95c1d5ccc91db012868ff + languageName: node + linkType: hard + +"@types/babel__generator@npm:*": + version: 7.27.0 + resolution: "@types/babel__generator@npm:7.27.0" + dependencies: + "@babel/types": "npm:^7.0.0" + checksum: 10c0/9f9e959a8792df208a9d048092fda7e1858bddc95c6314857a8211a99e20e6830bdeb572e3587ae8be5429e37f2a96fcf222a9f53ad232f5537764c9e13a2bbd + languageName: node + linkType: hard + +"@types/babel__template@npm:*": + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" + dependencies: + "@babel/parser": "npm:^7.1.0" + "@babel/types": "npm:^7.0.0" + checksum: 10c0/cc84f6c6ab1eab1427e90dd2b76ccee65ce940b778a9a67be2c8c39e1994e6f5bbc8efa309f6cea8dc6754994524cd4d2896558df76d92e7a1f46ecffee7112b + languageName: node + linkType: hard + +"@types/babel__traverse@npm:*": + version: 7.28.0 + resolution: "@types/babel__traverse@npm:7.28.0" + dependencies: + "@babel/types": "npm:^7.28.2" + checksum: 10c0/b52d7d4e8fc6a9018fe7361c4062c1c190f5778cf2466817cb9ed19d69fbbb54f9a85ffedeb748ed8062d2cf7d4cc088ee739848f47c57740de1c48cbf0d0994 + languageName: node + linkType: hard + +"@types/cors@npm:^2.8.12": + version: 2.8.19 + resolution: "@types/cors@npm:2.8.19" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/b5dd407040db7d8aa1bd36e79e5f3f32292f6b075abc287529e9f48df1a25fda3e3799ba30b4656667ffb931d3b75690c1d6ca71e39f7337ea6dfda8581916d0 + languageName: node + linkType: hard + +"@types/crypto-js@npm:^4.2.2": + version: 4.2.2 + resolution: "@types/crypto-js@npm:4.2.2" + checksum: 10c0/760a2078f36f2a3a1089ef367b0d13229876adcf4bcd6e8824d00d9e9bfad8118dc7e6a3cc66322b083535e12be3a29044ccdc9603bfb12519ff61551a3322c6 + languageName: node + linkType: hard + +"@types/draco3d@npm:^1.4.0": + version: 1.4.10 + resolution: "@types/draco3d@npm:1.4.10" + checksum: 10c0/431e333b2fd67e2b081e8697e71dfb82a125fc04b2cbfa0205e4d521719d3749c964b2bf82bb7944ba0a12e5bc3d9afe387a58e68b195d838fcdbc65c8572a35 + languageName: node + linkType: hard + +"@types/estree@npm:1.0.9, @types/estree@npm:^1.0.6": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + +"@types/history@npm:^4.7.11": + version: 4.7.11 + resolution: "@types/history@npm:4.7.11" + checksum: 10c0/3facf37c2493d1f92b2e93a22cac7ea70b06351c2ab9aaceaa3c56aa6099fb63516f6c4ec1616deb5c56b4093c026a043ea2d3373e6c0644d55710364d02c934 + languageName: node + linkType: hard + +"@types/js-yaml@npm:^4.0.9": + version: 4.0.9 + resolution: "@types/js-yaml@npm:4.0.9" + checksum: 10c0/24de857aa8d61526bbfbbaa383aa538283ad17363fcd5bb5148e2c7f604547db36646440e739d78241ed008702a8920665d1add5618687b6743858fae00da211 + languageName: node + linkType: hard + +"@types/json-schema@npm:^7.0.15": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db + languageName: node + linkType: hard + +"@types/node@npm:*, @types/node@npm:>=10.0.0": + version: 26.4.0 + resolution: "@types/node@npm:26.4.0" + dependencies: + undici-types: "npm:~8.3.0" + checksum: 10c0/e6fc94ea3b58fb8040b38c465dec1c53c1fd9d2c092c81f699d28799726baf59f12e1600318e79e8e6f985bb919dab6ae820180b942b8e38c51acaad63e8aada + languageName: node + linkType: hard + +"@types/node@npm:^22.15.30": + version: 22.20.1 + resolution: "@types/node@npm:22.20.1" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/f2ba54d3d1fb92e1c57c78d32c3a17655b1e87363b707136f55c422b4838d4054901ce5d27f75bb0e5ecb7ebfee3804e0987822d22b473473008091857a09353 + languageName: node + linkType: hard + +"@types/offscreencanvas@npm:^2019.6.4": + version: 2019.7.3 + resolution: "@types/offscreencanvas@npm:2019.7.3" + checksum: 10c0/6d1dfae721d321cd2b5435f347a0e53b09f33b2f9e9333396480f592823bc323847b8169f7d251d2285cb93dbc1ba2e30741ac5cf4b1c003d660fd4c24526963 + languageName: node + linkType: hard + +"@types/react-dom@npm:^19.1.2": + version: 19.2.5 + resolution: "@types/react-dom@npm:19.2.5" + peerDependencies: + "@types/react": ^19.2.0 + checksum: 10c0/7a59467b043debf392d109daa1ba672e791f20ce6f24ae81fb54715f890f8119d24967e61e3644b6307428603b695b7d59e69eaf531bd1963a74a6cb462f5f49 + languageName: node + linkType: hard + +"@types/react-reconciler@npm:^0.28.9": + version: 0.28.9 + resolution: "@types/react-reconciler@npm:0.28.9" + peerDependencies: + "@types/react": "*" + checksum: 10c0/9fe71fa856aab2cd4742fe0416bdd4f5c82ecc05ef6451ee7fcb65a5efdf5fa588f5820fbe2a665b15371b0da3bfc4097f28bb6d450b9a834af2d0fc00f403bd + languageName: node + linkType: hard + +"@types/react-router-dom@npm:^5.3.3": + version: 5.3.3 + resolution: "@types/react-router-dom@npm:5.3.3" + dependencies: + "@types/history": "npm:^4.7.11" + "@types/react": "npm:*" + "@types/react-router": "npm:*" + checksum: 10c0/a9231a16afb9ed5142678147eafec9d48582809295754fb60946e29fcd3757a4c7a3180fa94b45763e4c7f6e3f02379e2fcb8dd986db479dcab40eff5fc62a91 + languageName: node + linkType: hard + +"@types/react-router@npm:*": + version: 5.1.20 + resolution: "@types/react-router@npm:5.1.20" + dependencies: + "@types/history": "npm:^4.7.11" + "@types/react": "npm:*" + checksum: 10c0/1f7eee61981d2f807fa01a34a0ef98ebc0774023832b6611a69c7f28fdff01de5a38cabf399f32e376bf8099dcb7afaf724775bea9d38870224492bea4cb5737 + languageName: node + linkType: hard + +"@types/react@npm:*, @types/react@npm:^19.1.2": + version: 19.2.18 + resolution: "@types/react@npm:19.2.18" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/d04216172b4b4362b310017c210dfbe019fb4f4e7dffd0313e70b3acb3051d5c3e76e7e84e3cf4c6a3c824993ffadfe3949e589a6398a09d4200e7670e2962de + languageName: node + linkType: hard + +"@types/roslib@npm:^1.3.5": + version: 1.9.0 + resolution: "@types/roslib@npm:1.9.0" + checksum: 10c0/5b4217e7d1b121b1ab9ad21c497b2827822cb00e248be8c5d02d03dd7602e83919bc4c150b2718cbf0bdd94c3be76ace4ab23baf578c3cfc36f38e6800a4fed2 + languageName: node + linkType: hard + +"@types/stats.js@npm:*": + version: 0.17.4 + resolution: "@types/stats.js@npm:0.17.4" + checksum: 10c0/4fe0429998519f0476f03a25b4900b4d4a1474606478657271e40a884f7936ba902ea564b1c95cfd33a8e84af46cef6e1e98bb23e86fd3b6676cd5b974987151 + languageName: node + linkType: hard + +"@types/three@npm:*": + version: 0.185.4 + resolution: "@types/three@npm:0.185.4" + dependencies: + "@dimforge/rapier3d-compat": "npm:~0.12.0" + "@tweenjs/tween.js": "npm:~23.1.3" + "@types/stats.js": "npm:*" + "@types/webxr": "npm:>=0.5.17" + fflate: "npm:~0.8.2" + meshoptimizer: "npm:~1.1.1" + checksum: 10c0/f9cc28d3f343067e56741017bb0ac0257d8d2994707c2ee69512b668a57c8cd8892798978e54b2224920e9495eebe0878fcd8ea18b9a5ab555a7d445c689f804 + languageName: node + linkType: hard + +"@types/webxr@npm:*, @types/webxr@npm:>=0.5.17, @types/webxr@npm:^0.5.2": + version: 0.5.24 + resolution: "@types/webxr@npm:0.5.24" + checksum: 10c0/ff59ffd390d06ca3f89ab2531d583ac10bc5e2ab82e5a01ecc40fbc365224a3375e7caa5b14649cf6141db21fb024940da7ad2bd8faa3cd497a6665257cb53b5 + languageName: node + linkType: hard + +"@types/ws@npm:^8.5.12": + version: 8.18.1 + resolution: "@types/ws@npm:8.18.1" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/61aff1129143fcc4312f083bc9e9e168aa3026b7dd6e70796276dcfb2c8211c4292603f9c4864fae702f2ed86e4abd4d38aa421831c2fd7f856c931a481afbab + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.68.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.12.2" + "@typescript-eslint/scope-manager": "npm:8.68.0" + "@typescript-eslint/type-utils": "npm:8.68.0" + "@typescript-eslint/utils": "npm:8.68.0" + "@typescript-eslint/visitor-keys": "npm:8.68.0" + ignore: "npm:^7.0.5" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + "@typescript-eslint/parser": ^8.68.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/d8a363761a59af6f537f2b30ff10b95c96ddb300dd192c091c695ae3c0c30f646f7618415d058e1e5430663d646f4f76b4e636ce0fe6b533d7768f1f6880c43c + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/parser@npm:8.68.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.68.0" + "@typescript-eslint/types": "npm:8.68.0" + "@typescript-eslint/typescript-estree": "npm:8.68.0" + "@typescript-eslint/visitor-keys": "npm:8.68.0" + debug: "npm:^4.4.3" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/852082bc5bf405c9780e683c9779ff855dbe142bbb4ea27f12a37aa643bab8b08157987c75545bd2888a129eda2636cbd60ae6e50f4926873d12c31462d872bd + languageName: node + linkType: hard + +"@typescript-eslint/project-service@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/project-service@npm:8.68.0" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.68.0" + "@typescript-eslint/types": "npm:^8.68.0" + debug: "npm:^4.4.3" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/0db01ba39f5051f646a4917a228bca09821e771449576ae13958ed61fe56af597321bfdb8aa553bde7cdd0c745f0bb332ae503996f8248f8de3fa299c304391d + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/scope-manager@npm:8.68.0" + dependencies: + "@typescript-eslint/types": "npm:8.68.0" + "@typescript-eslint/visitor-keys": "npm:8.68.0" + checksum: 10c0/8e96017c966a559c5add0a6ce3916b8b6bff0ef823785c7ffdaf34a31a89cc0745432b7d2f4058a8ef9c3350ed86fc8a0e63a1bd99473ae8cb934c047dad906c + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.68.0, @typescript-eslint/tsconfig-utils@npm:^8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.68.0" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/7a3e025ed292ea11333f645b95ae60da9db38bded295ddccc1a66fb960b528f83c5abd5c0df54d2bd7eec8c05f83ed0b600379256129a0b594311b589ea31c74 + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/type-utils@npm:8.68.0" + dependencies: + "@typescript-eslint/types": "npm:8.68.0" + "@typescript-eslint/typescript-estree": "npm:8.68.0" + "@typescript-eslint/utils": "npm:8.68.0" + debug: "npm:^4.4.3" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/da9da01d82da86a391ecfd682c85502c0fc82d4c83ebfe2c28b18fca121b34cd96d17235fda57d4ff71589ada451bd22fb8f4b85a06a3aac3f95ccc0cb582ae7 + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.68.0, @typescript-eslint/types@npm:^8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/types@npm:8.68.0" + checksum: 10c0/c7e64094de90ff81a53572651e670f7566163df8a14ffd6c5822212d8412db590976a9a2aaed965b390cefe389e9ec3d498862127212ca840a4a80527dae6972 + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.68.0" + dependencies: + "@typescript-eslint/project-service": "npm:8.68.0" + "@typescript-eslint/tsconfig-utils": "npm:8.68.0" + "@typescript-eslint/types": "npm:8.68.0" + "@typescript-eslint/visitor-keys": "npm:8.68.0" + debug: "npm:^4.4.3" + minimatch: "npm:^10.2.2" + semver: "npm:^7.7.3" + tinyglobby: "npm:^0.2.15" + ts-api-utils: "npm:^2.5.0" + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/3211ac5823a48ba805fcdd8a55a8c2e62aef7f1345f5001d9bf8bdb058386d9fccc309a618fcfdc1206acf13202f6b2ac3b88549ed9af7b970f2ff479670197d + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/utils@npm:8.68.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.9.1" + "@typescript-eslint/scope-manager": "npm:8.68.0" + "@typescript-eslint/types": "npm:8.68.0" + "@typescript-eslint/typescript-estree": "npm:8.68.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/0fb018f5f769b88a3e270cacfeb1075828d04a114cb29ae7f97fcc5e58d1c44a675ba09f2c6c7da394ab164a9a826c4fc14d39e2181d367ac2bad8b6d72e0c25 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:8.68.0": + version: 8.68.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.68.0" + dependencies: + "@typescript-eslint/types": "npm:8.68.0" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10c0/8ca526122b46dc035752ae9715ad0d8b387bca60c885d7bba39f99471af8d1ec0918dd0921dae2d3c6c865bafd509e77693b9e37d48380d3483d54c7dfcc6ffe + languageName: node + linkType: hard + +"@use-gesture/core@npm:10.3.1": + version: 10.3.1 + resolution: "@use-gesture/core@npm:10.3.1" + checksum: 10c0/2e3b5c0f7fe26cdb47be3a9c2a58a6a9edafc5b2895b07d2898eda9ab5a2b29fb0098b15597baa0856907b593075cd44cc69bba4785c9cfb7b6fabaa3b52cd3e + languageName: node + linkType: hard + +"@use-gesture/react@npm:^10.3.1": + version: 10.3.1 + resolution: "@use-gesture/react@npm:10.3.1" + dependencies: + "@use-gesture/core": "npm:10.3.1" + peerDependencies: + react: ">= 16.8.0" + checksum: 10c0/978da66e4e7c424866ad52eba8fdf0ce93a4c8fc44f8837c7043e68c6a6107cd67e817fffb27f7db2ae871ef2f6addb0c8ddf1586f24c67b7e6aef1646c668cf + languageName: node + linkType: hard + +"@vitejs/plugin-react@npm:^4.4.1": + version: 4.7.0 + resolution: "@vitejs/plugin-react@npm:4.7.0" + dependencies: + "@babel/core": "npm:^7.28.0" + "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1" + "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1" + "@rolldown/pluginutils": "npm:1.0.0-beta.27" + "@types/babel__core": "npm:^7.20.5" + react-refresh: "npm:^0.17.0" + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + checksum: 10c0/692f23960972879485d647713663ec299c478222c96567d60285acf7c7dc5c178e71abfe9d2eefddef1eeb01514dacbc2ed68aad84628debf9c7116134734253 + languageName: node + linkType: hard + +"@xmldom/xmldom@npm:^0.8.0": + version: 0.8.15 + resolution: "@xmldom/xmldom@npm:0.8.15" + checksum: 10c0/59d09b85824b684fba5a0a224d5b43abea1a0d745e8d8b8136a7013e996eb6da122fdbb3deb32ed1621923a10e1937b1f7917cea43481d9d5b976e8c1ff836c1 + languageName: node + linkType: hard + +"abbrev@npm:^5.0.0": + version: 5.0.0 + resolution: "abbrev@npm:5.0.0" + checksum: 10c0/8e88f5c798ea4562d28c5a3e9ad69e3879890bc5d695d8f2dffb8609be4c890aacc8f80ef4553fdd2c6a62d70c2ce8bc57b38074e383beb7487bdafa9ed42ea5 + languageName: node + linkType: hard + +"accepts@npm:~1.3.4": + version: 1.3.8 + resolution: "accepts@npm:1.3.8" + dependencies: + mime-types: "npm:~2.1.34" + negotiator: "npm:0.6.3" + checksum: 10c0/3a35c5f5586cfb9a21163ca47a5f77ac34fa8ceb5d17d2fa2c0d81f41cbd7f8c6fa52c77e2c039acc0f4d09e71abdc51144246900f6bef5e3c4b333f77d89362 + languageName: node + linkType: hard + +"acorn-jsx@npm:^5.3.2": + version: 5.3.2 + resolution: "acorn-jsx@npm:5.3.2" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10c0/4c54868fbef3b8d58927d5e33f0a4de35f59012fe7b12cf9dfbb345fb8f46607709e1c4431be869a23fb63c151033d84c4198fa9f79385cec34fcb1dd53974c1 + languageName: node + linkType: hard + +"acorn@npm:^8.15.0": + version: 8.18.0 + resolution: "acorn@npm:8.18.0" + bin: + acorn: bin/acorn + checksum: 10c0/be771be2135cc07910cf76f444ad514d7dcfd6d4a8026e597e93155275abc8ef61eee12211d52146e9d962874269b634f397464942087be013c89d0c54c5f8e5 + languageName: node + linkType: hard + +"agent-base@npm:6": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: "npm:4" + checksum: 10c0/dc4f757e40b5f3e3d674bc9beb4f1048f4ee83af189bae39be99f57bf1f48dde166a8b0a5342a84b5944ee8e6ed1e5a9d801858f4ad44764e84957122fe46261 + languageName: node + linkType: hard + +"ajv@npm:^6.14.0": + version: 6.15.0 + resolution: "ajv@npm:6.15.0" + dependencies: + fast-deep-equal: "npm:^3.1.1" + fast-json-stable-stringify: "npm:^2.0.0" + json-schema-traverse: "npm:^0.4.1" + uri-js: "npm:^4.2.2" + checksum: 10c0/67966499dd272ecde1c2e467084411132891523d057487587879d39ac04207f4351b7b2324c83198013967fbfa632c1612adc960114a30770fbe07a0773b32c2 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"antd@npm:^5.25.4": + version: 5.29.3 + resolution: "antd@npm:5.29.3" + dependencies: + "@ant-design/colors": "npm:^7.2.1" + "@ant-design/cssinjs": "npm:^1.23.0" + "@ant-design/cssinjs-utils": "npm:^1.1.3" + "@ant-design/fast-color": "npm:^2.0.6" + "@ant-design/icons": "npm:^5.6.1" + "@ant-design/react-slick": "npm:~1.1.2" + "@babel/runtime": "npm:^7.26.0" + "@rc-component/color-picker": "npm:~2.0.1" + "@rc-component/mutate-observer": "npm:^1.1.0" + "@rc-component/qrcode": "npm:~1.1.0" + "@rc-component/tour": "npm:~1.15.1" + "@rc-component/trigger": "npm:^2.3.0" + classnames: "npm:^2.5.1" + copy-to-clipboard: "npm:^3.3.3" + dayjs: "npm:^1.11.11" + rc-cascader: "npm:~3.34.0" + rc-checkbox: "npm:~3.5.0" + rc-collapse: "npm:~3.9.0" + rc-dialog: "npm:~9.6.0" + rc-drawer: "npm:~7.3.0" + rc-dropdown: "npm:~4.2.1" + rc-field-form: "npm:~2.7.1" + rc-image: "npm:~7.12.0" + rc-input: "npm:~1.8.0" + rc-input-number: "npm:~9.5.0" + rc-mentions: "npm:~2.20.0" + rc-menu: "npm:~9.16.1" + rc-motion: "npm:^2.9.5" + rc-notification: "npm:~5.6.4" + rc-pagination: "npm:~5.1.0" + rc-picker: "npm:~4.11.3" + rc-progress: "npm:~4.0.0" + rc-rate: "npm:~2.13.1" + rc-resize-observer: "npm:^1.4.3" + rc-segmented: "npm:~2.7.0" + rc-select: "npm:~14.16.8" + rc-slider: "npm:~11.1.9" + rc-steps: "npm:~6.0.1" + rc-switch: "npm:~4.1.0" + rc-table: "npm:~7.54.0" + rc-tabs: "npm:~15.7.0" + rc-textarea: "npm:~1.10.2" + rc-tooltip: "npm:~6.4.0" + rc-tree: "npm:~5.13.1" + rc-tree-select: "npm:~5.27.0" + rc-upload: "npm:~4.11.0" + rc-util: "npm:^5.44.4" + scroll-into-view-if-needed: "npm:^3.1.0" + throttle-debounce: "npm:^5.0.2" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/e2e488dfd51f0a7c1e5ef2aa51ab6809b0015151d338dcdd27a7bef6f7ac9f7644e73693b37d0213089f6aa02718090ceba957966c76c387b258c789b3ca28fd + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73 + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10c0/2c50ef856c543ad500d8d8777d347e3c1ba623b93e99c9263ecc5f965c1b12d2a140e2ab6e43c3d0b85366110696f28114649411cbcd10b452a92a2318394186 + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d + languageName: node + linkType: hard + +"axios@npm:^1.9.0": + version: 1.20.0 + resolution: "axios@npm:1.20.0" + dependencies: + follow-redirects: "npm:^1.16.0" + form-data: "npm:^4.0.6" + https-proxy-agent: "npm:^5.0.1" + proxy-from-env: "npm:^2.1.0" + checksum: 10c0/976088acf532286356f4c2e65df1ef47ba7da3936d83e928d0d64357bbf5370456abd3eb5826c15df322335fcb1fe200d4a84b4fcf20ffccdd63b40d80fa495c + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"balanced-match@npm:^4.0.2": + version: 4.0.4 + resolution: "balanced-match@npm:4.0.4" + checksum: 10c0/07e86102a3eb2ee2a6a1a89164f29d0dbaebd28f2ca3f5ca786f36b8b23d9e417eb3be45a4acf754f837be5ac0a2317de90d3fcb7f4f4dc95720a1f36b26a17b + languageName: node + linkType: hard + +"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + +"base64id@npm:2.0.0, base64id@npm:~2.0.0": + version: 2.0.0 + resolution: "base64id@npm:2.0.0" + checksum: 10c0/6919efd237ed44b9988cbfc33eca6f173a10e810ce50292b271a1a421aac7748ef232a64d1e6032b08f19aae48dce6ee8f66c5ae2c9e5066c82b884861d4d453 + languageName: node + linkType: hard + +"baseline-browser-mapping@npm:^2.11.12, baseline-browser-mapping@npm:^2.9.3": + version: 2.11.20 + resolution: "baseline-browser-mapping@npm:2.11.20" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/67588b7edafa4e6c52996ec4b96b6e007312961cefc5a179cb49232f6681a38ba68d4d43990081e9d0e1dcadcbd28f13105ee7dc3e43b267385d0beb14824ecc + languageName: node + linkType: hard + +"bidi-js@npm:^1.0.2": + version: 1.0.3 + resolution: "bidi-js@npm:1.0.3" + dependencies: + require-from-string: "npm:^2.0.2" + checksum: 10c0/fdddea4aa4120a34285486f2267526cd9298b6e8b773ad25e765d4f104b6d7437ab4ba542e6939e3ac834a7570bcf121ee2cf6d3ae7cd7082c4b5bedc8f271e1 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.18 + resolution: "brace-expansion@npm:1.1.18" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10c0/3432c18a9e2ebf94162d4effb62198bd0adea06a9f332b2c0188df5d5e30b1e51ea3c848b6608e47d0b857ebe1ea5b3888ed3326dd3c4f6f9645c94153cf9c14 + languageName: node + linkType: hard + +"brace-expansion@npm:^5.0.8": + version: 5.0.9 + resolution: "brace-expansion@npm:5.0.9" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/3dea38884a1c3c8b1c9c44a7402a0c76fca460f70cffb3127242b0b4cbf4472019e022ade021eec44838ff19f1dac2625dfd11dd459d7e1e055b0698a8d52fec + languageName: node + linkType: hard + +"browserslist@npm:^4.24.0": + version: 4.28.8 + resolution: "browserslist@npm:4.28.8" + dependencies: + baseline-browser-mapping: "npm:^2.11.12" + caniuse-lite: "npm:^1.0.30001809" + electron-to-chromium: "npm:^1.5.402" + node-releases: "npm:^2.0.53" + update-browserslist-db: "npm:^1.3.0" + bin: + browserslist: cli.js + checksum: 10c0/047dd517c8d1f1f56a253d752c3127b8ad01c58e4cbb7e21cb5cd07ebd13e083a2237c3afb13eb60674282c9132bd4534aea2c3ff7c6f7d29be0687a00cbd537 + languageName: node + linkType: hard + +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0 + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 + languageName: node + linkType: hard + +"camera-controls@npm:^3.1.0": + version: 3.1.2 + resolution: "camera-controls@npm:3.1.2" + peerDependencies: + three: ">=0.126.1" + checksum: 10c0/2d42162efd6789e713332a72212819fb04b261f7babc45b5794cd902997e4a4826c9401b672ffab2e09f9808fc2b615d9154d67c57f38a7c90487d4cf5429655 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001809": + version: 1.0.30001810 + resolution: "caniuse-lite@npm:1.0.30001810" + checksum: 10c0/d29bc73f33c888025d54c7ff2984372d3b350b15a6a9e174884fb40976175e9177f202e2a72c2d428dddfc032784bc8ba635a31d168fcab9fac65756d7b3d6b4 + languageName: node + linkType: hard + +"cbor-js@npm:^0.1.0": + version: 0.1.0 + resolution: "cbor-js@npm:0.1.0" + checksum: 10c0/1204d0eba63ef41546f622175663fad91c681cc9e7cb4e3f09e8be4081b7ecc0ce07b3f2a83004124bdb74c1824693eebf50991e092f6ca7a4bfcf1bd93a785d + languageName: node + linkType: hard + +"chalk@npm:^4.0.0": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"classnames@npm:2.x, classnames@npm:^2.2.1, classnames@npm:^2.2.3, classnames@npm:^2.2.5, classnames@npm:^2.2.6, classnames@npm:^2.3.1, classnames@npm:^2.3.2, classnames@npm:^2.5.1": + version: 2.5.1 + resolution: "classnames@npm:2.5.1" + checksum: 10c0/afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69 + languageName: node + linkType: hard + +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: "npm:~1.0.0" + checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5 + languageName: node + linkType: hard + +"compute-scroll-into-view@npm:^3.0.2": + version: 3.1.1 + resolution: "compute-scroll-into-view@npm:3.1.1" + checksum: 10c0/59761ed62304a9599b52ad75d0d6fbf0669ee2ab7dd472fdb0ad9da36628414c014dea7b5810046560180ad30ffec52a953d19297f66a1d4f3aa0999b9d2521d + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f + languageName: node + linkType: hard + +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + +"cookie@npm:^1.0.1": + version: 1.1.1 + resolution: "cookie@npm:1.1.1" + checksum: 10c0/79c4ddc0fcad9c4f045f826f42edf54bcc921a29586a4558b0898277fa89fb47be95bc384c2253f493af7b29500c830da28341274527328f18eba9f58afa112c + languageName: node + linkType: hard + +"cookie@npm:~0.7.2": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10c0/9596e8ccdbf1a3a88ae02cf5ee80c1c50959423e1022e4e60b91dd87c622af1da309253d8abdb258fb5e3eacb4f08e579dc58b4897b8087574eee0fd35dfa5d2 + languageName: node + linkType: hard + +"copy-to-clipboard@npm:^3.3.3": + version: 3.3.3 + resolution: "copy-to-clipboard@npm:3.3.3" + dependencies: + toggle-selection: "npm:^1.0.6" + checksum: 10c0/3ebf5e8ee00601f8c440b83ec08d838e8eabb068c1fae94a9cda6b42f288f7e1b552f3463635f419af44bf7675afc8d0390d30876cf5c2d5d35f86d9c56a3e5f + languageName: node + linkType: hard + +"cors@npm:~2.8.5": + version: 2.8.6 + resolution: "cors@npm:2.8.6" + dependencies: + object-assign: "npm:^4" + vary: "npm:^1" + checksum: 10c0/ab2bc57b8af8ef8476682a59647f7c55c1a7d406b559ac06119aa1c5f70b96d35036864d197b24cf86e228e4547231088f1f94ca05061dbb14d89cc0bc9d4cab + languageName: node + linkType: hard + +"cross-env@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-env@npm:7.0.3" + dependencies: + cross-spawn: "npm:^7.0.1" + bin: + cross-env: src/bin/cross-env.js + cross-env-shell: src/bin/cross-env-shell.js + checksum: 10c0/f3765c25746c69fcca369655c442c6c886e54ccf3ab8c16847d5ad0e91e2f337d36eedc6599c1227904bf2a228d721e690324446876115bc8e7b32a866735ecf + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + +"crypto-js@npm:^4.2.0": + version: 4.2.0 + resolution: "crypto-js@npm:4.2.0" + checksum: 10c0/8fbdf9d56f47aea0794ab87b0eb9833baf80b01a7c5c1b0edc7faf25f662fb69ab18dc2199e2afcac54670ff0cd9607a9045a3f7a80336cccd18d77a55b9fdf0 + languageName: node + linkType: hard + +"csstype@npm:^3.1.3, csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce + languageName: node + linkType: hard + +"dayjs@npm:^1.11.11": + version: 1.11.23 + resolution: "dayjs@npm:1.11.23" + checksum: 10c0/69ab04bf19c676e44ab50cc2fca223d265f3dafd107151ef3b0f3ad74d360c37caa602abe62f87cb25d90bd9f6bee003698af47df5b0dbf10a998b7b5f3bb3f1 + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.4.3, debug@npm:~4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"deep-is@npm:^0.1.3": + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19 + languageName: node + linkType: hard + +"detect-gpu@npm:^5.0.56": + version: 5.0.70 + resolution: "detect-gpu@npm:5.0.70" + dependencies: + webgl-constants: "npm:^1.1.1" + checksum: 10c0/5a2053d4779edb3490821599b316b5362c8b55c30b82806c64bcfd21f40c67e2debfd677937899080aea2b27765471552bafeeae9c9f1cf63bdd07461a7e2163 + languageName: node + linkType: hard + +"draco3d@npm:^1.4.1": + version: 1.5.7 + resolution: "draco3d@npm:1.5.7" + checksum: 10c0/4419509bb93c31560a22a1a54e1c394a5b5017cab39941120c75151d941c11dec05925abf31f597ac2694c570b78c04f82aa3d69e5311f8f8e71fc8b9d92d12f + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.5.402": + version: 1.5.416 + resolution: "electron-to-chromium@npm:1.5.416" + checksum: 10c0/d69c0fef4fc18f4a01f7c1dfda2e7e648e4e8c6e60072dde693643a9725ba19c99353d0f5f6ba75a214c7fd7410817a518342281ee638461accb6cf24aa254ca + languageName: node + linkType: hard + +"engine.io-parser@npm:~5.2.1": + version: 5.2.3 + resolution: "engine.io-parser@npm:5.2.3" + checksum: 10c0/ed4900d8dbef470ab3839ccf3bfa79ee518ea8277c7f1f2759e8c22a48f64e687ea5e474291394d0c94f84054749fd93f3ef0acb51fa2f5f234cc9d9d8e7c536 + languageName: node + linkType: hard + +"engine.io@npm:~6.6.0": + version: 6.6.9 + resolution: "engine.io@npm:6.6.9" + dependencies: + "@types/cors": "npm:^2.8.12" + "@types/node": "npm:>=10.0.0" + "@types/ws": "npm:^8.5.12" + accepts: "npm:~1.3.4" + base64id: "npm:2.0.0" + cookie: "npm:~0.7.2" + cors: "npm:~2.8.5" + debug: "npm:~4.4.1" + engine.io-parser: "npm:~5.2.1" + ws: "npm:~8.21.0" + checksum: 10c0/8e4a64b6e3861ffc3ab3fa7f864fd06358b918b85696c5fb53113ddde6661bb2f31f2d091a2613511ff16dbb0bff970a166e5c2dfaf2be51a022f7e6573b1153 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.2 + resolution: "es-object-atoms@npm:1.1.2" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/1772861f094f739d6f41b579cfb9a18579daffeb434552a370a5fbef50a32d22227e27b63fdbb757b7ddd429d1b42fe52ccae7966d9302a2ec221b6f1b41bbc4 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af + languageName: node + linkType: hard + +"esbuild@npm:^0.21.3": + version: 0.21.5 + resolution: "esbuild@npm:0.21.5" + dependencies: + "@esbuild/aix-ppc64": "npm:0.21.5" + "@esbuild/android-arm": "npm:0.21.5" + "@esbuild/android-arm64": "npm:0.21.5" + "@esbuild/android-x64": "npm:0.21.5" + "@esbuild/darwin-arm64": "npm:0.21.5" + "@esbuild/darwin-x64": "npm:0.21.5" + "@esbuild/freebsd-arm64": "npm:0.21.5" + "@esbuild/freebsd-x64": "npm:0.21.5" + "@esbuild/linux-arm": "npm:0.21.5" + "@esbuild/linux-arm64": "npm:0.21.5" + "@esbuild/linux-ia32": "npm:0.21.5" + "@esbuild/linux-loong64": "npm:0.21.5" + "@esbuild/linux-mips64el": "npm:0.21.5" + "@esbuild/linux-ppc64": "npm:0.21.5" + "@esbuild/linux-riscv64": "npm:0.21.5" + "@esbuild/linux-s390x": "npm:0.21.5" + "@esbuild/linux-x64": "npm:0.21.5" + "@esbuild/netbsd-x64": "npm:0.21.5" + "@esbuild/openbsd-x64": "npm:0.21.5" + "@esbuild/sunos-x64": "npm:0.21.5" + "@esbuild/win32-arm64": "npm:0.21.5" + "@esbuild/win32-ia32": "npm:0.21.5" + "@esbuild/win32-x64": "npm:0.21.5" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/fa08508adf683c3f399e8a014a6382a6b65542213431e26206c0720e536b31c09b50798747c2a105a4bbba1d9767b8d3615a74c2f7bf1ddf6d836cd11eb672de + languageName: node + linkType: hard + +"escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 + languageName: node + linkType: hard + +"eslint-plugin-react-hooks@npm:^5.2.0": + version: 5.2.0 + resolution: "eslint-plugin-react-hooks@npm:5.2.0" + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + checksum: 10c0/1c8d50fa5984c6dea32470651807d2922cc3934cf3425e78f84a24c2dfd972e7f019bee84aefb27e0cf2c13fea0ac1d4473267727408feeb1c56333ca1489385 + languageName: node + linkType: hard + +"eslint-plugin-react-refresh@npm:^0.4.19": + version: 0.4.26 + resolution: "eslint-plugin-react-refresh@npm:0.4.26" + peerDependencies: + eslint: ">=8.40" + checksum: 10c0/11c2b25b7a7025e621b02970c4cf3815b0b77486027df9f8bb731cc52972156804fd163b0f99404b33e36a3c60cd1a1be8199ba64c66b5276da3173bbb5ab6e7 + languageName: node + linkType: hard + +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^5.0.0": + version: 5.0.1 + resolution: "eslint-visitor-keys@npm:5.0.1" + checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678 + languageName: node + linkType: hard + +"eslint@npm:^9.25.0": + version: 9.39.5 + resolution: "eslint@npm:9.39.5" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.8.0" + "@eslint-community/regexpp": "npm:^4.12.1" + "@eslint/config-array": "npm:^0.21.2" + "@eslint/config-helpers": "npm:^0.4.2" + "@eslint/core": "npm:^0.17.0" + "@eslint/eslintrc": "npm:^3.3.6" + "@eslint/js": "npm:9.39.5" + "@eslint/plugin-kit": "npm:^0.4.1" + "@humanfs/node": "npm:^0.16.6" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@humanwhocodes/retry": "npm:^0.4.2" + "@types/estree": "npm:^1.0.6" + ajv: "npm:^6.14.0" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.6" + debug: "npm:^4.3.2" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" + esquery: "npm:^1.5.0" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^8.0.0" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.5" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + bin: + eslint: bin/eslint.js + checksum: 10c0/46335bfcf180ffea9955b38122b578ac9e075b6220e5695be1c988354ea429b04f5db05edc132aa9019ce9847736e9ca0c9ea6d6cedda3c06209c9b52dbd0fd5 + languageName: node + linkType: hard + +"espree@npm:^10.0.1, espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" + dependencies: + acorn: "npm:^8.15.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b + languageName: node + linkType: hard + +"esquery@npm:^1.5.0": + version: 1.7.0 + resolution: "esquery@npm:1.7.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 10c0/77d5173db450b66f3bc685d11af4c90cffeedb340f34a39af96d43509a335ce39c894fd79233df32d38f5e4e219fa0f7076f6ec90bae8320170ba082c0db4793 + languageName: node + linkType: hard + +"esrecurse@npm:^4.3.0": + version: 4.3.0 + resolution: "esrecurse@npm:4.3.0" + dependencies: + estraverse: "npm:^5.2.0" + checksum: 10c0/81a37116d1408ded88ada45b9fb16dbd26fba3aadc369ce50fcaf82a0bac12772ebd7b24cd7b91fc66786bf2c1ac7b5f196bc990a473efff972f5cb338877cf5 + languageName: node + linkType: hard + +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 10c0/1ff9447b96263dec95d6d67431c5e0771eb9776427421260a3e2f0fdd5d6bd4f8e37a7338f5ad2880c9f143450c9b1e4fc2069060724570a49cf9cf0312bd107 + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 + languageName: node + linkType: hard + +"eventemitter2@npm:^6.4.0": + version: 6.4.9 + resolution: "eventemitter2@npm:6.4.9" + checksum: 10c0/b2adf7d9f1544aa2d95ee271b0621acaf1e309d85ebcef1244fb0ebc7ab0afa6ffd5e371535d0981bc46195ad67fd6ff57a8d1db030584dee69aa5e371a27ea7 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.3 + resolution: "exponential-backoff@npm:3.1.3" + checksum: 10c0/77e3ae682b7b1f4972f563c6dbcd2b0d54ac679e62d5d32f3e5085feba20483cf28bd505543f520e287a56d4d55a28d7874299941faf637e779a1aa5994d1267 + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 + languageName: node + linkType: hard + +"fast-json-stable-stringify@npm:^2.0.0": + version: 2.1.0 + resolution: "fast-json-stable-stringify@npm:2.1.0" + checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b + languageName: node + linkType: hard + +"fast-levenshtein@npm:^2.0.6": + version: 2.0.6 + resolution: "fast-levenshtein@npm:2.0.6" + checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4 + languageName: node + linkType: hard + +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + +"fflate@npm:^0.6.9": + version: 0.6.11 + resolution: "fflate@npm:0.6.11" + checksum: 10c0/5bbf4a7557db6f65b25c7bb12621008c400a116ec1cc65bea78abd75c0c049d8a22fb92f0a59fbf9a7ad1a24a47d2112ce10fd817d08db0e1bf7dc82e780faa1 + languageName: node + linkType: hard + +"fflate@npm:~0.8.2": + version: 0.8.3 + resolution: "fflate@npm:0.8.3" + checksum: 10c0/eab181ca37f5348ae76d4b6f840e0026e30220e33153289ac942222d8b9638237d486507dbcc09878d724095bd354993a2ee48bbee99c8f2c6440d4448719aa7 + languageName: node + linkType: hard + +"file-entry-cache@npm:^8.0.0": + version: 8.0.0 + resolution: "file-entry-cache@npm:8.0.0" + dependencies: + flat-cache: "npm:^4.0.0" + checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638 + languageName: node + linkType: hard + +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: "npm:^6.0.0" + path-exists: "npm:^4.0.0" + checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a + languageName: node + linkType: hard + +"flat-cache@npm:^4.0.0": + version: 4.0.1 + resolution: "flat-cache@npm:4.0.1" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.4" + checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc + languageName: node + linkType: hard + +"flatted@npm:^3.2.9": + version: 3.4.4 + resolution: "flatted@npm:3.4.4" + checksum: 10c0/a3a52a88ea5a4c333e5a1f097dcd87a037fa31c236a77cf46222c2aa4036ef895ab9bc76138a66d9dc22bdb3652128f9598cd26e7439561bf19f67276e2bc37e + languageName: node + linkType: hard + +"follow-redirects@npm:^1.16.0": + version: 1.16.0 + resolution: "follow-redirects@npm:1.16.0" + peerDependenciesMeta: + debug: + optional: true + checksum: 10c0/a1e2900163e6f1b4d1ed5c221b607f41decbab65534c63fe7e287e40a5d552a6496e7d9d7d976fa4ba77b4c51c11e5e9f683f10b43011ea11e442ff128d0e181 + languageName: node + linkType: hard + +"form-data@npm:^4.0.6": + version: 4.0.6 + resolution: "form-data@npm:4.0.6" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.4" + mime-types: "npm:^2.1.35" + checksum: 10c0/43947a77bf0ff45c6ceed789778982d47a3f3e720a74b71721174ebf3310a5f1a8be1d6b38a3ee3688e8a18a2c4273073ec0844cd37efda3eaf46d41c9c318ff + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10c0/8a9f59df0f01cfefafdb3b451b80555e5cf6d76487095db91ac461a0e682e4ff7a9dbce15f4ecec191e53586d59eece01949e05a4b4492879600bbbe8e28d6b8 + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.2.6": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" + dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/9f4ab0cf7efe0fd2c8185f52e6f637e708f3a112610c88869f8f041bb9ecc2ce44bf285dfdbdc6f4f7c277a5b88d8e94a432374d97cca22f3de7fc63795deb5d + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c + languageName: node + linkType: hard + +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: "npm:^4.0.3" + checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 + languageName: node + linkType: hard + +"globals@npm:^14.0.0": + version: 14.0.0 + resolution: "globals@npm:14.0.0" + checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d + languageName: node + linkType: hard + +"globals@npm:^16.0.0": + version: 16.5.0 + resolution: "globals@npm:16.5.0" + checksum: 10c0/615241dae7851c8012f5aa0223005b1ed6607713d6813de0741768bd4ddc39353117648f1a7086b4b0fa45eae733f1c0a0fe369aa4e543bb63f8de8990178ea9 + languageName: node + linkType: hard + +"glsl-noise@npm:^0.0.0": + version: 0.0.0 + resolution: "glsl-noise@npm:0.0.0" + checksum: 10c0/442630e86ed109079f4ed92f3b59d24d0b9da60c03cb8ac9313a056996bf4b5d696b4c6121ee29952e8a19c5cf174a999cfe672464959db36fc75334c3134677 + languageName: node + linkType: hard + +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead + languageName: node + linkType: hard + +"graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"hasown@npm:^2.0.2, hasown@npm:^2.0.4": + version: 2.0.4 + resolution: "hasown@npm:2.0.4" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/2d8de939e270b70618f8cebb69746620db10617dbb495bc66ddad326955ea24d3ca4af133aff3eb7c1853e0218f867bc2b050ec26fe02e3aea58f880ffc5e506 + languageName: node + linkType: hard + +"hls.js@npm:^1.5.17": + version: 1.7.1 + resolution: "hls.js@npm:1.7.1" + checksum: 10c0/316762dd811aaacf9e551a8a635f6b24b07333e9b45cf854f0c1031f3340d91de76b6205de9f92e7b753ce0bcb0dd69489671131071de2419e5bdecbf0d182b6 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^5.0.1": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: "npm:6" + debug: "npm:4" + checksum: 10c0/6dd639f03434003577c62b27cafdb864784ef19b2de430d8ae2a1d45e31c4fd60719e5637b44db1a88a046934307da7089e03d6089ec3ddacc1189d8de8897d1 + languageName: node + linkType: hard + +"ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + +"ignore@npm:^5.2.0": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 + languageName: node + linkType: hard + +"ignore@npm:^7.0.5": + version: 7.0.6 + resolution: "ignore@npm:7.0.6" + checksum: 10c0/fc01ef1d14efbe003439b60538726351e81483d1b6f55bdbb3a4465c6346d9481afad5b350dfbd604ddd7049618ef9093ff26dc147984aabc303c56ba53ea3b5 + languageName: node + linkType: hard + +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: 10c0/f8ba7ede69bee9260241ad078d2d535848745ff5f6995c7c7cb41cfdc9ccc213f66e10fa5afb881f90298b24a3f7344b637b592beb4f54e582770cdce3f1f039 + languageName: node + linkType: hard + +"import-fresh@npm:^3.2.1": + version: 3.3.1 + resolution: "import-fresh@npm:3.3.1" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.3": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-mobile@npm:^5.0.0": + version: 5.0.0 + resolution: "is-mobile@npm:5.0.0" + checksum: 10c0/70b31c3e4489109e02deb9b590e74858aeec7ef775a882d89ec030cb7dbaeb2be1173d7faee495049629e49822d0aba9b20e6653b1e25dd7c9121247cb8829d7 + languageName: node + linkType: hard + +"is-promise@npm:^2.1.0": + version: 2.2.2 + resolution: "is-promise@npm:2.2.2" + checksum: 10c0/2dba959812380e45b3df0fb12e7cb4d4528c989c7abb03ececb1d1fd6ab1cbfee956ca9daa587b9db1d8ac3c1e5738cf217bdb3dfd99df8c691be4c00ae09069 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"isexe@npm:^4.0.0": + version: 4.0.0 + resolution: "isexe@npm:4.0.0" + checksum: 10c0/5884815115bceac452877659a9c7726382531592f43dc29e5d48b7c4100661aed54018cb90bd36cb2eaeba521092570769167acbb95c18d39afdccbcca06c5ce + languageName: node + linkType: hard + +"its-fine@npm:^2.0.0": + version: 2.0.0 + resolution: "its-fine@npm:2.0.0" + dependencies: + "@types/react-reconciler": "npm:^0.28.9" + peerDependencies: + react: ^19.0.0 + checksum: 10c0/1ff1ff3257c0c7eb115c9c26cf0506eb84162edc1a63d3136780d146f7c7833298b240d0fcb46888909773f1a7d16539e0c03f2482cff1a5a502d6436686fe21 + languageName: node + linkType: hard + +"js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.1, js-yaml@npm:^4.3.0": + version: 4.3.2 + resolution: "js-yaml@npm:4.3.2" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/dedd34c2e8fef1d504687f1bc94ed0ee1311a1f78770fa2800352c6d59c635ccf555009d74e9afe529ae62199da2b1ccef30e53dc84dcd8d2d8187c22574bc97 + languageName: node + linkType: hard + +"jsesc@npm:^3.0.2": + version: 3.1.0 + resolution: "jsesc@npm:3.1.0" + bin: + jsesc: bin/jsesc + checksum: 10c0/531779df5ec94f47e462da26b4cbf05eb88a83d9f08aac2ba04206508fc598527a153d08bd462bae82fc78b3eaa1a908e1a4a79f886e9238641c4cdefaf118b1 + languageName: node + linkType: hard + +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 10c0/0d1c91569d9588e7eef2b49b59851f297f3ab93c7b35c7c221e288099322be6b562767d11e4821da500f3219542b9afd2e54c5dc573107c1126ed1080f8e96d7 + languageName: node + linkType: hard + +"json-schema-traverse@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-traverse@npm:0.4.1" + checksum: 10c0/108fa90d4cc6f08243aedc6da16c408daf81793bf903e9fd5ab21983cda433d5d2da49e40711da016289465ec2e62e0324dcdfbc06275a607fe3233fde4942ce + languageName: node + linkType: hard + +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: 10c0/cb168b61fd4de83e58d09aaa6425ef71001bae30d260e2c57e7d09a5fd82223e2f22a042dedaab8db23b7d9ae46854b08bb1f91675a8be11c5cffebef5fb66a5 + languageName: node + linkType: hard + +"json2mq@npm:^0.2.0": + version: 0.2.0 + resolution: "json2mq@npm:0.2.0" + dependencies: + string-convert: "npm:^0.2.0" + checksum: 10c0/fc9e2f2306572522d3e61d246afdf70b56ca9ea32f4ad5924c30949867851ab59c926bd0ffc821ebb54d32f3e82e95225f3906eacdb3e54c1ad49acdadf7e0c7 + languageName: node + linkType: hard + +"json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c + languageName: node + linkType: hard + +"keyv@npm:^4.5.4": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: "npm:3.0.1" + checksum: 10c0/aa52f3c5e18e16bb6324876bb8b59dd02acf782a4b789c7b2ae21107fab95fab3890ed448d4f8dba80ce05391eeac4bfabb4f02a20221342982f806fa2cf271e + languageName: node + linkType: hard + +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: "npm:^1.2.1" + type-check: "npm:~0.4.0" + checksum: 10c0/effb03cad7c89dfa5bd4f6989364bfc79994c2042ec5966cb9b95990e2edee5cd8969ddf42616a0373ac49fac1403437deaf6e9050fbbaa3546093a59b9ac94e + languageName: node + linkType: hard + +"lie@npm:^3.0.2": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: "npm:~3.0.5" + checksum: 10c0/56dd113091978f82f9dc5081769c6f3b947852ecf9feccaf83e14a123bc630c2301439ce6182521e5fbafbde88e88ac38314327a4e0493a1bea7e0699a7af808 + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: "npm:^5.0.0" + checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3 + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: "npm:^3.0.2" + checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 + languageName: node + linkType: hard + +"lucycontrolpanel@workspace:.": + version: 0.0.0-use.local + resolution: "lucycontrolpanel@workspace:." + dependencies: + "@ant-design/icons": "npm:^6.0.0" + "@dnd-kit/core": "npm:^6.3.1" + "@dnd-kit/sortable": "npm:^10.0.0" + "@dnd-kit/utilities": "npm:^3.2.2" + "@eslint/js": "npm:^9.25.0" + "@mediapipe/camera_utils": "npm:^0.3.1675466862" + "@mediapipe/drawing_utils": "npm:^0.3.1675466124" + "@mediapipe/hands": "npm:^0.4.1675469240" + "@mediapipe/tasks-vision": "npm:^0.10.22-rc.20250304" + "@react-three/drei": "npm:^10.1.2" + "@react-three/fiber": "npm:^9.1.2" + "@types/crypto-js": "npm:^4.2.2" + "@types/js-yaml": "npm:^4.0.9" + "@types/node": "npm:^22.15.30" + "@types/react": "npm:^19.1.2" + "@types/react-dom": "npm:^19.1.2" + "@types/react-router-dom": "npm:^5.3.3" + "@types/roslib": "npm:^1.3.5" + "@vitejs/plugin-react": "npm:^4.4.1" + antd: "npm:^5.25.4" + axios: "npm:^1.9.0" + baseline-browser-mapping: "npm:^2.9.3" + crypto-js: "npm:^4.2.0" + eslint: "npm:^9.25.0" + eslint-plugin-react-hooks: "npm:^5.2.0" + eslint-plugin-react-refresh: "npm:^0.4.19" + globals: "npm:^16.0.0" + js-yaml: "npm:^4.1.1" + react: "npm:^19.1.0" + react-dom: "npm:^19.1.0" + react-router-dom: "npm:^7.6.2" + react-webcam: "npm:^7.2.0" + roslib: "npm:^1.4.1" + three: "npm:^0.177.0" + three-stdlib: "npm:^2.36.0" + typescript: "npm:~5.8.3" + typescript-eslint: "npm:^8.30.1" + urdf-loader: "npm:^0.12.7" + vite: "npm:5.4.14" + languageName: unknown + linkType: soft + +"maath@npm:^0.10.8": + version: 0.10.8 + resolution: "maath@npm:0.10.8" + peerDependencies: + "@types/three": ">=0.134.0" + three: ">=0.134.0" + checksum: 10c0/fb61faac89567595f7e25f261f8a9750d0ced674fd16c77c6e486a0c4a00c572fa37969d613d017380f8ea4e8ff357d481dcd94240a104a7969bc7e95b9fe99e + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f + languageName: node + linkType: hard + +"meshline@npm:^3.3.1": + version: 3.3.1 + resolution: "meshline@npm:3.3.1" + peerDependencies: + three: ">=0.137" + checksum: 10c0/95d5c7c90aca350530f37f9ea5c5e333a66ea0c50416fa6372c24a23321837a4c70d92d6a9a5942ee124484855a2d3fc8ca0656305feb8d8f86aa10513e324e4 + languageName: node + linkType: hard + +"meshoptimizer@npm:~1.1.1": + version: 1.1.1 + resolution: "meshoptimizer@npm:1.1.1" + checksum: 10c0/aacd816cbe206e49f16c88ed8b13deafec40cdb8b844080b6d614b4d945dfc7242d5dccfbc1269375a5d4dce2f354261c107d3ce6e564961bd9a220e4616dafb + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa + languageName: node + linkType: hard + +"mime-types@npm:^2.1.35, mime-types@npm:~2.1.34": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 + languageName: node + linkType: hard + +"minimatch@npm:^10.2.2": + version: 10.2.6 + resolution: "minimatch@npm:10.2.6" + dependencies: + brace-expansion: "npm:^5.0.8" + checksum: 10c0/4559a836243b98bd4d17ea9f7edae698717c76399eea7be374f3737f33164e4907f19e9726891ddeb122f750a5a7fa80d2ac43e851d6e5984dc4ff42ec127d3a + languageName: node + linkType: hard + +"minimatch@npm:^3.1.5": + version: 3.1.5 + resolution: "minimatch@npm:3.1.5" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/2ecbdc0d33f07bddb0315a8b5afbcb761307a8778b48f0b312418ccbced99f104a2d17d8aca7573433c70e8ccd1c56823a441897a45e384ea76ef401a26ace70 + languageName: node + linkType: hard + +"minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.3 + resolution: "minipass@npm:7.1.3" + checksum: 10c0/539da88daca16533211ea5a9ee98dc62ff5742f531f54640dd34429e621955e91cc280a91a776026264b7f9f6735947629f920944e9c1558369e8bf22eb33fbb + languageName: node + linkType: hard + +"minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" + dependencies: + minipass: "npm:^7.1.2" + checksum: 10c0/5aad75ab0090b8266069c9aabe582c021ae53eb33c6c691054a13a45db3b4f91a7fb1bd79151e6b4e9e9a86727b522527c0a06ec7d45206b745d54cd3097bcec + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.17": + version: 3.3.18 + resolution: "nanoid@npm:3.3.18" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/b994b4e396730f8be2520923284e2040d61eaee55cc6d4935ef6d38d34bafdc46133eda4d3faea5073bda545aa6079d82b886caeac5c731cf9ac18bcc1301425 + languageName: node + linkType: hard + +"natural-compare@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare@npm:1.4.0" + checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447 + languageName: node + linkType: hard + +"negotiator@npm:0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 13.0.2 + resolution: "node-gyp@npm:13.0.2" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + graceful-fs: "npm:^4.2.6" + nopt: "npm:^10.0.0" + proc-log: "npm:^7.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.5.4" + tinyglobby: "npm:^0.2.12" + undici: "npm:^8.4.1" + which: "npm:^7.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/29d33ccf47ffeeb5e7af925550b416f698a26a72eb10975c7eb5775c1d0cecacd76d164a4aa45503d3ddcece209db65c712be00423c69381a4cd14e2e6540559 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.53": + version: 2.0.54 + resolution: "node-releases@npm:2.0.54" + checksum: 10c0/196b54ab06fd03b742816b6ca8185c69af42735b283083036e2579682c430b8f9ff89046891fe8c625557e5885793a2032d28070100bb2e57e6d6586b2f483c1 + languageName: node + linkType: hard + +"nopt@npm:^10.0.0": + version: 10.0.1 + resolution: "nopt@npm:10.0.1" + dependencies: + abbrev: "npm:^5.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/980d89257f9587f3e1f77877ddbf905d6aa3b738ec33e49a4fa1a059a0dd82eb28063982b150654a7ae9de386f2ead60e56172db7d37cf56de545f7392a2a26a + languageName: node + linkType: hard + +"object-assign@npm:^4, object-assign@npm:^4.0.0": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + +"optionator@npm:^0.9.3": + version: 0.9.4 + resolution: "optionator@npm:0.9.4" + dependencies: + deep-is: "npm:^0.1.3" + fast-levenshtein: "npm:^2.0.6" + levn: "npm:^0.4.1" + prelude-ls: "npm:^1.2.1" + type-check: "npm:^0.4.0" + word-wrap: "npm:^1.2.5" + checksum: 10c0/4afb687a059ee65b61df74dfe87d8d6815cd6883cb8b3d5883a910df72d0f5d029821f37025e4bccf4048873dbdb09acc6d303d27b8f76b1a80dd5a7d5334675 + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: "npm:^3.0.2" + checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^4.0.4": + version: 4.0.7 + resolution: "picomatch@npm:4.0.7" + checksum: 10c0/beb6ae02c43ae44e84883b90830196d9046b1726ead292adcf7f57945e0bb0d992d68563d87e03b484b6f3c9a5c6defda7523477f047d7f0e663f126cc01787f + languageName: node + linkType: hard + +"pngparse@npm:^2.0.0": + version: 2.0.1 + resolution: "pngparse@npm:2.0.1" + checksum: 10c0/0c051572f1046c9fe2a368bdfcedd5d94fc803cdc7d64f1b50246f582bd93f08a476197a818c592c3a74f2122ca0ef27620c43b9bd801ed9e750ee36495a61db + languageName: node + linkType: hard + +"postcss@npm:^8.4.43": + version: 8.5.26 + resolution: "postcss@npm:8.5.26" + dependencies: + nanoid: "npm:^3.3.17" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/2bdafc00d96bd57b6649a52e458864a4bf58ee56cfdbe4aea1472b5cccc127e6c1ad653bd0bec50d211e650eb0b9270c80e1e72aff2e2fa40d9e7363234d6e43 + languageName: node + linkType: hard + +"potpack@npm:^1.0.1": + version: 1.0.2 + resolution: "potpack@npm:1.0.2" + checksum: 10c0/670c23898a4257130858b960c2e654d3327c0f6a7e7091ff5846f213e65af8f9476320b995b8ad561a47a4d1c359c7ef347de57d22e7b02597051abb52bc85c4 + languageName: node + linkType: hard + +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: 10c0/b00d617431e7886c520a6f498a2e14c75ec58f6d93ba48c3b639cf241b54232d90daa05d83a9e9b9fef6baa63cb7e1e4602c2372fea5bc169668401eb127d0cd + languageName: node + linkType: hard + +"proc-log@npm:^7.0.0": + version: 7.0.0 + resolution: "proc-log@npm:7.0.0" + checksum: 10c0/b89c2d862604f35fec795477b0c7e376feab3ba0d4f4d291c4e959567442697cf451ac557d0623c1cc38af45a78128b983410f397a10c5d3a67f76c33de4754b + languageName: node + linkType: hard + +"promise-worker-transferable@npm:^1.0.4": + version: 1.0.4 + resolution: "promise-worker-transferable@npm:1.0.4" + dependencies: + is-promise: "npm:^2.1.0" + lie: "npm:^3.0.2" + checksum: 10c0/110d273cbba2631c318762f02c159531f1e05e4af91080368b9d4c02b880824ae4c49a8ec7094c6434989ea2ee8de913b21c809279babeaf447fc90a682dcd7c + languageName: node + linkType: hard + +"proxy-from-env@npm:^2.1.0": + version: 2.1.0 + resolution: "proxy-from-env@npm:2.1.0" + checksum: 10c0/ed01729fd4d094eab619cd7e17ce3698b3413b31eb102c4904f9875e677cd207392795d5b4adee9cec359dfd31c44d5ad7595a3a3ad51c40250e141512281c58 + languageName: node + linkType: hard + +"punycode@npm:^2.1.0": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 + languageName: node + linkType: hard + +"rc-cascader@npm:~3.34.0": + version: 3.34.0 + resolution: "rc-cascader@npm:3.34.0" + dependencies: + "@babel/runtime": "npm:^7.25.7" + classnames: "npm:^2.3.1" + rc-select: "npm:~14.16.2" + rc-tree: "npm:~5.13.0" + rc-util: "npm:^5.43.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/1fc8c55e0f78ff2be59e2bcd8faa53aafecebbb28f4bb9982ad39e8f9f9620e15d6119797c7890347e46b05c32b43177ece047e81ef04c22a9f041eb0dd53e0a + languageName: node + linkType: hard + +"rc-checkbox@npm:~3.5.0": + version: 3.5.0 + resolution: "rc-checkbox@npm:3.5.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:^2.3.2" + rc-util: "npm:^5.25.2" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/53fd419030a8c9e3d08ebb7c51dee79be810ccd92ed177066c2afa8f61a8fe4417232bbc4741ecc0a627d0c4b939a5e945c6f0d6a941c748d65c2ddad71775e3 + languageName: node + linkType: hard + +"rc-collapse@npm:~3.9.0": + version: 3.9.0 + resolution: "rc-collapse@npm:3.9.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:2.x" + rc-motion: "npm:^2.3.4" + rc-util: "npm:^5.27.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/68d2c7a6614fea2bf4a30a39e67d5b74b933fd25e31762cd810ff0f7bcf7e57676db6c3c1389461d5d18be4a68b9cfeda65321a8d1f5978ec2a5aa3d7b9010cc + languageName: node + linkType: hard + +"rc-dialog@npm:~9.6.0": + version: 9.6.0 + resolution: "rc-dialog@npm:9.6.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + "@rc-component/portal": "npm:^1.0.0-8" + classnames: "npm:^2.2.6" + rc-motion: "npm:^2.3.0" + rc-util: "npm:^5.21.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/19e9acef746baa25c3167a961919123b0b457288188c18bc4d468ae31144bf750d6d6468dd3be43b376eba42ddda26fef1aac8ae9bd016f5d0428ffee0c615e7 + languageName: node + linkType: hard + +"rc-drawer@npm:~7.3.0": + version: 7.3.0 + resolution: "rc-drawer@npm:7.3.0" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@rc-component/portal": "npm:^1.1.1" + classnames: "npm:^2.2.6" + rc-motion: "npm:^2.6.1" + rc-util: "npm:^5.38.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/e2c3211d6a3790813bf2c1626cebf3fdb3a4c48ab56bee2d208ba07dd0e5058154981563e89e02571d573dd56c2ddc65db33a0cf37c58820ecc4b08785e8d169 + languageName: node + linkType: hard + +"rc-dropdown@npm:~4.2.0, rc-dropdown@npm:~4.2.1": + version: 4.2.1 + resolution: "rc-dropdown@npm:4.2.1" + dependencies: + "@babel/runtime": "npm:^7.18.3" + "@rc-component/trigger": "npm:^2.0.0" + classnames: "npm:^2.2.6" + rc-util: "npm:^5.44.1" + peerDependencies: + react: ">=16.11.0" + react-dom: ">=16.11.0" + checksum: 10c0/ec980e6c9f8bbba53e895002a0c3a28f294ae07f3ebc6c9a9cb80c7e1bb74ba9f0e0c4b9c23f487fdf8c5a4531000e05b5b43744ef506f0fd869165486768817 + languageName: node + linkType: hard + +"rc-field-form@npm:~2.7.1": + version: 2.7.1 + resolution: "rc-field-form@npm:2.7.1" + dependencies: + "@babel/runtime": "npm:^7.18.0" + "@rc-component/async-validator": "npm:^5.0.3" + rc-util: "npm:^5.32.2" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/2493cb9f26e69e17d55f32ad689da103a325e613d9222bfb332c2dcbdc96d44ce7dc4c8642a9b89a932ad2c2573508c997a4685e7fe6de2e951a027d2837403a + languageName: node + linkType: hard + +"rc-image@npm:~7.12.0": + version: 7.12.0 + resolution: "rc-image@npm:7.12.0" + dependencies: + "@babel/runtime": "npm:^7.11.2" + "@rc-component/portal": "npm:^1.0.2" + classnames: "npm:^2.2.6" + rc-dialog: "npm:~9.6.0" + rc-motion: "npm:^2.6.2" + rc-util: "npm:^5.34.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/229f848725f8cff5b6015eb0468a24a3d92c2aead48dc98abe19e6ff15840defc9b42f1a126b7f8180f678b5380ff99528bb89e972298ad456773e4070f33934 + languageName: node + linkType: hard + +"rc-input-number@npm:~9.5.0": + version: 9.5.0 + resolution: "rc-input-number@npm:9.5.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + "@rc-component/mini-decimal": "npm:^1.0.1" + classnames: "npm:^2.2.5" + rc-input: "npm:~1.8.0" + rc-util: "npm:^5.40.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/332aef42cd1f0e9eeee08c85db978c9615dfec5c8972e91c37a2ba4e06c3578d84dda05698e98893c6e62620d0d53aa910c0fbee2afac8f54c6f68759c296a58 + languageName: node + linkType: hard + +"rc-input@npm:~1.8.0": + version: 1.8.0 + resolution: "rc-input@npm:1.8.0" + dependencies: + "@babel/runtime": "npm:^7.11.1" + classnames: "npm:^2.2.1" + rc-util: "npm:^5.18.1" + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 10c0/fe4e67b6980b22f77d62dcd87177a2381976baeaff265a27c4adb63bab48735f7c89b271c541eb0aab8c9d58af66979f45e79b44b72342838ac038ee5db0ba73 + languageName: node + linkType: hard + +"rc-mentions@npm:~2.20.0": + version: 2.20.0 + resolution: "rc-mentions@npm:2.20.0" + dependencies: + "@babel/runtime": "npm:^7.22.5" + "@rc-component/trigger": "npm:^2.0.0" + classnames: "npm:^2.2.6" + rc-input: "npm:~1.8.0" + rc-menu: "npm:~9.16.0" + rc-textarea: "npm:~1.10.0" + rc-util: "npm:^5.34.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/2b242221772bad982c47916328e0245365134ba48519d171a93a8d79ddbdfb20a98421d7962215867dc9097dd58c307ea9bb6c9590125c0484c01d0b78e207e0 + languageName: node + linkType: hard + +"rc-menu@npm:~9.16.0, rc-menu@npm:~9.16.1": + version: 9.16.1 + resolution: "rc-menu@npm:9.16.1" + dependencies: + "@babel/runtime": "npm:^7.10.1" + "@rc-component/trigger": "npm:^2.0.0" + classnames: "npm:2.x" + rc-motion: "npm:^2.4.3" + rc-overflow: "npm:^1.3.1" + rc-util: "npm:^5.27.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/b61f21013cd679777b673d6e1a9f8429ffd9481a49665fc9a9c78198025d52aa76ae162186c46ca9f332117c8c7ff32f30d72435e1ae1b2a3daec0f86cb48810 + languageName: node + linkType: hard + +"rc-motion@npm:^2.0.0, rc-motion@npm:^2.0.1, rc-motion@npm:^2.3.0, rc-motion@npm:^2.3.4, rc-motion@npm:^2.4.3, rc-motion@npm:^2.4.4, rc-motion@npm:^2.6.1, rc-motion@npm:^2.6.2, rc-motion@npm:^2.9.0, rc-motion@npm:^2.9.5": + version: 2.9.5 + resolution: "rc-motion@npm:2.9.5" + dependencies: + "@babel/runtime": "npm:^7.11.1" + classnames: "npm:^2.2.1" + rc-util: "npm:^5.44.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/84b12b2443dc1b929c8a688e8c9834a44cf88897402e9363fcea80b77f2803b2de83b24dac5873a8695304827e02fb3103c74349d0451ed247cb366553f9d88e + languageName: node + linkType: hard + +"rc-notification@npm:~5.6.4": + version: 5.6.4 + resolution: "rc-notification@npm:5.6.4" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:2.x" + rc-motion: "npm:^2.9.0" + rc-util: "npm:^5.20.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/ea6a587b6a6057e8e6273d642cac5608b44948374ed636c9a83d104d21731c114b43036e33add05f755ceefb3f9258b881776672d5745c0e5d19f1d66449f37a + languageName: node + linkType: hard + +"rc-overflow@npm:^1.3.1, rc-overflow@npm:^1.3.2": + version: 1.5.0 + resolution: "rc-overflow@npm:1.5.0" + dependencies: + "@babel/runtime": "npm:^7.11.1" + classnames: "npm:^2.2.1" + rc-resize-observer: "npm:^1.0.0" + rc-util: "npm:^5.37.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/7e9ce2fc3db7eb6e960b2848c0da9f67dcdc2a06eadd22baebe9933253424f6243c98fa7f093b765cb355d5fc3a880542748e199bee279dabc790fcf1bb2d34b + languageName: node + linkType: hard + +"rc-pagination@npm:~5.1.0": + version: 5.1.0 + resolution: "rc-pagination@npm:5.1.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:^2.3.2" + rc-util: "npm:^5.38.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/6cc6f0fa591c3d9f1cd0abcc1f918ddf18d6b5c71fefb97a6c3888b8492505e8e8951903de2bae7c64c0947cf1d53bc70f52577a3f6b38bdb3e9140a7bb5a32e + languageName: node + linkType: hard + +"rc-picker@npm:~4.11.3": + version: 4.11.3 + resolution: "rc-picker@npm:4.11.3" + dependencies: + "@babel/runtime": "npm:^7.24.7" + "@rc-component/trigger": "npm:^2.0.0" + classnames: "npm:^2.2.1" + rc-overflow: "npm:^1.3.2" + rc-resize-observer: "npm:^1.4.0" + rc-util: "npm:^5.43.0" + peerDependencies: + date-fns: ">= 2.x" + dayjs: ">= 1.x" + luxon: ">= 3.x" + moment: ">= 2.x" + react: ">=16.9.0" + react-dom: ">=16.9.0" + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + checksum: 10c0/5136966ae7f9c0fa9acb9620f3d5e12341a4f2d1369d1c0e12697fbd519be47025a427acd63c8c39ec6b0a88acc5408b8318d197d8bf37f8d2b19d6726e6868d + languageName: node + linkType: hard + +"rc-progress@npm:~4.0.0": + version: 4.0.0 + resolution: "rc-progress@npm:4.0.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:^2.2.6" + rc-util: "npm:^5.16.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/d3b47565470c5fec71a16f8d1939f1b1dd7d2dc9260893c6f70cafa84d9ee4231f3466be817db5fb9580932af46e34d52c74a710700ca9391b1901fa06c31f1e + languageName: node + linkType: hard + +"rc-rate@npm:~2.13.1": + version: 2.13.1 + resolution: "rc-rate@npm:2.13.1" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:^2.2.5" + rc-util: "npm:^5.0.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/b26d4741fffb06e1beebe1aba135ba6ab4ee898faf1f876ce802ed5ddcdc8dabe7a4662be63e60226713fad9b3dd8d4034ed9b8b3e27ba5ef9673d7e8f47d497 + languageName: node + linkType: hard + +"rc-resize-observer@npm:^1.0.0, rc-resize-observer@npm:^1.1.0, rc-resize-observer@npm:^1.3.1, rc-resize-observer@npm:^1.4.0, rc-resize-observer@npm:^1.4.3": + version: 1.4.3 + resolution: "rc-resize-observer@npm:1.4.3" + dependencies: + "@babel/runtime": "npm:^7.20.7" + classnames: "npm:^2.2.1" + rc-util: "npm:^5.44.1" + resize-observer-polyfill: "npm:^1.5.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/93073c9ef5cc704f9d99307f58f8eeccabb953edf4e8a056b090104fc28ed19b77c2a32bd88ca2e0407fbedeb266d1985e655b35b8bc36b04d243e9d0471c911 + languageName: node + linkType: hard + +"rc-segmented@npm:~2.7.0": + version: 2.7.1 + resolution: "rc-segmented@npm:2.7.1" + dependencies: + "@babel/runtime": "npm:^7.11.1" + classnames: "npm:^2.2.1" + rc-motion: "npm:^2.4.4" + rc-util: "npm:^5.17.0" + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 10c0/051626c9124b641284d0b63a64c56bc21282d5e4bbd906a722f92b87a63e7b0d0ede1f541c623dab0355b16f7aaecb26792915e46d74fb1540aa9e0881715d3e + languageName: node + linkType: hard + +"rc-select@npm:~14.16.2, rc-select@npm:~14.16.8": + version: 14.16.8 + resolution: "rc-select@npm:14.16.8" + dependencies: + "@babel/runtime": "npm:^7.10.1" + "@rc-component/trigger": "npm:^2.1.1" + classnames: "npm:2.x" + rc-motion: "npm:^2.0.1" + rc-overflow: "npm:^1.3.1" + rc-util: "npm:^5.16.1" + rc-virtual-list: "npm:^3.5.2" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10c0/45f93e270c4b5e5ffc4b0ba0ce5e5ea72fff591a9a7a19b460b1ead0517d17327af9a4c32ce3c7f92b765724f4dabd1aa7146f5a06db73be91c884fe13c92774 + languageName: node + linkType: hard + +"rc-slider@npm:~11.1.9": + version: 11.1.9 + resolution: "rc-slider@npm:11.1.9" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:^2.2.5" + rc-util: "npm:^5.36.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/193c432e2859ba42b2235cc1949de2d929ba70fa4aa5672eaa5da692797f3fe927f8c0b2a75cc45c6b9f666f204f4ed038ccf904273d6cbc39e112f4a00ddd4a + languageName: node + linkType: hard + +"rc-steps@npm:~6.0.1": + version: 6.0.1 + resolution: "rc-steps@npm:6.0.1" + dependencies: + "@babel/runtime": "npm:^7.16.7" + classnames: "npm:^2.2.3" + rc-util: "npm:^5.16.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/0ba1051a469ae95916cafbb6d7fe76f94e6666181129d3012174d8cc25913c6afd691f551ee0dac48a4a86b59cb91699d6a44a1398dcafd661a8a519f70c95e1 + languageName: node + linkType: hard + +"rc-switch@npm:~4.1.0": + version: 4.1.0 + resolution: "rc-switch@npm:4.1.0" + dependencies: + "@babel/runtime": "npm:^7.21.0" + classnames: "npm:^2.2.1" + rc-util: "npm:^5.30.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/5ce5b1cadea6e7cd38c0725456ea15c39748fefc39576f7c9288192e69b7f426c4ac7627e266369ece164f281ae08e14ab8f54d4d7858c8bd20707b296980743 + languageName: node + linkType: hard + +"rc-table@npm:~7.54.0": + version: 7.54.0 + resolution: "rc-table@npm:7.54.0" + dependencies: + "@babel/runtime": "npm:^7.10.1" + "@rc-component/context": "npm:^1.4.0" + classnames: "npm:^2.2.5" + rc-resize-observer: "npm:^1.1.0" + rc-util: "npm:^5.44.3" + rc-virtual-list: "npm:^3.14.2" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/bf78af66c97aca8437bcf1f88df81b4171afbf647959c9b3600b246c15f9db8763fa50255749390d7f2c0ba0a6aea4f467f64aa4dd6265d56ebf35ffc0d6b94f + languageName: node + linkType: hard + +"rc-tabs@npm:~15.7.0": + version: 15.7.0 + resolution: "rc-tabs@npm:15.7.0" + dependencies: + "@babel/runtime": "npm:^7.11.2" + classnames: "npm:2.x" + rc-dropdown: "npm:~4.2.0" + rc-menu: "npm:~9.16.0" + rc-motion: "npm:^2.6.2" + rc-resize-observer: "npm:^1.0.0" + rc-util: "npm:^5.34.1" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/472561f2ec611e9f2a396ba9fec6b83138621651aa2e9fb5a1c68b4da8cb6cab01c23ca71b3940876a595f30c2b8324b8899f93486862271be8eb16a95433764 + languageName: node + linkType: hard + +"rc-textarea@npm:~1.10.0, rc-textarea@npm:~1.10.2": + version: 1.10.2 + resolution: "rc-textarea@npm:1.10.2" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:^2.2.1" + rc-input: "npm:~1.8.0" + rc-resize-observer: "npm:^1.0.0" + rc-util: "npm:^5.27.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/ccfe7bae33187c382e12bc14e9f2617fe183a4d4e8c0d3b9f71455728172f31a6140d0855ff557b6c658daf31c7ff935a1a347a336f8106ddda84e042ab23448 + languageName: node + linkType: hard + +"rc-tooltip@npm:~6.4.0": + version: 6.4.0 + resolution: "rc-tooltip@npm:6.4.0" + dependencies: + "@babel/runtime": "npm:^7.11.2" + "@rc-component/trigger": "npm:^2.0.0" + classnames: "npm:^2.3.1" + rc-util: "npm:^5.44.3" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/49b9c56fc877b38084b4076edb1b61f0272bdd290c6ef161a0e1cf6426488e948c20439cf4ae31e076f3957b894feb326e4a1d7880400de2c29b1d54f736a342 + languageName: node + linkType: hard + +"rc-tree-select@npm:~5.27.0": + version: 5.27.0 + resolution: "rc-tree-select@npm:5.27.0" + dependencies: + "@babel/runtime": "npm:^7.25.7" + classnames: "npm:2.x" + rc-select: "npm:~14.16.2" + rc-tree: "npm:~5.13.0" + rc-util: "npm:^5.43.0" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10c0/26aad0e13e5f9fe501574ba50826edda9b67a5bf22adbe1dc8e3a793fb784318b235165d6054a047b5934cdfbbd88ea1a524726edbad9107cd0f1d28782f9cc5 + languageName: node + linkType: hard + +"rc-tree@npm:~5.13.0, rc-tree@npm:~5.13.1": + version: 5.13.1 + resolution: "rc-tree@npm:5.13.1" + dependencies: + "@babel/runtime": "npm:^7.10.1" + classnames: "npm:2.x" + rc-motion: "npm:^2.0.1" + rc-util: "npm:^5.16.1" + rc-virtual-list: "npm:^3.5.1" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10c0/4a27783d319f9e5367e9d123a2f9a6daa0383e705e055abb47f3ff7fa93249c5c26bbb27b7c6602163faefbfe0f3e923eb3a55d1e1f1d09d04b7bdf37942c2d4 + languageName: node + linkType: hard + +"rc-upload@npm:~4.11.0": + version: 4.11.0 + resolution: "rc-upload@npm:4.11.0" + dependencies: + "@babel/runtime": "npm:^7.18.3" + classnames: "npm:^2.2.5" + rc-util: "npm:^5.2.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/9ce59e22e0f9839e482fd37e63d8489e1a9d418113bfe39cd4d2b1e88f59236d4f7c60a3bb5928d2d85781d6f4cf8c30b6085863c802de605ef339fa415903d3 + languageName: node + linkType: hard + +"rc-util@npm:^5.0.1, rc-util@npm:^5.16.1, rc-util@npm:^5.17.0, rc-util@npm:^5.18.1, rc-util@npm:^5.2.0, rc-util@npm:^5.20.1, rc-util@npm:^5.21.0, rc-util@npm:^5.24.4, rc-util@npm:^5.25.2, rc-util@npm:^5.27.0, rc-util@npm:^5.30.0, rc-util@npm:^5.31.1, rc-util@npm:^5.32.2, rc-util@npm:^5.34.1, rc-util@npm:^5.35.0, rc-util@npm:^5.36.0, rc-util@npm:^5.37.0, rc-util@npm:^5.38.0, rc-util@npm:^5.38.1, rc-util@npm:^5.40.1, rc-util@npm:^5.43.0, rc-util@npm:^5.44.0, rc-util@npm:^5.44.1, rc-util@npm:^5.44.3, rc-util@npm:^5.44.4": + version: 5.44.4 + resolution: "rc-util@npm:5.44.4" + dependencies: + "@babel/runtime": "npm:^7.18.3" + react-is: "npm:^18.2.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/748b71a6280ddaaac93d1fb2c92f03818775468e7ccb6c221484687cc0b7e879d083e98e338f75ac0fe2e942dbb9c2405bd32d25e5a804bf1fb7a11f3f897127 + languageName: node + linkType: hard + +"rc-virtual-list@npm:^3.14.2, rc-virtual-list@npm:^3.5.1, rc-virtual-list@npm:^3.5.2": + version: 3.19.2 + resolution: "rc-virtual-list@npm:3.19.2" + dependencies: + "@babel/runtime": "npm:^7.20.0" + classnames: "npm:^2.2.6" + rc-resize-observer: "npm:^1.0.0" + rc-util: "npm:^5.36.0" + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 10c0/3778ade183a33d113555fb99465c2f59391c9b5178629cb7bb2947d5ee71a1b166bf9468b063394f63384965166ef368acf78cb5f4b3a23e9393af04543b6626 + languageName: node + linkType: hard + +"react-dom@npm:^19.1.0": + version: 19.2.8 + resolution: "react-dom@npm:19.2.8" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.8 + checksum: 10c0/41ba2247b76f687fcfe5bbc99f514d6b851d8c8041c2f5ded36ed05bd7fdc5208cacbac9de51e3e6633e77f96f44cec9f0d4a5a55184dba4e00738f224439134 + languageName: node + linkType: hard + +"react-is@npm:^18.2.0": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 + languageName: node + linkType: hard + +"react-is@npm:^19.2.7": + version: 19.2.8 + resolution: "react-is@npm:19.2.8" + checksum: 10c0/ed5322c84efe035c8fc814b1614ff5ca7fb8c2872a7c045197daf99f9b1bd68f32a2c79ffcbcfcff2fc5d93924ff9f9447400898f5b1534e6302bb0736a257f0 + languageName: node + linkType: hard + +"react-refresh@npm:^0.17.0": + version: 0.17.0 + resolution: "react-refresh@npm:0.17.0" + checksum: 10c0/002cba940384c9930008c0bce26cac97a9d5682bc623112c2268ba0c155127d9c178a9a5cc2212d560088d60dfd503edd808669a25f9b377f316a32361d0b23c + languageName: node + linkType: hard + +"react-router-dom@npm:^7.6.2": + version: 7.18.3 + resolution: "react-router-dom@npm:7.18.3" + dependencies: + react-router: "npm:7.18.3" + peerDependencies: + react: ">=18" + react-dom: ">=18" + checksum: 10c0/1d04bb38f64e9c429d4f162ca6c5ffc163a02c0a5c08748440a58fc0e9b1e8c8edc2bc4509e9966dcd0873e57fc6a090fd356244d581567737aac1ae6236e799 + languageName: node + linkType: hard + +"react-router@npm:7.18.3": + version: 7.18.3 + resolution: "react-router@npm:7.18.3" + dependencies: + cookie: "npm:^1.0.1" + set-cookie-parser: "npm:^2.6.0" + peerDependencies: + react: ">=18" + react-dom: ">=18" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10c0/77686cad916f8928e6c4aeafe5f8dc26384e84897849ff7d771ddee67af02b788c103503d029ffa442f7bf42aa47b3779440e70fc1aeae90532120fae4036b0f + languageName: node + linkType: hard + +"react-use-measure@npm:^2.1.7": + version: 2.1.7 + resolution: "react-use-measure@npm:2.1.7" + peerDependencies: + react: ">=16.13" + react-dom: ">=16.13" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10c0/ff24130e6f95e853feb6892fb74af08dbc5aae3574b701169e3bc3adb392c3162f51a58ddfe39bb7337db13ae609bbec0bb51a9de8b5fae5420f9d17e1f8b542 + languageName: node + linkType: hard + +"react-webcam@npm:^7.2.0": + version: 7.2.0 + resolution: "react-webcam@npm:7.2.0" + peerDependencies: + react: ">=16.2.0" + react-dom: ">=16.2.0" + checksum: 10c0/d639a9e4cd545f66a5ecbfdc6c38344f6ae40e6609ba5fb26db730b48723158dedeeecdecba94e4d117517f4db0be828169db270c329b9b71d847e54aba6f7f0 + languageName: node + linkType: hard + +"react@npm:^19.1.0": + version: 19.2.8 + resolution: "react@npm:19.2.8" + checksum: 10c0/5f86bdb56426652fd6d989d30a6f2e603c057272c47c9ca3a3fbe190a3a39ee9ccce937d63cfc039717abed1b8891d6a499134bc35311acc07eafdacd86537cd + languageName: node + linkType: hard + +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: 10c0/aaa267e0c5b022fc5fd4eef49d8285086b15f2a1c54b28240fdf03599cbd9c26049fee3eab894f2e1f6ca65e513b030a7c264201e3f005601e80c49fb2937ce2 + languageName: node + linkType: hard + +"resize-observer-polyfill@npm:^1.5.1": + version: 1.5.1 + resolution: "resize-observer-polyfill@npm:1.5.1" + checksum: 10c0/5e882475067f0b97dc07e0f37c3e335ac5bc3520d463f777cec7e894bb273eddbfecb857ae668e6fb6881fd6f6bb7148246967172139302da50fa12ea3a15d95 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 + languageName: node + linkType: hard + +"rollup@npm:^4.20.0": + version: 4.63.1 + resolution: "rollup@npm:4.63.1" + dependencies: + "@napi-rs/lzma-linux-x64-gnu": "npm:1.5.1" + "@rollup/rollup-android-arm-eabi": "npm:4.63.1" + "@rollup/rollup-android-arm64": "npm:4.63.1" + "@rollup/rollup-darwin-arm64": "npm:4.63.1" + "@rollup/rollup-darwin-x64": "npm:4.63.1" + "@rollup/rollup-freebsd-arm64": "npm:4.63.1" + "@rollup/rollup-freebsd-x64": "npm:4.63.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.63.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.63.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.63.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.63.1" + "@rollup/rollup-linux-loong64-gnu": "npm:4.63.1" + "@rollup/rollup-linux-loong64-musl": "npm:4.63.1" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.63.1" + "@rollup/rollup-linux-ppc64-musl": "npm:4.63.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.63.1" + "@rollup/rollup-linux-riscv64-musl": "npm:4.63.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.63.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.63.1" + "@rollup/rollup-linux-x64-musl": "npm:4.63.1" + "@rollup/rollup-openbsd-x64": "npm:4.63.1" + "@rollup/rollup-openharmony-arm64": "npm:4.63.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.63.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.63.1" + "@rollup/rollup-win32-x64-gnu": "npm:4.63.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.63.1" + "@types/estree": "npm:1.0.9" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@napi-rs/lzma-linux-x64-gnu": + optional: true + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/90cac5c59bdeb7762483b502f1b547057b0a5e18d044a971b9fbd5629ee262518d00c526e92244726c91e11c4fd5005e9e48359787af034035f0668bb5949ab0 + languageName: node + linkType: hard + +"roslib@npm:^1.4.1": + version: 1.4.1 + resolution: "roslib@npm:1.4.1" + dependencies: + "@xmldom/xmldom": "npm:^0.8.0" + cbor-js: "npm:^0.1.0" + eventemitter2: "npm:^6.4.0" + object-assign: "npm:^4.0.0" + pngparse: "npm:^2.0.0" + socket.io: "npm:^4.0.0" + webworkify: "npm:^1.5.0" + webworkify-webpack: "npm:^2.1.5" + ws: "npm:^8.0.0" + checksum: 10c0/ddcb6f2935dbcab63bf5ddd00b39a7facb5f06eacb170dcf030448907b98788418853c0c59ee5384ff245d1536076048234ca48247fac21a823fa36afbdaa1bf + languageName: node + linkType: hard + +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 + languageName: node + linkType: hard + +"scroll-into-view-if-needed@npm:^3.1.0": + version: 3.1.0 + resolution: "scroll-into-view-if-needed@npm:3.1.0" + dependencies: + compute-scroll-into-view: "npm:^3.0.2" + checksum: 10c0/1f46b090e1e04fcfdef1e384f6d7e615f9f84d4176faf4dbba7347cc0a6e491e5d578eaf4dbe9618dd3d8d38efafde58535b3e00f2a21ce4178c14be364850ff + languageName: node + linkType: hard + +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.7.3": + version: 7.8.5 + resolution: "semver@npm:7.8.5" + bin: + semver: bin/semver.js + checksum: 10c0/b1f3127a5be8125a94f37188b361c212466c292c6910adce3ec106cff5dc211ccaedc4739c11bb70fda59d6fc1f040a9bca289f4e093451521a2372e5231fe0c + languageName: node + linkType: hard + +"set-cookie-parser@npm:^2.6.0": + version: 2.7.2 + resolution: "set-cookie-parser@npm:2.7.2" + checksum: 10c0/4381a9eb7ee951dfe393fe7aacf76b9a3b4e93a684d2162ab35594fa4053cc82a4d7d7582bf397718012c9adcf839b8cd8f57c6c42901ea9effe33c752da4a45 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"socket.io-adapter@npm:~2.5.2": + version: 2.5.8 + resolution: "socket.io-adapter@npm:2.5.8" + dependencies: + debug: "npm:~4.4.1" + ws: "npm:~8.21.0" + checksum: 10c0/17fc68a7953d6b640b78b2191d8a575b30c7efb9fdec91b4f0788e2f9d093dfb9a84f6fcb396f9b922c32dd0584b01c2d3410a8f69b4cae3f309530b8859bc8a + languageName: node + linkType: hard + +"socket.io-parser@npm:~4.2.4": + version: 4.2.7 + resolution: "socket.io-parser@npm:4.2.7" + dependencies: + "@socket.io/component-emitter": "npm:~3.1.0" + debug: "npm:~4.4.1" + checksum: 10c0/16a5579718b871114ca644d688987dd3cf9292010c949fb083633eefbc1d8fdaf424691d6511d746e5c10f5c88cbd8911cd0b1e807242159f40989be90842b4e + languageName: node + linkType: hard + +"socket.io@npm:^4.0.0": + version: 4.8.3 + resolution: "socket.io@npm:4.8.3" + dependencies: + accepts: "npm:~1.3.4" + base64id: "npm:~2.0.0" + cors: "npm:~2.8.5" + debug: "npm:~4.4.1" + engine.io: "npm:~6.6.0" + socket.io-adapter: "npm:~2.5.2" + socket.io-parser: "npm:~4.2.4" + checksum: 10c0/1f7c4118cdbcb346e63db9d8fd657c3dc5caf148404762ed98ac14c4e7b74984a65fe51657bfe1696adcf7c168d1a3aad4a26b52864ce8491556f38218598f0b + languageName: node + linkType: hard + +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"stats-gl@npm:^2.2.8": + version: 2.4.2 + resolution: "stats-gl@npm:2.4.2" + dependencies: + "@types/three": "npm:*" + three: "npm:^0.170.0" + peerDependencies: + "@types/three": "*" + three: "*" + checksum: 10c0/cff079103d4004cdfd72d65c977b7237a929b02f57b275081188e9313d74620b70ea0a57e0561fb6a43b1597830068b4adb2625dbc08b99799fd12a71ec54729 + languageName: node + linkType: hard + +"stats.js@npm:^0.17.0": + version: 0.17.0 + resolution: "stats.js@npm:0.17.0" + checksum: 10c0/03392b4b9eb15cb285b8cf46c5dd68e4e7b556d8e4c72a3a48e6bd2ab6f2e72a02b145d465f594450df081d8f94a45e8ac1112a0d507a200a5768504082ca080 + languageName: node + linkType: hard + +"string-convert@npm:^0.2.0": + version: 0.2.1 + resolution: "string-convert@npm:0.2.1" + checksum: 10c0/00673ed8a3106137395436537ace7d3672c91a3290da73466055daa0134331dc84bc58c54ba2d2ea40711adc5744426d3c8239dbfc30290438fa3e9ff65db528 + languageName: node + linkType: hard + +"strip-json-comments@npm:^3.1.1": + version: 3.1.1 + resolution: "strip-json-comments@npm:3.1.1" + checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd + languageName: node + linkType: hard + +"stylis@npm:^4.3.4": + version: 4.4.0 + resolution: "stylis@npm:4.4.0" + checksum: 10c0/259be096d90dfbfe903c8656dcb7591e52a421e577e950ef42ebd9ca02f387623a1165dd08761492fb6e92a7a562d62a53a694a10b0a2f6dcd7a0db107b4bf55 + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124 + languageName: node + linkType: hard + +"suspend-react@npm:^0.1.3": + version: 0.1.3 + resolution: "suspend-react@npm:0.1.3" + peerDependencies: + react: ">=17.0" + checksum: 10c0/c35436f492a5cab85a2aac8f5ee5af8f67edbab3a49e12d15c7f770aae4cf09fe2925b3b8c122929210c1f28aa0958f91684a687d6608f116f7da8406d8a2f1c + languageName: node + linkType: hard + +"tar@npm:^7.5.4": + version: 7.5.22 + resolution: "tar@npm:7.5.22" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.1.0" + yallist: "npm:^5.0.0" + checksum: 10c0/1311f6be85a8157ac4c9147bae43e13923d2a1aae15e4aa1bd5239e4e03d2cf53cfe103dde7f35832fbb4c938b042856bc8e9a0afd29abd05e2d1608788c4fea + languageName: node + linkType: hard + +"three-mesh-bvh@npm:^0.8.3": + version: 0.8.3 + resolution: "three-mesh-bvh@npm:0.8.3" + peerDependencies: + three: ">= 0.159.0" + checksum: 10c0/d1f052d4606498acf57e08e28d01c84157eb388d46be8e03bc839630dc4eb919d5bb5e2ed4e9113d39cbc3cda29f09212e3450e1bb21de6615ec330f2575fc28 + languageName: node + linkType: hard + +"three-stdlib@npm:^2.35.6, three-stdlib@npm:^2.36.0": + version: 2.36.1 + resolution: "three-stdlib@npm:2.36.1" + dependencies: + "@types/draco3d": "npm:^1.4.0" + "@types/offscreencanvas": "npm:^2019.6.4" + "@types/webxr": "npm:^0.5.2" + draco3d: "npm:^1.4.1" + fflate: "npm:^0.6.9" + potpack: "npm:^1.0.1" + peerDependencies: + three: ">=0.128.0" + checksum: 10c0/6d04a3e6e1cf5a23e1d8807085609e5cfa999d342a8f4a3168432c4152e99ab555f7fd7ab22903d9639f33d8d19d2183c62931be34ad6c3ac8bd3ed879d973c2 + languageName: node + linkType: hard + +"three@npm:^0.170.0": + version: 0.170.0 + resolution: "three@npm:0.170.0" + checksum: 10c0/eeaa1eccb31467654a044fb90634b3b6bb8d0768a4f029c082fb6aa22aec0d6362d0e3af74938caa1db512b7823a031b54a76e88fba6cfd535054165ea9667c9 + languageName: node + linkType: hard + +"three@npm:^0.177.0": + version: 0.177.0 + resolution: "three@npm:0.177.0" + checksum: 10c0/3e2ac1b9908e0d4dcecaf67c3d3e1b0b021dbfc7c80c2780005cc4e50532fb95b4c7f21669931e93c7b934d5576eb2364e1b34271bbd0e9484d1d723f836907b + languageName: node + linkType: hard + +"throttle-debounce@npm:^5.0.0, throttle-debounce@npm:^5.0.2": + version: 5.0.2 + resolution: "throttle-debounce@npm:5.0.2" + checksum: 10c0/9a10ac51400b353562770721718486847adb5d7287c94a0c0d47df5326e8d47e5d92fcb74dac53d6734efb9344a2d46d68c7f996c2d0aedfd11446522e4bb356 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + +"toggle-selection@npm:^1.0.6": + version: 1.0.6 + resolution: "toggle-selection@npm:1.0.6" + checksum: 10c0/f2cf1f2c70f374fd87b0cdc8007453ba9e981c4305a8bf4eac10a30e62ecdfd28bca7d18f8f15b15a506bf8a7bfb20dbe3539f0fcf2a2c8396c1a78d53e1f179 + languageName: node + linkType: hard + +"troika-three-text@npm:^0.52.4": + version: 0.52.5 + resolution: "troika-three-text@npm:0.52.5" + dependencies: + bidi-js: "npm:^1.0.2" + troika-three-utils: "npm:^0.52.5" + troika-worker-utils: "npm:^0.52.0" + webgl-sdf-generator: "npm:1.1.1" + peerDependencies: + three: ">=0.125.0" + checksum: 10c0/0c3dbe6bc0addf43dbbec2a5d2deafe18775d3971df745655b86ed08341940ab6fed6a2e2082779656709081fd5113cc6deb1f4808aa10dc6e913827d2f1ea4a + languageName: node + linkType: hard + +"troika-three-utils@npm:^0.52.5": + version: 0.52.5 + resolution: "troika-three-utils@npm:0.52.5" + peerDependencies: + three: ">=0.125.0" + checksum: 10c0/97e5bdf78f8633c42f0665b39320c890fa898ff6e9f521a06e3dd1f0b97c235b201e0ab0c3813c9e0a49e2be82f3d14509a02ef1d830b2190d244d2fdc48f0bb + languageName: node + linkType: hard + +"troika-worker-utils@npm:^0.52.0": + version: 0.52.0 + resolution: "troika-worker-utils@npm:0.52.0" + checksum: 10c0/bcd776324ce3e941c1a913531cee6022c867b71bc2d6bb67f1eadf6df96fd4f865c51b90ac58fc5c0ae3346a4bd8360b0a52398864800a7a9c60473151d452d5 + languageName: node + linkType: hard + +"ts-api-utils@npm:^2.5.0": + version: 2.5.0 + resolution: "ts-api-utils@npm:2.5.0" + peerDependencies: + typescript: ">=4.8.4" + checksum: 10c0/767849383c114e7f1971fa976b20e73ac28fd0c70d8d65c0004790bf4d8f89888c7e4cf6d5949f9c1beae9bc3c64835bef77bbe27fddf45a3c7b60cebcf85c8c + languageName: node + linkType: hard + +"tslib@npm:^2.0.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"tunnel-rat@npm:^0.1.2": + version: 0.1.2 + resolution: "tunnel-rat@npm:0.1.2" + dependencies: + zustand: "npm:^4.3.2" + checksum: 10c0/93cd50c7c9141e2662707602a21401145092e5a3c815b57a752937419ab6187a2ff36fa7e0f65e0c587022149bf2d323ace07dff61106511b7d4845e53390cc9 + languageName: node + linkType: hard + +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: "npm:^1.2.1" + checksum: 10c0/7b3fd0ed43891e2080bf0c5c504b418fbb3e5c7b9708d3d015037ba2e6323a28152ec163bcb65212741fa5d2022e3075ac3c76440dbd344c9035f818e8ecee58 + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.30.1": + version: 8.68.0 + resolution: "typescript-eslint@npm:8.68.0" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.68.0" + "@typescript-eslint/parser": "npm:8.68.0" + "@typescript-eslint/typescript-estree": "npm:8.68.0" + "@typescript-eslint/utils": "npm:8.68.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + checksum: 10c0/6f7f38c6bc599cddac92cdadce0ec159d081c7abfdcf2cbbda6fc460edcb39d102e558bef9bddb0090dd2cbadcfcea04e742ff036a3894502f1889d3eee57925 + languageName: node + linkType: hard + +"typescript@npm:~5.8.3": + version: 5.8.3 + resolution: "typescript@npm:5.8.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A~5.8.3#optional!builtin": + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + +"undici-types@npm:~8.3.0": + version: 8.3.0 + resolution: "undici-types@npm:8.3.0" + checksum: 10c0/c8aa7e2fbebfce519654dafadc0ece59be888d2ccaf180fb4495da875e7b536d2456345c384069c7e6f3e9c9ab7435f074957da306f142343eee86ff8048855a + languageName: node + linkType: hard + +"undici@npm:^8.4.1": + version: 8.10.0 + resolution: "undici@npm:8.10.0" + checksum: 10c0/37ae2b1db8f65c3a003504e2040e96887b99f9db16ba07dcf49c37ed8b7f8c72f4452f2d46f52f7c9e49518fe8325e3b5e7e02ac3a2424886bd67d4b62a0ecab + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.3.0": + version: 1.3.2 + resolution: "update-browserslist-db@npm:1.3.2" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/837b4ea8c4934228c5c2a922ea747a54b0ec9d1c408d2fbc9220a9b98327c04109776e6ea1206fa056ea61ed8534863af472d9f2cfce569d35f04e98c95db739 + languageName: node + linkType: hard + +"urdf-loader@npm:^0.12.7": + version: 0.12.7 + resolution: "urdf-loader@npm:0.12.7" + peerDependencies: + three: ">=0.152.0" + checksum: 10c0/60edfae78c2ede7509c81db41d7b7ec0a2d0951345c5aad6528b28321c4b45f3a601222f925010b3432f7ffb2ca042709ed6c8c2a91309c0050354d79d3db2b9 + languageName: node + linkType: hard + +"uri-js@npm:^4.2.2": + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10c0/4ef57b45aa820d7ac6496e9208559986c665e49447cb072744c13b66925a362d96dd5a46c4530a6b8e203e5db5fe849369444440cb22ecfc26c679359e5dfa3c + languageName: node + linkType: hard + +"use-sync-external-store@npm:^1.2.2, use-sync-external-store@npm:^1.4.0": + version: 1.6.0 + resolution: "use-sync-external-store@npm:1.6.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/35e1179f872a53227bdf8a827f7911da4c37c0f4091c29b76b1e32473d1670ebe7bcd880b808b7549ba9a5605c233350f800ffab963ee4a4ee346ee983b6019b + languageName: node + linkType: hard + +"utility-types@npm:^3.11.0": + version: 3.11.0 + resolution: "utility-types@npm:3.11.0" + checksum: 10c0/2f1580137b0c3e6cf5405f37aaa8f5249961a76d26f1ca8efc0ff49a2fc0e0b2db56de8e521a174d075758e0c7eb3e590edec0832eb44478b958f09914920f19 + languageName: node + linkType: hard + +"vary@npm:^1": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: 10c0/f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f + languageName: node + linkType: hard + +"vite@npm:5.4.14": + version: 5.4.14 + resolution: "vite@npm:5.4.14" + dependencies: + esbuild: "npm:^0.21.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.43" + rollup: "npm:^4.20.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/8842933bd70ca6a98489a0bb9c8464bec373de00f9a97c8c7a4e64b24d15c88bfaa8c1acb38a68c3e5eb49072ffbccb146842c2d4edcdd036a9802964cffe3d1 + languageName: node + linkType: hard + +"webgl-constants@npm:^1.1.1": + version: 1.1.1 + resolution: "webgl-constants@npm:1.1.1" + checksum: 10c0/525a9838b0cbcbab902d1bc5457882424a9c146f851042c901ae457123193f5c5e47846b47c1c999e4f3abc24322cac494cea0cc52fec28bd8eb3a05cd79d573 + languageName: node + linkType: hard + +"webgl-sdf-generator@npm:1.1.1": + version: 1.1.1 + resolution: "webgl-sdf-generator@npm:1.1.1" + checksum: 10c0/769bfab22a9485a59bda9448d1d8684228adc9ebdac2cc69012a0854282385d1bef319f703758829e2c647e7532a9844e62977110e825bd75a3e0b8a0ee276ce + languageName: node + linkType: hard + +"webworkify-webpack@npm:^2.1.5": + version: 2.1.5 + resolution: "webworkify-webpack@npm:2.1.5" + checksum: 10c0/672108eef17df3d01d7a499509d722a09dd65dd6e7f9316c21cd559c106fb162a1304b5f326977748aa92ad2bab917a0891ce26d244da083a5e16b752ab91058 + languageName: node + linkType: hard + +"webworkify@npm:^1.5.0": + version: 1.5.0 + resolution: "webworkify@npm:1.5.0" + checksum: 10c0/f451e42c5dc6e8f467736788ce05eef74455d6735705e5459825250b1c10db3e6087abdca149d965e86974a988109c4aa328d93ace3a252bad2f2889ab181bbb + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"which@npm:^7.0.0": + version: 7.0.0 + resolution: "which@npm:7.0.0" + dependencies: + isexe: "npm:^4.0.0" + bin: + node-which: bin/which.js + checksum: 10c0/ca0b54f198f78bbc4b7c02e34bda8d335cb352e0adb4cbca1c37b1a957af3a879a82c4c27ca6525bc942f548d8b64f816ef6528360af9f3de55ffb9b979b620d + languageName: node + linkType: hard + +"word-wrap@npm:^1.2.5": + version: 1.2.5 + resolution: "word-wrap@npm:1.2.5" + checksum: 10c0/e0e4a1ca27599c92a6ca4c32260e8a92e8a44f4ef6ef93f803f8ed823f486e0889fc0b93be4db59c8d51b3064951d25e43d434e95dc8c960cc3a63d65d00ba20 + languageName: node + linkType: hard + +"ws@npm:^8.0.0, ws@npm:~8.21.0": + version: 8.21.3 + resolution: "ws@npm:8.21.3" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/7b28dc2863ea0e2cece68d142a3eee90361021b73f750431e6d8076bb7dede5fdfdb75b3d29534b62f411261147b76b1bc80fb5cc63ab0aabd467280e85b22e0 + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f + languageName: node + linkType: hard + +"zustand@npm:^4.3.2": + version: 4.5.7 + resolution: "zustand@npm:4.5.7" + dependencies: + use-sync-external-store: "npm:^1.2.2" + peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0.6" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + checksum: 10c0/55559e37a82f0c06cadc61cb08f08314c0fe05d6a93815e41e3376130c13db22a5017cbb0cd1f018c82f2dad0051afe3592561d40f980bd4082e32005e8a950c + languageName: node + linkType: hard + +"zustand@npm:^5.0.1, zustand@npm:^5.0.3": + version: 5.0.15 + resolution: "zustand@npm:5.0.15" + peerDependencies: + "@types/react": ">=18.0.0" + immer: ">=9.0.6" + react: ">=18.0.0" + use-sync-external-store: ">=1.2.0" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + checksum: 10c0/ed3a883950a8c22d98e8c72cef103af18043ce66b45faf218ad20ccc8ee29716aacfc6b0a3e783b2152ea38b7663f3982bb6bf2b13ce3df692b05276446afa04 + languageName: node + linkType: hard