diff --git a/web/app/page.tsx b/web/app/page.tsx index 5c6531e..341dce0 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -2,11 +2,14 @@ import React, { useMemo, useState } from "react"; import Image from "next/image"; +import { ChevronDownIcon, GithubIcon, LockIcon, ShieldIcon } from "lucide-react"; import toast from "react-hot-toast"; import { useBoolean } from "usehooks-ts"; import { parseUnits } from "viem"; -import { useAccount, useWriteContract } from "wagmi"; -import { ArrowsUpDownIcon, LockClosedIcon, WalletIcon } from "@heroicons/react/24/outline"; +import { useWriteContract } from "wagmi"; +import { ArrowsUpDownIcon, WalletIcon } from "@heroicons/react/24/outline"; +import { Footer } from "~~/components/Footer"; +import HiddenContent from "~~/components/HiddenContent"; import { Button } from "~~/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "~~/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~~/components/ui/select"; @@ -37,7 +40,6 @@ export default function UniswapClone() { const wbtc = useWbtcToken(); const { writeContractAsync } = useWriteContract(); const { daiPoolLiquidity, wbtcPoolLiquidity } = useCpamm(); - const { isConnected } = useAccount(); const { value: isSwapping, setValue: setIsSwapping } = useBoolean(false); const [swapState, setSwapState] = useState({ from: { @@ -242,65 +244,154 @@ export default function UniswapClone() { }; return ( -
-
{ - e.preventDefault(); - handleSwap(); - }} - > -

Swap

- - handleAmountChange(e, "from")} - onTokenChange={e => handleTokenChange(e, "from")} - disabled={isSwapping} - /> - -
- -
+
+
+ { + e.preventDefault(); + handleSwap(); + }} + > +

+ Private swaps, +
+ anytime. +

+ + + <> + handleAmountChange(e, "from")} + onTokenChange={e => handleTokenChange(e, "from")} + disabled={isSwapping} + daiPoolLiquidity={daiPoolLiquidity} + wbtcPoolLiquidity={wbtcPoolLiquidity} + /> + +
+ +
- handleAmountChange(e, "to")} - onTokenChange={e => handleTokenChange(e, "to")} - disabled={isSwapping} - /> - -
- Swap Price: 1 {swapState.from.token.symbol} = {formatTokenWithDecimals(swapPrice, 18)}{" "} - {swapState.to.token.symbol} + handleAmountChange(e, "to")} + onTokenChange={e => handleTokenChange(e, "to")} + disabled={isSwapping} + daiPoolLiquidity={daiPoolLiquidity} + wbtcPoolLiquidity={wbtcPoolLiquidity} + /> + +
+ 1 {swapState.from.token.symbol} = {formatTokenWithDecimals(swapPrice, 18)} {swapState.to.token.symbol} +
+ + + + + + +
+
Scroll to learn more
+
+
- - - {!isConnected && ( -
-
- - Connect your wallet to start +
+
+

What makes Double Zero swap unique?

+
+ } + className="bg-blue-500/10 text-blue-400 min-h-[250px]" + /> + } + className="bg-purple-500/10 text-purple-500" + />
-
- )} + +
+

How it works?

+
+ + + +
+
+
+

Learn more

+ +
+ +
+
); } @@ -314,6 +405,8 @@ function SwapCard({ displayAmount, token, selectedToken, + daiPoolLiquidity, + wbtcPoolLiquidity, }: { heading: string; balance: bigint; @@ -323,6 +416,8 @@ function SwapCard({ selectedToken: Token["symbol"]; onTokenChange: (value: string) => void; disabled: boolean; + daiPoolLiquidity: bigint | undefined; + wbtcPoolLiquidity: bigint | undefined; }) { return ( @@ -331,13 +426,18 @@ function SwapCard({
- +
+ + {!daiPoolLiquidity || !wbtcPoolLiquidity ? ( + No liquidity available in pool + ) : null} +
{ + return ( + <> + {children} +
+ + ); +}; + +export default Layout; diff --git a/web/app/pool/page.tsx b/web/app/pool/page.tsx index ad40365..6900a40 100644 --- a/web/app/pool/page.tsx +++ b/web/app/pool/page.tsx @@ -4,48 +4,55 @@ import React, { useState } from "react"; import Link from "next/link"; import toast from "react-hot-toast"; import { PlusIcon } from "@heroicons/react/24/solid"; +import HiddenContent from "~~/components/HiddenContent"; import { Button } from "~~/components/ui/button"; import { Card } from "~~/components/ui/card"; import { CPAMM_ADDRESS } from "~~/contracts/cpamm"; import { CPAMM_ABI } from "~~/contracts/cpamm"; import useCpamm from "~~/hooks/use-cpamm"; +import { cn } from "~~/utils/cn"; import { formatTokenWithDecimals } from "~~/utils/currency"; import waitForTransactionReceipt from "~~/utils/wait-for-transaction"; export default function AddLiquidity() { - const { userShares, writeContractAsync } = useCpamm(); + const { userShares, writeContractAsync, refetchAll } = useCpamm(); const [removingLiquidity, setRemovingLiquidity] = useState(false); - const formattedShares = formatTokenWithDecimals(userShares ?? 0n, 18); const handleRemoveLiquidity = async () => { if (!userShares) return; setRemovingLiquidity(true); - const tx = await toast.promise( - writeContractAsync({ - address: CPAMM_ADDRESS, - abi: CPAMM_ABI, - functionName: "removeLiquidity", - args: [userShares ?? 0n], - }), - { + + try { + const tx = await toast.promise( + writeContractAsync({ + address: CPAMM_ADDRESS, + abi: CPAMM_ABI, + functionName: "removeLiquidity", + args: [userShares ?? 0n], + }), + { + success: "Liquidity removed successfully", + loading: "Removing liquidity...", + error: err => { + console.error(err); + return "Failed to remove liquidity"; + }, + }, + ); + await toast.promise(waitForTransactionReceipt({ hash: tx }), { success: "Liquidity removed successfully", - loading: "Removing liquidity...", + loading: "Waiting for confirmation...", error: err => { console.error(err); return "Failed to remove liquidity"; }, - }, - ); - await toast.promise(waitForTransactionReceipt({ hash: tx }), { - success: "Liquidity removed successfully", - loading: "Waiting for confirmation...", - error: err => { - console.error(err); - return "Failed to remove liquidity"; - }, - }); + }); + } finally { + setRemovingLiquidity(false); + refetchAll(); + } }; return ( @@ -58,23 +65,27 @@ export default function AddLiquidity() { Add Liquidity - - {userShares ? ( -

- {formattedShares} share{formattedShares === "1" ? "" : "s"} -

- ) : ( -

No positions found

- )} -
- + + + + {userShares ? ( +

+ {formattedShares} share{formattedShares === "1" ? "" : "s"} +

+ ) : ( +

No positions found

+ )} +
+ +
); diff --git a/web/public/msl-dark.svg b/web/assets/msl.svg similarity index 100% rename from web/public/msl-dark.svg rename to web/assets/msl.svg diff --git a/web/components/Footer.tsx b/web/components/Footer.tsx index aa2f351..7f31869 100644 --- a/web/components/Footer.tsx +++ b/web/components/Footer.tsx @@ -1,42 +1,27 @@ import React from "react"; import Image from "next/image"; import { HeartIcon } from "@heroicons/react/24/solid"; +import MslLogo from "~~/assets/msl.svg"; +import { cn } from "~~/utils/cn"; /** * Site footer */ -export const Footer = () => { +export const Footer = ({ className }: { className?: string }) => { return ( -
- +
+

+ Built with at +

+ + Moonsong Labs + Moonsong Labs +
); }; diff --git a/web/components/Header.tsx b/web/components/Header.tsx index f03fdb1..0d3f69b 100644 --- a/web/components/Header.tsx +++ b/web/components/Header.tsx @@ -56,15 +56,13 @@ const HeaderMenuLinks = () => { */ export const Header = () => { return ( -
-
- -
- Double Zero Swap logo -
- Double Zero Swap - -
+
+ +
+ Double Zero Swap logo +
+ Double Zero Swap +
diff --git a/web/components/HiddenContent.tsx b/web/components/HiddenContent.tsx new file mode 100644 index 0000000..1ceecd3 --- /dev/null +++ b/web/components/HiddenContent.tsx @@ -0,0 +1,23 @@ +import { LockIcon } from "lucide-react"; +import { useAccount } from "wagmi"; +import { cn } from "~~/utils/cn"; + +export default function HiddenContent({ children, className }: { children: React.ReactNode; className?: string }) { + const { isConnected } = useAccount(); + + if (isConnected) { + return children; + } + + return ( +
+
{children}
+
+
+ + Connect your wallet to start +
+
+
+ ); +} diff --git a/web/components/ScaffoldEthAppWithProviders.tsx b/web/components/ScaffoldEthAppWithProviders.tsx index 998dba4..531132a 100644 --- a/web/components/ScaffoldEthAppWithProviders.tsx +++ b/web/components/ScaffoldEthAppWithProviders.tsx @@ -8,7 +8,6 @@ import { useTheme } from "next-themes"; import { Toaster } from "react-hot-toast"; import { WagmiProvider } from "wagmi"; import { BlockieAvatar } from "~~/components/BlockieAvatar"; -import { Footer } from "~~/components/Footer"; import { Header } from "~~/components/Header"; import { wagmiConfig } from "~~/services/web3/wagmiConfig"; @@ -18,7 +17,6 @@ const ScaffoldEthApp = ({ children }: { children: React.ReactNode }) => {
{children}
-
diff --git a/web/components/ui/button.tsx b/web/components/ui/button.tsx index c115d21..7c493f2 100644 --- a/web/components/ui/button.tsx +++ b/web/components/ui/button.tsx @@ -1,6 +1,7 @@ import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { type VariantProps, cva } from "class-variance-authority"; +import { Loader2 } from "lucide-react"; import { cn } from "~~/utils/cn"; const buttonVariants = cva( @@ -33,12 +34,22 @@ export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; + loading?: boolean; } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { + ({ className, variant, size, asChild = false, children, loading, disabled, ...props }, ref) => { const Comp = asChild ? Slot : "button"; - return ; + return ( + + {loading ? : children} + + ); }, ); Button.displayName = "Button"; diff --git a/web/hooks/use-cpamm.ts b/web/hooks/use-cpamm.ts index ad68eb4..7e9de8c 100644 --- a/web/hooks/use-cpamm.ts +++ b/web/hooks/use-cpamm.ts @@ -1,38 +1,50 @@ -import { useCallback } from "react"; -import { useAccount, useReadContract, useWriteContract } from "wagmi"; +import { useCallback, useMemo } from "react"; +import { useAccount, useReadContract, useSimulateContract, useWriteContract } from "wagmi"; import { CPAMM_ABI, CPAMM_ADDRESS } from "~~/contracts/cpamm"; +const options = { + address: CPAMM_ADDRESS, + abi: CPAMM_ABI, +} as const; + export default function useCpamm() { const { address } = useAccount(); const { writeContractAsync } = useWriteContract(); - const { - data: liquidity, - refetch: refetchLiquidity, - error, - } = useReadContract({ - address: CPAMM_ADDRESS, - abi: CPAMM_ABI, + const { data: liquidity, refetch: refetchLiquidity } = useReadContract({ + ...options, functionName: "getReserves", }); const { data: userShares } = useReadContract({ - address: CPAMM_ADDRESS, - abi: CPAMM_ABI, + ...options, functionName: "balanceOf", args: [address ?? ""], }); - if (error) { - console.error("error", error); - } + const { error: simulateAddLiquidityError } = useSimulateContract({ + ...options, + functionName: "addLiquidity", + args: [1n, 1n], + query: { + retry: 1, + }, + }); const refetchAll = useCallback(() => { refetchLiquidity(); }, [refetchLiquidity]); + const addLiquidityAllowed = useMemo(() => { + if (simulateAddLiquidityError === null) { + return true; + } + return !simulateAddLiquidityError.message.includes("Internal JSON-RPC error."); + }, [simulateAddLiquidityError]); + return { daiPoolLiquidity: liquidity?.[0], wbtcPoolLiquidity: liquidity?.[1], refetchAll, writeContractAsync, userShares, + addLiquidityAllowed, }; } diff --git a/web/package.json b/web/package.json index 102f0d4..489a3b6 100644 --- a/web/package.json +++ b/web/package.json @@ -11,7 +11,8 @@ "format:check": "prettier --check . '!(node_modules|.next|contracts)/**/*'", "lint": "next lint", "lint:check": "next lint --max-warnings=0", - "serve": "next start" + "serve": "next start", + "ci:prepare": "run-s format lint typecheck" }, "dependencies": { "@heroicons/react": "~2.1.5", @@ -53,6 +54,7 @@ "eslint-config-prettier": "~8.10.0", "eslint-plugin-prettier": "~5.2.1", "eslint-plugin-unused-imports": "^4.1.4", + "npm-run-all": "^4.1.5", "postcss": "~8.4.45", "prettier": "~3.3.3", "tailwindcss": "~3.4.11", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index a7173de..ccf5ff7 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -120,6 +120,9 @@ importers: eslint-plugin-unused-imports: specifier: ^4.1.4 version: 4.1.4(@typescript-eslint/eslint-plugin@5.40.1(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1) + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 postcss: specifier: ~8.4.45 version: 8.4.49 @@ -2162,6 +2165,10 @@ packages: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -2427,6 +2434,10 @@ packages: caniuse-lite@1.0.30001687: resolution: {integrity: sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==} + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2482,10 +2493,16 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -2550,6 +2567,10 @@ packages: cross-fetch@4.0.0: resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + cross-spawn@6.0.6: + resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} + engines: {node: '>=4.8'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2803,6 +2824,10 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -3233,6 +3258,10 @@ packages: has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3283,6 +3312,9 @@ packages: hmac-drbg@1.0.1: resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -3714,6 +3746,10 @@ packages: lit@2.8.0: resolution: {integrity: sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==} + load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -3775,6 +3811,10 @@ packages: memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -3973,6 +4013,9 @@ packages: sass: optional: true + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} @@ -4012,6 +4055,9 @@ packages: node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -4020,6 +4066,11 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + npm-run-all@4.1.5: + resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} + engines: {node: '>= 4'} + hasBin: true + npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -4164,6 +4215,10 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -4179,6 +4234,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-type@3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4193,6 +4252,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + pidtree@0.3.1: + resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} + engines: {node: '>=0.10'} + hasBin: true + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -4459,6 +4523,10 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + read-pkg@3.0.0: + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -4642,10 +4710,18 @@ packages: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -4695,6 +4771,18 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.20: + resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + split-on-first@1.1.0: resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} engines: {node: '>=6'} @@ -4758,6 +4846,10 @@ packages: resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} engines: {node: '>= 0.4'} + string.prototype.padend@3.1.6: + resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} + engines: {node: '>= 0.4'} + string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} @@ -4824,6 +4916,10 @@ packages: resolution: {integrity: sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==} engines: {node: '>=14.0.0'} + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -5147,6 +5243,9 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + valtio@1.11.2: resolution: {integrity: sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==} engines: {node: '>=12.20.0'} @@ -5218,6 +5317,10 @@ packages: resolution: {integrity: sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==} engines: {node: '>= 0.4'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -8062,6 +8165,10 @@ snapshots: ansi-regex@6.1.0: {} + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -8379,6 +8486,12 @@ snapshots: caniuse-lite@1.0.30001687: {} + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -8458,10 +8571,16 @@ snapshots: clsx@2.1.1: {} + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + color-convert@2.0.1: dependencies: color-name: 1.1.4 + color-name@1.1.3: {} + color-name@1.1.4: {} commander@12.1.0: {} @@ -8525,6 +8644,14 @@ snapshots: transitivePeerDependencies: - encoding + cross-spawn@6.0.6: + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -8810,6 +8937,8 @@ snapshots: escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} + escape-string-regexp@2.0.0: {} escape-string-regexp@4.0.0: {} @@ -9349,6 +9478,8 @@ snapshots: has-bigints@1.0.2: {} + has-flag@3.0.0: {} + has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -9400,6 +9531,8 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 + hosted-git-info@2.8.9: {} + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -9877,6 +10010,13 @@ snapshots: lit-element: 3.3.3 lit-html: 2.8.0 + load-json-file@4.0.0: + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -9934,6 +10074,8 @@ snapshots: memoize-one@5.2.1: {} + memorystream@0.3.1: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -10236,6 +10378,8 @@ snapshots: - '@babel/core' - babel-plugin-macros + nice-try@1.0.5: {} + node-abort-controller@3.1.1: {} node-addon-api@2.0.2: {} @@ -10260,10 +10404,29 @@ snapshots: node-releases@2.0.18: {} + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + normalize-path@3.0.0: {} normalize-range@0.1.2: {} + npm-run-all@4.1.5: + dependencies: + ansi-styles: 3.2.1 + chalk: 2.4.2 + cross-spawn: 6.0.6 + memorystream: 0.3.1 + minimatch: 3.1.2 + pidtree: 0.3.1 + read-pkg: 3.0.0 + shell-quote: 1.8.2 + string.prototype.padend: 3.1.6 + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -10411,6 +10574,8 @@ snapshots: path-is-absolute@1.0.1: {} + path-key@2.0.1: {} + path-key@3.1.1: {} path-key@4.0.0: {} @@ -10422,6 +10587,10 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-type@3.0.0: + dependencies: + pify: 3.0.0 + path-type@4.0.0: {} pathe@1.1.2: {} @@ -10430,6 +10599,8 @@ snapshots: picomatch@2.3.1: {} + pidtree@0.3.1: {} + pify@2.3.0: {} pify@3.0.0: {} @@ -10733,6 +10904,12 @@ snapshots: dependencies: pify: 2.3.0 + read-pkg@3.0.0: + dependencies: + load-json-file: 4.0.0 + normalize-package-data: 2.5.0 + path-type: 3.0.0 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -10946,10 +11123,16 @@ snapshots: dependencies: kind-of: 6.0.3 + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 + shebang-regex@1.0.0: {} + shebang-regex@3.0.0: {} shell-quote@1.8.2: {} @@ -11000,6 +11183,20 @@ snapshots: source-map@0.6.1: {} + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.20 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.20 + + spdx-license-ids@3.0.20: {} + split-on-first@1.1.0: {} split2@4.2.0: {} @@ -11063,6 +11260,13 @@ snapshots: set-function-name: 2.0.2 side-channel: 1.0.6 + string.prototype.padend@3.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.5 + es-object-atoms: 1.0.0 + string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 @@ -11130,6 +11334,10 @@ snapshots: superstruct@1.0.4: {} + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -11440,6 +11648,11 @@ snapshots: v8-compile-cache-lib@3.0.1: optional: true + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + valtio@1.11.2(@types/react@18.3.14)(react@18.3.1): dependencies: proxy-compare: 2.5.1 @@ -11564,6 +11777,10 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/web/public/msl.svg b/web/public/msl.svg deleted file mode 100644 index f305435..0000000 --- a/web/public/msl.svg +++ /dev/null @@ -1 +0,0 @@ -