diff --git a/apps/frontend/app/(website)/studio/components.json b/apps/frontend/app/(website)/studio/components.json deleted file mode 100644 index b1ccf6135..000000000 --- a/apps/frontend/app/(website)/studio/components.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "default", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "tailwind.config.ts", - "css": "src/index.css", - "baseColor": "slate", - "cssVariables": true - }, - "aliases": { - "components": "@studio/components", - "utils": "@studio/lib/utils" - } -} diff --git a/apps/frontend/app/(website)/studio/main/5PaneSetup.tsx b/apps/frontend/app/(website)/studio/main/5PaneSetup.tsx index 58ccd3f2b..5d08c89ba 100644 --- a/apps/frontend/app/(website)/studio/main/5PaneSetup.tsx +++ b/apps/frontend/app/(website)/studio/main/5PaneSetup.tsx @@ -4,6 +4,7 @@ import type { KnownEngines } from "@codemod-com/utilities"; import { useTheme } from "@context/useTheme"; import { getCodeDiff } from "@studio/api/getCodeDiff"; import Panel from "@studio/components/Panel"; +import { BoundResizePanel } from "@studio/components/ResizePanel/BoundResizePanel"; import ResizeHandle from "@studio/components/ResizePanel/ResizeHandler"; import InsertExampleButton from "@studio/components/button/InsertExampleButton"; import { @@ -13,6 +14,7 @@ import { SelectTrigger, SelectValue, } from "@studio/components/ui/select"; +import { VisibilityIcon } from "@studio/icons"; import { TestTabsComponent } from "@studio/main/PageBottomPane/TestTabsComponent"; import { AssistantTab } from "@studio/main/PaneLayout"; import { LoginWarningModal } from "@studio/main/PaneLayout/LoginWarningModal"; @@ -25,11 +27,9 @@ import Codemod from "./Codemod"; import { Header } from "./Header/Header"; import Layout from "./Layout"; import { - BoundResizePanel, CodeSnippets, type PanelsRefs, ResizablePanelsIndices, - ShowPanelTile, } from "./PageBottomPane"; import { useSnippetsPanels } from "./PageBottomPane/hooks"; @@ -133,7 +133,6 @@ const Main = () => { const beforeAfterBottomPanels = ( <> { panels={[beforePanel, afterPanel]} > {onlyAfterHidden && ( - { afterPanel.visibilityOptions?.show(); panelRefs.current[ResizablePanelsIndices.AFTER_SNIPPET]?.resize( 50, ); }} - /> + > + + After + )} diff --git a/apps/frontend/app/(website)/studio/main/ASTViewer.tsx b/apps/frontend/app/(website)/studio/main/ASTViewer.tsx deleted file mode 100644 index 10c8b76e9..000000000 --- a/apps/frontend/app/(website)/studio/main/ASTViewer.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import Text from "@studio/components/Text"; -import Tree, { type TreeNode } from "@studio/components/Tree"; -import useScrollNodeIntoView from "@studio/hooks/useScrollNodeIntoView"; -import { - useSelectFirstTreeNodeForSnippet, - useSnippetsStore, -} from "@studio/store/snippets"; -import { useRangesOnTarget } from "@studio/store/utils/useRangesOnTarget"; -import { useEffect, useRef, useState } from "react"; -import { flushSync } from "react-dom"; - -type Props = { - type: "before" | "after" | "output"; -}; -const ASTViewer = ({ type }: Props) => { - const ASTTreeRef = useRef(null); - const getFirstTreeNode = useSelectFirstTreeNodeForSnippet(); - const [firstNode, setFirstNode] = useState(null); - const { getSelectedEditors } = useSnippetsStore(); - const { - [type]: { rootNode }, - } = getSelectedEditors(); - - const setRangesOnTarget = useRangesOnTarget(); - const scrollNodeIntoView = useScrollNodeIntoView(); - const handleNodeClick = (node: TreeNode = rootNode) => { - scrollNodeIntoView(node, ASTTreeRef); - - flushSync(() => { - setFirstNode(node); - setRangesOnTarget({ - target: type === "before" ? "BEFORE_INPUT" : "AFTER_INPUT", - ranges: [node], - }); - const setRange = getSelectedEditors().setSelection(type); - return setRange({ - kind: "FIND_CLOSEST_PARENT", - ranges: [node], - }); - }); - }; - - useEffect(() => { - if (getFirstTreeNode(type) !== null) { - scrollNodeIntoView(getFirstTreeNode(type), ASTTreeRef); - setFirstNode(getFirstTreeNode(type)); - } - }, [scrollNodeIntoView, getFirstTreeNode, type]); - - return ( - <> -
- {rootNode ? ( - - ) : ( - - Please provide a snippet to render an Abstract Syntax Tree for it. - - )} -
- - ); -}; - -export default ASTViewer; diff --git a/apps/frontend/app/(website)/studio/main/ASTViewer/AboristViewer.tsx b/apps/frontend/app/(website)/studio/main/ASTViewer/AboristViewer.tsx new file mode 100644 index 000000000..15bf2b669 --- /dev/null +++ b/apps/frontend/app/(website)/studio/main/ASTViewer/AboristViewer.tsx @@ -0,0 +1,144 @@ +import { cn } from "@/utils"; +import type { Node } from "@babel/types"; +import Text from "@studio/components/Text"; +import { useScrollNodeIntoView } from "@studio/main/ASTViewer/useScrollNodeIntoView"; +import { + useSelectFirstTreeNodeForSnippet, + useSnippetsStore, +} from "@studio/store/snippets"; +import { useRangesOnTarget } from "@studio/store/utils/useRangesOnTarget"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import { type NodeRendererProps, Tree } from "react-arborist"; +import { flushSync } from "react-dom"; +import useResizeObserver from "use-resize-observer"; + +type TreeNode = { + id: string; + actualNode: Node; + label: string; + children?: TreeNode[]; + start: number; + end: number; +}; + +type EditorType = "before" | "after" | "output"; + +interface Props { + type: EditorType; +} + +const transformTreeData = (node: TreeNode): TreeNode => ({ + ...node, + children: node.children?.length + ? node.children.map(transformTreeData) + : undefined, +}); + +export const ASTViewer: React.FC = ({ type }) => { + const ASTTreeRef = useRef(null); + const { width = 0, height = 0 } = useResizeObserver({ ref: ASTTreeRef }); + const getFirstTreeNode = useSelectFirstTreeNodeForSnippet(); + const [firstNode, setFirstNode] = useState(null); + const { getSelectedEditors } = useSnippetsStore(); + const { + [type]: { rootNode }, + } = getSelectedEditors(); + + const setRangesOnTarget = useRangesOnTarget(); + const scrollNodeIntoView = useScrollNodeIntoView(type); + + const handleNodeClick = useCallback( + (node: TreeNode = rootNode) => { + scrollNodeIntoView(node, ASTTreeRef); + + flushSync(() => { + setFirstNode(node); + setRangesOnTarget({ + target: `${type.toUpperCase()}_INPUT`, + ranges: [node], + }); + const setRange = getSelectedEditors().setSelection(type); + setRange({ + kind: "FIND_CLOSEST_PARENT", + ranges: [node], + }); + }); + }, + [rootNode, scrollNodeIntoView, setRangesOnTarget, type, getSelectedEditors], + ); + + useEffect(() => { + const firstTreeNode = getFirstTreeNode(type); + if (firstTreeNode) { + scrollNodeIntoView(firstTreeNode, ASTTreeRef); + setFirstNode(firstTreeNode); + } + }, [scrollNodeIntoView, getFirstTreeNode, type]); + + const NodeComponent = React.memo>( + ({ node, style, dragHandle }) => { + const isSelected = firstNode?.id === node.data.id; + return ( +
+ + {!node.isLeaf && ( + node.toggle()} + className={cn( + node.isOpen ? "text-red-600 " : "text-green-500", + )} + > + {node.isOpen ? "- " : "+ "} + + )} + { + e.stopPropagation(); + handleNodeClick(node.data); + }} + className="inline-block" + id={`${node.data.id}`} + > + {node.data.label} + + +
+ ); + }, + ); + + NodeComponent.displayName = "NodeComponent"; + + if (!rootNode) { + return ( +
+ + Please provide a snippet to render an Abstract Syntax Tree for it. + +
+ ); + } + + const transformedRootNode = transformTreeData(rootNode); + + return ( +
+ + {NodeComponent} + +
+ ); +}; diff --git a/apps/frontend/app/(website)/studio/main/ASTViewer/AstSectionBase.tsx b/apps/frontend/app/(website)/studio/main/ASTViewer/AstSectionBase.tsx new file mode 100644 index 000000000..123a4109c --- /dev/null +++ b/apps/frontend/app/(website)/studio/main/ASTViewer/AstSectionBase.tsx @@ -0,0 +1,40 @@ +import { BoundResizePanel } from "@studio/components/ResizePanel/BoundResizePanel"; +import ResizeHandle from "@studio/components/ResizePanel/ResizeHandler"; +import { ASTViewer } from "@studio/main/ASTViewer/AboristViewer"; +import type { + PanelData, + PanelsRefs, +} from "@studio/main/PageBottomPane/utils/types"; +import { isVisible } from "@studio/utils/visibility"; +import React, { memo } from "react"; + +const AstSectionBase = ({ + panels, + panelRefs, +}: { + panels: PanelData[]; + panelRefs: PanelsRefs; +}) => { + return panels.filter(isVisible).map((panel, i, { length }) => ( + + + + + {i !== length - 1 && isVisible(panels[i + 1]) && ( + + )} + + )); +}; + +export const AstSection = memo(AstSectionBase); diff --git a/apps/frontend/app/(website)/studio/src/hooks/useScrollNodeIntoView.ts b/apps/frontend/app/(website)/studio/main/ASTViewer/useScrollNodeIntoView.ts similarity index 57% rename from apps/frontend/app/(website)/studio/src/hooks/useScrollNodeIntoView.ts rename to apps/frontend/app/(website)/studio/main/ASTViewer/useScrollNodeIntoView.ts index c3d6e2c9f..5bedc90cc 100644 --- a/apps/frontend/app/(website)/studio/src/hooks/useScrollNodeIntoView.ts +++ b/apps/frontend/app/(website)/studio/main/ASTViewer/useScrollNodeIntoView.ts @@ -1,7 +1,9 @@ import type { TreeNode } from "@studio/components/Tree"; import type React from "react"; +import { useState } from "react"; -const useScrollNodeIntoView = () => { +export const useScrollNodeIntoView = () => { + const [selectedElem, setSelectedElem] = useState(); const scrollIntoView = async ( node: TreeNode | null, treeRef: React.RefObject, @@ -11,15 +13,19 @@ const useScrollNodeIntoView = () => { } // delay to make the animation smoother await delay(200); - const foundElem = document.getElementById( - `${node.label}-${node.start}-${node.end}`, + const foundElem = document.getElementById(`${node.id}`); + + console.log( + node.id, + { + foundElem, + }, + treeRef, + treeRef.current, ); if (foundElem && treeRef.current) { - treeRef.current.scrollTo({ - top: foundElem.offsetTop - treeRef.current.offsetTop, - behavior: "smooth", - }); + foundElem.scrollIntoView({ behavior: "smooth", block: "center" }); } }; return scrollIntoView; @@ -29,5 +35,3 @@ const delay = async (ms: number) => new Promise((resolve) => { setTimeout(resolve, ms); }); - -export default useScrollNodeIntoView; diff --git a/apps/frontend/app/(website)/studio/main/PageBottomPane/Components/index.ts b/apps/frontend/app/(website)/studio/main/PageBottomPane/Components/index.ts index bab636774..94e2274ae 100644 --- a/apps/frontend/app/(website)/studio/main/PageBottomPane/Components/index.ts +++ b/apps/frontend/app/(website)/studio/main/PageBottomPane/Components/index.ts @@ -1,4 +1,3 @@ export * from "./Snippets/CodeSnippets"; export * from "./WarningTexts"; export * from "./SnippedHeader"; -export * from "./side-components"; diff --git a/apps/frontend/app/(website)/studio/main/PageBottomPane/Components/side-components.tsx b/apps/frontend/app/(website)/studio/main/PageBottomPane/Components/side-components.tsx deleted file mode 100644 index 91e26532a..000000000 --- a/apps/frontend/app/(website)/studio/main/PageBottomPane/Components/side-components.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { cn } from "@/utils"; -import type { KnownEngines } from "@codemod-com/utilities"; -import ResizeHandle from "@studio/components/ResizePanel/ResizeHandler"; -import { isServer } from "@studio/config"; -import { VisibilityIcon } from "@studio/icons/VisibilityIcon"; -import type { Void } from "@studio/types/transformations"; -import { debounce } from "@studio/utils/debounce"; -import { isNil } from "@studio/utils/isNil"; -import { isVisible } from "@studio/utils/visibility"; -import React, { memo, type PropsWithChildren } from "react"; -import { PanelGroup } from "react-resizable-panels"; -import ASTViewer from "../../ASTViewer"; -import Layout from "../../Layout"; -import type { - ContentViewerProps, - PanelComponentProps, - PanelData, - PanelsRefs, -} from "../utils/types"; - -export const BoundResizePanel = ({ - defaultSize = 50, - minSize = 0, - panelRefs, - panelRefIndex, - children, - boundedIndex, - className, - style = { - maxHeight: isServer ? 0 : "unset", - flexBasis: isServer ? "50%" : "0", - }, -}: PanelComponentProps) => { - return ( - { - panelRefs.current[panelRefIndex] = ref; - }} - style={style} - onResize={ - !isNil(boundedIndex) - ? debounce((size) => { - const panel = panelRefs.current[boundedIndex]; - if (!isNil(panel) && !isNil(size)) panel.resize(size); - }, 5) - : undefined - } - > - {children} - - ); -}; - -export const ContentViewer: React.FC = ({ - type, - engine, -}) => ( - <> - {engine === "jscodeshift" ? ( - - ) : ( - "The AST View is not yet supported for tsmorph" - )} - -); - -export const BottomPanel: React.FC = ({ children }) => ( - - {children} - -); - -const AstSectionBase = ({ - panels, - panelRefs, - engine, -}: { - panels: PanelData[]; - panelRefs: PanelsRefs; - engine: KnownEngines; -}) => { - return panels.filter(isVisible).map((panel, i, { length }) => ( - - - - - {i !== length - 1 && isVisible(panels[i + 1]) && ( - - )} - - )); -}; - -export const ShowPanelTile = ({ - onClick, - panel, - header, -}: { panel: PanelData; header: string; onClick: Void }) => ( -
- - {header} -
-); - -export const AstSection = memo(AstSectionBase); diff --git a/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/panelsData.tsx b/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/panelsData.tsx index c12135282..0aa00714f 100644 --- a/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/panelsData.tsx +++ b/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/panelsData.tsx @@ -1,27 +1,15 @@ -import type { KnownEngines } from "@codemod-com/utilities"; -import ASTViewer from "@studio/main/ASTViewer"; import { - type ContentViewerVariant, type PanelData, ResizablePanelsIndices, } from "@studio/main/PageBottomPane/utils/types"; import type { Repeat } from "@studio/types/transformations"; -export const getContent = - (type: ContentViewerVariant) => (engine: KnownEngines) => - engine === "jscodeshift" ? ( - - ) : ( - "The AST View is not yet supported for tsmorph" - ); - const beforePanel: PanelData = { relatedAST: ResizablePanelsIndices.BEFORE_AST, boundIndex: ResizablePanelsIndices.BEFORE_AST, snippedIndex: ResizablePanelsIndices.BEFORE_SNIPPET, type: "before", hasBoundResize: true, - content: getContent("before"), snippetData: { header: ( @@ -40,7 +28,6 @@ const afterPanel: PanelData = { snippedIndex: ResizablePanelsIndices.AFTER_SNIPPET, type: "after", hasBoundResize: false, - content: getContent("after"), snippetData: { header: "After (Expected)", diffEditorWrapper: { @@ -54,7 +41,6 @@ const outputPanel: PanelData = { relatedAST: ResizablePanelsIndices.OUTPUT_AST, snippedIndex: ResizablePanelsIndices.OUTPUT_SNIPPET, type: "output", - content: getContent("output"), snippetData: { header: ( diff --git a/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/types.ts b/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/types.ts index f519eb6d4..0d0221072 100644 --- a/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/types.ts +++ b/apps/frontend/app/(website)/studio/main/PageBottomPane/utils/types.ts @@ -55,8 +55,6 @@ export enum ResizablePanelsIndices { BOTTOM = 16, } -export type PanelContentRenderer = (engine: KnownEngines) => React.ReactNode; - export type PanelData = Pick< PanelComponentProps, "visibilityOptions" | "hasBoundResize" @@ -65,7 +63,6 @@ export type PanelData = Pick< boundIndex?: ResizablePanelsIndices; snippedIndex: ResizablePanelsIndices; type: ContentViewerVariant; - content: PanelContentRenderer; relatedAST: ResizablePanelsIndices; defaultSize?: number; snippetData: SnippetData; diff --git a/apps/frontend/app/(website)/studio/main/PaneLayout/tabsData.tsx b/apps/frontend/app/(website)/studio/main/PaneLayout/tabsData.tsx index c616d899c..82b464844 100644 --- a/apps/frontend/app/(website)/studio/main/PaneLayout/tabsData.tsx +++ b/apps/frontend/app/(website)/studio/main/PaneLayout/tabsData.tsx @@ -2,12 +2,9 @@ import { Chat } from "@chatbot/Chat"; import { useAiService } from "@chatbot/useAiService/useAiService"; import type { KnownEngines } from "@codemod-com/utilities"; import LiveIcon from "@studio/icons/LiveIcon"; +import { AstSection } from "@studio/main/ASTViewer/AstSectionBase"; import Table from "@studio/main/Log/Table"; -import { - AstSection, - type PanelData, - type PanelsRefs, -} from "@studio/main/PageBottomPane"; +import type { PanelData, PanelsRefs } from "@studio/main/PageBottomPane"; import { SignInRequired } from "@studio/main/PaneLayout/SignInRequired"; import { useModStore } from "@studio/store/mod"; import { TabNames } from "@studio/store/view"; @@ -58,11 +55,14 @@ export const useTabsData = ({ name: "AST", content: ( - + {engine === "jscodeshift" ? ( + + ) : ( + "The AST View is not yet supported for tsmorph" + )} ), }, diff --git a/apps/frontend/app/(website)/studio/src/components/ResizePanel/BoundResizePanel.tsx b/apps/frontend/app/(website)/studio/src/components/ResizePanel/BoundResizePanel.tsx new file mode 100644 index 000000000..10bd7491e --- /dev/null +++ b/apps/frontend/app/(website)/studio/src/components/ResizePanel/BoundResizePanel.tsx @@ -0,0 +1,43 @@ +import { cn } from "@/utils"; +import { isServer } from "@studio/config"; +import Layout from "@studio/main/Layout"; +import type { PanelComponentProps } from "@studio/main/PageBottomPane"; +import { debounce } from "@studio/utils/debounce"; +import { isNil } from "@studio/utils/isNil"; + +export const BoundResizePanel = ({ + defaultSize = 50, + minSize = 0, + panelRefs, + panelRefIndex, + children, + boundedIndex, + className, + style = { + maxHeight: isServer ? 0 : "unset", + flexBasis: isServer ? "50%" : "0", + }, +}: PanelComponentProps) => { + return ( + { + panelRefs.current[panelRefIndex] = ref; + }} + style={style} + onResize={ + !isNil(boundedIndex) + ? debounce((size) => { + const panel = panelRefs.current[boundedIndex]; + if (!isNil(panel) && !isNil(size)) panel.resize(size); + }, 5) + : undefined + } + > + {children} + + ); +}; diff --git a/apps/frontend/app/(website)/studio/src/components/Snippet/CodeSnippedPanel.tsx b/apps/frontend/app/(website)/studio/src/components/Snippet/CodeSnippedPanel.tsx index 77aaf9465..e0477608f 100644 --- a/apps/frontend/app/(website)/studio/src/components/Snippet/CodeSnippedPanel.tsx +++ b/apps/frontend/app/(website)/studio/src/components/Snippet/CodeSnippedPanel.tsx @@ -1,6 +1,6 @@ import { cn } from "@/utils"; +import { BoundResizePanel } from "@studio/components/ResizePanel/BoundResizePanel"; import { - BoundResizePanel, type PanelData, type PanelsRefs, SnippetHeader, diff --git a/apps/frontend/app/(website)/studio/src/components/button/ClearInputButton.tsx b/apps/frontend/app/(website)/studio/src/components/button/ClearInputButton.tsx deleted file mode 100644 index acd652b31..000000000 --- a/apps/frontend/app/(website)/studio/src/components/button/ClearInputButton.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { cn } from "@/utils"; -import { Backspace as BackspaceIcon } from "@phosphor-icons/react"; -import Tooltip from "@studio/components/Tooltip/Tooltip"; -import { Button } from "@studio/components/ui/button"; -import { useModStore } from "@studio/store/mod"; -import { useSnippetsStore } from "@studio/store/snippets"; - -type Props = { className?: string }; - -const ClearInputButton = ({ className }: Props) => { - const { setContent } = useModStore(); - const { clearAll } = useSnippetsStore(); - return ( - { - clearAll(); - setContent(""); - }} - size="sm" - variant="outline" - > - - Clear Inputs - - } - content={

Clear all inputs

} - /> - ); -}; - -export default ClearInputButton; diff --git a/apps/frontend/package.json b/apps/frontend/package.json index c70a8e737..80de23f21 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -109,6 +109,7 @@ "prop-types": "catalog:", "ramda": "catalog:", "react": "18.2.0", + "react-arborist": "^3.4.0", "react-chartjs-2": "catalog:", "react-dom": "18.2.0", "react-hook-form": "catalog:", @@ -142,6 +143,7 @@ "ts-morph": "^19.0.0", "tslib": "catalog:", "universal-base64url": "catalog:", + "use-resize-observer": "^9.1.0", "valibot": "catalog:", "vaul": "catalog:", "web-vitals": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22e6d5159..057b79601 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1491,6 +1491,9 @@ importers: react: specifier: 18.2.0 version: 18.2.0 + react-arborist: + specifier: ^3.4.0 + version: 3.4.0(@types/hoist-non-react-statics@3.3.5)(@types/node@20.14.8)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-chartjs-2: specifier: 'catalog:' version: 5.2.0(chart.js@4.4.3)(react@18.2.0) @@ -1590,6 +1593,9 @@ importers: universal-base64url: specifier: 'catalog:' version: 1.1.0 + use-resize-observer: + specifier: ^9.1.0 + version: 9.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) valibot: specifier: 'catalog:' version: 0.24.1 @@ -10352,6 +10358,15 @@ packages: '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@react-dnd/asap@4.0.1': + resolution: {integrity: sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==} + + '@react-dnd/invariant@2.0.0': + resolution: {integrity: sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==} + + '@react-dnd/shallowequal@2.0.0': + resolution: {integrity: sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==} + '@reduxjs/toolkit@1.9.7': resolution: {integrity: sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==} peerDependencies: @@ -13294,6 +13309,9 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dnd-core@14.0.1: + resolution: {integrity: sha512-+PVS2VPTgKFPYWo3vAFEA8WPbTf7/xo43TifH9G8S1KqnrQu0o77A3unrF5yOugy4mIz7K5wAVFHUcha7wsz6A==} + doc-path@4.1.1: resolution: {integrity: sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==} engines: {node: '>=16'} @@ -15632,6 +15650,9 @@ packages: resolution: {integrity: sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==} engines: {node: '>= 4.0.0'} + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} @@ -17106,6 +17127,12 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-arborist@3.4.0: + resolution: {integrity: sha512-QI46oRGXJr0oaQfqqVobIiIoqPp5Y5gM69D2A2P7uHVif+X75XWnScR5drC7YDKgJ4CXVaDeFwnYKOWRRfncMg==} + peerDependencies: + react: '>= 16.14' + react-dom: '>= 16.14' + react-chartjs-2@5.2.0: resolution: {integrity: sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==} peerDependencies: @@ -17122,6 +17149,24 @@ packages: peerDependencies: react: ^15.3.0 || 16 || 17 || 18 + react-dnd-html5-backend@14.1.0: + resolution: {integrity: sha512-6ONeqEC3XKVf4eVmMTe0oPds+c5B9Foyj8p/ZKLb7kL2qh9COYxiBHv3szd6gztqi/efkmriywLUVlPotqoJyw==} + + react-dnd@14.0.5: + resolution: {integrity: sha512-9i1jSgbyVw0ELlEVt/NkCUkxy1hmhJOkePoCH713u75vzHGyXhPDm28oLfc2NMSBjZRM1Y+wRjHXJT3sPrTy+A==} + peerDependencies: + '@types/hoist-non-react-statics': '>= 3.3.1' + '@types/node': '>= 12' + '@types/react': '>= 16' + react: '>= 16.14' + peerDependenciesMeta: + '@types/hoist-non-react-statics': + optional: true + '@types/node': + optional: true + '@types/react': + optional: true + react-dom@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -17372,6 +17417,13 @@ packages: react: '>=16 || >=17 || >= 18' react-dom: '>=16 || >=17 || >= 18' + react-window@1.8.10: + resolution: {integrity: sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react@18.2.0: resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} engines: {node: '>=0.10.0'} @@ -17493,6 +17545,9 @@ packages: redux@4.2.1: resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + reflect.getprototypeof@1.0.6: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} @@ -19030,6 +19085,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-resize-observer@9.1.0: + resolution: {integrity: sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==} + peerDependencies: + react: 16.8.0 - 18 + react-dom: 16.8.0 - 18 + use-sidecar@1.1.2: resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} @@ -23508,6 +23569,12 @@ snapshots: '@radix-ui/rect@1.1.0': {} + '@react-dnd/asap@4.0.1': {} + + '@react-dnd/invariant@2.0.0': {} + + '@react-dnd/shallowequal@2.0.0': {} + '@reduxjs/toolkit@1.9.7(react-redux@7.2.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)': dependencies: immer: 9.0.21 @@ -27331,6 +27398,12 @@ snapshots: dlv@1.1.3: {} + dnd-core@14.0.1: + dependencies: + '@react-dnd/asap': 4.0.1 + '@react-dnd/invariant': 2.0.0 + redux: 4.2.1 + doc-path@4.1.1: {} doctrine@2.1.0: @@ -30191,6 +30264,8 @@ snapshots: tree-dump: 1.0.1(tslib@2.6.3) tslib: 2.6.3 + memoize-one@5.2.1: {} + memoize-one@6.0.0: {} memoizee@0.4.17: @@ -31933,6 +32008,20 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-arborist@3.4.0(@types/hoist-non-react-statics@3.3.5)(@types/node@20.14.8)(@types/react@18.2.55)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + react: 18.2.0 + react-dnd: 14.0.5(@types/hoist-non-react-statics@3.3.5)(@types/node@20.14.8)(@types/react@18.2.55)(react@18.2.0) + react-dnd-html5-backend: 14.1.0 + react-dom: 18.2.0(react@18.2.0) + react-window: 1.8.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + redux: 5.0.1 + use-sync-external-store: 1.2.2(react@18.2.0) + transitivePeerDependencies: + - '@types/hoist-non-react-statics' + - '@types/node' + - '@types/react' + react-chartjs-2@5.2.0(chart.js@4.4.3)(react@18.2.0): dependencies: chart.js: 4.4.3 @@ -31949,6 +32038,23 @@ snapshots: prop-types: 15.8.1 react: 18.2.0 + react-dnd-html5-backend@14.1.0: + dependencies: + dnd-core: 14.0.1 + + react-dnd@14.0.5(@types/hoist-non-react-statics@3.3.5)(@types/node@20.14.8)(@types/react@18.2.55)(react@18.2.0): + dependencies: + '@react-dnd/invariant': 2.0.0 + '@react-dnd/shallowequal': 2.0.0 + dnd-core: 14.0.1 + fast-deep-equal: 3.1.3 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + optionalDependencies: + '@types/hoist-non-react-statics': 3.3.5 + '@types/node': 20.14.8 + '@types/react': 18.2.55 + react-dom@18.2.0(react@18.2.0): dependencies: loose-envify: 1.4.0 @@ -32237,6 +32343,13 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + react-window@1.8.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@babel/runtime': 7.24.7 + memoize-one: 5.2.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react@18.2.0: dependencies: loose-envify: 1.4.0 @@ -32399,6 +32512,8 @@ snapshots: dependencies: '@babel/runtime': 7.24.7 + redux@5.0.1: {} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 @@ -34351,6 +34466,12 @@ snapshots: dependencies: react: 18.2.0 + use-resize-observer@9.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@juggle/resize-observer': 3.4.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-sidecar@1.1.2(@types/react@18.2.55)(react@18.2.0): dependencies: detect-node-es: 1.1.0