From c59a730c8032d819aad537c5ac1181e8a1708506 Mon Sep 17 00:00:00 2001 From: chelproc Date: Sat, 26 Jul 2025 13:13:48 +0900 Subject: [PATCH 1/4] 20250726 update --- .../edit/Editor/components/ContextMenu.tsx | 6 +- .../components/NodePinPropertyEditor.tsx | 24 ++- src/pages/edit/Editor/renderer/Connection.tsx | 32 +++- src/pages/edit/Editor/renderer/Node.tsx | 9 +- src/pages/edit/Editor/renderer/NodePin.tsx | 12 +- .../edit/Editor/store/slices/core/index.ts | 22 +-- src/pages/home/index.tsx | 113 ++++++++++--- src/store/componentEvaluator.ts | 18 +-- src/store/componentPin.ts | 89 +++------- src/store/connection.ts | 26 +-- src/store/intrinsics.ts | 1 - src/store/intrinsics/base.ts | 4 +- src/store/intrinsics/definitions.ts | 8 +- src/store/nodePin.ts | 153 +++++++++++------- 14 files changed, 314 insertions(+), 203 deletions(-) delete mode 100644 src/store/intrinsics.ts diff --git a/src/pages/edit/Editor/components/ContextMenu.tsx b/src/pages/edit/Editor/components/ContextMenu.tsx index 5daf777..86d525d 100644 --- a/src/pages/edit/Editor/components/ContextMenu.tsx +++ b/src/pages/edit/Editor/components/ContextMenu.tsx @@ -47,9 +47,9 @@ export default function CCComponentEditorContextMenu({ width: "200px", }} > - + {/* Create a node - + */} {componentEditorState.selectedNodeIds.size > 0 && ( { @@ -135,7 +135,7 @@ export default function CCComponentEditorContextMenu({ store.connections.unregister([ ...componentEditorState.selectedConnectionIds, ]); - componentEditorState.selectNode([], true); + // componentEditorState.selectNode([], true); componentEditorState.selectConnection([], false); componentEditorState.closeContextMenu(); }} diff --git a/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx b/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx index 50441c3..eee0265 100644 --- a/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx +++ b/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx @@ -28,7 +28,7 @@ export function CCComponentEditorNodePinPropertyEditor() { .getManyByNodeIdAndComponentPinId(target.nodeId, target.componentPinId) .toSorted((a, b) => a.order - b.order); invariant( - nodePins.every((p) => p.userSpecifiedBitWidth !== null), + nodePins.every((p) => p.manualBitWidth !== null), "NodePinPropertyEditor can only be used for node pins with user specified bit width", ); const componentPinAttributes = nullthrows( @@ -64,7 +64,7 @@ export function CCComponentEditorNodePinPropertyEditor() { const bitWidthList = newBitWidthList ?? - nodePins.map((nodePin) => nullthrows(nodePin.userSpecifiedBitWidth)); + nodePins.map((nodePin) => nullthrows(nodePin.manualBitWidth)); const isTouched = Boolean(newBitWidthList); const isValid = bitWidthList.every((bitWidth) => bitWidth > 0); @@ -103,7 +103,7 @@ export function CCComponentEditorNodePinPropertyEditor() { componentPinId: target.componentPinId, nodeId: target.nodeId, order: ++maxOrder, - userSpecifiedBitWidth: bitWidth, + manualBitWidth: bitWidth, }), ); continue; @@ -116,10 +116,24 @@ export function CCComponentEditorNodePinPropertyEditor() { // Update NodePin if (nodePin && bitWidth) { maxOrder = nodePin.order; // nodePins are sorted by order - if (nodePin.userSpecifiedBitWidth !== bitWidth) + if (nodePin.manualBitWidth !== bitWidth) { store.nodePins.update(nodePin.id, { - userSpecifiedBitWidth: bitWidth, + manualBitWidth: bitWidth, }); + const connections = store.connections.getConnectionsByNodePinId( + nodePin.id, + ); + for (const connection of connections) { + const anotherNodePinId = connection.from === nodePin.id + ? connection.to + : connection.from; + if (!store.nodePins.isConnectable( + nodePin.id, anotherNodePinId + )) { + store.connections.unregister([connection.id]); + } + } + } continue; } throw new Error("Unreachable"); diff --git a/src/pages/edit/Editor/renderer/Connection.tsx b/src/pages/edit/Editor/renderer/Connection.tsx index 88e2c02..b93180f 100644 --- a/src/pages/edit/Editor/renderer/Connection.tsx +++ b/src/pages/edit/Editor/renderer/Connection.tsx @@ -5,18 +5,34 @@ import { useStore } from "../../../../store/react"; import ensureStoreItem from "../../../../store/react/error"; import { useNode } from "../../../../store/react/selectors"; import getCCComponentEditorRendererNodeGeometry from "./Node.geometry"; +import { useComponentEditorStore } from "../store"; export type CCComponentEditorRendererConnectionCoreProps = { from: { x: number; y: number }; to: { x: number; y: number }; + connectionId?: CCConnectionId; }; export function CCComponentEditorRendererConnectionCore({ from, to, + connectionId, }: CCComponentEditorRendererConnectionCoreProps) { const straightGap = 10; const direction = from.x < to.x ? 1 : -1; + const componentEditorState = useComponentEditorStore()(); + + + const handleClick = (e: React.MouseEvent) => { + if (!connectionId) { + return; + } + componentEditorState.selectConnection( + [connectionId], + !e.shiftKey + ) + } + return ( { + if (!connectionId) { + return; + } + e.preventDefault(); + e.stopPropagation(); + componentEditorState.selectConnection( + [connectionId], + true + ); + componentEditorState.openContextMenu(e); + }} /> ); } @@ -70,6 +99,7 @@ const CCComponentEditorRendererConnection = ensureStoreItem( ); }, diff --git a/src/pages/edit/Editor/renderer/Node.tsx b/src/pages/edit/Editor/renderer/Node.tsx index 6fce541..7d3401e 100644 --- a/src/pages/edit/Editor/renderer/Node.tsx +++ b/src/pages/edit/Editor/renderer/Node.tsx @@ -28,7 +28,9 @@ const CCComponentEditorRendererNode = ensureStoreItem( ); const handlePointerDown = (e: React.PointerEvent) => { - componentEditorState.selectNode([nodeId], true); + if (e.button === 0) { + componentEditorState.selectNode([nodeId], !e.shiftKey); + } setDragStartPosition(vector2.fromDomEvent(e.nativeEvent)); setPreviousNodePosition(node.position); setDragging(true); @@ -65,7 +67,10 @@ const CCComponentEditorRendererNode = ensureStoreItem( onPointerUp={handlePointerUp} onContextMenu={(e) => { e.preventDefault(); - componentEditorState.selectNode([nodeId], true); + const selectedNodeIds = componentEditorState.selectedNodeIds; + if (!selectedNodeIds.has(nodeId)) { + componentEditorState.selectNode([nodeId], true); + } componentEditorState.openContextMenu(e); }} > diff --git a/src/pages/edit/Editor/renderer/NodePin.tsx b/src/pages/edit/Editor/renderer/NodePin.tsx index 1d90d80..0beac3d 100644 --- a/src/pages/edit/Editor/renderer/NodePin.tsx +++ b/src/pages/edit/Editor/renderer/NodePin.tsx @@ -126,7 +126,7 @@ export default function CCComponentEditorRendererNodePin({ setDraggingState(null); }, onClick: () => { - if (nodePin.userSpecifiedBitWidth === null) return; + if (nodePin.manualBitWidth === null) return; componentEditorState.setNodePinPropertyEditorTarget({ componentPinId: nodePin.componentPinId, nodeId: nodePin.nodeId, @@ -156,24 +156,24 @@ export default function CCComponentEditorRendererNodePin({ stroke={theme.palette.textPrimary} strokeWidth={2} /> - {nodePin.userSpecifiedBitWidth !== null && ( + {nodePin.manualBitWidth !== null && ( = 100 + nodePin.manualBitWidth >= 100 ? 4 - : nodePin.userSpecifiedBitWidth >= 10 + : nodePin.manualBitWidth >= 10 ? 6 : 8 } fill={theme.palette.textPrimary} > - {nodePin.userSpecifiedBitWidth >= 100 + {nodePin.manualBitWidth >= 100 ? "99+" - : nodePin.userSpecifiedBitWidth} + : nodePin.manualBitWidth} )} diff --git a/src/pages/edit/Editor/store/slices/core/index.ts b/src/pages/edit/Editor/store/slices/core/index.ts index bcb6fdb..dad7e53 100644 --- a/src/pages/edit/Editor/store/slices/core/index.ts +++ b/src/pages/edit/Editor/store/slices/core/index.ts @@ -68,20 +68,20 @@ export const createComponentEditorStoreCoreSlice: ComponentEditorSliceCreator< getInputValue(componentPinId: CCComponentPinId) { const value = get().inputValues.get(componentPinId); if (!value) { - const multiplexability = - store.componentPins.getComponentPinMultiplexability( + const bitWidthStatus = + store.componentPins.getComponentPinBitWidthStatus( componentPinId, ); - if (multiplexability === "undecidable") { - throw new Error("Cannot determine multiplexability"); - } - if (multiplexability.isMultiplexable) { - const newValue = [false]; + if (bitWidthStatus.isFixed) { + const newValue = new Array(bitWidthStatus.bitWidth).fill( + false, + ); return newValue; } - const newValue = new Array(multiplexability.multiplicity).fill( - false, - ); + if (bitWidthStatus.fixMode === "manual") { + throw new Error("Cannot determine bit width"); + } + const newValue = [false]; return newValue; } return value; @@ -169,7 +169,7 @@ export const createComponentEditorStoreCoreSlice: ComponentEditorSliceCreator< .join() + store.nodePins .getMany() - .map((nodePin) => nodePin.userSpecifiedBitWidth || 0) + .map((nodePin) => nodePin.id + "_" + (nodePin.manualBitWidth || 0)) .join(",") + store.connections .getMany() diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 9661bd1..6001600 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -1,9 +1,20 @@ import { Add as AddIcon, - FileOpen as FileOpenIcon, - Save as SaveIcon, + Upload as UploadIcon, + Download as DownloadIcon, + MoreVert, } from "@mui/icons-material"; -import { Box, Button, Container, Typography } from "@mui/material"; +import { + Box, + Button, + Card, + CardActionArea, + Container, + IconButton, + Menu, + MenuItem, + Typography, +} from "@mui/material"; import { useRef, useState } from "react"; import { ComponentPropertyDialog } from "../../components/ComponentPropertyDialog"; import type { CCStorePropsFromJson } from "../../store"; @@ -18,7 +29,7 @@ export type HomePageProps = { export default function HomePage({ onComponentSelected }: HomePageProps) { const { store, resetStore } = useStore(); const components = useComponents().filter( - (component) => !component.intrinsicType, + (component) => !component.intrinsicType ); const downloadStore = () => { const storeJSON = store.toJSON(); @@ -50,9 +61,14 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { const [isComponentPropertyDialogOpen, setIsComponentPropertyDialogOpen] = useState(false); + const [componentMenuState, setComponentMenuState] = useState<{ + componentId: CCComponentId; + anchorEl: HTMLElement | null; + } | null>(null); + return (
- + File @@ -61,9 +77,9 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { variant="outlined" color="inherit" onClick={downloadStore} - startIcon={} + startIcon={} > - Save + Export inputRef.current?.click()} - startIcon={} + startIcon={} > - Load + Import @@ -92,7 +108,6 @@ export default function HomePage({ onComponentSelected }: HomePageProps) {
+ onComponentSelected(component.id)} + > + {component.name} + + {store.nodes.getManyByParentComponentId(component.id).length}{" "} + nodes + + + theme.spacing(1), + right: (theme) => theme.spacing(1), + }} + onClick={(e) => { + setComponentMenuState({ + componentId: component.id, + anchorEl: e.currentTarget, + }); + }} + > + + + ))} + {componentMenuState && ( + setComponentMenuState(null)} + anchorOrigin={{ + vertical: "bottom", + horizontal: "left", + }} + > + { + setIsComponentPropertyDialogOpen(true); + setComponentMenuState(null); + }} + > + Rename + + { + store.components.unregister(componentMenuState.componentId); + setComponentMenuState(null); + }} + > + Delete + + + )} {isComponentPropertyDialogOpen && ( { - const multiplexability = store.nodePins.getNodePinMultiplexability( + const bitWidthStatus = store.nodePins.getNodePinBitWidthStatus( nodePin.id, ); - const bitWidth = multiplexability.isMultiplexable + const bitWidth = !bitWidthStatus.isFixed ? 1 - : multiplexability.multiplicity; + : bitWidthStatus.bitWidth; return Array(bitWidth).fill(false); }); input[key] = values; @@ -46,21 +46,21 @@ function createOutputShape( store: CCStore, nodePins: CCNodePin[], outputPin: CCComponentPin, -): { multiplicity: number }[] { +): { bitWidth: number }[] { const targetNodePins = nodePins.filter( (nodePin: CCNodePin) => outputPin.id === nodePin.componentPinId, ); targetNodePins.sort((a, b) => a.order - b.order); - const multiplicity = targetNodePins.map((nodePin: CCNodePin) => { - const multiplexability = store.nodePins.getNodePinMultiplexability( + const bitWidth = targetNodePins.map((nodePin: CCNodePin) => { + const bitWidthStatus = store.nodePins.getNodePinBitWidthStatus( nodePin.id, ); - if (multiplexability.isMultiplexable) { + if (!bitWidthStatus.isFixed) { return 1; } - return multiplexability.multiplicity; + return bitWidthStatus.bitWidth; }); - const outputShape = multiplicity.map((multiplicity) => ({ multiplicity })); + const outputShape = bitWidth.map((bitWidth) => ({ bitWidth })); return outputShape; } diff --git a/src/store/componentPin.ts b/src/store/componentPin.ts index 7256f6b..5908203 100644 --- a/src/store/componentPin.ts +++ b/src/store/componentPin.ts @@ -32,30 +32,16 @@ export const ccPinTypes: CCComponentPinType[] = ["input", "output"]; /** null for intrinsic components */ export type CCPinImplementation = CCNodePinId | null; -// | CCPinUserImplementation -// | CCPinIntrinsicImplementation; -// export type CCPinUserImplementation = { -// readonly type: "user"; -// readonly nodeId: CCNodeId; -// readonly pinId: CCComponentPinId; -// }; +export type CCNodePinBitWidthStatus = + | { isFixed: false } + | { isFixed: true; bitWidth: number }; -// export type CCPinIntrinsicImplementation = { -// readonly type: "intrinsic"; -// }; +export type CCComponentPinBitWidthStatus = + | { isFixed: false; fixMode: "automatic" | "manual" } + | { isFixed: true; bitWidth: number }; -export type CCPinMultiplexability = - | { isMultiplexable: true } - | { - isMultiplexable: false; - multiplicity: number; - }; -// | { isMultiplexable: false; multiplicity: number }; - -export type CCComponentPinMultiplexability = - | CCPinMultiplexability - | "undecidable"; +export type CCNodePinFixedBitWidth = number; export type CCComponentPinStoreEvents = { didRegister(pin: CCComponentPin): void; @@ -237,13 +223,13 @@ export class CCComponentPinStore extends EventEmitter } /** - * Get the multiplexability of a component pin + * Get the bit width status of a component pin * @param pinId id of pin - * @returns multiplexability of the pin + * @returns bit width status of the pin */ - getComponentPinMultiplexability( + getComponentPinBitWidthStatus( pinId: CCComponentPinId, - ): CCComponentPinMultiplexability { + ): CCComponentPinBitWidthStatus { const pin = this.#pins.get(pinId); invariant(pin); switch (pin.id) { @@ -262,65 +248,38 @@ export class CCComponentPinStore extends EventEmitter case nullthrows(input.outputPin.id): case nullthrows(flipflop.inputPin.In.id): case nullthrows(flipflop.outputPin.id): { - return { isMultiplexable: true }; + return { isFixed: false, fixMode: "automatic" }; } case nullthrows(aggregate.inputPin.In.id): { - return "undecidable"; + return { isFixed: false, fixMode: "manual" }; } case nullthrows(aggregate.outputPin.id): { - // const multiplicity = nodePins - // .filter((pin) => { - // const componentPin = this.#store.componentPins.get( - // pin.componentPinId, - // ); - // invariant(componentPin); - // return componentPin.type === "input"; - // }) - // .reduce((acc, pin) => { - // invariant(pin.userSpecifiedBitWidth); - // return acc + pin.userSpecifiedBitWidth; - // }, 0); - // return { - // isMultiplexable: false, - // multiplicity, - // }; - return "undecidable"; + return { isFixed: false, fixMode: "manual" }; } case nullthrows(decompose.outputPin.id): { - return "undecidable"; + return { isFixed: false, fixMode: "manual" }; } case nullthrows(decompose.inputPin.In.id): { - // const multiplicity = nodePins - // .filter((pin) => { - // const componentPin = this.#store.componentPins.get( - // pin.componentPinId, - // ); - // invariant(componentPin); - // return componentPin.type === "output"; - // }) - // .reduce((acc, pin) => { - // invariant(pin.userSpecifiedBitWidth); - // return acc + pin.userSpecifiedBitWidth; - // }, 0); - // return { - // isMultiplexable: false, - // multiplicity, - // }; - return "undecidable"; + return { isFixed: false, fixMode: "manual" }; } case nullthrows(broadcast.inputPin.In.id): { - return { isMultiplexable: false, multiplicity: 1 }; + return { isFixed: true, bitWidth: 1 }; } case nullthrows(broadcast.outputPin.id): { - return "undecidable"; + return { isFixed: false, fixMode: "manual" }; } default: { if (pin.implementation === null) { throw new Error("unreachable"); } - return this.#store.nodePins.getNodePinMultiplexability( + const bitWidthStatus = this.#store.nodePins.getNodePinBitWidthStatus( pin.implementation, ); + if (bitWidthStatus.isFixed) { + return bitWidthStatus; + } else { + return { isFixed: false, fixMode: "automatic" }; + } } } } diff --git a/src/store/connection.ts b/src/store/connection.ts index d91f970..a3f09d4 100644 --- a/src/store/connection.ts +++ b/src/store/connection.ts @@ -1,6 +1,5 @@ import EventEmitter from "eventemitter3"; import nullthrows from "nullthrows"; -import invariant from "tiny-invariant"; import type { Opaque } from "type-fest"; import type CCStore from "."; import type { CCComponentId } from "./component"; @@ -67,14 +66,15 @@ export class CCConnectionStore extends EventEmitter { * @param connection connection to be registered */ register(connection: CCConnection): void { - const fromNodeId = nullthrows( - this.#store.nodePins.get(connection.from), - ).nodeId; - const toNodeId = nullthrows(this.#store.nodePins.get(connection.to)).nodeId; - const fromNode = this.#store.nodes.get(fromNodeId); - const toNode = this.#store.nodes.get(toNodeId); - invariant(fromNode && toNode); - invariant(fromNode.parentComponentId === toNode.parentComponentId); + const fromNodePinId = connection.from; + const toNodePinId = connection.to; + if (!this.#store.nodePins.isConnectable(fromNodePinId, toNodePinId)) { + window.alert( + `Cannot connect pins: ${fromNodePinId} and ${toNodePinId + }` + ); + return; + } this.#connections.set(connection.id, connection); this.emit("didRegister", connection); } @@ -110,18 +110,18 @@ export class CCConnectionStore extends EventEmitter { * @returns map of id and connection (read only) */ getConnectionIdsByParentComponentId( - parentComponentId: CCComponentId, + parentComponentId: CCComponentId ): CCConnectionId[] { return [...this.#connections.values()] .filter( - (connection) => connection.parentComponentId === parentComponentId, + (connection) => connection.parentComponentId === parentComponentId ) .map((connection) => connection.id); } getManyByParentComponentId(parentComponentId: CCComponentId): CCConnection[] { return [...this.#connections.values()].filter( - (connection) => connection.parentComponentId === parentComponentId, + (connection) => connection.parentComponentId === parentComponentId ); } @@ -134,7 +134,7 @@ export class CCConnectionStore extends EventEmitter { getConnectionsByNodePinId(nodePinId: CCNodePinId): CCConnection[] { return [...this.#connections.values()].filter( (connection) => - connection.from === nodePinId || connection.to === nodePinId, + connection.from === nodePinId || connection.to === nodePinId ); } diff --git a/src/store/intrinsics.ts b/src/store/intrinsics.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/src/store/intrinsics.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/src/store/intrinsics/base.ts b/src/store/intrinsics/base.ts index fa5767c..157a2ef 100644 --- a/src/store/intrinsics/base.ts +++ b/src/store/intrinsics/base.ts @@ -17,7 +17,7 @@ type Props = { out: IntrinsicComponentPinAttributes; evaluate: ( input: Record, - outputShape: { multiplicity: number }[], + outputShape: { bitWidth: number }[], previousInput: Record, ) => SimulationValue[]; }; @@ -36,7 +36,7 @@ export class IntrinsicComponentDefinition { readonly outputPin: CCComponentPin; readonly evaluate: ( input: Record, - outputShape: { multiplicity: number }[], + outputShape: { bitWidth: number }[], previousInput: Record, ) => SimulationValue[]; diff --git a/src/store/intrinsics/definitions.ts b/src/store/intrinsics/definitions.ts index 67edb95..989a49a 100644 --- a/src/store/intrinsics/definitions.ts +++ b/src/store/intrinsics/definitions.ts @@ -108,9 +108,9 @@ export const decompose = new IntrinsicComponentDefinition({ let currentIndex = 0; for (const shape of outputShape) { outputValue.push([ - ...inputValue.slice(currentIndex, currentIndex + shape.multiplicity), + ...inputValue.slice(currentIndex, currentIndex + shape.bitWidth), ]); - currentIndex += shape.multiplicity; + currentIndex += shape.bitWidth; } return outputValue; }, @@ -128,8 +128,8 @@ export const broadcast = new IntrinsicComponentDefinition({ invariant(input.In[0][0] !== undefined && !input.In[0][1]); const inputValue = input.In[0][0]; invariant(outputShape[0] && !outputShape[1]); - const outputMultiplicity = outputShape[0].multiplicity; - return [Array.from({ length: outputMultiplicity }, () => inputValue)]; + const outputBitWidth = outputShape[0].bitWidth; + return [Array.from({ length: outputBitWidth }, () => inputValue)]; }, }); diff --git a/src/store/nodePin.ts b/src/store/nodePin.ts index de98a0d..e71e3d1 100644 --- a/src/store/nodePin.ts +++ b/src/store/nodePin.ts @@ -3,7 +3,7 @@ import nullthrows from "nullthrows"; import invariant from "tiny-invariant"; import type { Opaque } from "type-fest"; import type CCStore from "."; -import type { CCComponentPinId, CCPinMultiplexability } from "./componentPin"; +import type { CCComponentPinId, CCNodePinBitWidthStatus } from "./componentPin"; import { IntrinsicComponentDefinition } from "./intrinsics/base"; import { aggregate, broadcast, decompose } from "./intrinsics/definitions"; import type { CCNodeId } from "./node"; @@ -15,7 +15,7 @@ export type CCNodePin = { nodeId: CCNodeId; componentPinId: CCComponentPinId; order: number; - userSpecifiedBitWidth: number | null; + manualBitWidth: number | null; }; export type CCNodePinStoreEvents = { @@ -118,7 +118,7 @@ export class CCNodePinStore extends EventEmitter { this.#markedAsDeleted.delete(id); } - update(id: CCNodePinId, value: Pick) { + update(id: CCNodePinId, value: Pick) { const existingNodePin = nullthrows(this.#nodePins.get(id)); const newNodePin = { ...existingNodePin, ...value }; this.#nodePins.set(id, newNodePin); @@ -170,30 +170,33 @@ export class CCNodePinStore extends EventEmitter { } /** - * Get the multiplexability of a node pin + * Get the bit width status of a node pin * @param pinId id of pin * @param nodeId id of node - * @returns multiplexability of the pin + * @returns bit width status of the pin */ - getNodePinMultiplexability(nodePinId: CCNodePinId): CCPinMultiplexability { - const traverseNodePinMultiplexability = ( + getNodePinBitWidthStatus(nodePinId: CCNodePinId): CCNodePinBitWidthStatus { + const traverseNodePinBitWidthStatus = ( targetNodePinId: CCNodePinId, seen: Set, - ): CCPinMultiplexability => { + ): CCNodePinBitWidthStatus => { const { nodeId: targetNodeId, componentPinId: targetComponentPinId, - userSpecifiedBitWidth, + manualBitWidth, } = nullthrows(this.get(targetNodePinId)); seen.add(targetNodeId); const targetNode = nullthrows(this.#store.nodes.get(targetNodeId)); const targetNodePins = this.getManyByNodeId(targetNode.id); - const givenPinMultiplexability = - this.#store.componentPins.getComponentPinMultiplexability( + const givenComponentPinBitWidthStatus = + this.#store.componentPins.getComponentPinBitWidthStatus( targetComponentPinId, ); - if (givenPinMultiplexability === "undecidable") { + if (givenComponentPinBitWidthStatus.isFixed) { + return givenComponentPinBitWidthStatus; + } + if (givenComponentPinBitWidthStatus.fixMode === "manual") { const componentPin = this.#store.componentPins.get(targetComponentPinId); invariant(componentPin); @@ -202,15 +205,15 @@ export class CCNodePinStore extends EventEmitter { case nullthrows(broadcast.outputPin.id): case nullthrows(decompose.outputPin.id): invariant( - userSpecifiedBitWidth, - "aggregate inputPin, broadcast outputPin, or decompose outputPin must have a userSpecifiedBitWidth", + manualBitWidth, + "aggregate inputPin, broadcast outputPin, or decompose outputPin must have a manual bit width", ); return { - isMultiplexable: false, - multiplicity: userSpecifiedBitWidth, + isFixed: true, + bitWidth: manualBitWidth, }; case nullthrows(aggregate.outputPin.id): { - const multiplicity = targetNodePins + const bitWidth = targetNodePins .filter((pin) => { const componentPin = this.#store.componentPins.get( pin.componentPinId, @@ -219,16 +222,16 @@ export class CCNodePinStore extends EventEmitter { return componentPin.type === "input"; }) .reduce((acc, pin) => { - invariant(pin.userSpecifiedBitWidth); - return acc + pin.userSpecifiedBitWidth; + invariant(pin.manualBitWidth); + return acc + pin.manualBitWidth; }, 0); return { - isMultiplexable: false, - multiplicity, + isFixed: true, + bitWidth, }; } case nullthrows(decompose.inputPin.In.id): { - const multiplicity = targetNodePins + const bitWidth = targetNodePins .filter((pin) => { const componentPin = this.#store.componentPins.get( pin.componentPinId, @@ -237,70 +240,106 @@ export class CCNodePinStore extends EventEmitter { return componentPin.type === "output"; }) .reduce((acc, pin) => { - invariant(pin.userSpecifiedBitWidth); - return acc + pin.userSpecifiedBitWidth; + invariant(pin.manualBitWidth); + return acc + pin.manualBitWidth; }, 0); return { - isMultiplexable: false, - multiplicity, + isFixed: true, + bitWidth, }; } default: throw new Error( - `Multiplexability of ${componentPin.id} is undecidable`, + `Bit width status of ${componentPin.id} is undecidable`, ); } } - if (!givenPinMultiplexability.isMultiplexable) { - return givenPinMultiplexability; - } for (const targetNodePin of targetNodePins) { - const pinMultiplexability = - this.#store.componentPins.getComponentPinMultiplexability( + const targetComponentPinBitWidthStatus = + this.#store.componentPins.getComponentPinBitWidthStatus( targetNodePin.componentPinId, ); - if (pinMultiplexability === "undecidable") { + if (targetComponentPinBitWidthStatus.isFixed) { + continue; + } + if (targetComponentPinBitWidthStatus.fixMode === "manual") { throw new Error("unreachable"); } - if (pinMultiplexability.isMultiplexable) { - const connections = nullthrows( - this.#store.connections.getConnectionsByNodePinId(targetNodePin.id), + const connections = nullthrows( + this.#store.connections.getConnectionsByNodePinId(targetNodePin.id), + ); + for (const connection of connections) { + const componentPin = nullthrows( + this.#store.componentPins.get(targetNodePin.componentPinId), ); - for (const connection of connections) { - const componentPin = nullthrows( - this.#store.componentPins.get(targetNodePin.componentPinId), - ); - const connectedNodePinId = - componentPin.type === "input" ? connection.from : connection.to; - const connectedNodePin = nullthrows(this.get(connectedNodePinId)); - if (seen.has(connectedNodePin.nodeId)) { - continue; - } - const connectedPinMultiplexability = - traverseNodePinMultiplexability(connectedNodePinId, seen); - if (!connectedPinMultiplexability.isMultiplexable) { - return connectedPinMultiplexability; - } + const connectedNodePinId = + componentPin.type === "input" ? connection.from : connection.to; + const connectedNodePin = nullthrows(this.get(connectedNodePinId)); + if (seen.has(connectedNodePin.nodeId)) { + continue; + } + const connectedPinBitWidthStatus = + traverseNodePinBitWidthStatus(connectedNodePinId, seen); + if (connectedPinBitWidthStatus.isFixed) { + return connectedPinBitWidthStatus; } } } - return givenPinMultiplexability; + return givenComponentPinBitWidthStatus; }; - return traverseNodePinMultiplexability(nodePinId, new Set()); + return traverseNodePinBitWidthStatus(nodePinId, new Set()); } isMarkedAsDeleted(id: CCNodePinId) { return this.#markedAsDeleted.has(id); } + isConnectable(a: CCNodePinId, b: CCNodePinId) { + const aNodePin = this.get(a); + const bNodePin = this.get(b); + if (!aNodePin || !bNodePin) { + throw new Error( + `Node pin ${a} or ${b} does not exist in the store`, + ); + } + const aNode = this.#store.nodes.get(aNodePin.nodeId); + const bNode = this.#store.nodes.get(bNodePin.nodeId); + if (!aNode || !bNode) { + throw new Error( + `Node ${aNodePin.nodeId} or ${bNodePin.nodeId} does not exist in the store`, + ); + } + if (aNode.id === bNode.id) { + console.warn( + `Cannot connect pins of the same node: ${aNodePin.id} and ${bNodePin.id}`, + ); + return false; + } + if (aNode.parentComponentId !== bNode.parentComponentId) { + console.warn( + `Cannot connect pins of different components: ${aNodePin.id} and ${bNodePin.id}`, + ); + return false; + } + const aBitWidthStatus = this.getNodePinBitWidthStatus(a); + const bBitWidthStatus = this.getNodePinBitWidthStatus(b); + if (aBitWidthStatus.isFixed && bBitWidthStatus.isFixed) { + console.warn( + `Cannot connect pins with fixed bit width: ${aNodePin.id} and ${bNodePin.id}`, + ); + return aBitWidthStatus.bitWidth === bBitWidthStatus.bitWidth; + } + return true; + } + /** * Create a new pin * @param partialPin pin without `id` * @returns a new pin */ static create( - partialPin: Omit & - Partial>, + partialPin: Omit & + Partial>, ): CCNodePin { const attributes = IntrinsicComponentDefinition.intrinsicComponentPinAttributesByComponentPinId.get( @@ -309,8 +348,8 @@ export class CCNodePinStore extends EventEmitter { return { ...partialPin, id: crypto.randomUUID() as CCNodePinId, - userSpecifiedBitWidth: - partialPin.userSpecifiedBitWidth ?? + manualBitWidth: + partialPin.manualBitWidth ?? (attributes?.isBitWidthConfigurable ? 1 : null), }; } From c4f4f99cebd9c34c906f8e14e531cb0180d303fc Mon Sep 17 00:00:00 2001 From: chelproc Date: Sat, 30 Aug 2025 13:28:03 +0900 Subject: [PATCH 2/4] update --- biome.json | 2 +- package-lock.json | 1680 ++++++++++------- package.json | 36 +- .../components/NodePinPropertyEditor.tsx | 15 +- src/pages/edit/Editor/renderer/Background.tsx | 1 + src/pages/edit/Editor/renderer/Connection.tsx | 66 +- src/pages/edit/Editor/renderer/Node.tsx | 3 +- src/pages/edit/Editor/renderer/NodePin.tsx | 8 +- .../edit/Editor/store/slices/core/index.ts | 10 +- .../edit/Editor/store/slices/core/types.ts | 2 +- src/pages/edit/SidePanel.tsx | 3 +- src/pages/home/index.tsx | 4 +- src/store/componentEvaluator.ts | 143 +- src/store/connection.ts | 13 +- src/store/fixtures.ts | 53 + src/store/intrinsics/base.ts | 2 +- src/store/intrinsics/definitions.ts | 19 +- src/store/node.test.ts | 12 + src/store/node.ts | 2 +- src/store/nodePin.ts | 12 +- src/store/react/error.tsx | 2 +- src/store/react/selectors.ts | 2 +- src/store/testUtils.ts | 31 + 23 files changed, 1345 insertions(+), 776 deletions(-) create mode 100644 src/store/fixtures.ts create mode 100644 src/store/node.test.ts create mode 100644 src/store/testUtils.ts diff --git a/biome.json b/biome.json index 3238a74..6a8dcc0 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "$schema": "https://biomejs.dev/schemas/2.2.2/schema.json", "vcs": { "enabled": true, "clientKind": "git", diff --git a/package-lock.json b/package-lock.json index 29fd7f0..9b6547f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,31 +9,32 @@ "version": "0.0.0", "dependencies": { "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.3.0", - "@mui/material": "^6.3.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.1", + "@mui/material": "^7.3.1", + "es-toolkit": "^1.39.10", "eventemitter3": "^5.0.1", - "lodash-es": "^4.17.21", "memoize-one": "^6.0.0", - "mnemonist": "^0.39.8", + "mnemonist": "^0.40.3", "nullthrows": "^1.1.1", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", "react-use": "^17.6.0", "tiny-invariant": "^1.3.3", - "transformation-matrix": "^2.16.1", - "zustand": "^5.0.2" + "transformation-matrix": "^3.1.0", + "zustand": "^5.0.8" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", + "@biomejs/biome": "^2.2.2", "@tsconfig/strictest": "^2.0.5", "@types/lodash-es": "^4.17.12", - "@types/react": "^19.0.2", - "@types/react-dom": "^19.0.2", - "@vitejs/plugin-react": "^4.3.4", - "type-fest": "^4.31.0", - "typescript": "^5.7.2", - "vite": "^6.0.6" + "@types/react": "^19.1.12", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.2", + "type-fest": "^4.41.0", + "typescript": "^5.9.2", + "vite": "^7.1.3", + "vitest": "^3.2.4" } }, "node_modules/@ampproject/remapping": { @@ -41,7 +42,6 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -51,46 +51,43 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -109,19 +106,17 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -129,14 +124,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -145,29 +139,35 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -177,64 +177,58 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "license": "MIT", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -244,13 +238,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -260,13 +253,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -276,69 +268,60 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", - "license": "MIT", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", - "license": "MIT", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", - "license": "MIT", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@biomejs/biome": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", - "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.2.tgz", + "integrity": "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==", "dev": true, - "hasInstallScript": true, - "license": "MIT OR Apache-2.0", "bin": { "biome": "bin/biome" }, @@ -350,25 +333,24 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.9.4", - "@biomejs/cli-darwin-x64": "1.9.4", - "@biomejs/cli-linux-arm64": "1.9.4", - "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" + "@biomejs/cli-darwin-arm64": "2.2.2", + "@biomejs/cli-darwin-x64": "2.2.2", + "@biomejs/cli-linux-arm64": "2.2.2", + "@biomejs/cli-linux-arm64-musl": "2.2.2", + "@biomejs/cli-linux-x64": "2.2.2", + "@biomejs/cli-linux-x64-musl": "2.2.2", + "@biomejs/cli-win32-arm64": "2.2.2", + "@biomejs/cli-win32-x64": "2.2.2" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", - "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.2.tgz", + "integrity": "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" @@ -378,14 +360,13 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", - "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz", + "integrity": "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==", "cpu": [ "x64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "darwin" @@ -395,14 +376,13 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", - "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.2.tgz", + "integrity": "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -412,14 +392,13 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", - "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.2.tgz", + "integrity": "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -429,14 +408,13 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", - "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.2.tgz", + "integrity": "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -446,14 +424,13 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", - "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.2.tgz", + "integrity": "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==", "cpu": [ "x64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "linux" @@ -463,14 +440,13 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", - "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.2.tgz", + "integrity": "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" @@ -480,14 +456,13 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", - "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.2.tgz", + "integrity": "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT OR Apache-2.0", "optional": true, "os": [ "win32" @@ -593,10 +568,9 @@ "license": "MIT" }, "node_modules/@emotion/styled": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", - "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", - "license": "MIT", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -643,14 +617,13 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "aix" @@ -660,14 +633,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -677,14 +649,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -694,14 +665,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -711,14 +681,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -728,14 +697,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -745,14 +713,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -762,14 +729,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -779,14 +745,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -796,14 +761,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -813,14 +777,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -830,14 +793,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -847,14 +809,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -864,14 +825,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -881,14 +841,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -898,14 +857,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -915,14 +873,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -932,14 +889,13 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -949,14 +905,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "netbsd" @@ -966,14 +921,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -983,14 +937,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" @@ -999,15 +952,30 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "sunos" @@ -1017,14 +985,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1034,14 +1001,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1051,14 +1017,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1068,17 +1033,12 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "license": "MIT", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1090,47 +1050,35 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.3.0.tgz", - "integrity": "sha512-/d8NwSuC3rMwCjswmGB3oXC4sdDuhIUJ8inVQAxGrADJhf0eq/kmy+foFKvpYhHl2siOZR+MLdFttw6/Bzqtqg==", - "license": "MIT", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.1.tgz", + "integrity": "sha512-+mIK1Z0BhOaQ0vCgOkT1mSrIpEHLo338h4/duuL4TBLXPvUMit732mnwJY3W40Avy30HdeSfwUAAGRkKmwRaEQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.3.0.tgz", - "integrity": "sha512-3uWws6DveDn5KxCS34p+sUNMxehuclQY6OmoJeJJ+Sfg9L7LGBpksY/nX5ywKAqickTZnn+sQyVcp963ep9jvw==", - "license": "MIT", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.1.tgz", + "integrity": "sha512-upzCtG6awpL6noEZlJ5Z01khZ9VnLNLaj7tb6iPbN6G97eYfUTs8e9OyPKy3rEms3VQWmVBfri7jzeaRxdFIzA==", "dependencies": { - "@babel/runtime": "^7.26.0" + "@babel/runtime": "^7.28.2" }, "engines": { "node": ">=14.0.0" @@ -1140,7 +1088,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.3.0", + "@mui/material": "^7.3.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1151,22 +1099,21 @@ } }, "node_modules/@mui/material": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.3.0.tgz", - "integrity": "sha512-qhlTFyRMxfoVPxUtA5e8IvqxP0dWo2Ij7cvot7Orag+etUlZH+3UwD8gZGt+3irOoy7Ms3UNBflYjwEikUXtAQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.3.0", - "@mui/system": "^6.3.0", - "@mui/types": "^7.2.20", - "@mui/utils": "^6.3.0", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.1.tgz", + "integrity": "sha512-Xf6Shbo03YmcBedZMwSpEFOwpYDtU7tC+rhAHTrA9FHk0FpsDqiQ9jUa1j/9s3HLs7KWb5mDcGnlwdh9Q9KAag==", + "dependencies": { + "@babel/runtime": "^7.28.2", + "@mui/core-downloads-tracker": "^7.3.1", + "@mui/system": "^7.3.1", + "@mui/types": "^7.4.5", + "@mui/utils": "^7.3.1", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1", - "react-is": "^19.0.0", + "react-is": "^19.1.1", "react-transition-group": "^4.4.5" }, "engines": { @@ -1179,7 +1126,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.3.0", + "@mui/material-pigment-css": "^7.3.1", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1200,13 +1147,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.3.0.tgz", - "integrity": "sha512-tdS8jvqMokltNTXg6ioRCCbVdDmZUJZa/T9VtTnX2Lwww3FTgCakst9tWLZSxm1fEE9Xp0m7onZJmgeUmWQYVw==", - "license": "MIT", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.1.tgz", + "integrity": "sha512-WU3YLkKXii/x8ZEKnrLKsPwplCVE11yZxUvlaaZSIzCcI3x2OdFC8eMlNy74hVeUsYQvzzX1Es/k4ARPlFvpPQ==", "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.3.0", + "@babel/runtime": "^7.28.2", + "@mui/utils": "^7.3.1", "prop-types": "^15.8.1" }, "engines": { @@ -1227,13 +1173,12 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.3.0.tgz", - "integrity": "sha512-iWA6eyiPkO07AlHxRUvI7dwVRSc+84zV54kLmjUms67GJeOWVuXlu8ZO+UhCnwJxHacghxnabsMEqet5PYQmHg==", - "license": "MIT", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.1.tgz", + "integrity": "sha512-Nqo6OHjvJpXJ1+9TekTE//+8RybgPQUKwns2Lh0sq+8rJOUSUKS3KALv4InSOdHhIM9Mdi8/L7LTF1/Ky6D6TQ==", "dependencies": { - "@babel/runtime": "^7.26.0", - "@emotion/cache": "^11.13.5", + "@babel/runtime": "^7.28.2", + "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", @@ -1261,16 +1206,15 @@ } }, "node_modules/@mui/system": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.3.0.tgz", - "integrity": "sha512-L+8hUHMNlfReKSqcnVslFrVhoNfz/jw7Fe9NfDE85R3KarvZ4O3MU9daF/lZeqEAvnYxEilkkTfDwQ7qCgJdFg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.3.0", - "@mui/styled-engine": "^6.3.0", - "@mui/types": "^7.2.20", - "@mui/utils": "^6.3.0", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.1.tgz", + "integrity": "sha512-mIidecvcNVpNJMdPDmCeoSL5zshKBbYPcphjuh6ZMjhybhqhZ4mX6k9zmIWh6XOXcqRQMg5KrcjnO0QstrNj3w==", + "dependencies": { + "@babel/runtime": "^7.28.2", + "@mui/private-theming": "^7.3.1", + "@mui/styled-engine": "^7.3.1", + "@mui/types": "^7.4.5", + "@mui/utils": "^7.3.1", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1301,10 +1245,12 @@ } }, "node_modules/@mui/types": { - "version": "7.2.20", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.20.tgz", - "integrity": "sha512-straFHD7L8v05l/N5vcWk+y7eL9JF0C2mtph/y4BPm3gn2Eh61dDwDB65pa8DLss3WJfDXYC7Kx5yjP0EmXpgw==", - "license": "MIT", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.5.tgz", + "integrity": "sha512-ZPwlAOE3e8C0piCKbaabwrqZbW4QvWz0uapVPWya7fYj6PeDkl5sSJmomT7wjOcZGPB48G/a6Ubidqreptxz4g==", + "dependencies": { + "@babel/runtime": "^7.28.2" + }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1315,17 +1261,16 @@ } }, "node_modules/@mui/utils": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.3.0.tgz", - "integrity": "sha512-MkDBF08OPVwXhAjedyMykRojgvmf0y/jxkBWjystpfI/pasyTYrfdv4jic6s7j3y2+a+SJzS9qrD6X8ZYj/8AQ==", - "license": "MIT", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.1.tgz", + "integrity": "sha512-/31y4wZqVWa0jzMnzo6JPjxwP6xXy4P3+iLbosFg/mJQowL1KIou0LC+lquWW60FKVbKz5ZUWBg2H3jausa0pw==", "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.20", - "@types/prop-types": "^15.7.14", + "@babel/runtime": "^7.28.2", + "@mui/types": "^7.4.5", + "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^19.0.0" + "react-is": "^19.1.1" }, "engines": { "node": ">=14.0.0" @@ -1348,273 +1293,272 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.34.tgz", + "integrity": "sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==", + "dev": true + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", - "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.49.0.tgz", + "integrity": "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz", - "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.49.0.tgz", + "integrity": "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz", - "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz", + "integrity": "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz", - "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.49.0.tgz", + "integrity": "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz", - "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.49.0.tgz", + "integrity": "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz", - "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.49.0.tgz", + "integrity": "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz", - "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.49.0.tgz", + "integrity": "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz", - "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.49.0.tgz", + "integrity": "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz", - "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.49.0.tgz", + "integrity": "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz", - "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.49.0.tgz", + "integrity": "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz", - "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.49.0.tgz", + "integrity": "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz", - "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.49.0.tgz", + "integrity": "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz", - "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.49.0.tgz", + "integrity": "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.49.0.tgz", + "integrity": "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz", - "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.49.0.tgz", + "integrity": "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", - "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.49.0.tgz", + "integrity": "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz", - "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.49.0.tgz", + "integrity": "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz", - "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.49.0.tgz", + "integrity": "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz", - "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.49.0.tgz", + "integrity": "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz", - "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz", + "integrity": "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -1672,12 +1616,26 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", "dev": true, - "license": "MIT" + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true }, "node_modules/@types/js-cookie": { "version": "2.2.7", @@ -1709,26 +1667,23 @@ "license": "MIT" }, "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "license": "MIT" + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==" }, "node_modules/@types/react": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.2.tgz", - "integrity": "sha512-USU8ZI/xyKJwFTpjSVIrSeHBVAGagkHQKPNbxeWwql/vDmnTIBgx+TJnhFnj1NXgz8XfprU0egV2dROLGpsBEg==", - "license": "MIT", + "version": "19.1.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", + "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", - "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz", + "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==", "dev": true, - "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" } @@ -1737,29 +1692,136 @@ "version": "4.4.12", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", - "license": "MIT", "peerDependencies": { "@types/react": "*" } }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.2.tgz", + "integrity": "sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@babel/core": "^7.28.3", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.34", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, "node_modules/@xobotyi/scrollbar-width": { @@ -1768,6 +1830,15 @@ "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==", "license": "MIT" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -1784,9 +1855,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.25.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", + "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==", "dev": true, "funding": [ { @@ -1802,12 +1873,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" + "caniuse-lite": "^1.0.30001737", + "electron-to-chromium": "^1.5.211", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -1816,6 +1886,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1825,9 +1904,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001737", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", "dev": true, "funding": [ { @@ -1842,14 +1921,37 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "engines": { + "node": ">= 16" + } }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", "engines": { "node": ">=6" } @@ -1932,11 +2034,11 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1947,22 +2049,29 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "node_modules/electron-to-chromium": { - "version": "1.5.56", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.56.tgz", - "integrity": "sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw==", - "dev": true, - "license": "ISC" + "version": "1.5.211", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz", + "integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==", + "dev": true }, "node_modules/error-ex": { "version": "1.3.2", @@ -1982,13 +2091,27 @@ "stackframe": "^1.3.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true + }, + "node_modules/es-toolkit": { + "version": "1.39.10", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz", + "integrity": "sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -1996,31 +2119,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escalade": { @@ -2028,7 +2152,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -2044,11 +2167,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2065,6 +2206,23 @@ "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==", "license": "MIT" }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -2077,7 +2235,6 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -2100,20 +2257,10 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2224,7 +2371,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -2238,11 +2384,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2254,16 +2395,30 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", @@ -2277,18 +2432,17 @@ "license": "MIT" }, "node_modules/mnemonist": { - "version": "0.39.8", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", - "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", - "license": "MIT", + "version": "0.40.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.40.3.tgz", + "integrity": "sha512-Vjyr90sJ23CKKH/qPAgUKicw/v6pRoamxIEDFOF8uSgFME7DqPRpHgRTejWVjkdGg5dXj0/NyxZHZ9bcjH+2uQ==", "dependencies": { - "obliterator": "^2.0.1" + "obliterator": "^2.0.4" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nano-css": { "version": "5.6.2", @@ -2317,9 +2471,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -2327,7 +2481,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2336,11 +2489,10 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true, - "license": "MIT" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true }, "node_modules/nullthrows": { "version": "1.1.1", @@ -2402,16 +2554,43 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -2427,9 +2606,8 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -2453,38 +2631,34 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", - "license": "MIT", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", - "license": "MIT", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "dependencies": { - "scheduler": "^0.25.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.0.0" + "react": "^19.1.1" } }, "node_modules/react-is": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", - "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", - "license": "MIT" + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz", + "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==" }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2493,7 +2667,6 @@ "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -2546,12 +2719,6 @@ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", "license": "0BSD" }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, "node_modules/resize-observer-polyfill": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", @@ -2583,13 +2750,12 @@ } }, "node_modules/rollup": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", - "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", + "version": "4.49.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz", + "integrity": "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==", "dev": true, - "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -2599,25 +2765,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.29.1", - "@rollup/rollup-android-arm64": "4.29.1", - "@rollup/rollup-darwin-arm64": "4.29.1", - "@rollup/rollup-darwin-x64": "4.29.1", - "@rollup/rollup-freebsd-arm64": "4.29.1", - "@rollup/rollup-freebsd-x64": "4.29.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.29.1", - "@rollup/rollup-linux-arm-musleabihf": "4.29.1", - "@rollup/rollup-linux-arm64-gnu": "4.29.1", - "@rollup/rollup-linux-arm64-musl": "4.29.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.29.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1", - "@rollup/rollup-linux-riscv64-gnu": "4.29.1", - "@rollup/rollup-linux-s390x-gnu": "4.29.1", - "@rollup/rollup-linux-x64-gnu": "4.29.1", - "@rollup/rollup-linux-x64-musl": "4.29.1", - "@rollup/rollup-win32-arm64-msvc": "4.29.1", - "@rollup/rollup-win32-ia32-msvc": "4.29.1", - "@rollup/rollup-win32-x64-msvc": "4.29.1", + "@rollup/rollup-android-arm-eabi": "4.49.0", + "@rollup/rollup-android-arm64": "4.49.0", + "@rollup/rollup-darwin-arm64": "4.49.0", + "@rollup/rollup-darwin-x64": "4.49.0", + "@rollup/rollup-freebsd-arm64": "4.49.0", + "@rollup/rollup-freebsd-x64": "4.49.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.49.0", + "@rollup/rollup-linux-arm-musleabihf": "4.49.0", + "@rollup/rollup-linux-arm64-gnu": "4.49.0", + "@rollup/rollup-linux-arm64-musl": "4.49.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.49.0", + "@rollup/rollup-linux-ppc64-gnu": "4.49.0", + "@rollup/rollup-linux-riscv64-gnu": "4.49.0", + "@rollup/rollup-linux-riscv64-musl": "4.49.0", + "@rollup/rollup-linux-s390x-gnu": "4.49.0", + "@rollup/rollup-linux-x64-gnu": "4.49.0", + "@rollup/rollup-linux-x64-musl": "4.49.0", + "@rollup/rollup-win32-arm64-msvc": "4.49.0", + "@rollup/rollup-win32-ia32-msvc": "4.49.0", + "@rollup/rollup-win32-x64-msvc": "4.49.0", "fsevents": "~2.3.2" } }, @@ -2631,10 +2798,9 @@ } }, "node_modules/scheduler": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", - "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", - "license": "MIT" + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" }, "node_modules/screenfull": { "version": "5.2.0", @@ -2666,6 +2832,12 @@ "node": ">=6.9" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -2680,7 +2852,6 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -2694,6 +2865,12 @@ "stackframe": "^1.3.4" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", @@ -2730,6 +2907,30 @@ "stacktrace-gps": "^3.0.4" } }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true + }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -2762,6 +2963,61 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", @@ -2769,10 +3025,9 @@ "license": "MIT" }, "node_modules/transformation-matrix": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-2.16.1.tgz", - "integrity": "sha512-tdtC3wxVEuzU7X/ydL131Q3JU5cPMEn37oqVLITjRDSDsnSHVFzW2JiCLfZLIQEgWzZHdSy3J6bZzvKEN24jGA==", - "license": "MIT", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-3.1.0.tgz", + "integrity": "sha512-oYubRWTi2tYFHAL2J8DLvPIqIYcYZ0fSOi2vmSy042Ho4jBW2ce6VP7QfD44t65WQz6bw5w1Pk22J7lcUpaTKA==", "funding": { "url": "https://github.com/sponsors/chrvadala" } @@ -2790,11 +3045,10 @@ "peer": true }, "node_modules/type-fest": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.31.0.tgz", - "integrity": "sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" }, @@ -2803,11 +3057,10 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2817,9 +3070,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -2835,10 +3088,9 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -2848,21 +3100,23 @@ } }, "node_modules/vite": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.6.tgz", - "integrity": "sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", + "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", "dev": true, - "license": "MIT", "dependencies": { - "esbuild": "^0.24.2", - "postcss": "^8.4.49", - "rollup": "^4.23.0" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -2871,14 +3125,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -2919,12 +3173,121 @@ } } }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/yaml": { "version": "2.6.1", @@ -2942,10 +3305,9 @@ } }, "node_modules/zustand": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.2.tgz", - "integrity": "sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==", - "license": "MIT", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", + "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", "engines": { "node": ">=12.20.0" }, diff --git a/package.json b/package.json index 8e18814..6360953 100644 --- a/package.json +++ b/package.json @@ -10,34 +10,36 @@ "type-check": "tsc", "type-check:watch": "tsc --watch", "check": "biome check", - "check:fix": "biome check --fix" + "check:fix": "biome check --fix", + "test": "vitest" }, "dependencies": { "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^6.3.0", - "@mui/material": "^6.3.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.1", + "@mui/material": "^7.3.1", + "es-toolkit": "^1.39.10", "eventemitter3": "^5.0.1", - "lodash-es": "^4.17.21", "memoize-one": "^6.0.0", - "mnemonist": "^0.39.8", + "mnemonist": "^0.40.3", "nullthrows": "^1.1.1", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", "react-use": "^17.6.0", "tiny-invariant": "^1.3.3", - "transformation-matrix": "^2.16.1", - "zustand": "^5.0.2" + "transformation-matrix": "^3.1.0", + "zustand": "^5.0.8" }, "devDependencies": { - "@biomejs/biome": "^1.9.4", + "@biomejs/biome": "^2.2.2", "@tsconfig/strictest": "^2.0.5", "@types/lodash-es": "^4.17.12", - "@types/react": "^19.0.2", - "@types/react-dom": "^19.0.2", - "@vitejs/plugin-react": "^4.3.4", - "type-fest": "^4.31.0", - "typescript": "^5.7.2", - "vite": "^6.0.6" + "@types/react": "^19.1.12", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.2", + "type-fest": "^4.41.0", + "typescript": "^5.9.2", + "vite": "^7.1.3", + "vitest": "^3.2.4" } } diff --git a/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx b/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx index eee0265..c7aebd0 100644 --- a/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx +++ b/src/pages/edit/Editor/components/NodePinPropertyEditor.tsx @@ -1,5 +1,5 @@ import { Button, Popover, Stack, TextField, Typography } from "@mui/material"; -import { zip } from "lodash-es"; +import { zip } from "es-toolkit"; import nullthrows from "nullthrows"; import { useState } from "react"; import invariant from "tiny-invariant"; @@ -124,12 +124,13 @@ export function CCComponentEditorNodePinPropertyEditor() { nodePin.id, ); for (const connection of connections) { - const anotherNodePinId = connection.from === nodePin.id - ? connection.to - : connection.from; - if (!store.nodePins.isConnectable( - nodePin.id, anotherNodePinId - )) { + const anotherNodePinId = + connection.from === nodePin.id + ? connection.to + : connection.from; + if ( + !store.nodePins.isConnectable(nodePin.id, anotherNodePinId) + ) { store.connections.unregister([connection.id]); } } diff --git a/src/pages/edit/Editor/renderer/Background.tsx b/src/pages/edit/Editor/renderer/Background.tsx index 7dd3b1c..55da52f 100644 --- a/src/pages/edit/Editor/renderer/Background.tsx +++ b/src/pages/edit/Editor/renderer/Background.tsx @@ -6,6 +6,7 @@ export default function CCComponentEditorRendererBackground() { const viewBox = componentEditorState.getViewBox(); return ( + // biome-ignore lint/a11y/noStaticElementInteractions: SVG { diff --git a/src/pages/edit/Editor/renderer/Connection.tsx b/src/pages/edit/Editor/renderer/Connection.tsx index b93180f..ebaa715 100644 --- a/src/pages/edit/Editor/renderer/Connection.tsx +++ b/src/pages/edit/Editor/renderer/Connection.tsx @@ -1,39 +1,42 @@ import nullthrows from "nullthrows"; +import { useState } from "react"; import { theme } from "../../../../common/theme"; import type { CCConnectionId } from "../../../../store/connection"; import { useStore } from "../../../../store/react"; import ensureStoreItem from "../../../../store/react/error"; import { useNode } from "../../../../store/react/selectors"; -import getCCComponentEditorRendererNodeGeometry from "./Node.geometry"; import { useComponentEditorStore } from "../store"; +import getCCComponentEditorRendererNodeGeometry from "./Node.geometry"; export type CCComponentEditorRendererConnectionCoreProps = { from: { x: number; y: number }; to: { x: number; y: number }; connectionId?: CCConnectionId; + onMouseEnter?: React.MouseEventHandler; + onMouseLeave?: React.MouseEventHandler; }; + export function CCComponentEditorRendererConnectionCore({ from, to, connectionId, + onMouseEnter, + onMouseLeave, }: CCComponentEditorRendererConnectionCoreProps) { const straightGap = 10; const direction = from.x < to.x ? 1 : -1; const componentEditorState = useComponentEditorStore()(); - const handleClick = (e: React.MouseEvent) => { if (!connectionId) { return; } - componentEditorState.selectConnection( - [connectionId], - !e.shiftKey - ) - } + componentEditorState.selectConnection([connectionId], !e.shiftKey); + }; return ( + // biome-ignore lint/a11y/noStaticElementInteractions: SVG ); } @@ -75,6 +83,7 @@ const CCComponentEditorRendererConnection = ensureStoreItem( (props, store) => store.connections.get(props.connectionId), ({ connectionId }: CCComponentEditorRendererConnectionProps) => { const { store } = useStore(); + const componentEditorState = useComponentEditorStore()(); const connection = nullthrows(store.connections.get(connectionId)); const fromNodePin = nullthrows(store.nodePins.get(connection.from)); const toNodePin = nullthrows(store.nodePins.get(connection.to)); @@ -95,12 +104,35 @@ const CCComponentEditorRendererConnection = ensureStoreItem( toNodeGeometry.nodePinPositionById.get(toNodePin.id), ); + const [isHovered, setIsHovered] = useState(false); + + // const fromNodePinValue = componentEditorState.getNodePinValue(fromNodePin.id); + return ( - + <> + { + setIsHovered(true); + }} + onMouseLeave={() => { + setIsHovered(false); + }} + /> + {isHovered && componentEditorState.editorMode === "play" && ( + + + hoge + + + )} + ); }, ); diff --git a/src/pages/edit/Editor/renderer/Node.tsx b/src/pages/edit/Editor/renderer/Node.tsx index 7d3401e..ddf682d 100644 --- a/src/pages/edit/Editor/renderer/Node.tsx +++ b/src/pages/edit/Editor/renderer/Node.tsx @@ -61,6 +61,7 @@ const CCComponentEditorRendererNode = ensureStoreItem( return ( <> + {/** biome-ignore lint/a11y/noStaticElementInteractions: SVG */} {component.name} diff --git a/src/pages/edit/Editor/renderer/NodePin.tsx b/src/pages/edit/Editor/renderer/NodePin.tsx index 0beac3d..59cff0f 100644 --- a/src/pages/edit/Editor/renderer/NodePin.tsx +++ b/src/pages/edit/Editor/renderer/NodePin.tsx @@ -171,9 +171,7 @@ export default function CCComponentEditorRendererNodePin({ } fill={theme.palette.textPrimary} > - {nodePin.manualBitWidth >= 100 - ? "99+" - : nodePin.manualBitWidth} + {nodePin.manualBitWidth >= 100 ? "99+" : nodePin.manualBitWidth} )} @@ -186,7 +184,9 @@ export default function CCComponentEditorRendererNodePin({ }[componentPin.type] } y={position.y} - textAnchor={{ input: "start", output: "end" }[componentPin.type]} + textAnchor={ + { input: "start" as const, output: "end" as const }[componentPin.type] + } dominantBaseline="central" fontSize={12} fill={theme.palette.textPrimary} diff --git a/src/pages/edit/Editor/store/slices/core/index.ts b/src/pages/edit/Editor/store/slices/core/index.ts index dad7e53..5614966 100644 --- a/src/pages/edit/Editor/store/slices/core/index.ts +++ b/src/pages/edit/Editor/store/slices/core/index.ts @@ -69,13 +69,9 @@ export const createComponentEditorStoreCoreSlice: ComponentEditorSliceCreator< const value = get().inputValues.get(componentPinId); if (!value) { const bitWidthStatus = - store.componentPins.getComponentPinBitWidthStatus( - componentPinId, - ); + store.componentPins.getComponentPinBitWidthStatus(componentPinId); if (bitWidthStatus.isFixed) { - const newValue = new Array(bitWidthStatus.bitWidth).fill( - false, - ); + const newValue = new Array(bitWidthStatus.bitWidth).fill(false); return newValue; } if (bitWidthStatus.fixMode === "manual") { @@ -169,7 +165,7 @@ export const createComponentEditorStoreCoreSlice: ComponentEditorSliceCreator< .join() + store.nodePins .getMany() - .map((nodePin) => nodePin.id + "_" + (nodePin.manualBitWidth || 0)) + .map((nodePin) => `${nodePin.id}_${nodePin.manualBitWidth || 0}`) .join(",") + store.connections .getMany() diff --git a/src/pages/edit/Editor/store/slices/core/types.ts b/src/pages/edit/Editor/store/slices/core/types.ts index 03b9342..fbad978 100644 --- a/src/pages/edit/Editor/store/slices/core/types.ts +++ b/src/pages/edit/Editor/store/slices/core/types.ts @@ -1,9 +1,9 @@ -import type { SimulationValue } from "."; import type { Vector2 } from "../../../../../../common/vector2"; import type { CCComponentPinId } from "../../../../../../store/componentPin"; import type { CCConnectionId } from "../../../../../../store/connection"; import type { CCNodeId } from "../../../../../../store/node"; import type { CCNodePinId } from "../../../../../../store/nodePin"; +import type { SimulationValue } from "."; export type EditorMode = EditorModeEdit | EditorModePlay; export type EditorModeEdit = "edit"; diff --git a/src/pages/edit/SidePanel.tsx b/src/pages/edit/SidePanel.tsx index 4c3f8a5..f64010d 100644 --- a/src/pages/edit/SidePanel.tsx +++ b/src/pages/edit/SidePanel.tsx @@ -71,7 +71,6 @@ function ComponentRenderer({ componentId }: { componentId: CCComponentId }) { }} >
-
{pin.name}
+
{pin.name}
))}
diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 6001600..69c674b 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -1,8 +1,8 @@ import { Add as AddIcon, - Upload as UploadIcon, Download as DownloadIcon, MoreVert, + Upload as UploadIcon, } from "@mui/icons-material"; import { Box, @@ -29,7 +29,7 @@ export type HomePageProps = { export default function HomePage({ onComponentSelected }: HomePageProps) { const { store, resetStore } = useStore(); const components = useComponents().filter( - (component) => !component.intrinsicType + (component) => !component.intrinsicType, ); const downloadStore = () => { const storeJSON = store.toJSON(); diff --git a/src/store/componentEvaluator.ts b/src/store/componentEvaluator.ts index d25f1fe..d4283a8 100644 --- a/src/store/componentEvaluator.ts +++ b/src/store/componentEvaluator.ts @@ -1,10 +1,10 @@ import nullthrows from "nullthrows"; import invariant from "tiny-invariant"; -import type CCStore from "."; import type { SimulationFrame, SimulationValue, } from "../pages/edit/Editor/store/slices/core"; +import type CCStore from "."; import type { CCComponentId } from "./component"; import type { CCComponentPin, CCComponentPinId } from "./componentPin"; import { definitionByComponentId, flipflop } from "./intrinsics/definitions"; @@ -52,9 +52,7 @@ function createOutputShape( ); targetNodePins.sort((a, b) => a.order - b.order); const bitWidth = targetNodePins.map((nodePin: CCNodePin) => { - const bitWidthStatus = store.nodePins.getNodePinBitWidthStatus( - nodePin.id, - ); + const bitWidthStatus = store.nodePins.getNodePinBitWidthStatus(nodePin.id); if (!bitWidthStatus.isFixed) { return 1; } @@ -64,12 +62,13 @@ function createOutputShape( return outputShape; } +// return: Map (output pins only) function simulateIntrinsic( store: CCStore, nodeId: CCNodeId, inputValues: Map, parentPreviousFrame: SimulationFrame | null, -): Map | null { +): Map { const node = nullthrows(store.nodes.get(nodeId)); const { componentId } = node; const nodePins = store.nodePins.getManyByNodeId(nodeId); @@ -161,7 +160,10 @@ function simulateNode( const children = store.nodes.getManyByParentComponentId(component.id); const foundInputNumber = new Map(); const nodePinInputNumber = new Map(); - const nodePinInputValues = new Map(); + const nodePinInputValues = new Map< + CCNodeId, + Map + >(); for (const child of children) { foundInputNumber.set(child.id, 0); const innerPins = store.nodePins.getManyByNodeId(child.id); @@ -175,6 +177,7 @@ function simulateNode( } } nodePinInputNumber.set(child.id, inputPinNumber); + nodePinInputValues.set(child.id, new Map()); } for (const nodePin of nodePins) { const componentPin = nullthrows( @@ -185,8 +188,11 @@ function simulateNode( store.nodePins.get(componentPin.implementation), ); nodePinInputValues.set( - connectedNodePin.id, - nullthrows(inputValues.get(nodePin.id)), + connectedNodePin.nodeId, + nullthrows(nodePinInputValues.get(connectedNodePin.nodeId)).set( + connectedNodePin.id, + nullthrows(inputValues.get(nodePin.id)), + ), ); foundInputNumber.set( connectedNodePin.nodeId, @@ -218,7 +224,7 @@ function simulateNode( const result = simulateNode( store, currentNodeId, - nodePinInputValues, + nodePinInputValues.get(currentNodeId) || new Map(), frame, ); if (!result) { @@ -235,7 +241,13 @@ function simulateNode( const connectedNodePin = nullthrows( store.nodePins.get(connection.to), ); - nodePinInputValues.set(connectedNodePin.id, outputValue); + nodePinInputValues.set( + connectedNodePin.nodeId, + nullthrows(nodePinInputValues.get(connectedNodePin.nodeId)).set( + connectedNodePin.id, + outputValue, + ), + ); foundInputNumber.set( connectedNodePin.nodeId, nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, @@ -270,7 +282,7 @@ function simulateNode( const result = simulateNode( store, currentNodeId, - nodePinInputValues, + nodePinInputValues.get(currentNodeId) || new Map(), frame, ); if (!result) { @@ -287,7 +299,13 @@ function simulateNode( const connectedNodePin = nullthrows( store.nodePins.get(connection.to), ); - nodePinInputValues.set(connectedNodePin.id, outputValue); + nodePinInputValues.set( + connectedNodePin.nodeId, + nullthrows(nodePinInputValues.get(connectedNodePin.nodeId)).set( + connectedNodePin.id, + outputValue, + ), + ); foundInputNumber.set( connectedNodePin.nodeId, nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, @@ -351,6 +369,8 @@ export default function simulateComponent( const foundInputNumber = new Map(); const nodePinInputNumber = new Map(); const nodePinInputValues = new Map(); + + // Initialize maps for (const child of children) { foundInputNumber.set(child.id, 0); const innerPins = store.nodePins.getManyByNodeId(child.id); @@ -365,6 +385,8 @@ export default function simulateComponent( } nodePinInputNumber.set(child.id, inputPinNumber); } + + // Set input values for component from parent for (const componentPin of componentPins) { if (componentPin.type === "input" && componentPin.implementation) { const connectedNodePin = nullthrows( @@ -380,6 +402,7 @@ export default function simulateComponent( ); } } + const unevaluatedNodes = new Set(); for (const child of children) { unevaluatedNodes.add(child.id); @@ -387,7 +410,50 @@ export default function simulateComponent( const outputValues = new Map(); const outputNodePinValues = new Map(); - const visitedFlipFlops = new Set(); + + // Evaluate flipflops first + for (const child of children) { + if (child.componentId === flipflop.id) { + const dummyInputValues = new Map(); + const flipFlopNodePins = store.nodePins.getManyByNodeId(child.id); + for (const nodePin of flipFlopNodePins) { + dummyInputValues.set(nodePin.id, [false]); + } + const flipFlopOutputValues = simulateIntrinsic( + store, + child.id, + dummyInputValues, + previousFrame, + ); + for (const [outputPinId, outputValue] of flipFlopOutputValues) { + outputNodePinValues.set(outputPinId, outputValue); + const connections = + store.connections.getConnectionsByNodePinId(outputPinId); + if (connections.length !== 0) { + for (const connection of connections) { + const connectedNodePin = nullthrows( + store.nodePins.get(connection.to), + ); + nodePinInputValues.set(connectedNodePin.id, outputValue); + foundInputNumber.set( + connectedNodePin.nodeId, + nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, + ); + } + } else { + const parentComponentPin = nullthrows( + componentPins.find((componentPin) => { + return ( + componentPin.type === "output" && + componentPin.implementation === outputPinId + ); + }), + ); + outputValues.set(parentComponentPin.id, outputValue); + } + } + } + } while (unevaluatedNodes.size > 0) { const currentNodeId = nullthrows([...unevaluatedNodes][0]); @@ -424,36 +490,35 @@ export default function simulateComponent( return null; } childMap.set(currentNodeId, result); + if (currentComponent.intrinsicType === "FLIPFLOP") { + // Do not re-propagate flipflop output in the same frame + continue; + } for (const [outputPinId, outputValue] of result.outputValues) { outputNodePinValues.set(outputPinId, outputValue); - if (!visitedFlipFlops.has(currentNodeId)) { - const connections = - store.connections.getConnectionsByNodePinId(outputPinId); - if (connections.length !== 0) { - for (const connection of connections) { - const connectedNodePin = nullthrows( - store.nodePins.get(connection.to), - ); - nodePinInputValues.set(connectedNodePin.id, outputValue); - foundInputNumber.set( - connectedNodePin.nodeId, - nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, - ); - } - } else { - const parentComponentPin = nullthrows( - componentPins.find((componentPin) => { - return ( - componentPin.type === "output" && - componentPin.implementation === outputPinId - ); - }), + const connections = + store.connections.getConnectionsByNodePinId(outputPinId); + if (connections.length !== 0) { + for (const connection of connections) { + const connectedNodePin = nullthrows( + store.nodePins.get(connection.to), + ); + nodePinInputValues.set(connectedNodePin.id, outputValue); + foundInputNumber.set( + connectedNodePin.nodeId, + nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, ); - outputValues.set(parentComponentPin.id, outputValue); - } - if (currentComponentId === flipflop.outputPin.componentId) { - visitedFlipFlops.add(currentNodeId); } + } else { + const parentComponentPin = nullthrows( + componentPins.find((componentPin) => { + return ( + componentPin.type === "output" && + componentPin.implementation === outputPinId + ); + }), + ); + outputValues.set(parentComponentPin.id, outputValue); } } } else { diff --git a/src/store/connection.ts b/src/store/connection.ts index a3f09d4..b94145e 100644 --- a/src/store/connection.ts +++ b/src/store/connection.ts @@ -69,10 +69,7 @@ export class CCConnectionStore extends EventEmitter { const fromNodePinId = connection.from; const toNodePinId = connection.to; if (!this.#store.nodePins.isConnectable(fromNodePinId, toNodePinId)) { - window.alert( - `Cannot connect pins: ${fromNodePinId} and ${toNodePinId - }` - ); + window.alert(`Cannot connect pins: ${fromNodePinId} and ${toNodePinId}`); return; } this.#connections.set(connection.id, connection); @@ -110,18 +107,18 @@ export class CCConnectionStore extends EventEmitter { * @returns map of id and connection (read only) */ getConnectionIdsByParentComponentId( - parentComponentId: CCComponentId + parentComponentId: CCComponentId, ): CCConnectionId[] { return [...this.#connections.values()] .filter( - (connection) => connection.parentComponentId === parentComponentId + (connection) => connection.parentComponentId === parentComponentId, ) .map((connection) => connection.id); } getManyByParentComponentId(parentComponentId: CCComponentId): CCConnection[] { return [...this.#connections.values()].filter( - (connection) => connection.parentComponentId === parentComponentId + (connection) => connection.parentComponentId === parentComponentId, ); } @@ -134,7 +131,7 @@ export class CCConnectionStore extends EventEmitter { getConnectionsByNodePinId(nodePinId: CCNodePinId): CCConnection[] { return [...this.#connections.values()].filter( (connection) => - connection.from === nodePinId || connection.to === nodePinId + connection.from === nodePinId || connection.to === nodePinId, ); } diff --git a/src/store/fixtures.ts b/src/store/fixtures.ts new file mode 100644 index 0000000..c7fcd2b --- /dev/null +++ b/src/store/fixtures.ts @@ -0,0 +1,53 @@ +import nullthrows from "nullthrows"; +import CCStore from "."; +import { CCComponentStore } from "./component"; +import { CCConnectionStore } from "./connection"; +import * as intrinsics from "./intrinsics/definitions"; +import { CCNodeStore } from "./node"; + +export function createStoreFixture(): CCStore { + const store = new CCStore(); + + const rootComponent = CCComponentStore.create({ + name: "Root", + }); + store.components.register(rootComponent); + + const sampleNode1 = CCNodeStore.create({ + parentComponentId: rootComponent.id, + componentId: intrinsics.and.component.id, + position: { x: -100, y: 0 }, + }); + store.nodes.register(sampleNode1); + + const sampleNode2 = CCNodeStore.create({ + parentComponentId: rootComponent.id, + componentId: intrinsics.not.component.id, + position: { x: 100, y: 0 }, + }); + store.nodes.register(sampleNode2); + + const fromNodePin = nullthrows( + store.nodePins + .getManyByNodeId(sampleNode1.id) + .find( + (nodePin) => nodePin.componentPinId === intrinsics.and.outputPin.id, + ), + ); + const toNodePin = nullthrows( + store.nodePins + .getManyByNodeId(sampleNode2.id) + .find( + (nodePin) => nodePin.componentPinId === intrinsics.not.inputPin.A.id, + ), + ); + const sampleConnection = CCConnectionStore.create({ + parentComponentId: rootComponent.id, + from: fromNodePin.id, + to: toNodePin.id, + bentPortion: 0.5, + }); + store.connections.register(sampleConnection); + + return store; +} diff --git a/src/store/intrinsics/base.ts b/src/store/intrinsics/base.ts index 157a2ef..929b6a8 100644 --- a/src/store/intrinsics/base.ts +++ b/src/store/intrinsics/base.ts @@ -1,4 +1,4 @@ -import { mapValues } from "lodash-es"; +import { mapValues } from "es-toolkit"; import type { CCComponent, CCComponentId } from "../component"; import type { CCComponentPin, CCComponentPinId } from "../componentPin"; import type { CCIntrinsicComponentType } from "./types"; diff --git a/src/store/intrinsics/definitions.ts b/src/store/intrinsics/definitions.ts index 989a49a..9ade908 100644 --- a/src/store/intrinsics/definitions.ts +++ b/src/store/intrinsics/definitions.ts @@ -8,6 +8,23 @@ import { ccIntrinsicComponentTypes, } from "./types"; +// function createNullaryOperator( +// type: CCIntrinsicComponentType, +// name: string, +// evaluate: () => boolean +// ) { +// return new IntrinsicComponentDefinition({ +// type, +// name, +// in: {}, +// out: { name: "Out" }, +// evaluate: (_, output) => { +// invariant(output[0]); +// return [new Array(output[0].bitWidth).fill(evaluate())]; +// }, +// }); +// } + function createUnaryOperator( type: CCIntrinsicComponentType, name: string, @@ -104,7 +121,7 @@ export const decompose = new IntrinsicComponentDefinition({ evaluate: (input, outputShape) => { invariant(input.In[0] && !input.In[1]); const inputValue = input.In[0]; - const outputValue = new Array(); + const outputValue = []; let currentIndex = 0; for (const shape of outputShape) { outputValue.push([ diff --git a/src/store/node.test.ts b/src/store/node.test.ts new file mode 100644 index 0000000..922d578 --- /dev/null +++ b/src/store/node.test.ts @@ -0,0 +1,12 @@ +import nullthrows from "nullthrows"; +import { describe, it } from "vitest"; +import { createStoreFixture } from "./fixtures"; +import { expectConsistentStore } from "./testUtils"; + +describe("Node store", () => { + it("should delete nodes correctly", () => { + const store = createStoreFixture(); + store.nodes.unregister([nullthrows(store.nodes.getMany()[0]).id]); + expectConsistentStore(store); + }); +}); diff --git a/src/store/node.ts b/src/store/node.ts index 3d3b74d..98a4785 100644 --- a/src/store/node.ts +++ b/src/store/node.ts @@ -2,8 +2,8 @@ import EventEmitter from "eventemitter3"; import nullthrows from "nullthrows"; import invariant from "tiny-invariant"; import type { Opaque } from "type-fest"; -import type CCStore from "."; import type { Vector2 } from "../common/vector2"; +import type CCStore from "."; import type { CCComponentId } from "./component"; export type CCNodeId = Opaque; diff --git a/src/store/nodePin.ts b/src/store/nodePin.ts index e71e3d1..5fc8791 100644 --- a/src/store/nodePin.ts +++ b/src/store/nodePin.ts @@ -278,8 +278,10 @@ export class CCNodePinStore extends EventEmitter { if (seen.has(connectedNodePin.nodeId)) { continue; } - const connectedPinBitWidthStatus = - traverseNodePinBitWidthStatus(connectedNodePinId, seen); + const connectedPinBitWidthStatus = traverseNodePinBitWidthStatus( + connectedNodePinId, + seen, + ); if (connectedPinBitWidthStatus.isFixed) { return connectedPinBitWidthStatus; } @@ -298,9 +300,7 @@ export class CCNodePinStore extends EventEmitter { const aNodePin = this.get(a); const bNodePin = this.get(b); if (!aNodePin || !bNodePin) { - throw new Error( - `Node pin ${a} or ${b} does not exist in the store`, - ); + throw new Error(`Node pin ${a} or ${b} does not exist in the store`); } const aNode = this.#store.nodes.get(aNodePin.nodeId); const bNode = this.#store.nodes.get(bNodePin.nodeId); @@ -313,7 +313,7 @@ export class CCNodePinStore extends EventEmitter { console.warn( `Cannot connect pins of the same node: ${aNodePin.id} and ${bNodePin.id}`, ); - return false; + return false; } if (aNode.parentComponentId !== bNode.parentComponentId) { console.warn( diff --git a/src/store/react/error.tsx b/src/store/react/error.tsx index bd9c794..555a62a 100644 --- a/src/store/react/error.tsx +++ b/src/store/react/error.tsx @@ -1,6 +1,6 @@ import { type ComponentType, createElement } from "react"; -import { useStore } from "."; import type CCStore from ".."; +import { useStore } from "."; export default function ensureStoreItem

>( check: (props: P, store: CCStore) => unknown, diff --git a/src/store/react/selectors.ts b/src/store/react/selectors.ts index c67b167..b2fb429 100644 --- a/src/store/react/selectors.ts +++ b/src/store/react/selectors.ts @@ -1,11 +1,11 @@ import memoizeOne from "memoize-one"; import nullthrows from "nullthrows"; import { useCallback, useMemo, useSyncExternalStore } from "react"; -import { useStore } from "."; import type { CCComponentId } from "../component"; import type { CCComponentPin } from "../componentPin"; import type { CCNode, CCNodeId } from "../node"; import type { CCNodePin } from "../nodePin"; +import { useStore } from "."; export function useComponents() { const { store } = useStore(); diff --git a/src/store/testUtils.ts b/src/store/testUtils.ts new file mode 100644 index 0000000..cd4903f --- /dev/null +++ b/src/store/testUtils.ts @@ -0,0 +1,31 @@ +import { uniqBy } from "es-toolkit"; +import { expect } from "vitest"; +import type CCStore from "."; + +export function expectConsistentStore(store: CCStore) { + const nodes = store.nodes.getMany(); + for (const node of nodes) { + expect(store.components.get(node.componentId)).toBeDefined(); + expect(store.components.get(node.parentComponentId)).toBeDefined(); + } + + const componentPins = store.componentPins.getMany(); + for (const componentPin of componentPins) { + expect(store.componentPins.get(componentPin.id)).toBeDefined(); + expect(store.components.get(componentPin.componentId)).toBeDefined(); + } + + const nodePins = store.nodePins.getMany(); + for (const nodePin of nodePins) { + expect(store.nodes.get(nodePin.nodeId)).toBeDefined(); + expect(store.componentPins.get(nodePin.componentPinId)).toBeDefined(); + } + + const connections = store.connections.getMany(); + for (const connection of store.connections.getMany()) { + expect(store.nodePins.get(connection.from)).toBeDefined(); + expect(store.nodePins.get(connection.to)).toBeDefined(); + } + // Ensure an input pin is only connected to one output pin + expect(connections).toEqual(uniqBy(connections, (c) => c.to)); +} From 7a44072f264da5db744f4563443e19759550e165 Mon Sep 17 00:00:00 2001 From: chelproc Date: Fri, 12 Sep 2025 23:03:22 +0900 Subject: [PATCH 3/4] update --- package-lock.json | 13 + package.json | 1 + src/pages/edit/Editor/renderer/Connection.tsx | 7 +- src/pages/edit/Editor/renderer/Node.tsx | 2 +- src/pages/home/index.tsx | 5 +- src/store/autoSaver.ts | 53 ++++ src/store/component.ts | 2 + src/store/componentEvaluator.ts | 295 +++++++++--------- src/store/componentPin.ts | 2 + src/store/connection.ts | 2 + src/store/index.ts | 53 ++-- src/store/node.ts | 5 + src/store/nodePin.ts | 16 + src/store/react/index.tsx | 83 ++--- 14 files changed, 318 insertions(+), 221 deletions(-) create mode 100644 src/store/autoSaver.ts diff --git a/package-lock.json b/package-lock.json index 9b6547f..33e7f91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@emotion/styled": "^11.14.1", "@mui/icons-material": "^7.3.1", "@mui/material": "^7.3.1", + "debounce": "^2.2.0", "es-toolkit": "^1.39.10", "eventemitter3": "^5.0.1", "memoize-one": "^6.0.0", @@ -2033,6 +2034,18 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/debounce": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", + "integrity": "sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", diff --git a/package.json b/package.json index 6360953..9339844 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@emotion/styled": "^11.14.1", "@mui/icons-material": "^7.3.1", "@mui/material": "^7.3.1", + "debounce": "^2.2.0", "es-toolkit": "^1.39.10", "eventemitter3": "^5.0.1", "memoize-one": "^6.0.0", diff --git a/src/pages/edit/Editor/renderer/Connection.tsx b/src/pages/edit/Editor/renderer/Connection.tsx index ebaa715..a2452f9 100644 --- a/src/pages/edit/Editor/renderer/Connection.tsx +++ b/src/pages/edit/Editor/renderer/Connection.tsx @@ -6,6 +6,7 @@ import { useStore } from "../../../../store/react"; import ensureStoreItem from "../../../../store/react/error"; import { useNode } from "../../../../store/react/selectors"; import { useComponentEditorStore } from "../store"; +import { stringifySimulationValue } from "../store/slices/core/index"; import getCCComponentEditorRendererNodeGeometry from "./Node.geometry"; export type CCComponentEditorRendererConnectionCoreProps = { @@ -106,8 +107,6 @@ const CCComponentEditorRendererConnection = ensureStoreItem( const [isHovered, setIsHovered] = useState(false); - // const fromNodePinValue = componentEditorState.getNodePinValue(fromNodePin.id); - return ( <> - hoge + {stringifySimulationValue( + nullthrows(componentEditorState.getNodePinValue(toNodePin.id)), + )} )} diff --git a/src/pages/edit/Editor/renderer/Node.tsx b/src/pages/edit/Editor/renderer/Node.tsx index ddf682d..52bcb20 100644 --- a/src/pages/edit/Editor/renderer/Node.tsx +++ b/src/pages/edit/Editor/renderer/Node.tsx @@ -79,7 +79,7 @@ const CCComponentEditorRendererNode = ensureStoreItem( fill={theme.palette.textPrimary} x={geometry.x} y={geometry.y - 5} - textAnchor="end" + textAnchor="start" fontSize={12} > {component.name} diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 69c674b..4251fe6 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -17,7 +17,6 @@ import { } from "@mui/material"; import { useRef, useState } from "react"; import { ComponentPropertyDialog } from "../../components/ComponentPropertyDialog"; -import type { CCStorePropsFromJson } from "../../store"; import { type CCComponentId, CCComponentStore } from "../../store/component"; import { useStore } from "../../store/react"; import { useComponents } from "../../store/react/selectors"; @@ -49,9 +48,7 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { if (!file) return; const reader = new FileReader(); reader.onload = () => { - const storeJSON = reader.result as string; - const storeData = JSON.parse(storeJSON); - resetStore(storeData as CCStorePropsFromJson); + resetStore(reader.result as string); }; reader.readAsText(file); }; diff --git a/src/store/autoSaver.ts b/src/store/autoSaver.ts new file mode 100644 index 0000000..27a8924 --- /dev/null +++ b/src/store/autoSaver.ts @@ -0,0 +1,53 @@ +import debounce from "debounce"; +import type CCStore from "."; +import { ccComponentStoreChangeEventTypes } from "./component"; +import { ccComponentPinStoreChangeEventTypes } from "./componentPin"; +import { ccConnectionStoreChangeEventTypes } from "./connection"; +import { ccNodeStoreChangeEventTypes } from "./node"; +import { ccNodePinStoreChangeEventTypes } from "./nodePin"; + +export class CCStoreAutoSaver { + #store: CCStore; + + static localStorageKey = "ccStoreAutoSaverState"; + + constructor(store: CCStore) { + this.#store = store; + } + + watch() { + const save = debounce(this.save.bind(this), 1000); + for (const eventType of ccComponentStoreChangeEventTypes) { + this.#store.components.addListener(eventType, save); + } + for (const eventType of ccComponentPinStoreChangeEventTypes) { + this.#store.componentPins.addListener(eventType, save); + } + for (const eventType of ccNodeStoreChangeEventTypes) { + this.#store.nodes.addListener(eventType, save); + } + for (const eventType of ccNodePinStoreChangeEventTypes) { + this.#store.nodePins.addListener(eventType, save); + } + for (const eventType of ccConnectionStoreChangeEventTypes) { + this.#store.connections.addListener(eventType, save); + } + } + + save(): void { + window.localStorage.setItem( + CCStoreAutoSaver.localStorageKey, + this.#store.toJSON(), + ); + } + + tryRestore(): boolean { + const json = window.localStorage.getItem(CCStoreAutoSaver.localStorageKey); + if (json) { + this.#store.importJson(json); + return true; + } else { + return false; + } + } +} diff --git a/src/store/component.ts b/src/store/component.ts index feb6fd9..b2835e5 100644 --- a/src/store/component.ts +++ b/src/store/component.ts @@ -20,6 +20,8 @@ export type CCComponentStoreEvents = { didUnregister(component: CCComponent): void; didUpdate(component: CCComponent): void; }; +export const ccComponentStoreChangeEventTypes: (keyof CCComponentStoreEvents)[] = + ["didRegister", "didUnregister", "didUpdate"]; /** * Store of components diff --git a/src/store/componentEvaluator.ts b/src/store/componentEvaluator.ts index d4283a8..4f4a614 100644 --- a/src/store/componentEvaluator.ts +++ b/src/store/componentEvaluator.ts @@ -158,14 +158,14 @@ function simulateNode( >(); const nodePins = store.nodePins.getManyByNodeId(nodeId); const children = store.nodes.getManyByParentComponentId(component.id); - const foundInputNumber = new Map(); const nodePinInputNumber = new Map(); const nodePinInputValues = new Map< CCNodeId, Map >(); + + // Initialize maps for (const child of children) { - foundInputNumber.set(child.id, 0); const innerPins = store.nodePins.getManyByNodeId(child.id); let inputPinNumber = 0; for (const innerPin of innerPins) { @@ -179,6 +179,8 @@ function simulateNode( nodePinInputNumber.set(child.id, inputPinNumber); nodePinInputValues.set(child.id, new Map()); } + + // Set input values for component from parent for (const nodePin of nodePins) { const componentPin = nullthrows( store.componentPins.get(nodePin.componentPinId), @@ -187,19 +189,45 @@ function simulateNode( const connectedNodePin = nullthrows( store.nodePins.get(componentPin.implementation), ); - nodePinInputValues.set( - connectedNodePin.nodeId, - nullthrows(nodePinInputValues.get(connectedNodePin.nodeId)).set( - connectedNodePin.id, - nullthrows(inputValues.get(nodePin.id)), - ), + updateNodePinInputValues( + store, + nodePinInputValues, + connectedNodePin.id, + nullthrows(inputValues.get(nodePin.id)), ); - foundInputNumber.set( - connectedNodePin.nodeId, - nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, + } + } + + // Evaluate flipflops first + for (const child of children) { + if (child.componentId === flipflop.id) { + const dummyInputValues = new Map(); + const flipFlopNodePins = store.nodePins.getManyByNodeId(child.id); + for (const nodePin of flipFlopNodePins) { + dummyInputValues.set(nodePin.id, [false]); + } + const flipFlopOutputValues = simulateIntrinsic( + store, + child.id, + dummyInputValues, + previousFrame, ); + for (const [outputPinId, outputValue] of flipFlopOutputValues) { + const connections = nullthrows( + store.connections.getConnectionsByNodePinId(outputPinId), + ); + for (const connection of connections) { + updateNodePinInputValues( + store, + nodePinInputValues, + connection.to, + outputValue, + ); + } + } } } + const unevaluatedNodes = new Set(); for (const child of children) { unevaluatedNodes.add(child.id); @@ -213,10 +241,13 @@ function simulateNode( unevaluatedNodes.delete(currentNodeId); const currentNode = nullthrows(store.nodes.get(currentNodeId)); const currentComponentId = currentNode.componentId; + const currentComponent = nullthrows( + store.components.get(currentComponentId), + ); if ( nullthrows(nodePinInputNumber.get(currentNodeId)) === - nullthrows(foundInputNumber.get(currentNodeId)) + nullthrows(nodePinInputValues.get(currentNodeId))?.size ) { const frame = previousFrame ? nullthrows(nullthrows(previousFrame).nodes.get(currentNodeId)).child @@ -231,64 +262,11 @@ function simulateNode( return null; } childMap.set(currentNodeId, result); - for (const [outputPinId, outputValue] of result.outputValues) { - if (!visitedFlipFlops.has(currentNodeId)) { - const connections = nullthrows( - store.connections.getConnectionsByNodePinId(outputPinId), - ); - if (connections.length !== 0) { - for (const connection of connections) { - const connectedNodePin = nullthrows( - store.nodePins.get(connection.to), - ); - nodePinInputValues.set( - connectedNodePin.nodeId, - nullthrows(nodePinInputValues.get(connectedNodePin.nodeId)).set( - connectedNodePin.id, - outputValue, - ), - ); - foundInputNumber.set( - connectedNodePin.nodeId, - nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, - ); - } - } else { - const parentNodePin = nullthrows( - nodePins.find((nodePin) => { - const componentPin = nullthrows( - store.componentPins.get(nodePin.componentPinId), - ); - return ( - componentPin.type === "output" && - componentPin.implementation === outputPinId - ); - }), - ); - outputValues.set(parentNodePin.id, outputValue); - } - if (currentComponentId === flipflop.id) { - visitedFlipFlops.add(currentNodeId); - } - } - } - } else if ( - currentComponentId === flipflop.id && - !visitedFlipFlops.has(currentNodeId) - ) { - const frame = previousFrame - ? nullthrows(previousFrame?.nodes.get(currentNodeId)).child - : null; - const result = simulateNode( - store, - currentNodeId, - nodePinInputValues.get(currentNodeId) || new Map(), - frame, - ); - if (!result) { - return null; + // Do not re-propagate flipflop output in the same frame + if (currentComponent.intrinsicType === "FLIPFLOP") { + continue; } - childMap.set(currentNodeId, result); + for (const [outputPinId, outputValue] of result.outputValues) { if (!visitedFlipFlops.has(currentNodeId)) { const connections = nullthrows( @@ -296,19 +274,11 @@ function simulateNode( ); if (connections.length !== 0) { for (const connection of connections) { - const connectedNodePin = nullthrows( - store.nodePins.get(connection.to), - ); - nodePinInputValues.set( - connectedNodePin.nodeId, - nullthrows(nodePinInputValues.get(connectedNodePin.nodeId)).set( - connectedNodePin.id, - outputValue, - ), - ); - foundInputNumber.set( - connectedNodePin.nodeId, - nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, + updateNodePinInputValues( + store, + nodePinInputValues, + connection.to, + outputValue, ); } } else { @@ -325,16 +295,66 @@ function simulateNode( ); outputValues.set(parentNodePin.id, outputValue); } - if (currentComponentId === flipflop.outputPin.componentId) { + if (currentComponentId === flipflop.id) { visitedFlipFlops.add(currentNodeId); } } } - visitedFlipFlops.add(currentNodeId); - unevaluatedNodes.add(currentNodeId); } else { unevaluatedNodes.add(currentNodeId); } + // else if ( + // currentComponentId === flipflop.id && + // !visitedFlipFlops.has(currentNodeId) + // ) { + // const frame = previousFrame + // ? nullthrows(previousFrame?.nodes.get(currentNodeId)).child + // : null; + // const result = simulateNode( + // store, + // currentNodeId, + // nodePinInputValues.get(currentNodeId) || new Map(), + // frame, + // ); + // if (!result) { + // return null; + // } + // childMap.set(currentNodeId, result); + // for (const [outputPinId, outputValue] of result.outputValues) { + // if (!visitedFlipFlops.has(currentNodeId)) { + // const connections = nullthrows( + // store.connections.getConnectionsByNodePinId(outputPinId), + // ); + // if (connections.length !== 0) { + // for (const connection of connections) { + // updateNodePinInputValues( + // store, + // nodePinInputValues, + // connection.to, + // outputValue, + // ); + // } + // } else { + // const parentNodePin = nullthrows( + // nodePins.find((nodePin) => { + // const componentPin = nullthrows( + // store.componentPins.get(nodePin.componentPinId), + // ); + // return ( + // componentPin.type === "output" && + // componentPin.implementation === outputPinId + // ); + // }), + // ); + // outputValues.set(parentNodePin.id, outputValue); + // } + // if (currentComponentId === flipflop.outputPin.componentId) { + // visitedFlipFlops.add(currentNodeId); + // } + // } + // } + // visitedFlipFlops.add(currentNodeId); + // } } const pins = new Map(); @@ -348,6 +368,34 @@ function simulateNode( return { outputValues, pins, child }; } +function updateNodePinInputValues( + store: CCStore, + nodePinInputValues: Map>, + nodePinId: CCNodePinId, + value: SimulationValue, +) { + const nodePin = nullthrows(store.nodePins.get(nodePinId)); + const map = nullthrows(nodePinInputValues.get(nodePin.nodeId)); + map.set(nodePinId, value); +} + +function reflectOutputValue( + store: CCStore, + outputPinId: CCNodePinId, + outputValue: SimulationValue, + nodePinInputValues: Map>, +) { + const connections = store.connections.getConnectionsByNodePinId(outputPinId); + for (const connection of connections) { + updateNodePinInputValues( + store, + nodePinInputValues, + connection.to, + outputValue, + ); + } +} + export default function simulateComponent( store: CCStore, componentId: CCComponentId, @@ -366,13 +414,14 @@ export default function simulateComponent( >(); const componentPins = store.componentPins.getManyByComponentId(componentId); const children = store.nodes.getManyByParentComponentId(component.id); - const foundInputNumber = new Map(); const nodePinInputNumber = new Map(); - const nodePinInputValues = new Map(); + const nodePinInputValues = new Map< + CCNodeId, + Map + >(); // Initialize maps for (const child of children) { - foundInputNumber.set(child.id, 0); const innerPins = store.nodePins.getManyByNodeId(child.id); let inputPinNumber = 0; for (const innerPin of innerPins) { @@ -384,6 +433,7 @@ export default function simulateComponent( } } nodePinInputNumber.set(child.id, inputPinNumber); + nodePinInputValues.set(child.id, new Map()); } // Set input values for component from parent @@ -392,14 +442,12 @@ export default function simulateComponent( const connectedNodePin = nullthrows( store.nodePins.get(componentPin.implementation), ); - nodePinInputValues.set( + updateNodePinInputValues( + store, + nodePinInputValues, connectedNodePin.id, nullthrows(inputValues.get(componentPin.id)), ); - foundInputNumber.set( - connectedNodePin.nodeId, - nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, - ); } } @@ -408,9 +456,6 @@ export default function simulateComponent( unevaluatedNodes.add(child.id); } - const outputValues = new Map(); - const outputNodePinValues = new Map(); - // Evaluate flipflops first for (const child of children) { if (child.componentId === flipflop.id) { @@ -426,31 +471,7 @@ export default function simulateComponent( previousFrame, ); for (const [outputPinId, outputValue] of flipFlopOutputValues) { - outputNodePinValues.set(outputPinId, outputValue); - const connections = - store.connections.getConnectionsByNodePinId(outputPinId); - if (connections.length !== 0) { - for (const connection of connections) { - const connectedNodePin = nullthrows( - store.nodePins.get(connection.to), - ); - nodePinInputValues.set(connectedNodePin.id, outputValue); - foundInputNumber.set( - connectedNodePin.nodeId, - nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, - ); - } - } else { - const parentComponentPin = nullthrows( - componentPins.find((componentPin) => { - return ( - componentPin.type === "output" && - componentPin.implementation === outputPinId - ); - }), - ); - outputValues.set(parentComponentPin.id, outputValue); - } + reflectOutputValue(store, outputPinId, outputValue, nodePinInputValues); } } } @@ -466,7 +487,7 @@ export default function simulateComponent( if ( nodePinInputNumber.get(currentNodeId) === - foundInputNumber.get(currentNodeId) + nodePinInputValues.get(currentNodeId)?.size ) { const frame = (() => { if (!previousFrame) return null; @@ -477,7 +498,9 @@ export default function simulateComponent( })(); const currentNodePinInputValues = new Map(); for (const nodePin of store.nodePins.getManyByNodeId(currentNodeId)) { - const inputValue = nodePinInputValues.get(nodePin.id); + const inputValue = nodePinInputValues + .get(currentNodeId) + ?.get(nodePin.id); if (inputValue) currentNodePinInputValues.set(nodePin.id, inputValue); } const result = simulateNode( @@ -495,31 +518,7 @@ export default function simulateComponent( continue; } for (const [outputPinId, outputValue] of result.outputValues) { - outputNodePinValues.set(outputPinId, outputValue); - const connections = - store.connections.getConnectionsByNodePinId(outputPinId); - if (connections.length !== 0) { - for (const connection of connections) { - const connectedNodePin = nullthrows( - store.nodePins.get(connection.to), - ); - nodePinInputValues.set(connectedNodePin.id, outputValue); - foundInputNumber.set( - connectedNodePin.nodeId, - nullthrows(foundInputNumber.get(connectedNodePin.nodeId)) + 1, - ); - } - } else { - const parentComponentPin = nullthrows( - componentPins.find((componentPin) => { - return ( - componentPin.type === "output" && - componentPin.implementation === outputPinId - ); - }), - ); - outputValues.set(parentComponentPin.id, outputValue); - } + reflectOutputValue(store, outputPinId, outputValue, nodePinInputValues); } } else { unevaluatedNodes.add(currentNodeId); diff --git a/src/store/componentPin.ts b/src/store/componentPin.ts index 5908203..65f006c 100644 --- a/src/store/componentPin.ts +++ b/src/store/componentPin.ts @@ -49,6 +49,8 @@ export type CCComponentPinStoreEvents = { didUnregister(pin: CCComponentPin): void; didUpdate(pin: CCComponentPin): void; }; +export const ccComponentPinStoreChangeEventTypes: (keyof CCComponentPinStoreEvents)[] = + ["didRegister", "didUnregister", "didUpdate"]; /** * Store of pins diff --git a/src/store/connection.ts b/src/store/connection.ts index b94145e..232d3ed 100644 --- a/src/store/connection.ts +++ b/src/store/connection.ts @@ -27,6 +27,8 @@ export type CCConnectionStoreEvents = { willUnregister(Connection: CCConnection): void; didUnregister(Connection: CCConnection): void; }; +export const ccConnectionStoreChangeEventTypes: (keyof CCConnectionStoreEvents)[] = + ["didRegister", "didUnregister"]; /** * Store of connections diff --git a/src/store/index.ts b/src/store/index.ts index c8c3265..150b03a 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,22 +1,12 @@ -import { type CCComponent, CCComponentStore } from "./component"; -import { type CCComponentPin, CCComponentPinStore } from "./componentPin"; -import { type CCConnection, CCConnectionStore } from "./connection"; +import { CCStoreAutoSaver } from "./autoSaver"; +import { CCComponentStore } from "./component"; +import { CCComponentPinStore } from "./componentPin"; +import { CCConnectionStore } from "./connection"; import * as intrinsics from "./intrinsics/definitions"; -import { type CCNode, CCNodeStore } from "./node"; -import { type CCNodePin, CCNodePinStore } from "./nodePin"; +import { CCNodeStore } from "./node"; +import { CCNodePinStore } from "./nodePin"; import TransactionManager from "./transaction"; -/** - * Properties of CCStore from JSON used when restoring store from JSON - */ -export type CCStorePropsFromJson = { - components: CCComponent[]; - nodes: CCNode[]; - componentPins: CCComponentPin[]; - nodePins: CCNodePin[]; - connections: CCConnection[]; -}; - /** * Store of components, nodes, pins, and connections */ @@ -31,27 +21,23 @@ export default class CCStore { connections: CCConnectionStore; - transactionManager = new TransactionManager(); + transactionManager: TransactionManager; + + autoSaver: CCStoreAutoSaver; /** * Constructor of CCStore * @param rootComponent root component * @param props properties of store from JSON used when restoring store from JSON */ - constructor(props?: CCStorePropsFromJson) { + constructor() { this.components = new CCComponentStore(this); this.nodes = new CCNodeStore(this); this.componentPins = new CCComponentPinStore(this); this.nodePins = new CCNodePinStore(this); this.connections = new CCConnectionStore(this); - if (props) { - const { components, nodes, componentPins, nodePins, connections } = props; - this.components.import(components); - this.nodes.import(nodes); - this.componentPins.import(componentPins); - this.nodePins.import(nodePins); - this.connections.import(connections); - } + this.transactionManager = new TransactionManager(); + this.autoSaver = new CCStoreAutoSaver(this); for (const definition of Object.values(intrinsics.definitions)) { this.components.register(definition.component); @@ -59,7 +45,9 @@ export default class CCStore { this.componentPins.register(pin); } } + } + mount() { this.components.mount(); this.nodes.mount(); this.componentPins.mount(); @@ -73,11 +61,22 @@ export default class CCStore { */ toJSON() { return JSON.stringify({ - components: this.components.getMany(), + // Only export non-intrinsic components + components: this.components.getMany().filter((c) => !c.intrinsicType), nodes: this.nodes.getMany(), componentPins: this.componentPins.getMany(), nodePins: this.nodePins.getMany(), connections: this.connections.getMany(), }); } + + importJson(json: string) { + const { components, nodes, componentPins, nodePins, connections } = + JSON.parse(json); + this.components.import(components); + this.nodes.import(nodes); + this.componentPins.import(componentPins); + this.nodePins.import(nodePins); + this.connections.import(connections); + } } diff --git a/src/store/node.ts b/src/store/node.ts index 98a4785..763f96e 100644 --- a/src/store/node.ts +++ b/src/store/node.ts @@ -21,6 +21,11 @@ export type CCNodeStoreEvents = { didUnregister(node: CCNode): void; didUpdate(node: CCNode): void; }; +export const ccNodeStoreChangeEventTypes: (keyof CCNodeStoreEvents)[] = [ + "didRegister", + "didUnregister", + "didUpdate", +]; /** * Store of nodes diff --git a/src/store/nodePin.ts b/src/store/nodePin.ts index 5fc8791..06c6a85 100644 --- a/src/store/nodePin.ts +++ b/src/store/nodePin.ts @@ -24,6 +24,11 @@ export type CCNodePinStoreEvents = { didUnregister(pin: CCNodePin): void; didUpdate(pin: CCNodePin): void; }; +export const ccNodePinStoreChangeEventTypes: (keyof CCNodePinStoreEvents)[] = [ + "didRegister", + "didUnregister", + "didUpdate", +]; export class CCNodePinStore extends EventEmitter { #store: CCStore; @@ -302,6 +307,17 @@ export class CCNodePinStore extends EventEmitter { if (!aNodePin || !bNodePin) { throw new Error(`Node pin ${a} or ${b} does not exist in the store`); } + const aComponentPin = this.#store.componentPins.get( + aNodePin?.componentPinId ?? null, + ); + const bComponentPin = this.#store.componentPins.get( + bNodePin?.componentPinId ?? null, + ); + if (!aComponentPin || !bComponentPin) { + throw new Error( + `Component pin ${aNodePin?.componentPinId} or ${bNodePin?.componentPinId} does not exist in the store`, + ); + } const aNode = this.#store.nodes.get(aNodePin.nodeId); const bNode = this.#store.nodes.get(bNodePin.nodeId); if (!aNode || !bNode) { diff --git a/src/store/react/index.tsx b/src/store/react/index.tsx index 0142e82..fd9b989 100644 --- a/src/store/react/index.tsx +++ b/src/store/react/index.tsx @@ -8,7 +8,7 @@ import { useState, } from "react"; import invariant from "tiny-invariant"; -import CCStore, { type CCStorePropsFromJson } from ".."; +import CCStore from ".."; import { CCComponentStore } from "../component"; import { CCConnectionStore } from "../connection"; import { and, not } from "../intrinsics/definitions"; @@ -17,44 +17,47 @@ import { CCNodeStore } from "../node"; function useContextValue() { const [store, setStore] = useState(() => { const tempStore = new CCStore(); + const isRestored = tempStore.autoSaver.tryRestore(); + tempStore.mount(); + if (!isRestored) { + const rootComponent = CCComponentStore.create({ + name: "Root", + }); + tempStore.components.register(rootComponent); - const rootComponent = CCComponentStore.create({ - name: "Root", - }); - tempStore.components.register(rootComponent); - - const sampleNode1 = CCNodeStore.create({ - parentComponentId: rootComponent.id, - componentId: and.component.id, - position: { x: -100, y: 0 }, - }); - tempStore.nodes.register(sampleNode1); + const sampleNode1 = CCNodeStore.create({ + parentComponentId: rootComponent.id, + componentId: and.component.id, + position: { x: -100, y: 0 }, + }); + tempStore.nodes.register(sampleNode1); - const sampleNode2 = CCNodeStore.create({ - parentComponentId: rootComponent.id, - componentId: not.component.id, - position: { x: 100, y: 0 }, - }); - tempStore.nodes.register(sampleNode2); - - const fromNodePin = nullthrows( - tempStore.nodePins - .getManyByNodeId(sampleNode1.id) - .find((nodePin) => nodePin.componentPinId === and.outputPin.id), - ); - const toNodePin = nullthrows( - tempStore.nodePins - .getManyByNodeId(sampleNode2.id) - .find((nodePin) => nodePin.componentPinId === not.inputPin.A.id), - ); - const sampleConnection = CCConnectionStore.create({ - parentComponentId: rootComponent.id, - from: fromNodePin.id, - to: toNodePin.id, - bentPortion: 0.5, - }); - tempStore.connections.register(sampleConnection); + const sampleNode2 = CCNodeStore.create({ + parentComponentId: rootComponent.id, + componentId: not.component.id, + position: { x: 100, y: 0 }, + }); + tempStore.nodes.register(sampleNode2); + const fromNodePin = nullthrows( + tempStore.nodePins + .getManyByNodeId(sampleNode1.id) + .find((nodePin) => nodePin.componentPinId === and.outputPin.id), + ); + const toNodePin = nullthrows( + tempStore.nodePins + .getManyByNodeId(sampleNode2.id) + .find((nodePin) => nodePin.componentPinId === not.inputPin.A.id), + ); + const sampleConnection = CCConnectionStore.create({ + parentComponentId: rootComponent.id, + from: fromNodePin.id, + to: toNodePin.id, + bentPortion: 0.5, + }); + tempStore.connections.register(sampleConnection); + } + tempStore.autoSaver.watch(); return tempStore; }); @@ -66,8 +69,12 @@ function useContextValue() { }); }, [store]); - const resetStore = useCallback((props: CCStorePropsFromJson) => { - setStore(new CCStore(props)); + const resetStore = useCallback((json: string) => { + const store = new CCStore(); + store.importJson(json); + store.mount(); + store.autoSaver.watch(); + setStore(store); }, []); return useMemo(() => ({ store, resetStore }), [store, resetStore]); } From 9c25a9d0dc5ac43f8be9aa81a578dfb2d3b15c61 Mon Sep 17 00:00:00 2001 From: chelproc Date: Sun, 19 Oct 2025 00:22:02 +0900 Subject: [PATCH 4/4] update 20251019 Co-authored-by: taka231 Co-authored-by: Rn86222 --- .github/workflows/lint.yml | 1 + package-lock.json | 10 ++ package.json | 1 + src/components/ComponentPropertyDialog.tsx | 133 +++++++++++++++--- src/pages/edit/Editor/index.tsx | 4 +- src/pages/edit/Editor/renderer/InputValue.tsx | 46 ++++-- src/pages/edit/Editor/renderer/NodePin.tsx | 5 +- src/pages/home/index.tsx | 47 +++---- src/store/componentEvaluator.ts | 81 ++++------- src/store/componentPin.ts | 30 ++-- src/store/connection.ts | 1 - src/store/fixtures.ts | 1 + src/store/intrinsics/definitions.ts | 6 + src/store/intrinsics/types.ts | 1 + src/store/nodePin.ts | 39 ++++- 15 files changed, 265 insertions(+), 141 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index badba29..508f26d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,3 +7,4 @@ jobs: - run: npm ci - run: npm run type-check - run: npm run check + - run: npm test diff --git a/package-lock.json b/package-lock.json index 33e7f91..466eb88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "react-use": "^17.6.0", "tiny-invariant": "^1.3.3", "transformation-matrix": "^3.1.0", + "zod": "^4.1.12", "zustand": "^5.0.8" }, "devDependencies": { @@ -3317,6 +3318,15 @@ "node": ">= 14" } }, + "node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zustand": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", diff --git a/package.json b/package.json index 9339844..0b19296 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "react-use": "^17.6.0", "tiny-invariant": "^1.3.3", "transformation-matrix": "^3.1.0", + "zod": "^4.1.12", "zustand": "^5.0.8" }, "devDependencies": { diff --git a/src/components/ComponentPropertyDialog.tsx b/src/components/ComponentPropertyDialog.tsx index e8cf37d..5a26ed5 100644 --- a/src/components/ComponentPropertyDialog.tsx +++ b/src/components/ComponentPropertyDialog.tsx @@ -1,56 +1,157 @@ import { + Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, + FormLabel, + Stack, TextField, } from "@mui/material"; +import nullthrows from "nullthrows"; import { useState } from "react"; +import { z } from "zod"; +import type CCStore from "../store"; +import type { CCComponentId } from "../store/component"; +import type { CCComponentPinId } from "../store/componentPin"; +import { useStore } from "../store/react"; export type ComponentPropertyDialogProps = { + componentId: CCComponentId; defaultName: string; - onAccept(newName: string): void; + onClose(): void; onCancel(): void; }; +const stateSchema = z.object({ + name: z.string().nonempty(), + pinNameById: z.map(z.custom(), z.string().nonempty()), +}); +function extractStateFromStore( + store: CCStore, + componentId: CCComponentId, +): z.input { + const component = nullthrows(store.components.get(componentId)); + return { + name: component.name, + pinNameById: new Map( + store.componentPins + .getManyByComponentId(componentId) + .map((pin) => [pin.id, pin.name]), + ), + }; +} +function applyStateToStore( + store: CCStore, + componentId: CCComponentId, + state: z.output, +) { + store.components.update(componentId, { name: state.name }); + for (const [pinId, pinName] of state.pinNameById) { + store.componentPins.update(pinId, { name: pinName }); + } +} + export function ComponentPropertyDialog({ - defaultName, - onAccept, - onCancel, + componentId, + onClose, }: ComponentPropertyDialogProps) { - const [newName, setNewName] = useState(defaultName); + const { store } = useStore(); + const component = nullthrows(store.components.get(componentId)); + const [state, setState] = useState(() => + extractStateFromStore(store, componentId), + ); + const result = stateSchema.safeParse(state); return ( -

+
{ e.preventDefault(); - if (!newName) return; - onAccept(newName); + if (!result.success) return; + applyStateToStore(store, componentId, result.data); + onClose(); }} > - Component property + {component.name} Properties + Name setNewName(e.target.value)} + onChange={(e) => setState({ ...state, name: e.target.value })} placeholder="Name" + sx={{ mt: 0.5 }} /> + + + Input Pins + {store.componentPins + .getManyByComponentId(componentId) + .filter((pin) => pin.type === "input") + .map((pin) => ( + + setState({ + ...state, + pinNameById: new Map(state.pinNameById).set( + pin.id, + e.target.value, + ), + }) + } + /> + ))} + + + Output Pins + {store.componentPins + .getManyByComponentId(componentId) + .filter((pin) => pin.type === "output") + .map((pin) => ( + + setState({ + ...state, + pinNameById: new Map(state.pinNameById).set( + pin.id, + e.target.value, + ), + }) + } + /> + ))} + + -
diff --git a/src/pages/edit/Editor/index.tsx b/src/pages/edit/Editor/index.tsx index 394d4a6..7d550ab 100644 --- a/src/pages/edit/Editor/index.tsx +++ b/src/pages/edit/Editor/index.tsx @@ -52,9 +52,9 @@ function CCComponentEditorContent({ {isComponentPropertyDialogOpen && ( { - store.components.update(componentId, { name: newName }); + onClose={() => { setIsComponentPropertyDialogOpen(false); }} onCancel={() => { diff --git a/src/pages/edit/Editor/renderer/InputValue.tsx b/src/pages/edit/Editor/renderer/InputValue.tsx index 7116330..05b0e7f 100644 --- a/src/pages/edit/Editor/renderer/InputValue.tsx +++ b/src/pages/edit/Editor/renderer/InputValue.tsx @@ -11,6 +11,7 @@ import getCCComponentEditorRendererNodeGeometry from "./Node.geometry"; export type CCComponentEditorRendererInputValueProps = { nodePinId: CCNodePinId; }; +// TODO: Change name to CCComponentEditorRendererComponentPin export default function CCComponentEditorRendererInputValue({ nodePinId, }: CCComponentEditorRendererInputValueProps) { @@ -22,16 +23,35 @@ export default function CCComponentEditorRendererInputValue({ ); const type = interfaceComponentPin.type; - const nodePinValue = - type === "input" - ? nullthrows(componentEditorState.getInputValue(interfaceComponentPin.id)) - : nullthrows(componentEditorState.getNodePinValue(nodePinId)); - const updateInputValue = () => { - componentEditorState.setInputValue( - interfaceComponentPin.id, - wrappingIncrementSimulationValue(nodePinValue), - ); - }; + const { label, onClick } = + componentEditorState.editorMode === "edit" + ? { + label: interfaceComponentPin.name, + onClick: null, + } + : { + label: stringifySimulationValue( + type === "input" + ? nullthrows( + componentEditorState.getInputValue(interfaceComponentPin.id), + ) + : nullthrows(componentEditorState.getNodePinValue(nodePinId)), + ), + onClick: + type === "input" + ? () => { + const nodePinValue = nullthrows( + componentEditorState.getInputValue( + interfaceComponentPin.id, + ), + ); + componentEditorState.setInputValue( + interfaceComponentPin.id, + wrappingIncrementSimulationValue(nodePinValue), + ); + } + : null, + }; const nodePinPosition = nullthrows( getCCComponentEditorRendererNodeGeometry( @@ -53,9 +73,9 @@ export default function CCComponentEditorRendererInputValue({ stroke={theme.palette.textPrimary} fill={theme.palette.white} strokeWidth={1} - {...(type === "input" + {...(onClick ? { - onPointerDown: updateInputValue, + onPointerDown: onClick, style: { cursor: "pointer" }, } : {})} @@ -70,7 +90,7 @@ export default function CCComponentEditorRendererInputValue({ dy={1} style={{ pointerEvents: "none" }} > - {stringifySimulationValue(nodePinValue)} + {label} ); diff --git a/src/pages/edit/Editor/renderer/NodePin.tsx b/src/pages/edit/Editor/renderer/NodePin.tsx index 59cff0f..90ed358 100644 --- a/src/pages/edit/Editor/renderer/NodePin.tsx +++ b/src/pages/edit/Editor/renderer/NodePin.tsx @@ -134,15 +134,12 @@ export default function CCComponentEditorRendererNodePin({ }, }); - const isSimulationMode = useComponentEditorStore()( - (s) => s.editorMode === "play", - ); const interfaceComponentPin = store.componentPins.getByImplementation(nodePinId); return ( <> - {isSimulationMode && interfaceComponentPin && ( + {interfaceComponentPin && ( )} diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 4251fe6..610984b 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -1,7 +1,8 @@ import { Add as AddIcon, Download as DownloadIcon, - MoreVert, + MoreVert as MoreVertIcon, + NoteAdd as NoteAddIcon, Upload as UploadIcon, } from "@mui/icons-material"; import { @@ -16,7 +17,6 @@ import { Typography, } from "@mui/material"; import { useRef, useState } from "react"; -import { ComponentPropertyDialog } from "../../components/ComponentPropertyDialog"; import { type CCComponentId, CCComponentStore } from "../../store/component"; import { useStore } from "../../store/react"; import { useComponents } from "../../store/react/selectors"; @@ -55,8 +55,13 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { const inputRef = useRef(null); - const [isComponentPropertyDialogOpen, setIsComponentPropertyDialogOpen] = - useState(false); + const onCreateComponent = () => { + const component = CCComponentStore.create({ + name: "New Component", + }); + store.components.register(component); + onComponentSelected(component.id); + }; const [componentMenuState, setComponentMenuState] = useState<{ componentId: CCComponentId; @@ -70,6 +75,9 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { File +
@@ -156,7 +164,7 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { }); }} > - + ))} @@ -171,14 +179,6 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { horizontal: "left", }} > - { - setIsComponentPropertyDialogOpen(true); - setComponentMenuState(null); - }} - > - Rename - { store.components.unregister(componentMenuState.componentId); @@ -189,21 +189,6 @@ export default function HomePage({ onComponentSelected }: HomePageProps) { )} - {isComponentPropertyDialogOpen && ( - { - const newComponent = CCComponentStore.create({ - name: newName, - }); - store.components.register(newComponent); - onComponentSelected(newComponent.id); - }} - onCancel={() => { - setIsComponentPropertyDialogOpen(false); - }} - /> - )} ); diff --git a/src/store/componentEvaluator.ts b/src/store/componentEvaluator.ts index 4f4a614..d9668b8 100644 --- a/src/store/componentEvaluator.ts +++ b/src/store/componentEvaluator.ts @@ -303,58 +303,6 @@ function simulateNode( } else { unevaluatedNodes.add(currentNodeId); } - // else if ( - // currentComponentId === flipflop.id && - // !visitedFlipFlops.has(currentNodeId) - // ) { - // const frame = previousFrame - // ? nullthrows(previousFrame?.nodes.get(currentNodeId)).child - // : null; - // const result = simulateNode( - // store, - // currentNodeId, - // nodePinInputValues.get(currentNodeId) || new Map(), - // frame, - // ); - // if (!result) { - // return null; - // } - // childMap.set(currentNodeId, result); - // for (const [outputPinId, outputValue] of result.outputValues) { - // if (!visitedFlipFlops.has(currentNodeId)) { - // const connections = nullthrows( - // store.connections.getConnectionsByNodePinId(outputPinId), - // ); - // if (connections.length !== 0) { - // for (const connection of connections) { - // updateNodePinInputValues( - // store, - // nodePinInputValues, - // connection.to, - // outputValue, - // ); - // } - // } else { - // const parentNodePin = nullthrows( - // nodePins.find((nodePin) => { - // const componentPin = nullthrows( - // store.componentPins.get(nodePin.componentPinId), - // ); - // return ( - // componentPin.type === "output" && - // componentPin.implementation === outputPinId - // ); - // }), - // ); - // outputValues.set(parentNodePin.id, outputValue); - // } - // if (currentComponentId === flipflop.outputPin.componentId) { - // visitedFlipFlops.add(currentNodeId); - // } - // } - // } - // visitedFlipFlops.add(currentNodeId); - // } } const pins = new Map(); @@ -451,6 +399,35 @@ export default function simulateComponent( } } + // Set 0s for unconnected inputs + for (const child of children) { + const innerPins = store.nodePins.getManyByNodeId(child.id); + for (const innerPin of innerPins) { + const connections = store.connections.getConnectionsByNodePinId( + innerPin.id, + ); + if (connections.length > 0) { + continue; + } + const componentPin = nullthrows( + store.componentPins.get(innerPin.componentPinId), + ); + if (componentPin.type === "input") { + const inputValuesMap = nullthrows(nodePinInputValues.get(child.id)); + if (!inputValuesMap.has(innerPin.id)) { + const bitWidthStatus = store.nodePins.getNodePinBitWidthStatus( + innerPin.id, + ); + const bitWidth = !bitWidthStatus.isFixed + ? 1 + : bitWidthStatus.bitWidth; + const zeroValue = Array(bitWidth).fill(false); + inputValuesMap.set(innerPin.id, zeroValue); + } + } + } + } + const unevaluatedNodes = new Set(); for (const child of children) { unevaluatedNodes.add(child.id); diff --git a/src/store/componentPin.ts b/src/store/componentPin.ts index 65f006c..96bd5c6 100644 --- a/src/store/componentPin.ts +++ b/src/store/componentPin.ts @@ -13,6 +13,7 @@ import { input, not, or, + output, xor, } from "./intrinsics/definitions"; import type { CCNodePinId } from "./nodePin"; @@ -78,6 +79,12 @@ export class CCComponentPinStore extends EventEmitter mount() { this.#store.nodePins.on("didRegister", (nodePin) => { + if ( + nodePin.componentPinId !== input.inputPin.A.id && + nodePin.componentPinId !== output.outputPin.id + ) { + return; + } this.register(this.createForNodePin(nodePin.id)); }); this.#store.nodePins.on("willUnregister", (nodePin) => { @@ -91,27 +98,6 @@ export class CCComponentPinStore extends EventEmitter const toComponentPin = this.getByImplementation(to); if (toComponentPin) this.unregister(toComponentPin.id); }); - this.#store.connections.on("willUnregister", (connection) => { - if ( - !this.#store.nodePins.isMarkedAsDeleted(connection.to) && - this.#store.nodePins.get(connection.to) - ) { - this.register(this.createForNodePin(connection.to)); - } - // output pins can have multiple connections - // so we need to check if the connection is the last one - if ( - this.#store.connections.getConnectionsByNodePinId(connection.from) - .length === 1 - ) { - if ( - !this.#store.nodePins.isMarkedAsDeleted(connection.from) && - this.#store.nodePins.get(connection.from) - ) { - this.register(this.createForNodePin(connection.from)); - } - } - }); } /** @@ -248,6 +234,8 @@ export class CCComponentPinStore extends EventEmitter case nullthrows(xor.outputPin.id): case nullthrows(input.inputPin.A.id): case nullthrows(input.outputPin.id): + case nullthrows(output.inputPin.A.id): + case nullthrows(output.outputPin.id): case nullthrows(flipflop.inputPin.In.id): case nullthrows(flipflop.outputPin.id): { return { isFixed: false, fixMode: "automatic" }; diff --git a/src/store/connection.ts b/src/store/connection.ts index 232d3ed..938e59a 100644 --- a/src/store/connection.ts +++ b/src/store/connection.ts @@ -71,7 +71,6 @@ export class CCConnectionStore extends EventEmitter { const fromNodePinId = connection.from; const toNodePinId = connection.to; if (!this.#store.nodePins.isConnectable(fromNodePinId, toNodePinId)) { - window.alert(`Cannot connect pins: ${fromNodePinId} and ${toNodePinId}`); return; } this.#connections.set(connection.id, connection); diff --git a/src/store/fixtures.ts b/src/store/fixtures.ts index c7fcd2b..c91cf87 100644 --- a/src/store/fixtures.ts +++ b/src/store/fixtures.ts @@ -7,6 +7,7 @@ import { CCNodeStore } from "./node"; export function createStoreFixture(): CCStore { const store = new CCStore(); + store.mount(); const rootComponent = CCComponentStore.create({ name: "Root", diff --git a/src/store/intrinsics/definitions.ts b/src/store/intrinsics/definitions.ts index 9ade908..8f472ea 100644 --- a/src/store/intrinsics/definitions.ts +++ b/src/store/intrinsics/definitions.ts @@ -98,6 +98,11 @@ export const input = createUnaryOperator( "Input", (a) => a, ); +export const output = createUnaryOperator( + ccIntrinsicComponentTypes.OUTPUT, + "Output", + (a) => a, +); export const aggregate = new IntrinsicComponentDefinition({ type: ccIntrinsicComponentTypes.AGGREGATE, @@ -166,6 +171,7 @@ export const definitions = { [ccIntrinsicComponentTypes.NOT]: not, [ccIntrinsicComponentTypes.XOR]: xor, [ccIntrinsicComponentTypes.INPUT]: input, + [ccIntrinsicComponentTypes.OUTPUT]: output, [ccIntrinsicComponentTypes.AGGREGATE]: aggregate, [ccIntrinsicComponentTypes.DECOMPOSE]: decompose, [ccIntrinsicComponentTypes.BROADCAST]: broadcast, diff --git a/src/store/intrinsics/types.ts b/src/store/intrinsics/types.ts index 5512675..5c121e5 100644 --- a/src/store/intrinsics/types.ts +++ b/src/store/intrinsics/types.ts @@ -4,6 +4,7 @@ export const ccIntrinsicComponentTypes = { NOT: "NOT", XOR: "XOR", INPUT: "INPUT", + OUTPUT: "OUTPUT", AGGREGATE: "AGGREGATE", BROADCAST: "BROADCAST", DECOMPOSE: "DECOMPOSE", diff --git a/src/store/nodePin.ts b/src/store/nodePin.ts index 06c6a85..911dd10 100644 --- a/src/store/nodePin.ts +++ b/src/store/nodePin.ts @@ -5,7 +5,13 @@ import type { Opaque } from "type-fest"; import type CCStore from "."; import type { CCComponentPinId, CCNodePinBitWidthStatus } from "./componentPin"; import { IntrinsicComponentDefinition } from "./intrinsics/base"; -import { aggregate, broadcast, decompose } from "./intrinsics/definitions"; +import { + aggregate, + broadcast, + decompose, + input, + output, +} from "./intrinsics/definitions"; import type { CCNodeId } from "./node"; export type CCNodePinId = Opaque; @@ -307,6 +313,17 @@ export class CCNodePinStore extends EventEmitter { if (!aNodePin || !bNodePin) { throw new Error(`Node pin ${a} or ${b} does not exist in the store`); } + if ( + aNodePin.componentPinId === input.inputPin.A.id || + bNodePin.componentPinId === input.inputPin.A.id || + aNodePin.componentPinId === output.outputPin.id || + bNodePin.componentPinId === output.outputPin.id + ) { + console.warn( + `Cannot connect to input pin A or output pin: ${aNodePin.id} and ${bNodePin.id}`, + ); + return false; + } const aComponentPin = this.#store.componentPins.get( aNodePin?.componentPinId ?? null, ); @@ -318,6 +335,12 @@ export class CCNodePinStore extends EventEmitter { `Component pin ${aNodePin?.componentPinId} or ${bNodePin?.componentPinId} does not exist in the store`, ); } + if (aComponentPin.type === bComponentPin.type) { + console.warn( + `Cannot connect pins of the same type: ${aNodePin.id} and ${bNodePin.id}`, + ); + return false; + } const aNode = this.#store.nodes.get(aNodePin.nodeId); const bNode = this.#store.nodes.get(bNodePin.nodeId); if (!aNode || !bNode) { @@ -337,6 +360,20 @@ export class CCNodePinStore extends EventEmitter { ); return false; } + const aConnections = this.#store.connections.getConnectionsByNodePinId( + aNodePin.id, + ); + const bConnections = this.#store.connections.getConnectionsByNodePinId( + bNodePin.id, + ); + if (aComponentPin.type === "input" && aConnections.length > 0) { + console.warn(`Input pin already has a connection: ${aNodePin.id}`); + return false; + } + if (bComponentPin.type === "input" && bConnections.length > 0) { + console.warn(`Input pin already has a connection: ${bNodePin.id}`); + return false; + } const aBitWidthStatus = this.getNodePinBitWidthStatus(a); const bBitWidthStatus = this.getNodePinBitWidthStatus(b); if (aBitWidthStatus.isFixed && bBitWidthStatus.isFixed) {