diff --git a/components.json b/components.json new file mode 100644 index 0000000..98e21f0 --- /dev/null +++ b/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": false, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 306191f..f94367b 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,14 @@ "@polkadot-api/substrate-bindings": "^0.9.3", "@polkadot-api/substrate-client": "^0.3.0", "@polkadot-api/utils": "^0.1.2", + "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-radio-group": "^1.2.1", + "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0", @@ -40,6 +44,7 @@ "buffer": "^6.0.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "1.0.0", "lucide-react": "^0.454.0", "polkadot-api": "^1.7.0", "react": "^18.3.1", @@ -52,6 +57,7 @@ "rxjs": "^7.8.1", "save-as": "^0.1.8", "tailwind-merge": "^2.5.4", + "tailwindcss-animate": "^1.0.7", "uuid": "^11.0.2" }, "devDependencies": { diff --git a/src/chain.state.ts b/src/chain.state.ts index b787d57..c3ecb65 100644 --- a/src/chain.state.ts +++ b/src/chain.state.ts @@ -12,11 +12,10 @@ import { createClient as createSubstrateClient } from "@polkadot-api/substrate-c import { toHex } from "@polkadot-api/utils" import { state } from "@react-rxjs/core" import { createClient, PolkadotClient } from "polkadot-api" -import { chainSpec } from "polkadot-api/chains/westend2" import { withLogsRecorder } from "polkadot-api/logs-provider" import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat" import { getSmProvider } from "polkadot-api/sm-provider" -import { Client } from "polkadot-api/smoldot" +import { Chain, Client } from "polkadot-api/smoldot" import { startFromWorker } from "polkadot-api/smoldot/from-worker" import SmWorker from "polkadot-api/smoldot/worker?worker" import { getWsProvider } from "polkadot-api/ws-provider/web" @@ -26,11 +25,18 @@ import { from, map, NEVER, + Observable, of, startWith, switchMap, tap, } from "rxjs" +import polkadotRawNetworks from "./pages/Network/polkadot.json" +import { createSignal } from "@react-rxjs/utils" +import { chainSpec } from "polkadot-api/chains/polkadot" +import { chainSpec as ksmChainSpec } from "polkadot-api/chains/ksmcc3" +import { chainSpec as westendChainSpec } from "polkadot-api/chains/westend2" +import { chainSpec as paseoChainSpec } from "polkadot-api/chains/paseo" export type ChainSource = { id: string } & ( | { @@ -46,11 +52,75 @@ export type ChainSource = { id: string } & ( } ) -const selectedSource$ = of({ - id: "polkadot", - type: "chainSpec", - value: { chainSpec }, +export type Network = { + id: string + display: string + endpoints: Record + lightclient: boolean +} + +export type NetworkCategory = { + name: string + networks: Network[] +} +export type SelectedChain = { + network: Network + endpoint: string +} + +const polkadot = polkadotRawNetworks.map( + (x): Network => ({ + endpoints: x.rpcs as any, + lightclient: x.hasChainSpecs, + id: x.id, + display: x.display, + }), +) + +export const networkCategories: NetworkCategory[] = [ + { + name: "Polkadot", + networks: polkadot, + }, +] + +export const [selectedChainChanged$, onChangeChain] = + createSignal() +export const selectedChain$ = state(selectedChainChanged$, { + network: polkadot[0], + endpoint: "light-client", }) +selectedChain$.subscribe() + +const relayChains = new Set(["polkadot", "kusama", "westend", "paseo"]) +const selectedSource$ = selectedChain$.pipe( + switchMap( + ({ endpoint, network }): Observable | Promise => { + if (endpoint !== "light-client") + return of({ + type: "websocket", + value: endpoint, + } as ChainSource) + const { id } = network + if (relayChains.has(id)) { + return of({ + type: "chainSpec", + value: { chainSpec: id }, + } as ChainSource) + } + return import(`./chainspecs/${id}.ts`).then(({ chainSpec }) => { + const parsed = JSON.parse(chainSpec) + return { + type: "chainSpec", + value: { + chainSpec, + relayChain: parsed.relayChain || parsed.relay_chain, + }, + } as ChainSource + }) + }, + ), +) type AnyMetadata = CodecType @@ -119,31 +189,48 @@ async function getMetadata(client: PolkadotClient): Promise { return decAnyMetadata(metadataResponse.asBytes()) } -let smoldot: Client | null = null -export function getProvider(source: ChainSource) { +let smoldot: { + client: Client + relayChains: Record> +} | null = null +function getProvider(source: ChainSource) { if (source.type === "websocket") { return withPolkadotSdkCompat(getWsProvider(source.value)) } if (!smoldot) { - smoldot = startFromWorker(new SmWorker(), { + const client = startFromWorker(new SmWorker(), { logCallback: (level, target, message) => { console.debug("[%s(%s)] %s", target, level, message) }, }) + smoldot = { + client, + relayChains: { + polkadot: client.addChain({ + chainSpec: chainSpec, + }), + kusama: client.addChain({ + chainSpec: ksmChainSpec, + }), + westend: client.addChain({ + chainSpec: westendChainSpec, + }), + paseo: client.addChain({ + chainSpec: paseoChainSpec, + }), + }, + } } const chain = source.value.relayChain - ? smoldot - .addChain({ - chainSpec: source.value.relayChain, + ? smoldot.relayChains[source.value.relayChain].then((chain) => { + return smoldot!.client.addChain({ + chainSpec: source.value.chainSpec, + potentialRelayChains: [chain], }) - .then((chain) => - smoldot!.addChain({ - chainSpec: source.value.chainSpec, - potentialRelayChains: [chain], - }), - ) - : smoldot.addChain({ + }) + : smoldot.relayChains[source.value.chainSpec] || + smoldot.client.addChain({ chainSpec: source.value.chainSpec, }) diff --git a/src/chainspecs/acala.ts b/src/chainspecs/acala.ts new file mode 100644 index 0000000..68ab589 --- /dev/null +++ b/src/chainspecs/acala.ts @@ -0,0 +1,30 @@ +export const chainSpec = JSON.stringify({ + name: "Acala", + id: "acala", + chainType: "Live", + bootNodes: [ + "/dns/acala-bootnode-4.aca-api.network/tcp/30333/p2p/12D3KooWBLwm4oKY5fsbkdSdipHzYJJHSHhuoyb1eTrH31cidrnY", + "/dns/acala-bootnode-4.aca-api.network/tcp/30334/ws/p2p/12D3KooWBLwm4oKY5fsbkdSdipHzYJJHSHhuoyb1eTrH31cidrnY", + "/dns/acala-bootnode-5.aca-api.network/tcp/80/ws/p2p/12D3KooWN6ZZ2LFSJo2vDci3hqmmcvqMcKJAbREvuYCdvoBvV2D4", + "/dns/acala-bootnode-5.aca-api.network/tcp/443/wss/p2p/12D3KooWN6ZZ2LFSJo2vDci3hqmmcvqMcKJAbREvuYCdvoBvV2D4", + "/dns/acala-bootnode-6.aca-api.network/tcp/80/ws/p2p/12D3KooWEBniruZHpoVj8RUtAFPahaN8UaGP6UtQb5Bdp4MVYbLc", + "/dns/acala-bootnode-6.aca-api.network/tcp/443/wss/p2p/12D3KooWEBniruZHpoVj8RUtAFPahaN8UaGP6UtQb5Bdp4MVYbLc", + "/dns/acala-bootnode-7.aca-api.network/tcp/80/ws/p2p/12D3KooWMq7AtHFx3ZboMT92HQw8BvhZFzJh8UrPCZeMB3yFLe1V", + ], + properties: { + ss58Format: 10, + tokenDecimals: [12, 12, 10, 10], + tokenSymbol: ["ACA", "AUSD", "DOT", "LDOT"], + }, + relayChain: "polkadot", + paraId: 2000, + consensusEngine: null, + codeSubstitutes: {}, + badBlocks: [ + "0xa820d0e6b3babb3a7023a229cfe61c32ceb68602f5339e2d416d7fbca5e82aa7", + ], + genesis: { + stateRootHash: + "0x010c5745a5d42bcfbe0a644d5a2a4e22e2ff0fd378d48208ecfacea5b7e05a74", + }, +}) diff --git a/src/chainspecs/ajuna.ts b/src/chainspecs/ajuna.ts new file mode 100644 index 0000000..d3660a4 --- /dev/null +++ b/src/chainspecs/ajuna.ts @@ -0,0 +1,28 @@ +export const chainSpec = JSON.stringify({ + name: "Ajuna Polkadot", + id: "ajuna_polkadot", + chainType: "Live", + bootNodes: [ + "/dns4/boot-node.helikon.io/tcp/8510/p2p/12D3KooWA1zjoSfN1CWRMY4nRxD94sSnNAj8UkhD7tsh4K6rwYSX", + "/dns4/boot-node.helikon.io/tcp/8512/wss/p2p/12D3KooWA1zjoSfN1CWRMY4nRxD94sSnNAj8UkhD7tsh4K6rwYSX", + "/dns4/node-7135141363928633344-0.p2p.onfinality.io/tcp/20256/ws/p2p/12D3KooWS8wVu6hmDwZvqDXJWnBX5sKb9edR1HWv5hYKD1Ysc2ud", + "/dns4/node-7163299142418038784-0.p2p.onfinality.io/tcp/20806/ws/p2p/12D3KooWKNTJ5zQr3SK2DphVxEzbBWW9wSqd64ruad1uiiZd6E1Y", + "/dns/ajuna.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWRyAHbMPNL7CuQtm987a6iLKftME3eQpVqBC6fUsjWuof", + "/dns/ajuna.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWLXSQdRNS5EXuhpFYiRKHufRsQtb64t4CYj2ah7zx86jA", + "/dns/rpc-para.ajuna.network/tcp/30332/p2p/12D3KooWLFfa4J2T3JGZft74q3Wu6kSYHPJHNzLsVhdLPGbAZ9Wf", + "/dns/rpc-para.ajuna.network/tcp/30333/ws/p2p/12D3KooWLFfa4J2T3JGZft74q3Wu6kSYHPJHNzLsVhdLPGbAZ9Wf", + "/dns/ajuna-polkadot-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWAPXggzmvxX8pefwGMNXDzUwigHEKCmrwZzMpySJbYwUK", + "/dns/ajuna-polkadot-boot-ng.dwellir.com/tcp/30363/p2p/12D3KooWAPXggzmvxX8pefwGMNXDzUwigHEKCmrwZzMpySJbYwUK", + "/dns/ajuna-polkadot-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWAPXggzmvxX8pefwGMNXDzUwigHEKCmrwZzMpySJbYwUK", + "/dns/ajuna-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWCfjWNEmYcGJwF8S1xizhSHyFHwwDLmQxQCr2Wu9JUTYw", + "/dns/ajuna-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWCfjWNEmYcGJwF8S1xizhSHyFHwwDLmQxQCr2Wu9JUTYw", + ], + properties: { ss58Format: 1328, tokenDecimals: 12, tokenSymbol: "AJUN" }, + relay_chain: "polkadot", + para_id: 2051, + codeSubstitutes: {}, + genesis: { + stateRootHash: + "0x6e666a1df855628a99876f9f876b94d6d20a397fb1b5d92a3747df75e29c61b1", + }, +}) diff --git a/src/chainspecs/astar.ts b/src/chainspecs/astar.ts new file mode 100644 index 0000000..b86137c --- /dev/null +++ b/src/chainspecs/astar.ts @@ -0,0 +1,24 @@ +export const chainSpec = JSON.stringify({ + name: "Astar", + id: "astar", + chainType: "Live", + bootNodes: [ + "/ip4/109.238.14.102/tcp/30333/ws/p2p/12D3KooWPH9bkXRkPcHGKd7DSgYfLLVE1Cw7QNr2M4kKwdVwGZXN", + "/ip4/199.85.208.179/tcp/30333/ws/p2p/12D3KooWMwrAXhSuzCrtiwfVQuB4oLAZj3CLxVzMrqBJB4GKjnQa", + "/ip4/131.153.79.50/tcp/30333/ws/p2p/12D3KooWB2XY9Uw1ZR8qtD5DQ4TKivJkQdqM5c6hyY9ixSDAASBa", + "/dns/bootnode-01.astar.network/tcp/443/wss/p2p/12D3KooWPH9bkXRkPcHGKd7DSgYfLLVE1Cw7QNr2M4kKwdVwGZXN", + "/dns/bootnode-02.astar.network/tcp/443/wss/p2p/12D3KooWMwrAXhSuzCrtiwfVQuB4oLAZj3CLxVzMrqBJB4GKjnQa", + "/dns/bootnode-03.astar.network/tcp/443/wss/p2p/12D3KooWB2XY9Uw1ZR8qtD5DQ4TKivJkQdqM5c6hyY9ixSDAASBa", + "/dns4/astar-bootnode-1-tls.p2p.onfinality.io/tcp/443/wss/p2p/12D3KooWERrQFE8ss7zYfcHp8ULVCc1N7gur7GqZ8ESuZB1Nmioh", + ], + properties: { ss58Format: 5, tokenDecimals: 18, tokenSymbol: "ASTR" }, + relayChain: "polkadot", + paraId: 2006, + consensusEngine: null, + codeSubstitutes: {}, + badBlocks: [], + genesis: { + stateRootHash: + "0xc9451593261d67c47e14c5cbefeeffff5b5a1707cf81800becfc79e6df354da9", + }, +}) diff --git a/src/chainspecs/hydradx.ts b/src/chainspecs/hydradx.ts new file mode 100644 index 0000000..3336dbf --- /dev/null +++ b/src/chainspecs/hydradx.ts @@ -0,0 +1,25 @@ +export const chainSpec = JSON.stringify({ + name: "Hydration", + id: "hydra", + chainType: "Live", + bootNodes: [ + "/dns/p2p-01.hydra.hydradx.io/tcp/30333/p2p/12D3KooWHzv7XVVBwY4EX1aKJBU6qzEjqGk6XtoFagr5wEXx6MsH", + "/dns/p2p-02.hydra.hydradx.io/tcp/30333/p2p/12D3KooWR72FwHrkGNTNes6U5UHQezWLmrKu6b45MvcnRGK8J3S6", + "/dns/p2p-03.hydra.hydradx.io/tcp/30333/p2p/12D3KooWFDwxZinAjgmLVgsideCmdB2bz911YgiQdLEiwKovezUz", + "/dns4/boot.helikon.io/tcp/15120/p2p/12D3KooWDcQY1L2ny3F7YPyP4snCZZYc4eKWgPLEzdBvWBUjH5Yt", + "/dns4/boot.helikon.io/tcp/15125/wss/p2p/12D3KooWDcQY1L2ny3F7YPyP4snCZZYc4eKWgPLEzdBvWBUjH5Yt", + "/dns/hydration.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWGZaDfqPyzVxhA3k1qv72P7xqYTJS8W9U7GWUEdXYhtUU", + "/dns/hydration.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWBJMG8LCh6pLYbGapA3SNzjhQWE87ieGux41jKQrrf5js", + "/dns/hydration-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWCtrMH4H2p5XkGHkU7K4CcbSmErouNuN3j7Bysj4a8hJX", + "/dns/hydration-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWCtrMH4H2p5XkGHkU7K4CcbSmErouNuN3j7Bysj4a8hJX", + ], + properties: { tokenDecimals: 12, tokenSymbol: "HDX" }, + relay_chain: "polkadot", + para_id: 2034, + consensusEngine: null, + codeSubstitutes: {}, + genesis: { + stateRootHash: + "0x33a542156b00e7dd467e2b7704563abd84f888ccbc6afd6f1a1802a55db1d4de", + }, +}) diff --git a/src/chainspecs/kusama.ts b/src/chainspecs/kusama.ts new file mode 100644 index 0000000..6dfef7d --- /dev/null +++ b/src/chainspecs/kusama.ts @@ -0,0 +1 @@ +export { chainSpec } from "polkadot-api/chains/ksmcc3" diff --git a/src/chainspecs/polkadot.ts b/src/chainspecs/polkadot.ts new file mode 100644 index 0000000..1c44d79 --- /dev/null +++ b/src/chainspecs/polkadot.ts @@ -0,0 +1 @@ +export { chainSpec } from "polkadot-api/chains/polkadot" diff --git a/src/chainspecs/polkadot_asset_hub.ts b/src/chainspecs/polkadot_asset_hub.ts new file mode 100644 index 0000000..760098b --- /dev/null +++ b/src/chainspecs/polkadot_asset_hub.ts @@ -0,0 +1 @@ +export { chainSpec } from "polkadot-api/chains/polkadot_asset_hub" diff --git a/src/chainspecs/polkadot_bridge_hub.ts b/src/chainspecs/polkadot_bridge_hub.ts new file mode 100644 index 0000000..797144e --- /dev/null +++ b/src/chainspecs/polkadot_bridge_hub.ts @@ -0,0 +1 @@ +export { chainSpec } from "polkadot-api/chains/polkadot_bridge_hub" diff --git a/src/chainspecs/polkadot_collectives.ts b/src/chainspecs/polkadot_collectives.ts new file mode 100644 index 0000000..128c07d --- /dev/null +++ b/src/chainspecs/polkadot_collectives.ts @@ -0,0 +1 @@ +export { chainSpec } from "polkadot-api/chains/polkadot_collectives" diff --git a/src/chainspecs/polkadot_people.ts b/src/chainspecs/polkadot_people.ts new file mode 100644 index 0000000..0fffa55 --- /dev/null +++ b/src/chainspecs/polkadot_people.ts @@ -0,0 +1 @@ +export { chainSpec } from "polkadot-api/chains/polkadot_people" diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..e6a723d --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..36496a2 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx new file mode 100644 index 0000000..0ac9af3 --- /dev/null +++ b/src/components/ui/command.tsx @@ -0,0 +1,153 @@ +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { Command as CommandPrimitive } from "cmdk" +import { Search } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 1aeaa6e..c23630e 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,6 +1,8 @@ import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" -import { cn } from "@/utils" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" const Dialog = DialogPrimitive.Root @@ -18,7 +20,7 @@ const DialogOverlay = React.forwardRef< ref={ref} className={cn( "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", - className, + className )} {...props} /> @@ -34,12 +36,16 @@ const DialogContent = React.forwardRef< {children} + + + Close + )) @@ -52,7 +58,7 @@ const DialogHeader = ({
@@ -66,7 +72,7 @@ const DialogFooter = ({
@@ -81,7 +87,7 @@ const DialogTitle = React.forwardRef< ref={ref} className={cn( "text-lg font-semibold leading-none tracking-tight", - className, + className )} {...props} /> diff --git a/src/components/ui/radio-group.tsx b/src/components/ui/radio-group.tsx new file mode 100644 index 0000000..43b43b4 --- /dev/null +++ b/src/components/ui/radio-group.tsx @@ -0,0 +1,42 @@ +import * as React from "react" +import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" +import { Circle } from "lucide-react" + +import { cn } from "@/lib/utils" + +const RadioGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + ) +}) +RadioGroup.displayName = RadioGroupPrimitive.Root.displayName + +const RadioGroupItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + + + + + ) +}) +RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName + +export { RadioGroup, RadioGroupItem } diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..cf253cf --- /dev/null +++ b/src/components/ui/scroll-area.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area" + +import { cn } from "@/lib/utils" + +const ScrollArea = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + {children} + + + + +)) +ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName + +const ScrollBar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, orientation = "vertical", ...props }, ref) => ( + + + +)) +ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName + +export { ScrollArea, ScrollBar } diff --git a/src/index.css b/src/index.css index 890de2c..9a8151c 100644 --- a/src/index.css +++ b/src/index.css @@ -2,8 +2,9 @@ @tailwind components; @tailwind utilities; -input, dialog { - @apply bg-black +input, +dialog { + @apply bg-black; } :root { @@ -38,3 +39,66 @@ input, dialog { opacity: 1; pointer-events: all; } +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/src/pages/Header.tsx b/src/pages/Header.tsx index 5a089af..454cc21 100644 --- a/src/pages/Header.tsx +++ b/src/pages/Header.tsx @@ -1,47 +1,55 @@ import { Link } from "react-router-dom" +import { NetworkSwitcher } from "./Network/Network" export const Header = () => (
- papi-logo -

papi console

+ papi-logo +

+ papi console +

+
Explorer - - Metadata - Storage + + Extrinsics + Constants Runtime Calls - Extrinsics + Metadata
diff --git a/src/pages/Network/Network.tsx b/src/pages/Network/Network.tsx new file mode 100644 index 0000000..13caef3 --- /dev/null +++ b/src/pages/Network/Network.tsx @@ -0,0 +1,151 @@ +import { useState } from "react" +import { Check, ChevronDown } from "lucide-react" + +import { Button } from "@/components/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog" +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion" +import { Label } from "@/components/ui/label" +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import { ScrollArea } from "@/components/ui/scroll-area" +import { useStateObservable } from "@react-rxjs/core" +import { + Network, + networkCategories, + onChangeChain, + selectedChain$, +} from "@/chain.state" + +export function NetworkSwitcher() { + const [open, setOpen] = useState(false) + const selectedChain = useStateObservable(selectedChain$) + const [selectedNetwork, setSelectedNetwork] = useState( + selectedChain.network, + ) + const [selectedRpc, setSelecteRpc] = useState( + selectedChain.endpoint ?? "light-client", + ) + + const handleNetworkSelect = (network: Network) => { + setSelectedNetwork(network) + setSelecteRpc( + network.lightclient + ? "light-client" + : Object.values(network.endpoints)[0], + ) + } + + const handleConfirm = () => { + setOpen(false) + onChangeChain({ + network: selectedNetwork, + endpoint: selectedRpc, + }) + } + + return ( + + + + + + + Switch Network + + + + + No networks found. + + {networkCategories.map((category) => ( + + {category.networks.map((network) => ( + handleNetworkSelect(network)} + > + + {network.display} + + ))} + + ))} + + + + {selectedNetwork && ( + + + Connection Options + + + + {selectedNetwork.lightclient ? ( +
+ setSelecteRpc("light-client")} + /> + +
+ ) : null} + {Object.entries(selectedNetwork.endpoints).map( + ([rpcName, url]) => ( +
+ setSelecteRpc(url)} + /> + +
+ ), + )} +
+
+
+
+
+ )} + +
+
+ ) +} diff --git a/src/pages/Network/index.ts b/src/pages/Network/index.ts new file mode 100644 index 0000000..01c3f9a --- /dev/null +++ b/src/pages/Network/index.ts @@ -0,0 +1 @@ +export * from "./Network" diff --git a/src/pages/Network/polkadot.json b/src/pages/Network/polkadot.json new file mode 100644 index 0000000..4197e6f --- /dev/null +++ b/src/pages/Network/polkadot.json @@ -0,0 +1,642 @@ +[ + { + "id": "polkadot", + "display": "Polkadot Relay Chain", + "hasChainSpecs": true, + "rpcs": { + "Allnodes": "wss://polkadot-rpc.publicnode.com", + "Blockops": "wss://polkadot-public-rpc.blockops.network/ws", + "Dwellir": "wss://polkadot-rpc.dwellir.com", + "Dwellir Tunisia": "wss://polkadot-rpc-tn.dwellir.com", + "IBP1": "wss://rpc.ibp.network/polkadot", + "IBP2": "wss://polkadot.dotters.network", + "LuckyFriday": "wss://rpc-polkadot.luckyfriday.io", + "OnFinality": "wss://polkadot.api.onfinality.io/public-ws", + "RadiumBlock": "wss://polkadot.public.curie.radiumblock.co/ws", + "RockX": "wss://rockx-dot.w3node.com/polka-public-dot/ws", + "Stakeworld": "wss://dot-rpc.stakeworld.io", + "SubQuery": "wss://polkadot.rpc.subquery.network/public/ws" + }, + "nativeToken": { + "symbol": "DOT", + "decimals": 10 + } + }, + { + "id": "polkadot_asset_hub", + "display": "AssetHub", + "rpcs": { + "Dwellir": "wss://asset-hub-polkadot-rpc.dwellir.com", + "Dwellir Tunisia": "wss://statemint-rpc-tn.dwellir.com", + "IBP1": "wss://sys.ibp.network/asset-hub-polkadot", + "IBP2": "wss://asset-hub-polkadot.dotters.network", + "LuckyFriday": "wss://rpc-asset-hub-polkadot.luckyfriday.io", + "OnFinality": "wss://statemint.api.onfinality.io/public-ws", + "Parity": "wss://polkadot-asset-hub-rpc.polkadot.io", + "RadiumBlock": "wss://statemint.public.curie.radiumblock.co/ws", + "Stakeworld": "wss://dot-rpc.stakeworld.io/assethub" + }, + "relayChainInfo": { + "id": "polkadot", + "isSystem": true, + "parachain": 1000 + }, + "nativeToken": { + "symbol": "DOT", + "decimals": 10 + }, + "hasChainSpecs": true + }, + { + "id": "polkadot_bridge_hub", + "display": "BridgeHub", + "rpcs": { + "Dwellir": "wss://bridge-hub-polkadot-rpc.dwellir.com", + "Dwellir Tunisia": "wss://polkadot-bridge-hub-rpc-tn.dwellir.com", + "IBP1": "wss://sys.ibp.network/bridgehub-polkadot", + "IBP2": "wss://bridge-hub-polkadot.dotters.network", + "LuckyFriday": "wss://rpc-bridge-hub-polkadot.luckyfriday.io", + "OnFinality": "wss://bridgehub-polkadot.api.onfinality.io/public-ws", + "Parity": "wss://polkadot-bridge-hub-rpc.polkadot.io", + "RadiumBlock": "wss://bridgehub-polkadot.public.curie.radiumblock.co/ws", + "Stakeworld": "wss://dot-rpc.stakeworld.io/bridgehub" + }, + "relayChainInfo": { + "id": "polkadot", + "isSystem": true, + "parachain": 1002 + }, + "nativeToken": { + "symbol": "DOT", + "decimals": 10 + }, + "hasChainSpecs": true + }, + { + "id": "polkadot_collectives", + "display": "Collectives", + "rpcs": { + "Dwellir": "wss://collectives-polkadot-rpc.dwellir.com", + "Dwellir Tunisia": "wss://polkadot-collectives-rpc-tn.dwellir.com", + "IBP1": "wss://sys.ibp.network/collectives-polkadot", + "IBP2": "wss://collectives-polkadot.dotters.network", + "LuckyFriday": "wss://rpc-collectives-polkadot.luckyfriday.io", + "OnFinality": "wss://collectives.api.onfinality.io/public-ws", + "Parity": "wss://polkadot-collectives-rpc.polkadot.io", + "RadiumBlock": "wss://collectives.public.curie.radiumblock.co/ws", + "Stakeworld": "wss://dot-rpc.stakeworld.io/collectives" + }, + "relayChainInfo": { + "id": "polkadot", + "isSystem": true, + "parachain": 1001 + }, + "nativeToken": { + "symbol": "DOT", + "decimals": 10 + }, + "hasChainSpecs": true + }, + { + "id": "polkadot_core_time", + "display": "Coretime", + "rpcs": { + "IBP2": "wss://coretime-polkadot.dotters.network", + "Parity": "wss://polkadot-coretime-rpc.polkadot.io" + }, + "relayChainInfo": { + "id": "polkadot", + "isSystem": true, + "parachain": 1005 + }, + "nativeToken": { + "symbol": "DOT", + "decimals": 10 + }, + "hasChainSpecs": false + }, + { + "id": "polkadot_people", + "display": "People", + "rpcs": { + "IBP1": "wss://sys.ibp.network/people-polkadot", + "IBP2": "wss://people-polkadot.dotters.network", + "LuckyFriday": "wss://rpc-people-polkadot.luckyfriday.io", + "Parity": "wss://polkadot-people-rpc.polkadot.io", + "RadiumBlock": "wss://people-polkadot.public.curie.radiumblock.co/ws" + }, + "relayChainInfo": { + "id": "polkadot", + "isSystem": true, + "parachain": 1004 + }, + "nativeToken": { + "symbol": "DOT", + "decimals": 10 + }, + "hasChainSpecs": true + }, + { + "id": "acala", + "display": "Acala", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2000 + }, + "rpcs": { + "Dwellir": "wss://acala-rpc.dwellir.com", + "OnFinality": "wss://acala-polkadot.api.onfinality.io/public-ws" + }, + "nativeToken": { + "symbol": "ACA", + "decimals": 12 + }, + "hasChainSpecs": true + }, + { + "id": "ajuna", + "display": "Ajuna Network", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2051 + }, + "rpcs": { + "AjunaNetwork": "wss://rpc-para.ajuna.network", + "IBP1": "wss://ajuna.ibp.network", + "IBP2": "wss://ajuna.dotters.network", + "OnFinality": "wss://ajuna.api.onfinality.io/public-ws", + "RadiumBlock": "wss://ajuna.public.curie.radiumblock.co/ws" + }, + "nativeToken": { + "symbol": "AJUN", + "decimals": 12 + }, + "hasChainSpecs": true + }, + { + "id": "astar", + "display": "Astar", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2006 + }, + "rpcs": { + "Astar": "wss://rpc.astar.network", + "Automata 1RPC": "wss://1rpc.io/astr", + "Dwellir": "wss://astar-rpc.dwellir.com", + "OnFinality": "wss://astar.api.onfinality.io/public-ws", + "RadiumBlock": "wss://astar.public.curie.radiumblock.co/ws" + }, + "nativeToken": { + "symbol": "ASTR", + "decimals": 18 + }, + "hasChainSpecs": true + }, + { + "id": "bifrost", + "display": "Bifrost", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2030 + }, + "rpcs": { + "Dwellir": "wss://bifrost-polkadot-rpc.dwellir.com", + "IBP1": "wss://bifrost-polkadot.ibp.network", + "IBP2": "wss://bifrost-polkadot.dotters.network", + "Liebi": "wss://hk.p.bifrost-rpc.liebi.com/ws", + "LiebiEU": "wss://eu.bifrost-polkadot-rpc.liebi.com/ws", + "RadiumBlock": "wss://bifrost.public.curie.radiumblock.co/ws" + }, + "nativeToken": { + "symbol": "BNC", + "decimals": 12 + }, + "hasChainSpecs": false + }, + { + "id": "bitgreen", + "display": "Bitgreen", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2048 + }, + "rpcs": { + "Bitgreen": "wss://mainnet.bitgreen.org" + }, + "nativeToken": { + "symbol": "BBB", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "centrifuge", + "display": "Centrifuge", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2031 + }, + "rpcs": { + "Centrifuge": "wss://fullnode.centrifuge.io", + "Dwellir": "wss://centrifuge-rpc.dwellir.com", + "LuckyFriday": "wss://rpc-centrifuge.luckyfriday.io", + "OnFinality": "wss://centrifuge-parachain.api.onfinality.io/public-ws" + }, + "nativeToken": { + "symbol": "CFG", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "crustParachain", + "display": "Crust", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2008 + }, + "rpcs": { + "Crust": "wss://crust-parachain.crustapps.net", + "Crust APP": "wss://crust-parachain.crustnetwork.app", + "Crust CC": "wss://crust-parachain.crustnetwork.cc", + "Crust XYZ": "wss://crust-parachain.crustnetwork.xyz" + }, + "nativeToken": { + "symbol": "CRU", + "decimals": 12 + }, + "hasChainSpecs": false + }, + { + "id": "darwinia", + "display": "Darwinia", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2046 + }, + "rpcs": { + "Darwinia": "wss://rpc.darwinia.network", + "Dcdao": "wss://darwinia-rpc.dcdao.box", + "Dwellir": "wss://darwinia-rpc.dwellir.com" + }, + "nativeToken": { + "symbol": "RING", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "frequency", + "display": "Frequency", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2091 + }, + "rpcs": { + "Dwellir": "wss://frequency-rpc.dwellir.com", + "Frequency 0": "wss://0.rpc.frequency.xyz", + "Frequency 1": "wss://1.rpc.frequency.xyz", + "OnFinality": "wss://frequency-polkadot.api.onfinality.io/public-ws" + }, + "nativeToken": { + "symbol": "FRQCY", + "decimals": 8 + }, + "hasChainSpecs": false + }, + { + "id": "hydradx", + "display": "Hydration", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2034 + }, + "rpcs": { + "Dwellir": "wss://hydradx-rpc.dwellir.com", + "Galactic Council": "wss://rpc.hydradx.cloud", + "Helikon": "wss://rpc.helikon.io/hydradx", + "IBP1": "wss://hydradx.paras.ibp.network", + "IBP2": "wss://hydration.dotters.network" + }, + "nativeToken": { + "symbol": "HDX", + "decimals": 12 + }, + "hasChainSpecs": true + }, + { + "id": "hyperbridge", + "display": "Hyperbridge (Nexus)", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 3367 + }, + "rpcs": { + "BlockOps": "wss://hyperbridge-nexus-rpc.blockops.network", + "IBP1": "wss://nexus.ibp.network", + "IBP2": "wss://nexus.dotters.network" + }, + "nativeToken": { + "symbol": "BRIDGE", + "decimals": 12 + }, + "hasChainSpecs": false + }, + { + "id": "integritee", + "display": "Integritee Network", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 3359 + }, + "rpcs": { + "Dwellir": "wss://integritee-rpc.dwellir.com", + "Integritee": "wss://polkadot.api.integritee.network" + }, + "nativeToken": { + "symbol": "TEER", + "decimals": 12 + }, + "hasChainSpecs": false + }, + { + "id": "kilt", + "display": "KILT Spiritnet", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2086 + }, + "rpcs": { + "BOTLabs": "wss://spiritnet.kilt.io/", + "Dwellir": "wss://kilt-rpc.dwellir.com", + "IBP1": "wss://kilt.ibp.network", + "IBP2": "wss://kilt.dotters.network" + }, + "nativeToken": { + "symbol": "KILT", + "decimals": 15 + }, + "hasChainSpecs": false + }, + { + "id": "laos", + "display": "Laos", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 3370 + }, + "rpcs": { + "Dwellir": "wss://laos-rpc.dwellir.com", + "freeverse.io": "wss://rpc.laos.laosfoundation.io" + }, + "nativeToken": { + "symbol": "LAOS", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "litentry", + "display": "Litentry", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2013 + }, + "rpcs": { + "Dwellir": "wss://litentry-rpc.dwellir.com", + "Litentry": "wss://rpc.litentry-parachain.litentry.io" + }, + "nativeToken": { + "symbol": "LIT", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "logion", + "display": "Logion", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 3354 + }, + "rpcs": { + "Logion 1": "wss://para-rpc01.logion.network" + }, + "nativeToken": { + "symbol": "LGNT", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "manta", + "display": "Manta", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2104 + }, + "rpcs": { + "Manta Network": "wss://ws.manta.systems" + }, + "nativeToken": { + "symbol": "MANTA", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "moonbeam", + "display": "Moonbeam", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2004 + }, + "rpcs": { + "Dwellir": "wss://moonbeam-rpc.dwellir.com", + "IBP1": "wss://moonbeam.ibp.network", + "IBP2": "wss://moonbeam.dotters.network", + "Moonbeam Foundation": "wss://wss.api.moonbeam.network", + "OnFinality": "wss://moonbeam.api.onfinality.io/public-ws", + "RadiumBlock": "wss://moonbeam.public.curie.radiumblock.co/ws", + "UnitedBloc": "wss://moonbeam.unitedbloc.com", + "Allnodes": "wss://moonbeam-rpc.publicnode.com" + }, + "nativeToken": { + "symbol": "GLMR", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "mythos", + "display": "Mythos", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 3369 + }, + "rpcs": { + "parity": "wss://polkadot-mythos-rpc.polkadot.io" + }, + "nativeToken": { + "symbol": "MYTH", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "neuroweb", + "display": "NeuroWeb", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2043 + }, + "rpcs": { + "Dwellir": "wss://neuroweb-rpc.dwellir.com", + "TraceLabs": "wss://parachain-rpc.origin-trail.network" + }, + "nativeToken": { + "symbol": "NEURO", + "decimals": 12 + }, + "hasChainSpecs": false + }, + { + "id": "nodle", + "display": "Nodle", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2026 + }, + "rpcs": { + "Dwellir": "wss://nodle-rpc.dwellir.com", + "OnFinality": "wss://nodle-parachain.api.onfinality.io/public-ws" + }, + "nativeToken": { + "symbol": "NODL", + "decimals": 11 + }, + "hasChainSpecs": false + }, + { + "id": "pendulum", + "display": "Pendulum", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2094 + }, + "rpcs": { + "PendulumChain": "wss://rpc-pendulum.prd.pendulumchain.tech" + }, + "nativeToken": { + "symbol": "PEN", + "decimals": 12 + }, + "hasChainSpecs": false + }, + { + "id": "phala", + "display": "Phala Network", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2035 + }, + "rpcs": { + "Dwellir": "wss://phala-rpc.dwellir.com", + "Helikon": "wss://rpc.helikon.io/phala", + "OnFinality": "wss://phala.api.onfinality.io/public-ws", + "Phala": "wss://api.phala.network/ws", + "RadiumBlock": "wss://phala.public.curie.radiumblock.co/ws" + }, + "nativeToken": { + "symbol": "PHA", + "decimals": 12 + }, + "hasChainSpecs": false + }, + { + "id": "polimec", + "display": "Polimec", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 3344 + }, + "rpcs": { + "Amforc": "wss://polimec.rpc.amforc.com", + "Helikon": "wss://rpc.helikon.io/polimec", + "IBP1": "wss://polimec.ibp.network", + "IBP2": "wss://polimec.dotters.network", + "Polimec Foundation": "wss://rpc.polimec.org" + }, + "nativeToken": { + "symbol": "PLMC", + "decimals": 10 + }, + "hasChainSpecs": false + }, + { + "id": "unique", + "display": "Unique Network", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2037 + }, + "rpcs": { + "Dwellir": "wss://unique-rpc.dwellir.com", + "Geo Load Balancer": "wss://ws.unique.network", + "IBP1": "wss://unique.ibp.network", + "IBP2": "wss://unique.dotters.network", + "Unique America": "wss://us-ws.unique.network", + "Unique Asia": "wss://asia-ws.unique.network", + "Unique Europe": "wss://eu-ws.unique.network" + }, + "nativeToken": { + "symbol": "UNQ", + "decimals": 18 + }, + "hasChainSpecs": false + }, + { + "id": "zeitgeist", + "display": "Zeitgeist", + "relayChainInfo": { + "id": "polkadot", + "isSystem": false, + "parachain": 2092 + }, + "rpcs": { + "OnFinality": "wss://zeitgeist.api.onfinality.io/public-ws", + "ZeitgeistPM": "wss://main.rpc.zeitgeist.pm/ws" + }, + "nativeToken": { + "symbol": "ZTG", + "decimals": 10 + }, + "hasChainSpecs": false + } +] diff --git a/tailwind.config.js b/tailwind.config.js index d4771a9..569b717 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -2,30 +2,99 @@ * @type {import("tailwindcss").Config} */ export default { + darkMode: ["class"], content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { extend: { colors: { polkadot: { - 950: "#030110", - 900: "#0C052C", - 800: "#23126D", - 700: "#4027AE", - 650: "#6543EF", - 600: "#5E39F2", - 500: "#E6007A", - 400: "#FC76FF", - 300: "#DB9EFF", - 200: "#E4DAFF", - 100: "#F2F3FF", 0: "#FBFCFF", + 100: "#F2F3FF", + 200: "#E4DAFF", + 300: "#DB9EFF", + 400: "#FC76FF", + 500: "#E6007A", + 600: "#5E39F2", + 650: "#6543EF", + 700: "#4027AE", + 800: "#23126D", + 900: "#0C052C", + 950: "#030110", }, + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + chart: { + 1: "hsl(var(--chart-1))", + 2: "hsl(var(--chart-2))", + 3: "hsl(var(--chart-3))", + 4: "hsl(var(--chart-4))", + 5: "hsl(var(--chart-5))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { + height: "0", + }, + to: { + height: "var(--radix-accordion-content-height)", + }, + }, + "accordion-up": { + from: { + height: "var(--radix-accordion-content-height)", + }, + to: { + height: "0", + }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", }, }, }, plugins: [ function ({ addVariant }) { - addVariant("group-state-open", ':merge(.group)[data-state="open"] &'); + addVariant("group-state-open", ':merge(.group)[data-state="open"] &') }, + require("tailwindcss-animate"), ], -}; +} diff --git a/tsconfig.app.json b/tsconfig.app.json index 7610c89..c7118ba 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": ".", "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], diff --git a/tsconfig.json b/tsconfig.json index 1ffef60..fec8c8e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,5 +3,11 @@ "references": [ { "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" } - ] + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } }